How to plot the real and imaginary parts of an array? - arrays

I've written a program that calculates the Discrete Fourier Transform of a sample, where in this case I'm sampling a sine wave. To test it, I need to plot the result. However, the resultant array is filled with complex values.
So how do I extract the real and imaginary components of these array elements, and then plot them against their indexes?
Here's my code:
program DFT
implicit none
integer :: k, N, x, y, j, r, l, istat
integer, parameter :: dp = selected_real_kind(15,300)
real, allocatable,dimension(:) :: h
complex, allocatable, dimension(:) :: rst
complex, dimension(:,:), allocatable :: W
real(kind=dp) :: pi, z, P, A, i
pi = 3.14159265359
P = 2*pi
A = 1
!open file to write results to
open(unit=100, file="dft.dat", status='replace')
N = 10
!allocate arrays as length N, apart from W (NxN)
allocate(h(N))
allocate(rst(N))
allocate(W(-N/2:N/2,1:N))
pi = 3.14159265359
!loop to fill the sample containing array
do k=1,N
h(k) = sin((2*k*pi)/N)
end do
!loop to fill the product matrix with values
do j = -N/2,N/2
do k = 1, N
W(j,k) = EXP((2.0_dp*pi*cmplx(0.0_dp,1.0_dp)*j*k)/N)
end do
end do
!use of matmul command to multiply matrices
rst = matmul(W,h)
!print *, h, w
write(100,*) rst
end program
Thanks.

The REAL intrinsic function returns the real part of a complex number in Fortran. It is an elemental function as well, so for an array of type complex simply REAL( array ) will return a real array with the same kind as the original containing the results you want.
The AIMAG intrinsic function returns the imaginary part of a complex number in Fortran. It is an elemental function as well, so for an array of type complex simply AIMAG( array ) will return a real array with the same kind as the original containing the results you want.
Alternatively in Fortran 2003 latter %re and %im can be used to access the real and imaginary part respectively of a complex variable. The comments about their elemental nature again apply.
These are easily found by googling, or better I think every Fortran programmer should at least have access to a copy of Metcalf, Reid and Cohen "Modern Fortran Explained".

Related

Sum and assign of array is slower in derived types

