how to initialize a large array in Fortran? - arrays

I have a Fortran function in which I would like to initialize a large array at compile time. A simplified working example is below, where the parameter coeff in fill_coefficients has been reduced in size greatly.
How do I write similar code when coeff is large, without exceeding the maximum of 255 continuation lines, or the maximum of 132 characters per line? Here fill_coefficients should really be PURE, which probably makes it impossible to read coeff from a file once during runtime, and then store the result.
The file "main.f03":
PROGRAM main
USE coefficients
IMPLICIT NONE
REAL(dp), ALLOCATABLE, DIMENSION(:,:) :: matrix
CALL fill_coefficients(matrix,2)
PRINT *, "The first row of 'matrix':"
PRINT *, matrix(1,:)
END PROGRAM main
The file "coefficients.f03":
MODULE coefficients
USE iso_fortran_env
IMPLICIT NONE
INTEGER, PARAMETER :: dp = REAL64
CONTAINS
PURE SUBROUTINE fill_coefficients(my_coefficients, n)
IMPLICIT NONE
REAL(dp), ALLOCATABLE, DIMENSION(:,:), INTENT(OUT) :: my_coefficients
INTEGER, INTENT(IN) :: n
! The size of the following array would be roughly 200 x 200 = 40.000.
REAL(dp), DIMENSION(3,3), PARAMETER :: coeff = &
RESHAPE ( &
[ + 10.6770782520313112108115239655957106_dp, &
- 854.166260162504896864921917247656850_dp, &
- 85.4166260162504896864921917247656850_dp, &
+ 16250.5130995916556628551394756366716_dp, &
+ 6747.91345528378868523288314625648912_dp, &
+ 106.770782520313112108115239655957106_dp, &
- 123256.191341449456617608232658836883_dp, &
- 8328.12103658442274443298869316465429_dp, &
+ 500381.272281447399894682070647642979_dp ], &
[3,3] )
IF (ALLOCATED(my_coefficients)) DEALLOCATE(my_coefficients)
ALLOCATE(my_coefficients(n,n))
my_coefficients = coeff(1:n,1:n)
END SUBROUTINE fill_coefficients
END MODULE coefficients
The output:
The first row of 'matrix':
10.677078252031311 16250.513099591655

From a maintenance perspective (and as perhaps suggested in the comments), I would read the data into a module variable in a separate non-pure subroutine that is called once at program start-up. fill_coefficients then becomes a simple assignment from that module variable and can still be PURE.
MODULE coefficients
IMPLICIT NONE
...
! Could be PUBLIC, PROTECTED, then you could directly
! assign from it and dispense with fill_coefficients
! altogether.
REAL(dp), PRIVATE :: coeff(200,200)
CONTAINS
SUBROUTINE init
INTEGER :: unit
OPEN( NEWUNIT=unit, &
FILE='lots-of-numbers.bin', &
FORM='UNFORMATTED', &
! ACCESS='STREAM', & ! Maybe - depending on how you write it.
STATUS='OLD' )
READ (unit) coeff
CLOSE(unit)
END SUBROUTINE init
PURE SUBROUTINE fill_coefficients(my_coefficients, n)
! implicit none already in force due to the statement in
! the specification part of the host module.
! IMPLICIT NONE
REAL(dp), ALLOCATABLE, DIMENSION(:,:), INTENT(OUT) :: my_coefficients
INTEGER, INTENT(IN) :: n
! This test is redundant - my_coefficients is INTENT(OUT) so
! it must be not allocated at this point.
! IF (ALLOCATED(my_coefficients)) DEALLOCATE(my_coefficients)
! This allocate statement is redundant - allocation will
! happen automatically under F2003 with the assignment.
! ALLOCATE(my_coefficients(n,n))
my_coefficients = coeff(1:n,1:n)
END SUBROUTINE fill_coefficients
END MODULE coefficients
If you must have coeff as a compile time parameter, then assemble it in source manageable chunks - perhaps column by column. Your limits per declaration are line length (132) and number of continuation lines (255).
REAL(dp), PARAMETER :: column_1(200) = [ &
+ 10.6770782520313112108115239655957106_dp, &
- 854.166260162504896864921917247656850_dp, &
- 85.4166260162504896864921917247656850_dp, &
... ]
REAL(dp), PARAMETER :: column_2(200) = [ ... ]
...
REAL(dp), PARAMETER :: column_200(200) = [ ... ]
REAL(dp), PARAMETER :: coeff(200,200) = RESHAPE( [ &
column_1, column_2, ..., column_200 ], &
SHAPE=[200,200] )
Things declared with PARAMETER are named constants. Conceptually these only exist at compile time - depending on what you do with a named constant the compiler may or may not set aside storage in the executable image for the constants.
Large named constants may result in the compiler having issues compiling the file.

