1
0
mirror of https://github.com/TREX-CoE/trexio.git synced 2024-06-02 03:15:40 +02:00
trexio/src/templates_front/templator_front.org

208 KiB
Raw Blame History

Front end API

Coding conventions

  • integer types will be defined using types given in stdint.h
  • pointers are always initialized to NULL
  • when memory is freed, the pointer is set to NULL
  • assert.h should be used extensively
  • variable names are in lower case
  • #define constants are in upper case
  • structs are suffixed by _s
  • types are suffixed by _t
  • API calls return trexio_exit_code (except for trexio_open function)

Memory allocation

Memory allocation of structures can be facilitated by using the following macros, which ensure that the size of the allocated object is the same as the size of the data type pointed by the pointer. For CALLOC, we allocate N+1 to avoid errors when N=0.

#define MALLOC(T) (T*) malloc (sizeof(T))
#define CALLOC(N,T) (T*) calloc ( (N)+1 , sizeof(T) )

When a pointer is freed, it should be set to NULL. This can be facilitated by the use of the following macro:

#define FREE(X) { free(X) ; (X)=NULL; }

The maximum string size for the filenames is 4096 characters.

#define TREXIO_MAX_FILENAME_LENGTH 4096

Front end

All calls to TREXIO are thread-safe. TREXIO front end is modular, which simplifies implementation of new back ends.

Error handling

Macro Code Description
TREXIO_FAILURE -1 'Unknown failure'
TREXIO_SUCCESS 0 'Success'
TREXIO_INVALID_ARG_1 1 'Invalid argument 1'
TREXIO_INVALID_ARG_2 2 'Invalid argument 2'
TREXIO_INVALID_ARG_3 3 'Invalid argument 3'
TREXIO_INVALID_ARG_4 4 'Invalid argument 4'
TREXIO_INVALID_ARG_5 5 'Invalid argument 5'
TREXIO_END 6 'End of file'
TREXIO_READONLY 7 'Read-only file'
TREXIO_ERRNO 8 strerror(errno)
TREXIO_INVALID_ID 9 'Invalid ID'
TREXIO_ALLOCATION_FAILED 10 'Allocation failed'
TREXIO_HAS_NOT 11 'Element absent'
TREXIO_INVALID_NUM 12 'Invalid (negative or 0) dimension'
TREXIO_ATTR_ALREADY_EXISTS 13 'Attribute already exists'
TREXIO_DSET_ALREADY_EXISTS 14 'Dataset already exists'
TREXIO_OPEN_ERROR 15 'Error opening file'
TREXIO_LOCK_ERROR 16 'Error locking file'
TREXIO_UNLOCK_ERROR 17 'Error unlocking file'
TREXIO_FILE_ERROR 18 'Invalid file'
TREXIO_GROUP_READ_ERROR 19 'Error reading group'
TREXIO_GROUP_WRITE_ERROR 20 'Error writing group'
TREXIO_ELEM_READ_ERROR 21 'Error reading element'
TREXIO_ELEM_WRITE_ERROR 22 'Error writing element'
TREXIO_UNSAFE_ARRAY_DIM 23 'Access to memory beyond allocated'
TREXIO_ATTR_MISSING 24 'Attribute does not exist in the file'
TREXIO_DSET_MISSING 25 'Dataset does not exist in the file'
TREXIO_BACK_END_MISSING 26 'Requested back end is disabled'
TREXIO_INVALID_ARG_6 27 'Invalid argument 6'
TREXIO_INVALID_ARG_7 28 'Invalid argument 7'
TREXIO_INVALID_ARG_8 29 'Invalid argument 8'
TREXIO_INVALID_STR_LEN 30 'Invalid max_str_len'
TREXIO_INT_SIZE_OVERFLOW 31 'Possible integer overflow'
TREXIO_SAFE_MODE 32 'Unsafe operation in safe mode'
TREXIO_INVALID_ELECTRON_NUM 33 'Inconsistent number of electrons'
TREXIO_INVALID_DETERMINANT_NUM 34 'Inconsistent number of determinants'
TREXIO_INVALID_STATE 35 'Inconsistent state of the file'
TREXIO_VERSION_PARSING_ISSUE 36 'Failed to parse package_version'
TREXIO_PHASE_CHANGE 37 'The function succeeded with a change of sign'

IMPORTANT! The code below has to be executed within Emacs each time a new error code is added to the table above. Otherwise, the codes and the corresponding message are not propagated to the source code.

""" This script generates the C and Fortran constants for the error
 codes from the org-mode table.
"""

result = [ "#+begin_src c :tangle prefix_front.h :exports none" ]
for (text, code,_) in table:
text=text.replace("~","")
result += [ f"#define {text:30s} ((trexio_exit_code) {code:d})" ]
result += [ "#+end_src" ]

result += [ "" ]

result += [ "#+begin_src f90 :tangle prefix_fortran.f90 :exports none" ]
for (text, code,_) in table:
text=text.replace("~","")
result += [ f"   integer(trexio_exit_code), parameter :: {text:30s} = {code:d}" ]
result += [ "#+end_src" ]

result += [ "" ]

result += [ "#+begin_src python :tangle prefix_python.py :exports none" ]
result += [ "# define TREXIO exit codes" ]
for (text, code,_) in table:
text=text.replace("~","")
result += [ f"{text:30s} = {code:d}" ]
result += [ "#+end_src" ]

return '\n'.join(result)

Decoding errors

The trexio_string_of_error converts an exit code into a string. The string is assumed to be large enough to contain the error message (typically 128 characters).

To decode the error messages, trexio_string_of_error converts an error code into a string.

128

IMPORTANT! The code below has to be executed within Emacs each time a new error code is added to the table above. Otherwise, the codes and the corresponding message are not propagated to the source code.

#+NAME:cases

C source code
const char*
trexio_string_of_error (const trexio_exit_code error)
{
switch (error) {
<<cases()>>
}
return "Unknown error";
}

void
trexio_string_of_error_f (const trexio_exit_code error, char result[<<MAX_STRING_LENGTH()>>])
{
strncpy(result, trexio_string_of_error(error), <<MAX_STRING_LENGTH()>>);
}
Fortran interface
interface
 subroutine trexio_string_of_error (error, string) bind(C, name='trexio_string_of_error_f')
   use, intrinsic :: iso_c_binding
   import
   integer(trexio_exit_code), intent(in), value :: error
   character(kind=c_char), intent(out)          :: string(<<MAX_STRING_LENGTH()>>)
 end subroutine trexio_string_of_error
end interface
Python interface
class Error(Exception):
  """Base class for TREXIO errors.

  Attributes:
     error: int -- exit code returned by the call to TREXIO library;
     message: str -- decoded string corresponding to trexio_exit_code.

  """

  def __init__(self, trexio_return_code):
      self.error = trexio_return_code
      self.message = string_of_error(trexio_return_code)
      super().__init__(self.message)


def string_of_error(return_code: int) -> str:
  """Decode the TREXIO exit code.

  Argument is an integer return code that correspond to one of the TREXIO errors.
  Returns a string that contains description of TREXIO ~return_code~.
  """

  return pytr.trexio_string_of_error(return_code)

Back ends

TREXIO has several back ends:

  1. TREXIO_HDF5 relies on extensive use of the HDF5 library and the associated file format. The HDF5 file is binary and tailored to high-performance I/O. This back end is the default one. HDF5 can be compiled with MPI for parallel I/O. Note, that HDF5 has to be downloaded and installed independently of TREXIO, which may cause some obstacles, especially when the user is not allowed to install external software. The produced files usually have .h5 extension.
  2. TREXIO_TEXT relies on basic file I/O in C, namely fopen, fclose, fprintf, fscanf etc. from stdio.h library. This back end is not optimized for performance. It is supposed to be used for debug purposes or, for example, when the user wants to modify some data manually within the file. This back end is supposed to work "out-of-the-box" since there are no external dependencies, which might be useful for users that do not have access to HDF5 library. The produced files usually have .txt extension.

TREXIO_AUTO can be provided as a back end when opening an existing TREXIO file in read-only 'r' mode. In this case, TREXIO will try to automatically detect the back end, which should be used to open the file.

Additional back ends can be implemented thanks to the modular nature of the front end. This can be achieved by adding a new case (corresponding to the desired back end) in the front-end switch. Then the corresponding back-end has/read/write functions has to be implemented. For example, see the commented lines that correspond to the TREXIO_JSON back end (not implemented yet).

Note: It is important to increment the value of TREXIO_INVALID_BACK_END when a new back end is implemented. Otherwise, it will not be available.

C

typedef int32_t back_end_t;

#define TREXIO_HDF5             ( (back_end_t) 0 )
#define TREXIO_TEXT             ( (back_end_t) 1 )
#define TREXIO_INVALID_BACK_END ( (back_end_t) 2 )
#define TREXIO_AUTO             TREXIO_INVALID_BACK_END
/*#define TREXIO_JSON             ( (back_end_t) 2 )*/

#define TREXIO_DELIM            "\n"

The helper function trexio_has_back_end returns true if TREXIO compilation includes a back end provided as an argument; false otherwise. This is useful due to the fact that HDF5 back end can be disabled at configure step.

bool trexio_has_backend(back_end_t back_end);
bool trexio_has_back_end(back_end_t back_end);
bool trexio_has_back_end(back_end_t back_end) {
switch (back_end) {
 case TREXIO_TEXT:
   return true;
 case TREXIO_HDF5:
#ifdef HAVE_HDF5
   return true;
#else
   return false;
#endif
}
return false;
}

bool trexio_has_backend(back_end_t back_end) {
return trexio_has_back_end(back_end);
}

Fortran

integer(trexio_back_end_t), parameter :: TREXIO_HDF5 = 0
integer(trexio_back_end_t), parameter :: TREXIO_TEXT = 1
! integer(trexio_back_end_t), parameter :: TREXIO_JSON = 2
integer(trexio_back_end_t), parameter :: TREXIO_INVALID_BACK_END = 2
integer(trexio_back_end_t), parameter :: TREXIO_AUTO = TREXIO_INVALID_BACK_END

The function below is a Fortran interface for the aforementioned C-compatible trexio_has_back_end function.

interface
 logical(c_bool) function trexio_has_back_end (back_end) bind(C)
   use, intrinsic :: iso_c_binding
   import
   integer(trexio_back_end_t), intent(in), value :: back_end
 end function trexio_has_back_end
end interface

interface
 logical(c_bool) function trexio_has_backend (back_end) bind(C)
   use, intrinsic :: iso_c_binding
   import
   integer(trexio_back_end_t), intent(in), value :: back_end
 end function trexio_has_backend
end interface

Originally, the function was named trexio_has_backend. For consistency, in version 2.2 it was renamed trexio_has_back_end.

Python

# define TREXIO back ends
TREXIO_HDF5 = 0
TREXIO_TEXT = 1
#TREXIO_JSON = 2
TREXIO_INVALID_BACK_END = 2
TREXIO_AUTO = TREXIO_INVALID_BACK_END

Read/write behavior

Every time a reading function is called, the data is read from the disk. If data needs to be cached, this is left to the user of the library.

Writing to TREXIO files is done with transactions (all-or-nothing effect). File writes are attempted by calling explicitly the write (TREXIO_HDF5) or flush (TREXIO_TEXT) function, or when the TREXIO file is closed. If writing is impossible because the data is not valid, no data is written.

The order in which the data is written is not necessarily consistent with the order in which the function calls were made.

The TREXIO files are supposed to be opened by only one program at a time: if the same TREXIO file is modified simultaneously by multiple concurrent programs, the behavior is not specified.

TREXIO file type

trexio_s is the the main type for TREXIO files, visible to the users of the library. This type is kept opaque, and all modifications to the files will be necessarily done through the use of functions, taking such a type as argument.

File creation and opening functions will return TREXIO file handles, namely pointers to trexio_s types. All functions accessing to the TREXIO files will have as a first argument the TREXIO file handle.

typedef struct trexio_s trexio_t;
struct trexio_s {
char              file_name[TREXIO_MAX_FILENAME_LENGTH];
pthread_mutex_t   thread_lock;
back_end_t        back_end;
char              mode;
bool              one_based;
int32_t           state;
int16_t           version_major;
int16_t           version_minor;
int16_t           version_patch;
char              version[16];
};

File class for the Python API is defined below. Use of Python class make it more intuitive and more python-ic to work with TREXIO files.

class File:
 """trexio.File class.

 General information about the TREXIO file.

 Parameters:

 filename: str
     Is a name of the full path to the TREXIO file.
 back_end: int
     One of the currently supported TREXIO back ends.
     For example, TREXIO_HDF5 (0) or TREXIO_TEXT (1).
 mode: str
     One of the currently supported TREXIO open modes.
     For example, 'r' or 'w'.
 state: int
     Active (excited or ground) state of the file.
     Default is 0.
 isOpen: bool
     Flag indicating whether the current object is still open for I/O
 pytrexio_s:
     A PyObject corresponding to SWIG proxy of the trexio_s struct in C.
     This argument is in fact a TREXIO file handle, which is required for
     communicating with the C back end.
 info: dict
     Dictionary of key-value pairs with additional information about the file.
 """


 def __init__(self, filename, mode='r', back_end=TREXIO_HDF5,
              pytrexio_s=None, info=None):
     """TREXIO File class constructor."""
     self.filename = filename
     self.mode = mode
     self.state = 0

     self.isOpen = False
     self.exists = False
     if pytrexio_s is None:
         self.pytrexio_s = _open(filename, mode, back_end)
         self.isOpen = True
         self.exists = True
     else:
         self.pytrexio_s = pytrexio_s
         self.isOpen = None
         self.exists = None

     self.back_end = self.pytrexio_s.back_end
     self.info = info


 def __enter__(self):
     """Enter statement for with ... as ... handling."""
     return self


 def __exit__(self, *args):
     """Exit statement for with ... as ... handling."""
     if self.isOpen:
        self.close()


 def close(self):
     """Close a TREXIO File."""
     if self.isOpen:
         _close(self.pytrexio_s)
         self.isOpen = False
     else:
         raise Exception("TREXIO File object has not been opened.")


 def set_state(self, state):
     """Set the state of the TREXIO File."""
     if not isinstance(state, int):
         raise TypeError("state argument has to be int")

     rc = pytr.trexio_set_state(self.pytrexio_s, state)
     if rc != TREXIO_SUCCESS:
         raise Error(rc)

     self.state = state


 def get_state(self):
     """Get the state of the TREXIO File."""
     rc, state = pytr.trexio_get_state(self.pytrexio_s)
     if rc != TREXIO_SUCCESS:
         raise Error(rc)

     if state != self.state:
         raise Exception("Inconsistent state of the TREXIO file.")

     return self.state

 def inquire(self):
     """Inquire whether a TREXIO file exists."""
     self.exists = _inquire(self.filename)


 def __del__(self):
     """TREXIO File class destructor."""
     if self.isOpen:
         _close(self.pytrexio_s)
     elif self.isOpen is None:
         raise Exception("[WIP]: TREXIO file handle provided but what if the file is already closed?")
     else:
         pass

File opening

trexio_open creates a new TREXIO file or opens the existing one.

Input parameters:

  1. file_name - string containing file name
  2. mode - character containing open mode (see below)

    • 'w' - (write) creates a new file as READWRITE
    • 'r' - (read) opens existing file as READONLY
    • 'u' - (unsafe) opens existing file as READWRITE with the possibility to overwrite blocks and delete full groups.
  3. back_end - integer number (or the corresponding global parameter) specifying the back end

    • TREXIO_HDF5 - for HDF5 back end (integer alternative: 0)
    • TREXIO_TEXT - for TEXT back end (integer alternative: 1)
    • TREXIO_AUTO - to automatically detect the applicable back end

Output:

  • Pointer to the trexio_t (struct) file handle.

Upon creation of the TREXIO file, the package_version attribute of the metadata group is set to the TREXIO_PACKAGE_VERSION string. This attribute is not automatically updated when the file is modified with newer versions. However, this can be done manually by the user in unsafe mode.

Note: internal consistency is not guaranteed once the file has been modified in 'u' (unsafe) mode. Upon the first unsafe call to trexio_open the unsafe attribute of the metadata group will be set to 1 (true) in order to indicate that the file might be inconsistent due to the use of 'u' mode.

Note: the file_name in TEXT back end actually corresponds to the name of the directory where .txt data files are stored. The actual name of each .txt file corresponds to the group name provided in trex.config (e.g. nucleus.txt for nuclei-related data). These names are populated by the generator.py (i.e. they are hard-coded), which is why the user should tend to avoid renaming the .txt data files.

C

trexio_t*
trexio_open(const char* file_name, const char mode,
         const back_end_t back_end, trexio_exit_code* const rc_open)
{

if (file_name == NULL || file_name[0] == '\0') {
 if (rc_open != NULL) *rc_open = TREXIO_INVALID_ARG_1;
 return NULL;
}
/* Check overflow in file_name */

/* Check that the mode is valid */
if (mode != 'r' && mode != 'w' && mode != 'u') {
 if (rc_open != NULL) *rc_open = TREXIO_INVALID_ARG_2;
 return NULL;
}

/* Check that the back end is valid in non-read mode */
if ((back_end <  0 || back_end >= TREXIO_INVALID_BACK_END) && mode != 'r') {
 if (rc_open != NULL) *rc_open = TREXIO_INVALID_ARG_3;
 return NULL;
}

/* Check that the back end is valid in read-only mode */
if ((back_end <  0 || back_end > TREXIO_INVALID_BACK_END) && mode == 'r') {
 if (rc_open != NULL) *rc_open = TREXIO_INVALID_ARG_3;
 return NULL;
}

back_end_t back_end_local = back_end;
/* Try to determine the applicable backend if the back_end argument is TREXIO_AUTO */
if (back_end == TREXIO_AUTO && mode == 'r') {
#ifdef HAVE_HDF5
 /* Check if the TREXIO file exists and if it is a directory */
 trexio_exit_code rc_text = trexio_text_inquire(file_name);
 if (rc_text == TREXIO_SUCCESS) {
   back_end_local = TREXIO_TEXT;
 } else {
 /* If not, check if it is an HDF5 file */
   trexio_exit_code rc_hdf5 = trexio_hdf5_inquire(file_name);
   if (rc_hdf5 == TREXIO_SUCCESS) {
     back_end_local = TREXIO_HDF5;
   } else {
 /* File is neither a directory nor an HDF5 file -> return an error */
     if (rc_open != NULL) *rc_open = TREXIO_FILE_ERROR;
     return NULL;
   }
 }
#else
 /* In the current implementation if HDF5 back end is not available - then there is only back end left */
 back_end_local = TREXIO_TEXT;
#endif
}

trexio_t* result = NULL;
void* result_tmp = NULL;

/* Allocate data structures */
switch (back_end_local) {

case TREXIO_TEXT:
 result_tmp = malloc(sizeof(trexio_text_t));
 break;

case TREXIO_HDF5:
#ifdef HAVE_HDF5
 result_tmp = malloc(sizeof(trexio_hdf5_t));
 break;
#else
 if (rc_open != NULL) *rc_open = TREXIO_BACK_END_MISSING;
 return NULL;
#endif

default:
 if (rc_open != NULL) *rc_open = TREXIO_FILE_ERROR;
 return NULL;

/*
case TREXIO_JSON:
 result = (trexio_t*) malloc (sizeof(trexio_json_t));
 break;
,*/
}
result = (trexio_t*) result_tmp;

assert (result != NULL);    /* TODO: Error handling */

/* Data for the parent type */

strncpy(result->file_name, file_name, TREXIO_MAX_FILENAME_LENGTH);
if (result->file_name[TREXIO_MAX_FILENAME_LENGTH-1] != '\0') {
 if (rc_open != NULL) *rc_open = TREXIO_INVALID_ARG_1;
 free(result);
 return NULL;
}

result->back_end    = back_end_local;
result->mode        = mode;
result->one_based   = false;  // Need to be flipped in Fortran interface
result->state       = 0;      // By default the file corresponds to a ground state
int irc = pthread_mutex_init ( &(result->thread_lock), NULL);
if (irc != 0) {
 if (rc_open != NULL) *rc_open = TREXIO_FAILURE;
 free(result);
 return NULL;
}
// assert (irc == 0);

trexio_exit_code rc;

/* Back end initialization */

rc = TREXIO_OPEN_ERROR;

switch (back_end_local) {

case TREXIO_TEXT:
 rc = trexio_text_init(result);
 break;

case TREXIO_HDF5:
#ifdef HAVE_HDF5
 rc = trexio_hdf5_init(result);
 break;
#else
 if (rc_open != NULL) *rc_open = TREXIO_BACK_END_MISSING;
 free(result);
 return NULL;
#endif
/*
case TREXIO_JSON:
 rc = trexio_json_init(result);
 break;
,*/
}

if (rc != TREXIO_SUCCESS) {
 if (rc_open != NULL) *rc_open = TREXIO_OPEN_ERROR;
 free(result);
 return NULL;
}


/* File locking */

rc = TREXIO_LOCK_ERROR;

switch (back_end_local) {

case TREXIO_TEXT:
 rc = trexio_text_lock(result);
 break;
/* HDF5 v.>=1.10 has file locking activated by default */
case TREXIO_HDF5:
#ifdef HAVE_HDF5
 rc = TREXIO_SUCCESS;
 break;
#else
 if (rc_open != NULL) *rc_open = TREXIO_BACK_END_MISSING;
 free(result);
 return NULL;
#endif
/*
case TREXIO_JSON:
 rc = trexio_json_lock(result);
 break;
,*/
}

if (rc != TREXIO_SUCCESS) {
 if (rc_open != NULL) *rc_open = TREXIO_LOCK_ERROR;
 free(result);
 return NULL;
}

/* Write metadata (i.e. package version) upon creation */
rc = trexio_has_metadata_package_version(result);
if (rc == TREXIO_FAILURE) {
 if (rc_open != NULL) *rc_open = TREXIO_OPEN_ERROR;
 free(result);
 return NULL;
}

if (rc == TREXIO_HAS_NOT) {
/* Write TREXIO_PACKAGE_VERSION upon creation of the file */
 switch (back_end_local) {

 case TREXIO_TEXT:
   rc = trexio_text_write_metadata_package_version(result, TREXIO_PACKAGE_VERSION);
   break;

 case TREXIO_HDF5:
#ifdef HAVE_HDF5
   rc = trexio_hdf5_write_metadata_package_version(result, TREXIO_PACKAGE_VERSION);
   break;
#else
   if (rc_open != NULL) *rc_open = TREXIO_BACK_END_MISSING;
   free(result);
   return NULL;
#endif

 }

 if (rc != TREXIO_SUCCESS) {
   if (rc_open != NULL) *rc_open = rc;
   free(result);
   return NULL;
 }

 result->version_major = TREXIO_VERSION_MAJOR;
 result->version_minor = TREXIO_VERSION_MINOR;
 result->version_patch = TREXIO_VERSION_PATCH;
 strncpy(result->version, TREXIO_PACKAGE_VERSION, 16);

} else {
/* Otherwise read the metadata_package_version to get the TREXIO version upon creation of the file */
 char version_origin[16];

 switch (back_end_local) {

 case TREXIO_TEXT:
   rc = trexio_text_read_metadata_package_version(result, version_origin, 16);
   break;

 case TREXIO_HDF5:
#ifdef HAVE_HDF5
   rc = trexio_hdf5_read_metadata_package_version(result, version_origin, 16);
   break;
#else
   if (rc_open != NULL) *rc_open = TREXIO_BACK_END_MISSING;
   free(result);
   return NULL;
#endif

 }

 if (rc != TREXIO_SUCCESS) {
   if (rc_open != NULL) *rc_open = rc;
   free(result);
   return NULL;
 }

 int16_t version_major, version_minor, version_patch;
 int rc_scan = sscanf(version_origin,
                      "%3" SCNd16 ".%5" SCNd16 ".%5" SCNd16,
                      &version_major, &version_minor, &version_patch);
 if (rc_scan != 3) {
   if (rc_open != NULL) *rc_open = TREXIO_VERSION_PARSING_ISSUE;
   free(result);
   return NULL;
 }

 result->version_major = version_major;
 result->version_minor = version_minor;
 result->version_patch = version_patch;
 strncpy(result->version, version_origin, 16);

}

if (result->version[15] != '\0' || result->version_major == 0) {
 if (rc_open != NULL) *rc_open = TREXIO_FAILURE;
 free(result);
 return NULL;
}

/* Mark the file as unsafe upon opening in UNSAFE 'u' mode */
if (mode == 'u') {

 rc = trexio_has_metadata_unsafe(result);
 if (rc == TREXIO_FAILURE) {
   if (rc_open != NULL) *rc_open = TREXIO_OPEN_ERROR;
   free(result);
   return NULL;
 }

 if (rc == TREXIO_HAS_NOT) {
   int64_t unsafe_val = 1;
   switch (back_end) {

   case TREXIO_TEXT:
     rc = trexio_text_write_metadata_unsafe(result, unsafe_val);
     break;

   case TREXIO_HDF5:
#ifdef HAVE_HDF5
     rc = trexio_hdf5_write_metadata_unsafe(result, unsafe_val);
     break;
#else
     if (rc_open != NULL) *rc_open = TREXIO_BACK_END_MISSING;
     free(result);
     return NULL;
#endif
   }
 }

 if (rc != TREXIO_SUCCESS) {
   if (rc_open != NULL) *rc_open = rc;
   free(result);
   return NULL;
 }

}


/* Exit upon success */
if (rc_open != NULL) *rc_open = TREXIO_SUCCESS;

return result;
}