I was comparing the performance of doing a sum followed by an assignment of two arrays, in the form of c=a+b, between a native Fortran type, real, and a derived data type that only contains one array of real. The class is very simple: it contains operators for addition and assignment and a destructor, as follows:
module type_mod
use iso_fortran_env
type :: class_t
real(8), dimension(:,:), allocatable :: a
contains
procedure :: assign_type
generic, public :: assignment(=) => assign_type
procedure :: sum_type
generic :: operator(+) => sum_type
final :: destroy
end type class_t
contains
subroutine assign_type(lhs, rhs)
class(class_t), intent(inout) :: lhs
type(class_t), intent(in) :: rhs
lhs % a = rhs % a
end subroutine assign_type
subroutine destroy(this)
type(class_t), intent(inout) :: this
if (allocated(this % a)) deallocate(this % a)
end subroutine destroy
function sum_type (lhs, rhs) result(res)
class(class_t), intent(in) :: lhs
type(class_t), intent(in) :: rhs
type(class_t) :: res
res % a = lhs % a + rhs % a
end function sum_type
end module type_mod
The assign subroutine contains different modes of operations, just for the sake of benchmarking.
To test it against performing the same operations on a real I created the following module
module subroutine_mod
use type_mod, only: class_t
contains
subroutine sum_real(a, b, c)
real(8), dimension(:,:), intent(inout) :: a, b, c
c = a + b
end subroutine sum_real
subroutine sum_type(a, b, c)
type(class_t), intent(inout) :: a, b, c
c = a + b
end subroutine sum_type
end module subroutine_mod
Everything is executed in the program below, considering arrays of size (10000,10000) and repeating the operation 100 times:
program test
use subroutine_mod
integer :: i
integer :: N = 100 ! Number of times to repeat the assign
integer :: M = 10000 ! Size of the arrays
real(8) :: tf, ts
real(8), dimension(:,:), allocatable :: a, b, c
type(class_t) :: a2, b2, c2
allocate(a2%a(M,M), b2%a(M,M), c2%a(M,M))
a2%a = 1.0d0
b2%a = 2.0d0
c2%a = 3.0d0
allocate(a(M,M), b(M,M), c(M,M))
a = 1.0d0
b = 2.0d0
c = 3.0d0
! Benchmark timing with
call cpu_time(ts)
do i = 1, N
call sum_type(a2, b2, c2)
end do
call cpu_time(tf)
write(*,*) "Type : ", tf-ts
call cpu_time(ts)
do i = 1, N
call sum_real(a, b, c)
end do
call cpu_time(tf)
write(*,*) "Real : ", tf-ts
end program test
To my surprise, the operation with my derived datatype consistently underperformed the operation with the Fortran arrays by a factor of 2 with gfortran and a factor of 10 with ifort. For instance, using the CHECK_SIZE mode, which saves allocation time, I got the following timings compiling with the -O2 flag:
gfortran
Data type: 33 s
Real : 13 s
ifort
Data type: 30 s
Real : 3 s
Question
Is this normal behaviour? If so, are there any recommendations to achieve better performance?
Context
To provide some context, the type with a single array will be very useful for a code refactoring task, where we need to keep similar interfaces to a previous type.
Compiler versions
gfortran 9.4.0
ifort 2021.6.0 20220226
You are worried about allocation time, but you do a lot of allocations of arrays of shape [M,M] for the derived type, and almost none for the intrinsic type.
The only allocations for the intrinsic type are in the main program, for a, b and c. These are outside the timing loop.
For the derived type, you allocate for a2%a, b2%a and c2%a (again outside the timing loop), but also res%a in the function sum, N times inside the timing loop.
Equally, inside the sum_real subroutine the assignment statement c=a+b involves no allocatable object but inside sum_type the c in c=a+b is an allocatable array: the compiler checks whether c is allocated and if so, whether its shape matches the right-hand side expression.
In summary: you are not comparing like with like. There's a lot of overhead in wrapping an intrinsic array as an allocatable component of a derived type.
Tangential to your timing concerns is the "cleverness" of the subroutine assign. It's horrible.
Calling an argument lhs when it's associated with the right-hand side of the assignment statement is a little confusing, but the select case construct is confusing beyond a little.
In
case (ASSUMED_SIZE)
this % a = lhs % a
under rules where the rest of the program makes any sense, invokes a couple of checks:
is this%a allocated? If not, allocate it to the shape of lhs%a.
if it is allocated, check whether the shape matches lhs%a, if not deallocate it then allocate it to the shape of lhs%a.
Those checks and actions which are done manually in the CHECK_SIZE case, in other words.
The final subroutine does nothing of value, so the entire assign subroutine's execution can be replaced by this%a = lhs%a.
(Things would be different if the final subroutine had substantive effect or the compiler had been asked to ignore the rules of intrinsic assignment using -fno-realloc-arrays or -nostandard-realloc-lhs for example, or this%a(:,:)=lhs%a had been used.)

Array assignment erases previous values on array

