Fortran function arrays - arrays

I am trying to write a function which has a scalar input but an array output.
My example is:
Ts(theta) = reshape((/ dcos(theta)**2.d0, dsin(theta)**2.d0, -dsin(2.d0*theta)/2.d0, &
dsin(theta)**2.d0, dcos(theta)**2.d0, dsin(2.d0*theta)/2.d0, &
dsin(2.d0*theta), -dsin(2.d0*theta), dcos(2.d0*theta) /), (/3,3/))
The Ts(theta) is read as a 1 dimensional array but I want an output as a 2 dimensional array.
Is this possible?

Of course
function Ts(theta)
real(rp) :: Ts(3,3)
real(rp),intent(in) :: theta
Ts = reshape((/ cos(theta)**2, sin(theta)**2, -sin(2*theta)/2, &
sin(theta)**2, cos(theta)**2, sin(2*theta)/2, &
sin(2*theta), -sin(2*theta), cos(2*theta) /), (/3,3/))
end function
where rp is the right kind constant for current real precision.
Remark: do not use specific functions as dsin and dcos, they are remnants of FORTRAN 66 and obsolete since FORTRAN 77.
Your syntax Ts(theta) = means you probably tried a statement function. They are obsolete also. I am not sure if they can be array-valued, you may try it.

Related

Assign complex array from real arrays in Fortran [duplicate]

I want to assign complex array as variable.
My code is like
complex indx(3,3)
integer i,j
do i=1,3
do j=1,3
indx(i,j) = (i,j)
write(*,*) indx(i,j)
end do
end do
and in this case I am getting an error like
A symbol must be a defined parameter in this context. [I]
indx(i,j) = (i,j)
You must use function cmplx to build a complex value you want to assign.
complex indx(3,3)
integer i,j
do i=1,3
do j=1,3
indx(i,j) = cmplx(i,j)
write(*,*) indx(i,j)
end do
end do
The syntax you tried is only valid for constant literals.
The answer by Vladimir F tells the important part: for (i,j) to be a complex literal constant i and j must be constants.1 As stated there, the intrinsic complex function cmplx can be used in more general cases.
For the sake of some variety and providing options, I'll look at other aspects of complex arrays. In the examples which follow I'll ignore the output statement and assume the declarations given.
We have, then, Vladimir F's correction:
do i=1,3
do j=1,3
indx(i,j) = CMPLX(i,j) ! Note that this isn't in array element order
end do
end do
We could note, though, that cmplx is an elemental function:
do i=1,3
indx(i,:) = CMPLX(i,[(j,j=1,3)])
end do
On top of that, we can consider
indx = RESHAPE(CMPLX([((i,i=1,3),j=1,3)],[((j,i=1,3),j=1,3)]),[3,3])
where this time the right-hand side is in array element order for indx.
Well, I certainly won't say that this last (or perhaps even the second) is better than the original loop, but it's an option. In some cases it could be more elegant.
But we've yet other options. If one has compiler support for complex part designators we have an alternative for the first form:
do i=1,3
do j=1,3
indx(i,j)%re = i
indx(i,j)%im = j
end do
end do
This doesn't really give us anything, but note that we can have the complex part of an array:
do i=1,3
indx(i,:)%re = [(i,j=1,3)]
indx(i,:)%im = [(j,j=1,3)]
end do
or
do i=1,3
indx(i,:)%re = i ! Using scalar to array assignment
indx(i,:)%im = [(j,j=1,3)]
end do
And we could go all the way to
indx%re = RESHAPE([((i,i=1,3),j=1,3))],[3,3])
indx%im = RESHAPE([((j,i=1,3),j=1,3))],[3,3])
Again, that's all in the name of variety or for other applications. There's even spread to consider in some of these. But don't hate the person reviewing your code.
1 That's constants not constant expresssions.

non-contiguous data and temporary array creation

