MAP Examples

The CGNS.MAP provides the user with the two functions load and save, these are direct mapping of the CHLone functions. The actual actions these functions are performing heavily depends on the arguments you pass to these functions, the load takes a CGNS/HDF5 tree and returns at least a CGNS/Python tree, while the save takes a CGNS/Python tree and writes (modifies) a CGNS/HDF5 tree. Rather than listing all options, we suggest your try to figure out how to use these functions reading the examples.

All the examples are assuming the following imports:

import CGNS.MAP
import CGNS.PAT.cgnsutils    as CGU
import CGNS.PAT.cgnskeywords as CGK
import CGNS.PAT.cgnslib      as CGL

import numpy as NPY

Complete load of a CGNS/HDF5 file

The translation of a CGNS/HDF5 file into a CGNS/Python tree is performed with the simple line:

(tree,links,paths)=CGNS.MAP.load("mesh.cgns")

If the testfile.cgns has no link the links and paths are empty lists and tree contains the CGNS/Python tree. The file name extension is not required, if the file doesn’t exist or if it is not a CGNS/HDF5 file exceptions are raised:

>>> T=CGNS.MAP.load('a.hdf')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "pyCHLone.pyx", line 137, in CHLone.load (pyCHLone.c:2130)
CHLone.CHLoneException: (900, 'No such file [a.hdf]')

>>> T=CGNS.MAP.load('cgnscheck.pdf')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "pyCHLone.pyx", line 151, in CHLone.load (pyCHLone.c:2398)
CHLone.CHLoneException: (101, 'Target is not an HDF5 file [cgnscheck.pdf]')

You can see here the exception are coming from CHLone interface, but as CGNS.MAP wraps CHLone you can also catch them as a CGNS.MAP error:

>>> try:
...   T=CGNS.MAP.load('cgnscheck.pdf')
... except CGNS.MAP.error, e:
...   print e
...
(101, 'Target is not an HDF5 file [cgnscheck.pdf]')

And if you write your own function, this leads to:

def myload(filename):
  try:
    T=CGNS.MAP.load('cgnscheck.pdf')[0]
  except CGNS.MAP.error, e:
    T=None
  return T

In this latter example, we are forgetting links and paths.

Complete save of a CGNS/Python tree

Now you have a CGNS/Python tree of your own, use the save to write the contents in a CGNS/HDF5 file:

CGNS.MAP.save("solution.hdf", tree)

Again, the file extension is not significant. You can save your file with the name you want as far as the file system is happy with it:

CGNS.MAP.save('../solution.cgns',T)
CGNS.MAP.save('RESULT',T)
CGNS.MAP.save('solution.doc',T)

Of course, all these files are HDF5 files, and you may have problem with tools is you play with file names. Then we strongly suggest you stay with usual file extensions such as cgns or hdf.

If the file exist or if your file system doesn’t want you to write for any reason, you have an exception.

>>> T=CGNS.MAP.load('r.hdf')[0]
>>> CGNS.MAP.save('r.hdf',T)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "pyCHLone.pyx", line 159, in CHLone.save (pyCHLone.c:2718)
CHLone.CHLoneException: (901, 'File already exists [r.hdf]')

>>> CGNS.MAP.save('tmp/solution.cgns',T)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "pyCHLone.pyx", line 169, in CHLone.save (pyCHLone.c:2918)
CHLone.CHLoneException: (104, 'Cannot create new file [tmp/solution.cgns]')

Partial load of a CGNS/HDF file

One of the big advantage of CGNS is to provide a complete and consistent tree in data. Then you often want to parse it to get some information about its structure or about a particuliar data. You do not want to load all cooridnates and solution arrays, you just want the targeted information. The CGNS.MAP load allows you to filter your target in the CGNS/HDF5 file.

In this first example we load only the zone layout of the tree. You want to distribute your zones on a cluster processors and you need to now the number of zones and their sizes:

T=CGNS.MAP.load('z.hdf',depth=3)

The depth argument tells CGNS.MAP to stop loading at level 3, which is the Zone_t level; The first level is the root of the tree (CGNSTree_t) then you have the base level (CGNSBase_t) and the zones.

Note you have all nodes at level 3, not only the zones. You need to select the zones:

zlist=CGU.getAllNodesByTypeSet(T[0],['Zone_t'])

In the case you have several CGNSBase_t you may want to filter a specific base, use the subtree to tell to load to select only the nodes with the given prefix, all together this leads to the following lines.

>>> T=CGNS.MAP.load('z.hdf',subtree='/Base_1',depth=3)
>>> print CGU.getAllPaths(T[0])
['/Base_1', '/Base_1/About', '/Base_1/Zone_0001', '/Base_1/Zone_0002']
>>> zlist=CGU.getAllNodesByTypeSet(T[0],['Zone_t'])

Load of a CGNS/HDF file layout

The previous example actually reads all the data of the CGNS/HDF5 file, its stops parsing the tree but every node is retrieved with its associated array. Now we would like to find out the coordinates names, the node names of the children of GridCoordinates_t, but we do not want to load a probably large amount of data. We use the madata threshold parameter, all data above this size is returned as a no-data node:

flags=CGNS.MAP.S2P_DEFAULT|CGNS.MAP.S2P_NODATA
T=CGNS.MAP.load('mesh.hdf',maxdata=20,flags=flags)

The CGNS.MAP.S2P_NODATA is required, without it the maxdata is ignored. Here all the CGNS/HDF5 tree is retrieved but nodes with a data array with more than 20 entries is set as an MT (empty) node. We can still parse the node names:

>>> g=CGU.getNodeByPath(T[0],'/Mesh/Zone-004/GridCoordinates')
['GridCoordinates', None, [['CoordinateX', None, [], 'DataArray_t'],
['CoordinateY', None, [], 'DataArray_t'],
['CoordinateZ', None, [], 'DataArray_t']], 'GridCoordinates_t']

You should not save this tree, because you would overwrite the coordinates nodes with empty arrays.

When you use the S2P_NODATA flag your result tree may contain MT nodes that actually have no data in the CGNS/HDF5 file or node arrays that have been ignored because of the maxdata threshold. The third entry in the load return tuple is the paths, it contains the paths of the nodes that were ignored:

>>> flags=CGNS.MAP.S2P_DEFAULT|CGNS.MAP.S2P_NODATA
>>> T=CGNS.MAP.load('mesh.hdf',maxdata=20,flags=flags)
>>> print T[2]
[('/Mesh/Zone-004/GridCoordinates/CoordinateX',1),
 ('/Mesh/Zone-004/GridCoordinates/CoordinateY',1)
 ('/Mesh/Zone-004/GridCoordinates/CoordinateZ',1)]

The 1 associated with the path is the integer value corresponding to the S2P_NODATA flag, it is useless at this time.

Update a nodata node from a CGNS/HDF file

Now next step is to load the data for a node. Your first step was to parse the tree layout an discover the nodes you are intersted in. You have a list with node paths taken from your previously loaded tree. Some nodes have data, because their size was below the maxdata threshold, some other have None and they appear in the paths as the third returned value of the load function.

First case, we want to load data from a None valued node. We use the subtree named argument of the load. It creates all the nodes of the subtree starting from the path you give:

flags=CGNS.MAP.S2P_DEFAULT|CGNS.MAP.S2P_NODATA
(t1,l1,p1)=CGNS.MAP.load('mesh.hdf',maxdata=20,flags=flags)

# this path is in p1
pth='/Mesh/Zone-004/GridCoordinates/CoordinateX'
(t2,l2,p2)=CGNS.MAP.load('mesh.hdf',subtree=pth)

# replace the new node in the original tree
nodatanode=CGU.getNodeByPath(t1,pth)
datanode  =CGU.getNodeByPath(t2,pth)
nodatanode[1]=datanode[1]

The numpy array is replaced in the list, python insures this is not a copy and also takes care of the object references. So you can remore t2 now. In that case a new numpy array has been created. You can give an actual subtree instead of a leaf, the result is the same, all children nodes are recursively loaded:

flags=CGNS.MAP.S2P_DEFAULT|CGNS.MAP.S2P_NODATA
(t1,l1,p1)=CGNS.MAP.load('mesh.hdf'),maxdata=20,flags=CGNS.MAP.S2P_NODATA)

pth='/Mesh/Zone-004/GridCoordinates'
(t2,l2,p2)=CGNS.MAP.load('mesh.hdf',subtree=pth)

# replace the new node in the original tree
# here we change the children list instead of a single node value
nodatanode=CGU.getNodeByPath(t1,pth)
datanode  =CGU.getNodeByPath(t2,pth)
nodatanode[2]=datanode[2]

Sometimes you just want a node that already exists to be updated. The update argument of the load is a dictionnary of path/value entries:

(t1,l1,p1)=CGNS.MAP.load('mesh.hdf')
pth_g='/Mesh/Zone-004/GridCoordinates'
pth_x=pth_g+'/CoordinateX'
targetnode=CGU.getNodeByPath(t,pth_x)

upd={ pth_x:targetnode[1] }
(t2,l2,p2)=CGNS.MAP.load('mesh.hdf',update=upd,subtree=pth_g)

# both t1 and t2 are sharing the /Mesh/Zone-004/GridCoordinates/CoordinateX
# array, as the second load actullay updated the numpy array passed as arg
# in the update

The previous example loads the X,Y and Z arrays in the t2 tree. However, only Y and Z had a new memory allocation. If you do not set the subtree arg the t2 tree will contain a completely new CGNS/Python, only the X coordinate array is shared with t1.

Another way to load the coordinates X,Y,Z having the X shared and Y and Z new numpy array is to set None in the update dictionnary and to add the S2P_UPDATEONLY flag. Only the paths into update would be parsed in the CGNS/HDF5 file, if the update is None a new numpy array is allocated, otherwise the existing numpy array found in the dictionnary is updated:

(t1,l1,p1)=CGNS.MAP.load('mesh.hdf')
pth_g='/Mesh/Zone-004/GridCoordinates'
pth_x=pth_g+'/CoordinateX'
pth_y=pth_g+'/CoordinateY'
pth_z=pth_g+'/CoordinateZ'
targetnode=CGU.getNodeByPath(t,pth_x)

upd={ pth_x:targetnode[1], pth_y:None, pth_z:None }

flags=CGNS.MAP.S2P_DEFAULT|CGNS.MAP.S2P_UPDATEONLY
(t2,l2,p2)=CGNS.MAP.load('mesh.hdf',update=upd,flags=flags)

We suggest you use this last method instead of calling as many load with as many subtree you want to update. The CGNS/HDF5 file would be opened only once, you increase parsing performance, you limit the file system access and you reduce the critical section for other applications trying to read/write simultaneously the same file.

Update a file

to add: example with recursive tree save per link

Merge all or some linked-to files

to add: example with recursive tree save per link

Some usual issues

to do: add here users' usual mistakes