.. ====================================================================== CHLone - CGNS HDF5 LIBRARY only node edition See license.txt in the root directory of this source release ---------------------------------------------------------------------- $Release: $ ====================================================================== Interfaces ========== *CHLone* is a library, there is no tool, no application. Three levels of interfaces are available in CHLone, **C1** is the higher level, it is the *CGNS MLL Clone* same API as CGNS/MLL with a different implementation **F2** the *Fortran Facilities*, an interface designed for Fortran users, **L3** the *Low Level Library*, manages HDF5 nodes. *CHLone* also includes its own Python interface, it is not an API but rather an translator from/to `CGNS/Python`. Two functions, `load` and `save`, provide the user with a simple but versatile entry door for CGNS data manipulation in Python. See :ref:`python CHLone module <pythonCHLone>`. .. image:: ./images/libstack.png :width: 16cm :align: center The schema above shows the relationships between the three `CHLone` interfaces and the existing application . The **L3** is the node management, all other interfaces are built on top of it. The **SIDS-to-Python** interface offers a Python module so-called ``CHLone``, which can load/save *CGNS/Python* complete trees as lists of lists, containing plain strings or `numpy` arrays. The **F2** is for Fortran users but actually has a C interface as well, nodes are selected and manipulated by means of their paths. The **C1** is built to provide the user with exactely the same interface as *CGNS/MLL*, thus allowing the user to change its *CGNS* implementation with a simple relink of his programs. .. _l3interface: L3 interface ++++++++++++ The L3 interface is the *Low Level Library*. It is not intend to be used by developpers, the target CHLone interfaces are F2 and SIDS-to-Python, the C1 interface is of lower priority. The CGNS data structure is based on a tree. Each node can contain a value and may have a set of children. The basic data structure of L3 is this node, in the L3 interface you can find function to handle the data of this node and to parse its children. Each L3 function handles only one L3 node. The access to the target file is performed using a context, it is a required argument to all L3 functions. The L3 node itself is a C structure used to gather all the required arguments for an L3 function call. Each call actually read/writes data in/from `HDF5`, there is no data buffering. The actual data pointer, for example for a *coordinates array* can be a new pointer coming from the L3 layer or an existing pointer passed as an argument to the L3 layer. This actual pointer is the one used by the `HDF5` calls, then there is no memory copy or buffering, which is helpfull when you are dealing with very large amount of data. Open and close a context ------------------------ The `L3_openFile` function is the first you call. It returns a context you should give as argument to the forthcoming L3 calls. You close or free a context using ``L3_close`` function. .. highlight:: c .. code-block:: c L3_Cursor_t *cgnsfile; cgnsfile=L3_openFile("M6wing.cgns",L3E_OPEN_NEW,L3F_OPEN_DEFAULT); L3_close(cgnsfile); The second argument indicates if the open has to create a new file or has to open an previously created file (or a so-called 'old' file). You can open an existing file using the ``L3E_OPEN_OLD`` flag instead of ``L3E_OPEN_NEW`` . If you try to access an 'old' file using the new flag, or to create a 'new' file without the new flag, an error occurs. The open function returns a ``NULL`` context if an error occurs. Please, refer to section about :ref:`error <l3errors>` management to know how to detect and get more about errors. All subsequent L3 calls using a ``NULL`` context with return without complaining, only the first error found is kept. A simple way to test if an error occurs is to use the ``L3M_ECHECK`` macro, which returns 1 if there is NO error. You can also print it, if you use to the standard output, with the `printError` function:: L3_printError(cgnsfile); The context can be created using ``L3_openHID``. The function uses an existing ``hid_t`` as root id or the CGNS tree, then you don't need to provide a filename to read or write a *CGNS/HDF5* tree, you just have to find an HDF5 object acting as a root of your tree. The user has to check if the ``hid_t`` can be used for such a purpose. For example, you can open an in-memory HDF5 or even have your own HDF5 driver:: L3_Context_t *cgnstree; cgnstree=L3_openHID(rootid); The ``L3_close`` has no action on such a rootid. All the L3 functions will accept the context without restriction on the way you get the actual root id. Create and delete a node ------------------------ Once you have a context, you can manage the nodes. We mean the *L3 node*, not the actual HDF5 node. In practice, these nodes very close but we need an intermediate structure in L3 to hide some implemention details and to make the user believe its node is a CGNS low level node. An actual node is identified with its ID, of type ``hid_t``. This ID is the HDF5 ID, you can also use it for any HDF5 API call. The root id of the current context you have is stored in the root_id member of your context. The way to create, read or update a node is to use a ``L3_Node_t`` descriptor. The contents of a node is always the same, whichever type of CGNS SIDS node it is. The table below shows this contents. +---------------+-------------------------+-------------+ | **Attribute** | **Type** | **Remark** | +===============+=========================+=============+ | ``name`` | *string* | \(1) | +---------------+-------------------------+-------------+ | ``label`` | *string* | \(2) | +---------------+-------------------------+-------------+ | ``dimensions``| *array of integers* | | +---------------+-------------------------+-------------+ | ``datatype`` | *string* | \(3) | +---------------+-------------------------+-------------+ | ``data`` | *depends on datatype* | \(4) | +---------------+-------------------------+-------------+ | ``flags`` | *integer* | \(5) | +---------------+-------------------------+-------------+ **Remarks:** **1** *Name of the node* The name can have any ASCII char except the ``[/]`` <slash> and the ``[>]`` <greater than>. The first is used to separate node names in a path, the second is internally used by *CGNS/HDF5* for the symbolic links. The ``[ ]`` <space> and ``[.]`` <dot> are allowed except as the first character of the name. **2** *CGNS/SIDS type of the node* Usually it is a capitalized string with the ``[_t]`` postfix. **3** *CGNS/SIDS type of the data in the node* The CGNS/SIDS datatypes are: * ``C1`` a string (that is an array of characters, the array should not contain a trailing ``[\0]``). * ``I4``, ``I8`` integer and long integer * ``R4``, ``R8`` float and double float. **4** *Actual data* A contiguous array of data using the so-called *Fortran* indexing **5** *A bitfields as an integer* The value ``0x0001`` indicates that the FORTRAN flag is set. In that case, the node has been written using the MLL library. See section The ``L3_Node_t`` structure has other attributes, see section implementation remarks about these. All the CGNS/SIDS types and some names of its annex A have constants. The names are postfixed with ``[_n]`` and the types with ``[_ts]``. The next code snippet shows a call to ``L3_nodeSet``. It returns a new pointer to a ``L3_Node_t`` structure with the arguments:: L3_Context_t cgnsfile; L3_Node_t node; int dims[2]={1,-1}; node=L3_nodeSet(cgnsfile,NULL, CGNSLibraryVersion_n,CGNSLibraryVersion_ts,dims,L3E_R4,data) The first argument is the context, the second is the ``L3_Node_t`` where to set the values. In that case, we pass ``NULL``, this means we have no ``L3_Node_t`` to set, we want a new one. In all cases, the returned pointer is the ``L3_Node_t`` that has been set. The arguments 3 and 4 are the name and the label of the node, here we set the ``CGNSLibraryVersion`` node. Then comes the dimensions of the data and its type, here we have a single one float. The data itself, the last argument, is the float. The dimensions are stored into an integer array, it is read until it reaches a -1 value, indicating that the rest of the data is not relevant. Now we can create an actual CGNS/HDF5 node, we use the ``L3_nodeCreate`` fonction:: hid_t cgnslibraryversion; cgnslibraryversion=L3_nodeCreate(cgnsfile,cgnsfile->root_id,node); The first argument is the context, the second one is the parent of the node to create, in that case we use the context to get the root ID of the current file. The returned value is the new CGNS/HDF5 node ID. This two steps creation is useful when you have a lot of similar nodes to create, or when you want to update. The example hereafter shows an incomplete example for the creation of some CGNS zones with the same dimensions. The ``NULL`` arguments passed to ``L3_nodeSet`` let the previous value of the node unchanged:: node=L3_nodeSet(cgnsfile,NULL,NULL, Zone_ts,dims,L3E_I4,data); L3_nodeCreate(cgnsfile,base,L3_nodeSet(db,node,"Zone-001",NULL,NULL,NULL,NULL)); L3_nodeCreate(cgnsfile,base,L3_nodeSet(db,node,"Zone-002",NULL,NULL,NULL,NULL)); L3_nodeCreate(cgnsfile,base,L3_nodeSet(db,node,"Zone-003",NULL,NULL,NULL,NULL)); The deletion of a node requires its parent ID and its name, no ``L3_Node_t``:: L3_nodeDelete(cgnsfile,base,"Zone-003"); The deletion is performed by the HDF5 library, all children nodes are recursively deleted. Find and modify a node ---------------------- The ``L3_Node_t`` has been filled with data to create a new node. You can also use this structure to update an existing node. First, you have to retrieve the information of an HD5 object and fill the ``L3_Node_t`` node with it:: L3_Context_t *cgnsfile; L3_Node_t *node; hid_t id; node=L3_nodeRetrieve(cgnsfile,id); The ``L3_Node_t`` node found contains its name, label, dimensions and data, these are all the data used for creation. You can display a node content on the standard output using the ``L3_nodePrint``. The output is limited to the name, the label and the data type:: L3_nodePrint(node); The node contains more thant these data. For example, the list of children Ids are also stored into the ``L3_Node_t``. You can loop on this list, you have to use the children attribute of the returned ``L3_Node_t``, it is an array of ``hid_t`` Ids, one per actual child. The list ends with a -1 value instead of a valid hid_t:: L3_Node_t *childnode; node=L3_nodeRetrieve(cgnsfile,id); while ((node->children != NULL) && (node->children[n] != -1)) { childnode=L3_nodeRetrieve(cgnsfile,rode->children[n])); n++; } The ``L3_Node_t`` you get using the ``L3_nodeRetrieve`` also has the ID you used to retrieve it. You have not this information in a ``L3_Node_t`` you created with ``L3_nodeSet``. You can change the values in this ``L3_Node_t`` in order to update the HDF5 object:: L3_nodeSet(cgnsfile,node,"New Name",NULL,NULL,NULL,NULL); L3_nodeUpdate(cgnsfile,node); The ID is not an argument of the function, it is in the ``L3_Node_t`` structure. The example above acts like a rename of the HDF5 object. A simple rename, not a move, in other words the parent HDF5 object is unchanged. If you intend to move your node, that is to change its father, you have to use the ``L3_nodeMove`` function:: L3_nodeMove(cgnsfile,parentid,newparentid,"name","newname"); The first argument is the context, the second is the current parent of the node to move. The third is the new parent of the node. The two last strings are the current name and the new name. You can also update a C variable with the contents of an existing ``L3_Node_t``. The ``L3_nodeGet`` function takes C variables as arguments, these are updated with the values found in the ``L3_Node_t`` structure:: int dims[L3_MAX_DIMS]; node=L3_nodeRetrieve(cgnsfile,id); L3_nodeGet(cgnsfile,node,NULL,NULL,dims,NULL,NULL); The ``L3_nodeFind`` function looks for a child node starting from a given HDF5 object. The returned value is a hid_t not a ``L3_Node_t``. The third argument of the find is a path defined as the classical string. The example hereafter shows the composition of L3 function calls, used to get an ``hid_t`` and then to fill the ``L3_Node_t``:: L3_nodeRetrieve(cgnsfile,L3_nodeFind(cgnsfile,id,"./Zone-001/ZoneBC")) Links ----- The CGNS link is more or less like the UNIX file system symbolic link. An HDF5 object refers to another object, that can be in the same ile or into another file. When you access to this object, it is up to HDF5 library to follow the link and let you believe that you are accessing the linked-to object instead of the current object. The policies related to linked-to files are detailled in You may want to detect if a node is a link, for example if you do not want to parse some files The creation of such an HDF5 object is performed with the ``L3_nodeLink`` function:: zoneid=L3_nodeFind(cgnsfile,cgnsfile->root_id,"/Base-02/Zone-001") L3_nodeLink(cgnsfile,zoneid,"GridCoordinates", "M6wing.cgns","/Base-01/Zone-001/Grid#01"); In this former example, the zone ID is found using ``L3_nodeFind``. A link is made from the current file to another file, the local name is ``GridCoordinates`` and the linked-to node is identified by a path. An important point to know is that you do not need to open the destination file. The destination file and the destination node may exist or not, the error will only occur when you try to access to the HDF5 object. If you want to be sure your linked-to file and node exist, you can set the ``L3F_FAILSONLINK`` flag. The ``L3F_FOLLOWLINKS`` is set by default, you have to set it to alse if you want to detect links before the HDF5 library automatically jumps to the linked-to object. Other functions --------------- You can get the HDF5 id of a object using its path and retrieve the path of an object giving its HDF5 id. The two functions are ``L3_path2Node`` and ``L3_node2Path``. The ``L3_isSameNode`` unction checks wether to ``hid_t`` are refering to the same HDF5 actual object. The two functions ``L3_typeAsStr`` and ``L3_typeAsEnum`` are used to translate the HDF5 data type into the L3 string or the L3 enumerate. Some convenience functions are used to initialize or set dimensions and data:: L3_initDims(dims,3,3,-1); data=L3_initData(dims,data,L3E_I4,9,3,5,8,2,4,0,0,0); data=L3_fillData(dims,data,L3E_I4,0); About flags ----------- The flags are an important topic of the L3 interface. The behavior of the interface can be changed just by setting or unsetting one flag. The flags are associated with the context, each L3 function looks at the context flags, so that you can change flags between two function calls. You can change the flags using the ``L3_setFlags`` and retrieve them with ``L3_getFlags``. A set of macros are useful, the L3M_HASFLAG, L3M_SETFLAG and L3M_UNSETFLAG. +-----------------------+-----------------------------------------------+---+ | **Flag** | **Usage** | | +=======================+===============================================+===+ | ``L3F_NONE`` | All flags set to False | x | +-----------------------+-----------------------------------------------+---+ | ``L3F_NULL`` | Unsignificant flags value | x | +-----------------------+-----------------------------------------------+---+ | ``L3F_WITHDATA`` | Load/Save DataArray_t | 1 | +-----------------------+-----------------------------------------------+---+ | ``L3F_WITHCHILDREN`` | Add children in the process, not recursive | 1 | +-----------------------+-----------------------------------------------+---+ | ``L3F_FAILSONLINK`` | Raises an error if the link is not reachable | 1 | +-----------------------+-----------------------------------------------+---+ | ``L3F_FOLLOWLINKS`` | Traverse the link and continue the process | 1 | +-----------------------+-----------------------------------------------+---+ | ``L3F_MERGELINKS`` | Reserved | 0 | +-----------------------+-----------------------------------------------+---+ | ``L3F_COMPRESS`` | Compress DataArray_t | 0 | +-----------------------+-----------------------------------------------+---+ | ``L3F_SKIPONERROR`` | First error forces skip of following L3 calls | 1 | +-----------------------+-----------------------------------------------+---+ | ``L3F_OWNDATA`` | Reserved | 0 | +-----------------------+-----------------------------------------------+---+ | ``L3F_NOCHILDMERGE`` | Reserved | 0 | +-----------------------+-----------------------------------------------+---+ | ``L3F_WITHLINKINFO`` | Reserved | 0 | +-----------------------+-----------------------------------------------+---+ | ``L3F_WITHMUTEX`` | Use the mutual exclusion locks | 1 | +-----------------------+-----------------------------------------------+---+ | ``L3F_FILLNANDATA`` | Fill created DataArray_t with NaN | 1 | +-----------------------+-----------------------------------------------+---+ | ``L3F_BULLETPROOF`` | Force checks on args and functions returns | 0 | +-----------------------+-----------------------------------------------+---+ | ``L3F_HASEXTFLAGS`` | Extension flags are present | 0 | +-----------------------+-----------------------------------------------+---+ | ``L3F_DEBUG`` | Verbose trace with internal data debug | 0 | +-----------------------+-----------------------------------------------+---+ | ``L3F_TRACE`` | End-user trace | 0 | +-----------------------+-----------------------------------------------+---+ A flag is an integer. The default flags are set using the L3F_DEFAULT value, which sets:: L3F_FOLLOWLINKS|L3F_WITHDATA|L3F_WITHMUTEX|L3F_WITHCHILDREN|L3F_SKIPONERROR .. _l3errors: Error management ---------------- CHLone defines its own set of error codes. The L3 interface, which is the closest to HDF5 calls, traps the HDF5 errors and translates them in CHLone codes. You can ask to CHLone to let HDF5 error stack appear if you set the TRACE flag. The error functions of the CHLone interface are:: void CHL_printError (L3_Cursor_t *ctxt); void CHL_setError (L3_Cursor_t *ctxt, int err, ...); int CHL_getError (L3_Cursor_t *ctxt); int CHL_setMessage (L3_Cursor_t* ctxt,char *msg); char *CHL_getMessage (L3_Cursor_t* ctxt); If you want a more detailled explanation of what an error code means, you can refer to the table in annex. Interface summary ----------------- The interface contents with all enumarates, macros, functions, error codes. .. toctree:: :maxdepth: 1 l3 .. _f2interface: F2 interface ++++++++++++ The F2 interface is a node-based interface with fortran oriented functions. The user application parses a CGNS/HDF5 tree by means of paths, it stores selected paths in stacks and access to nodes using both stack or paths. The node is the actual data the application wants, a node may contain an array and F2 provides node array manipulation too. The parsing of a tree is an extra processing added to usual applications. The application can performs F2 node selections using path-based functions. A set of paths can be extracted from a complete tree with respect to a pattern including specific tokens such as plain node name, plain node type or even regular expressions. See the section about path querying hereafter. .. note: The F2 fortran functions are a direct mapping of F2 C functions. Then the F2 API is available in Fortran as well as in C langage. +--------+-------------------------------------------------------------------+ | Entity | Purpose | +========+===================================================================+ | ID | A CGNS/HDF5 node as a unique identifier (the HDF5 `hid_t`). This | | | ID is a key to a node as well as the Path. | +--------+-------------------------------------------------------------------+ | Path | A string that identify a node in a CGNS/HDF5 tree. The Path is a | | | suite of `token`s separated by the `/` character. | +--------+-------------------------------------------------------------------+ | Token | A node name, this string may not be unique in all the CGNS/HDF5 | | | tree. | +--------+-------------------------------------------------------------------+ | Stack | An ordered list of `Path`s, used for application purpose. For | | | example you store the X,Y and Z coordinate nodes' paths in a | | | stack and you manipulate the stack as a whole instead of each | | | node. | +--------+-------------------------------------------------------------------+ The interface use is then to define or to find out paths, to get corresponding IDs and to call functions with these IDs as arguments. For example, you want to get dimensions for all structured zones in a base. .. code-block:: fortran F=1 S=1 path="/CGNSTree_t/Base#01/Zone_t/ZoneType_t" nzones=f2_stackFetchByString(F,S,path,"Structured") do n=1,nzones f2_nodeDimensions(F,f2_stackParentId(F,S,n)) enddo S=2 path="/CGNSTree_t/Base#01/Zone_t/GridCoordinates/^Coordinates[XYZ]$" ncoords=f2_stackFetch(F,S,path) f2_arrayAsContiguous(F,path,T) Context ------- A ``Context`` is more or less a CGNS/HDF5 file. It could be more in the case of links, but we approximate here as one context is one file. Most F2 functions do require a context as first argument, then the first function you have to call is the ``F2_open_f`` which opens a context related to the given filename. The returned value ``C`` is the index of the context in the F2 internal tables. .. code-block:: fortran integer C call F2_open_f("/tmp/file.cgns",1,-1,C) call F2_close_f(C) The remaining arguments are unused here, the first 1 is the flags and the -1 is the root id. You can have more than one context, and opening, using and closing a context is thread-safe. .. _pathpattern: Paths and Stacks ---------------- A path is a string used to identify a node in a CGNS tree. The path syntax is the same as the Unix file system syntax, and obviously the same as the HDF5 syntax. .. note:: The path we use in CHLone is different to the HDF5 node path as we have the root node, ``CGNSTree`` of type ``CGNSTree_t``. If you remove this first token of the path, you actually have the HDF5 path. A path is composed of tokens separated by the `/` char. A token can be a plain name token, a plain type token or a regular expression token. A *plain name token* is a token used to select a node name matches exactly the characters. A *plain type token* is a token used to select a node *SIDS type* instead of its name. For example `GridCoordinates_t` can be used to match all nodes having this string as *SIDS type*. The token should have ``_t`` as the two last characters to make CHLone detect it is a SIDS type token. A *regular expression token* is a used to select node names given a regular expression. The first character should be a ``^`` and the last should be ``$``, this is the way CHLone detects a regular expression token to the other token types. The examples hereafter show usual patterns used for a CGNS tree parsing. Some paths may represent a set of paths instead of a single one, we use the term `matches` to say a node actually belong to the set defined by the path. A plain path, all tokens are plain tokens. Then it should lead to a single node, here it matchesThe X coordinates of the zone `D.001` in the base `Flap`. .. code-block:: shell /CGNSTree/Flap/D.001/GridCoordinates/CoordinatesX A path with both a type selection and a regular expression. The third token ``Zone_t`` defines the type of the node rather than its name. Then all nodes with the SIDS type ``Zone_t`` would match. The last token is a regular expression, enclosed by ``^`` and ``$``, that defines a plain string ``Coordinates`` and a set of possible characters ``X``,``Y`` or ``Z``. All X,Y and Z coordinates would match this token. The result of the path search in a CGNS tree is all the coordinates of all the zones of the base `Flap`. .. code-block:: shell /CGNSTree/Flap/Zone_t/GridCoordinates/^Coordinates[XYZ]$ When the result contains more then one path it needs to be stored in a list. The F2 interface defines **stacks** for that purpose. All the returning path, even if there is only one, is stored in the stack. You can use the stack for operations on a set of nodes instead of a single one. A stack is identified by an integer index. You can have many stacks, you have to select the stack you operate on by specifying its index. In the exemple hereafter, ``C`` is the context and ``S`` is the stack. .. code-block:: fortran integer C,S S=1 path="/CGNSTree/Flap/Zone_t/GridCoordinates/^Coordinates[XYZ]$" call F2_open_f("/tmp/file.cgns",1,-1,C) call F2_stackClear(C,S) call F2_stackFetch(C,S,path) call F2_close_f(C) You have to retrieve the actual node from its path if you want to operate on its data. Errors ------ Interface summary ----------------- The interface contents with all enumarates, macros, functions, error codes. .. toctree:: :maxdepth: 1 f2 .. _c1interface: C1 interface ++++++++++++ The C1 interface is the exact copy (or clone) of the *CGNS/MLL* interface. The file ``cgnslib.h`` provided by CHLone is a copy of the original file with an add include to the ``CHLone.h`` header. The *CHLone* interface defines the ``CHLONE_IMPLEMENTATION``, so that you can detect at preprocessing time wether you are including the original or the clone. CHLone vs CGNS/MLL ------------------ The *CGNS/MLL* stores the actual node ids and returns its own indexes. CHLone returns the actual node id. The CGNS/MLL buffers the data, it creates its own data structure as a copy of the CGNS tree you have on disk. Check operations are performed on the buffered data. CHLone calls actually read/write the data on/to disk. .. warning:: The *C1* interface is not ready to use today. Compatibility ------------- The interface is 100% compatible. This means you can recompile your *CGNS/MLL* application without modification and obtain the same results. The CHLone lib has, however, a different implementation. If your application uses some *CGNS/MLL* tricks based on the legacy implementation, the change to CHLone may result in changes in your results. In particular if you bet on indexes returnd by *CGNS/MLL*. Interface summary ----------------- The C1 interface does not cover 100% of the *CGNS/MLL* interface. The reason it is so is that the *CGNS/Python* interface is far more used by CHLone users. Then the effort is, today, focused on the CHLone python module. The implemented functions, as of v0.47 of CHLone, are: - cg_open - cg_close - cg_nbases - cg_base_read - cg_base_write - cg_zone_write - cg_zone_type - cg_zone_read - cg_nzones - cg_ncoords - cg_coord_info - cg_coord_read - cg_coord_write .. toctree:: :maxdepth: 1 c1