Fortran array mismatch in subroutine but no error? - arrays

I have a program that seems to work properly,
but I don't understand how it can,
because there seems to be an obvious error.
Main program(which use 'implicit') calls a subroutine:
implicit integer(i-n)
implicit double precision(a-h,o-y)
implicit complex*16(z)
call do_coulomb(zvecs0,zdualvecs0,n_f,n_f,1,
$ nsubfilling,zcorrmatrix,0,aldet01abs,
$ zcoulomb,epsilon)
where arrays, 'zvecs0','zdualvecs0','zcorrmatrix' are declared,
but there is no declaration of 'nsubfilling' anywhere.
(It could be a mistake or remnant of old version.)
Since I could not find
any declaration of array 'nsubfilling' in main program
('grep -i nsubfilling *' ),
I suppose 'nsubfilling' will be treated as an integer variable.
This is a subroutine of question:
subroutine do_coulomb(zvecs,zdualvecs,n_A,n_Ap,n_C,
$ nsubfilling,zcorrmatrix,n1_p0,aldet01abs,
$ zcoulomb,eps)
implicit integer(i-n)
implicit double precision(a-h,o-y)
implicit complex*16(z)
include "input.inc"
dimension zvecs(0:L-1,0:L-1,0:L-1,0:Lt,0:1,0:1,0:n_f-1)
dimension zdualvecs(0:L-1,0:L-1,0:L-1,0:Lt,0:1,0:1,0:n_f-1)
dimension nsubfilling(0:n_Ap-1,0:n_C-1)
dimension zcorrmatrix(0:n_Ap-1,0:n_Ap-1)
('L' 'Lt' are defined in the "input.inc" file.)
Because 'nsubfilling' is defined as an array in the subroutine,
I thought the mismatch between main and subroutine would cause an error.
However, the program seems to run okay even with mismatch.
I tried to print out some variables.('n_f'=4,'n_c'=1 in this run)
This is the output:
Before 1st call in main:
nsubfilling= 1
zcorrmatrix(0,0)= (20.510951695209510,0.13579118691198364)
zcorrmatrix(0,1)= (-1.0490102491588316,0.59453967445518319)
zcorrmatrix(1,0)= (-1.3791781667351797,-0.26247624491732802)
zcorrmatrix(n_f-1,n_f-1)= (20.510951695209513,-0.13579118691198364)
Inside do_coulomb subroutine:
nsubfilling= 1 0 1 1
zcorrmatrix(0,0)= (20.510951695209510,0.13579118691198364)
zcorrmatrix(0,1)= (-1.0490102491588316,0.59453967445518319)
zcorrmatrix(1,0)= (-1.3791781667351797,-0.26247624491732802)
zcorrmatrix(n_f-1,n_f-1)= (20.510951695209513,-0.13579118691198364)
n1p0= 0
After 1st call in main:
nsubfilling= 1
zcorrmatrix(0,0)= (20.510951695209510,0.13579118691198364)
zcorrmatrix(0,1)= (-1.0490102491588316,0.59453967445518319)
zcorrmatrix(1,0)= (-1.3791781667351797,-0.26247624491732802)
zcorrmatrix(n_f-1,n_f-1)= (20.510951695209513,-0.13579118691198364)
The output shows that 'nsubfilling' is treated as an integer in main routine,
but considered as an array in the subroutine and also
the subroutine recognizes 'zcorrmatrix' correctly
even with mismatch.
But, how this can be possible? I think there should be an error.
Could you let me understand how it work?
Thank you.

The main program does not have an explicit interface to subroutine do_coulomb. This means that the main program cannot check at compilation time whether the ranks of the actual arguments in the main program match the ranks of the dummy arguments in the subroutine.
An explicit interface can be provided in a number of ways:
having subroutine do_coulomb as an internal subprogram of the main program (subroutine after main subprogram, after a CONTAINS statement and before the END statement of the main program), this is called host association;
via an interface block in the main program (provide name of subroutine, list of dummy arguments, their types and attributes); or
by including the subroutine inside of a module (foo) and adding a use statement (use module foo) before the implicit declarations, this is called use association.
Why is your program not crashing? Well, because subroutine do_coulomb thinks it has access to a chunk of memory corresponding to nsubfilling of the size specified inside of the subroutine, and it happens to not do anything otherwise illegal when manipulating it - the chunk of memory happens to be in use by the same application. If the declared size of nsubfilling was very very big, and/or you had fewer arrays declared, it is likely that access to nsubfilling led to a segmentation fault.
Note that, even if you have an explicit interface, the ranks and the dimensions may not match - and that can be legal, as long as the total size of the dummy argument in the subroutine is not larger than the total size of the actual argument in the main program.

