Passing allocatable array into a subroutine - io

I am working on a f90 code that I didn't write. I am not a very experienced fortran user.
There is a part that bothers me a bit and I am not sure if it is a normal practice. Can someone help me to clarify a bit please ?
I have a structure with an allocatable array defined in a module.
This variable is passed un-allocated to a subroutine.
The subroutine then allocates its corresponding local variable.
In the main, the output of the passed structure variable is allocated.
What I am not sure I understand is how the main program is handling the size of the return variable as it is not defined before. Is it a common practice?
Personally, I would have think that a variable with a defined size should have been passed to the subroutine.
If I resume coarsly the code:
modulus_mod.f90:
module modulus_mod
public :: mod
type mod
real,allocatable,dimension(:,:) :: rec
end type mod
end module modulus_mod
subtoto.f90:
subroutine subtoto(recloc)
implicit none
real,allocatable,dimension(:,:) :: recloc
WRITE(*,*) 'in'
ALLOCATE( recloc(10,10) )
WRITE(*,*) 'inout'
recloc(:,:)=1.
WRITE(*,*) 'out'
endsubroutine subtoto
toto.f90:
program toto
use modulus_mod
implicit none
type(mod) :: model
!>>> Here not allocated if tested
if(allocated(model%rec)) WRITE(*,*) 'allocated bf'
if(.NOT.allocated(model%rec)) WRITE(*,*) 'NOT allocated bf'
CALL subtoto(model%rec)
WRITE(*,*) 'out sub'
!>>>Here it should be allocated correctly if tested
if(allocated(model%rec)) WRITE(*,*) 'allocated af'
if(.NOT.allocated(model%rec)) WRITE(*,*) 'NOT allocated af'
end program toto

Trying to make a hands-on summary here:
Variable is an allocatable dummy argument in a subroutine, for example:
subroutine sub(variable)
real, allocatable, [intent(...)] :: variable(:)
here, status is controlled by the intent keyword:
intent(out): initially not allocated, when the subroutine is called
intent(in), intent(inout) or [no intent declared]: initial allocation status depends on what the variable was like outside of this routine
At any times, you can check both the status and the size of this variable by
allocated(variable) and size(variable).
Note that size is undefined if the variable is not allocated, so you may want to use something like:
safe_size = merge(size(variable),-1,allocated(variable))
to prevent the undefined behavior.
Variable is in a module: it is saved. Its status is not allocated at the beginning of the program; both status and contents can change during runtime (for example being changed by a subroutine or function, with the rules of 1.), but nothing is modified between these calls.
In your case, if you want your variable to always be allocated by the routine, you should specify an intent(out), like:
subroutine subtoto(recloc)
implicit none
real,allocatable,dimension(:,:), intent(out) :: recloc
[...]
end subroutine subtoto

Related

Binary Search algorithm random array

