Generating and passing real array to procedure with assumed shape [duplicate] - fortran

This question already has answers here:
Sum and Construct an Array in one statement
(1 answer)
Implied-do loops in fortran for accumulated-sum-like array
(2 answers)
Is it possible to initialize a Fortran parameter array with a loop expression?
(2 answers)
Closed last year.
I want to pass a real array to a subroutine. The array is a real array with a lower bound of 0.1 and an upper bound of 1.0
real, dimension(10) :: r = [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0]
The array is passed successfully with the way of explicit shape dummy array. Now I want to change the increment or step size in the array as
real, dimension(10) :: r = [0.10,0.11,0.12,0.13,0.14...,1.0]
Ideally, the increment should be small enough. Therefore, I do not see the option of passing the array explicitly. The other way apparently is using an assumed shape dummy array and I have seen that it would need an explicit interface.
But the original problem remains the same as how to create that type of array before passing to the procedure. I have seen few ways of using the do loop Fortran DO loop, warning to use integer only
, Fortran: do-loop with real-type argument. It seems to me that they are just creating the incrementation and not creating any array.
So how to create an array with 0.1 lower bound, 1.0 upper bound, and increment e.g. 0.01? Or each incremental value should be passed to the procedure by following the mentioned references? What is the way? I hope it could be asked on this forum but somehow I could not figured it out.

Related

Why do I always get the same result when using function and contains in fortran

I'm trying to do a basic calculation by calling a function using contains
Program main
implicit none
integer*8 Nmax,i
Parameter (Nmax=5)
real*8 x, f(Nmax), n
do i=1, Nmax
n=i
f=func(n,Nmax)
write(*,*) f(i)
end do
Contains
real*8 function func(x,Nmax)
integer*8 Nmax,i
real*8 x, f(Nmax)
do i=1, Nmax-1
f(i)=i**2d0-4d0*i-7d0
end do
end function
end program main
I get this result:
-9.255963134931783E+061
-9.255963134931783E+061
-9.255963134931783E+061
-9.255963134931783E+061
-9.255963134931783E+061
I think I'm making the wrong variable definitions. Thank you for your help.
There are multiple problems with your program.
First, you probably meant to write:
f(i)=func(n,Nmax)
in the main program. Without the subscript you assign the same value to each element of the array. You might think that explains the results, but it doesn't as you'd still see what you expect.
Another problem is highlighted by the following warning I get when I compile your code with Intel Fortran:
t.f90(14): warning #6178: The return value of this FUNCTION has not been defined. [FUNC]
real*8 function func(x,Nmax)
-------------------^
You never assign the value of func, so you get whatever garbage happens to be in the return register.
The function you have isn't really what you want, either. You probably want one that computes and returns a scalar (single) value and hence there is no need for an array inside func.
A third problem is that func is ignoring the n argument (which, contrary to convention, you have declared as a real.)
If you want a loop in the main program, have the function compute and return a single result based on the argument passed to it. There is no need to pass both the loop index and nmax each time. Other options, slightly more advanced, would be to keep the array assignment in the main program but do away with the loop there and either have the function return an array or make the function ELEMENTAL. I will leave it as an exercise for you once you figure out what you really intend here.
Lastly, I would discourage you from using nonstandard syntax such as "real*8". Please learn about KIND specifiers and the SELECTED_REAL_KIND intrinsic function.

Compile errors when declaring and iterating over an array

Could you please help me to understand the error in this Fortran code?
program NAME
implicit none
real :: i, j(i)
do i=1, 100
j(i)=2*i
write(*,*) i , j(i)
end do
pause
end program
There are 3 main errors in this snippet:
You are declaring the variable i as real, but trying to use it as a dimension of another variable and as a index of a do loop - two contexts in that a integer type variable are mandatory.
When you declare a fixed-size array in the scope of the main program, its shape must be known at compile time. Instead, you are referring to another variable, i, whose vale is not known at compile time. If you know the size of the array will be 100, declare it as j(100). If you don't want to hardcode the size everywhere, declare a constant and reference it.
Like:
integer, parameter :: n = 100
real :: j(n)
Or else, if you don't know the shape at compile time and want it to be decide at each program run, use a dynamic array (more info all over the web, e.g. here).
The pause statement was deleted from the language since like... more than 20 years ago.

Allocate Multiple Arrays with Single Shape

