I am often confronted with the question whether it is better to pass large arrays and subsets thereof as arguments or use modules instead. For example:
type( plant_type ), dimension( 5, 50000 ) :: plant
Note that one dimension is very long and the other much smaller.
The argument that is passed is only a subset of the array:
call plantgrowth( plant(:,icell) )
Instead of passing this argument , I could also call plantgrowth() without the argument by adding a
use md_biosphere, only: plant
inside the subroutine plantgrowth().
==> Which option works faster?
Here are the alternative Fortran examples:
Using arguments:
module md_biosphere
implicit none
private
public :: biosphere, plant_type ! only plant_type (not plant) is made public, but not instance of the type ('plant')
! the important aspect here is that ncells >> nplant
integer, parameter :: ncells = 500000
integer, parameter :: nplant = 50
type plant_type
real :: foliage
real :: roots
real :: wood
end type plant_type
type( plant_type ), dimension( nplant, ncells ) :: plant
contains
subroutine biosphere()
integer :: icell
do icell=1,ncells
call plantgrowth( plant(:,icell) )
end do
end subroutine biosphere
subroutine plantgrowth( plant )
type( plant_type ), dimension(nplant), intent(inout) :: plant
integer :: iplant
! more stuff here ...
do iplant=1,nplant
plant(iplant)%foliage = 1.0
plant(iplant)%roots = 1.0
plant(iplant)%wood = 1.0
end do
end subroutine plantgrowth
end module md_biosphere
!//////////////////////////////////////////////////////////
program example_module
use md_biosphere, only: biosphere
implicit none
call biosphere()
end program example_module
Using 'use module':
module md_biosphere
implicit none
private
public :: biosphere, plant ! instance 'plant' is made public and does not have to be passed as argument
! the important aspect here is that ncells >> nplant
integer, parameter :: ncells = 500000
integer, parameter :: nplant = 50
type plant_type
real :: foliage
real :: roots
real :: wood
end type plant_type
type( plant_type ), dimension( nplant, ncells ) :: plant
contains
subroutine biosphere()
integer :: icell
do icell=1,ncells
! no arguments are passed
call plantgrowth( icell )
end do
end subroutine biosphere
subroutine plantgrowth( icell )
integer, intent(in) :: icell
integer :: iplant
! more stuff here ...
do iplant=1,nplant
plant(iplant,icell)%foliage = 1.0
plant(iplant,icell)%roots = 1.0
plant(iplant,icell)%wood = 1.0
end do
end subroutine plantgrowth
end module md_biosphere
!//////////////////////////////////////////////////////////
program example_module
use md_biosphere, only: biosphere
implicit none
call biosphere()
end program example_module
Related
I have a memory leak when using a "constructor"(which is just a static function) to create an initialize a derived type object.
I have two subroutines that seem to be equivalent, but one of them has a memory leak (mult_leak), while the other hasn't (mult_noleak).
I don't understand which is the difference.
program main
use lala
type(mytype) :: mt1, mt2, mt3
integer :: i
real, allocatable, dimension(:,:) :: dat
allocate(dat(1000, 1000))
dat = 1.5
do i=1,10000000
mt1 = creador(dat)
mt2 = creador(dat)
mt3 = mult_leak(mt1, mt2)
if(modulo(i,1000)==0) then
print*, i, mt3%dat(1,1)
endif
end do
end program
module lala
type mytype
integer :: nx, ny
real, allocatable, dimension(:,:) :: dat
contains
private
procedure :: init
end type
interface creador
procedure p_creador
end interface
contains
subroutine init(this, dat)
class(mytype) :: this
real, allocatable, dimension(:,:) :: dat
integer, dimension(2) :: s
s = shape(dat)
this%nx = s(1)
this%ny = s(2)
allocate(this%dat, source=dat)
end subroutine
function p_creador(dat) result(res)
type(mytype), allocatable :: res
real, allocatable, dimension(:,:) :: dat
allocate(res)
call res%init(dat)
end function
function mult_noleak(cf1, cf2) result(cfres)
class(mytype), intent(in) :: cf1, cf2
class(mytype), allocatable :: cfres
real, dimension(:,:), allocatable :: aux
allocate(cfres)
aux=cf1%dat * cf2%dat
call cfres%init(aux)
end function
function mult_leak(cf1, cf2) result(cfres)
class(mytype), intent(in) :: cf1, cf2
class(mytype), allocatable :: cfres
real, dimension(:,:), allocatable :: aux
aux=cf1%dat * cf2%dat
cfres = creador(aux)
end function
end module
I have found that the leak disappears when I change the "cfres" definition in "mult_leak" subroutine.
function mult_leak(cf1, cf2) result(cfres)
class(mytype), intent(in) :: cf1, cf2
!class(mytype), allocatable :: cfres
type(mytype), allocatable :: cfres
real, dimension(:,:), allocatable :: aux
aux=cf1%dat * cf2%dat
cfres = creador(aux)
end function
It seems that the problem is the assignment of the constructor to a polymorphic entity.
I have written the following program in fortran90:
module constants
real*8, parameter :: zero = 0.0d0
real*8, parameter :: one = 1.0d0
real*8, parameter :: two = 2.0d0
end module constants
module common
use constants
real*8 :: a_A, a_B, e_A, e_B, b_A, b_B
end module common
module procedures
use constants
contains
function f(p)
use common
real*8, dimension(2), intent(in) :: p
real*8, dimension(2) :: f
f(1) = a_A*((two*p(1)/p(1) + p(2))**(-e_A)) - (exp(b_A*p(1)) - one)
f(2) = a_B*((two*p(2)/p(1) + p(2))**(-e_B)) - (exp(b_B*p(2)) - one)
f = f
return
end function f
subroutine wait
write(6,"('Waiting...')")
read *
return
end subroutine wait
end module procedures
program main
use constants
use common
use procedures
use toolbox
real*8, dimension(2) :: plow,phigh,pmin
a_A = one
a_B = one
e_A = one
e_B = one
b_A = 0.1d0
b_b = 0.1d0
plow(1) = 6.0d0
plow(2) = 6.0d0
phigh(1) = 8.0d0
phigh(2) = 8.0d0
pmin = one
call fminsearch(pmin,fret,plow,phigh,f)
write(6,"(3f15.8)") pmin(1),pmin(2), fret
end program main
When I run it I get the following error
call fminsearch(pmin, fret, plow, phigh, f)
1
Error: There is no specific subroutine for the generic ‘fminsearch’ at (1)
I think it is a problem with my function arguments, but could anybody give me some clarity as to what is going wrong? I am new to fortran and do not quite understand. As far as I can tell, I have declared the x with intent(in) and have declared the vector output of the function.
Thank you
I am learning correct use of subroutine, function and module, below is an simple example, no error occurs while compiling, after executing, the outcome is 4.57187637E-41 rather than pi, I have looked up several references, haven't found out the mistake yet.
module wz
implicit none
private
public :: print_out
real,parameter :: pi = 3.14159
contains
subroutine print_out
implicit none
real :: area
print *, area
end subroutine print_out
function f(x) result(area)
implicit none
real, intent(in):: x
real :: area
area = pi * x ** 2
end function f
end module wz
program test_module
use wz
implicit none
real :: x
x = 1.
call print_out
end program test_module
The value you are printing is area just after declaration and before doing anything with it. You need to pass x to f function, and you can do that via the print_out subroutine:
module wz
implicit none
private
public :: print_out
real,parameter :: pi = 3.14159
contains
subroutine print_out(x)
implicit none
real, intent(in) :: x
real :: area
area = f(x)
print *, area
end subroutine print_out
function f(x) result(area)
implicit none
real, intent(in):: x
real :: area
area = pi * x ** 2
end function f
end module wz
program test_module
use wz
implicit none
real :: x
x = 1.
call print_out(x)
end program test_module
I want to build a hash table module that can use different instances in Fortran 90 (I am not exactly sure what to name this). This is what I am trying to achieve:
module hashing
implicit none
private
real, dimension(:), allocatable :: &
real1
public :: &
Hash, hash_init, hash_push, hash_set, hash_get, &
hash_print
type Hash
character (len=128) :: &
data_type
integer :: &
array_size
end type Hash
contains
subroutine hash_init (this)
type(Hash), intent (in) :: &
this
if (trim(this%dtype) == "real1) then
call make_real1(this%array_size)
end if
end subroutine hash_init
subroutine hash_print (this)
type(Hash), intent (in) :: &
this
if (trim(this%dtype) == "real1) then
print *, real1
end if
end subroutine hash_print
end module hashing
So, in my test program, both call hash_print statements prints an array of size 5, because the real1 array is a general instance of module hashing, instead of (what I am trying to achieve) printing the first instance of size 10 and the second instance of size 5.
program test
use hashing
implicit none
type(Hash) :: &
inst1, inst2
inst1 = Hash("real1", 10)
inst2 = Hash("real1", 5)
call hash_init(inst1)
call hash_init(inst2)
call hash_print(inst1)
call hash_print(inst2)
end program test
The main idea is to add integer and character type dimensions as well and using and expanding on the if statements, but I need to know where in the module I need to declare these allocatable dimensions so that they are bound to a specific instance only. If possible! I've tried declaring the allocatable array inside the hash_init subroutine, but then it is not available for the hash_print subroutine.
I have a nested loop which is supposed to iterate over a an N-dim cube (line, square, cube,...)
So far I do this using nested loops, eg.
for 2D:
do i = 1,Ni
do j = 1,Nj
! do stuff with f(i,j)
enddo
enddo
or 3D:
do i = 1,Ni
do j = 1,Nj
do k = 1,Nk
! do stuff with f(i,j,k)
enddo
enddo
enddo
I would like to replace these nested loops with a construct that works for every possible N-dimensional case. How can I do this in Fortran? In C++ one can use iterators. Is there something like this in Fortran?
I am considering writing a class, which kind of works like a mechanical n-dimensional counter (i.e. if we count 3 per dimension):
0,0,0,0,0
1,0,0,0,0
2,0,0,0,0
0,1,0,0,0
1,1,0,0,0
Would this be somewhat fast, compared to nested for loops? Do you guys have better ideas?
This may be the easiest...
Also you will note that the index order for the array is reversed. Fortran in fastest in the left, so it is opposite of C.
When you call it it uses which ever one matches the array dimensions that you sent to it automagically, by checking the interface of the module.
MODULE Stuff
IMPLICIT NONE
PUBLIC MY$Work
INTERFACE MY$Work
MODULE PROCEDURE MY$Work1D, MY$Work2D, MY$Work3D
END INTERFACE
PRIVATE
CONTAINS
!~~~~~~~~~~
SUBROUTINE MY$Work1d(Array, F_of_Array)
REAL, DIMENSION(:), INTENT(IN ) :: Array
REAL, DIMENSION(:), INTENT(INOUT) :: F_of_Array
INTEGER :: I
!DIR$ DO SIMD
do i = LBOUND(Array,1) ,UBOUND(Array,1)
! do stuff with f(i)
enddo
RETURN
END SUBROUTINE MY$Work1d
!~~~~~~~~~~
SUBROUTINE MY$Work2d(Array, F_of_Array)
REAL, DIMENSION(:,:), INTENT(IN ) :: Array
REAL, DIMENSION(:,:), INTENT(INOUT) :: F_of_Array
INTEGER :: I,J
!DEC$ UNROLL_AND_JAM
do j = LBOUND(Array,2) ,UBOUND(Array,2)
do i = LBOUND(Array,1) ,UBOUND(Array,1)
! do stuff with f(i,j)
enddo
enddo
RETURN
END SUBROUTINE MY$Work2d
!~~~~~~~~~~
SUBROUTINE MY$Work3d(Array, F_of_Array)
REAL, DIMENSION(:,:,:), INTENT(IN ) :: Array
REAL, DIMENSION(:,:,:), INTENT(INOUT) :: F_of_Array
INTEGER :: I,J,K
!DEC$ UNROLL_AND_JAM
do k = LBOUND(Array,3) , UBOUND(Array,3)
do j = LBOUND(Array,2) ,UBOUND(Array,2)
do i = LBOUND(Array,1) ,UBOUND(Array,1)
! do stuff with f(i,j,k)
enddo
enddo
enddo
RETURN
END SUBROUTINE MY$Work3d(Array, F_of_Array)
END MODULE Stuff
PROGRAM XX
USE STUFF
...
So you could have a 3D array and do this
DO K = 1, nk
CALL MY$WORK(Array(:,:,k), ResultArray(:,:,K) )
ENDDO
That would use the 2D version and loop over it for all the K-cases...
I decided to use a super index. The size of my dimensions is limited to powers of two. This allows me to use bitshift operations and regular addition. Hopefully this is faster than a object-oriented counter.
pure function supI(j, k, n) result(vec)
implicit none
integer*8, intent(in) :: j
integer*8 :: copy
integer*4, intent(in) :: k, n
integer*4 :: vec(n), x, mask
! transforms super index j to a n-dimensional iteration,
! where each dimension has a range of 2^k
! mask to extract left most indicies
mask = ISHFT(1, k) -1
copy = j
do x = 1,n
! extract current value using mask
vec(x) = and(copy, mask)
! shift to next value
copy = ISHFT(copy, -k)
enddo
end function supI
A 3d counter with 4 in each dimension would look like this:
do j = 0,64
write(*,*) supI(j, 2, 3)
enddo
Edit: Compared to loop speed without optimization it is not to bad: ~20% more time, but If I use the optimizer it is much much slower than nested loops.