I don't understand why the recursive function always gives me zero result, even if I put values inside the array.
it seems that size (a) == 0
recursive function binarySearch_R (a, value) result (bsresult)
real, intent(in) :: a(6), value
integer :: bsresult, mid
mid = size(a)/2 + 1
if (size(a) == 0) then
bsresult = 0 ! not found
else if (a(mid) > value) then
bsresult= binarySearch_R(a(:mid-1), value)
else if (a(mid) < value) then
bsresult = binarySearch_R(a(mid+1:), value)
if (bsresult /= 0) then
bsresult = mid + bsresult
end if
else
bsresult = mid ! SUCCESS!!
end if
end function binarySearch_R
program hji
read*, a
read*, value
print*, binarySearch_R
end program hji
Chapter 1: The dangers of implicit typing
The first thing I strongly recommend you do is to include the line
implicit none
after the program line. This will suppress implicit typing, and the resulting errors will give you some useful insight into what is happening.
If you did that, you'd get an error message:
$ gfortran -o binsearch binsearch.f90
binsearch.f90:23:12:
read*, a
1
Error: Symbol ‘a’ at (1) has no IMPLICIT type
binsearch.f90:27:25:
print*,binarySearch_R
1
Error: Symbol ‘binarysearch_r’ at (1) has no IMPLICIT type
binsearch.f90:24:16:
read*, value
1
Error: Symbol ‘value’ at (1) has no IMPLICIT type
It doesn't matter that a, value, and binarySearch_R were defined in the function. As the function is not part of the program block, the program doesn't know what these are.
With implicit typing active, it simply assumed that all three are simple real variables. (The type depends on the first letter of the variable name, i through n are integer, everything else is real)
Because this implicit typing can so easily hide coding errors, it's strongly, strongly suggested to always switch it off.
Which also means that we have to declare the variables a and value in the program:
program hji
implicit none
real :: a(6), value
...
end program hji
Chapter 2: How to introduce a function to the program?
So how does the program get access to the function? There are four ways:
The best way: Use a module
module mod_binsearch
implicit none
contains
recursive function binarySearch_R (a, value) result (bsresult)
...
end function binarySearch_R
end module mod_binsearch
program hji
use mod_binsearch
implicit none
real :: a(6), value
...
end program hji
Note that the use statement has to be before the implicit none.
This method leaves the function separate, but callable.
It automatically checks that the parameters (that's something we'll be coming to in a bit) are correct.
Have the function contained in the program.
Between the final line of code of the program and the end program statement, add the keyword contains, followed by the function code (everything from recursive function ... to end function ...).
This is the quick-and-dirty method. You have to be careful with this method as the function will automatically have access to the program's variables unless there's a new variable with that name declared inside the function.
The convoluted way: Interfaces
Create an interface block in the declaration section of your program's source code,
and repeat the interface information in there.
This still allows the compiler to check whether the function is invoked correctly, but it's up to you to ensure that this interface block is correct and matches the actual implementation.
The really, really ugly way: Declare it like a variable, invoke it like a function.
Please don't do that.
Chapter 3: Calling a function
When you call a function, you have to use the parentheses and give it all the parameters that it expects. In your case, you need to type
print *, binarySearch_r(a, value)
Chapter 4: Dynamic arrays as dummy parameters
In the successive recursive calls to the function, the array gets smaller and smaller.
But the dummy parameter is always the same size (6). Not only will this interfere with your algorithm, but this can also lead to dangerously undefined memory access.
Fortunately, specially for intent(in) dummy parameters, you can use dynamic arrays:
recursive function binarySearch_R(a, value)
real, intent(in) :: a(:), value
The single colon tells the compiler to expect a one-dimensional array, but not the length of it. Since you're already using size(a), it should automatically work.
Too long for a comment, but not an answer (and to any Fortran experts reading this, yes, there are one or two places where I gloss over some details because I think they are unimportant at this stage) ...
The way the code is written does not allow the compiler to help you. As far as the compiler is concerned there is no connection between the function and the program. As far as the program is concerned a is, because you haven't told the compiler otherwise, assumed to be a real scalar value. The a in the program is not the same thing as the a in the function - there is no connection between the function and the program.
The same is true for value.
The same is true for binarysearch_r - and if you don't believe this delete the function definition from the source code and recompile the program.
So, what must you do to fix the code ?
First step: modify your source code so that it looks like this:
program hji
... program code goes here ...
contains
recursive function binarySearch_R (a, value) result (bsresult)
... function code goes here ...
end function binarySearch_R
end program hji
This first step allows the compiler to see the connection between the program and the function.
Second step: insert the line implicit none immediately after the line program hji. This second step allows the compiler to spot any errors you make with the types (real or integer, etc) and ranks (scalar, array, etc) of the variables you declare.
Third step: recompile and start dealing with the errors the compiler identifies. One of them will be that you do not pass the arguments to the function so the line
print*, binarySearch_R
in the program will have to change to
print*, binarySearch_R(a, value)

Testing for memory leak in Fortran (using pFUnit)

