Fortran 90 - Algebra operation with scalar and arrays - 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.

Related

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.

How to concatenate two arrays in Fortran 90

I have an original array called pres_lev3d, whose size is defined by pres_lev3d(im*jm, levsi), where im*jm is 72960 and levsi is 64. This corresponds to global atmospheric data, thus the size. The array is allocatable: real (kind=kind_io8), allocatable :: pres_lev3d(:, :). I have a second array, press_1d, whose size is also defined in a similar fashion pres_1d(im*jm, levsi), but in this array levsi is 1.
I need to concatenate both arrays (technically a 2d and 1d array) to an array of the shape (/72960, 65/). In MATLAB this seems like a very simple process, however, I can't seem to find an easy way to go around it in Fortran 90.
I have tried to create a third array
pres_lev=(/pres_lev3d, pres_1d/)
and also tried to use merge, but none of these approaches seem to work out.
I am fairly new to Fortran.
If I've followed your explanation correctly this would probably work
real(kind_io8), dimension(72960,65) :: out_array
...
out_array(:,1:64) = pres_lev3d
out_array(:,65) = pres_1d
If that's not easy enough, or if I've misunderstood your question, explain further. To allocate out_array to conform to your input arrays, try something like
real(kind_io8), dimension(:,:), allocatable :: out_array
...
allocate(out_array(size(pres_lev3d,1),size(pres_lev3d,2)+1))
...
out_array(:,1:64) = pres_lev3d
out_array(:,65) = pres_1d

How can a scalar be passed to a vector (1D array) to a Fortran subroutine?