Related

How to initialize the size of an array in fortran module

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.

Can I pass a variable to a derived type such that each instance of my derived type could have arrays of different lengths?

What is the best way to organize 11 similar but varying size arrays in a program, without the allocatable property?
I'm imagining something like this:
TYPE MyType(i)
integer, intent(in) :: i
integer, dimension(i,i) :: A
integer, dimension(2*i,i) :: B
integer, dimension(i,2*i) :: C
end type MyType
Then in the main program I can declare something like this:
type(mytype), dimension(N) :: Array
Wherein the i'th element of 'Array' has access to three arrays A, B, and C and each of these three arrays have different sizes.
The problem I have currently is I am solving a QM problem and I have 11 different arrays that vary in size but all depend on the same parameter (as the size A, B and C all depend on i). The actually values of these arrays don't change either.
My program looks at different kinds of systems, each with their own A, B and C (just to keep the analogy going) and in each system A, B and C have a unique size.
If I knew I was looking at 6 different kinds of systems, I'd need 6 different copies of A, B and C.
Currently, A, B and C are not part of a derived type but instead are allocatable and recalculated at each iteration. This calculation takes upwards of a tenth of a second for the larger systems. But I average my results ~100,000 times which means this could offer some serious time savings. In addition, memory is not something I lack.
I tried calculating these arrays in another program and writing them to file and reading them when needed but unfortunately this was not faster than recalculating at the same time.
Note: Here is what my actual arrays look like:
integer, dimension(:,:), allocatable :: fock_states
integer, dimension(:,:), allocatable :: PES_down, PES_up
integer, dimension(:,:), allocatable :: IPES_down, IPES_up
integer, dimension(:,:), allocatable :: phase_PES_down, phase_PES_up
integer, dimension(:,:), allocatable :: phase_IPES_down, phase_IPES_up
integer, dimension(:,:), allocatable :: msize
integer, dimension(:,:), allocatable :: mblock
Each array is a different size for each system.
Edit:
So what I really need is N copies of the arrays in the list just above this edit. The arrays belonging to the i'th copy have size that scales with i (e.g. PES_down has dimension (i,4**i)). As I understand it, this means that I need N different declarations of variables with type 'MyType'. This would normally be ok but the issue is that N is defined at compile time but can change between runs.
N does have a defined maximum but it seems like a lot of wasted memory when I know I won't be using the arrays.
(Relate to this answer for a more detailed explanation).
As #roygvib said in his comment, yes, using parameterized derived types in this case is not only possible, it is a perfect match. This is one of the main problem-cases PDT aims to solve.
type :: MyType(i)
integer, len :: i
integer, dimension(i,i) :: A
integer, dimension(2*i,i) :: B
integer, dimension(i,2*i) :: C
! (...)
end type
Then, in the main program, you would declare your object like this (where i is the known length parameter for the current kind of system):
type(mytype(i)), dimension(N) :: Array
But first, check the availability of this feature in your compiler.
I guess it would be most straightforward to use a derived type containing A, B, and C with the size variable i and allocate them for each i using some initialization routine (here, MyType_init()).
module mytype_mod
implicit none
type MyType
integer :: i
integer, dimension(:,:), allocatable :: A, B, C
end type
contains
subroutine MyType_init( this, i )
type(MyType), intent(inout) :: this
integer, intent(in) :: i
allocate( this % A( i, i ), &
this % B( 2*i, i ), &
this % C( i, 2*i ) )
this % A = 0 !! initial values
this % B = 0
this % C = 0
end subroutine
end module
program main
use mytype_mod
implicit none
integer, parameter :: N = 2
type(MyType) :: array( N )
integer i
do i = 1, N
call MyType_init( array( i ), i )
array( i ) % A(:,:) = i * 10 !! dummy data for check
array( i ) % B(:,:) = i * 20
array( i ) % C(:,:) = i * 30
enddo
do i = 1, N
print *
print *, "i = ", i
print *, " A = ", array( i ) % A(:,:)
print *, " B = ", array( i ) % B(:,:)
print *, " C = ", array( i ) % C(:,:)
enddo
end program
Result (with gfortran 8.1):
i = 1
A = 10
B = 20 20
C = 30 30
i = 2
A = 20 20 20 20
B = 40 40 40 40 40 40 40 40
C = 60 60 60 60 60 60 60 60
The array(:) in the main program can be allocatable, such that
type(MyType), allocatable :: array(:)
N = 6
allocate( array( N ) )
which may be useful when N is read in from an input file. Further, we can create a type-bound procedure from MyType_init() by changing the lines with (*) below (to use an OO style).
module mytype_m
implicit none
type MyType
integer :: i
integer, dimension(:,:), allocatable :: A, B, C
contains
procedure :: init => MyType_init ! (*) a type-bound procedure
end type
contains
subroutine MyType_init( this, i )
class(MyType), intent(inout) :: this ! (*) use "class" instead of "type"
...
end subroutine
end module
program main
...
do i = 1, N
call array( i ) % init( i ) ! (*) use a type-bound procedure
...
enddo
...
end program