I've wrote my first program using allocatable. It works as expected. But, does it really? And more importantly, how can I set up a unit-test to catch memory leaks?
The idea behind the program is to allocate a chunck of storage room for my list of objects in the first place. And every time I add one more element more to the list than the allocated size, I double the allocation. I do this to reduce the number of allocations and subsequent copying of data from the old allocated memory, to the newly allocated memory.
I might over complicate this, but I'd like to spend some time now understanding the pitfalls, rather than falling head first into them a year or two down into the project.
The ode is compiled with gfortran 8.3.0 on linux. And using pFUnit 4.1. The code below is an extract to test only the allocation part.
Heres my test-program:
program test_realloc
use class_test_malloc
integer :: i
real :: x, y
type(tmalloc) :: myobject
call myobject%initialize()
do i=1, 100
x = i * i
y = sqrt(x)
call myobject%add_nbcell(i, x, y)
end do
call myobject%dump()
end program test_realloc
array_reallocation.f:
!
! Simple test to see if my understanding of dynamicly allocation
! of arrays is correct.
!
module class_test_malloc
use testinglistobj
implicit none
type tmalloc
integer :: numnbcells, maxnbcells
type(listobj), allocatable :: nbcells(:)
contains
procedure, public :: initialize => init
procedure, public :: add_nbcell ! Might be private?
procedure, private :: expand_nbcells
procedure, public :: dump
end type tmalloc
contains
subroutine init(this)
class(tmalloc), intent(inout) :: this
this%numnbcells = 0
this%maxnbcells = 4
allocate (this%nbcells(this%maxnbcells))
end subroutine init
subroutine add_nbcell(this, idx, x, y)
class(tmalloc), intent(inout) :: this
integer, intent(in) :: idx
real, intent(in) :: x, y
type(listobj) :: nbcell
if(this%numnbcells .eq. this%maxnbcells) then
call this%expand_nbcells()
print *,"Expanding"
endif
this%numnbcells = this%numnbcells + 1
nbcell%idx = idx
nbcell%x = x
nbcell%y = y
this%nbcells(this%numnbcells) = nbcell
print *,"Adding"
end subroutine add_nbcell
subroutine expand_nbcells(this)
class(tmalloc), intent(inout) :: this
type(listobj), allocatable :: tmpnbcells(:)
integer :: size
size = this%maxnbcells *2
allocate (tmpnbcells(size))
tmpnbcells(1:this%maxnbcells) = this%nbcells
call move_alloc( from=tmpnbcells, to=this%nbcells)
this%maxnbcells = size
end subroutine
subroutine dump(this)
class(tmalloc), intent(inout) :: this
integer :: i
do i=1, this%numnbcells
print*, this%nbcells(i)%x, this%nbcells(i)%y
end do
end subroutine
end module
listobj.f:
module testinglistobj
type listobj
integer :: idx
real :: x
real :: y
end type
end module testinglistobj
You will not get any memory leaks with this code. The reason is, and this is fundamental to the understanding of allocatable arrays, is that in Fortran 95 onwards it is required that allocatable arrays without the save attribute automatically get deallocated when they go out of scope. The nett result of this is that memory leaks for such arrays are impossible. This is one very good reason why you should prefer allocatable arrays to pointers. Related is the general software engineering principle of keeping the scope of variables as limited as possible, so that arrays are in memory for as short a period as possible.
Note this does not mean that you should never deallocate them as an array may remain in scope long after it is actually useful. Here "manual" deallocation may be of use. But it is not a memory leak.

Private and public variables inside a module and a a subroutine in OpenMP

