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
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.
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]')
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'])
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.
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.
If you want to parse a CGNS/HDF5 file and avoid to load any linked-file found during the parse, remove the S2P_FOLLOWLINKS from the flags:
flags=CGNS.MAP.S2P_DEFAULT & ~CGNS.MAP.S2P_FOLLOWLINKS
(t,l,p)=CGNS.MAP.load('mesh.hdf',flags=flags)
The l list is empty.
When you load a file all the linked-to files areresolved to produce a full CGNS/Python tree with actual node data. The only way to find out from which file comes a specific node you have to check the link list, returned as the second value of the load function. The way back, that is when you want to save this previously loaded file, you have to give back this link list:
(t,l,p)=CGNS.MAP.load('mesh.hdf')
CGNS.MAP.save('mesh.hdf',t,links=l)
If you miss this links argument, the save will store all the nodes ot t in the file. This is a merge like describe here.
The save will only write the first top file, all other sub parts of the tree having an ancestor in the links list would be ignored. If you want the save modifications of nodes coming from a linked-to file, you have to force the S2P_PROPAGATE flag:
(t,l,p)=CGNS.MAP.load('mesh.hdf')
flags=CGNS.MAP.S2P_DEFAULT|CGNS.MAP.S2P_PROPAGATE
CGNS.MAP.save('mesh.hdf',t,links=l,flags=flags)
In that latter case, the linked-to file are set in the update mode as described here.
to add: example with recursive tree save per link
to add: example with recursive tree save per link
to add: example with recursive tree save per link
to do: add here users’ usual mistakes