I am allocating several rank-3 arrays to be exactly the same shape, and I wonder whether it's possible to specify the shape only once. For example, I'm currently doing:
program main
implicit none
integer :: long_name_dimension_1 = 3
integer :: long_name_dimension_2 = 5
integer :: long_name_dimension_3 = 8
real, allocatable, dimension(:,:,:) :: A, B, C
allocate(A(long_name_dimension_1,long_name_dimension_2,long_name_dimension_3), &
B(long_name_dimension_1,long_name_dimension_2,long_name_dimension_3), &
C(long_name_dimension_1,long_name_dimension_2,long_name_dimension_3))
end program main
This is annoying to type, and it's difficult to immediately see that these arrays have the same shape. I could use mold or source, after allocating the first array, such as:
allocate(A(long_name_dimension_1,long_name_dimension_2,long_name_dimension_3))
allocate(B, source=A)
allocate(C, mold=A)
but I don't really like this either - perhaps because, in my mind, it should be one allocate statement.
I'm looking for syntax such as:
allocate( SHAPE :: (long_name_dimension_1,long_name_dimension_2,long_name_dimension_3), &
A, B, C)
which I've been unable to find. Does such a syntax (or something similar) exist?
Well, there is the syntax for the allocate statement in the last (2015) Fortran Standard:
The shape must follow each allocation term in allocation-list, so there isn't any syntax structure for declaring one common shape for all the variables, except with the SOURCE or MOLD options.
I could use mold or source, after allocating the first array, but I
don't really like this either - perhaps because, in my mind, it should
be one allocate statement.
Actually, you cannot define the shape of an array and reference it in the same statement. Later in this same chapter, it says:
source-expr shall not be allocated within the ALLOCATE statement in
which it appears; nor shall it depend on the value, bounds, deferred
type parameters, allocation status, or association status of any
allocate-object in that statement.
So, summing up, the closest thing to what you want is exactly what you don't like:
allocate(A(long_name_dimension_1,long_name_dimension_2,long_name_dimension_3))
allocate(B, C, mold=A)
Or source=A, if you want the contents of A to be copied on.
Edit:
What about this:
allocate(A, B, C, mold=reshape([integer ::], &
[long_name_dimension_1,long_name_dimension_2,long_name_dimension_3])
I just checked in Intel Fortran and confirmed it works. It seems strange that a zero-sized array can have any shape. I strongly believe it's not standard, though.
Fortran doesn't support allocating multiple arrays as you would like it to. You're just about stuck with the mold and source options to the allocate statement. You could write
allocate(A(long_name_dimension_1,long_name_dimension_2,long_name_dimension_3))
allocate(B,C,mold=A)
which saves a few keystrokes. You could save a few more by writing
allocate(A(long_name_dimension_1,long_name_dimension_2,long_name_dimension_3))
A = 1
B = A
C = A
albeit that this sets the values of the elements of B and C as well as their shapes.
I edited the code to deal with the issue #evets raised in a comment.

Passing scalars and array elements to a procedure expecting an array

I have some legacy Fortran 77 code which I'm trying to at least compile without warnings (without disabling warnings). There are subroutine calls that pass a scalar where subroutine expects an array, since the scalar is used as a size-1 array, this causes no problems. But with the Intel compiler, if I enable interface warnings I get this error:
error #8284: If the actual argument is scalar, the dummy argument shall be scalar unless the actual argument is of type character or is an element of an array that is not assumed shape, pointer, or polymorphic.
In some cases, I've tried to solve this by overloading the subroutine with array and scalar variants, but then I face a problem when the argument passed is an "an element of an array", which is detected as a scalar. Consider the following sample (tested with gfortran):
program a
integer n,dum(3)
interface readn
subroutine readn_s(n,m)
integer m,n
end subroutine
subroutine readn_a(n,m)
integer m,n(*)
end subroutine
end interface
call readn(n,1)
write(6,*) 'n=',n
call readn(dum,3)
write(6,*) 'dum=',dum
call readn(dum(2),2)
write(6,*) 'dum=',dum
end program
subroutine readn_s(n,m)
integer i,m,n
n=2
end subroutine
subroutine readn_a(n,m)
integer i,m,n(*)
do i=1,m
n(i)=1
end do
end subroutine
The readn(dum,3) call correctly uses readn_a, while the second one uses readn_s. The intended behaviour is that both should use readn_a. Indeed, if I replace both calls with readn_a everything is as expected.
Is it possible to have this working properly and use the "array" version of the overloaded routine when the actual argument is an array element? I've found out it works if I call the subroutine with readn(dum(2:),2), but I'm afraid that creates a temporary copy of the array...
Original problem:
file.f90
program a
integer n,dum(3)
call readn_a(n,1)
write(6,*) 'n=',n
call readn_a(dum,3)
write(6,*) 'dum=',dum
dum=3
call readn_a(dum(2),2)
write(6,*) 'dum=',dum
end program
file2.f90
subroutine readn_a(n,m)
integer i,m,n(*)
do i=1,m
n(i)=1
end do
end subroutine
Compile with gfortran -Wall file.f90 file2.f90 or ifort file.f90 file2.f90, all is fine and the output is the intended:
n= 1
dum= 1 1 1
dum= 3 1 1
Compile with ifort -warn all file.f90 file2.f90 and I get the error #8284 above. So that's why I wanted a version of the subroutine that would work with scalars or arrays... but would give the array version with an array element.
In your attempted solution the type-kind-rank generic resolution will delegate all array elements to the scalar version of the subroutine and that will not do the intended work on the part of the array. So "The intended behaviour is that both should use readn_a." is not really possible with the approach you chose.
Well, it is possible when you rewrite the code to pass array sections as you noted yourself. But we are again at the same problem as we were before, you example is simplified. We can be certain there there is no temporary array in the example you have shown, we definitely can't say that about your real code. If you use some 2D subsections starting at some random place, you will have temporary arrays for sure and it might be difficult to make the correct subsection at all.
The original warning should be reasonably easy to avoid in your original legacy code. FORTRAN 77 has no non-contiguous arrays so if you keep your arguments to be integer :: a(*) instead of integer :: a and pass that further. If your original variable is scalar, that is a problem.
The problem is that an integer scalar does not constitute an element sequence:
F2008 12.5.2.11 1 An actual argument represents an element sequence if
it is an array expression, an array element designator, a default
character scalar, or a scalar of type character with the C character
kind (15.2.2). ...
An array element designator a(2) is an element sequence but the scalar n is not.
So it is not allowed for sequence association which is used when an array element is passed:
F2008 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. ...
Your code is not strictly standard Fortran conforming, but very likely to work as expected if you manage to compile it.
Possible solutions:
You can pass the argument as an array expression, but the argument may not be modified inside the subroutine
call readn_a([n],1)
You can just disable the warnings about interface matching
ifort -warn nointerfaces
You can create separate subroutines which only work for scalars and call it under some different name.
You can also disable argument checking for that dummy argument by an Intel Compiler directive
!DEC$ ATTRIBUTES NO_ARG_CHECK :: dummy-arg-name

Pointers in Fortran

Im unsure how the below 2 in C can be represented in Fortran.
*VariableOne (Could be used possibly by using 'inout intent' in functions?
&VariableOne (No clue about how I could do this though)
Could someone tell me how I might be able to replicate this in Fortran? I looked up Pointers in Fortran but couldnt clearly understand what relates to the above two. I was hoping someone here would show me an equivalent to help me understand.
You seem to be confusing two different concepts. Values in Fortran act as though they're passed by reference so you can modify a variable within a function and have its change reflected outside. The intent statements are there to specify what you're going to do with that dummy variable. intent(in) doesn't allow you to change that variable, intent(out) you use it to pass data out of a function and its value is undefined on entry, and intent(inout) means that it can both provide data to the function (not undefined on entry) and write to it as well.
But there are also explicit pointers you can use to dynamically allocate memory and act similarly to pointers in C.
Pointers in Fortran can't point to just anything. What they point to must have the target attribute or be a pointer. You also don't need to explicitly dereference a pointer to access its value. Here's a short example of what you can do with pointers and how they work:
real, pointer :: ptr(:) => null() !Can point to an array of reals or be allocated
real, target, allocatable :: trgt(:) !Array of reals
real, allocatable :: array(:)
real, pointer :: scalar => null() !Initially points to null to signify that it's not associated
!I can check if the pointer currently points to anything by using the associated function
!Be careful: the initial associaton status is undefined so you must set it to null or nullify it first
if (associated(ptr)) then
nullify(ptr) !Redundant in this example, same as setting it equal to null()
end if
allocate(ptr(3)) !I can allocate a pointer directly with memory for 3 elements
allocate(trgt(5)) !Allocate the target with 5 elements
allocate(array(6)) !Allocate array
!Assign values to my freshly allocated variables
ptr = 1.0
trgt = 3.0
array = 5.0
deallocate(ptr) !Avoid memory leaks by deallocating memory before pointing to something else
ptr => trgt !Pointer now points to the data stored in trgt. No need to use anything like '&' to reference trgt
ptr => array !Error because array does not have target attribute
scalar => ptr(1) !Both are pointers so no error, now points to a value of 3.0
print *, scalar + 5.0 !I can use this value directly without dereferencing it and prints 8.0