I'm running the following code, that is the implementation of a Runge-Kutta method to solve a system of differential equations.
The main code just calls the rk subroutine, which is the implementation itself, and myfun is just an example to test the code.
program main
use ivp_odes
implicit none
double precision, allocatable :: t(:), y(:,:)
double precision :: t0, tf, y0(2), h
integer :: i
t0 = 0d0
tf = 0.5d0
y0 = [0d0, 0d0]
h = 0.1d0
call rk4(t, y, myfun, t0, tf, y0, h)
do i=0,size(t)
print *, t(i), y(:,i)
end do
contains
pure function myfun(t,y) result(dy)
! input variables
double precision, intent(in) :: t, y(:)
! output variables
double precision :: dy(size(y))
dy(1) = -4*y(1) + 3*y(2) + 6
dy(2) = -2.4*y(1) + 1.6*y(2) + 3.6
end function myfun
end program main
and the subroutine is inside a module:
module ivp_odes
implicit none
contains
subroutine rk4(t, y, f, t0, tf, y0, h)
! input variables
double precision, intent(in) :: t0, tf, y0(1:)
double precision, intent(in) :: h
interface
pure function f(t,y0) result(dy)
double precision, intent(in) :: t, y0(:)
double precision :: dy(size(y))
end function
end interface
! output variables
double precision, allocatable :: t(:), y(:,:)
! Variáveis auxiliares
integer :: i, m, NN
double precision, allocatable :: k1(:), k2(:), k3(:), k4(:)
m = size(y0)
allocate(k1(m),k2(m),k3(m),k4(m))
NN = ceiling((tf-t0)/h)
if (.not. allocated(y)) then
allocate(y(m,0:NN))
else
deallocate(y)
allocate(y(m,0:NN))
end if
if (.not. allocated(t)) then
allocate(t(0:NN))
else
deallocate(t)
allocate(t(0:NN))
end if
t(0) = t0
y(:,0) = y0
do i=1,NN
k1(:) = h * f(t(i-1) , y(:,i-1) )
k2(:) = h * f(t(i-1)+h/2 , y(:,i-1)+k1(:)/2)
k3(:) = h * f(t(i-1)+h/2 , y(:,i-1)+k2(:)/2)
k4(:) = h * f(t(i-1)+h , y(:,i-1)+k3(:) )
y(:,i) = y(:,i-1) + (k1(:) + 2*k2(:) + 2*k3(:) + k4(:))/6
t(i) = t(i-1) + h
end do
deallocate(k1,k2,k3,k4)
return
end subroutine rk4
end module ivp_odes
The problem here is that the assignment in the rk subroutine
y(:,i) = y(:,i-1) + (k1(:) + 2*k2(:) + 2*k3(:) + k4(:))/6
is erasing the previous values calculated. In the i-th iteration of the do-loop, it erases the previous values of the array y and assigns just the i-th column of the array y, so when the subroutine ends, y has only the last value saved.
Since Fortran has implemented element-wise operations and assignments to arrays, I think the code this is easier to read and probably runs faster than doing assignments to each element in a loop. So, why is it not working? What am I missing in the assignment here? Shouldn't it just change the values in the i-th row, instead of also erasing the rest of the array?
This is a typical case of accessing an array out of its bounds. You can find these errors easily using the appropriate compiler flags. With gfortran, this would be -fbounds-check.
With such checks you will find the error to be an erroneous size of the function result in the interface block - dy should have the same length as y0 (the one-dimensional dummy argument of f), and not y:
interface
pure function f(t,y0) result(dy)
double precision, intent(in) :: t, y0(:)
double precision :: dy(size(y0))
end function
end interface
Additionally, although not related to your particular error, you started indexing of t and the second dimension of y with zero. So you need to adjust the loop in the main program run to size(t)-1 only, or use ubound(t). Otherwise you will, again, exceed the boundaries of the arrays.

How are these arrays being used in this Fortran algorithm?

