External Programming Interfaces

This section describes ways in which Open GENIE may be interfaced to externally written code in FORTRAN or C++ for a variety of purposes.

External software may be either called from Open GENIE whilst Open GENIE maintains control of the session and is responsible for handling any exception conditions which might arise. Alternatively, external code may call into specific parts of Open GENIE in more of the fashion of a subroutine library. Currently there is one defined interface of each sort in Open GENIE.

Callout Interfaces

Open GENIE supports a simple callout interface which allows user FORTRAN code to be called from within an Open GENIE session and provides a mechanism for providing the code with data from Open GENIE internal variables.

FORTRAN module interface
C module interface

Callin Interfaces

Open GENIE supports a callin interface to allow any user program to use the Open GENIE Data Access Interface (GDAI). This interface provides a multi-language way of getting at the same data as Open GENIE.

GDAI interface

Module Subroutines Callable from FORTRAN

This section describes the user written FORTRAN interface which allows modules in FORTRAN to be compiled and loaded into Open GENIE so that they can be run as if they had been built into the original code. Once loaded, a module can be called as efficiently as any other hard coded function from Open GENIE (see the Module() command). The function will run in a "safe" environment so that if it should crash, control will return to Open GENIE without causing a crash of Open GENIE itself.

Here we give reference information for:

  1. FORTRAN template for a user written module.
  2. Helper functions to aid communication of the module with Open GENIE.

For more general information and examples of using modules, see the Open GENIE User Manual

FORTRAN Template

The essential purpose of this interface is to allow data transfer and communication with the running Open GENIE session from which the external FORTRAN module was called. All data transfer is handled by allowing the Module() command in GCL to take a workspace of parameters which are made available to the FORTRAN SUBROUTINEs if they adhere to a certain template. The workspace itself, is an advanced data type and as such is not easily handled itself in FORTRAN, what is provided in the FORTRAN interface is a set of Helper Functions which allow data to be read and written to and from this parameter workspace.

The FORTRAN template is actually very simple and is given below. A normal procedure for converting an existing program is to turn the program into one or more subroutines taking appropriate data as parameters and then to call these subroutines from the FORTRAN template subroutine to perform the appropriate functions. It may be necessary to separate the functional part of the program from any subroutines that query a user interactively for data, these subroutines will probably need replacing with helper functions (or they may more easily be done from GCL and/or the TK graphical interface anyway).

C An F77 template subroutine for creating an Open GENIE module in FORTRAN
C Several subroutines like the one routine below may be included in one
C module.
C
C Called from GCL with e.g. MODULE:EXECUTE("my_fortran_module", PARS)
C
C Freddie Akeroyd, ISIS, 20/2/97 - modified by CM-S 2/7/97
C
      SUBROUTINE MY_FORTRAN_MODULE(PARS_GET, PARS_PUT)
      IMPLICIT NONE

C Include mandatory definitions 

      INCLUDE 'genie_modules.inc'
      EXTERNAL PARS_GET, PARS_PUT

C ... User declarations here ...

C Check we have a recent enough version of the genie library -
C all modules should do this to avoid unexpected crashes

      IF (MODULE_VERSION_OK(GENIE_MAJOR, GENIE_MINOR) .EQ. 0) THEN
          MODULE_ERROR("my_fortran_module",
     +                 "Error - Open GENIE Version mismatch",
     +                 "Please re-compile this module")
      ENDIF

C All user code goes here, generally the format will be to access some
C data using the helper functions, process it via new or exising user
C written subroutines, and finally return any modified parameters using the
C helper functions

      RETURN
      END

Helper Functions

There are three sorts of helper functions provided by the FORTRAN module interface. They are all listed below

