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 python CHLone module.
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.
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.
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 error 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 integerR4
,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
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.
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.
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.
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.
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.
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.
/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.
/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.
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.
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