I'm writing some subroutines in Fortran90 to perform some numerical computations. However, as part of this, I need to include some codes from the netlib templates library that are written in Fortran77. I'm having a hard time getting them to work - specifically understanding the usage of arrays.
For instance, I need to use a subroutine called GMRES. Here are the arguments:
SUBROUTINE GMRES( N, B, X, RESTRT, WORK, LDW, WORK2, LDW2,
$ ITER, RESID, MATVEC, PSOLVE, INFO )
* .. Scalar Arguments ..
INTEGER N, RESTRT, LDW, LDW2, ITER, INFO
DOUBLE PRECISION RESID
* ..
* .. Array Arguments ..
DOUBLE PRECISION B( * ), X( * ), WORK( * ), WORK2( * )
* ..
* .. Function Arguments ..
*
EXTERNAL MATVEC, PSOLVE
*
*
* Arguments
* =========
*
* N (input) INTEGER.
* On entry, the dimension of the matrix.
* Unchanged on exit.
*
* B (input) DOUBLE PRECISION array, dimension N.
* On entry, right hand side vector B.
* Unchanged on exit.
*
* X (input/output) DOUBLE PRECISION array, dimension N.
* On input, the initial guess; on exit, the iterated solution.
*
* RESTRT (input) INTEGER
* Restart parameter, <= N. This parameter controls the amount
* of memory required for matrix WORK2.
*
* WORK (workspace) DOUBLE PRECISION array, dimension (LDW,5).
* Note that if the initial guess is the zero vector, then
* storing the initial residual is not necessary.
*
* LDW (input) INTEGER
* The leading dimension of the array WORK. LDW >= max(1,N).
*
* WORK2 (workspace) DOUBLE PRECISION array, dimension (LDW2,2*RESTRT+2).
* This workspace is used for constructing and storing the
* upper Hessenberg matrix. The two extra columns are used to
* store the Givens rotation matrices.
*
* LDW2 (input) INTEGER
* The leading dimension of the array WORK2.
* LDW2 >= max(1,RESTRT).
*
* ITER (input/output) INTEGER
* On input, the maximum iterations to be performed.
* On output, actual number of iterations performed.
*
* RESID (input/output) DOUBLE PRECISION
* On input, the allowable error tolerance.
* On ouput, the norm of the residual vector if solution
* approximated to tolerance, otherwise reset to input
* tolerance.
*
* INFO (output) INTEGER
* = 0: successful exit
* = 1: maximum number of iterations performed;
* convergence not achieved.
*
* MATVEC (external subroutine)
* The user must provide a subroutine to perform the
* matrix-vector product A*x = y.
* Vector x must remain unchanged. The solution is
* over-written on vector y.
*
* The call is:
*
* CALL MATVEC( X, Y )
Notice how the arrays are defined WORK( * ), WORK2( * ). So to my mind these are 1D arrays of arbitrary length. But then in the argument description, it seems to be suggesting that they are 2D arrays, matrices, with dimension WORK(LDW, 5). So are they 1D or 2D?
Also, in the GMRES algorithm, these WORK arrays are used like this:
CALL MATVEC(SCLR1, WORK(NDX1), SCLR2, WORK(NDX2))
So if the WORK arrays are 2D, wouldn't the above give a rank mismatch? What does it mean to access a 2D array with just one index like that? Should I define the WORK arrays as 2D or 1D?
Edit
The GMRES routine requires a matvec routine to be supplied. In the GMRES code it is being called like
CALL MATVEC(SCLR1, X, SCLR2, WORK(NDX2))
and also like
CALL MATVEC(SCLR1, WORK(NDX1), SCLR2, WORK(NDX2))
My subroutine MATVEC that I'm trying to supply looks like:
subroutine matvec(alpha, x, beta, y)
real(dp), intent(in) :: alpha, beta
real(dp), dimension(*), intent(in) :: x
real(dp), dimension(*), intent(inout) :: y
real(dp), dimension(*,*) :: A
integer :: n
call make_Jac(n,A)
call dgemv('notranspose', n, n, alpha, A, n, x, 1, beta, y, 1)
end subroutine matvec
Where make_Jac returns my matrix for the problem I'm working on, along with its dimension n. The blas routine dgemv then handles the matrix-vector product.
WORK( * ) declares an assumed size array, which can be two-dimensional. See here.
The compiler will not complain if you feed a one-dimensional array to the subroutine, but weird things (up to and including a segmentation fault) might happen.
Better use arrays matching the specifications.
The same Fortran array can be managed as one dimensional, two dimensional, etc. It is stored in contiguous memory in any case.
Let's say you have
double precision x(3, 2)
call somefunc (x)
This can be accessed, inside somefunc, as y (6).
The array elements are stored in "column major order" which means
x(1, 1) is y(1)
x(2, 1) is y(2)
x(3, 1) is y(3)
x(1, 2) is y(4)
x(2, 2) is y(5)
x(3, 2) is y(6)
As long as the function knows the limits of each dimension, it can calculate linear access by simple arithmetic. Alas, this "relaxed type" is also a frequent source of bugs.
Further to the answer by wallyk the dummy argument work(*) is a rank-1 assumed size array. With such an array
The rank and extents may differ for the effective and dummy arguments; only the size of the effective argument is assumed by the dummy argument.
This means that it is quite acceptable for such a structure as
double precision work(LDW,5)
call GMRES(..., work, ...)
! ...
end
subroutine GMRES(..., work, ...)
double precision work(*)
! ...
end subroutine
Indeed, with the dummy argument as a rank-1 array it isn't allowed to reference it as a rank-2 array. A rank-2 assumed size array would look something like
double precision work(LDW,*)
where then, of course, work(ndx1) would be bad.
Coming to the comment by roygvib, later on in the Netlib source code there is the line
call GMRESREVCOM(..., work, ...)
where that subroutine has the dummy argument
double precision work(LDW,*)
There is probably, then, an expectation that the user of the code will provide initially a rank-2 actual argument.
What all of this means is that it doesn't matter what rank the actual argument passed to work in GMRES as long as it has at least LDW*5 elements. I'd be careful calling the dummy argument as being of "arbitrary length", though, as referencing work(LDW*5+1) (according to my first example) would be wrong. The size of the dummy array is exactly the size of the passed array.
The later call to matvec is not troublesome for yet another reason. This subroutine has four arguments, the first and third of which are scalar. The second and fourth are again assumed-size arrays of rank-1. We've established that assumed-size arrays don't care about the rank of the effective/actual argument, but you're likely wondering why you can pass the scalar argument work(ndx1) to this rank-1 array.
The answer to that is something called sequence association. This means that when your actual argument is an array element designator and the dummy argument is an array dummy argument then that dummy argument is argument associated with a sequence of elements from the actual argument, starting with the element element designated.
That is, you have a rank-1 array like [work(ndx1), work(ndx1+1), ...] as your array x in matvec.
This is all fine, as long as you don't attempt to reference beyond the extent of your actual argument.