Commands to obtain data from Open GENIE Description
MODULE_GET_DOUBLE Reads a double precision real from the PARS workspace
MODULE_GET_INT Reads an integer from the PARS workspace
MODULE_GET_REAL Reads a real from the PARS workspace
MODULE_GET_STRING Reads a string from the PARS workspace
MODULE_GET_DOUBLE_ARRAY Reads a double precision real array from the PARS workspace
MODULE_GET_INT_ARRAY Reads an integer array from the PARS workspace
MODULE_GET_REAL_ARRAY Reads a real array from the PARS workspace
MODULE_GET_STRING_ARRAY Reads a string array from the PARS workspace
Commands to return data to Open GENIE Description
MODULE_PUT_DOUBLE Replaces a double precision real in the PARS workspace
MODULE_PUT_INT Replaces an integer in the PARS workspace
MODULE_PUT_REAL Replaces a real in the PARS workspace
MODULE_PUT_STRING Replaces a string in the PARS workspace
MODULE_PUT_DOUBLE_ARRAY Replaces an double precision real array in the PARS workspace
MODULE_PUT_INT_ARRAY Replaces an integer array in the PARS workspace
MODULE_PUT_REAL_ARRAY Replaces an real array in the PARS workspace
MODULE_PUT_STRING_ARRAY Replaces an string array in the PARS workspace
MODULE_PUT_ND_REAL_ARRAY Replaces an n-dimensional real array in the PARS workspace
Commands to communicate with the user Description
MODULE_PRINT Prints to the terminal
MODULE_INFORMATION Prints to the terminal as an informational message (in blue)
MODULE_ERROR Prints a formatted error message

HELPER FUNCTION REFERENCE

Commands to Obtain Data from Open Genie

All commands to read data from Genie into a user program start with the "module_get_" prefix, and have their first two parameters in common. The first parameter, called PARS_GET, is an EXTERNAL entity passed by Genie. The second parameter, NAME, is a character variable which is the label assigned to the variable in GCL when the parameters workspace was created. For arrays, a LENGTH variable must also be passed; on input this should be set to the maximum allowed number of points, and on output it will be set to the number actually read. If too many points are passed out from GCL, LENGTH will still indicate how many were passed, but the number actually read will be the length of the array which was passed as input; thus testing LENGTH on return provides a check for array overflow.

There is no need for MODULE_GET_ND_REAL_ARRAY to correspond to the MODULE_PUT_ND_REAL_ARRAY function because an array can always be converted to 1-D using the Redim() command from GCL before passing it out.

The subroutine names and parameter types and names are given below:

MODULE_GET_DOUBLE	(EXTERNAL PARS_GET, CHARACTER*80 NAME, REAL*8 VALUE)
MODULE_GET_INT		(EXTERNAL PARS_GET, CHARACTER*80 NAME, INTEGER VALUE) 
MODULE_GET_REAL		(EXTERNAL PARS_GET, CHARACTER*80 NAME, REAL VALUE) 
MODULE_GET_STRING	(EXTERNAL PARS_GET, CHARACTER*80 NAME, CHARACTER*512 VALUE)
MODULE_GET_DOUBLE_ARRAY	(EXTERNAL PARS_GET, CHARACTER*80 NAME, REAL*8 VALUE(*), INTEGER LENGTH) 
MODULE_GET_INT_ARRAY	(EXTERNAL PARS_GET, CHARACTER*80 NAME, INTEGER VALUE(*), INTEGER LENGTH)
MODULE_GET_REAL_ARRAY	(EXTERNAL PARS_GET, CHARACTER*80 NAME, REAL VALUE(*), INTEGER LENGTH)
MODULE_GET_STRING_ARRAY	(EXTERNAL PARS_GET, CHARACTER*80 NAME, CHARACTER*512(*) VALUE, INTEGER LENGTH) 

Commands to return Data to Open Genie

These commands return data to Open GENIE by replacing the fields in the PARS workspace, otherwise they are very similar to the corresponding "module_get_" commands. The NAME parameter will be the field name in the result workspace returned to GCL, and the LENGTH parameter for the array routines will indicate how many data points to send.

