Passing parameters to a subroutine in Fortran - fortran

I'm trying to understand how to pass some variables with values, to a subroutine, from another subroutine.
I have the code:
program pg
call sub()
end
subroutine sub()
character :: P(12,12)
character :: D(20), N(26), userInput
integer :: A,B
A = 5
B = 2
userInput = "hello"
call test(P,D,N,userInput,A,B)
end subroutine
subroutine test(P,D,N,userInput,A,B)
end subroutine test
But that gives me a warning of:
Warning: Type mismatch in argument 'p' at (1); passed CHARACTER(1) to REAL(4)
I'm not exactly sure on how to get rid of this warning, let alone if the subroutine call is correct (syntax wise). Any help would be much appreciated.

First, why this error is happening: You have not explicitly declared any variables in subroutine test so by default, the IMPLICIT variable type rules are applied - variables starting with [A-H,O-Z] are treated as single-precision reals (REAL(4)) and the remainder (starting with [I-N]) are integers. In this case, the real arguments to test are P, D, userInput, A, and B; N is treated as an integer. Also these are all treated as scalar quantities since no dimension information is provided in test.
In subroutine sub, the variable P is declared as a 12-by-12 array of length-1 strings (single characters or CHARACTER(1)). This explains why you're getting the type mismatch error.
Advice: Read up on the IMPLICIT statement and start your code with IMPLICIT NONE, always. This forces you to declare all your variables. This slight inconvenience allows the compiler to catch typos and namespace collisions and is considered best practice. It won't solve your next problem (P, D, and N are arrays passed in as arguments to a routine which expects them to be scalars) but it will get rid of the type mismatch error.
For bonus points, look into the INTENT attribute for argument declarations. If you know that certain arguments are read-only or will only have their values set within a routine, you can declare them as INTENT(IN) or INTENT(OUT). This is an added guard against accidentally changing read-only variables or reading variables before they've been initialized.

Related

How to use intent (inout) in a Fortran function