Related

Using modules in Fortran: Undefined reference to `parse_'

I am trying to use the parse routine described here to parse a string in Fortran90. When following the link in the document one can download a .zip with two f90-files, which I have done. I have then compiled them gfortran -c precmod.f90 && gfortran -c stringmod.f90. I have also added use strings to my program.
Despite of this, I receive the following error when compiling (gfortran stringmod.o precmod.o calcs.o server.o):
calcs.o: In function `calculate_':
calcs.f90:(.text+0x174): undefined reference to `parse_'
collect2: error: ld returned exit status 1
calcs.f90 is shown below and server.o is a server written in C that should be invoked by calcs.
program name
use strings
use iso_c_binding, only: C_CHAR, C_NULL_CHAR, C_INT
implicit none
! type declaration statements
character(255) query
integer calc, ans, portnum, calculate
interface
subroutine server(portnum) bind(C, name="server")
use iso_c_binding, only: c_int
integer(kind=c_int), value :: portnum
end subroutine server
end interface
! executable statements
print *, "Please provide me with a port number. Plz. <3"
read "(1i9)", portnum
call server(portnum)
end program name
function calculate(query)
implicit none
character(255) query, op
integer length, i, calculate
integer, dimension (:,:), allocatable :: inputarray
call parse(query, ' ', inputarray, length)
do i=1,size(inputarray)
print *, inputarray(i, 1)
end do
calculate = 5
end function calculate
I have tried to add public to the top of stringmod.f90.
When we have something like
program
end program
function func()
end function
then the function func is an external function, even when this is given in the same source code file as the main program. This external function knows nothing about the main program, and in turn the main program knows little about the external function.
Part of this lack of knowledge is that, in the example of the question, the fact that the subroutine parse has an explicit interface (through the module) in the main program is irrelevant to calculate.
That is, the function calculate has its own scope and has no host. To gain access to the module procedure parse it can itself use the module strings:
function calculate(query)
use strings, only : parse
implicit none
end function
There is a hint to this lack of being aware that parse is a module procedure in strings. The name decoration in parse_ (that single trailing underscore) is a common way to mangle an external procedure. Module procedures (without bind(c)) generally have much more elaborate symbol names.
Finally, I'll repeat something from the comments. Earlier, I said that the main program knows little about the external function. In the main program we have the declaration
integer ... calculate
which says that the external function calculate (which has an implicit interface) has a return type of integer. The function, in this case, could instead be made an internal function
program
use strings, only : parse
contains
integer function calculate
end function
end program
and not only does the function calculate have an explicit interface (removing also the need for that return declaration in the main program) but it also has access to parse through host association.

Pass a real array if a complex array is expected

I have a Fortran subroutine that expects a complex array like
subroutine foo(cnumbers, n)
integer :: n
complex :: cnumbers(n)
...
end subroutine foo
and later I want to call it like
real :: rnumbers(40)
...
call foo(rnumbers, 20)
However, I get the compiler error:
error #6633: The type of the actual argument differs from the type of the dummy argument.
Of course, this is comprehensible since a real array is not a complex array. But there must be a way to make it work.
Because if the subroutine foo and the call of foo are in different modules and are written down in different Fortran files, then the compiler does not complain, and everything works fine.
Does someone know how to make it work? How to pass a real array if a complex array is expected?
You can either use transfer(rnumbers, ...) to convert the type (a temporary array is likely to be created) or use equivalence to avoid it
real :: rnumbers(40)
complex :: cnumbers(20)
equivalence (rnumbers, cnumbers)
set the value of rnumbers
call foo(cnumbers, 20)
If you need allocatable arrays the equivalence will not work.
You can also use an external subroutine and lie the compiler about the interface and just pass the real array instead of the complex one. It is not standard conforming, but it is sometimes used. See also Gfortran complex actual to real dummy argument

Fortran and C Mixed Programming (Shared Memory)

I have an existing Fortran codebase I'm working with and it's quite large. I am no Fortran programmer so I know that I'm not doing everything correctly here.
I'm trying to create and initialize an array of 1.6 million integers. I cannot get this to initialize in Fortran (using ifort or gfort) as I either would have too many line continuations or too long of lines.
So naturally, I switched to C and wrote a function to just initialize an array and it compiles in seconds with no problem. Now I'm trying to link the two together properly. I created a small test case here to simplify things. Here are the three files I'm working with:
init.c
void c_init_()
{
static const int f_init_g[1600000] =
{
3263, 322, 3261, 60, 32249, 32244, 3229, 23408, 252407, 25326,
25805, 25723, 25562, 25787, 4549, 32248, 32244, 32243, 253207, 21806,
---------------------------------------------------------------------
25805, 25723, 25562, 25787, 4549, 32248, 32244, 32243, 253207, 21806
};
}
init_mod.f90
MODULE INIT_MOD
USE, INTRINSIC :: ISO_C_BINDING
IMPLICIT NONE
SAVE
TYPE :: INIT_TYPE
INTEGER (C_INT), DIMENSION(1600000) :: INIT
END TYPE INIT_TYPE
TYPE (C_PTR), BIND(C,NAME="f_init_g") :: INIT_CP
TYPE (INIT_TYPE), POINTER :: INIT_FP
END MODULE INIT_MOD
main.f90
PROGRAM INIT
USE INIT_MOD
USE, INTRINSIC :: ISO_C_BINDING
TYPE (INIT_TYPE) :: INIT_T
CALL c_init()
CALL C_F_POINTER(INIT_CP,INIT_FP)
INIT_T = INIT_FP
END PROGRAM INIT
I compile this using the following commands:
icc -c init.c
ifort -c init_mod.f90
ifort main.f90 init_mod.o init.o
I get a segmentation fault when running because INIT_CP points to nothing as far as I can tell. I know I'm not successfully getting INIT_CP to point at the array in my C function. So I'm trying to figure out how to do that.
I would like if someone has a suggestion on how to initialize this array natively in Fortran. My final option that I'll do is make a small initialization of this array in assembly and write a script to generate the assembly code to initialize this array myself (based off the assembly from the small initialization I can mimic the same thing for any size array). I'm not as excited to do that, but it may be the easiest and most reliable solution.
Most importantly I want other Fortran subroutines that use this array to see that it is static in shape and value so that appropriate inter procedural optimizations can be done.
Fortran-C Interoperable variables must have external linkage. As suggested by others in the comments, move the C declaration to file scope and lose the static specifier.
There is no need for an intermediate C_PTR - a Fortran array variable is directly interoperable with the appropriate C array.
Reducing the size of the array slightly:
/* C File scope */
const int f_init_g[3] = { 101, 102, 103 };
! Fortran
MODULE m
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_INT
IMPLICIT NONE
INTEGER(C_INT), BIND(C, NAME='f_init_g') :: f_init_g(3)
END MODULE m
PROGRAM p
USE m
IMPLICIT NONE
PRINT *, f_init_g(2)
END PROGRAM p
Note that the starting premise - that it is impossible to define or initialize such an array from within Fortran only - is false. The rules around constant expressions in Fortran permit reference to existing named constants, including named constants that are arrays. If you decide to persist with this madness, and assuming that the value of the initializer cannot be described by some sort of expression, consider:
INTEGER, PARAMETER :: first(10) &
= [ 3263, 322, 3261, 60, 32249, &
32244, 3229, 23408, 252407, 25326 ]
INTEGER, PARAMETER :: second(10) &
= [ 25805, 25723, 25562, 25787, 4549, &
32248, 32244, 32243, 253207, 21806]
...
INTEGER, PARAMETER :: f_init_g(1600000) = [ first, second, ... ]
You will probably need intermediate named constant arrays before the final array constructor.
In the immediate above, f_init_g is a named constant, which is very visible to the compiler, and more likely to result in the optimisations that you seek.
However, you may run into compiler complexity limits, that defeat this latter approach.
When f_init_g is a variable initialized by C, you are basically reliant on the inter-language and inter-procedural optimisation capabilities of your tool set - and if those capabilities even exist, I wouldn't expect much from them for this case. I expect that you aren't going to lose much performance wise, beyond the one-off time for IO, if you read the value of the array in from a file at runtime.

Does the Intel Fortran 95 compiler allow module arrays to be of non-constant size?

I have downloaded a Fortran 90/95 adaptive mesh refinment library (Paramesh), and now I'm trying to compile an example program that came with it. In the process I modified the Makefile to use gfortran instead of the Intel Fortran compiler.
In the library code, there is a module containing this snippet:
module physicaldata
! Many many lines of variable definitions here
!....
Public :: nfluxvar
Integer,Save :: nfluxvar
! Many many lines of variable definitions here
!....
end module physicaldata
Elsewhere there is
module flux_assign
use physicaldata
integer :: iflux_target(nfluxvar)
end module flux_assign
which is causing this error:
advance_soln_vdt.F90:16.40:
Included at amr_main_prog.F90:29:
integer :: iflux_target(nfluxvar)
1
Error: The module or main program array 'iflux_target' at (1) must have constant shape
Would that code work if compiled using another compiler? I know that with standard Fortran, or at least the one used by gfortran, requires that integer variables which are used to denote array sizes should have the parameter keyword attached to them. Is that not the case for other Fortran compilers? Do other compiler include non-standard features such as this?
Current Intel Fortran issues an error for this code.
The standard language requires that non-allocatable, non-pointer arrays declared in the specification part of a module (or main program or block data or submodule, and arrays used in a few other places) must have constant array bounds.
iflux_target is such an array.
A program with such an array is non-conforming, and will not be accepted without diagnostics by a standard conforming Fortran processor. If portability is your goal, then do not use this sort of feature. Lack of a diagnostic from previous versions of Intel Fortran was presumably an oversight.
Module arrays that need to have their size specified by a variable should be made allocatable, with the array allocated in an "initialise" procedure or similar before the operations proper provided by the module are used.
The integer used to specify a statically allocated array cannot have the save attribute in fortran as this implies it will change during the run (otherwise why use save). As the array is statically allocated, its bounds cannot change. Check out this answer for more details.
This flags the following error on the intel compiler too,
An automatic object must not appear in the specification part of a module
Note that specifying the value of nfluxvar, e.g.
integer :: nfluxvar=5
does not mean you can use it to define an array size unless you explicitly tell the compiler that it's a parameter.
There is no need to use a parameter statement for the array size nfluxvar if you use a dynamically allocated array. If you want to avoid this problem, using dynamic allocation is the best solution as it explicitly sets the array size to the current value of nfluxvar. You can even reallocate if this changes, for example,
module physicaldata
! Many many lines of variable definitions here
!....
Public :: nfluxvar
Integer,Save :: nfluxvar
! Many many lines of variable definitions here
!....
end module physicaldata
module flux_assign
use physicaldata
integer, allocatable, dimension(:) :: iflux_target
end module flux_assign
program main
use flux_assign
if (.not. allocated(iflux_target)) then
allocate(iflux_target(nfluxvar))
elseif (size(iflux_target) .ne. nfluxvar) then
deallocate(iflux_target)
allocate(iflux_target(nfluxvar))
endif
end program main
In a way this isn't truly an answer. However, any compliant Fortran compiler must have the ability to detect and report violations of specified constraints within the Fortran language specification. The constraint you come up against in the code is indeed one of those. So, does there exist a Fortran compiler which has the ability to detect this but chooses not to? I don't know. But I'd think not.
So, what is the constraint? Unless nfluxvar is a constant expression iflux_target will be an automatic object. Such an automatic object is not allowed in the scoping unit of a module - see C554 and surrounding text in Fortran 2008.
To answer the question in the title: ifort will complain loudly about such attempts.

FORTRAN 95, array must have constant shape error

I'm an absolute beginner to Fortran95 so I apologise for the simplicity of this question.
I want to be able define the dimensions of an array in terms of integers but I get the error
'The module or main program array 'arrayx' at (1) must have constant shape'
and 'Variable 'y' cannot appear in the expression at (1)' (same error for x).
Is it simply not possible to define the dimensions in terms of variables or am I just missing something obvious.
program arrayForm
implicit none
integer, save :: x=40, y=2
real, dimension(x,y) :: arrayX
end program arrayForm
Variables with the save attribute are not constants and the compiler complains with justification. In your snippet replace that attribute with the parameter attribute which makes them constant and, at compile-time, the array can be fixed in size and the compiler shouldn't complain.
As you have employed it the save attribute is redundant. When applied to variables which may go out of scope, such as variables inside the scope of a subroutine or function, it requires that the value of the variable is saved from one invocation of the sub-scope to the next. But since your variables are declared in your program scope they won't ever go out of scope during execution.
Note also that one of Fortran's gotchas for new (and old) programmers is that variables which are initialised in their declaration acquire the save attribute automatically. So, in a subroutine your line
integer, save :: x=40, y=2
could simply be written
integer :: x=40, y=2
Personally I tend to use the save attribute so that my intentions when I write a subroutine are clear to me later.

Resources