The subroutine names and parameter types and names are given below:

MODULE_PUT_DOUBLE	(EXTERNAL PARS_PUT, CHARACTER*80 NAME, REAL*8 VALUE)
MODULE_PUT_INT		(EXTERNAL PARS_PUT, CHARACTER*80 NAME, INTEGER VALUE) 
MODULE_PUT_REAL		(EXTERNAL PARS_PUT, CHARACTER*80 NAME, REAL VALUE) 
MODULE_PUT_STRING	(EXTERNAL PARS_PUT, CHARACTER*80 NAME, CHARACTER*512 VALUE)
MODULE_PUT_DOUBLE_ARRAY	(EXTERNAL PARS_PUT, CHARACTER*80 NAME, REAL*8 VALUE(*), INTEGER LENGTH) 
MODULE_PUT_INT_ARRAY	(EXTERNAL PARS_PUT, CHARACTER*80 NAME, INTEGER VALUE(*), INTEGER LENGTH)
MODULE_PUT_REAL_ARRAY	(EXTERNAL PARS_PUT, CHARACTER*80 NAME, REAL VALUE(*), INTEGER LENGTH)
MODULE_PUT_STRING_ARRAY	(EXTERNAL PARS_PUT, CHARACTER*80 NAME, CHARACTER*512(*) VALUE, INTEGER LENGTH)
MODULE_PUT_ND_REAL_ARRAY(EXTERNAL PARS_PUT, CHARACTER*80 NAME, REAL VALUE(*),
                         INTEGER DIMS_ARRAY(NDIMS), INTEGER NDIMS)

Commands to Communiate With The User

These provide a means for a FORTRAN program to send messages to the user through the standard Open GENIE messaging system. This means, for example that informational messages can be switched off using the Toggle/Info command as with ordinary Open GENIE informational messages. Using these routines ensures that the timing of output messages will be properly synchronized with Open GENIE.

The subroutine names and parameter types and names are given below:

MODULE_PRINT		(CHARACTER*(*) MESSAGE)
MODULE_INFORMATION	(CHARACTER*(*) MESSAGE)
MODULE_ERROR		(CHARACTER*(*) FUNCTION_NAME, ERROR_MESSAGE, POSSIBLE_SOLUTION)

Module Subroutines Callable from C

This section describes the user written C interface which allows modules in C to be compiled and loaded into Open GENIE so that they can be run as if they had been built into the original code. Once loaded, a module can be called as efficiently as any other hard coded function from Open GENIE (see the Module() command). The function will run in a "safe" environment so that if it should crash, control will return to Open GENIE without causing a crash of Open GENIE itself.

Here we give reference information for:

  1. C template for a user written module.
  2. Helper functions to aid communication of the module with Open GENIE.

For more general information and examples of using modules, see the Open GENIE User Manual

C Template

The essential purpose of this interface is to allow data transfer and communication with the running Open GENIE session from which the external C module was called. All data transfer is handled by allowing the Module() command in GCL to take a workspace of parameters which are made available to C functions if they adhere to a certain template. To access parts of the workspace in the C interface a set of Helper Functions which allow data to be read and written to and from this parameter workspace are provided.

The C template is actually very simple and is given below. A normal procedure for converting an existing C program is to take the existing analysis functions and to call them from one or more functions following the C template below. User interface functions doing output can be replaced with the print helper functions (or they may more easily be done from GCL and/or the TK graphical interface anyway once the module is in Open GENIE).

/* A C template function for creating an Open GENIE module in C
 * Several functions like the one routine below may be included in one
 * module.
 * Called from GCL with e.g. MODULE:EXECUTE:C("my_c_module", PARS)
 *
 * Freddie Akeroyd, ISIS, 19/2/97 - modified by CM-S 2/7/97
 */
/* 
 * These two genie includes must be present in any C module 
 * They include important definitions and typedefs
 */