I am trying to parallelize a fairly complicated simulation code used for oil field calculations.
My question is, if I can declare a variable or some allocatable arrays and a few subroutines in a module, then use this module in another module/subroutine which contains the parallel region, will those variables and arrays be considered private to each thread (i.e. they will have separate copies of those variables and changes made to a variables in a thread won't be seen by other threads) or they'll be shared?
Like this:
module m2
implicit none
integer :: global
contains
subroutine s2()
implicit none
integer :: tid
tid = ! Some calculation
end subroutine s2
end module m2
module m1
use m2
implicit none
contains
subroutine s1
!$omp parallel do
do i=1, 9
call s2()
end do
!$omp end parallel do
end subroutine s1
end module m1
Will tid and global be private or shared?
Any help is greatly appreciated!
Module variables are always shared in OpenMP unless you use the threadprivate directive. See Difference between OpenMP threadprivate and private for detailed description of threadprivate. So global will be shared.
The local variable tid is declared in the subroutine and called from the parallel region. Therefore it will be private unless it has the save attribute.
(Note that initialization like integer :: tid = 0 also adds the save implicitly, so be careful.)

Fortran: Initialize character string with unknown length in main program

I have a character string I would like to initialize with intent(out) data from a subroutine call. I kind of looks like that:
character(*) :: path
call get_path(path)
The compiler tells me:
Error: Entity with assumed character length at (1) must be a dummy
argument or a PARAMETER
The construct works just fine in a subroutine but fails in the main program. Is it possible to initialize the path variable without knowing its length?
EDIT: Stuff I already tried but failed.
character(99) :: path_temp
character(:), allocatable :: path
call get_path(path_temp)
allocate(path(len(trim(path_temp))))
Error: Shape specification for allocatable scalar at (1)
I don't get why the compiler thinks path is a scalar.
A function that returns a character with assumed length apparently is illegal.
character(*) function get_path ()
get_path = '/path/to/folder/'
end function get_path
Error: Character-valued module procedure 'get_path' at (1) must not be assumed length
What works but gives me a headache because I find it very bad style is to give path an insane length and trim it every time it's used. I think my compiler is having trouble with allocatable character strings because it isn't quite up to date (mpif90). Not sure if it supports them.
Many of the points are covered in other answers linked by comments, such as what "assumed length" requires and how to allocate the scalar deferred length character variable.
I'll point out two things, before coming to an particular answer.
Intrinsic assignment to a deferred length allocatable scalar character variable results in (if required) allocation of that variable to the length of the expression. That is
character(99) :: path_temp
character(:), allocatable :: path
call get_path(path_temp)
allocate(character(len(trim(path_temp))) :: path) ! Note the correct form
path = TRIM(path_temp) ! Or path(:)=path_temp
can be replaced by
character(99) :: path_temp
character(:), allocatable :: path
call get_path(path_temp)
path = TRIM(path_temp)
The other thing to note is quite pedantic, but using the terminology incorrectly may hinder searching. Initialization in Fortran means something specific which isn't applicable here.
You say that a function with an assumed length character result is apparently illegal, based on the compiler error message
Error: Character-valued module procedure 'get_path' at (1) must not be assumed length
That isn't entirely true: character function results can (currently - it's an obsolescent feature of modern Fortran) be of assumed length in some circumstances. They must, though, be external functions. See that the compiler complains about a module procedure (which then isn't external).
That said, having an assumed length character result doesn't help you. The length of the result still has to be assumed from something, and that something isn't in the function body, but a declaration that defines the external function in a scope.
Something like
implicit none
character(99) get_path ! The length of the get_path result is assumed from here
character(:), allocatable :: path
path = TRIM(get_path())
...
As you seem to have complete control over the subroutine get_path, there's the final answer-worthy comment to make. You could directly have the argument allocatable.
subroutine get_path(path)
character(:), allocatable, intent(out) :: path
path = '/path/to/folder/' ! Allocation through intrinsic assignment
path = TRIM(path) ! In general, if it's likely to have trailing blanks
end subroutine

To host functions within a threaded subroutine

I encountered a problem when I port my Fortran project to OpenMP. In my original code, there are two functions named add and mpy being passed to a threaded subroutine submodel that throws respective function into another subroutine defined in a module toolbox.
Now, for my new code, I am wondering whether there is a way to produce exactly the same outcome as with my original code but with a tiny twist that moves the two functions add and mpy to be hosted (i.e., contained) within the subroutine submodel.
Thanks.
Lee
--- My original code consists of four files: MAIN.F90, MODEL.F90, VARIABLE.F90, and TOOLBOX.F90
OUTPUT:
--- addition ---
3 7 11 15
--- multiplication ---
2 12 30 56
Press any key to continue . . .
MAIN.F90
program main
use model
implicit none
call sandbox()
end program main
MODEL.F90
module model
use omp_lib
use variable
implicit none
contains
subroutine submodel(func,x,y)
implicit none
interface
function func(z)
implicit none
integer :: z,func
end function func
end interface
integer :: x,y
call tool(func,x,y)
end subroutine submodel
function add(a)
implicit none
integer :: a,add
add=a+thread_private
end function add
function mpy(m)
implicit none
integer :: m,mpy
mpy=m*thread_private
end function mpy
subroutine sandbox()
implicit none
integer :: a(4),b(4),c(4),i
a=[((i),i=1,7,2)]
b=[((i),i=2,8,2)]
!$omp parallel do
do i=1,4
thread_private=b(i)
call submodel(add,a(i),c(i))
enddo
!$omp end parallel do
write(6,'(a)') '--- addition ---'
write(6,'(4(i5))') c
!$omp parallel do
do i=1,4
thread_private=b(i)
call submodel(mpy,a(i),c(i))
enddo
!$omp end parallel do
write(6,'(a)') '--- multiplication ---'
write(6,'(4(i5))') c
end subroutine sandbox
end module model
TOOLBOX.F90
module toolbox
implicit none
contains
subroutine tool(funct,input,output)
implicit none
interface
function funct(x)
implicit none
integer :: x,funct
end function funct
end interface
integer :: input,output
output = funct(input)
end subroutine tool
end module toolbox
VARIABLE.F90
module variable
use toolbox
implicit none
integer :: thread_private
!$omp threadprivate(thread_private)
end module variable
Is it possible to simply rearrange them in this way? (I have tried and apparently it failed):
subroutine submodel(func,x,y)
implicit none
interface
function func(z)
implicit none
integer :: z,func
end function func
end interface
integer :: x,y
call tool(func,x,y)
contains
function add(a)
implicit none
integer :: a,add
add=a+thread_private
end function add
function mpy(m)
implicit none
integer :: m,mpy
mpy=m*thread_private
end function mpy
end subroutine submodel
You can make the two procedures internal to the subroutine submodel exactly as you did in your last code snippet. The problem is you cannot pass these two subroutines as actual arguments from outside of the subroutine, because you have no access to them there.
Even if you have procedure pointers to them stored somewhere, these would be invalid as soon as the original run of submodel that could have created them ended.
I would think about using some switch:
subroutine submodel(switch,x,y)
implicit none
integer :: switch,x,y
select case(switch)
case(USE_ADD)
call tool(add,x,y)
case(USE_MPY)
call tool(mpy,x,y)
case default
stop "unknown switch value"
end select
contains
function add(a)
implicit none
integer :: a,add
add=a+thread_private
end function add
function mpy(m)
implicit none
integer :: m,mpy
mpy=m*thread_private
end function mpy
end subroutine submodel
Another option is to keep your original design.

Resources