There is this program:
INTEGER i,k
REAL*8 mp(15,48)
REAL*8 sp(15)
k=0
do i=1,12
k=k+1
call Equaltensors(sp,mp(1,k),15)
enddo
end
c=====================
subroutine Equaltensors(tensA,tensB,n)
REAL*8 tensA(n),tensB(n)
INTEGER i
do i=1,n
tensB(i)=tensA(i)
enddo
return
end
So basically the value of mp(1,1) and so on is passed to the subroutine as a vector tensB(15) with n=15. What I don't understand is how a real number can be stored in a one-dimension array in a subroutine.
The title of your question is a bit misleading. Fortran doesn't allow you to pass a scalar to an array. But what it DOES allow is passing a single element of an array to a routine's array dummy argument - this is called "sequence association" in Fortran. As IanH and others have said, the following elements are automatically associated with the elements of the dummy array, up to the last element in the called routine's actual array.
There are some restrictions on this feature, though. If the element is of a POINTER array,you can't do this.
Going back to your title, I have seen many programs pass, say, the constant 3 to a routine where the dummy is an array. The routine only uses the first element, but this is not legal and newer compilers may detect the error and complain. One workaround for this is to turn the argument into an array by using an array constructor - for example, CALL FOO ([3]), but this works only if the value is to be read, not written.
I've written some blog posts on this general issue - see Doctor Fortran in “I’ve Come Here For An Argument” and Doctor Fortran in “I’ve Come Here For An Argument, Side 2”
EDIT: corrected per the comment by IanH, who points out that the behavior is guaranteed without making assumptions about the argument passing convention.
This approach started in early FORTRAN, by assuming that the argument is being passed as an address, typically called "call by reference". The address of scaler mp(1,k) is the address of the first element of this column k. Since Fortran stores arrays in column major format (http://en.wikipedia.org/wiki/Row-major_order#Column-major_order), the 15 values of the kth column will be sequential in memory. So if the called subroutine interprets this address as that of a 1-D array tensB of length 15, it will access the elements of the kth column.
In modern Fortran one could write the argument in a clearer manner by selecting a column with an array slice: mp (:,k).

Passing c arrays into fortran as a variable sized matrix

So, i've been commissioned to translate some fortran subroutines into C. These subroutines are being called as part of the control flow of a large porgram based primarily in C.
I am translating the functions one at a time, starting with the functions that are found at the top of call stacks.
The problem I am facing is the hand-off of array data from C to fortran.
Suppose we have declared an array in c as
int* someCArray = (int*)malloc( 50 * 4 * sizeof(int) );
Now, this array needs to be passed down into a fortran subroutine to be filled with data
someFortranFunc( someCArray, someOtherParams );
when the array arrives in fortran land, it is declared as a variable sized matrix as such:
subroutine somefortranfunc(somecarray,someotherparams)
integer somefarray(50,*)
The problem is that fortran doesn't seem to size the array correctly, becuase the program seg-faults. When I debug the program, I find that indexing to
somefarray(1,2)
reports that this is an invalid index. Any references to any items in the first column work fine, but there is only one available column in the array when it arrives in fortran.
I can't really change the fact that this is a variable sized array in fortran. Can anyone explain what is happening here, and is there a way that I can mitigate the problem from the C side of things?
[edit]
By the way, the fortran subroutine is being called from the replaced fortran code as
integer somedatastorage(plentybignumber)
integer someindex
...
call somefarray(somedatastorage(someindex))
where the data storage is a large 1d array. There isn't a problem with overrunning the size of the data storage. Somehow, though, the difference between passing the C array and the fortran (sub)array is causing a difference in the fortran subroutine.
Thanks!
Have you considered the Fortran ISO C Binding? I've had very good results with it to interface Fortran and C in both directions. My preference is to avoid rewriting existing, tested code. There are a few types that can't be transferred with the current version of the ISO C Binding, so a translation might be necessary.
What it shouldn't be that others suggested:
1. Size of int vs. size of Integer. Since the first column has the right values.
2. Row vs. column ordering. Would just get values in wrong order not segmentation faulted.
3. Reference vs value passing. Since the first column has the right values. Unless the compiler is doing something evil behind your back.
Are you sure you don't do this in some secret way?:
someCArray++
print out the value of the someCArray pointer right after you make it and right before you pass it. You also should print it out using the debugger in the fortran code just to verify that the compiler is not generating some temporary copies to help you.

Arrays and derived types

For my new project, I have to use an array instead of a scratch file to store information from users. To do this, I need to create derived types, too.
However, I haven't understood what an array is and what a derived type is, how to use them, what they can do, and some other basic ideas.
Can anyone give me some information about array and derived types?
I wrote code for them, but I don't know it is written correctly.
If anyone can check this for me, I would appreciate it.
Here are my array and derived types:
! derived type
TYPE Bank
INTEGER :: acNumber, acChecks
REAL :: acBlance, acRate
CHARACTER :: acType*1, acLName*15, acFName*15
END TYPE
! array
INTEGER, PARAMETER :: MaxRow, MaxColum = 7
INTEGER, DIMENSION(MaxRow:MaxColum) :: AccountData
If you are a fortran programmer you have probably seen a subroutine accepting 10/15 arguments. If you think about it, it's insane (they are too many, you run the risk of swapping them) and you quickly realize that some arguments always travel together.
It would make sense to pack them under a single entity that carries everything around as a whole, non as independent entities. This would reduce the number of arguments considerably, giving you only the burden to find proper association. This single entity is the type.
In your code, you say that a Bank is an aggregate of those informations. You can now declare a concrete variable of that type, which will represent and provide access to the single variables acNumber, acChecks and so on. In order to do so, you have to use the % symbol. so if your bank variable is called b, you can say for example
b%acNumber = 5
You can imagine b as a closet, containing different shelves. You move the closed, all the shelves and their content move together.
An array is a group of entities of the same type (say, integer, or Character(len=1024), or Bank) and they are one after another so you can access each of them with a numeric index. Remember that, unless specified differently, arrays indexes in fortran start at 1 (in all the other major languages, the first index is zero instead)
As for your code, I suggest you to:
write
INTEGER, DIMENSION(MaxRow:MaxColum) :: AccountData
as
INTEGER :: AccountData(MaxRow,MaxColum)
it is the same, but you write less. Please also note that there is a difference between using the : and the ,. If you want to define a matrix (your case), which is a two-dimension array, you have to use the comma. What you wrote is wrong.
for the strings, it's better if you write
CHARACTER :: acType*1, acLName*15, acFName*15
as
CHARACTER(LEN=1) :: acType
CHARACTER(LEN=15) :: acLName
CHARACTER(LEN=15) :: acFName
in this case, you write more, but your syntax is deprecated (I could be wrong, though)
Also, remember that it's better if you write one member variable per line in the types. It's a matter of taste, but I prefer to see the full size of a type by having one line per member variable.
For MaxRows and MaxColumns, I would write them as MAX_ROWS and MAX_COLUMNS. Parameters and stuff that is highly constant by tradition is identified with an all capital, underscore separated name in any major language.
Edit: to answer your comment, here is an example of the use of an array
$ more foo.f90
program test
integer :: myarray(10)
myarray = 0 ! equivalent to zeroing the single elements one by one
myarray(2) = 5
myarray(7) = 10
print *, myarray
end program
$ g95 foo.f90 -o foo
$ ./foo
0 5 0 0 0 0 10 0 0 0
an array is just like multiple variables with the same name, identified by an index. Very useful to express vectors, or matrices.
You can of course do an array of an aggregated type you define, instead of a predefined type (eg. integer).
An array is an ordered list of variables, all of the same type, indexed by integers. See Array in Wikipedia Note that in Fortran array indexing is more flexible than most other low level languages, in that instead of a single index per dimension, you can have an index triplet consisting of lower bound, upper bound, and stride. In that case the lvalue of the expression is a subarray rather than a single element of the array type.
A derived type is a composite type defined by the users, which is made up of multiple components which can be of different types. In some other languages these are knows as structs, structure types, or record types. See Record in Wikipedia
You can also make an array of a derived type, or you can have a derived type where one or more components are themselves arrays, or for that matter, other derived types. It's up to you!
The easiest way to check your code is to try to compile it. Making it past the compiler is of course no guarantee that the program works as expected, but it certainly is a required step.

Resources