#include <genie_cmodule.h>
#include <genie_cmodule_ver.h>

void my_c_module(GenieWorkspace* pars_get, GenieWorkspace* pars_put)
{
/* ... User definitions here ... */
/* 
 * All C modules should do the following test - it ensures that a module
 * linked against a recent version of the genie library is
 * not executed by a program linked against an older version
 */
    if (!cmodule_version_ok(CMODULE_MAJOR_VERSION, CMODULE_MINOR_VERSION))
    {
	cmodule_print("Library version mismatch for MY_C_MODULE",
			CMODULE_PRINT_ERROR);
	return;
    }
/* All user code goes here, generally the format will be to access some
 * data using the helper functions, process it via new or exising user
 * written functions, and finally return any modified parameters using the
 * helper functions
 */
}

Helper Functions

There are three sorts of helper functions provided by the C module interface. They are all listed below

Commands to obtain data from Open GENIE Description
cmodule_get_double Reads a double precision real from the PARS workspace
cmodule_get_int Reads an integer from the PARS workspace
cmodule_get_real Reads a real from the PARS workspace
cmodule_get_string Reads a string from the PARS workspace
cmodule_get_double_array Reads a double precision real array from the PARS workspace
cmodule_get_int_array Reads an integer array from the PARS workspace
cmodule_get_real_array Reads a real array from the PARS workspace
cmodule_get_string_array Reads a string array from the PARS workspace
Commands to return data to Open GENIE Description
cmodule_put_double Replaces a double precision real in the PARS workspace
cmodule_put_int Replaces an integer in the PARS workspace
cmodule_put_real Replaces a real in the PARS workspace
cmodule_put_string Replaces a string in the PARS workspace
cmodule_put_double_array Replaces an double precision real array in the PARS workspace
cmodule_put_int_array Replaces an integer array in the PARS workspace
cmodule_put_real_array Replaces an real array in the PARS workspace
cmodule_put_string_array Replaces an string array in the PARS workspace
cmodule_put_nd_real_array Replaces an n-dimensional real array in the PARS workspace
Commands to communicate with the user Description
cmodule_print Prints to the terminal

HELPER FUNCTION REFERENCE

Commands to Obtain Data from Open Genie

All commands to read data from Genie into a user program start with the "cmodule_get_" prefix, and have their first two parameters in common. The first parameter, called "pars_get", is an pointer to the workspace passed by Genie. The second parameter, "name", is a character variable which is the workspace field name assigned to the variable in GCL when the parameters workspace was created. For arrays, a "len" variable must also be passed; on input this should be set to the maximum allowed number of points, and on output it will be set to the number actually read. If too many points are passed out from GCL, "len" will still indicate how many were passed, but the number actually read will be the length of the array which was passed as input; thus testing "len" on return provides a check for array overflow.

Because C is able to cope with memory allocation, it is possible for the cmodule_get functions to allocate memory if required. You will of course be responsible for calling free() afterwards if this is the case! To get the functions to allocate memory pass a pointer variable containing NULL as the destination for the data and sufficient memory to hold the requested item will be allocated (actual length allocated returned in the "len" parameter where applicable). Note that functions returning strings will always allocate memory and hence you are responsible for calling free().

There is no need for cmodule_get_nd_real_array to correspond to the cmodule_put_nd_real_array function because an array can always be converted to 1-D using the Redim() command from GCL before passing it out.

The function prototypes are given below:

void cmodule_get_double (GenieWorkspace* pars_get, const char* name, fort_double* val);
void cmodule_get_int    (GenieWorkspace* pars_get, const char* name, fort_int* val);
void cmodule_get_real   (GenieWorkspace* pars_get, const char* name, fort_real* val);
void cmodule_get_string (GenieWorkspace* pars_get, const char* name, char** val);
void cmodule_get_double_array (GenieWorkspace* pars_get, const char* name, fort_double** val, fort_int* len);
void cmodule_get_int_array    (GenieWorkspace* pars_get, const char* name, fort_int** val, fort_int* len);
void cmodule_get_real_array   (GenieWorkspace* pars_get, const char* name, fort_real** val, fort_int* len);
void cmodule_get_string_array (GenieWorkspace* pars_put, const char* name, const char** val[], fort_int* len);