Fortran

interface
integer(trexio_t) function trexio_open_c (filename, mode, back_end, rc_open) bind(C, name="trexio_open")
  use, intrinsic :: iso_c_binding
  import
  character(kind=c_char), dimension(*)          :: filename
  character(kind=c_char), intent(in), value     :: mode
  integer(trexio_back_end_t), intent(in), value :: back_end
  integer(trexio_exit_code), intent(out)        :: rc_open
end function trexio_open_c
end interface

Python

def _open(file_name: str, mode: str, back_end: int):
 """Create TREXIO file or open existing one.

 Parameters:

 file_name: str
     Name of the TREXIO file

 mode: str
     One of the currently supported ~open~ modes (e.g. 'w', 'r')

 back_end: int
     One of the currently supported TREXIO back ends (e.g. TREXIO_HDF5, TREXIO_TEXT)

 Return:
     SWIG object of type trexio_s.

 Examples:
 >>> from trexio import open as tr_open
 >>> trex_file = tr_open("example.h5", "w", TREXIO_HDF5)
 """

 # The new trexio_open function is capable of returning error code which SWIG can append to the output trexio_s file struct
 # However, if trexio_s* == NULL, then SWIG returns only an error code rc_open instead of a list [trexio_s, rc_open]
 # Thus, the following try/except sequence is needed
 try:
     return_obj = pytr.trexio_open(file_name, mode, back_end)
     assert return_obj is not None
     if isinstance(return_obj, int):
         raise Error(return_obj)
     else:
         rc_open = return_obj[1]
         # this is a sanity check in case the code evolves and SWIG issue is patched
         if rc_open == TREXIO_SUCCESS:
             trexio_file = return_obj[0]
             assert trexio_file is not None
 except AssertionError:
     raise Exception(f"Could not open TREXIO file {file_name} using trexio_open function. The return value is None (NULL pointer).")

 return trexio_file

Zero-based versus one-based arrays of indices

Because arrays are zero-based in Fortran, we need to set a flag to know if we need to shift by 1 arrays of indices.

trexio_exit_code trexio_set_one_based(trexio_t* file)
{
if (file == NULL) return TREXIO_FILE_ERROR;

file->one_based = true;

return TREXIO_SUCCESS;
}
interface
integer(trexio_exit_code) function trexio_set_one_based(trex_file) bind(C)
  use, intrinsic :: iso_c_binding
  import
  integer(trexio_t), intent(in), value :: trex_file
end function trexio_set_one_based
end interface

File closing

trexio_close closes an existing trexio_t file.

input parameters: file TREXIO file handle.

output: trexio_exit_code exit code.

C

trexio_exit_code
trexio_close (trexio_t* file)
{

if (file == NULL) return TREXIO_FILE_ERROR;

trexio_exit_code rc = TREXIO_FAILURE;

assert(file->back_end < TREXIO_INVALID_BACK_END);

/* Things to be done before closing the file in the back-end */
rc = trexio_pre_close(file);
if (rc != TREXIO_SUCCESS) return rc;

/* Terminate the back end */
switch (file->back_end) {

case TREXIO_TEXT:
rc = trexio_text_deinit(file);
break;

case TREXIO_HDF5:
#ifdef HAVE_HDF5
rc = trexio_hdf5_deinit(file);
break;
#else
FREE(file);
return TREXIO_BACK_END_MISSING;
#endif
/*
case TREXIO_JSON:
rc = trexio_json_deinit(file);
break;
,*/
}

if (rc != TREXIO_SUCCESS) {
FREE(file);
return rc;
}

/* File unlocking */

rc = TREXIO_UNLOCK_ERROR;

switch (file->back_end) {

case TREXIO_TEXT:
rc = trexio_text_unlock(file);
break;

case TREXIO_HDF5:
#ifdef HAVE_HDF5
rc = TREXIO_SUCCESS;
break;
#else
FREE(file);
return TREXIO_BACK_END_MISSING;
#endif
/*
case TREXIO_JSON:
rc = trexio_json_unlock(file);
break;
,*/
}

/* Terminate front end */

int irc = pthread_mutex_destroy( &(file->thread_lock) );

free(file);

if (irc != 0) return TREXIO_ERRNO;
if (rc != TREXIO_SUCCESS) return rc;

return TREXIO_SUCCESS;
}

Fortran

interface
integer(trexio_exit_code) function trexio_close (trex_file) bind(C)
 use, intrinsic :: iso_c_binding
 import
 integer(trexio_t), intent(in), value :: trex_file
end function trexio_close
end interface

Python

def _close(trexio_file):
 """Close TREXIO file.

 Parameter is a ~trexio_file~ object that has been created by a call to ~open~ function.
 """

 rc = pytr.trexio_close(trexio_file)
 if rc != TREXIO_SUCCESS:
     raise Error(rc)

File flushing

trexio_flush flushes all buffers into the trexio_t file.

input parameters: file TREXIO file handle.

output: trexio_exit_code exit code.

C

trexio_exit_code
trexio_flush (trexio_t* file)
{

if (file == NULL) return TREXIO_FILE_ERROR;

trexio_exit_code rc = TREXIO_FAILURE;

assert(file->back_end < TREXIO_INVALID_BACK_END);

/* Terminate the back end */
switch (file->back_end) {

case TREXIO_TEXT:
rc = trexio_text_flush(file);
break;

case TREXIO_HDF5:
#ifdef HAVE_HDF5
rc = trexio_hdf5_flush(file);
break;
#else
return TREXIO_BACK_END_MISSING;
#endif
/*
case TREXIO_JSON:
rc = trexio_json_flush(file);
break;
,*/
}

return rc;
}

Fortran

interface
integer(trexio_exit_code) function trexio_flush (trex_file) bind(C)
 use, intrinsic :: iso_c_binding
 import
 integer(trexio_t), intent(in), value :: trex_file
end function trexio_flush
end interface

Python

def flush(trexio_file):
 """Flush buffers into the TREXIO file.

 Parameter is a ~trexio_file~ object that has been created by a call to ~open~ function.
 """

 rc = pytr.trexio_flush(trexio_file)
 if rc != TREXIO_SUCCESS:
     raise Error(rc)

File existence

trexio_inquire check whether TREXIO file exists.

input parameters: file_name - string containing file name

output: trexio_exit_code exit code.

It returns:

  • TREXIO_SUCCESS if the file_name exists and if it correspond to a valid TREXIO file (i.e. a directory OR an HDF5 file)
  • TREXIO_FILE_ERROR if file_name exists but it is not a TREXIO file
  • TREXIO_FAILURE otherwise

Also, there is an implicit way to have the aforementioned functionality. Attempt to open a non-existing file with trexio_open function will result in a TREXIO_OPEN_ERROR exit code (see the modified value that has been passed as a 4-th argument to trexio_open).

You can see examples of both functionalities in test_f.f90 (search for calls with _void suffix).

C

trexio_exit_code
trexio_inquire (const char* file_name)
{

if (file_name == NULL || file_name[0] == '\0') return TREXIO_INVALID_ARG_1;

/* First check if the TREXIO file exists and if it is a directory */
trexio_exit_code rc_text = trexio_text_inquire(file_name);

#ifdef HAVE_HDF5
/* FILE_ERROR here means that the file exists but it is not a directory -> check with HDF5 */
if (rc_text == TREXIO_FILE_ERROR) {
trexio_exit_code rc_hdf5 = trexio_hdf5_inquire(file_name);
return rc_hdf5;
/* If rc_text is TREXIO_SUCCESS -> file is a TREXIO directory; TREXIO_FAILURE -> file/directory does not exist */
} else {
return rc_text;
}
#else
return rc_text;
#endif
}

Fortran

The function below is a C binding. The front end Fortran function for trexio_inquire can be found in the Fortran helper/wrapper functions.

interface
integer(trexio_exit_code) function trexio_inquire_c (filename) bind(C, name="trexio_inquire")
  use, intrinsic :: iso_c_binding
  import
  character(kind=c_char), dimension(*)       :: filename
end function trexio_inquire_c
end interface

Python

def _inquire(file_name: str) -> bool:
 """Check whether ~file_name~ TREXIO file exists.
 """
 rc = pytr.trexio_inquire(trexio_file_name)
 if rc == TREXIO_SUCCESS:
     return True
 elif rc == TREXIO_FAILURE:
     return False
 else:
     raise Error(rc)

File copy

trexio_cp copies a TREXIO file using cp.

Input parameters:

  1. source_file_name - string containing the name of the source file
  2. destination_file_name - string containing the name of the new file

Output:

  • trexio_exit_code

C

trexio_exit_code
trexio_cp(const char* source, const char* dest)
{

if (source == NULL || source[0] == '\0') {
 return TREXIO_INVALID_ARG_1;
}

if (dest == NULL || dest[0] == '\0') {
 return TREXIO_INVALID_ARG_2;
}


back_end_t back_end_local = TREXIO_AUTO;
/* Try to determine the applicable backend if the back_end argument is TREXIO_AUTO */
/* Check if the TREXIO file exists and if it is a directory */
trexio_exit_code rc_text = trexio_text_inquire(source);
if (rc_text == TREXIO_SUCCESS) {
 back_end_local = TREXIO_TEXT;
}
#ifdef HAVE_HDF5
else {
 /* If not, check if it is an HDF5 file */
 trexio_exit_code rc_hdf5 = trexio_hdf5_inquire(source);
 if (rc_hdf5 == TREXIO_SUCCESS) {
   back_end_local = TREXIO_HDF5;
 } else {
   /* File is neither a directory nor an HDF5 file -> return an error */
   return TREXIO_FILE_ERROR;
 }
}
#else
else {
 return TREXIO_FILE_ERROR;
}
#endif
assert (back_end_local != TREXIO_AUTO);

if (trexio_inquire(dest) == TREXIO_SUCCESS) {
 /* Destination file already exists */
 return TREXIO_FILE_ERROR;
}

/* Call cp */

#ifndef CP_COMMAND
#define CP_COMMAND "cp", "-r"
#endif


pid_t pid = fork();
if (pid == 0) {
 execlp("cp", CP_COMMAND, source, dest, (char *)0);
} else if (pid < 0) {
 return TREXIO_FILE_ERROR;
} else {
 int wstatus;
 pid_t ws = waitpid( pid, &wstatus, 0);
 if (ws != pid || !WIFEXITED(wstatus) )
   return TREXIO_FILE_ERROR;
}

return TREXIO_SUCCESS;
}

Fortran

interface
integer(trexio_exit_code) function trexio_cp_c (source, destination) bind(C, name="trexio_cp")
  use, intrinsic :: iso_c_binding
  import
  character(kind=c_char), dimension(*)          :: source, destination
end function trexio_cp_c
end interface

Python

def _cp(source: str, destination: str):
 """Copies a TREXIO file

 Parameters:

 source: str
     Name of the source file

 destination: str
     Name of the destination file

 Examples:
 >>> from trexio import cp as tr_cp
 >>> tr_cp("example.h5", "new.h5")
 """

 from shutil import copytree, copyfile

 f = File(filename=source, mode='r', back_end=TREXIO_AUTO)
 if f.back_end == TREXIO_TEXT:
   copytree(source, destination)
 elif f.back_end == TREXIO_HDF5:
   copyfile(source, destination)

File state

Note: the use of the functions below is discouraged as of version 2.3.0. Please use trexio_write_state_id and trexio_read_state_id to get the state corresponding to a given trexio_t file. There can be only one state_id per file, namely data for excited state wave functions have to go in a file different from the ground state one (see the state group description for more info about how you can link the ground state TREXIO file with its excited states).

trexio_set_state set an existing trexio_t file handle to a given state and write it as state_id attribute. trexio_get_state returns current state of the trexio_t file handle.

input parameters: file TREXIO file handle. state int32_t ID of a state (0 for ground state).

output: trexio_exit_code exit code.

C

trexio_exit_code
trexio_set_state (trexio_t* file, const int32_t num)
{
if (file == NULL) return TREXIO_INVALID_ARG_1;

/* Write state_id in the file (as of v.2.3.0) */
trexio_exit_code rc = trexio_has_state_id(file);
if (rc == TREXIO_HAS_NOT || file->mode == 'u') {
trexio_exit_code rc_w = trexio_write_state_id(file, num);
if (rc_w != TREXIO_SUCCESS) return rc_w;
}

file->state = num;

return TREXIO_SUCCESS;
}

trexio_exit_code
trexio_get_state (trexio_t* file, int32_t* const num)
{
if (file == NULL) return TREXIO_INVALID_ARG_1;
if (num  == NULL) return TREXIO_INVALID_ARG_2;

/* Read state_id from the file (as of v.2.3.0) */
int32_t state_id = 0;
trexio_exit_code rc = trexio_has_state_id(file);
if (rc == TREXIO_SUCCESS) {
trexio_exit_code rc_r = trexio_read_state_id(file, &state_id);
if (rc_r != TREXIO_SUCCESS) return rc_r;
}

/* If the state is not in a file then state_id=0, i.e. ground state */
*num = state_id;

return TREXIO_SUCCESS;
}

Fortran

interface
integer(trexio_exit_code) function trexio_set_state (trex_file, state) bind(C)
 use, intrinsic :: iso_c_binding
 import
 integer(trexio_t), intent(in), value :: trex_file
 integer(c_int32_t), intent(in), value :: state
end function trexio_set_state
end interface

interface
integer(trexio_exit_code) function trexio_get_state (trex_file, state) bind(C)
 use, intrinsic :: iso_c_binding
 import
 integer(trexio_t), intent(in), value :: trex_file
 integer(c_int32_t), intent(out)       :: state
end function trexio_get_state
end interface

Python

See TREXIO File Python class.

Tasks to be done before closing

trexio_exit_code
trexio_pre_close (trexio_t* file)
{

if (file == NULL) return TREXIO_FILE_ERROR;

trexio_exit_code rc;

/* Check consistency between number of determinants and coefficients stored in the file */

if (file->version_major >= 2 && file->version_minor >= 2) {

bool has_det   = (trexio_has_determinant_list(file) == TREXIO_SUCCESS);
bool has_coeff = (trexio_has_determinant_coefficient(file) == TREXIO_SUCCESS);
int64_t ndet, ncoeff;

if (has_det && has_coeff) {
  rc = trexio_read_determinant_num_64(file, &ndet);
  if (rc != TREXIO_SUCCESS) return rc;

  rc = trexio_read_determinant_coefficient_size(file, &ncoeff);
  if (rc != TREXIO_SUCCESS) return rc;

  /* Maybe be even more direct and use assert here so that the user's code crushes in case of inconsistency */
  if (ndet != ncoeff) return TREXIO_INVALID_DETERMINANT_NUM;
}

}
/* Up-spin and down-spin electrons */

int32_t nup, ndn, nelec;
bool has_up   = (trexio_has_electron_up_num(file) == TREXIO_SUCCESS);
bool has_dn   = (trexio_has_electron_dn_num(file) == TREXIO_SUCCESS);
bool has_updn = (trexio_has_electron_num(file) == TREXIO_SUCCESS);

if (file->mode != 'r') {
if (has_updn && has_up && has_dn) {
  rc = trexio_read_electron_up_num(file, &nup);
  if (rc != TREXIO_SUCCESS) return rc;

  rc = trexio_read_electron_dn_num(file, &ndn);
  if (rc != TREXIO_SUCCESS) return rc;

  rc = trexio_read_electron_num(file, &nelec);
  if (rc != TREXIO_SUCCESS) return rc;

  if (nelec != nup + ndn) {
    if (file->mode == 'u') {
      nelec = nup + ndn;
      rc = trexio_write_electron_num(file, nelec);
      if (rc != TREXIO_SUCCESS) return rc;
    } else {
      return TREXIO_INVALID_ELECTRON_NUM;
    }
  }
} else if (has_up && has_dn) {
  rc = trexio_read_electron_up_num(file, &nup);
  if (rc != TREXIO_SUCCESS) return rc;

  rc = trexio_read_electron_dn_num(file, &ndn);
  if (rc != TREXIO_SUCCESS) return rc;

  nelec = nup + ndn;
  rc = trexio_write_electron_num(file, nelec);
  if (rc != TREXIO_SUCCESS) return rc;
} else if (has_up) {
  rc = trexio_read_electron_up_num(file, &nup);
  if (rc != TREXIO_SUCCESS) return rc;

  ndn = 0;
  rc = trexio_write_electron_dn_num(file, ndn);
  if (rc != TREXIO_SUCCESS) return rc;

  nelec = nup;
  rc = trexio_write_electron_num(file, nelec);
  if (rc != TREXIO_SUCCESS) return rc;
} else if (has_dn) {
  rc = trexio_read_electron_dn_num(file, &ndn);
  if (rc != TREXIO_SUCCESS) return rc;

  nup = 0;
  rc = trexio_write_electron_up_num(file, nup);
  if (rc != TREXIO_SUCCESS) return rc;

  nelec = ndn;
  rc = trexio_write_electron_num(file, nelec);
  if (rc != TREXIO_SUCCESS) return rc;
}
}

return TREXIO_SUCCESS;
}

Templates for front end

Description

Consider the following block of trex.json:

{
 "nucleus": {
     "num"                : [ "int"  , [                     ] ]
   , "charge"             : [ "float", [ "nucleus.num"       ] ]
   , "coord"              : [ "float", [ "nucleus.num", "3"  ] ]
   , "label"              : [ "str" ,  [ "nucleus.num"       ] ]
   , "point_group"        : [ "str"  , [                     ] ]
 }
}

TREXIO is generated automatically by the generator.py Python script based on the tree-like configuration provided in the trex.json file. Because of that, generalized templates can be implemented and re-used. This approach minimizes the number of bugs as compared with manual copy-paste-modify scheme.

