Replace nested loops to be n-dimensional - iterator

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.

Related

fortran array=0 not given values when optimized

I have a rather large code (>6000 lines)that with a problem that I try to illustrate with the following very simplified extract:
program TestFortran
implicit none
real, parameter :: eps = 1d-30
real :: res
real :: tmp
call TestInitialisation(res,tmp)
if (res>0 .and. res <eps) then
write(*,*) "res is small but not zero and tmp = ", tmp
res = 0d0
else
write(*,*) "res is zero and tmp = ", tmp
end if
contains
subroutine TestInitialization(output,out2)
real,intent(out) :: output
real,intent(out) :: out2
real :: origa(10,10)
real :: copya(10,10)
origa = 0d0
copya = origa
call TIS1(copya,output,out2)
end subroutine TestInitialization
subroutine TIS1(arr2d,ret,out2)
real,intent(in) :: arr2d(:,:)
real,intent(out) :: ret
real,intent(out) :: out2
integer :: ii
do ii = 1,size(arr2d,2)
call TIS2(arr2d(:,ii),ii,ret,out2)
if (ret > 0) then
exit
end if
end do
end subroutine TIS1
subroutine TIS2(arr1d,jj,ret,out2)
real,intent(in) :: arr1d(:)
real,intent(out) :: ret
real,intent(out) :: out2
integer :: ii
integer,intent(in):: jj
do ii = 1,size(arr1d)
ret = arr1d(ii)
out2 = real(jj)
if (ret > 0) then
out2 = -1d0
exit
end if
end do
end subroutine TIS2
end program TestFortran
The real program does work as expected in debug mode. However, when in release mode (visual studio 2017 with intel compiler 2017), switch /O3, the res value is just garbage (like 1.0831d-273, however I am unsure weather to trust the variable explorer when debugging optimized code). I could not recreate the situation with the example above, it just serves as illustration (thetmp variable is there for it not to just optimize the whole thing away). If I add a write(*,*) "res in TIS2 =",resin subroutine TIS2 in the real program code, the result is correct, but this it not wanted (especially because of speed degradation).
I have tested various compiler flags compbinations; i.e. the following:
/debug:full /O2 /Qinit:snan /Qinit:arrays /fpe:0 /Qipo /traceback
/check:uninit /arch:SSE3 /real_size:64 /fp:fast=2 /Qvec-threshold:60 /recursive
Does anyone have any hint's or comments on this?
I had made a mistake (bug) where an array wasn't fully populated correctly. I found this using the compiler flags: /Qinit:snan together with /Qinit:arrays in debug mode. It did not show up in release mode.
So all in all it was just a fairly simple bug in my code that went unnoticed, mostly (in 99 % of the cases) everything went well, but not always. The strange thing was that it wasn't caught in release mode.

There is no specific subroutine for the generic ‘fminsearch’

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

Modules containing subroutines and functions in fortran

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

Performance: Fortran 90 argument vs. use module

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

How to read text files with possibly very long lines in Fortran

Suppose I have a text file with potentially very long lines of text, for example
short
short
reeeeeeeeeeeeeeeeeeeeeeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaally loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooonnnnggg
short as well
short
I can write a simple program which reads this file:
program main
implicit none
integer, parameter :: BIG_NUMBER = 400
integer :: lun
character(len=BIG_NUMBER) :: line
integer :: istat
open(newunit = lun, file = 'myfile')
do
read (lun, '(A)', iostat = istat) line
if (istat /= 0) exit
end do
end program main
This only supports text files, where all lines are not longer than 400 characters. In C, pointers are used and a similar program would automatically support any text file.
How can I rewrite the sample program in such a way that it can read lines of all lengths?
You can use non-advancing input to read successive chunks of the line into a buffer, and then combine each chunk together to form the complete line in a deferred length character variable. For example:
subroutine get_line(lun, line, iostat, iomsg)
integer, intent(in) :: lun
character(len=:), intent(out), allocatable :: line
integer, intent(out) :: iostat
character(*), intent(inout) :: iomsg
integer, parameter :: buffer_len = 80
character(len=buffer_len) :: buffer
integer :: size_read
line = ''
do
read ( lun, '(A)', &
iostat = iostat, &
iomsg = iomsg, &
advance = 'no', &
size = size_read ) buffer
if (is_iostat_eor(iostat)) then
line = line // buffer(:size_read)
iostat = 0
exit
else if (iostat == 0) then
line = line // buffer
else
exit
end if
end do
end subroutine get_line
Never seek another solution when you have one at hand, unless it has some limitations. Since you can do it in C and with all the interoperability stuff between C and Fortran, I suggest you write a small part of the program in C to handle the reading.
The C part can be as simple as a set of 2 functions and few global variables:
a function to query the size of the next line that actually reads the next line and returns its size to fortran, in addition to a EOF flag
a function to get the next line data that returns the line data to fortran.
If you are using getline for example, the global variables will be the File object, the line buffer and the buffer size. In addition, you can add two other functions to be called by fortran to open and close the file, or you can handle all of that in the query function.