A similar question was answered in Fortran runtime warning: temporary array. However, the solutions do not quite help me in my case.
Inside a subroutine, I have a subroutine call as:
subroutine initialize_prim(prim)
real(kind=wp), dimension(2, -4:204), intent(out) :: prim
call double_gaussian(prim(1, :))
end subroutine initialize_prim
subroutine double_gaussian(y)
real(kind=wp), dimension(-4:204), intent(out) :: y
integer :: i
do i = -4, 204
y(i) = 0.5 * ( &
exp(-((r(i) - r0))**2) + exp(-((r(i) + r0)/std_dev)**2))
end do
end subroutine double_gaussian
This gives an error message saying that fortran creates a temporary array for "y" in "double_gaussian". Having read a bit about continguous arrays, I understand why this error appears.
Now, looking at my whole program, it would be very tedious to invert the order of the arrays for "prim", so that solution is not really possible.
For creating assumed-shapes in "double_gaussian", I tried doing,
real(kind=wp), dimension(:), intent(out) :: y
integer :: i
do i = -4, 204
y(i) = 0.5 * ( &
exp(-((r(i) - r0))**2) + exp(-((r(i) + r0)/std_dev)**2))
end do
end subroutine double_gaussian
This, however, causes fortran to crash with the error message
"Index '-4' of dimension 1 of array 'y' below lower bound of 1".
It seems that for the assumed-shape format, the indexing is nonetheless assumed to start with 1, whereas it starts at -4 as in my case.
Is there a way to resolve this issue?
I think that you have perhaps misinterpreted a compiler warning as an error. Usually compilers issue a warning when they create temporary arrays - it's a useful aid to high-performance programming. But I'm not sure a compiler ever regards that as an error. And yes, I understand why you might not want to re-order your array just to avoid that
As for the crash - you have discovered that Fortran routines don't automagically know about the lower bounds of arrays which you have carefully set to be other than 1 (nor their upper bounds either). If it is necessary you have to pass the bounds (usually only the lower bound, the routine can figure out the upper bound itself) in the argument list.
However, it rarely is necessary, and it doesn't seem to be in your code - the loop to set each value of the y array could (if I understand correctly) be replaced by
y = 0.5 * (exp(-((r - r0))**2) + exp(-((r + r0)/std_dev)**2))
PS I think that this part of your question, about routines not respecting other-than-1 array lower bounds, is almost certainly a duplicate of several others asked hereabouts but which I couldn't immediately find.

Access element of N-D array using a vector [duplicate]