How to create array of arrays from all variables in a module

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

Fortran: Choosing the rank of an allocatable array

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.

Access violation error in passing array

Befor finding an answer for the quesion I asked before, I wrote very simple code for very sime mesh(there are just two triangles) as below to call C function in fortran. For the simplicity of code. Neither interface module here nor iso_c_binding(as possible as) isn't used here. It is very similar to this post, There is another error.
program METIS_NoInterface
implicit none
integer :: ne, nn
integer, dimension(:), allocatable :: eptr, eind
integer, pointer :: vwgt=>null(), vsize=>null()
integer :: nparts
real, pointer :: tpwgts=>null()
integer, dimension(0:39) :: opts
integer :: objval
integer, dimension(:), allocatable :: epart, npart
! Input => 2 tri: too small mesh
!ne = 2
!nn = 4
!nparts = 2
!allocate(eptr(0:2), eind(0:5))
!eptr=[0, 3, 4]
!eind=[0,1,3,1,2,3]
! Output
!allocate(epart(0:1), npart(0:4))
! Input => 4 quad : reasonable result
ne = 4
nn = 9
nparts = 2
allocate(eptr(0:ne), eind(0:15))
eptr=[0,4,8,12,16]
eind=[0,1,8,7,1,2,3,8,3,4,5,8,5,6,7,8]
! Output
allocate(epart(0:ne-1), npart(0:nn-1))
! METIS function call
call METIS_SetDefaultOptions(opts)
!call METIS_PartMeshNodal(ne,ne,eptr,eind,vwgt,vsize,nparts,tpwgts,& !<=syntax error
call METIS_PartMeshNodal(ne,nn,eptr,eind,vwgt,vsize,nparts,tpwgts,&
opts,objval,epart,npart)
! Result print
write(*,*) epart
write(*,*) ''
write(*,*) npart
write(*,*) ''
end program
It gives access violation error because of eind. It runs anyway if c_null_ptr are passed instead of eind. The size of array eind and number of elements are coincident. How can it be fixed? => resolved. Thank you!
The problem I had was systax error on calling METIS_PartMeshNodal, the 2nd argument was ne which is the same the 1st argument ne. It should have been nn.
Solution was to replace the 2nd argument to nn. Above code can be compiled and executed.
NOTE: Too small mesh may not have reasonable solution due to METIS' scheme.
Please refer another post in order to use interface module.

Resources