Can a subroutine be contained in a subroutine (not in a program) in Fortran? - visual-studio-2017

I have to deal with an old piece of software written in Fortran (mainly written in the 70's and badly maintained up to 3 years ago).
I am trying to get it compiled with VS2017 and the Intel oneAPI compiler (Fortran Compiler Classic 2021.3.0) for 64-bits architectures.
In the process of updating one of its modules, I convinced myself that the compiler does not support what I have learned to be called "host association" between subroutines (via CONTAINS statement). None of the symbols defined in a containing subroutine seems to be visible in the contained subroutine (if I use IMPLICIT NONE in the contained subroutine the compiler tells me that I need to declare all of them while if I don't, the compilers gets the declarations very wrong and not matching with the declarations in the containing subroutine. Lots of misleading error messages are printed).
Can somebody of you confirm that this is the case or provide the compiler options to enable this feature that clearly was allowed in the past by some compilers? If needed I will post the source code (I am not posting it immediately because I think this could be a very naive question for a Fortran expert, I am instead a total novice).
Sincerely,
HERE I COMPLEMENT THE ORIGINAL POST AS REQUESTED IN THE COMMENTS
Original code:
Subroutine LoadUserLibs(TypesInDeck,*)
... OMITTED COMMENTS ...
! This routine is only used in the multi-DLL configuration (otherwise empty routine)
!dec$ if defined (TRNSYS_MULTI_DLL)
Use DFWIN
Use DFLIB
!Use KERNEL32
Use TrnsysConstants
Use TrnsysFunctions
Use TrnsysData, Only: steamMethod,isNISTSteamFound
! Force explicit variable declaration
Implicit None
! Local variable declarations
Type(T_WIN32_FIND_DATA):: WFD
Character (len=maxPathLength) UserDir,FoundListStr,SearchListStr
Character (len=maxMessageLength) msgString
Integer :: libFile, j, k
Logical :: bSt
Character (len=12) jStr,TypeNum,numDLLsStr
Character (len=20) routineName
Integer luw !listing file logical unit number
Integer (kind=8) ExistTest !declares an integer to temporarily contain a pointer
Integer i !not used but must be delcared as part of a POINTER declaration.
Integer (kind=8) LibHandles(100) !declares an array where handles to loaded libraries are stored.
Integer LibCount !a counter variable used to keep track of how many dlls have been loaded
Integer TypeCount !a counter variable used to count how many Types were found in a given dll.
Integer TypesInDeck(nMaxUnits) !an array containing a list of Types that are in the deck being run.
Integer TotalTypes/0/ !the total number of Types in the deck being run (no duplicates)
Integer TypesListed/0/ !a variable used to count how many of the Types that were found, have been listed so far.
Integer, Allocatable :: SearchList(:,:) !an array of Type numbers to look for in dlls.
Integer, Allocatable :: FoundList(:)
Logical :: isType155InDeck = .false., isType155DllFound = .false.
! Common black definitions
Integer(INT_PTR_KIND()) paa(nMaxTypes) !declares an integer array that will contain pointer addresses to the Types
Integer(INT_PTR_KIND()) saa !declares an integer that will contain a pointer address of the NIST steam routines.
Common /USRDLLS/ paa,saa
! Pointer definitions
Pointer (p,i)
... OMITTED CODE ...
!dec$ else
! Single-DLL configuration: empty routine
!dec$ endif
Return
Contains
Subroutine LoadTypesFromDll()
... OMITTED CODE ...
End Subroutine LoadTypesFromDll
End Subroutine LoadUserLibs
Errors I received compiling the original code:
Severity Code Description Project File Line Suppression State
Error error #6303: The assignment operation or the binary expression operation is invalid for the data types of the two operands. C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 276
Error error #6303: The assignment operation or the binary expression operation is invalid for the data types of the two operands. C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 284
Error error #6303: The assignment operation or the binary expression operation is invalid for the data types of the two operands. C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 303
Error error #6303: The assignment operation or the binary expression operation is invalid for the data types of the two operands. C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 309
Error error #6303: The assignment operation or the binary expression operation is invalid for the data types of the two operands. C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 314
Error error #6303: The assignment operation or the binary expression operation is invalid for the data types of the two operands. C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 315
Error error #6303: The assignment operation or the binary expression operation is invalid for the data types of the two operands. C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 317
Error error #6303: The assignment operation or the binary expression operation is invalid for the data types of the two operands. [CFILENAME] C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 302
Error error #6303: The assignment operation or the binary expression operation is invalid for the data types of the two operands. [CFILENAME] C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 308
Error error #6362: The data types of the argument(s) are invalid. [TRIM] C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 276
Error error #6362: The data types of the argument(s) are invalid. [TRIM] C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 284
Error error #6362: The data types of the argument(s) are invalid. [TRIM] C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 284
Error error #6362: The data types of the argument(s) are invalid. [TRIM] C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 303
Error error #6362: The data types of the argument(s) are invalid. [TRIM] C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 309
Error error #6362: The data types of the argument(s) are invalid. [TRIM] C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 314
Error error #6362: The data types of the argument(s) are invalid. [TRIM] C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 315
Error error #6362: The data types of the argument(s) are invalid. [TRIM] C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 315
Error error #6362: The data types of the argument(s) are invalid. [TRIM] C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 317
Error error #6410: This name has not been declared as an array or a function. [FOUNDLIST] C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 291
Error error #6410: This name has not been declared as an array or a function. [LIBHANDLES] C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 268
Error error #6423: This name has already been used as an external function name. [PAA] C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 289
Error error #6423: This name has already been used as an external function name. [SEARCHLIST] C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 292
Error error #6460: This is not a component name that is defined in the encompassing structure. [CFILENAME] C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 283
Error error #6514: Substring or array slice notation requires CHARACTER type or array. [CFILENAME] C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 302
Error error #6514: Substring or array slice notation requires CHARACTER type or array. [CFILENAME] C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 308
Error error #6514: Substring or array slice notation requires CHARACTER type or array. [FOUNDLIST] C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 321
Error error #6514: Substring or array slice notation requires CHARACTER type or array. [TYPENUM] C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 284
Error error #6515: This function, which is specified as the left side of an assignment statement, is invalid. [PAA] C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 289
Error error #6515: This function, which is specified as the left side of an assignment statement, is invalid. [SEARCHLIST] C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 292
Error error #6535: This variable or component must be of a derived or structure type. [WFD] C:\Development\trndll-vs2017-ifc\TRNDLL\SourceCode\Kernel\Userlib.f90 283

Internal procedures (following a CONTAINS) can see all entities declared in the "host scope" (before the CONTAINS), and can see other internal procedures, but cannot see entities declared within other internal procedures. Host association goes up the tree only. For example:
subroutine outer
integer :: X
...
contains
subroutine innerA
real :: A
end subroutine innerA
subroutine innerB
real :: B
end subroutine innerB
end subroutine outer
Subroutine outer can see explicit interfaces for innerA and innerB. innerA and innerB can see explicit interfaces for each other and variable X, but innerA cannot see innerB's variable B and innerB cannot see innerA's variable A.
I am skeptical that the behavior you describe was supported by other compilers. Given that you say it was written in the 70s, it could not have used modules, which started with Fortran 90. My guess is that your "updating one of the modules" introduced errors.

For the benefit of who will ever setup a VS2017 + oneAPI project to build the TRNSYS kernel, I post here the solution of the problem, which was not related to Fortran language incompatibilities between different versions. Instead, it was due to an undefined macro (TRNSYS_MULTI_DLL) and a sloppy usage of that macro in the above code.
As you can see from the comments in the source code above, the intention of the author was to provide an empty function when that macro is not defined. However, as the included function is placed after the end of the conditional compilation macro !dec$ endif, this results in the included function to exist even if the including function is empty. Therefore the compiler complains about symbols which really do not exist any more.
Better would be, in my opinion, to move the contained function within the conditional compilation macro, like below.
Subroutine LoadUserLibs(TypesInDeck,*)
... OMITTED COMMENTS ...
!dec$ if defined (TRNSYS_MULTI_DLL)
! This routine is only used in the multi-DLL configuration (otherwise empty routine)
... OMITTED CODE ...
Return
Contains
Subroutine LoadTypesFromDll()
... OMITTED CODE ...
Return
End Subroutine LoadTypesFromDll
!dec$ else
! Single-DLL configuration: empty routine
Return
!dec$ endif
End Subroutine LoadUserLibs

Related

Dummy argument associated actual procedure differs from dummy argument of the dummy procedure

I'm using a the function NEQNF from the IMSL Fortran Library to solve a non-linear equation system and get 3 errors. I'm using Visual Studio 2017 on a x64 system. The error says the following:
Error #7061: The characterístic of dummy argument 1 of the associated actual procedure differ from the characteristics of dummy argument 1 of the dummy procedure [FCN_SS]
Error #7062: The characterístic of dummy argument 2 of the associated actual procedure differ from the characteristics of dummy argument 2 of the dummy procedure [FCN_SS]
Error #7063: The characterístic of dummy argument 3 of the associated actual procedure differ from the characteristics of dummy argument 3 of the dummy procedure [FCN_SS]
The code is:
include 'link_fnl_shared.h'
use neqnf_int
use umach_int
implicit none
!Declaring variables
.
.
.
Contains
subroutine solve_ss(x, fnorm)
integer n
parameter (n=2)
integer k, nout
real(dp) :: fnorm, x(n), xguess(n)
data xguess/1.0_dp, 0.3_dp/ !guess for total output in units
call umach (2, nout)
call neqnf (fcn_ss, x, xguess=xguess, fnorm=fnorm)
end subroutine solve_ss
subroutine fcn_ss(x, f, n)
implicit none
!specification
integer n
real(dp) :: x(n), f(n)
.
.
.
F(1)=...
F(2)=...
end subroutine fcn_ss
I'm not sure what the error is about, since the declaration of the variables is the same in solve_ss and fcn_ss.
The documentation of the library explicitly states (here and here) that you are required to use the respective modules in order to have access to modern implementation of the routines.
Otherwise, you may be (I can't test, but I'm basing on this) accessing legacy support of the library. So, instead of the generic Fortran 90 interface, It may be leading you to the FORTRAN77 specific interface:
NEQNF (FCN, ERRREL, N, ITMAX, XGUESS, X, FNORM)
Other detail is that the documentation explicity states you must declare the passed function as external:
external fcn_ss
Still, I'm not sure about those guesses. Could be that or any other strange error. Please provide feedback.

Error in implicit declaration in Fortran

I have checked all the formats of the implicit type declaration but have not been able to find out the error in the following line . I have been running the code using f77 to compile this.
implicit real*4 (a-h,o-z)
while running the program it gives the following error for the above statement.
implicit real*4 (a-h,o-z)
1
Error: Unexpected character in variable list at (1)
A few basic rules if you are using F77 or earlier versions.
Put the implicit line first
Columns 1-5 are for labels
Column 6 is for continuation
Start the code in column 7
The code should not go past column 72. Some compilers have an option to extend the line length to 80 or 120 or 132 (widths of line printers when F77 first came out)
If you wish to use free format, use one of the later versions of fortran

How does automatic typecasting (type conversion) work in Fortran?

I am using gfortran compiler. Also tell me if gfortran uses something other than the Fortran standard while performing automatic typecasting (type conversion).
Assignment is defined by Fortran 2008 Section 7.2. Of note is Cl. 7.2.1.3 paragraph 8:
For an intrinsic assignment statement where the variable is of numeric type, the expr may have a different numeric
type or kind type parameter, in which case the value of expr is converted to the type and kind type parameter
of the variable according to the rules of Table 7.9.
Table 7.9: Numeric conversion and the assignment statement
Type of variable Value Assigned
integer INT(expr , KIND = KIND (variable))
real REAL(expr , KIND = KIND (variable))
complex CMPLX(expr , KIND = KIND (variable))
This means that any expression (expr) will be implicitly converted to the type and kind of the variable it is being assigned to. For character types, derived types and anything else, please see the standard.
Also note that Fortran only performs conversions like this during assignment and initialization but not contexts like procedure calls. For example, consider this procedure:
subroutine sub1(a)
implicit none
integer :: a
print *, a
end subroutine
This procedure has a dummy argument of type integer. You cannot, for example, do this:
call sub1(1.d0)
because this results in a mismatch of type between the actual and dummy arguments.
You can, however, do this:
integer :: a
a = 1.d0 !implicitly interpreted as: a = INT(1.d0, kind=kind(a))
call sub1(a)
because the implicit conversion is defined for the assignment.
The only documented extension to the standard for implicit type conversion in gfortran (5.1.0) is between logical and integer types during assignment.
See: https://gcc.gnu.org/onlinedocs/gcc-5.1.0/gfortran/Implicitly-convert-LOGICAL-and-INTEGER-values.html#Implicitly-convert-LOGICAL-and-INTEGER-values
Logical .true. is converted to integer 1
Logical .false. is converted to integer 0
Integer 0 is converted to .false.
Any other integer is converted to .true.
Do note that if you can do without legacy extensions then don't use them. Using them means your program is not standard Fortran and thus any compiler is free to reject it for being incorrect. This extensions is meant to allow legacy code to compile with modern compilers and not for use in new code.

Unexpected Statement Function at 1 in Fortran

I am new to Fortran and writing this small program to write out 100 ordered pairs for a circle.
But I get the error mentioned above and I don't know how to resolve.
implicit real*8(a-h,o-z)
parameter(N=100)
parameter(pi = 3.14159265358979d0)
integer*8 k
dtheta=2*pi/N
r=1.0d0
x00=0.0d0
y00=0.0d0
do k=0,N-1
xb(k)=r*cos(k*dtheta)-x00
yb(k)=r*sin(k*dtheta)-y00
enddo
open(64,file='xbyb.m',status='unknown')
write(64,*) (xb(k),k=0,N-1),(yb(k),k=0,N-1)
close(64)
end
You do not declare the arrays xb and yb.
Although not technically FORTRAN 77 I still suggest using implicit none or at least an equivalent compiler option to be forced to declare everything explicitly. Implicit typing is evil and leads to bugs.
As High Performance Mark reminds, the syntax
f(k) = something
declares a feature (now obsolescent in Fortran 95 and later) called a statement function. It declares a function of one argument k. The only way for the compiler to recognize you mean an array reference instead is to properly declare the array. The compiler complains the statement function is unexpected because the declaration must be placed before the executable statements.
Your implied do loop in the write statement is Fortran 90 anyway, so no need to stick to FORTRAN 77 in the 21st century.
Other tips:
status='unknown' is redundant, that is the default, just leave it out.
You can just write r = 1 and x00 = 0.

Intel Fortran error "allocatable array or pointer is not allocated"

When I tried to run a huge Fortran code (the code is compiled using Intel compiler version 13.1.3.192), it gave me error message like this:
...
Info[FDFI_Setup]: HPDF code version number is 1.00246
forrtl: severe (153): allocatable array or pointer is not allocated
Image PC Routine Line Source
arts 0000000002AD96BE Unknown Unknown Unknown
arts 0000000002AD8156 Unknown Unknown Unknown
arts 0000000002A87532 Unknown Unknown Unknown
...
Nonetheless, if I insert a small write statement (which is just to check the code, not to disturb the original purpose of the code) in one of the subroutines as the following (I couldn't put all the codes since they are too huge):
...
endif
call GetInputLine(Unit,line,eof,err)
enddo
if(err) return
! - [elfsummer] 20140815 Checkpoint 23
open(unit = 1, file = '/bin/monitor/log_checkpoint',status='old',position='append')
write(1,*) "BEFORE checking required keys: so far so good!"
close(1)
! check required keys
! for modes = 2,3, P and T are the required keys
if(StrmDat%ModeCI==2.or.StrmDat%ModeCI==3) then
...
then suddenly, the error message shown above disappears and the code can run correctly! I also tried to insert such write statements in other locations in the source code but the above error message still exists.
According to Intel's documentation:
severe (153): Allocatable array or pointer is not allocated
FOR$IOS_INVDEALLOC. A Fortran 90 allocatable array or pointer must already be allocated when you attempt to deallocate it. You must allocate the array or pointer before it can again be deallocated.
Note: This error can be returned by STAT in a DEALLOCATE statement.
However, I couldn't see any relations between the error and the "write statements" I added to the code. There is no such "allocate" command in the location I add the write statements.
So I am quite confused. Does anybody know the reasons? Any help is greatly appreciated!!
With traceback option, I could locate the error source directly:
subroutine StringRead(Str,delimiter,StrArray,ns) ! [private] read strings separated by delimiter
implicit none
character*(*),intent(in) :: Str
character*(*),intent(in) :: delimiter
character*(*),pointer :: StrArray(:)
integer,intent(out) :: ns
! - local variables
character(len=len(Str)) :: tline
integer :: nvalue,nvalue_max
character(len=len(StrArray)),pointer:: sarray(:),sarray_bak(:)
integer :: len_a,len_d,i
! deallocate StrArray
if(associated(StrArray)) deallocate(StrArray)
The error, according to the information the traceback gave me, lies in the last statement shown above. If I comment out this statement, then the "forrtl: severe (153)" error would disappear while new errors being generated... But still, I don't think this statement itself could go wrong...It acts as if it just ignores the if... condition and directly reads the deallocate commend, which seems weird to me.
You could have a bug in which you are illegally writing to memory and damaging the structure that stores the allocation information. Changing the code might cause the memory damage to occur elsewhere and that specific error to disappear. Generally, illegal memory accesses typically occur two ways in Fortran. 1) illegal subscripts, 2) mismatch between actual and dummy arguments, i.e., between variables in call and variables as declared in procedures. You can search for the first type of error by using your compiler's option for run-time subscript checking. You can guard against the second by placing all of your procedures in modules and useing those modules so that the compiler can check for argument consistency.
Sounds like some of the earlier comments give the general explanation. However,
1) Is StrArray(:) an Intent(out)? That is, are you reading the file's lines into StrArray() in the s/r, with the hope of returning that as the file's content? If so, declare it as an (Out), or whatever it should be.
2) Why is StrArray() a Pointer? Does it need to be a Pointer? If all you want is file content, you may be better off using a non-Pointer.
You may still need an Allocatable, or Automatic or something, but non-Pointers are easier in many cases.
3) If you must have StrArray(:) as a Pointer, then its size/shape etc must be created prior to use. If the calling sequence ACTUAL Arg is correctly defined (and if StrArray() is Intent(In) or Intent(InOUT), then that might do it.
By contrast, if it is an (Out), then, as with all Pointer arrays, it must be FIRST Allcoated() in the s/r.
If it is not Allocated somewhere early on, then it is undefined, and so the DeAllocate() fails, since it has nothing to DeAlloc, hence Stat = 153.
4) It is possible that you may wish to use this to read files without first knowing the number of lines to read. In that case, you cannot (at least not easily), Allocate StrArray() in advance, since you don't know the Size. In this case, alternate strategies are required.
One possible solution is a loop that simple reads the first char, or advances somehow, for each line in the file. Have the loop track the "sum" of each line read, until EOF. Then, you will know the size of the file (in terms of num lines), and you then allocate StrArray(SumLines) or something. Something like
SumLines = 0
Do i=1, ?? (or use a While)
... test to see if "line i" exists, or EOF, if so, Exit
SumLines = SumLines + 1
End Do
It may be best to do this in a separate s/r, so that the Size etc are known prior to calling the FileRead bits (i.e. that the file size is set prior to the FileRead s/r call).
However, that still leaves you with the problem of what Character(Len) to use. There are many possible solutions to this. Three of which are:
a) Use max length, like Character(Len = 2048), Intent(Out), or better yet, some compile time constant Parameter, call it MaxLineWidth
This has the obvious limitation to lines that <= MaxLineWidth, and that the memory usage may be excessively large when there many "short lines", etc.
b) Use a single char array, like Character(Len = 1), Intent(Out) :: StrArrayChar(:,:)
This is 2-D, since you need 1 D for the chars in each line, and the 2nd D for the lines.
This is a bit better compared to a) since it gives control over line width.
c) A more general approach might rely on a User Defined Type such as:
Type MyFileType
Character(Len=1), Allocatable :: FileLine(:) ! this give variable length lines, but each "line" must be allocated to the length of the line
End Type MyFileType
Then, create an array of this Type, such as:
Type(MyFileType), Allocatable :: MyFile(:) ! or, instead of Allocatable, can use Automatic etc etc
Then, Allocate MyFile to Size = num lines
... anyway, there are various choices, each with its own suitability for varying circumstances (and I have omitted much "housekeeping" re DeAllocs etc, which you will need to implement).
Incidentally, c) is also one possible prototype for "variable length strings" for many Fortran compilers that don't support such explicitly.