This question already has an answer here:
Using a vector to index a multidimensional Fortran array
(1 answer)
Closed 4 years ago.
Is there a way I can access the nth element of an array a, where n is a 1D array and size(n) is the rank of a.
Edit 2015-08-22 15:21
I am thinking of something similar to
program Example1D
integer :: a(6), b(1)
a = reshape( (/ (i , i = 1, size(a) ) /) , shape(a) )
b = (/ 5 /)
write(*,*) a(b)
end program Example1D
So I can call like this
program Want2D
integer :: a(6,5), b(2)
a = reshape( (/ (i , i = 1, size(a) ) /) , shape(a) )
b = (/ 5 , 3 /)
write(*,*) a(b)
end program Want2D
You are attempting to use a vector-subscript (e.g. Fortran 2008, cl. 6.5.3.3.2). This allows you to use a vector (1D array) to select random elements from an array dimension. This, however, cannot be used exactly as you intend it to select an element from multiple dimensions.
From 6.5.3.3 (emphasis mine):
In an array-section having a section-subscript-list, each subscript-triplet and vector-subscript in the section sub-
script list indicates a sequence of subscripts, which may be empty. Each subscript in such a sequence shall be
within the bounds for its dimension unless the sequence is empty. The array section is the set of elements from
the array determined by all possible subscript lists obtainable from the single subscripts or sequences of subscripts
specified by each section subscript.
If you goal in your example code is to select the element a(5,3) with your vector b = [5, 3], then you could change your write from
write (*,*) a(b) ! doesn't work
to:
write (*,*) a(b(1),b(2)) ! does work
You can do more complicated array-sections with b of higher ranks as long as you use a 1D section for each dimension of a. For example if a is a 5x5 array, you could get the corners with of a as a 2x2 array with:
integer :: b(2,2) ! 2 dimensional
b(1,:) = [1,5]
b(2,:) = [1,5]
write (*,*) a(b(1,:),b(2,:)) ! prints a(1,1), a(5,1), a(1,5), a(5,5)
In the comments below you requested that this be abstracted to an n-dimensional array a. Below is a function I consider ugly due to its need to use c interop in a way I consider a hack. You'll also need a newer compiler to even use the code, as it depends on assumed-rank arrays. Here is a module containing a subroutine that takes a 2-dimensional b containing array indices to print and an n-dimensional a to get the values from.
module future
implicit none
contains
subroutine print_array_vector(a, b)
use, intrinsic :: iso_c_binding, only: c_loc, c_f_pointer
implicit none
integer, dimension(..), target :: a
integer, dimension(:,:) :: b
integer :: a_rank, b_len1
integer, dimension(:,:,:), pointer :: a3
integer, dimension(:,:), pointer :: a2
integer, dimension(:), pointer :: a1
a_rank = rank(a)
if (a_rank /= size(b,1)) then
print *, "Rank mismatch between array and vector"
return
end if
if (a_rank == 3) then
call c_f_pointer(c_loc(a), a3, shape=[size(a,1), size(a,2), size(a,3)])
print *, a3(b(1,:),b(2,:),b(3,:))
else if (a_rank == 2) then
call c_f_pointer(c_loc(a), a2, shape=[size(a,1), size(a,2)])
print *, a2(b(1,:),b(2,:))
else if (a_rank == 1) then
call c_f_pointer(c_loc(a), a1, shape=[size(a,1)])
print *, a1(b(1,:))
else
print *, "Unsupported rank"
return
end if
end subroutine print_array_vector
end module future
This takes in assumed-rank a, which is not directly usable in Fortran except to pass as an actual argument to a C interface. However, we can use other parts of c-interop to get the C pointer to a, then turn it into a Fortran pointer of the appropriate shape. Now we have a in a usable form, but we have to do this within if/else blocks properly reference the different cases. I've only implemented up to 3-dimensional a, the rest is left as an exercise to the reader.
To use this function, here is an example:
program test
use future
implicit none
integer :: a3(5,5,5), a2(5,5), a1(5)
integer :: b3(3,2), b2(2,2), b1(1,2)
integer :: i
a3 = reshape([(i,i=1,125)],shape(a3))
a2 = reshape([(i,i=1,25)],shape(a2))
a1 = [(i,i=1,5)]
b3 = reshape([1,1,1,5,5,5],shape(b3))
b2 = reshape([1,1,5,5],shape(b2))
b1 = reshape([1,5],shape(b1))
call print_array_vector(a1,b1)
call print_array_vector(a2,b2)
call print_array_vector(a3,b3)
end program test
This constructs a 3-dim a, a 2-dim a, and a 1-dim a, and a few 2-dim b's with the locations of the corners of the arrays and then we call the function to print the locations of the vector from the array.
% ./arraysection
1 5
1 5 21 25
1 5 21 25 101 105 121 125
I compiled and tested this with gfortran 5.2 and I have no idea the current state of support for assumed-rank arrays in other version of gfortran or in other Fortran compilers.

fortran loop a list of 2D arrays using pointers