Fortran calculate distance of many points to fixed point

Is there a more elegant and concise way to calculate the distances between a set of points and a fixed point than doing this?
real, dimension(3, numPoints) :: points
real, dimension(3) :: source
real, dimension(numPoints) :: r
r = sqrt((points(1,:) - source(1))**2 + &
(points(2,:) - source(2))**2 +
(points(3,:) - source(3))**2)
I tried
r = sqrt(sum((points - source)**2,1))
but this - unsurprisingly - does not work.
If you have a bang-up-to-date compiler, one which implements the intrinsic functions added to Fortran in the 2008 revision, the following expression might satisfy your ideas of elegance and concision for the computation of one distance:
r(1) = norm2(points(:,1)-source)
If you don't have a bang-up-to-date compiler you'll probably find either that your compiler has a non-standard function for the L2 norm or that you have a library hanging around that implements it.
I don't have Fortran on this machine, so upranking this to a one-liner that calculates the distances of all the points from source, I leave that to the interested reader. But I wouldn't sniff at the obvious solution of looping over all the points in turn.
EDIT: OK, here's a one-liner, untested
r = norm2(points-spread(source,dim=1,ncopies=numpoints),dim=2)

Fortran: combine allocatable vectors in an array without copying and reshaping

How can one combine bunch of large allocatable vectors in an array in Fortran? I'd like to avoid copying and reshaping using reshape as the arrays are large. The effect I want to achieve is just like Fortran's equivalence, to illustrate:
program test_equiv
integer x(10), y(10), z(10), xyz(10,3)
equivalence (x, xyz(1,1))
equivalence (y, xyz(1,2))
equivalence (z, xyz(1,3))
x = 1
y = 2
z = 3
! and we can use just normal array syntax
print *, xyz(3,:)
end program
This will, however, not work with allocatable arrays. If it's about accessing vectors of matrix it can be easily implemented via pointers. But how can one combine vectors in a two dimensional array? Till now I came only to array of pointers which has problems:
program test_arofpntrs
implicit none
integer :: i
integer, allocatable, target :: xs(:), ys(:), zs(:)
type t_p_xs
integer, pointer :: p_xs(:)
end type t_p_xs
type(t_p_xs), allocatable :: coords(:)
allocate(coords(3), xs(10), ys(10), zs(10))
xs = 1
ys = 2
zs = 3
coords(1) % p_xs => xs
coords(2) % p_xs => ys
coords(3) % p_xs => zs
print *, coords(1) % p_xs(:)
! this fails:
!print *, coords(:) % p_xs(1)
end program
This is ugly and one cannot access xs(i), ys(i), zs(i). Is it possible to do what I want without copies?
This will not be possible, if you start with separate 1D arrays. The allocatable arrays may be anywhere in the memory. Although Fortran arrays don't have to be contiguous, there has to be some system of strides.
! this fails:
!print *, coords(:) % p_xs(1)
is prohibited by the Fortran standard, because one can not calculate the address of the next element simply. The 1D arrays are even not guaranteed to have the same length.
Also reshape as such does not have to be inefficient, it may just syntactically help with indexing, but don't touch the data at all.
Pointers are a great tool and may help here. You would have to use your 1D arrays bit differently. For example allocate a long 1D array and have 1D pointers for parts of it and a 2D pointer as a whole, or better the other way round:
real,allocatable,target :: xyz(:,:)
real,pointer :: x(:),y(:),z(:)
allocate(xyz(1:10,1:3))
x => xyz(:,1)
y => xyz(:,2)
z => xyz(:,3)
Even the other order of indexes is possible, i.e., xyz(3,10); x => xyz(1,:).
Also you can do
long1D(1:size(xyz)) => xyz
but note it is a Fortran 2008 feature in this direction (2003 otherwise).

Resources