In a calling function I have this:
call ESMF_TimeGet( date, yy=year, mm=month, dd=day, s=sec, rc=rc)
The signature for ESMF_TimeSet is:
! ESMF_TimeGet - Get value in user-specified units
subroutine ESMF_TimeGet(time, YY, YRl, MM, DD, D, Dl, H, M, S, Sl, MS, &
US, NS, d_, h_, m_, s_, ms_, us_, ns_, Sn, Sd, &
dayOfYear, dayOfYear_r8, dayOfYear_intvl, &
timeString, rc)
type(ESMF_Time), intent(in) :: time
integer, intent(out), optional :: YY
integer(ESMF_KIND_I8), intent(out), optional :: YRl
integer, intent(out), optional :: MM
integer, intent(out), optional :: DD
integer, intent(out), optional :: D
integer(ESMF_KIND_I8), intent(out), optional :: Dl
integer, intent(out), optional :: H
integer, intent(out), optional :: M
integer, intent(out), optional :: S
integer(ESMF_KIND_I8), intent(out), optional :: Sl
integer, intent(out), optional :: MS
integer, intent(out), optional :: US
integer, intent(out), optional :: NS
double precision, intent(out), optional :: d_
double precision, intent(out), optional :: h_
double precision, intent(out), optional :: m_
double precision, intent(out), optional :: s_
double precision, intent(out), optional :: ms_
double precision, intent(out), optional :: us_
double precision, intent(out), optional :: ns_
integer, intent(out), optional :: Sn
integer, intent(out), optional :: Sd
integer, intent(out), optional :: dayOfYear
real(ESMF_KIND_R8), intent(out), optional :: dayOfYear_r8
character (len=*), intent(out), optional :: timeString
type(ESMF_TimeInterval), intent(out), optional :: dayOfYear_intvl
integer, intent(out), optional :: rc
type(ESMF_TimeInterval) :: day_step
integer :: ierr
When I'm calling ESMF_TimeSet how is the subroutine able to convert the yy=yr argument into a yy variable inside the subroutine (and similarly for mm and dd)? Also, does FORTRAN care about case-sensitivity of variables?
how is the subroutine able to convert the "yy=yr" argument into a yy variable inside the subroutine?
There is no yy variable in your procedure. Only yy dummy argument. When you call this procedure you use so-called named arguments. This feature is not specific to Fortran. But what's your problem with optional arguments?
And the same for mm and dd. Does FORTRAN care about case-sensitivity of variables?
Fortran is case-insensitive language. So no, it doesn't care.
Okay. I'll try to explain some basics because I have some difficulties with understanding your comments. :)
When it comes to procedures the Fortran terminology differs from the "common" one in some respects:
It's common to distinguish between procedures (performs some tasks returning several results through arguments) and functions (invokes within expression and return a single value which is used in expression) while collectively calling them subroutines. In Fortran we have subroutines and functions collectively calling procedures.
While calling procedure you pass some info through arguments. Commonly entities appeared at procedure definition are called formal arguments and entities appeared in procedure call are called actual arguments. In Fortran formal arguments are called dummy arguments. Be aware, because often dummy arguments are referred to as meaningless arguments which are used only to conform the API, i.e. EmptyParam in Delphi.
When procedure is called (is referenced in terms of Fortran standard)
the actual argument list identies the correspondence between the
actual arguments and the dummy arguments of the procedure.
So basically the correspondence is by position. Some languages (including Fortran) also have the ability to establish correspondence by keyword. The long story short:
The keywords are the dummy argument names and there must be no further
positional arguments after the first keyword argument.
(c) Michael Metcalf, John Reid, Malcolm Cohen. Modern Fortran Explained.
For details see Fortran Standard (you can grab the copy the final draft of the Fortran 2008 standard by the link provided here), 12.5.2 Actual arguments, dummy arguments, and argument association.
So keyword arguments is a feature known as named arguments or named parameters in other languages.
But this feature is not the only one which can help programmer to write concise and readable code for calling procedures without overloading. Some languages also have default arguments. Fortran has similar feature: so-called optional arguments. It looks a bit different but the aim is the same.
Keyword arguments are often used in conjunction with optional arguments. For example, you should use keyword arguments when you leave out optional arguments in the middle of the argument list.
So what's your question is?
Related
This question already has an answer here:
what's the meaning of "iostat" argument in open statement?
(1 answer)
Closed 1 year ago.
I want to overcome the lousy and non-intuitive string handling in fortran by writing a more pythonic string type, but I stumpled across a mean issue with derived-type (overloaded) assignment.
The main type should look like
TYPE t_string
CHARACTER(:), ALLOCATABLE :: str
contains
...
END TYPE t_string
with its power in the derived-type procedures. Of course the new string type should be as indistinguishable from the intrinsic CHARACTER(len=*) type as possible. Especially I want to use intrinsic routines (which use the character type) without any extra type conversions. Therefore I defined an assignment operator between CLASS(t_string) and CHARACTER(len=*). E.g. opening a file with the new type should look like this:
type(t_string) :: filename
filename = '...'
open(file = filename, ...)
! ^ assignment here
Since there is an assignment file=filename between t_string and CHARACTER(len=*) there should be no problem in the call to open. But I get an error due to mismatch in types.
I guess the problem is, that the assignment in the subroutine call is not really an assignment but just some syntax convention.
Any ideas how to fix this?
What is the reason (in term of design of the fortran language) for the "subroutine assignment" not to be a real assignment?
I do not want to call open(file = filename%str, ...)
Here is a mwe:
MODULE m_string
IMPLICIT NONE
SAVE
INTERFACE ASSIGNMENT(=)
MODULE PROCEDURE :: string_operator_equal_s, string_operator_equal_c
END INTERFACE ASSIGNMENT(=)
TYPE t_string
CHARACTER(:), ALLOCATABLE :: str
END TYPE t_string
CONTAINS
ELEMENTAL SUBROUTINE string_operator_equal_s(lhs,rhs)
IMPLICIT NONE
CLASS(t_string), INTENT(inout) :: lhs
CLASS(t_string), INTENT(in) :: rhs
lhs%str = rhs%str
END SUBROUTINE string_operator_equal_s
ELEMENTAL SUBROUTINE string_operator_equal_c(lhs,rhs)
IMPLICIT NONE
CLASS(t_string), INTENT(inout) :: lhs
CHARACTER(len=*), INTENT(in) :: rhs
lhs%str = rhs
END SUBROUTINE string_operator_equal_c
SUBROUTINE routine(char)
CHARACTER(len=*) :: char
END SUBROUTINE routine
END MODULE m_string
PROGRAM test
USE m_string
TYPE(t_string) :: str
CHARACTER(len=10) :: char
CALL routine(char) ! no error
CALL routine(char=str) ! error: #6633: The type of the actual argument differs from the type of the dummy argument. [STR]
END PROGRAM test
Since there is an assignment file=filename between t_string and CHARACTER(len=*) there should be no problem in the call to open.
No such assignment is present. You are only using the specifier name to specify which argument of the statement you are passing (similar to keyword/named arguments in Python, but not the same). open is in fact not a procedure, it is a statement, but it also has its "arguments" (specifiers) distinguished by their names.
Hence no derived assignment shall be invoked. You must convert to character yourself.
This is a follow up to a post that I found on SO: Difference between intent(out) and intent(inout)
The linked question asked about the difference between intent(out) and intent(inout) in Fortran by asking an invalid program.
Could anyone come up with a simple valid program(s) that give different results by changing intent(inout) to intent(out) or vice versa?
Here you go...
program intent_test
implicit none
integer, allocatable :: a(:)
a = [1,2,3,4,5]
call intent_inout (a)
call intent_out (a)
contains
subroutine intent_inout (a)
integer, allocatable, intent(inout) :: a(:)
if (allocated(a)) then
print *, a
else
print *, "Unallocated"
end if
end subroutine intent_inout
subroutine intent_out (a)
integer, allocatable, intent(out) :: a(:)
if (allocated(a)) then
print *, a
else
print *, "Unallocated"
end if
end subroutine intent_out
end program intent_test
1 2 3 4 5
Unallocated
As Steve Lionel's answer, and the comments in my original answer which perhaps sparked this interest, the effects of intent(out) on the initial state of the dummy argument (and the actual argument) are a way in to answering this question.
intent(inout) has the dummy argument reflect the value of the actual argument on entry to the procedure. intent(out) "resets" the dummy argument. In the case of the linked question, this "undefinition" is the cause of the invalid nature of the program.
More precisely, we can say the following things about intent(out) dummy arguments:
allocatable actual arguments become deallocated;
the pointer association of pointer actual arguments becomes undefined;
for a non-pointer dummy argument any component not default initialized becomes undefined.
The linked question fell foul of the third point by trying to reference such an newly undefined value. However, default initialized components are not undefined, leading us to our first class of valid programs:
implicit none
type t
integer :: x=1
end type t
type(t) :: x=t(0)
call s1(x)
call s2(x)
contains
subroutine s1(x)
type(t), intent(inout) :: x
print*, x%x
end subroutine s1
subroutine s2(x)
type(t), intent(out) :: x
print*, x%x
end subroutine s2
end
Crucially, the default initialization of the component means that x%x isn't undefined even on entry to s2, but it takes a potentially different value from the actual argument's component prior to procedure entry.
Coming up with a suitable program with pointer arguments is tricky: a pointer with undefined pointer association status can't be referenced/queried before its pointer association status is redefined.
Which leaves us looking at allocatable components. Unlike with the pointers, where we can't query pointer association status when that's undefined, we can ask about the allocation status. An actual argument corresponding to an intent(out) dummy is deallocated; with intent(inout) the allocation status is unchanged:
implicit none
integer, allocatable :: i
allocate (i)
call s1(i); print*, allocated(i)
call s2(i); print*, allocated(i)
contains
subroutine s1(i)
integer, allocatable, intent(inout) :: i
end subroutine s1
subroutine s2(i)
integer, allocatable, intent(out) :: i
end subroutine s2
end
(This is a simpler version of Steve Lionel's example.)
This all shows that it's possible to have differences. Using intent incorrectly can lead to invalid programs or to significant changes in meaning. Understanding what intent(in), intent(inout), intent(out) and no specified intent mean is a crucial part of being a Fortran programmer.
In my code I have an elemental subroutine which is basically like this:
elemental subroutine calc_stuff (x, a, b, c)
real, intent(in) :: a, b, c
real, intent(out) :: x
x = a/b + c
end subroutine calc_stuff
which I changed to this:
elemental subroutine calc_stuff (x, a, t)
real, intent(in) :: a
type(mytype), intent(in) :: t
real, intent(out) :: x
x = a/t%b + t%c
end subroutine calc_stuff
where mytype is a type containing some scalar real and integer, as well as a real, allocatable array. The members b and c are reals, making the second version basically the same as the first one.
The second version compiles fine on various compilers (Cray, Intel, NEC, GFortran), but now I read that the standard states for elemental subroutines:
All dummy arguments must be scalar, and must not have the ALLOCATABLE or POINTER attribute.
Is my code therefore not standard-conforming when passing a user-defined type to an elemental subroutine, but all the compilers "know" what I want because I am only using scalars from the type and not the allocatable array? Or am I misunderstanding the wording of the standard and everything is fine with the second version?
The dummy argument t is scalar1 and does not have the pointer attribute and does not have the allocatable attribute. It does not violate the condition.
The attributes of components of the type do not have a bearing on the attributes of the type itself.
1 Being of derived type doesn't make an object necessarily non-scalar. Even with multiple, or array components, the object itself may still be scalar. A derived type array is an array with element(s) of that type. Think also of a character object like character(len=3) name: it is scalar but consists of multiple substrings.
In response to a question about character function results of non-constant length, an answer mentions "assumed length function result" but doesn't go into detail.
If I want to return a character result where the length depends on something else, that answer mentions automatic objects and deferred length as possible approaches:
function deferred_length(x)
character(*), intent(in) :: x
character(:), allocatable :: deferred_length
deferred_length = x//'!'
end function
or
function automatic(x)
character(*), intent(in) :: x
character(LEN(x)+1) :: automatic
automatic = x//'!'
end function
What is an assumed length function result and how does it differ from the forms above? Further, why is such mention relegated to a footnote?
An assumed length character function looks like the following:
function assumed_len(x)
character(*), intent(in) :: x
character(*) :: assumed_len
assumed_len = x//'!'
end function
Here the dummy argument x and the function result assumed_len are both assumed length characters.
Much like how x assumes its length from the actual argument when the function is referenced, the function result assumes its length from the function declaration in the referencing place.
With the above function, consider the program
implicit none
character(len=11) :: x='Hello world'
character(len=12) assumed_len
print *, assumed_len(x)
end
During the function's execution the function result has length 12 as declared in the main program. Equally, if we wanted it to have length 5 we simply change the declaration in the main program to character(len=5) assumed_len. In a different program unit the declaration could be something different and the function result would assume that length when referenced.
Well, that doesn't look so harmful: why should we avoid using functions like this?
In summary:
They go against the "philosophy" of modern Fortran.
There are better ways in modern Fortran to accomplish the same thing.
They are quite limited.
In every other aspect of Fortran, the attributes of a function result depend only on the arguments of the function and other accessible information. Here the attributes of the function result may differ with everything else the same through a declaration in an unrelated scope.
To the other points:
the interface of such a function must be implicit in a referencing place (and we don't like implicit interfaces);
we can't reference the same function twice in one place and get different length results, even with differing input;
the function has a much better idea of what length it wants to return.
In those cases where we really do want to control the length in the referencing place we can replace assumed_len with a function with explicit length result like
function replacement(x, n)
character(*), intent(in) :: x
integer, intent(in) :: n
character(n) :: replacement
...
end function
or a subroutine with assumed length dummy argument
subroutine replacement(x, y)
character(*), intent(in) :: x
character(*), intent(out) :: y
...
end subroutine
Finally, assumed length character functions are declared obsolescent in the current Fortran standard, and may be deleted in later revisions.
It is clear to my what pass by value and pass by reference mean, but what happens in the opposite direction ?
e.g:
subroutine get_flux(flux,ngrid,u,a,c)
implicit none
double precision, dimension(1:ngrid), intent(in) :: u
double precision, intent(in) :: a,c
integer, intent(in) :: ngrid
double precision, intent(out) :: flux
flux = a*u*(1-u)**c
end subroutine
if i call this subroutine by
double precision, dimension(1:ngrid) :: flux
double precision, parameter :: a = 1, c = 5
double precision, dimension(1:ngrid) :: u(:) = 2
call get_flux(flux,ngrid,u,a,c)
Does the subroutine passes a reference back, or a copy of the array ?
I'm not aware of any programming language which, when passing an argument in one direction by reference does not pass the same argument by reference in the other. Certainly in Fortran arguments appear to be passed by reference in both directions. I write appear to be because the standard does not require that arguments be passed by reference, just that the program behaves as if they are. In general that is, sometimes compilers will make copies, perhaps if an array section is passed.