Resizing of Multi-Dimensional Arrays when Passed - arrays

All references to arrays in this post are multi-dimensional.
I came to know that when an array is passed a subroutine, it can be declared with different dimensions/sizes as the caller.
As a specific example, BMAIN is declared with DIMENSION(6,5) in the main program. BMAIN is passed to a subroutine as BSUB, which is declared as:
INTEGER, INTENT(IN) :: BSUB(3,2,0:4)
Questions:
Are the entries in BSUB simply filled one by one from SBMAIN until it is filled (in the order explained here Linear Indexing of Multi-Dimension Arrays in Fortran)?
Are there any dimension matching performed by the compiler? For instance, had BSUB been declared as BSUB(0:4,3,2), would it still hold the correct entries in the correct places?

From the Fortran 2008 standard (12.5.2.11.4):
An actual argument that represents an element sequence and
corresponds to a dummy argument that is an array is sequence
associated with the dummy argument if the dummy argument is an
explicit-shape or assumed-size array. The rank and shape of the actual
argument need not agree with the rank and shape of the dummy argument,
but the number of elements in the dummy argument shall not exceed the
number of elements in the element sequence of the actual argument. If
the dummy argument is assumed-size, the number of elements in the
dummy argument is exactly the number of elements in the element
sequence.
It is completely legal to use a different shape and rank of the array in the subroutine, just do not reference more elements in the subroutine than the array actually has. Array temporary may be necessary, but often it is not.

Multi-dimensional arrays in Fortran are stored in column-major order. In memory, they the elements are linear order and the memory offset is calculated from the multi-dimensional indices. The equation is given at http://en.wikipedia.org/wiki/Row-major_order. The Fortran compiler will use that equation and calculate an position in memory based on the dimensions that you provide. To figure out the correspondence between elements when you declare a multi-dimensional array with different dimensions, apply the equation twice using the different dimensions. The compiler doesn't move values in memory. It calculates position in memory from the index values, based on the dimensions.
There are cases where the code generated by the Fortran compiler will copy values, creating a temporary array. e.g., if the actual array argument in the call involves a stride, the compiler might need to create a contiguous temporary array to be compatible with the argument of the subroutine.

Related

Passing subarray of 2-dimensional array in FORTRAN-77

I have 2-dimensional array
real triangle(0:2, 0:1)
where "triangle" is an array of vectors (1-dim arrays)
also i have subroutine
subroutine vecSub(lhs, rhs, result)
real lhs(0:1), rhs(0:1), result(0:1)
result(0) = lhs(0) - rhs(0)
result(1) = lhs(1) - rhs(1)
return
end
is there any way to pass one of the vectors from "triangle" variable to this subroutine? Fortran-90 can do this: triangle(0, :) which gives first array of triangle, but i'm allowed to use only FORTRAN-77, so this won't do, any suggestions?
#Javier Martin wrote "not with the current layout of your array", but missed the opportunity to suggest an alternative.
If instead you declared the variable as follows:
real triangle(0:1, 0:2)
reversing the order of the bounds, you could then pass triangle(0,0), triangle(0,1) or triangle(0,2) to the subroutine and get exactly the behavior you want, due to a Fortran feature called "sequence association". When you pass a single array element to a dummy argument that is an array, you are implicitly passing that and following elements, in array element order. This is about the only allowed violation of the normal Fortran shape-matching rules, and was part of FORTRAN 77.
No, not with the current layout of your array, because of two reasons:
Fortran uses an array element order in which the leftmost dimension is contiguous. That is, in an array of size (n,m,l) the distance between elements (the stride) is (1,n,m), measured in units of array elements (that is, not bytes).
F77 does not include assumed-shape arrays a(:) which are generally implemented by passing a small descriptor structure that communicates details like the stride or the number of elements. Instead, you can only use assumed-length arrays a(*) which are normally a pointer to the first element, kind of like C arrays. You have to pass the length as a separate argument, and array elements have to be contiguous
This is the reason why you can "pass a subarray" to an F77 subroutine, as long as that subarray is e.g. a matrix column: elements therein are contiguous.
A possible solution (one that many current Fortran compilers implement) is that when you try to pass a non-contiguous subarray to a function that is not known to accept them, they make a copy of the array, and even write it back in memory if required. This would be equivalent to:
! Actual array
integer m(3,5)
integer dummy(5)
dummy = m(2,:)
call myF77sub(dummy, 5)
m(2,:) = dummy
However, as others are saying, you should try not to call F77 functions directly, but either adapt them to or at least wrap them in more recent Fortran interfaces. Then you can have code like the above in the wrapper, and call that wrapper "normally" from modern Fortran routines. Then you may eventually get around to rewriting the actual implementation in modern Fortran without affecting client code.

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.

