Given the minimal working example below, I would like to modify it to avoid copy/pasting the calls of
call func_some_calc1(par)
call func_some_calc2(par)
in both main_func_problem1 and main_func_problem2.
Ideally I want to have one function main_func that is behaving differently for input parameters of type t_parameters_problem1 and t_parameters_problem2. I could declare its par parameter of base type class(t_parameters_base), but then having a switch inside that function depending on the actual argument type (using select type) is architecturally not good.
To solve this I tried to create a procedure in type t_parameters_base, which is calling those routines, to implement something like this (C++ syntax):
class t_parameters_base {
virtual void main_func() {
func_some_calc1(this)
func_some_calc2(this)
}
}
class t_parameters_problem1: public t_parameters_base {
virtual void main_func() {
t_parameters_base::main_func();
func_some_calc3_problem1(this);
}
}
But the problem is that those routines are using an input parameter of this type, which leads to circular dependency. How is it possible to solve this problem?
Update: Note that I really want to keep the implementation of func_some_calc1 and func_some_calc2 in different files (modulus/classes) as they implement very different logic using some private functions from their classes.
module parameters_base
type, public :: t_parameters_base
integer :: n
end type t_parameters_base
end module parameters_base
module parameters_problem1
use parameters_base
implicit none
type, extends(t_parameters_base), public :: t_parameters_problem1
integer :: p1
end type t_parameters_problem1
end module parameters_problem1
module parameters_problem2
use parameters_base
implicit none
type, extends(t_parameters_base), public :: t_parameters_problem2
integer :: p2
end type t_parameters_problem2
end module parameters_problem2
module some_calc1
use parameters_base
implicit none
contains
subroutine func_some_calc1(par)
class(t_parameters_base) :: par
end subroutine func_some_calc1
end module some_calc1
module some_calc2
use parameters_base
implicit none
contains
subroutine func_some_calc2(par)
class(t_parameters_base) :: par
end subroutine func_some_calc2
end module some_calc2
module some_calc3_problem1
use parameters_problem1
implicit none
contains
subroutine func_some_calc3_problem1(par)
type(t_parameters_problem1) :: par
print*, par%p1
end subroutine func_some_calc3_problem1
end module some_calc3_problem1
module some_calc3_problem2
use parameters_problem2
implicit none
contains
subroutine func_some_calc3_problem2(par)
type(t_parameters_problem2) :: par
print*, par%p2
end subroutine func_some_calc3_problem2
end module some_calc3_problem2
module main_problem1
use parameters_problem1
use some_calc1
use some_calc2
use some_calc3_problem1
implicit none
contains
subroutine main_func_problem1(par)
type(t_parameters_problem1) :: par
call func_some_calc1(par)
call func_some_calc2(par)
call func_some_calc3_problem1(par)
end subroutine main_func_problem1
end module main_problem1
module main_problem2
use parameters_problem2
use some_calc1
use some_calc2
use some_calc3_problem2
implicit none
contains
subroutine main_func_problem2(par)
type(t_parameters_problem2) :: par
call func_some_calc1(par)
call func_some_calc2(par)
call func_some_calc3_problem2(par)
end subroutine main_func_problem2
end module main_problem2
program module_test
use parameters_problem1
use parameters_problem2
use main_problem1
use main_problem2
implicit none
type(t_parameters_problem1) :: par1
type(t_parameters_problem2) :: par2
par1%p1 = 1
par2%p2 = 2
call main_func_problem1(par1)
call main_func_problem2(par2)
end program module_test
I think, what you are aiming for are polymorphic types with type bound procedures. Below you find a working example. To keep it simple, I do not have added any data to the types, but of course, that can be easily done. The routine invokeCalc12 is only defined in the base type, but can be invoked from the derived types, and as in Fortran all methods are virtual, it will call the right methods.
module calc_base
implicit none
type, abstract :: CalcBase
contains
procedure(calcInterface), deferred :: calc1
procedure(calcInterface), deferred :: calc2
procedure :: invokeCalc12
end type CalcBase
interface
subroutine calcInterface(self, ii)
import :: CalcBase
class(CalcBase), intent(inout) :: self
integer, intent(in) :: ii
end subroutine calcInterface
end interface
contains
subroutine invokeCalc12(self, ii)
class(CalcBase), intent(inout) :: self
integer, intent(in) :: ii
call self%calc1(ii)
call self%calc2(ii)
end subroutine invokeCalc12
end module calc_base
module some_calc
use calc_base
implicit none
type, extends(CalcBase) :: SomeCalc
contains
procedure :: calc1
procedure :: calc2
procedure :: calc3
end type SomeCalc
contains
subroutine calc1(self, ii)
class(SomeCalc), intent(inout) :: self
integer, intent(in) :: ii
print *, "SomeCalc1:calc1", ii
end subroutine calc1
subroutine calc2(self, ii)
class(SomeCalc), intent(inout) :: self
integer, intent(in) :: ii
print *, "SomeCalc1:calc2", ii
end subroutine calc2
subroutine calc3(self, ii)
class(SomeCalc), intent(inout) :: self
integer, intent(in) :: ii
call self%%invokeCalc12(ii)
end subroutine calc3
end module some_calc
program test
use some_calc
implicit none
type(SomeCalc) :: mySimulation
call mySimulation%calc3(42)
end program test
Note: I've seen that similar question has been posted at comp.lang.fortran, but at the moment I did not find a working example there, therefore the posting here.
Related
I have a code with lots of different features and methods. Some methods are all for the same feature, i.e. only one among a selection can be selected.
Furthermore, depending on a feature I might need to do modify subroutines elsewhere. So in a loop in routine inject I might have a small if statement asking if I have used feature A, then do a few extra operations.
This is very frustating since different features seems to be connected with others routines very arbirarirly, and can be difficult to maintain.
I have decided to do following to avoid this:
I define an object t_inject with the purpose to execture routine inject. I rewrite my routine inject such that it contains only the code that is common for all different scenarios.
type t_inject
contains
procedure,nopass :: inject => inject_default
end type
Now I have another object to handle my feature A in case it is selected.
type,extends(t_inject) :: t_inject_a
contains
procedure, nopass :: inject => inject_a
end type
My subroutines inject_a and inject have same interface. E.g.
subroutine inject_a( part )
type(t_part) , intent(inout) :: part % an external data type
call inject(part)
! do the extra bit of stuff you need to do
end subroutine
subroutine inject( part)
type(t_part) , intent(inout) :: part % an external data type
! carry out the default stuff
end subroutine
Now in my main program
class(t_inject) :: inj
allocate(inj :: t_inject_a)
call inj% inject ( part)
Is that the way you would do it and is it valid?
I initially thought of doing an abstract declared type with a deferred inject procedure where I then could extent.
But for a very trivial problem I might not need that - I am also wondering whether my call call inj% inject(part) is sufficient for the compiler to know to where to go. Sometimes I see codes which need the class is condition before making the call.
I think three points should be modified:
The type-bound procedures need to refer to actual procedure names (via =>). So, I have changed the name of a module procedure inject() to inject_default(). (But please see test2.f90 also).
We need to attach allocatable to a class variable (e.g., inj2) to allocate it with a concrete type (e.g., t_inject_a).
In the allocate statement, the name of a concrete type should appear before ::, such that allocate( t_inject_a :: inj2 ).
The modified code may look like this:
!! test.f90
module test_mod
implicit none
type t_inject
contains
procedure, nopass :: inject => inject_default
endtype
type, extends(t_inject) :: t_inject_a
contains
procedure, nopass :: inject => inject_a
endtype
type t_part !! some other type
integer :: x = 100, y = 200
endtype
contains
subroutine inject_default( part )
type(t_part), intent(inout) :: part
print *, "x = ", part % x
endsubroutine
subroutine inject_a( part )
type(t_part), intent(inout) :: part
call inject_default( part )
print *, "y = ", part % y
endsubroutine
end
program main
use test_mod
implicit none
class( t_inject ), allocatable :: inj1, inj2
type( t_part ) :: part
!! Polymorphic allocation with concrete types.
allocate( t_inject :: inj1 )
allocate( t_inject_a :: inj2 )
print *, "inj1:"
call inj1 % inject( part )
print *, "inj2:"
call inj2 % inject( part )
end
"gfortran-8 test.90 && ./a.out" gives
inj1:
x = 100
inj2:
x = 100
y = 200
We can also use a module procedure inject() (rather than inject_default()) by using procedure, nopass :: inject, for example:
!! test2.f90
module test_mod
implicit none
type t_inject
contains
procedure, nopass :: inject
! procedure, nopass :: inject => inject !! this also works
endtype
type, extends(t_inject) :: t_inject_a
contains
procedure, nopass :: inject => inject_a
endtype
type t_part !! some other type
integer :: x = 100, y = 200
endtype
contains
subroutine inject( part )
type(t_part), intent(inout) :: part
print *, "x = ", part % x
endsubroutine
subroutine inject_a( part )
type(t_part), intent(inout) :: part
call inject( part )
print *, "y = ", part % y
endsubroutine
end
!! The remaining part (and the result) is the same...
In addition, one can also separate actual procedures like inject() in a different file and use them to define new types like t_inject (see mylib.f90 and test3.f90 below). This might be useful to reuse routines in some library file.
!! mylib.f90
module mylib
implicit none
type t_part !! some other type
integer :: x = 100, y = 200
endtype
contains
subroutine inject( part )
type(t_part), intent(inout) :: part
print *, "x = ", part % x
end
subroutine inject_a( part )
type(t_part), intent(inout) :: part
call inject( part )
print *, "y = ", part % y
end
end
!! test3.f90
module test_mod
use mylib
implicit none
type t_inject
contains
procedure, nopass :: inject
endtype
type, extends(t_inject) :: t_inject_a
contains
procedure, nopass :: inject => inject_a
endtype
end
!! The main program is the same as test.f90.
!! compile: gfortran-8 mylib.f90 test3.f90
I have created a class called 'element' that has several attributes and type-bound procedures. One of the attributes is an abstract class type 'kin' that has two inherited type 'kin1' and 'kin2'. I would like to be able to assign 'kin1' or 'kin2' as a attribute to the object 'element' at run time using the constructor depending on the inputs. The objective is to have a list of elements, each one with element%kin being either 'kin1' type or 'kin2' type.
Module element
module element
use kin
implicit none
type,public :: element_type
class(kin_type),allocatable :: kin
contains
procedure,pass(this), private :: set_kin
procedure,pass(this), public :: get_kin
end type element_type
interface element_type
module procedure element_type_constructor
end interface element_type
contains
type (element_type) function element_type_constructor(kin)
implicit none
class(kin_type),allocatable, intent (in) :: kin
call element_type_constructor%set_kin(kin)
end function element_type_constructor
! my try of set_kin
subroutine set_kin(this,kin)
implicit none
class(element_type), intent(inout) :: this
class(kin_type),allocatable, intent(in) :: kin
this%kin = kin
end subroutine
end module element
Module kin
module kin
implicit none
private
type,abstract :: kin_type
end type kin_type
type,public, extends(kin_type) :: kin1_type
private
integer :: data1
contains
procedure,pass(this),private :: set_data1
procedure,pass(this),public :: get_data1
procedure,pass(this),public :: print =>print_kin1
end type kin1_type
type,public, extends(kin1_type) :: kin2_type
private
real :: data2
contains
procedure,pass(this),private :: set_data2
procedure,pass(this),public :: get_data2
procedure,pass(this),public :: print =>print_kin2
end type kin2_type
! constructor interface kin1_type
interface kin1_type
module procedure kin1_type_constructor
end interface kin1_type
! constructor interface kin2_type
interface kin2_type
module procedure kin2_type_constructor
end interface kin2_type
contains
! constructor kin1_type
type (kin1_type) function kin1_type_constructor(data1)
implicit none
integer, intent (in) :: data1
class(kin1_type), intent (in) :: kin
call kin1_type_constructor%set_data1(data1)
end function kin1_type_constructor
! constructor kin2_type
type (kin2_type) function kin1_type_constructor(data1,data2)
implicit none
integer, intent (in) :: data1
real, intent (in) :: data2
class(kin2_type), intent (in) :: kin
call kin2_type_constructor%set_data1(data1)
call kin2_type_constructor%set_data2(data2)
end function kin2_type_constructor
! Example of set subroutine
subroutine set_data1(this,data1)
class(kin1_type),intent(inout) :: this
integer, intent(in) :: data1
this%data1 = data1
end subroutine set_data1
! other procedures...
end module kin
Program
program test
use element
use kin
implicit none
type(element_type) :: thisElement
type(kin1_type) :: thisKin1
! constructor for thisKin1
thisKin1 = kin1_constructor(data1 = 1)
! constructor for thisElement
thisElement = element_type_constructor(kin = thisKin1)
! Check kin structure and values
call thisElement%kin%print
end program
Error
I receive the following error during the run of the element_type_constructor subroutine:
Program received signal SIGSEGV: Segmentation fault - invalid memory reference.
I can't comment yet, so here it goes as a first answer: the provided code is unfortunately incomplete. Furthermore, the vendor and version of the compiler is missing, which makes it really hard to guess what the actual problem is.
"Fixing" the code to get the following example shows that it is in principle working:
kin.f90:
module kin
implicit none
private
type,abstract,public :: kin_type
contains
procedure(print_iface), deferred :: print
end type kin_type
type,public, extends(kin_type) :: kin1_type
private
integer :: data1
contains
procedure,pass(this),private :: set_data1
procedure,pass(this),public :: print => print_kin1
end type kin1_type
! constructor interface kin1_type
interface kin1_type
module procedure kin1_type_constructor
end interface kin1_type
abstract interface
subroutine print_iface(this)
import kin_type
class(kin_type), intent(in) :: this
end subroutine
end interface
contains
! constructor kin1_type
type (kin1_type) function kin1_type_constructor(data1)
implicit none
integer, intent (in) :: data1
call kin1_type_constructor%set_data1(data1)
end function kin1_type_constructor
! Example of set subroutine
subroutine set_data1(this,data1)
class(kin1_type),intent(inout) :: this
integer, intent(in) :: data1
this%data1 = data1
end subroutine set_data1
subroutine print_kin1(this)
class(kin1_type),intent(in) :: this
print *, this%data1
end subroutine print_kin1
end module kin
element.f90:
module element
use kin, only: kin_type
implicit none
type,public :: element_type
class(kin_type), allocatable :: kin
contains
procedure,pass(this), private :: set_kin
end type element_type
interface element_type
module procedure element_type_constructor
end interface element_type
contains
type (element_type) function element_type_constructor(kin)
implicit none
class(kin_type), intent (in) :: kin
call element_type_constructor%set_kin(kin)
end function element_type_constructor
! my try of set_kin
subroutine set_kin(this,kin)
implicit none
class(element_type), intent(inout) :: this
class(kin_type), intent(in) :: kin
this%kin = kin
end subroutine
end module element
main.f90:
program test
use element
use kin
implicit none
type(element_type) :: thisElement
class(kin_type), allocatable :: thisKin1
! constructor for thisKin1
thisKin1 = kin1_type(data1 = 1)
! constructor for thisElement
thisElement = element_type(kin = thisKin1)
call thisElement%kin%print()
end program
Building it with gfortran 7.4.0 and runnig it yields:
$ gfortran -o prog kin.f90 element.f90 main.f90
$ ./prog
1
$
One notable difference to what was provided is the deferred print procedure on the abstract type since it is being called via an attribute defined as a class(kin_type). Unfortunately that does not explain the cited error.
Is there a way to overload the = operator so that you can write an assignment like in this example:
module constants_mod
integer,parameter :: dpn = selected_real_kind(14)
end module
module vectorField_mod
use constants_mod
implicit none
private
public :: vectorField
public :: allocateX,allocateY,allocateZ
public :: delete
! public :: operator(=)
type vectorField
integer,dimension(3) :: sx,sy,sz
real(dpn),dimension(:,:,:),allocatable :: x,y,z
end type
interface delete
module procedure deallocateVectorField
end interface
! interface operator (=)
! module procedure vectorAssign
! end interface
contains
! function vectorAssign(f) result(q)
! implicit none
! real(dpn),intent(in) :: f
! type(vectorField) :: q
! q%x = f; q%y = f; q%z = f
! end function
! subroutine vectorAssign(f,g)
! implicit none
! type(vectorField),intent(inout) :: f
! real(dpn),intent(in) :: g
! f%x = g; f%y = g; f%z = g
! end subroutine
subroutine allocateX(field,Nx,Ny,Nz)
implicit none
type(vectorField),intent(inout) :: field
integer,intent(in) :: Nx,Ny,Nz
if (allocated(field%x)) deallocate(field%x)
allocate(field%x(Nx,Ny,Nz))
field%sx = shape(field%x)
end subroutine
subroutine allocateY(field,Nx,Ny,Nz)
implicit none
type(vectorField),intent(inout) :: field
integer,intent(in) :: Nx,Ny,Nz
if (allocated(field%y)) deallocate(field%y)
allocate(field%y(Nx,Ny,Nz))
field%sy = shape(field%y)
end subroutine
subroutine allocateZ(field,Nx,Ny,Nz)
implicit none
type(vectorField),intent(inout) :: field
integer,intent(in) :: Nx,Ny,Nz
if (allocated(field%z)) deallocate(field%z)
allocate(field%z(Nx,Ny,Nz))
field%sz = shape(field%z)
end subroutine
subroutine deallocateVectorField(field)
implicit none
type(vectorField),intent(inout) :: field
deallocate(field%x,field%y,field%z)
field%sx = 0; field%sy = 0; field%sz = 0
end subroutine
end module
program test
use constants_mod
use vectorField_mod
implicit none
type(vectorField) :: a
integer :: N = 1
real(dpn) :: dt = 0.1
call allocateX(a,N,N,N)
call allocateY(a,N,N,N)
call allocateZ(a,N,N,N)
a%x = dble(1.0) ! want to avoid this
a%y = dble(1.0) ! want to avoid this
a%z = dble(1.0) ! want to avoid this
a = real(1.0,dpn) ! want this instead (does not compile)
call delete(a)
end program
I've tried two different ways (shown in comments) but I get errors saying that there is a syntax error in generic specification (for publicizing the = operator).
= is not an operator, it is an assignment in Fortran and they are very different beasts.
To the classical possibility found in Fortran 90 and explained well in other answers, Fortran 2003 added a better possibility to bind the overloaded operators and assignments with the derived type.
This way you are sure you will not import the type without the assignment (beware of public and private statement in this case!). It can have very unpleasant consequences and can be hard to debug:
type vectorField
integer,dimension(3) :: sx,sy,sz
real(dpn),dimension(:,:,:),allocatable :: x,y,z
contains
procedure :: assignVector
generic :: assignment(=) => assignVector
end type
This way you do not have to be that careful to not forget the public :: assignment (=)
For defined assignment operator(=) is not correct, but assignment(=) is: see Fortran 2008 12.4.3.4.3. So you instead want the two lumps
public :: assignment (=)
and
interface assignment (=)
module procedure vectorAssign
end interface
Note that the correct way to define the assignment is by the subroutine as you have it (although the assignee could have intent(out) instead of intent(inout)).
Yes, you can overload the assignment operator. The syntax and requirements are different for the assignment operator than for other operators because the semantics are fundamentally different: all other operators compute a new value based on one or two arguments, without changing the arguments, whereas assignment changes the value of the left-hand argument.
In your case, I think it should look like this:
module vectorField_mod
! ...
interface assignment (=)
module procedure vectorAssign
end interface
contains
! ...
subroutine vectorAssign(f,g)
implicit none
type(vectorField),intent(out) :: f
real(kind = dpn), intent(in) :: g
f%x = g
f%y = g
f%z = g
end subroutine vectorAssign
end module vectorField_mod
How to pass an encapsulated type bound function? I played with the example from the Modern Fortran Explained book (Metcalf, Reid and Cohen) and this is what I did:
module mod_polynoms_abstract
use mod_geometrics
implicit none
type, abstract :: bound_user_polynom
! No data
contains
procedure(user_polynom_interface), deferred :: eval
end type bound_user_polynom
abstract interface
real function user_polynom_interface(poly, pt)
import :: bound_user_polynom, point
class(bound_user_polynom) :: poly
type(point), intent(in) :: pt
end function user_polynom_interface
end interface
contains
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!Integral driver/chooser function
real function integral(userfun, options,status)
class(bound_user_polynom) :: userfun
integer, intent(in) :: options
real, intent(out) :: status
select case( options )
case (1)
integral = first_integral(userfun)
case (2)
integral = second_integral(userfun)
case default
integral = def_integral(userfun)
end select
end function
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!1. integration
real function first_integral(userfun)
class(bound_user_polynom),intent(in) :: userfun
first_integral= 1.0 * userfun%eval(point(x=2.,y=2.,z=0.))
end function
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!2. integration
real function second_integral(userfun)
class(bound_user_polynom),intent(in) :: userfun
second_integral= 2.0 * userfun%eval(point(x=2.,y=2.,z=0.))
end function
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!default integration
real function def_integral(userfun)
class(bound_user_polynom) :: userfun
def_integral= 0.0 * userfun%eval(point(x=2.,y=2.,z=0.))
end function
end module
This compiles, but when I run the program I get different results.
When I call the function, maybe like this:
integral_result = integral(poly, 2 , status)
I get sometimes the right result, which is computed with the second_integral(userfun)
function. But sometimes the result is wrong.
The function can't calculate userfun%eval(point(x=2.,y=2.,z=0.)) correctly, but I don't know why.
Is this the correct way to do this?
Edit:
I use :
COLLECT_GCC=gfortran4.8
COLLECT_LTO_WRAPPER=/usr/local/libexec/gcc/x86_64-unknown-linux-gnu/4.8.0/lto-wrapper
Ziel: x86_64-unknown-linux-gnu
Konfiguriert mit: ./configure --disable-multilib
Thread-Modell: posix
gcc-Version 4.8.0 (GCC)
The correct result of userfun%eval(point(x=2.,y=2.,z=0.)) is 0.962435484
So integral(poly, 2 , status) must give me 1.92487097.
But when I execute the program several times I got:
first run : 1.92487097
second run: 54877984.0
... : 1.92487097
... : 2.55142141E+27
... : 4.19146938E+33
... : 1.95548379
and so on ..
Edit 2:
The type polynom is defined as:
type, extends(bound_user_polynom) :: polynom
real(kind=kind(1.0D0)), allocatable, dimension(:) :: coeff
type(monomial),allocatable, dimension(:) :: monom
contains
procedure :: eval => poly_eval
procedure, private :: p_add
generic :: operator(+) => p_add
procedure, private :: p_subs
generic :: operator(-) => p_subs
end type
!constructor
interface polynom
module procedure construct_poly
end interface
and in my main program i call:
integral_result = integral(p(2), 2 , status)
Found the error with some help from comp.fortan:
in:
userfun%eval(point(x=2.,y=2.,z=0.))
i had one uninitialized variable which gave me this strange results.
Is seems there is nothing wrong with the rest of the code.
Thank You,
Jan
Thanks for the suggestions. I beg your pardon for not being clear enough. Let me describe it again to the best of my ability.
There are two models - A & B. Model A has a subroutine (which is not a part of module) compns.f which is called by a main program. Below, the compns.f code:
compns.f: (Model A)
subroutine compns(deltim,fhout)
use var_repos, only: compns_var_dump
open(unit=nlunit,file=gfs_namelist) ! reads a file for the variables deltim and fhout
rewind (nlunit)
read(nlunit,nam_mrf)
print *, deltim,fhout ! deltim = 360.0, fhout = 6.0
CALL compns_var_dump(deltim,fhout) ! calls the subroutine and passes the variables
end
Another module which contains the subroutine compns_var_dump (to collect the variables) is
var_repos.f90:
MODULE var_repos
IMPLICIT NONE
PUBLIC :: compns_var_dump
PUBLIC :: tstep_var_dump !!! to dump variables from another place
REAL, PUBLIC :: d_time ! dummy variable
! declare the variables which will go public here:
REAL, PUBLIC :: deltim, fhout
CONTAINS
SUBROUTINE compns_var_dump(deltim , fhout)
REAL, INTENT(inout) :: deltim , fhout
d_time = deltim
WRITE(*,*)'Inside var_repos: deltim = ',deltim,d_time
END SUBROUTINE compns_var_dump
SUBROUTINE tstep_var_dump
...
END SUBROUTINE tstep_var_dump
END MODULE var_repos
Now, I need the variables from var_repos.f90 in model B. The module in model B which requires them is the following:
mo_time_control.f90: (Model B)
MODULE time_control
PUBLIC :: get_delta_time
CONTAINS
REAL(dp) FUNCTION get_delta_time()
USE var_repos, ONLY: d_time
IMPLICIT NONE
REAL :: d_time
REAL :: a_time ! Testing
get_delta_time = d_time
a_time = d_time ! Testing
WRITE(*,*)'Inside function get_delta_time(): deltim= ',d_time,get_delta_time, a_time
END FUNCTION get_delta_time
END MODULE time_control
The outputs after running the models is as follows:
'Inside var_repos: deltim = ' 360.000 360.000
'Inside function get_delta_time(): deltim= ' 0.00000E+00 0.00000E+00 0.00000E+00
I hope that I am clear in this post. Is there a better way to do the above task? My philosophy was to collect the required variables from model A into one module through different subroutine calls, thus use this module as a repository and let model B use it for the variables it requires. Is this approach right?
Try this example:
MODULE var_repos
IMPLICIT NONE
PUBLIC :: compns_var_dump
REAL, PUBLIC :: deltim, var2
CONTAINS
SUBROUTINE compns_var_dump(deltim , fhout)
REAL, INTENT(in) :: deltim , fhout
WRITE(*,*)'Inside var_repos: args = ', deltim, fhout
var2 = fhout
END SUBROUTINE compns_var_dump
END MODULE var_repos
program test
use var_repos
call compns_var_dump ( 2.0, 3.0 )
write (*, *) "in main:", deltim, var2
end program test
The output is :
Inside var_repos: args = 2.00000000 3.00000000
in main: 0.00000000 3.00000000
I believe that answer is that the argument deltim of the subroutine and the module variable of the same name are different variables. Creating a subroutine dummy argument of the same name as the module variable masks the module module rather than automatically copying the value to the module variable. So in main the module variable deltim didn't receive the value 2 and is undefined. With the compiler I used the random value that it had was zero; the value might be different on a different compiler. On the other hand, the variables fhout and var2 are different, with the dummy argument fhout being actively copied to var2. Therefore the value of module var2 is set and available to any routine (here the main program) that uses the module.
Edit: The solution is what I show for argument fhout and module variable var2. Call the dummy arguments ARG_varX and the module variables GBL_varX. Inside the subroutine use assignment statements to copy each ARG_varX to GBL_varX. Then any procedures that uses the module will have access to the variables GBL_varX, which will have the values they were sent into the subroutine. Does this solve your problem.
Edit 2: Here is a version of your new code. It seems to work. If there is a bug either I fixed it or it is outside of the code that you are showing:
MODULE var_repos
IMPLICIT NONE
PUBLIC :: compns_var_dump
! declare the variables which will go public here:
REAL, PUBLIC :: GBL_deltim, GBL_fhout
CONTAINS
SUBROUTINE compns_var_dump(ARG_deltim, ARG_fhout)
REAL, INTENT(in) :: ARG_deltim , ARG_fhout
GBL_deltim = ARG_deltim
GBL_fhout = ARG_fhout
WRITE(*,*)'Inside compns_var_dump:', ARG_deltim, GBL_deltim, GBL_fhout
END SUBROUTINE compns_var_dump
END MODULE var_repos
! ------------------------------------------------------------
module my_b
contains
subroutine compns ()
use var_repos, only: compns_var_dump
real :: deltim, fhout
deltim = 360.0
fhout = 6.0
write (*, *) "compns:", deltim, fhout
CALL compns_var_dump(deltim,fhout) ! calls the subroutine and passes the variables
end subroutine compns
end module my_b
! ------------------------------------------------------------
MODULE time_control
PUBLIC :: get_delta_time
CONTAINS
FUNCTION get_delta_time()
USE var_repos, ONLY: GBL_deltim
IMPLICIT NONE
real :: get_delta_time
REAL :: a_time ! Testing
get_delta_time = GBL_deltim
a_time = GBL_deltim ! Testing
WRITE(*,*)'Inside function get_delta_time(): deltim= ', GBL_deltim, get_delta_time, a_time
END FUNCTION get_delta_time
END MODULE time_control
! ------------------------------------------------------------
program main
use var_repos, only: GBL_deltim, GBL_fhout
use my_b, only: compns
use time_control, only: get_delta_time
real :: local_var
call compns ()
local_var = get_delta_time ()
write (*, *) "main:", local_var, GBL_deltim, GBL_fhout
end program main