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.
Related
Background: GCC 10 removed support for calling subroutines with different typed arguments. My aim is to write an interface that respects both integer, dimension(:) and integer.
(Which means I can't use other options such as embedding the scalar in an array. I have to change the interface)
According to GCC docs:
It is possible to provide standard-conforming code which allows different types of arguments by using an explicit interface and TYPE(*).
and:
Note, however, that TYPE(*) only accepts scalar arguments, unless the DIMENSION is explicitly specified. As DIMENSION(*) only supports array (including array elements) but no scalars, it is not a full replacement for C_LOC. On the other hand, assumed-type assumed-rank dummy arguments (TYPE(*), DIMENSION(..)) allow for both scalars and arrays, but require special code on the callee side to handle the array descriptor.
In the interface below I have type(*), dimension(:) :: data. How can I change it according to the text I've emphasized above?
module z
interface
subroutine a(data)
type(*), dimension(:) :: data
end subroutine a
end interface
contains
subroutine b(data)
integer :: data
call a(data)
end subroutine
subroutine c(data)
integer, dimension(:) :: data
call a(data)
end subroutine
end module
Godbolt playground
I am not aware of GCC 10 removing anything (what is your source for that?) but exactly for the reasons you mention, GCC also introduced the directive
!GCC$ attributes no_arg_check::A
(see Procedure for any type of array in Fortran )
which was already used in other compilers that enables the procedure to be called with any arguments, scalars or arrays of any rank, mainly for routines that accept buffers of any type, particularly in MPI libraries.
The new DIMENSION(..) is not really suited for use within Fortran, it requires special code that understands the standard Fortran array descriptor and it is mainly expected that it will be written in C. However, according to the very page you linked https://gcc.gnu.org/onlinedocs/gfortran/Further-Interoperability-of-Fortran-with-C.html#Further-Interoperability-of-Fortran-with-C gfortran does not yet support the standard array descriptor.
I have a problem with passing of an array from Fortran to a c function:
In the fortran the array is defined as
REAL(KIND=real_normal) , DIMENSION(:), ALLOCATABLE :: array
call cFunc(array)
If define the cFunc as
void cFunc(double *data){...}
Than the data contains only "garbage" values. Where is the problem in this case? (with integers works this solution well).
thx.
EDIT:
My platform:
Compiler: VS 2008, Intel compiler 11 version
OS: Win7
EDIT 2:
I define the interface for the c-function like this (the code is reduced to one element, which makes problems, real function has more parameters):
interface c_interface
subroutine cFunc(array) bind (C, name = "cFunc")
use iso_c_binding
REAL(c_double), DIMENSION(*)::array
end subroutine cFunc
The memory in the fortran is allocated with
ALLOCATE (array(numberOfElements))
call cFunc(array)
At the moment i get an runtime error "Floating-point overflow". In some cases the array correct elements.
The fragment REAL(KIND=real_normal) is not a complete and standard specification of a datatype. Somehwere in the source you have there must be a declaration of the variable real_normal. I'd guess that it is declared such that array is either 4- or 8-byte floating-point numbers, but that is only a guess. What array isn't is an array of default floating-point numbers (called real by Fortran).
As one of the other answerers has suggested, investigate the interoperability with C features of Fortran 2003. If your compiler doesn't implement these, ditch it and get a compiler that does.
#High Performance Mark's suggestions are very good, and I highly recommend the ISO_C_Binding of Fortran 2003 (supported by numerous Fortran compilers) for interoperability between Fortran and C -- there is a larger issue here that makes the ISO_C_Binding more useful: Fortran allocatable arrays are more complicated then ordinary arrays. If you "hack it" and directly pass a pointer to the C code, you are likely to pass a pointer to a Fortran internal structure describing the allocatable array rather than a pointer to the sequence of numeric values. Allocatable arrays aren't directly supported by the ISO_C_Binding, but should work if you write an ISO_C_Binding interface (unlike what I wrote originally) -- the book "Fortran 95/2003 explained" says that the compiler will recognize the the called routine isn't receiving an allocatable array and will perform copy-in/copy-out to match the arrays.
P.S. My guess is that copy-in/copy-out shouldn't be necessary for a simple allocatable actual argument. When the compiler recognizes via an explicit interface (which could be an ISO_C_Binding interface) that the dummy argument of the called routine is not an allocatable, the compiler should just be able to extract the pointer to the actual array from the description of the allocatable and pass that as the argument. Copy-in/copy out will be required in some cases, such as a pointer to a non-contiguous array, such as pointer with a non-unit stride (e.g., a pointer that points to elements 1, 3, 5, ...). But without any interface, the compiler will likely pass the descriptor of the allocatable array, which won't be what C is expecting....
Is real_normal 32-bit or 64-bit floating-point? What happens if you declare the function as void cFunc(float*data) ?
REAL might default to REAL*4 in which case you want a float* instead of double*.
Also make sure that you are prototyping the function before using it, otherwise C has a tendency to auto-promote floats to doubles in the absence of a reason not to. And make sure that you aren't taking a double and then taking an address of it and passing it "as" a float*.
I'm working on a project, where the results of a numerical simulation program are to be optimized to fit measured behavior. I wrote some freeform Fortran routines to extract specific data and perform some preliminary calculations, which work fine. For the optimization purpose, i plan to use a local search algorithm provided here: http://mat.uc.pt/~zhang/software.html#bobyqa
I pass some arguments like dimensions and parameter vectors into the Fortran 77 routine, and the problem is, that the transferred argument arrays don't reach the other side. Only the first element will show up in an array with dimension 1.
I found some helpful answers in How to use Fortran 77 subroutines in Fortran 90/95? and tried to contain all 77 code in a module but i still don't get it done.
An explicit interface helps to get all variables into level1 f77 subroutine, but when stuff is beeing passed to another (level2), where assumed size arrays are to be constructed, 1-dimensional arrays are generated if at all.
I compile the f77 code first using ifort -c -fixed (and tried -f77rtl), then f90 and link all together.
Why are the assumed size arrays not generated properly?? The test program from vendor works fine!
How can i pass all needed data through and back in a defined way, without using explicit interfaces? Or is there a way to define suitable interfaces?
Here some example code:
program main_f90
use types
implicit none
real(dp) :: array(N)
interface
subroutine sub77_level1(array)
implicit real*8 (a-h,o-z)
real*8, intent(inout) :: array
dimension array(:)
end subroutine
end interface
[...fill array...]
call sub77_level1(array)
end
subroutine sub77_level1(array)
implicit real*8 (a-h,o-z)
integer i1, i2, i3, i4
dimension array(:)
[...modify array...]
call sub77_level2(array(i1), array(i2), array(i3), i4)
return
end
subroutine sub77_level2(array_1, array_2, array_3, i4)
implicit real*8 (a-h,o-z)
dimension array_1(*) array_2(*) array_3( i4, * )
[...modify...]
call sub_f90( <some other arrays, intent(in / out)> )
return
end
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.
I am Fortran beginner and I am trying to adopt some ifort code for compilation with gfortran.
I have problem with the c_loc() function, which in ifort seems to accept dynamic arrays but with gfortran compilation stops with error:
Error: Argument 'septr1' to 'c_loc' at (1) must be an associated scalar POINTER
So does anyone knows how to adapt the following ifort code for compilation with gfortran?
integer(c_int), dimension(:), pointer :: septr1=>null()
type(c_PTR) :: septr
allocate (septr1(10))
septr1 = 33
septr = c_loc(septr1)
This appears to be a requirement from the old Fortran 2003 and relaxed in Fortran 2008. More recent gfortran (5+) accepts this.
You can get the location of the start of the array, the value with offset 0 in C.
septr = c_loc(septr1(1))
or generally not 1 but lbound(septr1).
See the requirements for c_loc argument in Metcalf, Reid and Cohen or in Fortran standard.
It is generally much better to pass the array by reference in normal Fortran way and not to construct an explicit c pointer. For example:
call some_c_function(n,A)
where some_c_function has Fortran interface
interface
subroutine some_c_function(n,A) bind(C)
use iso_c_binding,only: c_int,c_float !you can use also import here
integer(c_int),value :: n
real(c_float),dimension(n):: A
end subroutine some_c_function
end interface
for C prototype
void some_c_function(int n, float* A) //(hope so, I am not so good in C).
ifort developers say that standards compliance checking of c_loc, including its more restricted usage than the legacy LOC(), is not required by the provisions of the Fortran standard.
Possibly in view of the wider scope for useful application of this intrinsic function in current standards, and the need to restrict the scope of uses which need to be tested, most compilers will reject the non-standard usage.