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.
Load a tree without links¶
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.
Save with links¶
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.
Save with new links¶
to add: example with recursive tree save per link
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