I have a number of variables declared in a module such as
module test
use othermod, only: n
integer, dimension(n) :: var0
real, dimension(n) :: var1
real, dimension(n) :: var2
.....
real, dimension(n) :: var1000
end module test
Then I have a subroutine that fills these variables with values.
At this point I would like to create an array of arrays with all the variables declared in module test so that I can easily copy or print all variables of a particular (n) at the same time, like dimension(n,allvariablesin module test). For example I would like to do something like array(3,:)=array(2,:).
Because this code is part of a very large program I cannot really modify too much, but rather I need to create an array of arrays from all the variables in this module without typing all the variables.
How can I easily integrate this change in the current code?
I urge you follow to #Vladimir F's advice and encapsulate your variables inside a derived data type. You can employ the associate construct to call old codes expecting var0, var1, .., etc. Lastly, we can overload the type's name to get a Java style constructor in the code below
module type_MyArray
implicit none
private
type, public :: MyArray
! type-components
real, dimension(:), allocatable :: var0, var1, var2
contains
! type-bound procedures
procedure :: create => create_my_array
procedure :: destroy => destroy_my_array
end type MyArray
interface MyArray
module procedure my_array_constructor
end interface MyArray
contains
pure function my_array_constructor(n) result (return_value)
! Dummy arguments
integer, intent (in) :: n
type (MyArray) :: return_value
call return_value%create(n)
end function my_array_constructor
pure subroutine create_my_array(self, n)
! Dummy arguments
class(MyArray), intent(in out) :: self
integer, intent(in) :: n
allocate( self%var0(n) )
allocate( self%var1(n) )
allocate( self%var2(n) )
end subroutine create_my_array
pure subroutine destroy_my_array(self)
! Dummy arguments
class(MyArray), intent(in out) :: self
if (allocated(self%var0)) deallocate( self%var0 )
if (allocated(self%var1)) deallocate( self%var1 )
if (allocated(self%var2)) deallocate( self%var2 )
end subroutine destroy_my_array
end module type_MyArray
program main
use type_MyArray, only: MyArray
use old_code, only: do_something
implicit none
type (MyArray) :: foo, bar
! Allocate memory
foo = MyArray(42)
bar = MyArray(4200)
associate( var0 => foo%var0, var1 => bar%var1 )
! Call old code using var0 and var1
call do_something(var0, var1)
end associate
! Release memory
call foo%destroy()
call bar%destroy()
end program main
Related
I'm trying to create my own module of array (just for practicing).
I wrote this module.
module myArray
implicit none
public set, get
integer :: size = 5
integer, allocatable :: array(:)
allocate(array(size))
contains
subroutine set(index, value)
implicit none
integer, intent(in) :: index
integer, intent(in) :: value
array(index) = value
end subroutine set
function get(index) result(value)
implicit none
integer, intent(in) :: index
integer :: value
value = array(index)
end function get
end module myArray
But I get this error
mymod.f90:8:25:
8 | allocate(array(size))
| 1
Error: Unexpected ALLOCATE statement in MODULE at (1)
What should I do to correct this?
The idea is right but you're placing global data in a module, it won't be usable in the real world. You want to define a class for your array type.
In fortran, you define a derived type binding both data and methods:
module myArrays
implicit none
type, public :: myArray
! Data here
integer :: n = 0
integer, allocatable :: array(:)
contains
! Type-bound procedures here
procedure :: set
procedure :: get
procedure :: new
end type myArray
contains
elemental subroutine set(this,index,value)
class(myArray), intent(inout) :: this
integer, intent(in) :: index,value
if (index>0 .and. index<=this%n) this%array(index) = value
end subroutine set
elemental integer function get(this, index) result(value)
class(myArray), intent(in) :: this
integer, intent(in) :: index
if (index>0 .and. index<=this%n) then
value = this%array(index)
else
value = -huge(value) ! return an "invalid" value
endif
end function get
elemental subroutine new(this, size)
class(myArray), intent(inout) :: this
integer, intent(in) :: size
integer :: ierr
! This should go in a destructor routine
this%n=0
deallocate(this%array,stat=ierr)
! Now allocate
if (size>0) then
this%n=size
allocate(this%array(Size),stat=ierr)
endif
end subroutine new
end module myArrays
Note that you'll have to call the initializer routine new before using your array. Here's a program example:
program test_myArray
use myArrays
implicit none
type(myArray) :: A,B
call A%new(size=10)
call B%new(size=20)
print *, 'A size=',A%n,' B size=',B%n
end program
You cannot put executable statements directly into a module. You have to create a subroutine, put the statements into the subroutine and then call the subroutine in some way.
I am trying to write a program where I want the allocatable array A to be of either rank 1, 2, or 3, depending on my input at run-time. I want to do this since the subsequent operations on A are similar, and I have defined in a module an interface work with module procedures that when acted on A, gives the desired result.
What I am doing currently is this:
program main
implicit none
integer :: rank,n=10
real*8, allocatable :: A1(:)
real*8, allocatable :: A2(:,:)
read (*,*) rank
if (rank.eq.1) then
allocate (A1(n))
else if (rank.eq.2) then
allocate (A2(n,n))
end if
! operate on the array
if (rank.eq.1) then
call work(A1)
else if (rank.eq.2) then
call work(A2)
end if
end program
Things would be much easier if somehow I could choose the rank of A, as then the if statements are not needed. Maybe this is not possible, but all help are appreciated.
The next Fortran standard (2015) has the select rank construct similar to select case. My example uses the select case construct on the rank intrinsic of an assumed-rank dummy variable.
module my_type
use, intrinsic :: iso_fortran_env, &
ip => INT32, &
wp => REAL64
implicit none
private
public :: MyType
type MyType
real (wp) :: rank0
real (wp), allocatable :: rank1(:)
real (wp), allocatable :: rank2(:,:)
real (wp), allocatable :: rank3(:,:,:)
contains
procedure :: create => create_my_type
procedure :: destroy => destroy_my_type
end type MyType
contains
subroutine create_my_type(this, array)
! calling arguments
class (MyType), intent (in out) :: this
real (wp), intent (in) :: array(..) !! Assumed-rank dummy variable
! local variables
integer (ip), allocatable :: r(:)
select case(rank(array))
case (0)
return
case (1)
r = shape(array)
allocate( this%rank1(r(1)) )
case (2)
r = shape(array)
allocate( this%rank2(r(1), r(2)) )
case (3)
r = shape(array)
allocate( this%rank3(r(1), r(2), r(3)) )
case default
error stop 'array must have rank 0,1,2, or 3'
end select
! Release memory
if (allocated(r)) deallocate( r )
end subroutine create_my_type
subroutine destroy_my_type(this)
! calling arguments
class (MyType), intent (in out) :: this
if (allocated(this%rank1)) deallocate( this%rank1 )
if (allocated(this%rank2)) deallocate( this%rank2 )
if (allocated(this%rank3)) deallocate( this%rank3 )
end subroutine destroy_my_type
end module my_type
program main
use, intrinsic :: iso_fortran_env, only: &
ip => INT32, &
wp => REAL64
use my_type, only: &
MyType
implicit none
type (MyType) :: foo
real (wp) :: a0, a1(42), a2(42,42), a3(42,42,42)
print *, rank(a0)
print *, rank(a1)
print *, rank(a2)
print *, rank(a3)
! Allocate array of rank 3
call foo%create(a3)
print *, rank(foo%rank3)
print *, shape(foo%rank3)
print *, size(foo%rank3)
! Release memory
call foo%destroy()
end program main
Declare the array to be rank three. If a lower rank array is required, allocate the relevant trailing dimensions to be of size one.
real, allocatable :: array(:,:,:)
...
select case (desired_rank)
case (1) ; allocate(array(n,1,1))
case (2) ; allocate(array(n,n,1))
case (3) ; allocate(array(n,n,n))
case default ; error stop 'bad desired rank'
end select
You can then use an array section to get a contiguous slice of array that is consistent with your desired rank. Alternatively, write the relevant procedures that operate on array to take a rank three argument, and make them aware of the meaning of a size one extent for the higher dimensions.
I'm attempting to create global-ish-ly available allocatable array of a set of derived types that share inheritance with a single object. Fortran does not seem to make this very easy. The below is what I have so far.
First the derived types and module with the allocatable array.
Module Environment
use Entity_M
type(Entity_C), dimenion(:), allocatable :: objects
End Module Environment
Module Entity_M
type Entity_T
integer :: id
real*8 :: time
real*8, dimension(3) :: currPos
type(TrajectoryDatum), dimension(:), allocatable :: trajTable
end type Entity_T
type Entity_C
class(Entity_T), pointer :: e
end type Entity_C
type, extends(Entity_T) :: Aircraft_T
real*8 :: altitude
end type Aircraft_T
type, extends(Entity_T) :: Missile_T
integer :: targetID
end type Missile_T
End Module Entity
Now the main program
Program Main
use Initialization
use Environment
use Entity_M
call simInit(3)
write(*,*) objects%trajTable !<---- this does not persist
call runSim()
End Program Main
The code with the issue
Module Initialization
use Entity_M
contains
subroutine simInit(numOfObjects)
integer, intent(in) :: numOfObjects
call objectsInit(numOfObjects)
call launchersInit()
end subroutine simInit
subroutine objectsInit(numOfObjects)
use Environment
integer, intent(in) :: numOfObjects
!local
type(Aircraft_T) :: aircraft
integer :: i
allocate(objects(numOfObjects)
do i = 1, numOfObjects
aircraft%trajTable = getTrajectoryData()
call allocatePointer(objects(i)%e, aircraft)
end do
end subroutine objectsInit
subroutine allocatePointer(c, t)
class(Entity), pointer, intent(out) :: c
type(Aircraft), target, intent(in) :: t
c => t
end subroutine allocatePointer
End Module Initialization
This above just example code written on a computer that doesn't have a compiler. I did my best and hopefully if there are typos they are few. I did my best to mirror the structure of the original code.
The problem is that the field "objects%trajTable" goes back to a undefined pointer after it leaves the "objectsInit" subroutine. The other values like time, id, and currPos are still correct. How can I correct this?
I am using Visual Studio 2012 and Intel Visual Fortran 2015.
Because the program has many overlapping names (like Aircraft and aircraft, which are regarded as the same in Fortran), I have attached "_t" to all the types (e.g., Aircraft to Aircraft_t etc) and "_m" to all the module names (e.g., Entity to Entity_m) to make the program work (at least formally).
More importantly, as #innoSPC commented above, type(Aircraft) :: aircraft is a local variable, so I think a pointer associated to it becomes undefined after exiting objectsInit(). The code works if
call allocatePointer( objects( i )% e, aircraft )
is replaced by
allocate( objects( i )% e, source=aircraft )
so that each objects( i )% e is given an independent memory having the type of Aircraft_t, with the contents of aircraft copied to it.
Edit Here is a minimum example that I used for test.
Module Entity_m
implicit none
type Entity_t !! base type
integer :: trajTable( 2 )
endtype
type, extends(Entity_t) :: Aircraft_t
real*8 :: altitude
endtype
type, extends(Entity_t) :: Missile_t !! dangerous...
integer :: targetID
endtype
type Entity_c !! container type
class(Entity_t), pointer :: e
endtype
type(Entity_c), allocatable :: objects(:)
contains
subroutine objectsInit( numObj )
integer :: numObj
!local
type(Aircraft_t) :: aircraft
type(Missile_t) :: missile
integer :: i
allocate( objects( numObj ) )
do i = 1, numObj
if ( mod( i, 2 ) == 1 ) then
aircraft% trajTable(:) = i
aircraft% altitude = 10.0d0 * i
allocate( objects( i )% e, source= aircraft )
else
missile% trajTable(:) = 10000 * i
missile% targetID = -100 * i
allocate( objects( i )% e, source= missile ) !! missile loaded !!
endif
enddo
endsubroutine
EndModule
Program Main
use Entity_m
call objectsInit( 3 )
do i = 1, 3
print *, objects( i )% e% trajTable(:) !! access members of base type
select type ( t => objects( i )% e ) !! access members of derived type
type is ( Aircraft_t ) ; print *, t% altitude
type is ( Missile_t ) ; print *, t% targetID
endselect
enddo
EndProgram
I am trying to write a subroutine to access arrays in a certain manner.
One input argument of the subroutine is a character containing the name of the array whose access is desired. Here is a rather simplified code example of how I generally imagine this to work:
PROGAM prog
real, dimension(3,3) :: array1(3,3)
real, dimension(3,3) :: array2(3,3)
real value1
real value2
... fill 'array1' and 'array2'...
call sub(array1,2,2,value1)
call sub(array2,2,2,value2)
... do something with 'value1' and 'value2'...
END
SUBROUTINE sub(name,x,y,out)
character(len=*), intent(in) :: name
integer, intent(in) :: x
integer, intent(in) :: y
real, intent(out) :: out
out = name(x,y)
RETURN
END
What I want is the subroutine to access array1(2,2) as requested in the argument and return this value to value1. Then access array2(2,2) and return this value to value2. Above code snippet does not work - no wonder about that. How do I get name(x,y) replaced with array1(x,y) respectively array2(x,y)?
Thanks a lot and best regards!
It is not necessary to use the name of the array to get at its content, if the array is declared in caller. You then use normal argument association.
You could write your subroutine as
subroutine sub(a,x,y,out)
integer, intent(in) :: x, y
real, intent(out) :: out
real, dimension(:,:), intent(in) :: a
out = a(x,y)
end subroutine sub
Much simpler would be to do, in the main program
value1 = array1(2,2)
value2 = array2(2,2)
I would recommend a textbook on Fortran, or even a look at http://en.wikipedia.org/wiki/Fortran_95_language_features .
Edit:
You could also use a SELECT CASE statement, like this:
subroutine bar(c, i, j, out)
character(len=*), intent(in) :: c
integer, intent(in) :: i,j
real, intent(out) :: out
select case (trim(c))
case ("array1")
out = array1(i,j)
case ("array2")
out = array2(i,j)
case default
stop "Argument is bletchful"
end case
end subroutine bar
I am simply at a loss to understand what good this would do, as opposed to using the array directly.
Is this what you want?
select case (array_name)
case ("array_dog")
array_ptr => array_dog
case ("array_cat")
array_ptr => array_cat
end select
where array_name is a character variable, array_dog and array_cat are arrays declared in the module (needing target attribute), and array_ptr is another array with the pointer attribute.
UPDATE: My modified code looks like this:
program run_module_test
use module_test
implicit none
TYPE(newXYZ), allocatable, intent(inout) :: xyzArray(:)
call update(xyzArray)
write(6,*)'xyzArray',xyzArray
end program run_module_test
module module_test
implicit none
TYPE :: newXYZ
real(4) :: x, u
real(4) :: y, v
real(4) :: z, w
real(4),dimension(3) :: uvw
END TYPE
integer(4) :: shape = 3
contains
subroutine update(xyzArray)
integer(4) :: i
TYPE(newXYZ), allocatable, intent(inout) :: xyzArray(:)
allocate( xyzArray(shape) )
do i = 1, shape
xyzArray(i)%x = 0
xyzArray(i)%y = 0
xyzArray(i)%z = 0
xyzArray(i)%u = 0
xyzArray(i)%v = 0
xyzArray(i)%w = 0
xyzArray(i)%uvw = (/0,0,0/)
end do
return
end subroutine update
end module module_test
When they are compiled, they generate a similar error:
TYPE(newXYZ), allocatable, intent(inout) :: xyzArray(:)
1
Error: ALLOCATABLE attribute conflicts with DUMMY attribute at (1)
When I eliminate the argument in update() subroutine, I receive a contradictory error:
TYPE(newXYZ), allocatable, intent(inout) :: xyzArray(:)
1
Error: Symbol at (1) is not a DUMMY variable
Have I eliminated the sources of error pointed out in the helpful suggestions? Could this be a compiler related error (using mpi90)?
~~~First Edit~~~
I have a subroutine whose input argument is an array of user defined type XYZ. I wish to deallocate xyzArray and allocate/modify it to a different size in the body of the subroutine. I attempted the method suggested by changing array dimensions in fortran, but when I do the following:
subroutine update(xyzArray, ...)
...
TYPE (XYZ), allocatable :: xyzArray(:)
I receive an error message:
Error: ALLOCATABLE attribute conflicts with DUMMY attribute at (1)
When I try:
subroutine update(xyzArray, ...)
...
deallocate( xyzArray(myshape) )
allocate( xyzArray(newshape) )
I receive error messages:
Error: Expression in DEALLOCATE statement at (1) must be ALLOCATABLE or a POINTER
Error: Expression in ALLOCATE statement at (1) must be ALLOCATABLE or a POINTER
What do I need to do to change the size of the array in the subroutine?
To do this:
The dummy argument must be allocatable. Allocatable dummy arguments require a compiler that implements the relevant part of the Fortran 2003 standard (or a Fortran 95 compiler that implements the so called "allocatable" TR).
An explicit interface to the procedure is required (the procedure must be a module procedure, an internal procedure or have an interface block in the calling scope).
The dummy argument must not be intent(in). If you are not using the allocation status or other aspects of the value of the dummy argument at all in the subroutine then intent(out) may be appropriate (if allocated beforehand the dummy argument will be automatically deallocated when the procedure is called), otherwise intent(inout) or no intent.
(Your second block of example code has a syntax error with the deallocate statement - you should simply specify the xyzArray variable, leave off the (myshape) shape specification))
For example, in a module:
subroutine update(xyzArray)
type(xyz), allocatable, intent(inout) :: xyzArray(:)
...
if (allocated(xyzArray)) deallocate(xyzArray)
allocate(xyzArray(newshape))
...
If you are sure, you want to deallocate the array in your subroutine, you can declare the dummy argument being intent(out), so that it is deallocated automatically when the subroutine is entered:
module whatever
implicit none
type :: xyz
:
end type xyz
contains
subroutine update(xyzArray)
type(xyz), allocatable, intent(out) :: xyzArray(:)
:
allocate(xyzArray(someshape))
:
end subroutine update
end module whatever
As already noted by IanH, the process must have an explicit interface (e.g. being enclosed in a module) and in the caller program you must declare the actual argument allocatable:
program test
use whatever
implicit none
type(xyz), allocatable :: array(:)
:
call update(array)
:
end program test