Fortran Modules and Global Variables
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.
jkedmond
Updated on June 04, 2022Comments
-
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 almost 7 yearsIn 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 Героям слава almost 7 years@jkedmond Only if they have access to the appropriate module.
-
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 Героям слава almost 7 yearsIn the subroutine or in the module which contains the subroutine or even higher in the cascade of modules. The
only
part is optional. -
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 Героям слава almost 7 yearsWhy would you call the reading??? You don't have to. They have nothing to do with access to
x
andi
... -
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 almost 7 yearsPlease be patient. I am new to both stack exchange and FORTRAN programming.
-
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.