All templates presented below use the $var$ notation to indicate the variable, which will be replaced by the generator.py. Sometimes the upper case is used, i.e. $VAR$ (for example, in #define statements). More detailed description of each variable can be found below:

Template variable Description Example
$group$ Name of the group nucleus
$group_num$ Name of the numerical attribute (scalar) nucleus_num
$group_str$ Name of the string attribute (scalar) nucleus_point_group
$group_dset$ Name of the dataset (vector/matrix/tensor) nucleus_coord
$group_dset_rank$ Rank of the dataset 2
$group_dset_dim$ Selected dimension of the dataset nucleus_num
$group_dset_dim_list$ All dimensions of the dataset {nucleus_num, 3}
$group_dset_dtype$ Basic type of the dataset (int/float/char) float
$group_dset_h5_dtype$ Type of the dataset in HDF5 double
$group_dset_format_scanf$ Input type of the dataset in TEXT [fscanf] %lf
$group_dset_format_printf$ Output type of the dataset in TEXT [fprintf] %24.16e
$group_dset_dtype_default$ Default datatype of the dataset [C] double/int32_t
$group_dset_dtype_single$ Single precision datatype of the dataset [C] float/int32_t
$group_dset_dtype_double$ Double precision datatype of the dataset [C] double/int64_t
$group_dset_f_dtype_default$ Default datatype of the dataset [Fortran] real(c_double)/integer(c_int32_t)
$group_dset_f_dtype_single$ Single precision datatype of the dataset [Fortran] real(c_float)/integer(c_int32_t)
$group_dset_f_dtype_double$ Double precision datatype of the dataset [Fortran] real(c_double)/integer(c_int64_t)
$group_dset_f_dims$ Dimensions in Fortran (:,:)
$group_dset_py_dtype$ Standard datatype of the dataset [Python] float/int
$default_prec$ Default precision for read/write without suffix [C] 64/32
$is_index$ Expands to true if dataset has a type index [C] true/false

Some of the aforementioned template variables with group_dset prefix are duplicated with group_num prefix, e.g. you might find $group_num_dtype_double$ in the templates corresponding to numerical attributes. The expanding values are the same as for group_dset and thus are not listed in the table above.

Note: parent group name is always added to the child objects upon construction of TREXIO (e.g. num of nucleus group becomes nucleus_num and should be accessed accordingly within TREXIO).

TREXIO generator parses the trex.json file. TREXIO operates with names of variables based on the 1-st (parent group) and 2-nd (child object) levels of trex.json . The parsed data is divided in 2 parts:

  1. Single attributes. These can be numerical values or strings.
  2. Datasets. These can be vectors, matrices or tensors. The types are indicated in trex.json. Currently supported data types: int, float and strings.

For each of the aforementioned objects, TREXIO provides has, read and write functionality. TREXIO supports I/O with single or double precision for integer and floating point numbers.

Note: single integer attributes that contain num in their name (e.g. nucleus_num) are considered dimensioning variables and cannot be negative or 0. An attempt to write negative or 0 value will result in TREXIO_INVALID_ARG_2 exit code.

Templates for front end has_group functions

Introduction

This section concerns API calls related to TREXIO groups

Function name Description
trexio_has_$group$ Check if a group exists in a file

C templates for front end

The C templates that correspond to each of the abovementioned functions can be found below. First parameter is the TREXIO file handle.

Function declarations
Source code
trexio_exit_code
trexio_has_$group$ (trexio_t* const file)
{

if (file == NULL) return TREXIO_INVALID_ARG_1;

assert(file->back_end < TREXIO_INVALID_BACK_END);

switch (file->back_end) {

case TREXIO_TEXT:
return trexio_text_has_$group$(file);

case TREXIO_HDF5:
#ifdef HAVE_HDF5
return trexio_hdf5_has_$group$(file);
#else
return TREXIO_BACK_END_MISSING;
#endif
/*
case TREXIO_JSON:
return trexio_json_has_$group$(file);
break;
,*/
}

return TREXIO_FAILURE;
}

Fortran templates for front end

The Fortran templates that provide an access to the C API calls from Fortran. These templates are based on the use of iso_c_binding. Pointers have to be passed by value.

interface
integer(trexio_exit_code) function trexio_has_$group$ (trex_file) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
end function trexio_has_$group$
end interface

Python templates for front end

def has_$group$(trexio_file) -> bool:
"""Check that $group$ group exists in the TREXIO file.

Parameter is a ~TREXIO File~ object that has been created by a call to ~open~ function.

Returns:
     True if the variable exists, False otherwise

Raises:
   - trexio.Error if TREXIO return code ~rc~ is TREXIO_FAILURE and prints the error message using string_of_error.
   - Exception from some other error (e.g. RuntimeError).
"""

rc = pytr.trexio_has_$group$(trexio_file.pytrexio_s)
if rc == TREXIO_FAILURE:
   raise Error(rc)

return rc == TREXIO_SUCCESS

Templates for front end has/read/write a single numerical attribute

Introduction

This section concerns API calls related to numerical attributes, namely single value of int/float types.

Function name Description Precision
trexio_has_$group_num$ Check if an attribute exists in a file
trexio_read_$group_num$ Read a attribute Single
trexio_write_$group_num$ Write a attribute Single
trexio_read_$group_num$_32 Read a attribute Single
trexio_write_$group_num$_32 Write a attribute Single
trexio_read_$group_num$_64 Read a attribute Double
trexio_write_$group_num$_64 Write a attribute Double

C templates for front end

The C templates that correspond to each of the abovementioned functions can be found below. First parameter is the TREXIO file handle. Second parameter is the variable to be written/read to/from the TREXIO file (except for trexio_has_ functions). Suffixes _32 and _64 correspond to API calls dealing with single and real(c_double), respectively. The basic (non-suffixed) API call on dimensioning variables deals with single precision (see Table above).

Function declarations
Source code for double precision functions
trexio_exit_code
trexio_read_$group_num$_64 (trexio_t* const file, $group_num_dtype_double$* const num)
{
if (file == NULL) return TREXIO_INVALID_ARG_1;
if (num  == NULL) return TREXIO_INVALID_ARG_2;
if (trexio_has_$group_num$(file) != TREXIO_SUCCESS) return TREXIO_ATTR_MISSING;

switch (file->back_end) {

case TREXIO_TEXT:
return trexio_text_read_$group_num$(file, num);

case TREXIO_HDF5:
#ifdef HAVE_HDF5
return trexio_hdf5_read_$group_num$(file, num);
#else
return TREXIO_BACK_END_MISSING;
#endif
/*
case TREXIO_JSON:
return trexio_json_read_$group_num$(file, num);
,*/
}

return TREXIO_FAILURE;
}
trexio_exit_code
trexio_write_$group_num$_64 (trexio_t* const file, const $group_num_dtype_double$ num)
{
if (file == NULL) return TREXIO_INVALID_ARG_1;
//if (num <= 0L) return TREXIO_INVALID_NUM; /* this line is uncommented by the generator for dimensioning variables; do NOT remove! */
if (trexio_has_$group_num$(file) == TREXIO_SUCCESS && file->mode != 'u') return TREXIO_ATTR_ALREADY_EXISTS;

switch (file->back_end) {

case TREXIO_TEXT:
return trexio_text_write_$group_num$(file, num);

case TREXIO_HDF5:
#ifdef HAVE_HDF5
return trexio_hdf5_write_$group_num$(file, num);
#else
return TREXIO_BACK_END_MISSING;
#endif
/*
case TREXIO_JSON:
return trexio_json_write_$group_num$(file, num);
,*/
}

return TREXIO_FAILURE;
}
Source code for single precision functions
trexio_exit_code
trexio_read_$group_num$_32 (trexio_t* const file, $group_num_dtype_single$* const num)
{
if (file == NULL) return TREXIO_INVALID_ARG_1;
if (num  == NULL) return TREXIO_INVALID_ARG_2;
if (trexio_has_$group_num$(file) != TREXIO_SUCCESS) return TREXIO_ATTR_MISSING;

$group_num_dtype_double$ num_64 = 0;
trexio_exit_code rc = TREXIO_GROUP_READ_ERROR;

switch (file->back_end) {

case TREXIO_TEXT:
rc = trexio_text_read_$group_num$(file, &num_64);
break;

case TREXIO_HDF5:
#ifdef HAVE_HDF5
rc = trexio_hdf5_read_$group_num$(file, &num_64);
break;
#else
rc = TREXIO_BACK_END_MISSING;
break ;
#endif
/*
case TREXIO_JSON:
rc =trexio_json_read_$group_num$(file, &num_64);
break;
,*/
}

if (rc != TREXIO_SUCCESS) return rc;

,*num = ($group_num_dtype_single$) num_64;
return TREXIO_SUCCESS;
}
trexio_exit_code
trexio_write_$group_num$_32 (trexio_t* const file, const $group_num_dtype_single$ num)
{

if (file == NULL) return TREXIO_INVALID_ARG_1;
//if (num <= 0) return TREXIO_INVALID_NUM; /* this line is uncommented by the generator for dimensioning variables; do NOT remove! */
if (trexio_has_$group_num$(file) == TREXIO_SUCCESS && file->mode != 'u') return TREXIO_ATTR_ALREADY_EXISTS;

switch (file->back_end) {

case TREXIO_TEXT:
return trexio_text_write_$group_num$(file, ($group_num_dtype_double$) num);

case TREXIO_HDF5:
#ifdef HAVE_HDF5
return trexio_hdf5_write_$group_num$(file, ($group_num_dtype_double$) num);
#else
return TREXIO_BACK_END_MISSING;
#endif
/*
case TREXIO_JSON:
return trexio_json_write_$group_num$(file, ($group_num_dtype_double$) num);
break;
,*/
}

return TREXIO_FAILURE;
}
Source code for default functions
trexio_exit_code
trexio_read_$group_num$ (trexio_t* const file, $group_num_dtype_default$* const num)
{
return trexio_read_$group_num$_$default_prec$(file, num);
}
trexio_exit_code
trexio_write_$group_num$ (trexio_t* const file, const $group_num_dtype_default$ num)
{
return trexio_write_$group_num$_$default_prec$(file, num);
}
trexio_exit_code
trexio_has_$group_num$ (trexio_t* const file)
{

if (file == NULL) return TREXIO_INVALID_ARG_1;

assert(file->back_end < TREXIO_INVALID_BACK_END);

switch (file->back_end) {

case TREXIO_TEXT:
return trexio_text_has_$group_num$(file);

case TREXIO_HDF5:
#ifdef HAVE_HDF5
return trexio_hdf5_has_$group_num$(file);
#else
return TREXIO_BACK_END_MISSING;
#endif
/*
case TREXIO_JSON:
return trexio_json_has_$group_num$(file);
break;
,*/
}

return TREXIO_FAILURE;
}

Fortran templates for front end

The Fortran templates that provide an access to the C API calls from Fortran. These templates are based on the use of iso_c_binding. Pointers have to be passed by value.

interface
integer(trexio_exit_code) function trexio_write_$group_num$_64 (trex_file, num) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
$group_num_f_dtype_double$, intent(in), value :: num
end function trexio_write_$group_num$_64
end interface
interface
integer(trexio_exit_code) function trexio_read_$group_num$_64 (trex_file, num) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
$group_num_f_dtype_double$, intent(out) :: num
end function trexio_read_$group_num$_64
end interface
interface
integer(trexio_exit_code) function trexio_write_$group_num$_32 (trex_file, num) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
$group_num_f_dtype_single$, intent(in), value :: num
end function trexio_write_$group_num$_32
end interface
interface
integer(trexio_exit_code) function trexio_read_$group_num$_32 (trex_file, num) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
$group_num_f_dtype_single$, intent(out) :: num
end function trexio_read_$group_num$_32
end interface
interface
integer(trexio_exit_code) function trexio_write_$group_num$ (trex_file, num) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
$group_num_f_dtype_default$, intent(in), value :: num
end function trexio_write_$group_num$
end interface
interface
integer(trexio_exit_code) function trexio_read_$group_num$ (trex_file, num) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
$group_num_f_dtype_default$, intent(out) :: num
end function trexio_read_$group_num$
end interface
interface
integer(trexio_exit_code) function trexio_has_$group_num$ (trex_file) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
end function trexio_has_$group_num$
end interface

Python templates for front end

def write_$group_num$(trexio_file, num_w: $group_num_py_dtype$) -> None:
"""Write the $group_num$ variable in the TREXIO file.

Parameters:

trexio_file:
   TREXIO File object.

num_w: int
   Value of the $group_num$ variable to be written.

Raises:
   - Exception from AssertionError if TREXIO return code ~rc~ is different from TREXIO_SUCCESS and prints the error message using trexio_string_of_error.
   - Exception from some other error (e.g. RuntimeError).
"""

rc = pytr.trexio_write_$group_num$(trexio_file.pytrexio_s, num_w)
if rc != TREXIO_SUCCESS:
   raise Error(rc)
def read_$group_num$(trexio_file) -> $group_num_py_dtype$:
"""Read the $group_num$ variable from the TREXIO file.

Parameter is a ~TREXIO File~ object that has been created by a call to ~open~ function.

Returns:
   ~num_r~: int
   Integer value of $group_num$ variable read from ~trexio_file~.

Raises:
   - Exception from AssertionError if TREXIO return code ~rc~ is different from TREXIO_SUCCESS and prints the error message using trexio_string_of_error.
   - Exception from some other error (e.g. RuntimeError).
"""

rc, num_r = pytr.trexio_read_$group_num$(trexio_file.pytrexio_s)
if rc != TREXIO_SUCCESS:
   raise Error(rc)

return num_r
def has_$group_num$(trexio_file) -> bool:
"""Check that $group_num$ variable exists in the TREXIO file.

Parameter is a ~TREXIO File~ object that has been created by a call to ~open~ function.

Returns:
     True if the variable exists, False otherwise

Raises:
   - trexio.Error if TREXIO return code ~rc~ is TREXIO_FAILURE and prints the error message using string_of_error.
   - Exception from some other error (e.g. RuntimeError).
"""

rc = pytr.trexio_has_$group_num$(trexio_file.pytrexio_s)
if rc == TREXIO_FAILURE:
   raise Error(rc)

return rc == TREXIO_SUCCESS

Templates for front end has/read/write a dataset of numerical data

Introduction

This section concerns API calls related to datasets.

Function name Description Precision
trexio_has_$group_dset$ Check if a dataset exists in a file
trexio_read_$group_dset$ Read a dataset in default precision Double/Single for float/int
trexio_write_$group_dset$ Write a dataset in default precision Double/Single for float/int
trexio_read_safe_$group_dset$ Read a bounded dataset Double
trexio_write_safe_$group_dset$ Write a bounded dataset Double
trexio_read_$group_dset$_32 Read a dataset in single precision Single
trexio_write_$group_dset$_32 Write a dataset in single precision Single
trexio_read_$group_dset$_64 Read a dataset in double precision Double
trexio_write_$group_dset$_64 Write a dataset in double precision Double

C templates for front end

The C templates that correspond to each of the abovementioned functions can be found below. First parameter is the TREXIO file handle. Second parameter is the variable to be written/read to/from the TREXIO file (except for trexio_has_ functions). Suffixes _32 and _64 correspond to API calls dealing with single and double precision, respectively. The basic (non-suffixed) API call on datasets deals with real(c_double) (see Table above).

Function declarations
Source code for double precision functions
trexio_exit_code
trexio_read_$group_dset$_64 (trexio_t* const file, $group_dset_dtype_double$* const $group_dset$)
{

if (file  == NULL) return TREXIO_INVALID_ARG_1;
if ($group_dset$ == NULL) return TREXIO_INVALID_ARG_2;
if (trexio_has_$group_dset$(file) != TREXIO_SUCCESS) return TREXIO_DSET_MISSING;

trexio_exit_code rc;
int64_t $group_dset_dim$ = 0;

/* Error handling for this call is added by the generator */
rc = trexio_read_$group_dset_dim$_64(file, &($group_dset_dim$));

if ($group_dset_dim$ == 0L) return TREXIO_INVALID_NUM;

uint32_t rank = $group_dset_rank$;
uint64_t dims[$group_dset_rank$] = {$group_dset_dim_list$};

assert(file->back_end < TREXIO_INVALID_BACK_END);

rc = TREXIO_FAILURE;
switch (file->back_end) {

case TREXIO_TEXT:
rc = trexio_text_read_$group_dset$(file, $group_dset$, rank, dims);
break;

case TREXIO_HDF5:
#ifdef HAVE_HDF5
rc = trexio_hdf5_read_$group_dset$(file, $group_dset$, rank, dims);
break;
#else
rc = TREXIO_BACK_END_MISSING;
break;
#endif
/*
case TREXIO_JSON:
rc = trexio_json_read_$group_dset$(file, $group_dset$, rank, dims);
break;
,*/
}
if (rc != TREXIO_SUCCESS) return rc;

/* Handle index type */
if ($is_index$) {
uint64_t dim_size = 1;
for (uint32_t i=0; i<rank; ++i){
   dim_size *= dims[i];
}
for (uint64_t i=0; i<dim_size; ++i){
  $group_dset$[i] += ($group_dset_dtype_double$) 1;
}
}

return TREXIO_SUCCESS;
}
trexio_exit_code
trexio_write_$group_dset$_64 (trexio_t* const file, const $group_dset_dtype_double$* $group_dset$)
{

if (file  == NULL) return TREXIO_INVALID_ARG_1;
assert(file->back_end < TREXIO_INVALID_BACK_END);

if ($group_dset$ == NULL) return TREXIO_INVALID_ARG_2;
if (trexio_has_$group_dset$(file) == TREXIO_SUCCESS && file->mode != 'u') return TREXIO_DSET_ALREADY_EXISTS;


trexio_exit_code rc;
int64_t $group_dset_dim$ = 0;

/* Error handling for this call is added by the generator */
rc = trexio_read_$group_dset_dim$_64(file, &($group_dset_dim$));

if ($group_dset_dim$ == 0L) return TREXIO_INVALID_NUM;

uint32_t rank = $group_dset_rank$;
uint64_t dims[$group_dset_rank$] = {$group_dset_dim_list$};

if ($is_index$) {
/* Handle index type : is_index = $is_index$ */
uint64_t dim_size = 1;
for (uint32_t i=0; i<rank; ++i){
 dim_size *= dims[i];
}

$group_dset_dtype_double$* $group_dset$_p =
  CALLOC(dim_size, $group_dset_dtype_double$);

if ($group_dset$_p == NULL) return TREXIO_ALLOCATION_FAILED;

for (uint64_t i=0; i<dim_size; ++i){
  $group_dset$_p[i] = $group_dset$[i] - ($group_dset_dtype_double$) 1;
}

rc = TREXIO_FAILURE;
switch (file->back_end) {

case TREXIO_TEXT:
 rc = trexio_text_write_$group_dset$(file,
                                     (const $group_dset_dtype_double$*) $group_dset$_p,
                                     rank, dims);
 break;

case TREXIO_HDF5:
#ifdef HAVE_HDF5
 rc = trexio_hdf5_write_$group_dset$(file,
                                     (const $group_dset_dtype_double$*) $group_dset$_p,
                                     rank, dims);
 break;
#else
 rc = TREXIO_BACK_END_MISSING;
 break;
#endif
 /*
   case TREXIO_JSON:
   rc = trexio_json_write_$group_dset$(file, $group_dset$_p, rank, dims);
   break;
 ,*/
 FREE($group_dset$_p);

}

} else {

rc = TREXIO_FAILURE;
switch (file->back_end) {

case TREXIO_TEXT:
 rc = trexio_text_write_$group_dset$(file, $group_dset$, rank, dims);
 break;

case TREXIO_HDF5:
#ifdef HAVE_HDF5
 rc = trexio_hdf5_write_$group_dset$(file, $group_dset$, rank, dims);
 break;
#else
 rc = TREXIO_BACK_END_MISSING;
 break;
#endif
 /*
   case TREXIO_JSON:
   rc = trexio_json_write_$group_dset$(file, $group_dset$, rank, dims);
   break;
 ,*/
}
}

return rc;
}
Source code for single precision functions
trexio_exit_code
trexio_read_$group_dset$_32 (trexio_t* const file, $group_dset_dtype_single$* const $group_dset$)
{

if (file  == NULL) return TREXIO_INVALID_ARG_1;
if ($group_dset$ == NULL) return TREXIO_INVALID_ARG_2;
if (trexio_has_$group_dset$(file) != TREXIO_SUCCESS) return TREXIO_DSET_MISSING;

trexio_exit_code rc;
int64_t $group_dset_dim$ = 0;

/* Error handling for this call is added by the generator */
rc = trexio_read_$group_dset_dim$_64(file, &($group_dset_dim$));

if ($group_dset_dim$ == 0L) return TREXIO_INVALID_NUM;

uint32_t rank = $group_dset_rank$;
uint64_t dims[$group_dset_rank$] = {$group_dset_dim_list$};

uint64_t dim_size = 1;
for (uint32_t i=0; i<rank; ++i){
dim_size *= dims[i];
}

$group_dset_dtype_double$* $group_dset$_64 = CALLOC(dim_size, $group_dset_dtype_double$);
if ($group_dset$_64 == NULL) return TREXIO_ALLOCATION_FAILED;

assert(file->back_end < TREXIO_INVALID_BACK_END);

rc = TREXIO_FAILURE;

switch (file->back_end) {

case TREXIO_TEXT:
rc = trexio_text_read_$group_dset$(file, $group_dset$_64, rank, dims);
break;

case TREXIO_HDF5:
#ifdef HAVE_HDF5
rc = trexio_hdf5_read_$group_dset$(file, $group_dset$_64, rank, dims);
break;
#else
rc = TREXIO_BACK_END_MISSING;
break;
#endif
/*
case TREXIO_JSON:
rc = trexio_json_read_$group_dset$(file, $group_dset$_64, rank, dims);
break;
,*/
}

if (rc != TREXIO_SUCCESS){
FREE($group_dset$_64);
return rc;
}

if ($is_index$) {
for (uint64_t i=0; i<dim_size; ++i){
 $group_dset$[i] = ($group_dset_dtype_single$) $group_dset$_64[i] + 1;
}
} else {
for (uint64_t i=0; i<dim_size; ++i){
 $group_dset$[i] = ($group_dset_dtype_single$) $group_dset$_64[i];
}
}

FREE($group_dset$_64);
return TREXIO_SUCCESS;
}
trexio_exit_code
trexio_write_$group_dset$_32 (trexio_t* const file, const $group_dset_dtype_single$* $group_dset$)
{

if (file  == NULL) return TREXIO_INVALID_ARG_1;
if ($group_dset$ == NULL) return TREXIO_INVALID_ARG_2;
if (trexio_has_$group_dset$(file) == TREXIO_SUCCESS && file->mode != 'u') return TREXIO_DSET_ALREADY_EXISTS;

trexio_exit_code rc;
int64_t $group_dset_dim$ = 0;

/* Error handling for this call is added by the generator */
rc = trexio_read_$group_dset_dim$_64(file, &($group_dset_dim$));

if ($group_dset_dim$ == 0L) return TREXIO_INVALID_NUM;

uint32_t rank = $group_dset_rank$;
uint64_t dims[$group_dset_rank$] = {$group_dset_dim_list$};

uint64_t dim_size = 1;
for (uint32_t i=0; i<rank; ++i){
dim_size *= dims[i];
}

$group_dset_dtype_double$* $group_dset$_64 = CALLOC(dim_size, $group_dset_dtype_double$);
if ($group_dset$_64 == NULL) return TREXIO_ALLOCATION_FAILED;

/* A type conversion from single precision to double required since back end only accepts 64-bit data */
if ($is_index$) {
for (uint64_t i=0; i<dim_size; ++i){
 $group_dset$_64[i] = ($group_dset_dtype_double$) $group_dset$[i] - ($group_dset_dtype_double$) 1;
}
} else {
for (uint64_t i=0; i<dim_size; ++i){
 $group_dset$_64[i] = ($group_dset_dtype_double$) $group_dset$[i];
}
}

assert(file->back_end < TREXIO_INVALID_BACK_END);

rc = TREXIO_FAILURE;
switch (file->back_end) {

case TREXIO_TEXT:
rc = trexio_text_write_$group_dset$(file, $group_dset$_64, rank, dims);
break;

case TREXIO_HDF5:
#ifdef HAVE_HDF5
rc = trexio_hdf5_write_$group_dset$(file, $group_dset$_64, rank, dims);
break;
#else
rc = TREXIO_BACK_END_MISSING;
break;
#endif
/*
case TREXIO_JSON:
rc = trexio_json_write_$group_dset$(file, $group_dset$_64, rank, dims);
break;
,*/
}

FREE($group_dset$_64);

if (rc != TREXIO_SUCCESS) return rc;

return TREXIO_SUCCESS;
}
Source code for memory-safe functions

#+NAME:dimCheck

trexio_exit_code rc;
int64_t $group_dset_dim$ = 0;

/* Error handling for this call is added by the generator */
rc = trexio_read_$group_dset_dim$_64(file, &($group_dset_dim$));

if ($group_dset_dim$ == 0L) return TREXIO_INVALID_NUM;

uint32_t rank = $group_dset_rank$;
uint64_t dims[$group_dset_rank$] = {$group_dset_dim_list$};

/* The block below is specific to safe API as it checks the boundaries */
uint64_t dim_size = 1;
for (uint32_t i=0; i<rank; ++i){
    dim_size *= dims[i];
}
trexio_exit_code
trexio_read_safe_$group_dset$_32 (trexio_t* const file, $group_dset_dtype_single$* const dset_out, const int64_t dim_out)
{

if (file  == NULL) return TREXIO_INVALID_ARG_1;
if (dset_out == NULL) return TREXIO_INVALID_ARG_2;
if (trexio_has_$group_dset$(file) != TREXIO_SUCCESS) return TREXIO_DSET_MISSING;

<<dimCheck>>

if (dim_out > (int64_t) dim_size) return TREXIO_UNSAFE_ARRAY_DIM;

return trexio_read_$group_dset$_32(file, dset_out);
}
trexio_exit_code
trexio_write_safe_$group_dset$_32 (trexio_t* const file, const $group_dset_dtype_single$* dset_in, const int64_t dim_in)
{

if (file  == NULL) return TREXIO_INVALID_ARG_1;
if (dset_in == NULL) return TREXIO_INVALID_ARG_2;
if (trexio_has_$group_dset$(file) == TREXIO_SUCCESS && file->mode != 'u') return TREXIO_DSET_ALREADY_EXISTS;

<<dimCheck>>

if (dim_in > (int64_t) dim_size) return TREXIO_UNSAFE_ARRAY_DIM;

return trexio_write_$group_dset$_32(file, dset_in);
}
trexio_exit_code
trexio_read_safe_$group_dset$_64 (trexio_t* const file, $group_dset_dtype_double$* const dset_out, const int64_t dim_out)
{

if (file  == NULL) return TREXIO_INVALID_ARG_1;
if (dset_out == NULL) return TREXIO_INVALID_ARG_2;
if (trexio_has_$group_dset$(file) != TREXIO_SUCCESS) return TREXIO_DSET_MISSING;

<<dimCheck>>

if (dim_out > (int64_t) dim_size) return TREXIO_UNSAFE_ARRAY_DIM;

return trexio_read_$group_dset$_64(file, dset_out);
}
trexio_exit_code
trexio_write_safe_$group_dset$_64 (trexio_t* const file, const $group_dset_dtype_double$* dset_in, const int64_t dim_in)
{

if (file  == NULL) return TREXIO_INVALID_ARG_1;
if (dset_in == NULL) return TREXIO_INVALID_ARG_2;
if (trexio_has_$group_dset$(file) == TREXIO_SUCCESS && file->mode != 'u') return TREXIO_DSET_ALREADY_EXISTS;

<<dimCheck>>

if (dim_in > (int64_t) dim_size) return TREXIO_UNSAFE_ARRAY_DIM;

return trexio_write_$group_dset$_64(file, dset_in);
}
Source code for default functions
trexio_exit_code
trexio_read_safe_$group_dset$ (trexio_t* const file, $group_dset_dtype_default$* const $group_dset$, const int64_t dim_out)
{
return trexio_read_safe_$group_dset$_$default_prec$(file, $group_dset$, dim_out);
}
trexio_exit_code
trexio_write_safe_$group_dset$ (trexio_t* const file, const $group_dset_dtype_default$* $group_dset$, const int64_t dim_in)
{
return trexio_write_safe_$group_dset$_$default_prec$(file, $group_dset$, dim_in);
}
trexio_exit_code
trexio_read_$group_dset$ (trexio_t* const file, $group_dset_dtype_default$* const $group_dset$)
{
return trexio_read_$group_dset$_$default_prec$(file, $group_dset$);
}
trexio_exit_code
trexio_write_$group_dset$ (trexio_t* const file, const $group_dset_dtype_default$* $group_dset$)
{
return trexio_write_$group_dset$_$default_prec$(file, $group_dset$);
}
trexio_exit_code
trexio_has_$group_dset$ (trexio_t* const file)
{

if (file  == NULL) return TREXIO_INVALID_ARG_1;

assert(file->back_end < TREXIO_INVALID_BACK_END);

switch (file->back_end) {

case TREXIO_TEXT:
return trexio_text_has_$group_dset$(file);

case TREXIO_HDF5:
#ifdef HAVE_HDF5
return trexio_hdf5_has_$group_dset$(file);
#else
return TREXIO_BACK_END_MISSING;
#endif
/*
case TREXIO_JSON:
return trexio_json_has_$group_dset$(file);
,*/
}
return TREXIO_FAILURE;
}

Fortran templates for front end

The Fortran templates that provide an access to the C API calls from Fortran. These templates are based on the use of iso_c_binding. Pointers have to be passed by value.

interface
integer(trexio_exit_code) function trexio_write_$group_dset$_64 (trex_file, dset) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
$group_dset_f_dtype_double$, intent(in) :: dset$group_dset_f_dims$
end function trexio_write_$group_dset$_64
end interface
interface
integer(trexio_exit_code) function trexio_read_$group_dset$_64 (trex_file, dset) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
$group_dset_f_dtype_double$, intent(out) :: dset$group_dset_f_dims$
end function trexio_read_$group_dset$_64
end interface
interface
integer(trexio_exit_code) function trexio_write_$group_dset$_32 (trex_file, dset) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
$group_dset_f_dtype_single$, intent(in) :: dset$group_dset_f_dims$
end function trexio_write_$group_dset$_32
end interface
interface
integer(trexio_exit_code) function trexio_read_$group_dset$_32 (trex_file, dset) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
$group_dset_f_dtype_single$, intent(out) :: dset$group_dset_f_dims$
end function trexio_read_$group_dset$_32
end interface
interface
integer(trexio_exit_code) function trexio_write_$group_dset$ (trex_file, dset) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
$group_dset_f_dtype_default$, intent(in) :: dset$group_dset_f_dims$
end function trexio_write_$group_dset$
end interface
interface
integer(trexio_exit_code) function trexio_read_$group_dset$ (trex_file, dset) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
$group_dset_f_dtype_default$, intent(out) :: dset$group_dset_f_dims$
end function trexio_read_$group_dset$
end interface
interface
integer(trexio_exit_code) function trexio_has_$group_dset$ (trex_file) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
end function trexio_has_$group_dset$
end interface

Python templates for front end

def write_$group_dset$(trexio_file, dset_w) -> None:
"""Write the $group_dset$ array of numbers in the TREXIO file.

Parameters:

trexio_file:
   TREXIO File object.

dset_w: list OR numpy.ndarray
   Array of $group_dset$ values to be written. If array data type does not correspond to int64 or float64, the conversion is performed.

Raises:
   - trexio.Error if TREXIO return code ~rc~ is different from TREXIO_SUCCESS and prints the error message.
   - Exception from some other error (e.g. RuntimeError).
"""

doConversion = False
doFlatten = False
if not isinstance(dset_w, (list, tuple)):
  # if input array is not a list or tuple then it is probably a numpy array
  if isinstance(dset_w, np.ndarray) and (not dset_w.dtype==np.int64 or not dset_w.dtype==np.float64):
      doConversion = True

  if len(dset_w.shape) > 1:
      doFlatten = True
      if doConversion:
          dset_64 = np.$group_dset_py_dtype$64(dset_w).flatten()
      else:
          dset_flat = np.array(dset_w, dtype=np.$group_dset_py_dtype$64).flatten()
  else:
      if doConversion:
          dset_64 = np.$group_dset_py_dtype$64(dset_w)

else:
  # if input array is a multidimensional list or tuple, we have to convert it
  try:
      doFlatten = True
      ncol = len(dset_w[0])
      dset_flat = np.array(dset_w, dtype=np.$group_dset_py_dtype$64).flatten()
  except TypeError:
      doFlatten = False
      pass


if doConversion:
   rc = pytr.trexio_write_safe_$group_dset$_64(trexio_file.pytrexio_s, dset_64)
elif doFlatten:
   rc = pytr.trexio_write_safe_$group_dset$_64(trexio_file.pytrexio_s, dset_flat)
else:
   rc = pytr.trexio_write_safe_$group_dset$_64(trexio_file.pytrexio_s, dset_w)

if rc != TREXIO_SUCCESS:
   raise Error(rc)
def read_$group_dset$(trexio_file, dim = None, doReshape = None, dtype = None):
"""Read the $group_dset$ array of numbers from the TREXIO file.

Parameters:

trexio_file:
   TREXIO File object.

dim (Optional): int
   Size of the block to be read from the file (i.e. how many items of $group_dset$ will be returned)
   If None, the function will read all necessary array dimensions from the file.

dtype (Optional): type
   NumPy data type of the output (e.g. np.int32|int16 or np.float32|float16). If specified, the output array will be converted from the default double precision.

doReshape (Optional): bool
   Flag to determine whether the output NumPy array has be reshaped or not. Be default, reshaping is performed
   based on the dimensions from the ~trex.json~ file. Otherwise, ~shape~ array (list or tuple) is used if provided by the user.

Returns:
   ~dset_64~ if dtype is None or ~dset_converted~ otherwise: numpy.ndarray
   1D NumPy array with ~dim~ elements corresponding to $group_dset$ values read from the TREXIO file.

Raises:
   - trexio.Error if TREXIO return code ~rc~ is different from TREXIO_SUCCESS and prints the error message.
   - Exception from some other error (e.g. RuntimeError).
"""

if doReshape is None:
   doReshape = True

# if dim is not specified, read dimensions from the TREXIO file
dims_list = None
if doReshape:
   $group_dset_dim$ = read_$group_dset_dim$(trexio_file)

   dims_list = [$group_dset_dim_list$]
   dim_real  = 1
   for i in range($group_dset_rank$):
       dim_real *= dims_list[i]

if dim:
   if dim_real != dim:
       raise Error(TREXIO_UNSAFE_ARRAY_DIM)
else:
   dim = dim_real

shape = tuple(dims_list)
if shape is None and doReshape:
   raise ValueError("Reshaping failure: shape is None.")


rc, dset_64 = pytr.trexio_read_safe_$group_dset$_64(trexio_file.pytrexio_s, dim)
if rc != TREXIO_SUCCESS:
   raise Error(rc)


isConverted = False
dset_converted = None
if dtype is not None:
   try:
       assert isinstance(dtype, type)
   except AssertionError:
       raise TypeError("dtype argument has to be an instance of the type class (e.g. np.float32).")

   if not dtype==np.int64 or not dtype==np.float64:
       dset_converted = np.array(dset_64, dtype=dtype)
       isConverted = True

# additional assert can be added here to check that read_safe functions returns numpy array of proper dimension

if doReshape:
   # in-place reshaping did not work so I have to make a copy
   if isConverted:
       dset_reshaped = np.reshape(dset_converted, shape, order='C')
   else:
       dset_reshaped = np.reshape(dset_64, shape, order='C')

if isConverted:
   return dset_converted
elif doReshape:
   return dset_reshaped
else:
   return dset_64
def has_$group_dset$(trexio_file) -> bool:
"""Check that $group_dset$ variable exists in the TREXIO file.

Parameter is a ~TREXIO File~ object that has been created by a call to ~open~ function.

Returns:
     True if the variable exists, False otherwise

Raises:
   - trexio.Error if TREXIO return code ~rc~ is TREXIO_FAILURE and prints the error message using string_of_error.
   - Exception from some other error (e.g. RuntimeError).
"""

rc = pytr.trexio_has_$group_dset$(trexio_file.pytrexio_s)
if rc == TREXIO_FAILURE:
   raise Error(rc)

return rc == TREXIO_SUCCESS

Templates for front end has/read/write a dataset of sparse data

Introduction

Sparse data structures are used typically for large tensors such as two-electron integrals. For example, in the trex.json file sparse arrays appear as for the eri :

"ao_2e_int"  : {
  "eri"      : [ "float sparse", [ "ao.num", "ao.num", "ao.num", "ao.num" ] ]
}

The electron repulsion integral (eri) $\langle ij | kl \rangle$ is represented as a quartet of integers $(i,j,k,l)$ and a floating point value.

To store $N$ integrals in the file, we store

  • An array of quartets of integers
  • An array of values (floats)

Both arrays have the same size, $N$, the number of non-zero integrals. Knowing the maximum dimensions allows to check that the integers are in a valid range, and also lets the library choose the smallest integer representation to compress the storage.

Fortran uses 1-based array indexing, while C uses 0-based indexing. Internally, we use a 0-based representation but the Fortran binding does the appropriate conversion when reading or writing.

As the number of integrals to store can be prohibitively large, we provide the possibility to read/write the integrals in chunks. So the functions take two extra parameters:

  • offset_file : how many integrals in the file should be skipped when reading/writing. An offset of zero implies to read the first integral.
  • buffer_size : the number of integrals to read/write. If EOF is encountered upon reading, the buffer_size is overwritten with the number of integrals that have been read before EOF and the trexio_read_ function return TREXIO_END exit code instead of TREXIO_SUCCESS.

The storage of int indices is internally compressed based on the maximum possible value of an index, which is derived from the corresponding dimension of the sparse array (e.g. ao_num is the upper bound of indices in the aforementioned ao_2e_int_eri dataset). The upper bounds for different int types (e.g. uint16_t) can be found in the in the stdint.h C library. Currently implemented list of compressions based on the upper bound of indices can be found below:

Max value of indices Internal representation (in the TREXIO file)
UINT8_MAX (e.g. $< 255$) 8-bit unsigned int
UINT16_MAX (e.g. $< 65535$) 16-bit unsigned int
Otherwise (e.g. $\ge 65535$) 32-bit signed int

This section concerns API calls related to sparse data structures.

Function name Description Precision
trexio_has_$group_dset$ Check if a sparse dset is present in a file
trexio_read_$group_dset$ Read indices and values of a sparse dset Single/Double for indices/values
trexio_read_$group_dset$_size Read the number of sparse data elements stored in the file Double for size
trexio_write_$group_dset$ Write indices and values of a sparse dset Single/Double for indices/values
trexio_read_safe_$group_dset$ Safe (bounded) read of indices and values (for Python API) Single/Double for indices/values
trexio_write_safe_$group_dset$ Safe (bounded) write of indices and values (for Python API) Single/Double for indices/values

C templates for front end

Function declarations
Source code for default functions
trexio_exit_code trexio_read_safe_$group_dset$(trexio_t* const file,
                                             const int64_t offset_file,
                                             int64_t* const buffer_size_read,
                                             int32_t* const index_sparse_read,
                                             const int64_t size_index_read,
                                             double* const value_sparse_read,
                                             const int64_t size_value_read
                                             )
{
return trexio_read_$group_dset$(file, offset_file, buffer_size_read, index_sparse_read, value_sparse_read);
}

trexio_exit_code
trexio_read_$group_dset$(trexio_t* const file,
                       const int64_t offset_file,
                       int64_t* const buffer_size,
                       int32_t* const index_sparse,
                       double* const value_sparse
                       )
{
if (file  == NULL) return TREXIO_INVALID_ARG_1;
if (offset_file < 0L) return TREXIO_INVALID_ARG_2;
if (*buffer_size <= 0L) return TREXIO_INVALID_ARG_3;
if (index_sparse == NULL) return TREXIO_INVALID_ARG_4;
if (value_sparse == NULL) return TREXIO_INVALID_ARG_5;
if (trexio_has_$group_dset$(file) != TREXIO_SUCCESS) return TREXIO_DSET_MISSING;

const uint32_t rank = $group_dset_rank$;  // To be set by generator : number of indices

int64_t size_max;         // Max number of integrals (already in the file)
trexio_exit_code rc;

/* Read the max number of integrals stored in the file */
rc = trexio_read_$group_dset$_size(file, &size_max);
if (rc != TREXIO_SUCCESS) return rc;

/* To be set by generator : number of unique dimensions
   (e.g. 1 for ERI in AO basis because only ao_num is present in the list of dimensions) */
const uint32_t unique_rank = $group_dset_unique_rank$;
int64_t unique_dims[$group_dset_unique_rank$];

// Below part is populated by the generator when unique_rank > 1
rc = trexio_read_$group_dset_unique_dim$_64(file, &unique_dims[$dim_id$]); if (rc != TREXIO_SUCCESS) return rc;

/* Find the maximal value along all dimensions to define the compression technique in the back end */
int64_t max_dim = unique_dims[0];
if (unique_rank != 1) {
  for (uint32_t i = 1; i < unique_rank; i++) {
    if (unique_dims[i] > max_dim) max_dim = unique_dims[i];
  }
}

// introduce a new variable which will be modified with the number of integrals being read if EOF is encountered
int64_t eof_read_size = 0L;

switch (file->back_end) {

case TREXIO_TEXT:
  rc = trexio_text_read_$group_dset$(file, offset_file, *buffer_size, max_dim, &eof_read_size, index_sparse, value_sparse);
  break;

case TREXIO_HDF5:
#ifdef HAVE_HDF5
  rc = trexio_hdf5_read_$group_dset$(file, offset_file, *buffer_size, max_dim, &eof_read_size, index_sparse, value_sparse);
  break;
#else
  rc = TREXIO_BACK_END_MISSING;
  break;
#endif
/*
case TREXIO_JSON:
  return trexio_json_read_$group_dset$(...);
  break;
,*/
default:
  rc = TREXIO_FAILURE;  /* Impossible case */
  break;
}

if (rc != TREXIO_SUCCESS && rc != TREXIO_END) return rc;

if (rc == TREXIO_END) *buffer_size = eof_read_size;

// shift indices to be one-based if Fortran API is used
if (file->one_based) {
  // if EOF is reached - shift only indices that have been read, not an entire buffer
  uint64_t index_size = rank*(*buffer_size) ;
  for (uint64_t i=0; i<index_size; ++i){
    index_sparse[i] += 1;
  }
}

return rc;
}
trexio_exit_code
trexio_read_$group_dset$_size(trexio_t* const file, int64_t* const size_max)
{
if (file  == NULL) return TREXIO_INVALID_ARG_1;
if (size_max == NULL) return TREXIO_INVALID_ARG_2;
if (trexio_has_$group_dset$(file) != TREXIO_SUCCESS) return TREXIO_DSET_MISSING;

switch (file->back_end) {

case TREXIO_TEXT:
  return trexio_text_read_$group_dset$_size(file, size_max);
  break;

case TREXIO_HDF5:
#ifdef HAVE_HDF5
  return trexio_hdf5_read_$group_dset$_size(file, size_max);
  break;
#else
  return TREXIO_BACK_END_MISSING;
#endif
/*
case TREXIO_JSON:
  return trexio_json_read_$group_dset$_size(...);
  break;
,*/
default:
  return TREXIO_FAILURE;  /* Impossible case */
}
}
trexio_exit_code trexio_write_safe_$group_dset$(trexio_t* const file,
                                              const int64_t offset_file,
                                              const int64_t buffer_size,
                                              const int32_t* index_sparse_write,
                                              const int64_t size_index_write,
                                              const double* value_sparse_write,
                                              const int64_t size_value_write
                                              )
{
return trexio_write_$group_dset$(file, offset_file, buffer_size, index_sparse_write, value_sparse_write);
}

trexio_exit_code
trexio_write_$group_dset$(trexio_t* const file,
                        const int64_t offset_file,
                        const int64_t buffer_size,
                        const int32_t* index_sparse,
                        const double* value_sparse
                        )
{
if (file  == NULL) return TREXIO_INVALID_ARG_1;
if (offset_file < 0L) return TREXIO_INVALID_ARG_2;
if (buffer_size <= 0L) return TREXIO_INVALID_ARG_3;
if (index_sparse == NULL) return TREXIO_INVALID_ARG_4;
if (value_sparse == NULL) return TREXIO_INVALID_ARG_5;

/* To be set by generator : number of indices */
const uint32_t rank = $group_dset_rank$;

int64_t size_max = 0L; // Max number of integrals (already in the file)
trexio_exit_code rc;

/* Read the max number of integrals stored in the file */
rc = trexio_read_$group_dset$_size(file, &size_max);
if (rc != TREXIO_SUCCESS && rc != TREXIO_DSET_MISSING) return rc;
if (rc == TREXIO_DSET_MISSING) size_max = 0L;

/* To be set by generator : number of unique dimensions
   (e.g. 1 for ERI in AO basis because only ao_num is present in the list of dimensions) */
const uint32_t unique_rank = $group_dset_unique_rank$;
int64_t unique_dims[$group_dset_unique_rank$];

// Below part is populated by the generator when unique_rank > 1
rc = trexio_read_$group_dset_unique_dim$_64(file, &unique_dims[$dim_id$]); if (rc != TREXIO_SUCCESS) return rc;

/* Find the maximal value along all dimensions to define the compression technique in the back end */
int64_t max_dim = unique_dims[0];
if (unique_rank != 1) {
  for (uint32_t i = 1; i < unique_rank; i++) {
    if (unique_dims[i] > max_dim) max_dim = unique_dims[i];
  }
}

// shift indices to be zero-based if Fortran API is used
if (file->one_based) {

  uint64_t index_size = rank * buffer_size;

  int32_t* index_sparse_p = CALLOC(index_size, int32_t);
  if (index_sparse_p == NULL) return TREXIO_ALLOCATION_FAILED;

  for (uint64_t i=0; i<index_size; ++i){
    index_sparse_p[i] = index_sparse[i] - 1;
  }

  switch (file->back_end) {

  case TREXIO_TEXT:
    rc = trexio_text_write_$group_dset$(file, offset_file, buffer_size, max_dim,
                                        size_max, index_sparse_p, value_sparse);
    break;

  case TREXIO_HDF5:
#ifdef HAVE_HDF5
    rc = trexio_hdf5_write_$group_dset$(file, offset_file, buffer_size, max_dim,
                                        index_sparse_p, value_sparse);
    break;
#else
    rc = TREXIO_BACK_END_MISSING;
    break;
#endif
    /*
      case TREXIO_JSON:
      rc = trexio_json_write_$group_dset$(...);
      break;
    ,*/
  default:
    rc = TREXIO_FAILURE;  /* Impossible case */
    break;
  }

  FREE(index_sparse_p);

}
else {

  switch (file->back_end) {

  case TREXIO_TEXT:
    rc = trexio_text_write_$group_dset$(file, offset_file, buffer_size, max_dim,
                                        size_max, index_sparse, value_sparse);
    break;

  case TREXIO_HDF5:
#ifdef HAVE_HDF5
    rc = trexio_hdf5_write_$group_dset$(file, offset_file, buffer_size, max_dim,
                                        index_sparse, value_sparse);
    break;
#else
    rc = TREXIO_BACK_END_MISSING;
    break;
#endif
    /*
      case TREXIO_JSON:
      rc = trexio_json_write_$group_dset$(...);
      break;
    ,*/
  default:
    rc = TREXIO_FAILURE;  /* Impossible case */
    break;
  }

}

return rc;
}
trexio_exit_code
trexio_has_$group_dset$ (trexio_t* const file)
{

if (file  == NULL) return TREXIO_INVALID_ARG_1;

assert(file->back_end < TREXIO_INVALID_BACK_END);

switch (file->back_end) {

case TREXIO_TEXT:
  return trexio_text_has_$group_dset$(file);

case TREXIO_HDF5:
#ifdef HAVE_HDF5
  return trexio_hdf5_has_$group_dset$(file);
#else
  return TREXIO_BACK_END_MISSING;
#endif
/*
case TREXIO_JSON:
  return trexio_json_has_$group_dset$(file);
  break;
,*/
}

return TREXIO_FAILURE;
}

Fortran templates for front end

The Fortran templates that provide an access to the C API calls from Fortran. These templates are based on the use of iso_c_binding. Pointers have to be passed by value.

interface
integer(trexio_exit_code) function trexio_write_$group_dset$ (trex_file, &
                                          offset_file, buffer_size, &
                                          index_sparse, value_sparse) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
integer(c_int64_t), intent(in), value :: offset_file
integer(c_int64_t), intent(in), value :: buffer_size
integer(c_int32_t), intent(in)        :: index_sparse(*)
real(c_double), intent(in)            :: value_sparse(*)
end function trexio_write_$group_dset$
end interface

interface
integer(trexio_exit_code) function trexio_write_safe_$group_dset$ (trex_file, &
                                               offset_file, buffer_size, &
                                               index_sparse, index_size, &
                                               value_sparse, value_size) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
integer(c_int64_t), intent(in), value :: offset_file
integer(c_int64_t), intent(in), value :: buffer_size
integer(c_int32_t), intent(in)        :: index_sparse(*)
integer(c_int64_t), intent(in), value :: index_size
real(c_double), intent(in)            :: value_sparse(*)
integer(c_int64_t), intent(in), value :: value_size
end function trexio_write_safe_$group_dset$
end interface
interface
integer(trexio_exit_code) function trexio_read_$group_dset$ (trex_file, &
                                         offset_file, buffer_size, &
                                         index_sparse, value_sparse) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
integer(c_int64_t), intent(in), value :: offset_file
integer(c_int64_t), intent(inout)     :: buffer_size
integer(c_int32_t), intent(out)       :: index_sparse(*)
real(c_double), intent(out)           :: value_sparse(*)
end function trexio_read_$group_dset$
end interface

interface
integer(trexio_exit_code) function trexio_read_safe_$group_dset$ (trex_file, &
                                              offset_file, buffer_size, &
                                              index_sparse, index_size, &
                                              value_sparse, value_size) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
integer(c_int64_t), intent(in), value :: offset_file
integer(c_int64_t), intent(inout)     :: buffer_size
integer(c_int32_t), intent(out)       :: index_sparse(*)
integer(c_int64_t), intent(in), value :: index_size
real(c_double), intent(out)           :: value_sparse(*)
integer(c_int64_t), intent(in), value :: value_size
end function trexio_read_safe_$group_dset$
end interface
interface
integer(trexio_exit_code) function trexio_read_$group_dset$_size (trex_file, &
                                              size_max) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
integer(c_int64_t), intent(out) :: size_max
end function trexio_read_$group_dset$_size
end interface
interface
integer(trexio_exit_code) function trexio_has_$group_dset$ (trex_file) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
end function trexio_has_$group_dset$
end interface

Python templates for front end

def write_$group_dset$(trexio_file: File, offset_file: int, buffer_size: int, indices: list, values: list) -> None:
"""Write the $group_dset$ indices and values in the TREXIO file.

Parameters:

trexio_file:
   TREXIO File object.

offset_file: int
   The number of integrals to be skipped in the file when writing.

buffer_size: int
   The number of integrals to write in the file from the provided sparse arrays.

indices: list OR numpy.ndarray
   Array of $group_dset$ indices to be written. If array data type does not correspond to int32, the conversion is performed.

values: list OR numpy.ndarray
   Array of $group_dset$ values to be written. If array data type does not correspond to float64, the conversion is performed.

Raises:
   - trexio.Error if TREXIO return code ~rc~ is different from TREXIO_SUCCESS and prints the error message.
   - Exception from some other error (e.g. RuntimeError).
"""

if not isinstance(offset_file, int):
   raise TypeError("offset_file argument has to be an integer.")
if not isinstance(buffer_size, int):
   raise TypeError("buffer_size argument has to be an integer.")
if not isinstance(indices, (list, tuple, np.ndarray)):
   raise TypeError("indices argument has to be an array (list, tuple or NumPy ndarray).")
if not isinstance(values, (list, tuple, np.ndarray)):
   raise TypeError("values argument has to be an array (list, tuple or NumPy ndarray).")

convertIndices = False
convertValues = False
flattenIndices = False
if isinstance(indices, np.ndarray):
  # convert to int32 if input indices are in a different precision
  if not indices.dtype==np.int32:
      convertIndices = True

  if len(indices.shape) > 1:
      flattenIndices = True
      if convertIndices:
          indices_32 = np.int32(indices).flatten()
      else:
          indices_32 = np.array(indices, dtype=np.int32).flatten()
  else:
      if convertIndices:
          indices_32 = np.int32(indices)
else:
  # if input array is a multidimensional list or tuple, we have to convert it
  try:
      doFlatten = True
      # if list of indices is flat - the attempt to compute len(indices[0]) will raise a TypeError
      ncol = len(indices[0])
      indices_32 = np.array(indices, dtype=np.int32).flatten()
  except TypeError:
      doFlatten = False
      pass

if isinstance(values, np.ndarray):
  # convert to float64 if input values are in a different precision
  if not values.dtype==np.float64:
      convertValues = True
  if convertValues:
      values_64 = np.float64(values)

if (convertIndices or flattenIndices) and convertValues:
   rc = pytr.trexio_write_safe_$group_dset$(trexio_file.pytrexio_s, offset_file, buffer_size, indices_32, values_64)
elif (convertIndices or flattenIndices) and not convertValues:
   rc = pytr.trexio_write_safe_$group_dset$(trexio_file.pytrexio_s, offset_file, buffer_size, indices_32, values)
elif not (convertIndices or flattenIndices) and convertValues:
   rc = pytr.trexio_write_safe_$group_dset$(trexio_file.pytrexio_s, offset_file, buffer_size, indices, values_64)
else:
   rc = pytr.trexio_write_safe_$group_dset$(trexio_file.pytrexio_s, offset_file, buffer_size, indices, values)

if rc != TREXIO_SUCCESS:
   raise Error(rc)
def read_$group_dset$(trexio_file: File, offset_file: int, buffer_size: int) -> tuple:
"""Read the $group_dset$ indices and values from the TREXIO file.

Parameters:

trexio_file:
   TREXIO File object.

offset_file: int
   The number of integrals to be skipped in the file when reading.

buffer_size: int
   The number of integrals to read from the file.

Returns:
   (indices, values, n_int_read, eof_flag) tuple where
     - indices and values are NumPy arrays [numpy.ndarray] with the default int32 and float64 precision, respectively;
     - n_int_read [int] is the number of integrals read from the trexio_file
       (either strictly equal to buffer_size or less than buffer_size if EOF has been reached);
     - eof_flag [bool] is True when EOF has been reached (i.e. when call to low-level pytrexio API returns TREXIO_END)
                          False otherwise.

Raises:
   - Exception from AssertionError if TREXIO return code ~rc~ is different from TREXIO_SUCCESS
       and prints the error message using trexio_string_of_error.
   - Exception from some other error (e.g. RuntimeError).
"""

if not isinstance(offset_file, int):
   raise TypeError("offset_file argument has to be an integer.")
if not isinstance(buffer_size, int):
   raise TypeError("buffer_size argument has to be an integer.")


# read the number of integrals already in the file
integral_num = read_$group_dset$_size(trexio_file)

# additional modification needed to avoid allocating more memory than needed if EOF will be reached during read
overflow = offset_file + buffer_size - integral_num
eof_flag = False
if overflow > 0:
   verified_size = buffer_size - overflow
   eof_flag = True
else:
   verified_size = buffer_size

# main call to the low-level (SWIG-wrapped) trexio_read function, which also requires the sizes of the output to be provided
# as the last 2 arguments (for numpy arrays of indices and values, respectively)
# read_buf_size contains the number of elements being read from the file, useful when EOF has been reached
rc, n_int_read, indices, values = pytr.trexio_read_safe_$group_dset$(trexio_file.pytrexio_s,
                                                                    offset_file,
                                                                    verified_size,
                                                                    verified_size * $group_dset_rank$,
                                                                    verified_size)
if rc != TREXIO_SUCCESS:
   raise Error(rc)
if n_int_read == 0:
   raise ValueError("No integrals have been read from the file.")
if indices is None or values is None:
   raise ValueError("Returned NULL array from the low-level pytrexio API.")

# conversion to custom types can be performed on the user side, here we only reshape the returned flat array of indices according to group_dset_rank
shape = tuple([verified_size, $group_dset_rank$])
indices_reshaped = np.reshape(indices, shape, order='C')

return (indices_reshaped, values, n_int_read, eof_flag)


def read_$group_dset$_size(trexio_file) -> int:
"""Read the number of $group_dset$ integrals stored in the TREXIO file.

Parameter is a ~TREXIO File~ object that has been created by a call to ~open~ function.

Returns:
   ~num_integral~: int
   Integer value of corresponding to the size of the $group_dset$ sparse array from ~trexio_file~.

Raises:
   - Exception from AssertionError if TREXIO return code ~rc~ is different from TREXIO_SUCCESS and prints the error message using trexio_string_of_error.
   - Exception from some other error (e.g. RuntimeError).
"""

rc, num_integral = pytr.trexio_read_$group_dset$_size(trexio_file.pytrexio_s)
if rc != TREXIO_SUCCESS:
   raise Error(rc)

return num_integral
def has_$group_dset$(trexio_file) -> bool:
"""Check that $group_dset$ variable exists in the TREXIO file.

Parameter is a ~TREXIO File~ object that has been created by a call to ~open~ function.

Returns:
     True if the variable exists, False otherwise

Raises:
   - trexio.Error if TREXIO return code ~rc~ is TREXIO_FAILURE and prints the error message using string_of_error.
   - Exception from some other error (e.g. RuntimeError).
"""

rc = pytr.trexio_has_$group_dset$(trexio_file.pytrexio_s)
if rc == TREXIO_FAILURE:
   raise Error(rc)

return rc == TREXIO_SUCCESS

Templates for front end has/read/write a dataset of strings

Introduction

This section concerns API calls related to datasets of strings.

Function name Description
trexio_has_$group_dset$ Check if a dataset exists in a file
trexio_read_$group_dset$ Read a dataset
trexio_write_$group_dset$ Write a dataset

C templates for front end

First parameter is the TREXIO file handle. Second parameter is the variable to be written/read to/from the TREXIO file (except for trexio_has_ functions).

Function declarations
Source code for default functions
trexio_exit_code
trexio_read_$group_dset$_low (trexio_t* const file, char* dset_out, const int32_t max_str_len)
{

if (file  == NULL) return TREXIO_INVALID_ARG_1;
if (dset_out == NULL) return TREXIO_INVALID_ARG_2;
if (max_str_len <= 0) return TREXIO_INVALID_ARG_3;
if (trexio_has_$group_dset$(file) != TREXIO_SUCCESS) return TREXIO_DSET_MISSING;

trexio_exit_code rc;
int64_t $group_dset_dim$ = 0;

/* Error handling for this call is added by the generator */
rc = trexio_read_$group_dset_dim$_64(file, &($group_dset_dim$));

if ($group_dset_dim$ == 0L) return TREXIO_INVALID_NUM;

uint32_t rank = $group_dset_rank$;
uint64_t dims[$group_dset_rank$] = {$group_dset_dim_list$};

assert(file->back_end < TREXIO_INVALID_BACK_END);
switch (file->back_end) {

case TREXIO_TEXT:
return trexio_text_read_$group_dset$(file, dset_out, rank, dims, (uint32_t) max_str_len);

case TREXIO_HDF5:
#ifdef HAVE_HDF5
return trexio_hdf5_read_$group_dset$(file, dset_out, rank, dims, (uint32_t) max_str_len);
#else
return TREXIO_BACK_END_MISSING;
#endif
/*
case TREXIO_JSON:
rc = trexio_json_read_$group_dset$(file, dset_out, rank, dims);
,*/
}

return TREXIO_FAILURE;
}

trexio_exit_code
trexio_read_$group_dset$ (trexio_t* const file, char** dset_out, const int32_t max_str_len)
{

if (file  == NULL) return TREXIO_INVALID_ARG_1;
if (dset_out == NULL) return TREXIO_INVALID_ARG_2;
if (max_str_len <= 0) return TREXIO_INVALID_ARG_3;
if (trexio_has_$group_dset$(file) != TREXIO_SUCCESS) return TREXIO_DSET_MISSING;

assert(file->back_end < TREXIO_INVALID_BACK_END);

trexio_exit_code rc;
int64_t dset_dim = 0;

/* Error handling for this call is added by the generator */
rc = trexio_read_$group_dset_dim$_64(file, &(dset_dim));

if (dset_dim == 0L) return TREXIO_INVALID_NUM;

char* str_compiled = CALLOC(dset_dim*(max_str_len+1) + 1, char);
if (str_compiled == NULL) return TREXIO_ALLOCATION_FAILED;

rc = trexio_read_$group_dset$_low(file, str_compiled, max_str_len);
if (rc != TREXIO_SUCCESS) {
FREE(str_compiled);
return rc;
}

for (uint64_t i=0; i < (uint64_t) dset_dim; i++) {

char * pch;
pch = i == 0 ? strtok(str_compiled, TREXIO_DELIM) : strtok(NULL, TREXIO_DELIM) ;
if (pch == NULL) {
 FREE(str_compiled);
 return TREXIO_FAILURE;
}

strcpy(dset_out[i], "");
strcat(dset_out[i], pch);

}

FREE(str_compiled);
return TREXIO_SUCCESS;

}
trexio_exit_code
trexio_write_$group_dset$_low (trexio_t* const file, char* dset_in, const int32_t max_str_len)
{

if (file  == NULL) return TREXIO_INVALID_ARG_1;
if (dset_in == NULL) return TREXIO_INVALID_ARG_2;
if (max_str_len <= 0) return TREXIO_INVALID_ARG_3;
if (trexio_has_$group_dset$(file) == TREXIO_SUCCESS && file->mode != 'u') return TREXIO_DSET_ALREADY_EXISTS;

trexio_exit_code rc;
int64_t $group_dset_dim$ = 0;

/* Error handling for this call is added by the generator */
rc = trexio_read_$group_dset_dim$_64(file, &($group_dset_dim$));

if ($group_dset_dim$ == 0L) return TREXIO_INVALID_NUM;

uint32_t rank = $group_dset_rank$;
uint64_t dims[$group_dset_rank$] = {$group_dset_dim_list$};

assert(file->back_end < TREXIO_INVALID_BACK_END);

char*  tmp_str  = CALLOC(dims[0]*(max_str_len+1), char);
if (tmp_str == NULL) return TREXIO_ALLOCATION_FAILED;
char** dset_str = CALLOC(dims[0], char*);
if (dset_str == NULL) {
FREE(tmp_str);
return TREXIO_ALLOCATION_FAILED;
}

/* parse the string using strtok */
for(uint64_t i=0; i<dims[0]; i++) {

char* pch;
pch = i == 0 ? strtok(dset_in, TREXIO_DELIM) : strtok(NULL, TREXIO_DELIM) ;

if (pch == NULL) {
 FREE(dset_str[0]);
 FREE(dset_str);
 return TREXIO_FAILURE;
}

size_t pch_len = strlen(pch) + 1;

if (pch_len > (size_t) max_str_len) {
 FREE(dset_str[0]);
 FREE(dset_str);
 return TREXIO_INVALID_STR_LEN;
}

dset_str[i] = tmp_str;
strncpy(tmp_str, pch, pch_len);
tmp_str += pch_len + 1;
}

rc = TREXIO_FAILURE;
switch (file->back_end) {

case TREXIO_TEXT:
rc = trexio_text_write_$group_dset$(file, (const char**) dset_str, rank, dims);
break;

case TREXIO_HDF5:
#ifdef HAVE_HDF5
rc = trexio_hdf5_write_$group_dset$(file, (const char**) dset_str, rank, dims);
break;
#else
rc =TREXIO_BACK_END_MISSING;
break;
#endif
/*
case TREXIO_JSON:
rc = trexio_json_write_$group_dset$(file, dset, rank, dims);
break;
,*/
}

FREE(dset_str[0]);
FREE(dset_str);

return rc;

}

trexio_exit_code
trexio_write_$group_dset$ (trexio_t* const file, const char** dset_in, const int32_t max_str_len)
{

if (file  == NULL) return TREXIO_INVALID_ARG_1;
if (dset_in == NULL) return TREXIO_INVALID_ARG_2;
if (max_str_len <= 0) return TREXIO_INVALID_ARG_3;
if (trexio_has_$group_dset$(file) == TREXIO_SUCCESS && file->mode != 'u') return TREXIO_DSET_ALREADY_EXISTS;

assert(file->back_end < TREXIO_INVALID_BACK_END);

trexio_exit_code rc;
int64_t dset_dim = 0;

/* Error handling for this call is added by the generator */
rc = trexio_read_$group_dset_dim$_64(file, &(dset_dim));

if (dset_dim == 0L) return TREXIO_INVALID_NUM;

char* str_compiled = CALLOC(dset_dim*max_str_len + 1, char);
if (str_compiled == NULL) return TREXIO_ALLOCATION_FAILED;

strcpy(str_compiled, "");
for (uint64_t i=0; i < (uint64_t) dset_dim; i++) {
strcat(str_compiled, dset_in[i]);
strcat(str_compiled, TREXIO_DELIM);
}

rc = trexio_write_$group_dset$_low(file, str_compiled, max_str_len);

FREE(str_compiled);

return rc;
}
trexio_exit_code
trexio_has_$group_dset$ (trexio_t* const file)
{

if (file  == NULL) return TREXIO_INVALID_ARG_1;

assert(file->back_end < TREXIO_INVALID_BACK_END);

switch (file->back_end) {

case TREXIO_TEXT:
return trexio_text_has_$group_dset$(file);

case TREXIO_HDF5:
#ifdef HAVE_HDF5
return trexio_hdf5_has_$group_dset$(file);
#else
return TREXIO_BACK_END_MISSING;
#endif
/*
case TREXIO_JSON:
return trexio_json_has_$group_dset$(file);
break;
,*/
}

return TREXIO_FAILURE;
}

Fortran templates for front end

The Fortran templates that provide an access to the C API calls from Fortran. These templates are based on the use of iso_c_binding. Pointers have to be passed by value.

interface
integer(trexio_exit_code) function trexio_write_$group_dset$_low (trex_file, dset, max_str_len) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
character(kind=c_char), intent(in)    :: dset(*)
integer(c_int32_t), intent(in), value :: max_str_len
end function trexio_write_$group_dset$_low
end interface
interface
integer(trexio_exit_code) function trexio_read_$group_dset$_low (trex_file, dset, max_str_len) bind(C)
use, intrinsic :: iso_c_binding
import
integer(c_int64_t), intent(in), value  :: trex_file
character(kind=c_char), intent(out)    :: dset(*)
integer(c_int32_t), intent(in), value  :: max_str_len
end function trexio_read_$group_dset$_low
end interface
interface
integer(trexio_exit_code) function trexio_has_$group_dset$ (trex_file) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
end function trexio_has_$group_dset$
end interface
 integer(trexio_exit_code) function trexio_read_$group_dset$ (trex_file, dset, max_str_len)
   implicit none
   integer(trexio_t), intent(in), value :: trex_file
   integer(c_int32_t), intent(in), value :: max_str_len
   character(len=*), intent(inout) :: dset(*)

   character, allocatable :: str_compiled(:)
   integer(c_int64_t) :: $group_dset_dim$
   integer(trexio_exit_code) :: rc

   rc = trexio_read_$group_dset_dim$_64(trex_file, $group_dset_dim$)
   if (rc /= TREXIO_SUCCESS) trexio_read_$group_dset$ = rc

   allocate(str_compiled($group_dset_dim$*(max_str_len+1)+1))

   rc = trexio_read_$group_dset$_low(trex_file, str_compiled, max_str_len)
   if (rc /= TREXIO_SUCCESS) then
     deallocate(str_compiled)
     trexio_read_$group_dset$ = rc
   else
     call trexio_str2strarray(str_compiled, $group_dset_dim$, max_str_len, dset)
     deallocate(str_compiled)
     trexio_read_$group_dset$ = TREXIO_SUCCESS
   endif

 end function trexio_read_$group_dset$
 integer(trexio_exit_code) function trexio_write_$group_dset$ (trex_file, dset, max_str_len)
   implicit none
   integer(trexio_t), intent(in), value :: trex_file
   integer(c_int32_t), intent(in), value :: max_str_len
   character(len=*), intent(in) :: dset(*)

   character(len=:), allocatable :: str_compiled
   integer(c_int64_t) :: $group_dset_dim$
   integer(trexio_exit_code) :: rc

   rc = trexio_read_$group_dset_dim$_64(trex_file, $group_dset_dim$)
   if (rc /= TREXIO_SUCCESS) then
     trexio_write_$group_dset$ = rc
   else
     call trexio_strarray2str(dset, $group_dset_dim$, max_str_len, str_compiled)
     trexio_write_$group_dset$ = trexio_write_$group_dset$_low(trex_file, str_compiled, max_str_len)
   endif

 end function trexio_write_$group_dset$

Python templates for front end

def write_$group_dset$(trexio_file, dset_w: list) -> None:
"""Write the $group_dset$ array of strings in the TREXIO file.

Parameters:

trexio_file:
   TREXIO File object.

dset_w: list
   Array of $group_dset$ strings to be written.

Raises:
   - Exception from AssertionError if TREXIO return code ~rc~ is different from TREXIO_SUCCESS and prints the error message using trexio_string_of_error.
   - Exception from some other error (e.g. RuntimeError).
"""

max_str_length = len(max(dset_w, key=len)) + 1

rc = pytr.trexio_write_$group_dset$(trexio_file.pytrexio_s, dset_w, max_str_length)
if rc != TREXIO_SUCCESS:
   raise Error(rc)
def read_$group_dset$(trexio_file, dim = None) -> list:
"""Read the $group_dset$ array of strings from the TREXIO file.

Parameters:

trexio_file:
   TREXIO File object.

dim (Optional): int
   Size of the block to be read from the file (i.e. how many items of $group_dset$ will be returned)
   If None, the function will read all necessary array dimensions from the file.

Returns:
   ~dset_r~: list
   1D list with ~dim~ elements corresponding to $group_dset$ strings read from the TREXIO file.

Raises:
   - Exception from AssertionError if TREXIO return code ~rc~ is different from TREXIO_SUCCESS and prints the error message using trexio_string_of_error.
   - Exception from some other error (e.g. RuntimeError).
"""

$group_dset_dim$ = read_$group_dset_dim$(trexio_file)

dims_list = [$group_dset_dim_list$]
dim_real  = 1
for i in range($group_dset_rank$):
   dim_real *= dims_list[i]

if dim:
   if dim_real != dim:
       raise Error(TREXIO_UNSAFE_ARRAY_DIM)
else:
   dim = dim_real

rc, dset_1d_r = pytr.trexio_read_$group_dset$_low(trexio_file.pytrexio_s, PYTREXIO_MAX_STR_LENGTH)
if rc != TREXIO_SUCCESS:
   raise Error(rc)


dset_full = dset_1d_r.split(pytr.TREXIO_DELIM)
dset_2d_r = [dset_full[i] for i in range(dim) if dset_full[i]]
if not dset_2d_r:
   raise ValueError(f"Output of {read_$group_dset$.__name__} function cannot be an empty list.")

return dset_2d_r
def has_$group_dset$(trexio_file) -> bool:
"""Check that $group_dset$ variable exists in the TREXIO file.

Parameter is a ~TREXIO File~ object that has been created by a call to ~open~ function.

Returns:
     True if the variable exists, False otherwise

Raises:
   - trexio.Error if TREXIO return code ~rc~ is TREXIO_FAILURE and prints the error message using string_of_error.
   - Exception from some other error (e.g. RuntimeError).
"""

rc = pytr.trexio_has_$group_dset$(trexio_file.pytrexio_s)
if rc == TREXIO_FAILURE:
   raise Error(rc)

return rc == TREXIO_SUCCESS

Templates for front end has/read/write a buffered vector

This corresponds to the buffer data type and is particularly useful for incremental additiona of values like it was done for sparse data but without the need to supply tuples of indices.

Function name Description Precision
trexio_has_$group_dset$ Check if a buffered dset is present in a file
trexio_read_$group_dset$ Read values of a vector in buffers Double
trexio_read_$group_dset$_size Read the number of elements stored in the file Double
trexio_write_$group_dset$ Write values of a vector in buffers Double
trexio_read_safe_$group_dset$ Safe (bounded) read (for Python API) Double
trexio_write_safe_$group_dset$ Safe (bounded) write (for Python API) Double

C source code

trexio_exit_code
trexio_read_$group_dset$ (trexio_t* const file, const int64_t offset_file, int64_t* const buffer_size_read, double* const dset)
{

if (file  == NULL) return TREXIO_INVALID_ARG_1;
if (dset  == NULL) return TREXIO_INVALID_ARG_2;
if (trexio_has_$group_dset$(file) != TREXIO_SUCCESS) return TREXIO_DSET_MISSING;

trexio_exit_code rc;

uint32_t rank = 1;
uint64_t det_size = (uint64_t) (*buffer_size_read);
uint64_t dims[1] = {det_size};

// introduce a new variable which will be modified with the number of integrals being read if EOF is encountered
int64_t eof_read_size = 0L;

switch (file->back_end) {

case TREXIO_TEXT:
rc = trexio_text_read_$group_dset$(file, offset_file, rank, dims, &eof_read_size, dset);
break;

case TREXIO_HDF5:
#ifdef HAVE_HDF5
rc = trexio_hdf5_read_$group_dset$(file, offset_file, rank, dims, &eof_read_size, dset);
break;
#else
rc = TREXIO_BACK_END_MISSING;
break;
#endif
/*
case TREXIO_JSON:
return trexio_json_read_$group_dset$(...);
break;
,*/
default:
rc = TREXIO_FAILURE;  /* Impossible case */
break;
}

if (rc != TREXIO_SUCCESS && rc != TREXIO_END) return rc;

if (rc == TREXIO_END) *buffer_size_read = eof_read_size;

return rc;
}
trexio_exit_code
trexio_read_$group_dset$_size(trexio_t* const file, int64_t* const size_max)
{

if (file  == NULL) return TREXIO_INVALID_ARG_1;
if (size_max == NULL) return TREXIO_INVALID_ARG_2;
if (trexio_has_$group_dset$(file) != TREXIO_SUCCESS) return TREXIO_DSET_MISSING;

switch (file->back_end) {

case TREXIO_TEXT:
return trexio_text_read_$group_dset$_size(file, size_max);
break;

case TREXIO_HDF5:
#ifdef HAVE_HDF5
return trexio_hdf5_read_$group_dset$_size(file, size_max);
break;
#else
return TREXIO_BACK_END_MISSING;
#endif
/*
case TREXIO_JSON:
return trexio_json_read_
break;
,*/
default:
return TREXIO_FAILURE;  /* Impossible case */
}
}
trexio_exit_code
trexio_read_safe_$group_dset$ (trexio_t* const file, const int64_t offset_file, int64_t* const buffer_size_read, double* const dset_out, const int64_t dim_out)
{
return trexio_read_$group_dset$(file, offset_file, buffer_size_read, dset_out);
}
trexio_exit_code
trexio_write_$group_dset$ (trexio_t* const file, const int64_t offset_file, const int64_t buffer_size, const double* dset)
{

if (file == NULL) return TREXIO_INVALID_ARG_1;
if (dset == NULL) return TREXIO_INVALID_ARG_2;

uint32_t rank = 1;
uint64_t dims[1] = {buffer_size};

assert(file->back_end < TREXIO_INVALID_BACK_END);

switch (file->back_end) {

case TREXIO_TEXT:
return trexio_text_write_$group_dset$(file, offset_file, rank, dims, dset);
break;

case TREXIO_HDF5:
#ifdef HAVE_HDF5
return trexio_hdf5_write_$group_dset$(file, offset_file, rank, dims, dset);
break;
#else
return TREXIO_BACK_END_MISSING;
break;
#endif
/*
case TREXIO_JSON:
rc = trexio_json_read_
break;
,*/
}

return TREXIO_FAILURE;
}
trexio_exit_code
trexio_write_safe_$group_dset$ (trexio_t* const file, const int64_t offset_file, const int64_t buffer_size, const double* dset_in, const int64_t dim_in)
{
return trexio_write_$group_dset$(file, offset_file, buffer_size, dset_in);
}
trexio_exit_code
trexio_has_$group_dset$ (trexio_t* const file)
{

if (file == NULL) return TREXIO_INVALID_ARG_1;

assert(file->back_end < TREXIO_INVALID_BACK_END);

switch (file->back_end) {

case TREXIO_TEXT:
return trexio_text_has_$group_dset$(file);

case TREXIO_HDF5:
#ifdef HAVE_HDF5
return trexio_hdf5_has_$group_dset$(file);
#else
return TREXIO_BACK_END_MISSING;
#endif
/*
case TREXIO_JSON:
return trexio_json_has_
break;
,*/
}

return TREXIO_FAILURE;
}

Fortran interface

The Fortran templates that provide an access to the C API calls from Fortran. These templates are based on the use of iso_c_binding. Pointers have to be passed by value.

interface
integer(trexio_exit_code) function trexio_write_$group_dset$(trex_file, &
                                          offset_file, buffer_size, dset) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
integer(c_int64_t), intent(in), value :: offset_file
integer(c_int64_t), intent(in), value :: buffer_size
real(c_double), intent(in)            :: dset(*)
end function trexio_write_$group_dset$
end interface

interface
integer(trexio_exit_code) function trexio_write_safe_$group_dset$ (trex_file, &
                                               offset_file, buffer_size, &
                                               dset, dset_size) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
integer(c_int64_t), intent(in), value :: offset_file
integer(c_int64_t), intent(in), value :: buffer_size
real(c_double), intent(in)            :: dset(*)
integer(c_int64_t), intent(in), value :: dset_size
end function trexio_write_safe_$group_dset$
end interface
interface
integer(trexio_exit_code) function trexio_read_safe_$group_dset$ (trex_file, &
                                              offset_file, buffer_size, &
                                              dset, dset_size) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
integer(c_int64_t), intent(in), value :: offset_file
integer(c_int64_t), intent(inout)     :: buffer_size
real(c_double), intent(out)           :: dset(*)
integer(c_int64_t), intent(in), value :: dset_size
end function trexio_read_safe_$group_dset$
end interface

interface
integer(trexio_exit_code) function trexio_read_$group_dset$(trex_file, &
                                         offset_file, buffer_size, dset) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
integer(c_int64_t), intent(in), value :: offset_file
integer(c_int64_t), intent(inout)     :: buffer_size
real(c_double), intent(out)           :: dset(*)
end function trexio_read_$group_dset$
end interface

interface
integer(trexio_exit_code) function trexio_read_$group_dset$_size (trex_file, &
                                              size_max) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
integer(c_int64_t), intent(out) :: size_max
end function trexio_read_$group_dset$_size
end interface
interface
integer(trexio_exit_code) function trexio_has_$group_dset$ (trex_file) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
end function trexio_has_$group_dset$
end interface

Python interface

def write_$group_dset$(trexio_file: File, offset_file: int, buffer_size: int, dset) -> None:
"""Write the $group_dset$ in the TREXIO file.

Parameters:

trexio_file:
   TREXIO File object.

offset_file: int
   The number of values to be skipped in the file when writing.

buffer_size: int
   The number of values to write in the file.

dset: list OR numpy.ndarray
   Array of $group_dset$ to be written. If array data type does not correspond to int64, the conversion is performed.

Raises:
   - trexio.Error if TREXIO return code ~rc~ is different from TREXIO_SUCCESS and prints the error message.
   - Exception from some other error (e.g. RuntimeError).
"""

if not isinstance(offset_file, int):
   raise TypeError("offset_file argument has to be an integer.")
if not isinstance(buffer_size, int):
   raise TypeError("buffer_size argument has to be an integer.")
if not isinstance(dset, (list, tuple, np.ndarray)):
   raise TypeError("dset argument has to be an array (list, tuple or NumPy ndarray).")

if isinstance(dset, np.ndarray) and not coefficients.dtype==np.float64:
   # convert to float64 if input is in a different precision
   dset_64 = np.float64(dset)
   rc = pytr.trexio_write_safe_$group_dset$(trexio_file.pytrexio_s, offset_file, buffer_size, dset_64)
else:
   rc = pytr.trexio_write_safe_$group_dset$(trexio_file.pytrexio_s, offset_file, buffer_size, dset)

if rc != TREXIO_SUCCESS:
   raise Error(rc)
def read_$group_dset$(trexio_file: File, offset_file: int, buffer_size: int) -> tuple:
"""Read $group_dset$ from the TREXIO file.

Parameters:

trexio_file:
   TREXIO File object.

offset_file: int
   The number of values to be skipped in the file when reading.

buffer_size: int
   The number of values to read from the file.

Returns:
   (dset, n_int_read, eof_flag) tuple where
     - dset is the NumPy array [numpy.ndarray] with the default int64 precision;
     - n_int_read [int] is the number of coefficients read from the trexio_file
       (either strictly equal to buffer_size or less than buffer_size if EOF has been reached);
     - eof_flag [bool] is True when EOF has been reached (i.e. when call to low-level pytrexio API returns TREXIO_END)
                          False otherwise.

Raises:
   - trexio.Error if TREXIO return code ~rc~ is different from TREXIO_SUCCESS and prints the error message.
   - Exception from some other error (e.g. RuntimeError).
"""

if not isinstance(offset_file, int):
   raise TypeError("offset_file argument has to be an integer.")
if not isinstance(buffer_size, int):
   raise TypeError("buffer_size argument has to be an integer.")


# read the number of values already in the file
det_num = read_$group_dset$_size(trexio_file)

# additional modification needed to avoid allocating more memory than needed if EOF will be reached during read
overflow = offset_file + buffer_size - det_num
eof_flag = False
if overflow > 0:
   verified_size = buffer_size - overflow
   eof_flag = True
else:
   verified_size = buffer_size

# main call to the low-level (SWIG-wrapped) trexio_read function, which also requires the sizes of the output to be provided
# read_buf_size contains the number of elements being read from the file, useful when EOF has been reached
rc, n_int_read, dset = pytr.trexio_read_safe_$group_dset$(trexio_file.pytrexio_s, offset_file, verified_size, verified_size)

if rc != TREXIO_SUCCESS:
   raise Error(rc)
if n_int_read == 0:
   raise ValueError("No integrals have been read from the file.")
if dset is None:
   raise ValueError("Returned NULL array from the low-level pytrexio API.")

return (dset, n_int_read, eof_flag)


def read_$group_dset$_size(trexio_file) -> int:
"""Read the number of elements stored in the TREXIO file.

Parameter is a ~TREXIO File~ object that has been created by a call to ~open~ function.

Returns:
   ~num~: int
   Integer value of corresponding to the size of the $group_dset$ array from ~trexio_file~.

Raises:
   - Exception from AssertionError if TREXIO return code ~rc~ is different from TREXIO_SUCCESS and prints the error message using trexio_string_of_error.
   - Exception from some other error (e.g. RuntimeError).
"""

rc, num = pytr.trexio_read_$group_dset$_size(trexio_file.pytrexio_s)
if rc != TREXIO_SUCCESS:
   raise Error(rc)

return num
def has_$group_dset$(trexio_file) -> bool:
"""Check that $group_dset$ exists in the TREXIO file.

Parameter is a ~TREXIO File~ object that has been created by a call to ~open~ function.

Returns:
     True if the variable exists, False otherwise

Raises:
   - trexio.Error if TREXIO return code ~rc~ is TREXIO_FAILURE and prints the error message using string_of_error.
   - Exception from some other error (e.g. RuntimeError).
"""

rc = pytr.trexio_has_$group_dset$(trexio_file.pytrexio_s)
if rc == TREXIO_FAILURE:
   raise Error(rc)

return rc == TREXIO_SUCCESS

Templates for front end has/read/write a single string attribute

Introduction

This section concerns API calls related to string attributes.

Function name Description
trexio_has_$group_str$ Check if a string attribute exists in a file
trexio_read_$group_str$ Read a string attribute
trexio_write_$group_str$ Write a string attribute

C templates for front end

Function declarations
Source code for default functions
trexio_exit_code
trexio_read_$group_str$ (trexio_t* const file, char* const str_out, const int32_t max_str_len)
{

if (file == NULL) return TREXIO_INVALID_ARG_1;
if (str_out  == NULL) return TREXIO_INVALID_ARG_2;
if (max_str_len <= 0) return TREXIO_INVALID_ARG_3;
if (trexio_has_$group_str$(file) != TREXIO_SUCCESS) return TREXIO_ATTR_MISSING;

switch (file->back_end) {

case TREXIO_TEXT:
return trexio_text_read_$group_str$(file, str_out, (uint32_t) max_str_len);

case TREXIO_HDF5:
#ifdef HAVE_HDF5
return trexio_hdf5_read_$group_str$(file, str_out, (uint32_t) max_str_len);
#else
return TREXIO_BACK_END_MISSING;
#endif
/*
case TREXIO_JSON:
return trexio_json_read_$group_str$(file, str);
break;
,*/
}

return TREXIO_FAILURE;
}
trexio_exit_code
trexio_write_$group_str$ (trexio_t* const file, const char* str, const int32_t max_str_len)
{

if (file == NULL) return TREXIO_INVALID_ARG_1;
if (str  == NULL) return TREXIO_INVALID_ARG_2;
if (max_str_len <= 0) return TREXIO_INVALID_ARG_3;
if (trexio_has_$group_str$(file) == TREXIO_SUCCESS && file->mode != 'u') return TREXIO_ATTR_ALREADY_EXISTS;

size_t len_write = strlen(str);
if ((size_t) max_str_len < len_write) return TREXIO_INVALID_STR_LEN;

switch (file->back_end) {

case TREXIO_TEXT:
return trexio_text_write_$group_str$(file, str);

case TREXIO_HDF5:
#ifdef HAVE_HDF5
return trexio_hdf5_write_$group_str$(file, str);
#else
return TREXIO_BACK_END_MISSING;
#endif
/*
case TREXIO_JSON:
return trexio_json_write_$group_str$(file, str);
,*/
}

return TREXIO_FAILURE;
}
trexio_exit_code
trexio_has_$group_str$ (trexio_t* const file)
{

if (file == NULL) return TREXIO_INVALID_ARG_1;

assert(file->back_end < TREXIO_INVALID_BACK_END);

switch (file->back_end) {

case TREXIO_TEXT:
return trexio_text_has_$group_str$(file);

case TREXIO_HDF5:
#ifdef HAVE_HDF5
return trexio_hdf5_has_$group_str$(file);
#else
return TREXIO_BACK_END_MISSING;
#endif
/*
case TREXIO_JSON:
return trexio_json_has_$group_str$(file);
,*/
}

return TREXIO_FAILURE;
}

Fortran templates for front end

The Fortran templates that provide an access to the C API calls from Fortran. These templates are based on the use of iso_c_binding. Pointers have to be passed by value.

interface
integer(trexio_exit_code) function trexio_write_$group_str$_c (trex_file, str, max_str_len) &
      bind(C, name="trexio_write_$group_str$")
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value  :: trex_file
character(kind=c_char), intent(in)    :: str(*)
integer(c_int32_t), intent(in), value :: max_str_len
end function trexio_write_$group_str$_c
end interface
interface
integer(trexio_exit_code) function trexio_read_$group_str$_c (trex_file, str, max_str_len) &
      bind(C, name="trexio_read_$group_str$")
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value  :: trex_file
character(kind=c_char), intent(out)   :: str(*)
integer(c_int32_t), intent(in), value :: max_str_len
end function trexio_read_$group_str$_c
end interface
interface
integer(trexio_exit_code) function trexio_has_$group_str$ (trex_file) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value  :: trex_file
end function trexio_has_$group_str$
end interface
 integer(trexio_exit_code) function trexio_read_$group_str$ (trex_file, str, max_str_len)
   implicit none
   integer(trexio_t), intent(in), value  :: trex_file
   integer(c_int32_t), intent(in), value :: max_str_len
   character, intent(out) :: str(*)

   trexio_read_$group_str$ = trexio_read_$group_str$_c(trex_file, str, max_str_len)

 end function trexio_read_$group_str$
 integer(trexio_exit_code) function trexio_write_$group_str$ (trex_file, str, max_str_len)
   use, intrinsic :: iso_c_binding, only : c_null_char
   implicit none
   integer(trexio_t), intent(in), value  :: trex_file
   integer(c_int32_t), intent(in), value :: max_str_len
   character(len=*), intent(in) :: str

   character(len=len_trim(str)+1) :: str_c

   str_c = trim(str) // c_null_char

   trexio_write_$group_str$ = trexio_write_$group_str$_c(trex_file, str_c, max_str_len)

 end function trexio_write_$group_str$

Python templates for front end

def write_$group_str$(trexio_file, str_w: str) -> None:
"""Write the $group_str$ variable in the TREXIO file.

Parameters:

trexio_file:
   TREXIO File object.

str_w: str
   String corresponding to the $group_str$ variable to be written.

Raises:
   - Exception from AssertionError if TREXIO return code ~rc~ is different from TREXIO_SUCCESS and prints the error message using trexio_string_of_error.
   - Exception from some other error (e.g. RuntimeError).
"""

max_str_length = len(str_w) + 1

rc = pytr.trexio_write_$group_str$(trexio_file.pytrexio_s, str_w, max_str_length)
if rc != TREXIO_SUCCESS:
   raise Error(rc)
def read_$group_str$(trexio_file) -> str:
"""Read the $group_str$ variable from the TREXIO file.

Parameter is a ~TREXIO File~ object that has been created by a call to ~open~ function.

Returns:
   ~str_r~: str
   String corresponding to the $group_str$ variable read from ~trexio_file~.

Raises:
   - Exception from AssertionError if TREXIO return code ~rc~ is different from TREXIO_SUCCESS and prints the error message using trexio_string_of_error.
   - Exception from some other error (e.g. RuntimeError).
"""

rc, str_r = pytr.trexio_read_$group_str$(trexio_file.pytrexio_s, PYTREXIO_MAX_STR_LENGTH)
if rc != TREXIO_SUCCESS:
   raise Error(rc)

return str_r
def has_$group_str$(trexio_file) -> bool:
"""Check that $group_str$ variable exists in the TREXIO file.

Parameter is a ~TREXIO File~ object that has been created by a call to ~open~ function.

Returns:
     True if the variable exists, False otherwise

Raises:
   - trexio.Error if TREXIO return code ~rc~ is TREXIO_FAILURE and prints the error message using string_of_error.
   - Exception from some other error (e.g. RuntimeError).
"""

rc = pytr.trexio_has_$group_str$(trexio_file.pytrexio_s)
if rc == TREXIO_FAILURE:
   raise Error(rc)

return rc == TREXIO_SUCCESS

Templates for front end delete an entire group (UNSAFE MODE)

Introduction

This section concerns API calls related to string attributes.

Function name Description
trexio_delete_$group$ Delete a given group from the TREXIO file

C templates for front end

trexio_exit_code
trexio_delete_$group$ (trexio_t* const file)
{

if (file == NULL) return TREXIO_INVALID_ARG_1;
if (file->mode != 'u') return TREXIO_SAFE_MODE;

switch (file->back_end) {

case TREXIO_TEXT:
return trexio_text_delete_$group$(file);

case TREXIO_HDF5:
#ifdef HAVE_HDF5
return trexio_hdf5_delete_$group$(file);
#else
return TREXIO_BACK_END_MISSING;
#endif
/*
case TREXIO_JSON:
return trexio_json_delete_$group$(file);
break;
,*/
}

return TREXIO_FAILURE;
}

Fortran templates for front end

The Fortran templates that provide an access to the C API calls from Fortran. These templates are based on the use of iso_c_binding. Pointers have to be passed by value.

interface
integer(trexio_exit_code) function trexio_delete_$group$ (trex_file) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value  :: trex_file
end function trexio_delete_$group$
end interface

Python templates for front end

def delete_$group$(trexio_file) -> None:
"""Delete the entire $group$ group from the TREXIO file.

Parameters:

trexio_file:
   TREXIO File object.

Raises:
   - Exception from AssertionError if TREXIO return code ~rc~ is different from TREXIO_SUCCESS and prints the error message using trexio_string_of_error.
   - Exception from some other error (e.g. RuntimeError).
"""

rc = pytr.trexio_delete_$group$(trexio_file.pytrexio_s)
if rc != TREXIO_SUCCESS:
   raise Error(rc)

Source code for the determinant part

Storage of the determinants is a particular case, which requires special treatment, but has to be coded only once (since there is only one group that corresponds to it). Thus, there is no need to auto-generate this part via templates.

This section concerns API calls related to Slater determinants.

Function name Description
trexio_has_determinant_list Check if an attribute exists in a file
trexio_write_determinant_list Write an attribute
trexio_read_determinant_list Read an attribute
trexio_get_int64_num Get the number of int64 bit fields per determinant

C source code

trexio_exit_code
trexio_get_int64_num(trexio_t* const file, int32_t* const num)
{

if (file  == NULL) return TREXIO_INVALID_ARG_1;
if (num   == NULL) return TREXIO_INVALID_ARG_2;

/* Read the number of mos */
int64_t mo_num = 0L;
trexio_exit_code rc = trexio_read_mo_num_64(file, &mo_num);
if (rc != TREXIO_SUCCESS) return rc;
if (mo_num == 0L) return TREXIO_INVALID_NUM;

/* Compute how many integer numbers is needed to represent a determinant */
int32_t int_num = 0;
int_num = (mo_num - 1L)/64 + 1;

*num = int_num;

return TREXIO_SUCCESS;
}
trexio_exit_code
trexio_read_determinant_list (trexio_t* const file, const int64_t offset_file, int64_t* const buffer_size_read, int64_t* const dset)
{

if (file  == NULL) return TREXIO_INVALID_ARG_1;
if (dset  == NULL) return TREXIO_INVALID_ARG_2;
if (trexio_has_determinant_list(file) != TREXIO_SUCCESS) return TREXIO_DSET_MISSING;

/* Get the number of int bit fields per determinant */
int32_t int_num = 0;
trexio_exit_code rc = trexio_get_int64_num(file, &int_num);
if (rc != TREXIO_SUCCESS) return rc;

uint32_t rank = 2;
uint64_t det_size = (uint64_t) (*buffer_size_read);
uint64_t dims[2] = {det_size, int_num*2UL};

// introduce a new variable which will be modified with the number of integrals being read if EOF is encountered
int64_t eof_read_size = 0L;

switch (file->back_end) {

case TREXIO_TEXT:
rc = trexio_text_read_determinant_list(file, offset_file, rank, dims, &eof_read_size, dset);
break;

case TREXIO_HDF5:
#ifdef HAVE_HDF5
rc = trexio_hdf5_read_determinant_list(file, offset_file, rank, dims, &eof_read_size, dset);
break;
#else
rc = TREXIO_BACK_END_MISSING;
break;
#endif
/*
case TREXIO_JSON:
return trexio_json_read_$group_dset$(...);
break;
,*/
default:
rc = TREXIO_FAILURE;  /* Impossible case */
break;
}

if (rc != TREXIO_SUCCESS && rc != TREXIO_END) return rc;

if (rc == TREXIO_END) *buffer_size_read = eof_read_size;

return rc;
}
trexio_exit_code
trexio_read_safe_determinant_list (trexio_t* const file, const int64_t offset_file, int64_t* const buffer_size_read, int64_t* const dset_out, const int64_t dim_out)
{
return trexio_read_determinant_list(file, offset_file, buffer_size_read, dset_out);
}
trexio_exit_code
trexio_write_determinant_list (trexio_t* const file, const int64_t offset_file, const int64_t buffer_size, const int64_t* dset)
{

if (file == NULL) return TREXIO_INVALID_ARG_1;
if (dset == NULL) return TREXIO_INVALID_ARG_2;

/* Get the number of int bit fields per determinant */
int32_t int_num = 0;
trexio_exit_code rc = trexio_get_int64_num(file, &int_num);
if (rc != TREXIO_SUCCESS) return rc;

uint32_t rank = 2;
uint64_t dims[2] = {buffer_size, int_num*2UL};

assert(file->back_end < TREXIO_INVALID_BACK_END);

switch (file->back_end) {

case TREXIO_TEXT:
rc = trexio_text_write_determinant_list(file, offset_file, rank, dims, dset);
break;

case TREXIO_HDF5:
#ifdef HAVE_HDF5
rc = trexio_hdf5_write_determinant_list(file, offset_file, rank, dims, dset);
break;
#else
return TREXIO_BACK_END_MISSING;
break;
#endif
/*
case TREXIO_JSON:
rc = trexio_json_read_
break;
,*/
}

if (rc != TREXIO_SUCCESS) return rc;

// Update the determinant_num value with the number of determinants written
int64_t det_num = 0L;
// Read the determinant_num if it exists already
if (trexio_has_determinant_num(file) == TREXIO_SUCCESS) {
rc = trexio_read_determinant_num_64(file, &det_num);
if (rc != TREXIO_SUCCESS) return rc;
}
// Check for the INT64 overflow before writing an updated value
if (INT64_MAX - det_num > buffer_size) {
det_num += buffer_size;
} else {
return TREXIO_INT_SIZE_OVERFLOW;
}
// Overwrite previous value. Here we have to temporarily set the file->mode to 'u' to trick the API
//   in order to overwrite existing determinant_num. Otherwise the API returns TREXIO_NUM_ALREADY_EXISTS.
char mode_tmp = file->mode;
file->mode = 'u';
rc = trexio_write_determinant_num_64(file, det_num);
file->mode = mode_tmp;
if (rc != TREXIO_SUCCESS) return rc;

return TREXIO_SUCCESS;
}
trexio_exit_code
trexio_write_safe_determinant_list (trexio_t* const file, const int64_t offset_file, const int64_t buffer_size, const int64_t* dset_in, const int64_t dim_in)
{
return trexio_write_determinant_list(file, offset_file, buffer_size, dset_in);
}
trexio_exit_code
trexio_has_determinant_list (trexio_t* const file)
{

if (file == NULL) return TREXIO_INVALID_ARG_1;

assert(file->back_end < TREXIO_INVALID_BACK_END);

switch (file->back_end) {

case TREXIO_TEXT:
return trexio_text_has_determinant_list(file);

case TREXIO_HDF5:
#ifdef HAVE_HDF5
return trexio_hdf5_has_determinant_list(file);
#else
return TREXIO_BACK_END_MISSING;
#endif
/*
case TREXIO_JSON:
return trexio_json_has_
break;
,*/
}

return TREXIO_FAILURE;
}

Fortran interface

The Fortran templates that provide an access to the C API calls from Fortran. These templates are based on the use of iso_c_binding. Pointers have to be passed by value.

interface
integer(trexio_exit_code) function trexio_write_determinant_list (trex_file, &
                                          offset_file, buffer_size, list) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
integer(c_int64_t), intent(in), value :: offset_file
integer(c_int64_t), intent(in), value :: buffer_size
integer(c_int64_t), intent(in)        :: list(*)
end function trexio_write_determinant_list
end interface

interface
integer(trexio_exit_code) function trexio_write_safe_determinant_list (trex_file, &
                                               offset_file, buffer_size, &
                                               list, list_size) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
integer(c_int64_t), intent(in), value :: offset_file
integer(c_int64_t), intent(in), value :: buffer_size
integer(c_int64_t), intent(in)        :: list(*)
integer(c_int64_t), intent(in), value :: list_size
end function trexio_write_safe_determinant_list
end interface
interface
integer(trexio_exit_code) function trexio_read_determinant_list(trex_file, &
                                         offset_file, buffer_size, list) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
integer(c_int64_t), intent(in), value :: offset_file
integer(c_int64_t), intent(inout)     :: buffer_size
integer(c_int64_t), intent(out)       :: list(*)
end function trexio_read_determinant_list
end interface

interface
integer(trexio_exit_code) function trexio_read_safe_determinant_list (trex_file, &
                                              offset_file, buffer_size, &
                                              list, list_size) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
integer(c_int64_t), intent(in), value :: offset_file
integer(c_int64_t), intent(inout)     :: buffer_size
integer(c_int64_t), intent(out)       :: list(*)
integer(c_int64_t), intent(in), value :: list_size
end function trexio_read_safe_determinant_list
end interface
interface
integer(trexio_exit_code) function trexio_has_determinant_list (trex_file) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
end function trexio_has_determinant_list
end interface

interface
integer(trexio_exit_code) function trexio_get_int64_num (trex_file, num) bind(C)
use, intrinsic :: iso_c_binding
import
integer(trexio_t), intent(in), value :: trex_file
integer(c_int32_t), intent(out) :: num
end function trexio_get_int64_num
end interface

Python interface

def write_determinant_list(trexio_file: File, offset_file: int, buffer_size: int, determinants: list) -> None:
"""Write the determinant list in the TREXIO file.

Parameters:

trexio_file:
   TREXIO File object.

offset_file: int
   The number of determinants to be skipped in the file when writing.

buffer_size: int
   The number of determinants to write in the file.

determinants: list OR numpy.ndarray
   Array of determinant_list to be written. If array data type does not correspond to int64, the conversion is performed.

Raises:
   - trexio.Error if TREXIO return code ~rc~ is different from TREXIO_SUCCESS and prints the error message.
   - Exception from some other error (e.g. RuntimeError).
"""

if not isinstance(offset_file, int):
   raise TypeError("offset_file argument has to be an integer.")
if not isinstance(buffer_size, int):
   raise TypeError("buffer_size argument has to be an integer.")
if not isinstance(determinants, (list, tuple, np.ndarray)):
   raise TypeError("determinants argument has to be an array (list, tuple or NumPy ndarray).")

convert = False
flatten = False
if isinstance(determinants, np.ndarray):
  # convert to int64 if input determinants are in a different precision
  if not determinants.dtype==np.int64:
      convert= True

  if len(determinants.shape) > 1:
      flatten = True
      if convert:
          dets_64 = np.int64(determinants).flatten()
      else:
          dets_64 = np.array(determinants, dtype=np.int64).flatten()
  else:
      if convert:
          dets_64 = np.int64(determinants)
else:
  # if input array is a multidimensional list or tuple, we have to convert it
  try:
      # if list is flat - the attempt to compute len() will raise a TypeError
      _ = len(determinants[0])
      dets_64 = np.array(determinants, dtype=np.int64).flatten()
      flatten = True
  except TypeError:
      pass

if flatten or convert:
   rc = pytr.trexio_write_safe_determinant_list(trexio_file.pytrexio_s, offset_file, buffer_size, dets_64)
else:
   rc = pytr.trexio_write_safe_determinant_list(trexio_file.pytrexio_s, offset_file, buffer_size, determinants)

if rc != TREXIO_SUCCESS:
   raise Error(rc)
def read_determinant_list(trexio_file: File, offset_file: int, buffer_size: int) -> tuple:
"""Read determinant_list from the TREXIO file.

Parameters:

trexio_file:
   TREXIO File object.

offset_file: int
   The number of determinants to be skipped in the file when reading.

buffer_size: int
   The number of determinants to read from the file.

Returns:
   (determinants, n_int_read, eof_flag) tuple where
     - determinants are NumPy arrays [numpy.ndarray] with the default int64 precision;
     - n_int_read [int] is the number of determinants read from the trexio_file
       (either strictly equal to buffer_size or less than buffer_size if EOF has been reached);
     - eof_flag [bool] is True when EOF has been reached (i.e. when call to low-level pytrexio API returns TREXIO_END)
                          False otherwise.

Raises:
   - trexio.Error if TREXIO return code ~rc~ is different from TREXIO_SUCCESS and prints the error message.
   - Exception from some other error (e.g. RuntimeError).
"""

if not isinstance(offset_file, int):
   raise TypeError("offset_file argument has to be an integer.")
if not isinstance(buffer_size, int):
   raise TypeError("buffer_size argument has to be an integer.")


# read the number of determinants already in the file
det_num = read_determinant_num(trexio_file)
# calculate the int_num (number of int bit fields per determinant)
int_num = 2 * get_int64_num(trexio_file)

# additional modification needed to avoid allocating more memory than needed if EOF will be reached during read
overflow = offset_file + buffer_size - det_num
eof_flag = False
if overflow > 0:
   verified_size = buffer_size - overflow
   eof_flag = True
else:
   verified_size = buffer_size

# main call to the low-level (SWIG-wrapped) trexio_read function, which also requires the sizes of the output to be provided
# read_buf_size contains the number of elements being read from the file, useful when EOF has been reached
rc, n_int_read, determinants = pytr.trexio_read_safe_determinant_list(trexio_file.pytrexio_s,
                                                                     offset_file,
                                                                     verified_size,
                                                                     verified_size * int_num)
if rc != TREXIO_SUCCESS:
   raise Error(rc)
if n_int_read == 0:
   raise ValueError("No integrals have been read from the file.")
if determinants is None:
   raise ValueError("Returned NULL array from the low-level pytrexio API.")

# conversion to custom types can be performed on the user side, here we only reshape the returned flat array according to int_num
dets_reshaped = np.reshape(determinants, (verified_size, int_num), order='C')

return (dets_reshaped, n_int_read, eof_flag)


def get_int64_num(trexio_file) -> int:
"""Compute the number of int64 bit fields corresponding to the TREXIO file.

Parameter is a ~TREXIO File~ object that has been created by a call to ~open~ function.

Returns:
   ~num~: int
   Number of int64 bit fields per determinant.

Raises:
   - Exception from AssertionError if TREXIO return code ~rc~ is different from TREXIO_SUCCESS and prints the error message using trexio_string_of_error.
   - Exception from some other error (e.g. RuntimeError).
"""

rc, num = pytr.trexio_get_int64_num(trexio_file.pytrexio_s)
if rc != TREXIO_SUCCESS:
   raise Error(rc)

return num
def has_determinant_list(trexio_file) -> bool:
"""Check that determinant_list exists in the TREXIO file.

Parameter is a ~TREXIO File~ object that has been created by a call to ~open~ function.

Returns:
     True if the variable exists, False otherwise

Raises:
   - trexio.Error if TREXIO return code ~rc~ is TREXIO_FAILURE and prints the error message using string_of_error.
   - Exception from some other error (e.g. RuntimeError).
"""

rc = pytr.trexio_has_determinant_list(trexio_file.pytrexio_s)
if rc == TREXIO_FAILURE:
   raise Error(rc)

return rc == TREXIO_SUCCESS

General helper functions

This section contains general helper functions like trexio_info.

trexio_info prints information about the TREXIO configuration (see config.h file). In particular:

  1. TREXIO_PACKAGE_VERSION [string]
  2. HAVE_HDF5 [bool]
  3. HDF5_VERSION [string] (optional, only if HAVE_HDF5 is true)
  4. TREXIO_GIT_HASH [string]

trexio_mark_safety checks if the file has been open in UNSAFE mode. If it was, the metadata_unsafe attribute can be overwritten with the value provided in a second argument of the function. Since metadata_unsafe is set to 1 (true) upon the first opening of the file in UNSAFE mode, this value is immutable. However, if the user validated that the file is correct (e.g. using trexio-tools), then value of the metadata_unsafe attribute can be changed using the aforementioned function.

trexio_to_orbital_list function converts the list of integer bit fields of a given determinant into a list of indices of the occupied orbitals (for one spin component). trexio_to_orbital_list_up_dn function does the same but for both up- and down-spin components of the determinant and returns two list of orbitals each corresponding to a different component.

trexio_to_bitfield_list function converts the list of occupied orbitals (up- or down-spin) into the corresponding int64_t bitfield representation of the determinant. If the creation of the bitfield requires a change of sign, the return code is TREXIO_PHASE_CHANGE.

C

trexio_exit_code trexio_info(void);
trexio_exit_code trexio_mark_safety(trexio_t* const file, const int32_t safety_flag);

typedef int64_t  bitfield_t;

#define TREXIO_ORBITAL_SHIFT      1
#define TREXIO_INT_SIZE           64
#define TREXIO_NORB_PER_INT       ( 8*sizeof(bitfield_t) )
#define TREXIO_NORB_PER_INT_SHIFT ( trailz( TREXIO_NORB_PER_INT ) )

trexio_exit_code trexio_to_orbital_list (const int32_t N_int, const bitfield_t* d1, int32_t* const list, int32_t* const occupied_num);
trexio_exit_code trexio_to_orbital_list_up_dn (const int32_t N_int, const bitfield_t* d1, int32_t* const list_up, int32_t* const list_dn, int32_t* const occ_num_up, int32_t* const occ_num_dn);
trexio_exit_code trexio_safe_to_orbital_list (const int32_t N_int, const bitfield_t* dset_in, const int64_t dim_in, int32_t* const dset_out, const int64_t dim_out, int32_t* const num);
trexio_exit_code trexio_safe_to_orbital_list_up_dn (const int32_t N_int, const bitfield_t* dset_in, const int64_t dim_in, int32_t* const dset_up_out, const int64_t dim_up_out, int32_t* const dset_dn_out, const int64_t dim_dn_out, int32_t* const num_up, int32_t* const num_dn);
trexio_exit_code trexio_to_bitfield_list (const int32_t* orb_list, const int32_t occupied_num, bitfield_t* const bit_list, const int32_t N_int);
trexio_exit_code trexio_to_bitfield_list (const int32_t* orb_list,
                                      const int32_t occupied_num,
                                      bitfield_t* const bit_list,
                                      const int32_t N_int)
{
if (orb_list == NULL)  return TREXIO_INVALID_ARG_1;
if (occupied_num <= 0) return TREXIO_INVALID_ARG_2;
if (bit_list == NULL)  return TREXIO_INVALID_ARG_3;
if (N_int <= 0)        return TREXIO_INVALID_ARG_4;

for (int32_t j = 0 ; j < N_int ; j++) {
  bit_list[j] = (bitfield_t) 0;
}

uint32_t i;
uint32_t k;
uint32_t iorb;
bitfield_t mask;
uint32_t nswaps = 0;

for (int32_t pos = 0 ; pos < occupied_num ; pos++) {
iorb = ((uint32_t) (orb_list[pos] + 1)) - TREXIO_ORBITAL_SHIFT;

// Set the bit of to one
i = (uint32_t) (iorb >> TREXIO_NORB_PER_INT_SHIFT);
k = (uint32_t) (iorb & (TREXIO_NORB_PER_INT - 1) );
mask = ((bitfield_t) 1) << k;
bit_list[i] |= mask;

// Check for phase changes
mask = ~(mask - (bitfield_t) 1);
nswaps += popcnt(mask & bit_list[i]) - 1;
for (int j=i+1 ; j < N_int ; ++j) {
  if (bit_list[j] != (bitfield_t) 0)
    nswaps += popcnt(bit_list[j]);
}
}

if ( (nswaps & 1) == 0)
return TREXIO_SUCCESS;
else
return TREXIO_PHASE_CHANGE;
}
trexio_exit_code trexio_to_orbital_list(const int32_t N_int,
                                   const bitfield_t* d1,
                                   int32_t* const list,
                                   int32_t* const occupied_num)
{
if (N_int <= 0) return TREXIO_INVALID_ARG_1;
if (d1 == NULL) return TREXIO_INVALID_ARG_2;
if (list == NULL) return TREXIO_INVALID_ARG_3;
if (occupied_num == NULL) return TREXIO_INVALID_ARG_4;

bitfield_t  tmp;
int32_t     shift;
int32_t     k;
int32_t     pos;

k = 0;
shift = TREXIO_ORBITAL_SHIFT;

for (int32_t i=0 ; i<N_int ; i++)
{
 tmp = d1[i];

 while (tmp != (bitfield_t) 0)
 {
     pos = trailz(tmp);
     if (pos < 0) return TREXIO_FAILURE;

     list[k] = ( (int32_t) pos) + shift - 1;
     tmp ^= ( ((bitfield_t) 1) << pos);
     k++;
 }
 shift += TREXIO_NORB_PER_INT;
}

,*occupied_num = (int32_t) k;
return TREXIO_SUCCESS;
}
trexio_exit_code trexio_to_orbital_list_up_dn(const int32_t N_int,
                                          const bitfield_t* d1,
                                          int32_t* const list_up,
                                          int32_t* const list_dn,
                                          int32_t* const occ_num_up,
                                          int32_t* const occ_num_dn)
{
if (N_int <= 0) return TREXIO_INVALID_ARG_1;
if (d1 == NULL) return TREXIO_INVALID_ARG_2;
if (list_up == NULL) return TREXIO_INVALID_ARG_3;
if (list_dn == NULL) return TREXIO_INVALID_ARG_4;
if (occ_num_up == NULL) return TREXIO_INVALID_ARG_5;
if (occ_num_dn == NULL) return TREXIO_INVALID_ARG_6;

trexio_exit_code rc;

/* First process up-spin electrons */
rc = trexio_to_orbital_list(N_int, &d1[0], list_up, occ_num_up);
if (rc != TREXIO_SUCCESS) return rc;

/* Now process down-spin electrons */
rc = trexio_to_orbital_list(N_int, &d1[N_int], list_dn, occ_num_dn);
if (rc != TREXIO_SUCCESS) return rc;

return TREXIO_SUCCESS;
}
trexio_exit_code
trexio_safe_to_orbital_list (const int32_t N_int,
    const bitfield_t* dset_in,
                         const int64_t dim_in,
    int32_t* const dset_out,
                         const int64_t dim_out,
    int32_t* const num)
{
return trexio_to_orbital_list(N_int, dset_in, dset_out, num);
}

trexio_exit_code
trexio_safe_to_orbital_list_up_dn (const int32_t N_int,
          const bitfield_t* dset_in,
                               const int64_t dim_in,
          int32_t* const dset_up_out,
                               const int64_t dim_up_out,
          int32_t* const dset_dn_out,
                               const int64_t dim_dn_out,
          int32_t* const num_up,
          int32_t* const num_dn)
{
return trexio_to_orbital_list_up_dn(N_int, dset_in, dset_up_out, dset_dn_out, num_up, num_dn);
}
/* Popcount and trailz */
#if TREXIO_INT_SIZE == 64

extern int __builtin_popcountll (unsigned long long x_0);
#define popcnt(X) __builtin_popcountll((unsigned long long) X)

extern int __builtin_ctzll (unsigned long long x_0);
#define trailz(X) __builtin_ctzll((unsigned long long) X)

#elif TREXIO_INT_SIZE == 32

extern int __builtin_popcountl (unsigned long x_0);
#define popcnt(X) __builtin_popcountl((unsigned long) X)

extern int __builtin_ctzl(unsigned long x_0);
#define trailz(X) __builtin_ctzl((unsigned long) X)

#elif TREXIO_INT_SIZE == 16

extern int __builtin_popcount (unsigned int x_0);
#define popcnt(X) __builtin_popcount((unsigned int) X)

extern int __builtin_ctz (unsigned int x_0);
#define trailz(X) __builtin_ctz((unsigned int) X)

#else

#error("Invalid TREXIO_INT_SIZE")

#endif
trexio_exit_code
trexio_info (void)
{
printf("TREXIO_PACKAGE_VERSION : %s\n", TREXIO_PACKAGE_VERSION);

#ifdef TREXIO_GIT_HASH
printf("TREXIO_GIT_HASH        : %s\n", TREXIO_GIT_HASH);
#else
printf("GIT_HASH is stored in the config.h file, which is missing.");
#endif

#ifdef HAVE_HDF5
printf("HAVE_HDF5              : true\n");
printf("%s\n", H5_VERS_INFO);
#else
printf("HAVE_HDF5              : false\n");
printf("TREXIO configured without the HDF5 library\n");
#endif

return TREXIO_SUCCESS;
}
trexio_exit_code
trexio_mark_safety (trexio_t* const file, const int32_t safety_flag)
{

if (file == NULL) return TREXIO_INVALID_ARG_1;
/* 1 for true ; 0 for false */
if (safety_flag != 0 && safety_flag != 1) return TREXIO_INVALID_ARG_2;
/* Cannot mark the file in safe mode */
if (file->mode != 'u') return TREXIO_FAILURE;

return trexio_write_metadata_unsafe(file, safety_flag);
}

Fortran

interface
integer function trexio_info () bind(C)
  use, intrinsic :: iso_c_binding
end function trexio_info
end interface
interface
integer(trexio_exit_code) function trexio_to_bitfield_list_c(list, occupied_num, det_list, N_int) &
bind(C, name="trexio_to_bitfield_list")
  use, intrinsic :: iso_c_binding
  import
  integer(c_int32_t), intent(in)        :: list(*)
  integer(c_int32_t), intent(in), value :: occupied_num
  integer(c_int64_t), intent(inout)     :: det_list(*)
  integer(c_int32_t), intent(in), value :: N_int
end function trexio_to_bitfield_list_c
end interface
interface
integer(trexio_exit_code) function trexio_to_orbital_list_c(N_int, d1, list, occupied_num) &
bind(C, name="trexio_to_orbital_list")
  use, intrinsic :: iso_c_binding
  import
  integer(c_int32_t), intent(in), value :: N_int
  integer(c_int64_t), intent(in)        :: d1(*)
  integer(c_int32_t), intent(inout)     :: list(*)
  integer(c_int32_t), intent(inout)     :: occupied_num
end function trexio_to_orbital_list_c
end interface
interface
integer(trexio_exit_code) function trexio_to_orbital_list_up_dn_c(N_int, d1, list_up, list_dn, occ_num_up, occ_num_dn) &
bind(C, name="trexio_to_orbital_list_up_dn")
  use, intrinsic :: iso_c_binding
  import
  integer(c_int32_t), intent(in), value :: N_int
  integer(c_int64_t), intent(in)        :: d1(*)
  integer(c_int32_t), intent(inout)     :: list_up(*)
  integer(c_int32_t), intent(inout)     :: list_dn(*)
  integer(c_int32_t), intent(inout)     :: occ_num_up
  integer(c_int32_t), intent(inout)     :: occ_num_dn
end function trexio_to_orbital_list_up_dn_c
end interface

Python

def info():
 """Print the info about the installed TREXIO library."""

 rc = pytr.trexio_info()
 if rc != TREXIO_SUCCESS:
     raise Error(rc)
def to_bitfield_list(n_int: int, orbitals: list) -> list:
 """Convert a list of occupied orbitals into a bitfield determinant.

 Input:
     ~orbitals~ - list of occupied orbital indices fields (integers)
     ~n_int~ - number of bitfields per determinant of a given spin

 Returns:
     ~bitfield_list~: list

 Raises:
     - Exception from AssertionError if TREXIO return code ~rc~ is different from TREXIO_SUCCESS and prints the error message using trexio_string_of_error.
     - Exception from some other error (e.g. RuntimeError).
 """


 rc, bitfield_list = pytr.trexio_to_bitfield_list(orbitals, n_int)
 if rc != TREXIO_SUCCESS:
     raise Error(rc)
 if len(bitfield_list) != n_int:
     raise Exception("Inconsistent size of the bitfield_list.")

 return bitfield_list
def to_orbital_list(n_int: int, determinant: list) -> list:
 """Convert a given determinant into a list of occupied orbitals.

 Input:
     ~determinant~ - list of bit fields (integers)
     ~n_int~ - number of bit fields per determinant of a given spin

 Returns:
     ~orbital_list~: list

 Raises:
     - Exception from AssertionError if TREXIO return code ~rc~ is different from TREXIO_SUCCESS and prints the error message using trexio_string_of_error.
     - Exception from some other error (e.g. RuntimeError).
 """

 # max possible size of the orbital list per spin component (upper limit on the number of MOs)
 size_max = n_int * 64

 rc, orbital_list, occ_num = pytr.trexio_safe_to_orbital_list(n_int, determinant, size_max)
 if rc != TREXIO_SUCCESS:
     raise Error(rc)
 if len(orbital_list) < occ_num:
     raise Exception("Inconsistent size of the orbital_list.")

 return orbital_list[0:occ_num]
def to_orbital_list_up_dn(n_int: int, determinant: list) -> tuple:
 """Convert a given determinant into two lists of occupied orbitals.

 Input:
     ~determinant~ - list of bit fields (integers)
     ~n_int~ - number of bit fields per determinant of a given spin

 Returns:
     result: tuple with the following items:
       ~orbital_list_up~: list of orbitals occupied by up-spin electrons
       ~orbital_list_dn~: list of orbitals occupied by down-spin electrons

 Raises:
     - Exception from AssertionError if TREXIO return code ~rc~ is different from TREXIO_SUCCESS and prints the error message using trexio_string_of_error.
     - Exception from some other error (e.g. RuntimeError).
 """

 # max possible size of the orbital list per spin component (upper limit on the number of MOs)
 size_max = n_int * 64

 rc, orbital_list_up, orbital_list_dn, occ_num_up, occ_num_dn = pytr.trexio_safe_to_orbital_list_up_dn(n_int, determinant, size_max, size_max)
 if rc != TREXIO_SUCCESS:
     raise Error(rc)
 if len(orbital_list_up) < occ_num_up:
     raise Exception("Inconsistent size of the orbital_list for up-spin electrons.")
 if len(orbital_list_dn) < occ_num_dn:
     raise Exception("Inconsistent size of the orbital_list for down-spin electrons.")

 return (orbital_list_up[0:occ_num_up], orbital_list_dn[0:occ_num_dn])

Fortran helper/wrapper functions

The function below adapts the original C-based trexio_open for Fortran. This is needed due to the fact that strings in C are terminated by NULL character \0 unlike strings in Fortran. Note, that Fortran interface calls the main TREXIO API, which is written in C.

contains
 integer(trexio_t) function trexio_open (filename, mode, back_end, rc_open)
   use, intrinsic :: iso_c_binding, only : c_null_char
   implicit none
   character(len=*), intent(in)                    :: filename
   character, intent(in), value                    :: mode
   integer(trexio_back_end_t), intent(in), value   :: back_end
   integer(trexio_exit_code), intent(out)          :: rc_open
   character(len=len_trim(filename)+1)             :: filename_c
   integer(trexio_exit_code) :: rc

   filename_c = trim(filename) // c_null_char
   trexio_open = trexio_open_c(filename_c, mode, back_end, rc_open)
   if (trexio_open == 0_8 .or. rc_open /= TREXIO_SUCCESS) then
     return
   endif
   rc = trexio_set_one_based(trexio_open)
   if (rc /= TREXIO_SUCCESS) then
      rc = trexio_close(trexio_open)
      trexio_open = 0_8
   endif
 end function trexio_open

The function below adapts the original C-based trexio_inquire for Fortran. This is needed due to the same reasons as for trexio_open function. Note, that Fortran interface calls the main TREXIO API, which is written in C.

 integer(trexio_exit_code) function trexio_inquire (filename)
   use, intrinsic :: iso_c_binding
   implicit none
   character(len=*), intent(in)        :: filename
   character(len=len_trim(filename)+1) :: filename_c

   filename_c = trim(filename) // c_null_char
   trexio_inquire = trexio_inquire_c(filename_c)
 end function trexio_inquire

Similarly, the following function adapts trexio_cp.

integer(trexio_exit_code) function trexio_cp (source, destination)
  use, intrinsic :: iso_c_binding
  implicit none
  character(len=*), intent(in)           :: source
  character(len=*), intent(in)           :: destination
  character(len=len_trim(source)+1)      :: source_c
  character(len=len_trim(destination)+1) :: destination_c

  source_c = trim(source) // c_null_char
  destination_c = trim(destination) // c_null_char
  trexio_cp = trexio_cp_c(source_c, destination_c)
end function trexio_cp

The subroutines below wrap the to_orbital_list functions to shift the MO indices by 1 since in Fortran arrays are 1-based and C/Python they are 0-based.

 integer(trexio_exit_code) function trexio_to_bitfield_list(list, occupied_num, det_list, N_int)
   use, intrinsic :: iso_c_binding
   implicit none

   integer(c_int32_t), intent(in)        :: list(*)
   integer(c_int32_t), intent(in), value :: occupied_num
   integer(c_int64_t), intent(out)       :: det_list(*)
   integer(c_int32_t), intent(in), value :: N_int
   integer(c_int32_t)                    :: list_0based(occupied_num)

   integer :: i
   do i = 1,occupied_num
     list_0based(i) = list(i) - 1
   enddo

   trexio_to_bitfield_list = trexio_to_bitfield_list_c(list_0based, occupied_num, det_list, N_int)
   if (trexio_to_bitfield_list /= TREXIO_SUCCESS) then
     return
   endif

 end function trexio_to_bitfield_list


 integer(trexio_exit_code) function trexio_to_orbital_list(N_int, d1, list, occupied_num)
   use, intrinsic :: iso_c_binding
   implicit none

   integer(c_int32_t), intent(in), value :: N_int
   integer(c_int64_t), intent(in)        :: d1(*)
   integer(c_int32_t), intent(out)       :: list(*)
   integer(c_int32_t), intent(out)       :: occupied_num

   integer :: i

   trexio_to_orbital_list = trexio_to_orbital_list_c(N_int, d1, list, occupied_num)
   if (trexio_to_orbital_list /= TREXIO_SUCCESS) then
     return
   endif

   do i = 1,occupied_num
     list(i) = list(i) + 1
   enddo
 end function trexio_to_orbital_list


 integer(trexio_exit_code) function trexio_to_orbital_list_up_dn(N_int, d1, list_up, list_dn, occ_num_up, occ_num_dn)
   use, intrinsic :: iso_c_binding
   implicit none
   integer(c_int32_t), intent(in), value :: N_int
   integer(c_int64_t), intent(in)        :: d1(*)
   integer(c_int32_t), intent(out)       :: list_up(*)
   integer(c_int32_t), intent(out)       :: list_dn(*)
   integer(c_int32_t), intent(out)       :: occ_num_up
   integer(c_int32_t), intent(out)       :: occ_num_dn

   integer :: i

   trexio_to_orbital_list_up_dn = trexio_to_orbital_list_up_dn_c(N_int, d1, list_up, list_dn, occ_num_up, occ_num_dn)
   if (trexio_to_orbital_list_up_dn /= TREXIO_SUCCESS) then
     return
   endif

   do i = 1,occ_num_up
     list_up(i) = list_up(i) + 1
   enddo
   do i = 1,occ_num_dn
     list_dn(i) = list_dn(i) + 1
   enddo
 end function trexio_to_orbital_list_up_dn

The subroutine below transforms an array of Fortran strings into one big string using TREXIO_DELIM symbol as a delimeter and adds NULL character in the end in order to properly pass the desired string to C API. This is needed due to the fact that strings in C are terminated by NULL character \0.

 subroutine trexio_strarray2str(str_array, max_num_str, max_len_str, str_res)
   use, intrinsic :: iso_c_binding, only : c_null_char
   implicit none

   integer(c_int64_t), intent(in), value   :: max_num_str  ! number of elements in strign array
   integer, intent(in), value   :: max_len_str  ! maximum length of a string in an array
   character(len=*), intent(in)  :: str_array(*)
   character(len=:), allocatable, intent(out) :: str_res
   integer(c_int64_t) :: i

   str_res = ''
   do i = 1, max_num_str
     str_res = str_res // trim(str_array(i)) // TREXIO_DELIM
   enddo
   str_res = str_res // c_null_char

 end subroutine trexio_strarray2str

The subroutine below does the reverse tranformation from one big string with delimeters into an array of Fortran strings.

 subroutine trexio_str2strarray(str_flat, max_num_str, max_len_str, str_array)
   implicit none

   integer(c_int64_t), intent(in), value   :: max_num_str  ! number of elements in strign array
   integer, intent(in), value              :: max_len_str  ! maximum length of a string in an array
   character(kind=c_char), intent(in)      :: str_flat(*)
   character(len=*), intent(inout)         :: str_array(*)

   character(len=max_len_str)  :: tmp_str
   integer(c_int64_t) :: i, j, k, ind, len_flat

   len_flat = (max_len_str+1)*max_num_str + 1

   ind=1
   do i=1,max_num_str
     k = 1
     tmp_str=''
     do j=ind,len_flat
       if (str_flat(j) == TREXIO_DELIM) then
         ind=j+1
         exit
       endif
       tmp_str(k:k) = str_flat(j)
       k = k + 1
     enddo
     str_array(i)=tmp_str
   enddo

 end subroutine trexio_str2strarray

The subroutine is a Fortran analogue of assert in C. It check that the the return code of the TREXIO API call is equal to a given return code. It can optionally print a success message if the two code are identical, i.e. if the assert statement pass.

 subroutine trexio_assert(trexio_rc, check_rc, success_message)
   implicit none

   integer, intent(in), value :: trexio_rc
   integer, intent(in), value :: check_rc
   character(len=*), intent(in), optional  :: success_message

   character*(128) :: str

   if (trexio_rc == check_rc) then
     if (present(success_message)) write(*,*) success_message
   else
     call trexio_string_of_error(trexio_rc, str)
     print *, trim(str)
     stop 1
   endif

 end subroutine trexio_assert