Commands to return Data to Open Genie

These commands return data to Open GENIE by replacing the fields in the "pars_put" workspace, otherwise they are very similar to the corresponding "cmodule_get_" commands. The "name" parameter will be the field name in the result workspace returned to GCL, and the "len" parameter for the array routines will indicate how many data points to send.

The function prototypes are given below:

void cmodule_put_double (GenieWorkspace* pars_put, const char* name, fort_double val);
void cmodule_put_int    (GenieWorkspace* pars_put, const char* name, fort_int val);
void cmodule_put_real   (GenieWorkspace* pars_put, const char* name, fort_real val);
void cmodule_put_string (GenieWorkspace* pars_put, const char* name, const char* val);
void cmodule_put_double_array (GenieWorkspace* pars_put, const char* name, const fort_double* val, fort_int len);
void cmodule_put_int_array    (GenieWorkspace* pars_put, const char* name, const fort_int* val, fort_int len);
void cmodule_put_real_array   (GenieWorkspace* pars_put, const char* name, const fort_real* val,fort_int len);
void cmodule_put_string_array (GenieWorkspace* pars_put, const char* name, const char* val[], fort_int len);
fort_int cmodule_put_nd_real_array (GenieWorkspace* pars_put, const char* name, const fort_real* val,
				    const fort_int* dims_array, fort_int ndims);

Commands to Communiate With The User

These provide a means for a C program to send messages to the user through the standard Open GENIE messaging system. This means, for example that informational messages can be switched off using the Toggle/Info command as with ordinary Open GENIE informational messages. Using these routines ensures that the timing of output messages will be properly synchronized with Open GENIE.

The C output helper functionality is combined into one function "cmodule_print". The "cmodule_print" function takes one of three constants, CMODULE_PRINT_NORMAL, CMODULE_PRINT_INFORMATION or CMODULE_PRINT_ERROR as the "option" parameter to select the corresponding Open GENIE I/O stream to print to.

The subroutine names and parameter types and names are given below:

void cmodule_print (const char* s, fort_int option);

Open GENIE Data Access Interface (new get)

This document describes a set of routines designed to create, populate, read and modify ISIS format and NeXus format data files. The aim of these routines is to provide one simple way to read and write all styles of data files. The routines are based on the routines used in Open GENIE and will therefore cope with automatic detection of file input types for all Open GENIE supported file formats.

Basic Principles

Any data interface design is a compromise between flexibility and complexity. The interface below is intended to be simple to use from C, C++ or FORTRAN programs but does make use of some of the most advanced parts of the Open GENIE data access subsystem to do this. As a result, it is worth being aware of how data is actually read or written by the interface.

File conversions

When reading a file on different machine types, there are inherently several different conversion problems which may arise. Some are due to differences in the structure of the data files, others may be due to differences in numerical precision or binary format. As a result, the access routines work in two stages.

For reading:

  1. Access and convert the desired data block from the file.
  2. Select the data from the block to read into the program.

For writing:

  1. Construct a data block to be written to the file.
  2. Write and convert the data to the file.

In both cases, this is a very simple operation if the structure of the data file is simple. On the other hand, if the structure of the data file is complex, the data access routines provide a unique and powerful data structure parsing mechanism which allows complex structures to be easily snipped up into manageable (if not byte sized!) pieces.

Handles

