20.3. Notable Fortran/C usage differences
While the Fortran 2003 interface to SUNDIALS closely follows the C API, some differences are inevitable due to the differences between Fortran and C. In this section, we note the most critical differences. Additionally, §20.2 discusses equivalencies of data types in the two languages.
20.3.1. Creating generic SUNDIALS objects
In the C API a SUNDIALS class, such as an N_Vector, is actually a pointer to
an underlying C struct. However, in the Fortran 2003 interface, the derived type
is bound to the C struct, not the pointer to the struct. For example,
type(N_Vector) is bound to the C struct _generic_N_Vector not the
N_Vector type. The consequence of this is that creating and declaring SUNDIALS
objects in Fortran is nuanced. This is illustrated in the code snippets below:
C code:
N_Vector x;
x = N_VNew_Serial(N, sunctx);
Fortran code:
type(N_Vector), pointer :: x
x => FN_VNew_Serial(N, sunctx)
Note that in the Fortran declaration, the vector is a type(N_Vector),
pointer, and that the pointer assignment operator is then used.
20.3.2. Arrays and pointers
Unlike in the C API, in the Fortran 2003 interface, arrays and pointers are treated differently when they are return values versus arguments to a function. Additionally, pointers which are meant to be out parameters, not arrays, in the C API must still be declared as a rank-1 array in Fortran. The reason for this is partially due to the Fortran 2003 standard for C bindings, and partially due to the tool used to generate the interfaces. Regardless, the code snippets below illustrate the differences.
C code:
N_Vector x;
sunrealtype* xdata;
long int leniw, lenrw;
/* create a new serial vector */
x = N_VNew_Serial(N, sunctx);
/* capturing a returned array/pointer */
xdata = N_VGetArrayPointer(x)
/* passing array/pointer to a function */
N_VSetArrayPointer(xdata, x)
/* pointers that are out-parameters */
N_VSpace(x, &leniw, &lenrw);
Fortran code:
type(N_Vector), pointer :: x
real(c_double), pointer :: xdataptr(:)
real(c_double) :: xdata(N)
integer(c_long) :: leniw(1), lenrw(1)
! create a new serial vector
x => FN_VNew_Serial(x, sunctx)
! capturing a returned array/pointer
xdataptr => FN_VGetArrayPointer(x)
! passing array/pointer to a function
call FN_VSetArrayPointer(xdata, x)
! pointers that are out-parameters
call FN_VSpace(x, leniw, lenrw)
20.3.3. Passing procedure pointers and user data
Since functions/subroutines passed to SUNDIALS will be called from within C
code, the Fortran procedure must have the attribute bind(C). Additionally,
when providing them as arguments to a Fortran 2003 interface routine, it is
required to convert a procedure’s Fortran address to C with the Fortran
intrinsic c_funloc.
Typically when passing user data to a SUNDIALS function, a user may simply cast
some custom data structure as a void*. When using the Fortran 2003
interfaces, the same thing can be achieved. Note, the custom data structure
does not have to be bind(C) since it is never accessed on the C side.
C code:
MyUserData *udata;
void *cvode_mem;
ierr = CVodeSetUserData(cvode_mem, udata);
Fortran code:
type(MyUserData) :: udata
type(c_ptr) :: arkode_mem
ierr = FARKStepSetUserData(arkode_mem, c_loc(udata))
On the other hand, Fortran users may instead choose to store problem-specific
data, e.g. problem parameters, within modules, and thus do not need the
SUNDIALS-provided user_data pointers to pass such data back to user-supplied
functions. These users should supply the c_null_ptr input for user_data
arguments to the relevant SUNDIALS functions.
20.3.4. Passing NULL to optional parameters
In the SUNDIALS C API some functions have optional parameters that a caller can
pass as NULL. If the optional parameter is of a type that is equivalent to a
Fortran type(c_ptr) (see §20.2),
then a Fortran user can pass the intrinsic c_null_ptr. However, if the
optional parameter is of a type that is not equivalent to type(c_ptr), then
a caller must provide a Fortran pointer that is dissociated. This is
demonstrated in the code example below.
C code:
SUNLinearSolver LS;
N_Vector x, b;
/* SUNLinSolSolve expects a SUNMatrix or NULL as the second parameter. */
ierr = SUNLinSolSolve(LS, NULL, x, b);
Fortran code:
type(SUNLinearSolver), pointer :: LS
type(SUNMatrix), pointer :: A
type(N_Vector), pointer :: x, b
! Disassociate A
A => null()
! SUNLinSolSolve expects a type(SUNMatrix), pointer as the second parameter.
! Therefore, we cannot pass a c_null_ptr, rather we pass a disassociated A.
ierr = FSUNLinSolSolve(LS, A, x, b)
20.3.5. Working with N_Vector arrays
Arrays of N_Vector objects are interfaced to Fortran 2003 as an opaque
type(c_ptr). As such, it is not possible to directly index an array of
N_Vector objects returned by the N_Vector “VectorArray” operations, or
packages with sensitivity capabilities (CVODES and IDAS). Instead, SUNDIALS
provides a utility function FN_VGetVecAtIndexVectorArray wrapping
N_VGetVecAtIndexVectorArray(). The example below demonstrates accessing
a vector in a vector array.
C code:
N_Vector x;
N_Vector* vecs;
/* Create an array of N_Vectors */
vecs = N_VCloneVectorArray(count, x);
/* Fill each array with ones */
for (int i = 0; i < count; ++i)
N_VConst(vecs[i], 1.0);
Fortran code:
type(N_Vector), pointer :: x, xi
type(c_ptr) :: vecs
! Create an array of N_Vectors
vecs = FN_VCloneVectorArray(count, x)
! Fill each array with ones
do index = 0,count-1
xi => FN_VGetVecAtIndexVectorArray(vecs, index)
call FN_VConst(xi, 1.d0)
enddo
SUNDIALS also provides the functions N_VSetVecAtIndexVectorArray() and
N_VNewVectorArray() for working with N_Vector arrays, that have
corresponding Fortran interfaces FN_VSetVecAtIndexVectorArray and
FN_VNewVectorArray, respectively. These functions are particularly
useful for users of the Fortran interface to the
NVECTOR_MANYVECTOR or
NVECTOR_MPIMANYVECTOR when creating the
subvector array. Both of these functions along with
N_VGetVecAtIndexVectorArray() (wrapped as
FN_VGetVecAtIndexVectorArray) are further described in
§8.1.1.
20.3.6. Providing file pointers
There are a few functions in the SUNDIALS C API which take a FILE* argument.
Since there is no portable way to convert between a Fortran file descriptor and
a C file pointer, SUNDIALS provides two utility functions for creating a
FILE* and destroying it. These functions are defined in the module
fsundials_core_mod.
-
SUNErrCode SUNDIALSFileOpen(const char *filename, const char *mode, FILE **fp)
Deprecated since version x.y.z: See
SUNFileOpen().
-
SUNErrCode SUNFileOpen(const char *filename, const char *mode, FILE **fp)
The function allocates a
FILE*by calling the C functionfopenwith the provided filename and I/O mode.- Parameters:
filename – the path to the file, that should have Fortran type
character(kind=C_CHAR, len=*). There are two special filenames:stdoutandstderr– these two filenames will result in output going to the standard output file and standard error file, respectively.mode –
the I/O mode to use for the file. This should have the Fortran type
character(kind=C_CHAR, len=*). The string begins with one of the following characters:rto open a text file for readingr+to open a text file for reading/writingwto truncate a text file to zero length or create it for writingw+to open a text file for reading/writing or create it if it does not existato open a text file for appending, see documentation offopenfor your system/compilera+to open a text file for reading/appending, see documentation forfopenfor your system/compiler
fp – The
FILE*that will be open when the function returns. This should be a type(c_ptr) in the Fortran.
- Returns:
Usage example:
type(c_ptr) :: fp ! Open up the file output.log for writing ierr = FSUNFileOpen("output.log", "w+", fp) ! The C function ARKStepPrintMem takes void* arkode_mem and FILE* fp as arguments call FARKStepPrintMem(arkode_mem, fp) ! Close the file ierr = FSUNDIALSFileClose(fp)
Added in version X.Y.Z: Replaces
SUNDIALSFileOpen
-
SUNErrCode SUNDIALSFileClose(FILE **fp)
Deprecated since version X.Y.Z: See
SUNFileClose()
-
SUNErrCode SUNFileClose(FILE **fp)
The function deallocates a C
FILE*by calling the C functionfclosewith the provided pointer.- Parameters:
fp – the C
FILE*that was previously obtained fromfopen. This should have the Fortran typetype(c_ptr). Note that if eitherstdoutorstderrwere opened usingSUNFileOpen()
- Returns:
Added in version X.Y.Z: Replaces
SUNDIALSFileClose