Fortran: Clearing defined array when leaving subroutine

I am doing a sensitivity analysis and thus need to run my program many times with different parameters.
I do have a main program which calls the subroutine that calculates the values I need. Thus I need to call the subroutine many times.
The subroutine has some arrays defined as:
real, dimension(1000) :: array_1, array_2
After leaving the array the program does not free the array, thus for two calls of the function I need to write:
real, dimension(2000) :: array_1, array_2
Is there an easy solution for this ?
Thank you
Let me restate your problem, just to make sure I understand it correctly; please correct me if I'm wrong.
You have a subroutine that calculates 1000 values in two separate arrays: array_1 and array_2.
From your main program you would like to call this subroutine many times, each time generating a unique sets of 1000 values.
Let's say you want to call your subroutine N times, do you want to compare the N sets of numbers out side of the subroutine, ie in your main program, or do you want to do the comparison inside the subroutine?
Either way, I declare the arrays like so:
real, dimension(N,1000) :: array_1, array_2
If you would like to do the comparison outside the subroutine, you will make the above declaration in the main program. Then you will call the subroutine N times, and after each i'th time copy the values into the main's array_1(i,*) and array_2(i,*). In this, the subroutine will only have the arrays defined like so:
real, dimension(1000) :: array_1, array_2
Each time the subroutine is called, it will reuse these arrays, overwriting the previous values. This should be OK if you recorded the previous values in the main's arrays.
If you are doing this in f90, you can dynamically allocate array_1 and array_2 and let N be variable length. Alternatively, you can allocate the arrays initially with enough space to store all possible calls to the subroutine. Lets say you are not going to run more than 100 calls to the subroutine:
real array_1(100,1000), array_2(100,1000)
I'm sorry if this is not what you are looking for, but please clarify if I miss understood your question.
If you have many arrays with essentially the same data, naming them array, array_1, array_2, etc. will soon become awkward, as you have realized. Two other possible designs are: 1) use one array and process the data in a loop. After one group of data is processed, read in the next group and overwrite the previous data in the same array. 2) Use a two-dimensional array as suggested by #Yann, with the second dimension being the number of datasets / different sets of parameters values.
If the arrays of each dataset have different lengths, for solution 1 you could declare the array "allocatable" and allocate it to the correct length at the start of each iteration, then deallocate at the end of each iteration. For 2 it would probably be easiest (but not most memory efficient) to make the length the maximum length and have an auxiliary array specifying the used length.
P.S. Are these arrays local to the subroutine? Then if you are passing the length to the subroutine as an argument, e.g., "len", you can declare the arrays as:
integer, intent (in) :: len
real, dimension(len) :: array_1, array_2
and the arrays will be recreated when the subroutine is re-invoked with the particular length "len". Inside a procedure (subroutine or function), and if the length is passed as an argument, you can get a variable length array very simply. You can even obtain the length using the size function applied to an array argument! In the main program, or it the length is not known as a constant or argument to a procedure, then you can defer the size of the array by giving it the "allocatable" attribute in the declaration and dynamically allocate the array with the "allocate" statement.

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