I'm having a weird issue with a Fortran code - arrays

I am fairly new to Fortran programming so this might be an obvious issue, so bear with me.
Here is the code I am working with:
program A1H
! Householder transformation
implicit none
integer,parameter::dp=selected_real_kind(15,307) ! Double precision kind
real(kind=dp), dimension(6,3)::A
real(kind=dp), dimension(6,1)::b
integer, dimension(6,6)::Pglobal ! Global identity matrix
integer::i,j,g
g = size(A,1)
do j=1,g
do i=1,g
Pglobal(i,j) = (i/j)*(j/i)
end do
end do
b(:,1) = [1237,1941,2417,711,1177,475]
A(1,:) = [1,0,0]
A(2,:) = [0,1,0]
A(3,:) = [0,0,1]
A(4,:) = [-1,1,0]
A(5,:) = [-1,0,1]
A(6,:) = [0,-1,1]
call mat_print('A',A)
call mat_print('b',b)
call mat_print('Pglobal',Pglobal)
call householder(A,b)
contains
subroutine householder(A,b)
real(kind=dp), intent(in)::A(:,:),b(:,:)
real(kind=dp)::alpha,gamma,beta
real(kind=dp), dimension(6,6)::H
real(kind=dp), dimension(6,3)::y,aa
real(kind=dp), dimension(6,1)::yy,v,dglobal,ek,bb
real(kind=dp), dimension(1,6)::d
integer::k,m,n,j
m = size(A,1)
n = size(A,2)
aa = A
bb = b
do k=1,n
dglobal(:,k) = [0,0,0,0,0,0]
alpha = -sign(aa(k,k),aa(k,k))*norm2(aa(k:m,k))
ek(:,1) = Pglobal(:,k)
dglobal(k:m,k) = aa(k:m,k)
v(:,k) = (dglobal(:,k)) - alpha*ek(:,1)
d(k,:) = v(:,k)
beta = dot_product(d(k,:),v(:,k))
if (beta==0) then
continue
end if
H = Pglobal - (2/beta)*(matmul(reshape(v(:,k),(/m,1/)),reshape(d(k,:),(/1,m/))))
y = matmul(H,aa)
yy = matmul(H,bb)
aa = y
bb = yy
call mat_print('aa',y)
call mat_print('bb',yy)
end do
end subroutine
! Matrix print subroutine
subroutine mat_print(b,a)
character(*), intent(in)::b
class(*), intent(in)::a(:,:)
integer::i
print*,' '
print*,b
do i=1,size(a,1)
select type (a)
type is (real(kind=dp)) ; print'(100f9.4)',a(i,:)
type is (integer) ; print'(100i9 )',a(i,:)
end select
end do
print*,' '
end subroutine
end program
The issue I'm having is that when I change the variable aa to another name, I get the wrong result for the y variable; if I leave it as is, it is correct; however, leaving the bb variable as is, the yy result is incorrect, but if I change the bb variable to another other name, I get the correct result for yy, but then my answer for y changes! I'm very confused how this can happen as my experience from coding tells me that the answer should not change based on a simple variable name change, and if you look at the code, the y and yy variables are not even connected in the algorithm. This is not the only Fortran code I have run into this issue before on. Any help would be much appreciated.

I was able to reproduce your bug with GNU Fortran (Homebrew GCC 8.2.0) 8.2.0. There is indeed a bug in your program. You can find this bug by compiling with the option -fbounds_check. When you run it, you will find that several of your array access don't make sense. For example, you access dglobal(:,k) = [0,0,0,0,0,0], but the second dimension of dglobal is only 1. Use this flag to help fix your code, and I'm sure this bug will disappear.
For anyone who wants to dig deep into why this bug's appearance depends on the variable name, I was able to reproduce it with the array name test_array, but not with other shorter names. I was also able to get the correct answer using the test_array name if I set -fmax-stack-var-size=100, and other values appeared with different sizes. My guess is that gfortran puts these arrays on the stack, and the order is based on the name. Certain names put it in a "safe" location, so that the values are not overwritten by the buffer overflow.

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.

How to assign values to an array except certain elements in fortran?

I want to assign values to an array with a certain rule, except the 5th element due to divide-by-zero problem. The program is like follows:
program main
implicit none
real(8) :: a(10)
integer :: i
a(5) = 0d0
do i = 1, 10
if (i /= 5) then
a(i) = 1.0d0 / dble(i-5)
end if
write(*,*) a(i)
end do
stop
end program main
Is there a more smart/efficient way to do the same thing?
Thank you very much!
If you want to save the amount of source code:
program main
implicit none
integer, parameter :: dbl = kind(1.d0)
real(dbl) :: a(10)
integer :: i
do i = 1, 10
a(i) = 1._dbl / (i-5)
end do
a(5) = 0
! I expect you want to do something more than just this with the array
do i = 1, 10
write(*,*) a(i)
end do
end program
As francescalus points out this may cause your program to crash if floating point exceptions are enabled. Anyway, also notice other things I used, which can shorten your code. = 0 instead of = 0.d0, avoiding the dbl() (use real(x,dbl) instead if necessary) and so on.
If this code is repeated very often, you could also save some CPU time by avoiding the branch. In a typical initialization code it is irrelevant.

Do loop index changing on its own

I have a couple hundred line program (including functions) in essentially free-form Fortran. At one point, I have a pair of nested do loops that call functions and store results in matrices. However, I don't believe any of that is the problem (although I could be wrong).
Immediately after the first do loop starts, I define an array using a column of another array. Immediately after that, the index is always set to 3. I haven't been able to find any useful information in the usual places. I've included a fragment of the code below.
do i = 1,n
print *, 'i:',i ! Gives i = 1
applyto = eig_vec(:,i)
print *, i ! Gives i = 3
state1 = create_state(ground,applyto,state,bin_state,num_s,ns)
first = destroy_state(ground,state1,state,bin_state,num_s,ns)
state1 = destroy_state(ground,applyto,state,bin_state,num_s,ns)
second = create_state(ground,state1,state,bin_state,num_s,
1 ns)
do j = 1,n
bra = eig_vec(:,j)
a_matrix(j,i) = sum(bra*first + bra*second)
matrix(j,i) = sum(bra*first - bra*second
end do
end do
Is this a bug? Am I missing something obvious? I am compiling the code with a high level of optimization, if that could potentially be a source of problems. I'm relatively new to Fortran, so debugging flags or commands (for gdb - I believe that's all I have available) would be welcome.

Fortran select a vector by indicating its name

In a program coded in F90, I have a set of 11 vectors, each of a size (7), with names going from "S1" to "S11".
I need to be able to read a number of elements from one vector, by giving the name of this latter.
Although this problem seems an elementary one, with my beginner level, I am unable to find a way to code it...
any help ?
When a Fortran program executes it doesn't really have the information available to identify a variable based on the value of a string containing the name of a variable. You could write a sequence of if statements such as
if (mystr=='S1') x = s1
if (mystr=='S2') x = s2
...
You could pretty this up a bit with a select case construction
select case (mystr)
case ('S1')
x = s1
case ('S2')
x = s2
...
but that may not appeal much more.
A better approach by far would be to declare your vectors as elements of a rank-2 array:
real, dimension(11,7) :: s
and you can then do all sorts of computations, at run-time, to select the vector you want
myrow = an_expression_returning_an_integer_between_1_and_11_inclusive
x = s(myrow,:)

Resources