i have allocated a lot of 2D arrays in my code, and I want each one array to read from a file named as array's name. The problem is that each array has different size, so I am looking for the most efficient way. The code is like this:
Module Test
USE ...
implicit NONE
private
public:: initializeTest, readFile
real(kind=8),dimension(:,:),allocatable,target:: ar1,ar2,ar3,ar4,ar5,...,ar10
real(kind=8),dimension(:,:),pointer:: pAr
CONTAINS
!
subroutine initializeTest
integer:: k1,k2,k3,k4,k5
integer:: ind1,ind2
allocate(ar1(k1,k1),ar2(k1,k2),ar3(k2,k4),ar4(k5,k5),...) !variable sizes
! here needs automatization - since its repeated
pAr => ar1
ind1 = size(pAr,1)
ind2 = size(pAr,2)
call readFile(par,ind1,ind2)
pAr => ar2
ind1 = size(pAr,1)
ind2 = size(pAr,2)
call readFile(par,ind1,ind2)
!....ar3, ... , ar9
pAr => ar10
ind1 = size(pAr,1)
ind2 = size(pAr,2)
call readFile(par,ind1,ind2)
end subroutine initializeTest
!
!
subroutine readFile(ar,row,col)
real(kind=8),dimension(row,col)
integer:: i,j,row,col
! it should open the file with same name as 'ar'
open(unit=111,file='ar.dat')
do i = 1, row
read(222,*) (ar(i,j),j=1,col)
enddo
end subroutine importFile
!
!
end module Test
If your arrays ar1, ar2, etc. had the same dimensions you could put them all in a 3-dimensional array. Since they have different dimensions, you can define a derived type, call it a "matrix", with an allocatable array component and then create an array of that derived type. Then you can read the i'th matrix from a file such as "input_1.txt" for i=1.
The program below, which works with g95 and gfortran, shows how the derived type can be declared and used.
module foo
implicit none
type, public :: matrix
real, allocatable :: xx(:,:)
end type matrix
end module foo
program xfoo
use foo, only: matrix
implicit none
integer, parameter :: nmat = 9
integer :: i
character (len=20) :: fname
type(matrix) :: y(nmat)
do i=1,nmat
allocate(y(i)%xx(i,i))
write (fname,"('input_',i0)") i
! in actual code, read data into y(i)%xx from file fname
y(i)%xx = 0.0
print*,"read from file ",trim(fname)
end do
end program xfoo
As far as I know, extracting the name of the variable from the variable at runtime isn't going to work.
If you need lots of automation for the arrays, consider using an array of a derived type, as one other answer suggests, in order to loop over them both for allocation and reading. Then you can enumerate the files, or store a label with the derived type.
Sticking to specific array names, an alternative is to just read/write the files with the required name as an argument to the routine:
Module Test
...
! here needs automatization - since its repeated
call readFile(ar1,'ar1')
call readFile(ar2,'ar2')
!....ar3, ... , ar9
call readFile(ar10,'ar10')
end subroutine initializeTest
subroutine readFile(ar,label)
real(kind=8) :: ar(:,:)
character(len=*) :: label
integer:: i,j,nrow,ncol,fd
nrow=size(ar,1)
ncol=size(ar,2)
open(newunit=fd,file=label)
do i = 1, row
read(fd,*) (ar(i,j),j=1,col)
enddo
end subroutine readFile
end module Test
Some unsollicited comments: I don't really get why (in this example) readFile is public, why the pointers are needed? Also, kind=8 shouldn't be used (Fortran 90 kind parameter).

Parsing a complicated function using MKL/VML

