iup-stack/fftw/doc/legacy-fortran.texi

384 lines
15 KiB
Plaintext
Raw Normal View History

2023-02-20 16:44:45 +00:00
@node Calling FFTW from Legacy Fortran, Upgrading from FFTW version 2, Calling FFTW from Modern Fortran, Top
@chapter Calling FFTW from Legacy Fortran
@cindex Fortran interface
This chapter describes the interface to FFTW callable by Fortran code
in older compilers not supporting the Fortran 2003 C interoperability
features (@pxref{Calling FFTW from Modern Fortran}). This interface
has the major disadvantage that it is not type-checked, so if you
mistake the argument types or ordering then your program will not have
any compiler errors, and will likely crash at runtime. So, greater
care is needed. Also, technically interfacing older Fortran versions
to C is nonstandard, but in practice we have found that the techniques
used in this chapter have worked with all known Fortran compilers for
many years.
The legacy Fortran interface differs from the C interface only in the
prefix (@samp{dfftw_} instead of @samp{fftw_} in double precision) and
a few other minor details. This Fortran interface is included in the
FFTW libraries by default, unless a Fortran compiler isn't found on
your system or @code{--disable-fortran} is included in the
@code{configure} flags. We assume here that the reader is already
familiar with the usage of FFTW in C, as described elsewhere in this
manual.
The MPI parallel interface to FFTW is @emph{not} currently available
to legacy Fortran.
@menu
* Fortran-interface routines::
* FFTW Constants in Fortran::
* FFTW Execution in Fortran::
* Fortran Examples::
* Wisdom of Fortran?::
@end menu
@c -------------------------------------------------------
@node Fortran-interface routines, FFTW Constants in Fortran, Calling FFTW from Legacy Fortran, Calling FFTW from Legacy Fortran
@section Fortran-interface routines
Nearly all of the FFTW functions have Fortran-callable equivalents.
The name of the legacy Fortran routine is the same as that of the
corresponding C routine, but with the @samp{fftw_} prefix replaced by
@samp{dfftw_}.@footnote{Technically, Fortran 77 identifiers are not
allowed to have more than 6 characters, nor may they contain
underscores. Any compiler that enforces this limitation doesn't
deserve to link to FFTW.} The single and long-double precision
versions use @samp{sfftw_} and @samp{lfftw_}, respectively, instead of
@samp{fftwf_} and @samp{fftwl_}; quadruple precision (@code{real*16})
is available on some systems as @samp{fftwq_} (@pxref{Precision}).
(Note that @code{long double} on x86 hardware is usually at most
80-bit extended precision, @emph{not} quadruple precision.)
For the most part, all of the arguments to the functions are the same,
with the following exceptions:
@itemize @bullet
@item
@code{plan} variables (what would be of type @code{fftw_plan} in C),
must be declared as a type that is at least as big as a pointer
(address) on your machine. We recommend using @code{integer*8} everywhere,
since this should always be big enough.
@cindex portability
@item
Any function that returns a value (e.g. @code{fftw_plan_dft}) is
converted into a @emph{subroutine}. The return value is converted into
an additional @emph{first} parameter of this subroutine.@footnote{The
reason for this is that some Fortran implementations seem to have
trouble with C function return values, and vice versa.}
@item
@cindex column-major
The Fortran routines expect multi-dimensional arrays to be in
@emph{column-major} order, which is the ordinary format of Fortran
arrays (@pxref{Multi-dimensional Array Format}). They do this
transparently and costlessly simply by reversing the order of the
dimensions passed to FFTW, but this has one important consequence for
multi-dimensional real-complex transforms, discussed below.
@item
Wisdom import and export is somewhat more tricky because one cannot
easily pass files or strings between C and Fortran; see @ref{Wisdom of
Fortran?}.
@item
Legacy Fortran cannot use the @code{fftw_malloc} dynamic-allocation routine.
If you want to exploit the SIMD FFTW (@pxref{SIMD alignment and fftw_malloc}), you'll
need to figure out some other way to ensure that your arrays are at
least 16-byte aligned.
@item
@tindex fftw_iodim
@cindex guru interface
Since Fortran 77 does not have data structures, the @code{fftw_iodim}
structure from the guru interface (@pxref{Guru vector and transform
sizes}) must be split into separate arguments. In particular, any
@code{fftw_iodim} array arguments in the C guru interface become three
integer array arguments (@code{n}, @code{is}, and @code{os}) in the
Fortran guru interface, all of whose lengths should be equal to the
corresponding @code{rank} argument.
@item
The guru planner interface in Fortran does @emph{not} do any automatic
translation between column-major and row-major; you are responsible
for setting the strides etcetera to correspond to your Fortran arrays.
However, as a slight bug that we are preserving for backwards
compatibility, the @samp{plan_guru_r2r} in Fortran @emph{does} reverse the
order of its @code{kind} array parameter, so the @code{kind} array
of that routine should be in the reverse of the order of the iodim
arrays (see above).
@end itemize
In general, you should take care to use Fortran data types that
correspond to (i.e. are the same size as) the C types used by FFTW.
In practice, this correspondence is usually straightforward
(i.e. @code{integer} corresponds to @code{int}, @code{real}
corresponds to @code{float}, etcetera). The native Fortran
double/single-precision complex type should be compatible with
@code{fftw_complex}/@code{fftwf_complex}. Such simple correspondences
are assumed in the examples below.
@cindex portability
@c -------------------------------------------------------
@node FFTW Constants in Fortran, FFTW Execution in Fortran, Fortran-interface routines, Calling FFTW from Legacy Fortran
@section FFTW Constants in Fortran
When creating plans in FFTW, a number of constants are used to specify
options, such as @code{FFTW_MEASURE} or @code{FFTW_ESTIMATE}. The
same constants must be used with the wrapper routines, but of course the
C header files where the constants are defined can't be incorporated
directly into Fortran code.
Instead, we have placed Fortran equivalents of the FFTW constant
definitions in the file @code{fftw3.f}, which can be found in the same
directory as @code{fftw3.h}. If your Fortran compiler supports a
preprocessor of some sort, you should be able to @code{include} or
@code{#include} this file; otherwise, you can paste it directly into
your code.
@cindex flags
In C, you combine different flags (like @code{FFTW_PRESERVE_INPUT} and
@code{FFTW_MEASURE}) using the @samp{@code{|}} operator; in Fortran
you should just use @samp{@code{+}}. (Take care not to add in the
same flag more than once, though. Alternatively, you can use the
@code{ior} intrinsic function standardized in Fortran 95.)
@c -------------------------------------------------------
@node FFTW Execution in Fortran, Fortran Examples, FFTW Constants in Fortran, Calling FFTW from Legacy Fortran
@section FFTW Execution in Fortran
In C, in order to use a plan, one normally calls @code{fftw_execute},
which executes the plan to perform the transform on the input/output
arrays passed when the plan was created (@pxref{Using Plans}). The
corresponding subroutine call in legacy Fortran is:
@example
call dfftw_execute(plan)
@end example
@findex dfftw_execute
However, we have had reports that this causes problems with some
recent optimizing Fortran compilers. The problem is, because the
input/output arrays are not passed as explicit arguments to
@code{dfftw_execute}, the semantics of Fortran (unlike C) allow the
compiler to assume that the input/output arrays are not changed by
@code{dfftw_execute}. As a consequence, certain compilers end up
optimizing out or repositioning the call to @code{dfftw_execute},
assuming incorrectly that it does nothing.
There are various workarounds to this, but the safest and simplest
thing is to not use @code{dfftw_execute} in Fortran. Instead, use the
functions described in @ref{New-array Execute Functions}, which take
the input/output arrays as explicit arguments. For example, if the
plan is for a complex-data DFT and was created for the arrays
@code{in} and @code{out}, you would do:
@example
call dfftw_execute_dft(plan, in, out)
@end example
@findex dfftw_execute_dft
There are a few things to be careful of, however:
@itemize @bullet
@item
You must use the correct type of execute function, matching the way
the plan was created. Complex DFT plans should use
@code{dfftw_execute_dft}, Real-input (r2c) DFT plans should use use
@code{dfftw_execute_dft_r2c}, and real-output (c2r) DFT plans should
use @code{dfftw_execute_dft_c2r}. The various r2r plans should use
@code{dfftw_execute_r2r}.
@item
You should normally pass the same input/output arrays that were used when
creating the plan. This is always safe.
@item
@emph{If} you pass @emph{different} input/output arrays compared to
those used when creating the plan, you must abide by all the
restrictions of the new-array execute functions (@pxref{New-array
Execute Functions}). The most difficult of these, in Fortran, is the
requirement that the new arrays have the same alignment as the
original arrays, because there seems to be no way in legacy Fortran to obtain
guaranteed-aligned arrays (analogous to @code{fftw_malloc} in C). You
can, of course, use the @code{FFTW_UNALIGNED} flag when creating the
plan, in which case the plan does not depend on the alignment, but
this may sacrifice substantial performance on architectures (like x86)
with SIMD instructions (@pxref{SIMD alignment and fftw_malloc}).
@ctindex FFTW_UNALIGNED
@end itemize
@c -------------------------------------------------------
@node Fortran Examples, Wisdom of Fortran?, FFTW Execution in Fortran, Calling FFTW from Legacy Fortran
@section Fortran Examples
In C, you might have something like the following to transform a
one-dimensional complex array:
@example
fftw_complex in[N], out[N];
fftw_plan plan;
plan = fftw_plan_dft_1d(N,in,out,FFTW_FORWARD,FFTW_ESTIMATE);
fftw_execute(plan);
fftw_destroy_plan(plan);
@end example
In Fortran, you would use the following to accomplish the same thing:
@example
double complex in, out
dimension in(N), out(N)
integer*8 plan
call dfftw_plan_dft_1d(plan,N,in,out,FFTW_FORWARD,FFTW_ESTIMATE)
call dfftw_execute_dft(plan, in, out)
call dfftw_destroy_plan(plan)
@end example
@findex dfftw_plan_dft_1d
@findex dfftw_execute_dft
@findex dfftw_destroy_plan
Notice how all routines are called as Fortran subroutines, and the
plan is returned via the first argument to @code{dfftw_plan_dft_1d}.
Notice also that we changed @code{fftw_execute} to
@code{dfftw_execute_dft} (@pxref{FFTW Execution in Fortran}). To do
the same thing, but using 8 threads in parallel (@pxref{Multi-threaded
FFTW}), you would simply prefix these calls with:
@example
integer iret
call dfftw_init_threads(iret)
call dfftw_plan_with_nthreads(8)
@end example
@findex dfftw_init_threads
@findex dfftw_plan_with_nthreads
(You might want to check the value of @code{iret}: if it is zero, it
indicates an unlikely error during thread initialization.)
To check the number of threads currently being used by the planner, you
can do the following:
@example
integer iret
call dfftw_planner_nthreads(iret)
@end example
@findex dfftw_planner_nthreads
To transform a three-dimensional array in-place with C, you might do:
@example
fftw_complex arr[L][M][N];
fftw_plan plan;
plan = fftw_plan_dft_3d(L,M,N, arr,arr,
FFTW_FORWARD, FFTW_ESTIMATE);
fftw_execute(plan);
fftw_destroy_plan(plan);
@end example
In Fortran, you would use this instead:
@example
double complex arr
dimension arr(L,M,N)
integer*8 plan
call dfftw_plan_dft_3d(plan, L,M,N, arr,arr,
& FFTW_FORWARD, FFTW_ESTIMATE)
call dfftw_execute_dft(plan, arr, arr)
call dfftw_destroy_plan(plan)
@end example
@findex dfftw_plan_dft_3d
Note that we pass the array dimensions in the ``natural'' order in both C
and Fortran.
To transform a one-dimensional real array in Fortran, you might do:
@example
double precision in
dimension in(N)
double complex out
dimension out(N/2 + 1)
integer*8 plan
call dfftw_plan_dft_r2c_1d(plan,N,in,out,FFTW_ESTIMATE)
call dfftw_execute_dft_r2c(plan, in, out)
call dfftw_destroy_plan(plan)
@end example
@findex dfftw_plan_dft_r2c_1d
@findex dfftw_execute_dft_r2c
To transform a two-dimensional real array, out of place, you might use
the following:
@example
double precision in
dimension in(M,N)
double complex out
dimension out(M/2 + 1, N)
integer*8 plan
call dfftw_plan_dft_r2c_2d(plan,M,N,in,out,FFTW_ESTIMATE)
call dfftw_execute_dft_r2c(plan, in, out)
call dfftw_destroy_plan(plan)
@end example
@findex dfftw_plan_dft_r2c_2d
@strong{Important:} Notice that it is the @emph{first} dimension of the
complex output array that is cut in half in Fortran, rather than the
last dimension as in C. This is a consequence of the interface routines
reversing the order of the array dimensions passed to FFTW so that the
Fortran program can use its ordinary column-major order.
@cindex column-major
@cindex r2c/c2r multi-dimensional array format
@c -------------------------------------------------------
@node Wisdom of Fortran?, , Fortran Examples, Calling FFTW from Legacy Fortran
@section Wisdom of Fortran?
In this section, we discuss how one can import/export FFTW wisdom
(saved plans) to/from a Fortran program; we assume that the reader is
already familiar with wisdom, as described in @ref{Words of
Wisdom-Saving Plans}.
@cindex portability
The basic problem is that is difficult to (portably) pass files and
strings between Fortran and C, so we cannot provide a direct Fortran
equivalent to the @code{fftw_export_wisdom_to_file}, etcetera,
functions. Fortran interfaces @emph{are} provided for the functions
that do not take file/string arguments, however:
@code{dfftw_import_system_wisdom}, @code{dfftw_import_wisdom},
@code{dfftw_export_wisdom}, and @code{dfftw_forget_wisdom}.
@findex dfftw_import_system_wisdom
@findex dfftw_import_wisdom
@findex dfftw_export_wisdom
@findex dfftw_forget_wisdom
So, for example, to import the system-wide wisdom, you would do:
@example
integer isuccess
call dfftw_import_system_wisdom(isuccess)
@end example
As usual, the C return value is turned into a first parameter;
@code{isuccess} is non-zero on success and zero on failure (e.g. if
there is no system wisdom installed).
If you want to import/export wisdom from/to an arbitrary file or
elsewhere, you can employ the generic @code{dfftw_import_wisdom} and
@code{dfftw_export_wisdom} functions, for which you must supply a
subroutine to read/write one character at a time. The FFTW package
contains an example file @code{doc/f77_wisdom.f} demonstrating how to
implement @code{import_wisdom_from_file} and
@code{export_wisdom_to_file} subroutines in this way. (These routines
cannot be compiled into the FFTW library itself, lest all FFTW-using
programs be required to link with the Fortran I/O library.)