Fortran 90 data statement does not overwrite data - arrays

I am trying to initialize an array using DATA statement in Fortran 90. The code is following:
PROGRAM dt_state
IMPLICIT NONE
INTEGER :: a(4), b(2:2), c(10)
DATA a/4*0/
WRITE (6,*) a(:)
DATA a/4,3,2,1/
WRITE (6,*) a(:)
END PROGRAM dt_state
I expected that results on the screen will be 0 0 0 0 and 4 3 2 1. However, what I got is 0 0 0 0 and 0 0 0 0. It means DATA statement does not overwrite the values of a, does it?

A variable can appear in a DATA statement only once. The DATA statement is for initialization, which is done only once at program start.
In executable code use assignment to set array values
a = (/ 4, 3, 2, 1 /)
(in Fortran 90)
or
a = [ 4, 3, 2, 1 ]
(in Fortran 2003).
It is better to use this syntax also for initialization.

Your code is not standard-compliant. That is: from F2008 5.2.3:
A variable, or part of a variable, shall not be explicitly initialized more than once in a program.
The DATA statement (is one thing that) performs such explicit initialization (5.4.7.1), and so in particular two cannot appear for the same variable.
For your second "initialization", use assignment. [As given by #VladimirF who is a faster typist than I.] Further, while one can put a DATA statement with executable statements, as in this case, the standard goes as far as making that obsolescent (B.2.5):
The ability to position DATA statements amongst executable statements is very rarely used, unnecessary, and a potential source of error.
As the code is non-standard, and the error is not one the compiler is required to detect, the compiler is free to whatever it likes with the code. I thought it would be interesting to see what we do see with a small selection:
One refused to compile (and also pointed out the obsolesence);
Two quietly went ahead, using the first initialization;
One used the second initialization, warning.
Of course, one wouldn't want to rely on any of these behaviours, even if it was a desired one.

Related

How do I generate a string array in MatLab 2016b? [duplicate]

When trying to run my code, for example
for ii= 1:10
output(ii)=rand(3);
end
I get the error
In an assignment A(:) = B, the number of elements in A and B must be the same
or
In an assignment A(I) = B, the number of elements in B and I must be the same.
What does this error mean? What is the approach to get rid of it?
This error comes because you are trying to fill a variable chunk with more (or less) values than its size. In other words, you have a statement A(:)=B on where size(A(:)) is different to size(B).
In the example in the question, rand(3) returns a 3x3 matrix, however, output(ii) is just a single value (even if output may be bigger, output(ii) is just a single value of output), thus the value returned by rand(3) does not fit inside output.
In order to solve this problem, you need to change the the size of the output variable, so you have space to fit all the result.
There are 2 ways of doing this. One of them is by creating a Matrix that fits the return, e.g. output=zeros(3,3,10).
Then we can change the code to
for ii= 1:10
output(:,:,ii)=rand(3);
end
Alternatively, you can fill the output as a cell array. This is particularly useful when the return of the function changes sizes each time, e.g. rand(ii);
In that case, the following would work
for ii= 1:10
output{ii}=rand(ii);
end
It is probable that unlike in the example in the question, in the real case you do not know the size of what the output returns, thus you do not know which of the two options to use to fix your code.
On possible way of learning that, is activating debugging help when the code errors, by typing dbstop if error in your command line. This will trigger a debugging stop when MATLAB throws an error, and you can type size(rand(ii)) and size(output(ii)) to see the sizes of both.
Often, reading the documentation of the function being used also helps, to see if different sizes are possible.
That said, the second option, cell arrays, will always ensure everything will fit. However matrices are generally a faster and easier to use in MATLAB, thus you should aim for the matrix based solution if you can.

Fortran 90 - Algebra operation with scalar and arrays

I am working with a Fortran 90 program that, amongst many others, has the following variables declared:
real(r8) :: smp_node_lf
real(r8), pointer :: sucsat(:,:)
real(r8), pointer :: h2osoi_vol(:,:)
real(r8), pointer :: watsat(:,:)
real(r8), pointer :: bsw(:,:)
And at some point in the program, there is an algebra operation that looks like this:
do j = 1,nlevgrnd
do c = 1,fn
...
smp_node_lf = -sucsat(c,j)*(h2osoi_vol(c,j)/watsat(c,j))**(-bsw(c,j))
...
end do
end do
I am trying to "translate" a dozen lines of this program to R, but the above excerpt in particular made me confused.
What is the dimension of smp_node_lf? Is it an scalar? Does it inherit the dimensions of the arrays sucsat,h2osoi_vol,watsat and bsw?
There is a lack of dimensions for smp_node_lf because it is a scalar, and it is receiving the value of that scalar operation multiple times, being rewritten, if there is nothing to save its value to a vector or something.
It will never inherit the dimensions of any of those elements, there is never a vector to be inherited, everything it is receiving is scalar
if you have to retrieve its value, assuming the original code is capable of it as it is, there should be another part inside this very loop that saves that value before it is overwritten by another pass.
If there is not such thing, implement it, you might be dealing with incomplete code that does nothing it was said to do.
I've dealt with my fair share of "perfect code" that "did miracles when I used last time" with not a single miracle to be found within its lines of code.

Initilalising an array with a sequence in Fortran

I am currently working on translating some legacy fortran code and I am having a hard time understanding a particular line in the code. The compiler also seems to find this line weird and throws out an error. From what I understand it is trying to initialize an array by sequencing 1 to 9 by increments of 1 and filling up the array matrix with this sequence in column major form.
program arrayProg
integer :: matrix(3,3), i , j !two dimensional real array
matrix = reshape((/1:9:1/), (/3,3/))
end program arrayProg
Is this syntax acceptable in fortran? (It has to be because it comes from the legacy code)
Am I misunderstanding what the line does?
The syntax is incorrect and such code cannot be compiled by a Fortran compiler, unless it implements some non-standard extension.
Intel Fortran accepts this:
A colon-separated triplet (instead of an implied-DO loop) to specify a range of values and a stride; for example, the following two array constructors are equivalent:
1 INTEGER D(3)
2 D = (/1:5:2/) ! Triplet form - also [1:5:2]
3 D = (/(I, I=1, 5, 2)/) ! implied-DO loop form
from Development Reference Guides:Array Constructors
(note: The links to Intel Documentation change frequently, if the link is dead, please notify me in the comment and try searching for "triplet form" and "array constructors")
To generate a sequence in a standard way one uses an implied do loop like
(/ (i, i=1,9) /)
the reshape then just changes the 1D array into a 2D one in column major order as you guessed.

In an assignment A(:) = B, the number of elements in A and B must be the same

When trying to run my code, for example
for ii= 1:10
output(ii)=rand(3);
end
I get the error
In an assignment A(:) = B, the number of elements in A and B must be the same
or
In an assignment A(I) = B, the number of elements in B and I must be the same.
What does this error mean? What is the approach to get rid of it?
This error comes because you are trying to fill a variable chunk with more (or less) values than its size. In other words, you have a statement A(:)=B on where size(A(:)) is different to size(B).
In the example in the question, rand(3) returns a 3x3 matrix, however, output(ii) is just a single value (even if output may be bigger, output(ii) is just a single value of output), thus the value returned by rand(3) does not fit inside output.
In order to solve this problem, you need to change the the size of the output variable, so you have space to fit all the result.
There are 2 ways of doing this. One of them is by creating a Matrix that fits the return, e.g. output=zeros(3,3,10).
Then we can change the code to
for ii= 1:10
output(:,:,ii)=rand(3);
end
Alternatively, you can fill the output as a cell array. This is particularly useful when the return of the function changes sizes each time, e.g. rand(ii);
In that case, the following would work
for ii= 1:10
output{ii}=rand(ii);
end
It is probable that unlike in the example in the question, in the real case you do not know the size of what the output returns, thus you do not know which of the two options to use to fix your code.
On possible way of learning that, is activating debugging help when the code errors, by typing dbstop if error in your command line. This will trigger a debugging stop when MATLAB throws an error, and you can type size(rand(ii)) and size(output(ii)) to see the sizes of both.
Often, reading the documentation of the function being used also helps, to see if different sizes are possible.
That said, the second option, cell arrays, will always ensure everything will fit. However matrices are generally a faster and easier to use in MATLAB, thus you should aim for the matrix based solution if you can.

Subscripts going out of range in an array

Here the subscripts of the array are out of range.
int a[10], i;
for (i = 1; i <= 10; i++)
a[i] = 0;
printf("India");
The output is an infinte loop and printf statment doesnt get executed. here's what written in K N King:
When i reaches 10. the program stores 0 into a [10] . But a [10] doesn’t exist.
so 0 goes into memory immediately after a [9] . If the variable i happens to follow
a [9] in memory—as might be the case—then i will be reset to O. causing the
loop to start over.
Can anyone explain it ?
The idea that this will form an infinite loop is based on an assumption about how the variables will be laid out in memory. In particular, it assumes that because i is defined immediately after a, that it will also be allocated in memory immediately after a.
That's certainly not guaranteed, but it equally certainly could happen. If it does, then the write to a[10] may actually overwrite i. Since it's writing 0 into the nonexistent a[10], doing so actually writes 0 into i. Then when the condition in the loop checks that i <= 10, that's true, so the loop continues -- and each time i gets to 10, it's immediately overwritten with 0 before the loop condition is evaluated, so the loop re-starts from the beginning.
As far as either the C or C++ standard cares, it's just undefined behavior--when the code writes past the end of the array anything can happen. It might do what somebody expects, or might might do something entirely different and unrelated that doesn't seem to make sense at all. The compiler is free to emit code that does pretty much anything in such a circumstance (or it could, for example, diagnose it as an error, and emit no code at all).
To give some idea of what conforming behavior could be: early versions of gcc had code to detect a specific case of implementation-defined behavior (pretty much like undefined behavior, except the implementation has to document what it does). In this case, the documented behavior was fairly complex. The compiler would attempt to do each of the following in order (and stop at the first one that succeeded):
run nethack (a game)
run rogue (another game)
start emacs, and have it execute a towers of hanoi simulation
print out "You are in a maze of twisty little passages, all alike".
I could be getting the order of those a bit wrong (this was a long time ago), but you get the idea. The result had nothing to do with anything a reasonable person would be likely to expect.

Resources