Updating the QUDA Interface - lattice/quda GitHub Wiki

The QUDA interface is self contained within include/quda.h, with only this file needing to be included in a host application in order to interface to QUDA. This file includes include/enum_quda.h which defines the many enumeration types used both by the QUDA interface and internally. Feature additions to QUDA, e.g., adding a new Krylov solver, exposing a new feature, etc. will likely require that both quda.h and enum_quda.h are updated.

Interface Functions

All QUDA interface functions are defined solely in quda.h. In order to maintain C compatibility, only valid C types, structures or pointers may be used as arguments to interface functions. The convention is to use camel case for these functions, and in order to avoid naming collisions, all functions must have the Quda suffix. Doxygen markup should be used to describe each parameter passed to the interface function as well as the action of the function. For example

/**
 * Perform the solve, according to the parameters set in param.  It
 * is assumed that the gauge field has already been loaded via
 * loadGaugeQuda().
 * @param h_x    Solution spinor field
 * @param h_b    Source spinor field
 * @param param  Contains all metadata regarding host and device
 *               storage and solver parameters
 */
void invertQuda(void *h_x, void *h_b, QudaInvertParam *param);

Interface Structs

For each algorithm or QUDA interaction exposed to the QUDA interface there is an equivalent struct that defines the necessary parameters needed to define this. For example QudaGaugeParam defines the necessary parameters for controlling the download or upload of a gauge field to QUDA.

Enumeration Types

Enumeration types should be defined using C-style enumerations, and C++-style, in order to maintain C compatibility. Since this is a C file, namespace sandboxing is not possible, and so all enumerations must be prefixed by QUDA_ and the enumeration type should be prefixed by Quda and suffixed by Type. For example, the QUDA precision enumeration type QudaPrecision is given by

  typedef enum QudaPrecision_s {
    QUDA_HALF_PRECISION = 2,
    QUDA_SINGLE_PRECISION = 4,
    QUDA_DOUBLE_PRECISION = 8,
    QUDA_INVALID_PRECISION = QUDA_INVALID_ENUM
  } QudaPrecision;

Enumeration values

QUDA defines the macro QUDA_INVALID_ENUM that is used as general invalid value for all enumeration types in order to make simple initialisation check. Thus all enum types should define an invalid enumeration, and set this value to be equal to QUDA_INVALID_TYPE.

In general specific values of enumerations should not be set (if nothing else this makes the Fortran interface easier), unless it makes sense to do so. For example, there is no reason to ascribe values to QudaMatPCType

typedef enum QudaMatPCType_s {
  QUDA_MATPC_EVEN_EVEN,
  QUDA_MATPC_ODD_ODD,
  QUDA_MATPC_EVEN_EVEN_ASYMMETRIC,
  QUDA_MATPC_ODD_ODD_ASYMMETRIC,
  QUDA_MATPC_INVALID = QUDA_INVALID_ENUM
} QudaMatPCType;

but ascribing values to QudaReconstructType does make sense

typedef enum QudaReconstructType_s {
  QUDA_RECONSTRUCT_NO = 18, // store all 18 real numbers explicitly
  QUDA_RECONSTRUCT_12 = 12, // reconstruct from 12 real numbers
  QUDA_RECONSTRUCT_8 = 8,  // reconstruct from 8 real numbers
  QUDA_RECONSTRUCT_9 = 9,   // used for storing HISQ long-link variables
  QUDA_RECONSTRUCT_13 = 13, // used for storing HISQ long-link variables
  QUDA_RECONSTRUCT_10 = 10, // 10-number parameterization used for storing the momentum field
  QUDA_RECONSTRUCT_INVALID = QUDA_INVALID_ENUM
} QudaReconstructType;

Fortran Interface

While most applications that link to QUDA are C/C++-based, QUDA also provides a Fortran interface. It is important to remember when any updates to the C interface are undertaken, the Fortran interface is also updated to ensure compatibility.

The Fortran interface is presently based on F90, which does not have enumerated types. Thus to ensure a type-compatible interface, the C preprocessor is used to define all the C enumerations into their literal values which are then included into the host Fortran application. This is extremely brittle, and will break if the any of the order of C enumerations is changed. Whenever any changes are made to include/enum_quda.h, equivalent changes must also be made to include/enum_quda_fortran.h.

The C-interface structs used to parameterise each interface function have Fortran module equivalents, defined in lib/quda_fortran.F90. The C-struct name is transliterated to Fortran with underscores between each word, with lower case being the convention. This file has the suffix .F90 to signify it is input to C preprocessor (to process the macro-based enumerations: from this valid Fortran 90 is produced (quda_fortran.f90) file which is compiled to create the Fortran module.

The Fortran function signature is equivalent to the C interface, except that it is the convention that it is all lower case, with underscores between each word with a trailing underscore used at the end of each function, as per the usual C/Fortran interfacing convention. Any function parameters that are pass by value become pass by reference. For example these two C interface functions

void invertQuda(void *h_x, void *h_b, QudaInvertParam *param); // C
void updateGaugeFieldQuda(void* gauge, void* momentum, double dt, 
                          int conj_mom, int exact, QudaGaugeParam* param);

become

void invert_quda_(void *h_x, void *h_b, QudaInvertParam *param);
void update_gauge_field_quda_(void* gauge, void* momentum, double *dt, 
                              bool *conj_mom, bool *exact, QudaGaugeParam* param);

in the Fortran interface. While Fortran does not require function declarations since function presence is checked at link time, a function header file is not required, nevertheless for documentation purposes the include/quda_fortran.h file serves this purpose. Thus whenever include/quda.h is updated, quda_fortran.h should also be updated. These fortran interface stubs should be implemented at the bottom of lib/interface_quda.cpp.