Fortran Modules and Global Variables

12,124

Data that is read once and accessed many times is very common. Here's a simple example of how that can work. The module my_data contains both the data to be stored x,i and the subroutine to read that data from disk read_data. Reading should be called once, and the data may be accessed multiple times from both the main program and subroutines. In the source file main.f90:

module my_data
   implicit none

   real :: x
   integer :: i

contains
subroutine read_data
   integer :: fid

   open(newunit=fid,file='config.txt',action='read',position='rewind')

   read(fid,*) x
   read(fid,*) i

   close(fid)
end subroutine read_data
end module my_data

module routines
   implicit none

contains
subroutine mysub
   use my_data, only : x, i

   ! -- Use data again
   write(*,*) 'x and i in subroutine are: ', x, i

end subroutine mysub
end module routines

program main
   use my_data, only : read_data, x, i
   use routines, only : mysub
   implicit none

   ! -- Initialize
   call read_data

   ! -- Use data
   write(*,*) 'x and i in main are: ', x, i

   ! -- Use data in subroutine
   call mysub

end program main

The file config.txt contains the data to be read.

mach5% more config.txt
1.23
5
mach5% ifort main.f90 && ./a.out
 x and i in main are:    1.230000               5
 x and i in subroutine are:    1.230000               5

Edit: A critical part of what's happening here is that x and i are stored in a place accessible by both the reading module and the main program. In this simple example I chose to store it in my_data, but it could conceivably be elsewhere. The sample code you posted, which has since been deleted (please edit it into your question), never stores the data you have read. Storing the data after reading it once is essential.

Edit 2: In your edited source, you read the data into the variable DataCube which is declared in the main program. Then, you attempt to access the data in the variable DataCube which is declared in the subroutine subrtn1. These are not the same variable. You must declare it once, and access it from multiple places. The best way to do this is by containing it in a module, as I show in my example. However, you can also pass it as arguments to routines, but this becomes cumbersome.

Share:
12,124
jkedmond
Author by

jkedmond

Updated on June 04, 2022

Comments

  • jkedmond
    jkedmond almost 2 years

    I'm trying to create a data structure accessible globally to the main program and all subroutines. The data structure is built from reading some .dat files.

    This kind of global accessibility seems appropriate for a module. So far my module solution includes: 1) define the data type globally; 2) include (contain) a bunch of subroutines in the module to open/read the .dat files; and 3) construct the data type from the .dat files.

    Ideally, I would like to construct this data structure ONCE within the module, and then have this single data structure globally accessible. I do NOT want to open/read the .dat files every time I call the module procedures.

    For example. Is there a way to declare the data structure as a global variable from the main program, and then call the module procedures to build the data structure once?

    @Ross. Source code:

    module DataTypeModule
    
      implicit none
    
      type :: DatCube
          integer :: NGrid(4)
          double precision, allocatable :: tgrid(:)
      end type DatCube
    
      contains
    
      subroutine DataArraySizes(NGrd)
        implicit none
        integer, intent(out) :: NGrd(4)
        open(unit=15, file='./Data/DataHeader.txt', status='old')
        read(15,*) NGrd(1)
        read(15,*) NGrd(2)
        read(15,*) NGrd(3)
        read(15,*) NGrd(4)
        close(15)
      end subroutine DataArraySizes
    
      subroutine DataTGrd(NGrd,tgrd)
        implicit none
        integer, intent(in) :: NGrd(4)
        double precision, intent(out) :: tgrd(NGrd(1))
        open(unit=16, file='./Data/tgrid.dat', status='old')
        read(16,*) tgrd
        close(16)
      end subroutine DataTGrd
    
      subroutine ConstructDataCube(DataCube)
        implicit none
        type(DatCube), Intent(out) :: DataCube
    
        integer, allocatable :: NGrd(:)
        double precision, allocatable :: tgrd(:)
    
        allocate( NGrd(4) )
        call DataArraySizes(NGrd)
        DataCube%NGrid = NGrd
    
        allocate( tgrd(NGrd(1)),DataCube%tgrid(NGrd(1)) )
        call DataTGrd(NGrd,tgrd)
        DataCube%tgrid = tgrd
    
        deallocate( NGrd,tgrd )
    
        return
      end
    
    end module DataTypeModule
    
    program main
      use DatatypeModule
      implicit none
      double precision :: arg1,out1(4)
      type(DatCube) :: DataCube
    
      call ConstructDataCube(DataCube)
    
      call subrtn1(arg1,out1)
    
      stop
    end
    
    
    subroutine subrtn1(arg1,out1)
      use DataTypeModule
      implicit none
      double precision, Intent(in)  :: arg1
      double precision, Intent(out) :: out1(4)
      type(DatCube) :: DataCube
    
      out1 = DataCube%NGrid
    
      return
    end
    
  • jkedmond
    jkedmond almost 7 years
    In your example, can (nested) subroutines called from the main program access the data x,i without passing the x and i as arguments?
  • Vladimir F Героям слава
    Vladimir F Героям слава almost 7 years
    @jkedmond Only if they have access to the appropriate module.
  • jkedmond
    jkedmond almost 7 years
    @VladimirF. What you're saying is that, I do not have to pass x and i as arguments as long as I add the declaration "use my_data, only : read_data, x, i" in the particular subroutine?
  • Vladimir F Героям слава
    Vladimir F Героям слава almost 7 years
    In the subroutine or in the module which contains the subroutine or even higher in the cascade of modules. The only part is optional.
  • jkedmond
    jkedmond almost 7 years
    @VladimirF. But then I am back to the same problem ... Each time I call the module procedures (from the main program or various subroutines), it open/reads the .dat files and constructs the data structure. That's a huge time sink. I would like to call the module procedure (construct the data structure) once and set this data structure as a global variable accessible to the main program and all subroutines. I do not want to pass this data structure as an argument to every subroutine, as I have many nested procedures.
  • Vladimir F Героям слава
    Vladimir F Героям слава almost 7 years
    Why would you call the reading??? You don't have to. They have nothing to do with access to x and i...
  • Ross
    Ross almost 7 years
    @jkedmond It seems like you have a fundamental misunderstanding about how modules work. If you want further help, I recommend you post some example code.
  • jkedmond
    jkedmond almost 7 years
    Please be patient. I am new to both stack exchange and FORTRAN programming.
  • jkedmond
    jkedmond almost 7 years
    @Ross. I think that I am not being clear about my question. I do not want to pass 'DataCube' as arguments to routines. In your example, your 'call read_data' within the main program reads 'x,i'; that's fine. However, it seems to me, if you 'call read_data' in a subroutine procedure within the main program, then the module will open/read 'x,i' again in the procedure. I want to avoid the multiple open/read executions in every nested subroutine of my main program; as these become a massive time sink.