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
Related
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.
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.
I have a 4-dimensional array in Fortran called covi It is declared as:
real, dimension(10,10,2,2) :: covi
and computed in a certain way.
At some point, I need to take 2-dimensional slices of that array, lke for example covi(1,1,:,:), covi(3,4,:,:),covi(7,5,:,:), etc.
I have declared another variable real, dimension(2,2) :: cov and written cov=covi(7,5,:,:) but this is not working. It gives the following error message:
error #6366: The shapes of the array expressions do not conform. [COVI]
covi = cov(i1,i2,:,:)
In Matlab this notation works perfectly. It is pretty intuitive. How does one achieve this in Fortran?
Thanks
If I want to create an allocatable multidimensional array, I can say:
program test
real, dimension(:,:), allocatable :: x
integer :: i,j
allocate(x(5, 5))
do i = 1,size(x,1)
do j = 1,size(x,2)
x(i,j) = i*j
end do
end do
write(*,*) x
end program test
However, what if I don't know how many dimension x will be. Is there a way to accommodate that?
Newer compilers allow the use of assumed-rank objects for interoperability.
I think that is what you are looking for. But this is for call to functions or subroutines. The function or subroutine declares the dummy argument as assumed-rank and the actual rank is passed with the actual argument at runtime.
Example from IBM website:
REAL :: a0
REAL :: a1(10)
REAL :: a2(10, 20)
REAL, POINTER :: a3(:,:,:)
CALL sub1(a0)
CALL sub1(a1)
CALL sub1(a2)
CALL sub1(a3)
CONTAINS
SUBROUTINE sub1(a)
REAL :: a(..)
PRINT *, RANK(a)
END
END
follow this or that for more details
It looks to me like you're trying to carry out stencil computations across an array of rank-1, -2 or -3 -- this isn't quite the same as needing arrays of arbitrary rank. And assumed-rank arrays are really only applicable when passing an array argument to a routine, there's no mechanism even in the forthcoming standard for declaring an array to have a rank determined at run-time.
If you're impatient to get on with your code and your compiler doesn't yet implement TS 29113:2012 perhaps the following approach will appeal to you.
real, dimension(:,:,:), allocatable :: voltage_field
if (nd == 1) allocate(voltage_field(nx,1,1))
if (nd == 2) allocate(voltage_field(nx,ny,1))
if (nd == 3) allocate(voltage_field(nx,ny,nz))
Your current approach faces the problem of not knowing, in advance of knowing the number of dimensions in the field, how many nearest-neighbours to consider in the stencil, so you might find yourself writing 3 versions of each stencil update. If you simply abuse a rank-3 array of size nx*1*1 to represent a 1D problem (mutatis mutandis a 2D problem) you always have 3 sets of nearest-neighbours in each stencil calculation. It's just that in the flattened dimensions the nearest neighbour is, well, either a ghost cell containing a boundary value, or the cell itself if your space wraps round.
Writing your code to work always in 3 dimensions but to make no assumptions about the extent of at least two of them will, I think, be easier than writing rank-sensitive code. But I haven't given the matter a lot of thought and I haven't really thought too much about its impact on your f-d scheme.
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.