I'm fairly new to Fortran, and while going through some documentation it seems like both subroutines and functions can use the 3 types of parameter intent:
intent (in)
intent (out)
intent (inout)
But when trying to implement intent (inout) in a function something similar to the snippet shown below
function cell_blocked(row,col,ncb) result (ncb)
integer, intent(in) :: row,col
integer, intent(inout):: ncb
if (row == col) then
ncb = ncb + 1
end if
end function cell_blocked
I get the following compiler error:
Symbol 'cell_blocked' at (1) has no IMPLICIT type
ncb is a counter that I would like to pass into the function and update given a condition and return the value.
If I try this with a subroutine I don't get any compiler errors, so I'm confused why the error while using a function
You are quite correct that each of the three intents (or using no intent) can be specified for any dummy argument of a function, just as they can for a subroutine.
However, the dummy arguments for a function are still quite distinct from the function result. The result(ncb) clause here is not saying "the argument ncb is the result of my function". Instead result(ncb) says "the result of the function is called ncb instead of cell_blocked".
You cannot give the function result the same name as one of the function's arguments. (You get an (unhelpful) compiler error saying that cell_blocked is not given an implicit type because the result clause is invalid, so the compiler is ignoring it and considering the function result to be called cell_blocked. A more helpful error message would include the fact that the result clause is itself invalid.)
The function result is a completely separate thing from any of the dummy arguments, and having a function result is what distinguishes a function from a subroutine. You may (whether it's advisable or not) change a dummy argument (intent(inout), intent(out) or no intent) independently of how you specify the result of the function.
Given the function
function f(a, b, c, d)
integer, intent(in) :: a
integer, intent(out) :: b
integer, intent(inout) :: c
integer :: d
integer :: f ! Function result unless there's a result clause
...
f = ...
end function f
the function result is f and the function result is used in the expression in the right-hand side of the assignment
y = f(a, b, c, d)
The value of the function result is whatever was assigned to f by the end of the function's evaluation. Not one of the dummy arguments, however modified, is a "result".
In the function of the question (which is perhaps better left as a subroutine) we are failing to give the function's result a value. If we want the function's value to be either ncb or an incremented version of that we don't use an intent(inout) dummy to do that:
function cell_blocked(row,col,ncb)
integer, intent(in) :: row,col, ncb
integer :: cell_blocked
! One of many ways writing this logic
if (row == col) then
cell_blocked = ncb + 1
else
cell_blocked = ncb
end if
end function cell_blocked
You may then write something like
ncb = cell_blocked(row, col, ncb)
but one then wonders whether a subroutine is what was wanted all along.

Parametrized derived types with kind parameter as subroutine argument

Say we have the following code:
module foo
use :: iso_fortran_env
implicit none
type :: bar (p, q)
integer, kind :: p
integer, len :: q
integer(kind = p), dimension(q) :: x
end type bar
contains
subroutine barsub (this)
class(bar(*,*)), intent(in) :: this
write (*,*) this%x
end subroutine barsub
end module foo
This code does not compile with either gfortran 8 or pgfort 18.4. The pgi compiler says
Illegal selector - KIND value must be non-negative
Assumed type parameter (*) cannot be used with non-length type parameter p
whereas gfortran yields
The KIND parameter 'p' at (1) cannot either be ASSUMED or DEFERRED
If I change above code to
subroutine barsub (this)
class(bar(INT32,*)), intent(in) :: this
write (*,*) this%x
end subroutine barsub
it compiles fine with both compilers.
Is it possible to write a subroutine where the kind parameter does not need to be specified explicitly? In the example above, the code would be the same for INT32, INT64, ... and I don't want to copy paste it for every imaginable value of the kind parameter. It works fine for the len parameter. Why can't I do the same with the kind parameter?
Is it possible to write a subroutine where the kind parameter does not need to be specified explicitly?
No, kind type parameters need to be given by a constant expression or defaulted, see, e.g., the Fortran 2008 standard, definition 1.3.147.12.3.
Why can't I do the same with the kind parameter?
The fact that len and kind type parameters have different uses and requirements is the point of having two types of type parameters, if their characteristics were the same we wouldn't need two of them.
Note that procedures require of the kind parameters of their dummy arguments of parameterized derived types just same they require of the kinds of their dummy arguments of intrinsic types: to have their values defined at compilation time.

Parameter precision in fortran

I am using intel fortran 2016. I have defined some precision variables as follows:
! definition of single, double and quad precision
integer, parameter :: SINGLE_PRECISION = selected_real_kind(6, 37)
integer, parameter :: DOUBLE_PRECISION = selected_real_kind(15, 307) ! is used as standard precision
integer, parameter :: QUAD_PRECISION = selected_real_kind(33, 4931)
! definition of variable precision for REAL, INTEGER
integer, parameter :: REAL_TYPE = DOUBLE_PRECISION
integer, parameter :: INTEGER_TYPE = 4
I would like to now use these to control the precision of a parameter that gets declared in a subroutine as follows:
SUBROUTINE SKIP(IUNIT,NBYTS)
IMPLICIT DOUBLE PRECISION (A-H,O-Z)
Character c*1
Parameter(n1 = 1024, nT1 = 8*n1)
I tried the following :
Parameter(INTEGER_TYPE)((n1 = 1024, nT1 = 8*n1)
Parameter(INTEGER_TYPE)((n1 = 1024, nT1 = 8*n1, kind = INTEGER_TYPE)
All to no avail. What is the proper way to define parameter precision in Fortran?
Thanks
Note: this is essentially the same as Francescalus' answer, but with some extra fluf.
The issue at hand here is related to the IMPLICIT statement.
The Fortran standard makes the following statements :
5.5 IMPLICIT statement
In a scoping unit, an IMPLICIT statement specifies a type, and
possibly type parameters, for all implicitly typed data entities whose
names begin with one of the letters specified in the statement.
Alternatively, it may indicate that no implicit typing rules are to
apply in a particular scoping unit.
<snip>
In each scoping unit, there is a mapping, which may be null, between
each of the letters A, B, ..., Z and a type (and type parameters).
An IMPLICIT statement specifies the mapping for the letters in its
letter-spec-list. IMPLICIT NONE specifies the null mapping for all
the letters. If a mapping is not specified for a letter, the
default for a program unit or an interface body is default integer if
the letter is I, J, ..., or N and default real otherwise, and the
default for an internal or module procedure is the mapping in the host
scoping unit.
So in short, by default everything is of type default REAL unless it starts with I,J,...,N, then it is of type default INTEGER.
In the example in the question, the variables n1 and nT1 are hence default INTEGER unless specified otherwise. Thus the following might be a solution :
subroutine skip(IUNIT,NBYTS)
implicit double precision (A-H,O-Z)
character c*1
integer(kind=integer_kind), parameter :: n1 = 1024, nT1 = 8*n1
...
end subroutine skip
As a general remark on variable declaration I would like to make the following remarks:
use implicit none as default. It makes debugging easier
avoid star notation, it is not part of the standard for anything but characters, and for characters it is declared obsolescent.
make use of kind parameters declared in the intrinsic module iso_fortran_env
be aware that double precision does not necessarily mean "double precision" in the literal or IEEE sense of the word. It just means, twice the storage of a real and a higher precision than real. (a higher precision could be one-digit).
You can define a parameter of a specific kind with
TYPE, PARAMETER :: name = value_kind
So to define, say, an integer n1 with kind INTEGER_TYPE you do
integer(kind=INTEGER_TYPE), parameter :: n1 = 1024_INTEGER_TYPE
You have an answer which gives the best way to specify the kind of a named constant. You can find much more detail around this question.
However, I'll add some detail about the parameter statement.
The parameter statement makes an object a named constant and specifies its value. It doesn't specify the type or kind of that object.
The example of the question uses implicit typing for n1 and nT1. These are implicitly default integer.
The implicit rule may be changed (I won't show how), but the type may also be given explicitly:
integer(INTEGER_TYPE) n1, nT1
parameter (n1=1024, nT1=8*n1)
Still, the form of the other answer is preferable. And, as noted there, the kind of the literal constants may also be given in the parameter statement if desired.

How do I read a parameter from a text file in a FORTRAN module? [duplicate]

In Fortran, is the PARAMETER attribute set at runtime or compilation time?
I was wondering if I can pass in the size the of the array at run time and have this set as PARAMETER.
Can this be done? If so, how? If not, why?
Yes, as repeatedly answered, a named constant (an object with the parameter attribute) must have its initial value "known at compile time". However, as you talk about the size of arrays I'll note something else.
When declaring the shape of an array there are many times when the bounds needn't be given by constant expressions (of which a simple named constant is one example). So, in the main program or a module
implicit none
...
integer, dimension(n) :: array ! Not allowed unless n is a named constant.
end program
the n must be a constant. In many other contexts, though, n need only be a specification expression.
implicit none
integer n
read(*,*) n
block
! n is valid specification expression in the block construct
integer, dimension(n) :: array1
call hello(n)
end block
contains
subroutine hello(n)
integer, intent(in) :: n ! Again, n is a specification expression.
integer, dimension(2*n) :: array2
end subroutine
end program
That is, array1 and array2 are explicit shape automatic objects. For many purposes one doesn't really need a named constant.
Beyond the array size, the following is certainly not allowed, though.
implicit none
integer n, m
read(*,*) n, m
block
! n and m are specifications expression in the block construct
integer(kind=m), dimension(n) :: array ! But the kind needs a constant expression.
...
end block
You need dynamic allocation if the size of your array is to be defined as runtime. All parameter (constants) must be defined as compiling time.
The value of a parameter is set at compile time.
A declaration such as
integer, parameter :: number_of_widgets = numwidge
requires that numwidge be known at compile time, indeed known before it is encountered on the rhs of the declaration.

No lifting of scalar arguments to arrays in Fortran

Why is it that Fortran will promote a scalar expression to an array, in an expression, but not as an argument to a procedure? In particular, why did the standards body make this design decision? Is it solely because of ambiguity, should the procedure be overloaded? Could an error message in that situation be an alternative approach?
For example, In the code below, the last statement, x = foo(7), produces the GFortran error: Error: Rank mismatch in argument 'a' at (1) (1 and 0).
module m
public :: foo
contains
function foo(a) result(b)
integer, dimension(:) :: a
integer, dimension(size(a)) :: b
b = a+1
end function foo
end module m
program p
use m
integer, dimension(4) :: x
integer, parameter, dimension(4) :: y = (/1,2,3,4/)
x = 7
x = foo(x)
x = foo(y)
x = foo(x + 7)
x = foo(7)
end program p
This question should have asked about why an array assignment will promote a scalar value source to an array target; unlike an array function. I expect that's simply a convenient special case though. Any comments gratefully received in the begging caps below.
If you want the function to handle scaler and array arguments, declare it as "elemental" and with scaler dummy arguments. Then it will be able to handle both scaler and array actual arguments, including scaler expressions. Will that meet your need?
The change:
elemental function foo(a) result(b)
integer, intent (in) :: a
integer :: b
b = a+1
end function foo
Perhaps they provided a way to do what you want, and one way was enough?
Procedure calling in Fortran with explicit interfaces (which you get automatically when using module procedures) requires a TKR (type, kind, rank) match. As an array is a different type than a scalar, not to mention the rank mismatch, this is not allowed.
Is it because of ambiguity should the procedure be overloaded?
That would be a problem, yes.
Could an error message in that situation be an alternative approach?
Could pink unicorns exist? Maybe, but to the best of my knowledge they don't. IOW, the Fortran standard currently requires TKR matching, and thus a standard conforming compiler must enforce this requirement. If you want to change that, I recommend making a proposal to the standards committee.
I would think the answer to this is pretty clear. Let's slightly modify your example:
module m
public :: foo
contains
function foo(a) result(b)
integer, dimension(:) :: a
integer, dimension(size(a)) :: b
b = a+1
a(2) = -777 ! new line; modify one element
end function foo
end module m
program p
use m
integer :: x
integer, dimension(4) :: y
x = 7
y = foo(x) ! pass a scalar
end program p
what is x supposed to be after the call to foo?
Now, sure, you could have the semantics of argument passing change depending on whether or not it's an intent(in) variable, but that is not something which is going to clarify things for programmers.
If the function call is supposed to 'distribute' somehow over array elements, then as MSB points out, elemental is the way to go. Otherwise, one just makes sure one's arguments match one's parameters.