A "Handle" is the name given to the data block which has been converted from the file but has not yet been loaded into your program. It is simply a user chosen name to identify the block of data in transition between stages (1) and (2) above, effectively the data enters a holding area where you can then make detailed requests of it. For example, if we read one experimental parameter from an ISIS raw file, "NSP1", the fragment of the program using handles would look something like the one below.

        ...
* Access the data item by name and put in the transfer area
    CALL GX_get('MY_HANDLE', 'NSP1', 0)

* Read the value into the INTEGER variable "my_nsp"
    CALL GX_transfer('MY_HANDLE', '-->', 'INTEGER', my_nsp, 0, 0)
        ...

The handle name links the request to get the data with the subsequent request to load the data into a program variable.

A handle can be re-used as often as required or several different handles may be used to allow to store data blocks before reading them out into the code. A handle expression can also be used to select a small amount of data to read into the program from a larger data block.

Let's assume now that we want to read the first "MAXLEN" elements of the detector to spectrum mapping table and write it out to a new file. We know the name of the data item in the ISIS raw file and can access it as below.

* Somewhere to put the data we are about to read
    INTEGER*4 my_tab(42)
        ...
* Get the whole data block to the handle.
    CALL GX_get('MY_HANDLE', 'SPEC', 0)

* Select part of the data to read out from the handle.
    CALL GX_transfer('MY_HANDLE[1:42]', '-->', 'INTEGER', my_tab, 42, 1)
        ...

This way we can slice the first 42 elements out of the array (assuming it has at least 42 elements). This example could have just specified a single array element if only one value was required. In some file formats, the block may contain named fields or attributes. These can be accessed using the syntax for accessing workspaces, for example "My_Handle.Two_Theta".

Data access routines descriptions

Before reading or writing data, a session must be initialised using GX_activate_session(), at the end of data access GX_deactivate_session() session should also be called. These routines control the allocation and deallocation of memory as well as setting the default output file format.

The interface makes the general assumption that a user may well want to have one file open for writing at the same time as having a different file open for reading. This is achieved by having both GX_select_source() and GX_select_destination routines(), either or both of these calls may be used in a data access session. The GX_directory() function allows the calling program to find out what data items are available for a program to read in the specified file.

The following is a generic description of the Application Programming Interface (API) and is divided into sections grouping similar functions. Language specific routine calls are given in the reference documentation for each rouitine.

Session Control

Function Return value Description
GX_activate_session(in default_format) status code Initialises the interface selecting a default data format for output.
GX_deactivate_session() status code Deactivates the interface, frees storage

File operations

Function Return value Description
GX_select_source(in filename) status code Selects a data source for subsequent operations
GX_select_destination(in filename, in file_format) status code Selects a data destination for subsequent operations
GX_directory(in filename, [out dir_listing]) status code Returns a directory of available fields in any data file.

Data reading/writing

Function Return value Description
GX_get(in handle, in tag, in object-id) status code Associates a handle with the referenced data file object.
GX_put(in handle, in tag, in object-id, in flag) status code Writes data associated with a handle into a data file.

Handle manipulation

Function Return value Description
GX_assign_handle(in handle_lval, in handle_rval) status code Assign one handle to the whole or part of another handle.
GX_release_handle(in handle) status code Deactivate the handle and release and storage.
GX_transfer(in handle, in direction, in type, inout data, in length, in dims) status code Transfer data to or from variables in the program.

 

NOTES:

  1. References to an object in the data file can be made either by a string tag or an object-id (number) for the data file. For a GX_get, if both are specified, the tag must correspond to the tag on the object selected by the object-id. Objects may also be accessed by block number if the object-id is negative (e.g. -1 for block 1).
  2. For a GX_put, the object at the object-id specified may be overwritten with the new tag and associated handle value. If the "flag" parameter to GX_put is set to "OVERWRITE", when the object-id corresponds with that already in an output file, the object and tag will be overwritten.
  3. Handles are always strings and are marked as "in" parameters. It is worth pointing out that although the handle is effectively a constant reference, what it points to acts like a variable.