I am trying to calculate a fairly complicated function, say func() - involving several additions, substractions, multiplications, divisions and trigonometric functions, of several two-dimensional arrays in fortran. The calculation is massively parrallel, in that each func() is independent over its row and column location. Each of the matrices is many gigabytes in size, and there are about a dozen of them as arguments.
I would like to make use of Intel MKL functions (invoking --mkl-parallel), in particular VML functions to add, subtract, divide etc. My question is: how can I render a complicated functional expression such as,
e.g.: func(x,y,z) = x*y+cos(z*x-x) where x,y,z are 2d arrays of several GB
in terms of VML functions but using more familiar binary operators. You see my problem requires, in principle, converting all the binary operators, such as "+" and "*" into binary functions taking arguments as ?vadd(x,y). Of course this would be very cumbersome and unsightly for large expressions. Is there a way to overload the binary arithmetic operators such as "+","-" to preferentially use MKL/VML versions in fortran. An example would be nice! Thanks!
I know this answer is a little bit off-topic.
Since all the operations are element-wise and your operations are simple, the func() could be a memory bandwidth bounded task. In this case, using VML may not be a good choice to maximum the performance.
Suppose each of your arrays is of 10GB in size, uisng VML as follows will need at least 9 x 10GB reading and 5 x 10GB writing.
func(...) {
tmp1=x*z
tmp1=tmp1-x;
tmp1=cos(tmp1);
tmp2=x*y;
return tmp1+tmp2;
}
where all the operations all overloaded for 2d array.
Instead you may find the following approach has much less memory access (3 x 10GB reading and 1 x 10GB writing) thus could be quicker (pseudo code).
$omp parallel for
for i in 1 to m
for j in 1 to n
result(i,j)= x(i,j)*y(i,j)+cos(z(i,j)*x(i,j)-x(i,j));
end
end
I developped a small example to show the addition of two vectors. As I don't have MKL installed anymore, I used the SAXPY command from BLAS. The principle should be the same.
At first you define a module with the appropriate definitions. In my case this would be an assignment to save a real array in my datatype (this is only a convenience function as you could also directly access the array variable) and the definition of the addition. Both are a new overload to the + operator and = assignment.
In the program, I define three fields. Two of them get assigned with random numbers and then added to get the third field. Then the first two fields get stored in my special variables, and the result of this addition is stored in a third variable of this type.
Finally, the result is compared by accessing the array directly. Please note, that the assignment from custom datatype to the same datatype is already defined (e.g. ffield3 = ffield1 is already defined.)
My module:
MODULE fasttype
IMPLICIT NONE
PRIVATE
PUBLIC :: OPERATOR(+), ASSIGNMENT(=)
TYPE,PUBLIC :: fastreal
REAL,DIMENSION(:),ALLOCATABLE :: array
END TYPE
INTERFACE OPERATOR(+)
MODULE PROCEDURE fast_add
END INTERFACE
INTERFACE ASSIGNMENT(=)
MODULE PROCEDURE fast_assign
END INTERFACE
CONTAINS
FUNCTION fast_add(fr1, fr2) RESULT(fr3)
TYPE(FASTREAL), INTENT(IN) :: fr1, fr2
TYPE(FASTREAL) :: fr3
INTEGER :: L
L = SIZE(fr2%array)
fr3 = fr2
CALL SAXPY(L, 1., fr1%array, 1, fr3%array, 1)
END FUNCTION
SUBROUTINE fast_assign(fr1, r2)
TYPE(FASTREAL), INTENT(OUT) :: fr1
REAL, DIMENSION(:), INTENT(IN) :: r2
INTEGER :: L
IF (.NOT. ALLOCATED(fr1%array)) THEN
L = SIZE(r2)
ALLOCATE(fr1%array(L))
END IF
fr1%array = r2
END SUBROUTINE
END MODULE
My program:
PROGRAM main
USE fasttype
IMPLICIT NONE
REAL, DIMENSION(:), ALLOCATABLE :: field1, field2, field3
TYPE(fastreal) :: ffield1, ffield2, ffield3
ALLOCATE(field1(10),field2(10),field3(10))
CALL RANDOM_NUMBER(field1)
CALL RANDOM_NUMBER(field2)
field3 = field1 + field2
ffield1 = field1
ffield2 = field2
ffield3 = ffield1 + ffield2
WRITE(*,*) field3 == ffield3%array
END PROGRAM

Resources