Search code examples
modulefortranfortran90double-precisionfortran-common-block

Convert common block to module


I am a researcher working with a program written in Fortran. I have very basic coding skills, so I need a bit of help getting some code to compile properly.

I will give a bit of background before showing the code. I am dealing with a large number of data, which will require 64-bit compiling and greater than 2 gb of memory. The first thing I noticed in the code is that many of the variables were written as "real", but in my research I found that "double precision" allows for much larger variables, and would be a more flexible choice, so I changed all "real" variables to "double precision" variables.

There is also in a file that is included in the compilation of the fortran build file "dist.f", called "geocoord.inc". I found that the variables are saved to a common block, but once again, I need something that can hold a larger amount of data. As I have been led to believe, a module would be a better program to use. I need some advice in converting this include file to work properly with the module program, which I will list below.

Dist.f:

c Convert latitude and longitude to kilometers relative
c to center of coordinates by short distance conversion.

subroutine dist(xlat, xlon, xkm, ykm)

implicit none

c   Parameters:
double precision    xlat, xlon  ! (input)
double precision        xkm, ykm    ! (output)

c   Local variables:
double precision lat1, lat2, lat3
double precision    q
double precision    xx
double precision    yp

include "geocoord.inc"

c Set up short distance conversion by subr. SETORG
  q=60*xlat-olat
  yp=q+olat
  lat1=datan(rlatc*dtan(RAD*yp/60.0))
  lat2=datan(rlatc*dtan(RAD*OLAT/60.0))
  LAT3=(LAT2+LAT1)/2.
  xx=60*xlon-olon  !  - wegen LON E
  q=q*aa
  xx = xx*bb*dcos(LAT3)
  IF(rotate.ne.0.) then
c** rotate coordinate system anticlockwise
    yp=cost*q+sint*xx
    xx=cost*xx-sint*q
    q=yp
  ENDIF

  xkm=xx
  ykm=q

  return
  end

Geocoord.inc:

double precision rearth
double precision ellip
double precision rlatc
double precision rad
double precision olat, olon
double precision aa, bb, bc
double precision sint, cost
double precision rotate
integer icoordsystem

common /GEO_COORSYSTEM/ rearth, ellip, rlatc, rad,
&    olat, olon, aa, bb, bc, sint, cost, rotate,
&    icoordsystem

I appreciate any advice that you can provide and apologize for my relative ignorance in all things Fortran!


Solution

  • Modernizing an old code is often not an easy task. At least for a beginner. The move from real to double precision is not in the spirit of modern Fortran, but until you introduce modules it is OK. When you have modules it is better to do:

    module precisions
      integer, parameter :: rp = kind(1.d0) !if you insist on double, otherwise use selected_real_kind()
    end module
    

    and everywhere use the new kind constant that denotes your real precision:

      use precisions
    
          real(rp) :: variables
    

    With the common blocks, the one you showed would be:

    module geo_coordsystem
      use precisions
    
      implicit none
    
      real(rp) :: rearth
      real(rp) ::  ellip
      real(rp) ::  rlatc
      real(rp) ::  rad
      real(rp) ::  olat, olon
      real(rp) ::  aa, bb, bc
      real(rp) ::  sint, cost
      real(rp) ::  rotate
      integer icoordsystem
    
    end module
    

    Then you use it:

    subroutine dist(xlat, xlon, xkm, ykm)
      use precisions
      use geo_coordsystem
    
      implicit none
    

    You can also continually move you subroutines to modules. Do that in small steps and always check that you didn't introduce some bug.