Converter.Mpi: distributed pyTree services
Preamble
This module provides services to deal with distributed pyTrees.
A distributed pyTree is a tree where zones are distributed over different processes.
The number of a process is called the rank.
Three new concepts are introduced in addition to standard pyTrees: the skeleton tree, the loaded skeleton tree and the partial tree.
A skeleton tree (S) is a full pyTree where numpy arrays of DataArray_t type nodes are replaced by None.
A loaded skeleton tree (LS) is a skeleton tree for which zones attributed to the current rank are fully loaded.
A partial tree (P) is a pyTree with only zones attributed to the current rank fully loaded and no skeleton zones. It can be viewed as a loaded skeleton tree with skeleton zones suppressed.
Generally, Cassiopee functions will operate seamlessly on a partial tree, if the function doesn’t requires data exchanges. If the function requires exchanges, then a specific version exists in the Module.Mpi module. For instance, for Converter, only center2Node requires exchanges and is redefined in Converter.Mpi.
To use the module:
import Converter.Mpi as Cmpi
To run a python script in parallel with two processes:
mpirun -np 2 python script.py
List of functions
– Input/output
Read a file and return a skeleton tree. |
|
Read a file and return a full tree or partial tree. |
|
Read some zones in a skeleton tree (by rank or name). |
|
Write zones in parallel. |
|
Write a skeleton or partial tree. |
– Conversion
Convert a tree to a partial tree. |
|
Convert a tree to a skeleton tree. |
|
Return a bbox tree of t. |
– Communication Graphs
Return the proc where zone is affected to. |
|
Set the proc number to a zone or a set of zones. |
|
Return the dictionary proc['zoneName']. |
|
Return the communication graph for different block relation types. |
– Exchanges
Set MPI communicator to com. |
|
Add zones specified in graph on current proc. |
|
Remove zones added by addXZones. |
|
Gather a distributed tree on all processors. |
– Actions
Write a trace of cpu and memory in a file or to stdout for current node. |
|
Contents
Input/output
- Converter.Mpi.convertFile2SkeletonTree(fileName, format=None, maxFloatSize=5, maxDepth=-1, links=None)
Read a skeleton tree (S) from file (adf or hdf file format only). The loaded in memory skeleton tree is identical on all processors.
If float data array size of DataArray_t type nodes is lower than maxFloatSize then the array is loaded. Otherwise it is set to None. If maxDepth is specified, load is limited to maxDepth levels.
- Parameters:
fileName (string) – file name to read from
format (string) – bin_cgns, bin_adf, bin_hdf (optional)
maxFloatSize (int) – the maxSize of float array to load
maxDepth (int) – max depth of load
links (list of list of 4 strings) – if not None, return a list of links in file
- Returns:
Skeleton tree
- Return type:
pyTree node
Example of use:
# - convertFile2SkeletonTree (pyTree) - import Converter.PyTree as C import Generator.PyTree as G import Converter.Mpi as Cmpi import Converter.Internal as Internal if Cmpi.rank == 0: a = G.cart((0.,0.,0.),(0.1,0.1,0.1),(11,11,11)) t = C.newPyTree(['Base', a]) C.convertPyTree2File(t, 'in.cgns') Cmpi.barrier() t1 = Cmpi.convertFile2SkeletonTree('in.cgns'); Internal.printTree(t1) #>> ['CGNSTree',None,[2 sons],'CGNSTree_t'] #>> |_['CGNSLibraryVersion',array([3.0999999046325684],dtype='float64'),[0 son],'CGNSLibraryVersion_t'] #>> |_['Base',array(shape=(2,),dtype='int32',order='F'),[1 son],'CGNSBase_t'] #>> |_['cart',array(shape=(3, 3),dtype='int32',order='F'),[2 sons],'Zone_t'] #>> |_['ZoneType',array('Structured',dtype='|S1'),[0 son],'ZoneType_t'] #>> |_['GridCoordinates',None,[3 sons],'GridCoordinates_t'] #>> |_['CoordinateX',None,[0 son],'DataArray_t'] #>> |_['CoordinateY',None,[0 son],'DataArray_t'] #>> |_['CoordinateZ',None,[0 son],'DataArray_t']
- Converter.Mpi.convertFile2PyTree(fileName, format=None, proc=None)
If proc=None, read a full tree. The fully loaded in memory tree is identical on all processors.
If proc is given, read a partial tree of zones corresponding to proc.
- Parameters:
fileName (string) – file name to read from
format (string) – any converter format (optional)
proc (None or int) – None or rank number
- Returns:
fully loaded or partial tree
- Return type:
pyTree node
Example of use:
# - convertFile2PyTree (pyTree) - import Converter.PyTree as C import Generator.PyTree as G import Converter.Mpi as Cmpi import Converter.Internal as Internal if Cmpi.rank == 0: a = G.cart((0.,0.,0.),(0.1,0.1,0.1),(11,11,11)) t = C.newPyTree(['Base',a]) C.convertPyTree2File(t, 'in.cgns') Cmpi.barrier() # Identique sur tous les procs t1 = Cmpi.convertFile2PyTree('in.cgns') if Cmpi.rank == 1 or Cmpi.rank == 0: Internal.printTree(t1)
- Converter.Mpi.readZones(t, fileName, format=None, rank=None, zoneNames=None)
Fill the data of skeleton zones of t following zone rank or zone name (adf or hdf).
If rank is not None, zone must have been attributed to ranks either with Distributor2.distribute or setProc. If the zone rank corresponds to process rank, the zone is filled with data read from file.
If zoneNames is not None, zone with corresponding name are filled with data read from file.
Exists also as in place version (_readZones) that modifies t and returns None.
- Parameters:
t ([pyTree]) – input data
fileName (string) – file name to read from
format (string) – bin_cgns, bin_adf, bin_hdf (optional)
rank (int) – the processor of zones to read
zoneNames (list of strings) – paths of zones to read (if rank is not set)
- Returns:
modified reference copy of t
- Return type:
Identical to t
Example of use:
# - readZones (pyTree) - import Converter.PyTree as C import Converter.Mpi as Cmpi import Distributor2.PyTree as Distributor2 import Generator.PyTree as G # Cree le fichier test if Cmpi.rank == 0: a = G.cart((0,0,0), (1,1,1), (10,10,10)) b = G.cart((12,0,0), (1,1,1), (10,10,10)) t = C.newPyTree(['Base',a,b]) C.convertPyTree2File(t, 'test.cgns') Cmpi.barrier() # Relit les zones par paths t = Cmpi.convertFile2SkeletonTree('test.cgns') Cmpi._readZones(t, 'test.cgns', zoneNames=['Base/cart']) # Relit des zones par procs t = Cmpi.convertFile2SkeletonTree('test.cgns') (t, dic) = Distributor2.distribute(t, NProc=Cmpi.size, algorithm='fast') t = Cmpi.readZones(t, 'test.cgns', rank=Cmpi.rank) if Cmpi.rank == 0: C.convertPyTree2File(t, 'out.cgns')
- Converter.Mpi.writeZones(t, fileName, format=None, rank=None, zoneNames=None)
Write some zones in an existing file (adf or hdf) according to zone rank or zone name. If by rank, zone must have been attributed to processors either with Distributor2.distribute or setProc.
- Parameters:
t ([pyTree]) – input data
fileName (string) – file name to write to
format (string) – bin_cgns, bin_adf, bin_hdf (optional)
rank (int) – the processor of written zones
zoneNames (list of strings) – paths of written zones (if rank is not set)
Example of use:
# - writeZones (pyTree) - import Converter.PyTree as C import Converter.Distributed as Distributed import Distributor2.PyTree as Distributor2 import Generator.PyTree as G a = G.cart((0,0,0), (1,1,1), (10,10,10)) b = G.cart((12,0,0), (1,1,1), (10,10,10)) t = C.newPyTree(['Base']) C.convertPyTree2File(t, 'out.adf') t[2][1][2] += [a,b] (t, dic) = Distributor2.distribute(t, NProc=2, algorithm='fast') Distributed.writeZones(t, 'out.adf', proc=0)
- Converter.Mpi.convertPyTree2File(t, fileName, format=None, links=[], ignoreProcNodes=False)
Write a skeleton tree (S), a loaded skeleton tree (LS) or a partial tree (P) to a file (adf or hdf).
- Parameters:
t ([pyTree]) – input data
fileName (string) – file name to write to
format (string) – bin_cgns, bin_adf, bin_hdf (optional)
links (list of list of 4 strings) – optional list of links to be written
ignoreProcNodes (boolean) – if true, only write zones with procNode set to rank, else write all proc zones
Example of use:
# - convertPyTree2File (pyTree) - import Converter.PyTree as C import Converter.Mpi as Cmpi import Generator.PyTree as G if Cmpi.rank == 0: a = G.cart((0.,0.,0.),(0.1,0.1,0.1),(11,11,11)) a[0] = 'cart0' else: a = G.cart((10,0,0),(0.1,0.1,0.1),(11,11,11)) a[0] = 'cart1' t = C.newPyTree(['Base', a]) Cmpi.convertPyTree2File(t, 'out.cgns')
Conversions
- Converter.Mpi.convert2SkeletonTree(t, maxSize=6)
Convert a tree (LS or P) to a skeleton tree (S). In a skeleton tree, numpys in DataArray_t nodes are replaced by None.
Exists also as in place version (_convert2SkeletonTree) that modifies t and returns None.
- Parameters:
t ([pyTree, base, zone, list of zones]) – input data
maxSize (int) – numpy bigger than maxSize are remplace by None
- Return type:
Identical to t
Example of use:
# - convert2SkeletonTree (pyTree) - import Converter.PyTree as C import Converter.Mpi as Cmpi import Generator.PyTree as G a = G.cart((0,0,0), (1,1,1), (10,10,10)) a = Cmpi.convert2SkeletonTree(a) C.convertPyTree2File(a, 'out.cgns')
- Converter.Mpi.convert2PartialTree(t, rank=-1)
Convert a loaded skeleton tree (LS) to a partial tree (P). If rank=-1, all skeleton zones are suppressed. If rank>=0, zones with proc != rank are suppressed.
Exists also as in place version (_convert2PartialTree) that modifies t and returns None.
- Parameters:
t ([pyTree, base, zone, list of zones]) – input data
rank – if rank=-1: suppress all skeleton zones, if rank>=0: suppress zones with proc=rank
- Return type:
Identical to t
Example of use:
# - convert2PartialTree (pyTree) - import Converter.PyTree as C import Converter.Mpi as Cmpi import Distributor2.PyTree as Distributor2 import Generator.PyTree as G # Cree le fichier test if Cmpi.rank == 0: a = G.cart((0,0,0), (1,1,1), (10,10,10)) b = G.cart((12,0,0), (1,1,1), (10,10,10)) t = C.newPyTree(['Base',a,b]) C.convertPyTree2File(t, 'test.cgns') Cmpi.barrier() # Relit des zones par procs t = Cmpi.convertFile2SkeletonTree('test.cgns') (t, dic) = Distributor2.distribute(t, NProc=Cmpi.size, algorithm='fast') t = Cmpi.readZones(t, 'test.cgns', proc=Cmpi.rank) # Arbre partiel (sans zones squelettes) t = Cmpi.convert2PartialTree(t) if Cmpi.rank == 0: C.convertPyTree2File(t, 'out.cgns')
- Converter.Mpi.createBBoxTree(t, method='AABB')
From a partial tree (P) or a loaded skeleton tree (LS), create a full tree containing the bbox of zones. A bbox is a structured grid made of 8 points englobing zone. The returned tree is identical on all processors. Argument method can be ‘AABB’ (axis aligned bbox) or ‘OBB’ (oriented bbox).
- Parameters:
t ([pyTree, base, zone, list of zones]) – input data
method – ‘AABB’: axis aligned bbox, ‘OBB’: oriented bbox
- Return type:
Identical to t
Example of use:
# - createBBoxTree (pyTree) - import Converter.PyTree as C import Converter.Mpi as Cmpi import Distributor2.PyTree as Distributor2 import Generator.PyTree as G # Cree le fichier test if Cmpi.rank == 0: a = G.cart((0,0,0), (1,1,1), (10,10,10)) b = G.cart((12,0,0), (1,1,1), (10,10,10)) t = C.newPyTree(['Base',a,b]) C.convertPyTree2File(t, 'in.cgns') Cmpi.barrier() # Relit des zones par procs t = Cmpi.convertFile2SkeletonTree('in.cgns') (t, dic) = Distributor2.distribute(t, NProc=Cmpi.size, algorithm='fast') t = Cmpi.readZones(t, 'in.cgns', rank=Cmpi.rank) # Cree le bbox tree tb = Cmpi.createBBoxTree(t) if Cmpi.rank == 0: C.convertPyTree2File(tb, 'out.cgns')
Graphs
- Converter.Mpi.getProc(z)
Get the rank of zone. It only returns the value stored in .Solver#Param/proc node. You can use setProc or Distributor2 to create the .Solver#Param/proc node.
- Parameters:
z ([zone]) – input zone
- Return type:
int
Example of use:
# - getProc (pyTree) - import Converter.PyTree as C import Converter.Internal as Internal import Converter.Mpi as Cmpi import Distributor2.PyTree as Distributor2 import Generator.PyTree as G a = G.cart((0,0,0), (1,1,1), (10,10,10)) b = G.cart((12,0,0), (1,1,1), (10,10,10)) t = C.newPyTree(['Base',a,b]) (t, dic) = Distributor2.distribute(t, NProc=2, algorithm='fast') zones = Internal.getNodesFromType(t, 'Zone_t') for z in zones: print(z[0]+' -> '+str(Cmpi.getProc(z))) #>> cart -> 0 #>> cart.0 -> 1
- Converter.Mpi.setProc(t, rank)
Set rank in t. t can be a skeleton (S), partial (P) or full tree. It only creates a .Solver#Param/proc node for the zones of t.
Exists also as in place version (_setProc) that modifies t and returns None.
- Parameters:
t ([pyTree, base, zone, list of zones]) – input data
rank (int) – the rank value to set
- Returns:
modified reference copy of t
- Return type:
Identical to t
Example of use:
# - setProc (pyTree) - import Converter.PyTree as C import Converter.Internal as Internal import Converter.Mpi as Cmpi import Generator.PyTree as G a = G.cart((0,0,0), (1,1,1), (10,10,10)) b = G.cart((12,0,0), (1,1,1), (10,10,10)) t = C.newPyTree(['Base',a,b]) t = Cmpi.setProc(t, 1) zones = Internal.getZones(t) for z in zones: print(z[0]+' -> '+str(Cmpi.getProc(z))) #>> cart -> 1 #>> cart.0 -> 1
- Converter.Mpi.getProcDict(t)
Return the rank information stored in .Solver#Param/proc as a dictionary proc[‘zoneName’].
- Parameters:
t ([pyTree, base, zone, list of zones]) – input data
- Return type:
Dictionary
Example of use:
# - getProcDict (pyTree) - import Converter.PyTree as C import Converter.Mpi as Cmpi import Distributor2.PyTree as Distributor2 import Generator.PyTree as G # Cree le fichier test if Cmpi.rank == 0: a = G.cart((0,0,0), (1,1,1), (10,10,10)) b = G.cart((12,0,0), (1,1,1), (10,10,10)) t = C.newPyTree(['Base',a,b]) C.convertPyTree2File(t, 'in.cgns') Cmpi.barrier() # Relit des zones par procs t = Cmpi.convertFile2SkeletonTree('in.cgns') (t, dic) = Distributor2.distribute(t, NProc=2, algorithm='fast') procDict = Cmpi.getProcDict(t) if Cmpi.rank == 0: print(procDict) #>> {'cart.0': 1, 'cart': 0}
- Converter.Mpi.computeGraph(t, type='bbox', t2=None, procDict=None, rank=0, intersectionsDict=None)
Compute a communication graph. The graph is a dictionary such that graph[proc1][proc2] contains the names of zones of proc1 that are “connected” to at least one zone on proc2.
If type=’bbox’, a zone is connected to another if their bbox intersects. A must be a bbox tree.
If type=’bbox2’, a zone is connected to another if their bbox intersects and are not in the same base. A must be a bbox tree.
If type=’bbox3’, a zone is connected to another of another tree if their bbox intersects. A and t2 must be a bbox tree.
If type=’match’ (S/LS/P), a zone is connected to another if they have a match between them. A can be a skeleton, loaded skeleton or a partial tree.
If type=’ID’ (S/LS/P), a zone is connected to another if they have interpolation data between them. A can be a skeleton, a loaded skeleton or a partial tree.
If type=’IBCD’ (S/LS/P), a zone is connected to another if they have IBC data between them. A can be a skeleton, a loaded skeleton or a partial tree.
If type=’ALLD’ (S/LS/P), a zone is connected to another if they have Interpolation or IBC data between them. A can be a skeleton, a loaded skeleton or a partial tree.
If type=’proc’, a zone is attributed to another proc than the one it is loaded on. A can be a skeleton, a loaded skeleton tree or a partial tree.
If type=’POST’, t defines the donor tree, where the interpolation data is stored and t2 the receptor tree, as they do not define the same zones. Requires procDict and procDict2
- Parameters:
t ([pyTree, base, zone, list of zones]) – input data
type (string in 'bbox', 'bbox2', 'bbox3', 'match', 'ID', 'IBCD', 'ALLD', 'proc') – type of graph
t2 (pyTree) – optional second tree for type=’bbox3’
procDict (dictionary) – if provided, used for zone affectation
intersectionDict (python dictionary) – dictionary of intersections
- Return type:
python dictionary of communications
Example of use:
# - computeGraph (pyTree) - import Converter.PyTree as C import Converter.Mpi as Cmpi import Distributor2.PyTree as Distributor2 import Generator.PyTree as G # Cree le fichier test if Cmpi.rank == 0: a = G.cart((0,0,0), (1,1,1), (10,10,10)) b = G.cart((9,0,0), (1,1,1), (10,10,10)) t = C.newPyTree(['Base',a,b]) C.convertPyTree2File(t, 'test.cgns') Cmpi.barrier() # Relit des zones par procs t = Cmpi.convertFile2SkeletonTree('test.cgns') (t, dic) = Distributor2.distribute(t, NProc=Cmpi.size, algorithm='fast') t = Cmpi.readZones(t, 'test.cgns', proc=Cmpi.rank) # Cree le bbox tree tb = Cmpi.createBBoxTree(t) # Cree le graph graph = Cmpi.computeGraph(tb) if Cmpi.rank == 0: print(graph)
Exchanges
- Converter.Mpi.setCommunicator(com)
Set the MPI communicator for Cassiopee exchanges. By default, it is set to MPI.COMM_WORLD.
- Parameters:
com (MPI communicator) – communicator to set
- Converter.Mpi.addXZones(t, graph)
For a partial tree, add zones loaded on a different process that are connected to local zones through the graph.
Exists also as in place version (_addXZones) that modifies t and returns None.
- Parameters:
t ([pyTree]) – input tree
graph (dictionary) – communication graph as defined by computeGraph
- Returns:
modified reference copy of t
- Return type:
tree with connected zones added
Example of use:
# - addXZones (pyTree) - import Converter.PyTree as C import Converter.Mpi as Cmpi import Distributor2.PyTree as Distributor2 import Generator.PyTree as G # Cree le fichier test if Cmpi.rank == 0: a = G.cart((0,0,0), (1,1,1), (10,10,10)) b = G.cart((9,0,0), (1,1,1), (10,10,10)) t = C.newPyTree(['Base',a,b]) C.convertPyTree2File(t, 'test.cgns') Cmpi.barrier() # Relit des zones par procs t = Cmpi.convertFile2SkeletonTree('test.cgns') (t, dic) = Distributor2.distribute(t, NProc=Cmpi.size, algorithm='fast') t = Cmpi.readZones(t, 'test.cgns', rank=Cmpi.rank) # Cree le bbox tree tb = Cmpi.createBBoxTree(t) # Cree le graph graph = Cmpi.computeGraph(tb) # Add X Zones t = Cmpi.addXZones(t, graph) if Cmpi.rank == 0: C.convertPyTree2File(t, 'out.cgns')
- Converter.Mpi.rmXZones(t)
For a partial tree, remove zones created by addXZones.
Exists also as in place version (_rmXZones) that modifies t and returns None.
- Parameters:
t ([pyTree]) – input tree
- Return type:
tree with connected zones suppressed
Example of use:
# - rmXZones (pyTree) - import Converter.PyTree as C import Converter.Mpi as Cmpi import Distributor2.PyTree as Distributor2 import Generator.PyTree as G # Cree le fichier test if Cmpi.rank == 0: a = G.cart((0,0,0), (1,1,1), (10,10,10)) b = G.cart((9,0,0), (1,1,1), (10,10,10)) t = C.newPyTree(['Base',a,b]) C.convertPyTree2File(t, 'test.cgns') Cmpi.barrier() # Relit des zones par procs t = Cmpi.convertFile2SkeletonTree('test.cgns') (t, dic) = Distributor2.distribute(t, NProc=Cmpi.size, algorithm='fast') t = Cmpi.readZones(t, 'test.cgns', rank=Cmpi.rank) # Cree le bbox tree tb = Cmpi.createBBoxTree(t) # Cree le graph graph = Cmpi.computeGraph(tb) # Add X Zones t = Cmpi.addXZones(t, graph) t = Cmpi.rmXZones(t) if Cmpi.rank == 0: C.convertPyTree2File(t, 'out.cgns')
- Converter.Mpi.allgatherTree(t)
Gather a distributed tree on all processors. All processors then see the same tree.
- Parameters:
t ([pyTree]) – input tree
- Return type:
merged and gathered tree (identical on all processors)
Example of use:
# - allgatherTree (pyTree) - import Converter.PyTree as C import Converter.Mpi as Cmpi import Distributor2.PyTree as Distributor2 import Generator.PyTree as G import Converter.Internal as Internal # Create test file if Cmpi.rank == 0: a = G.cart((0,0,0), (1,1,1), (10,10,10)) b = G.cart((9,0,0), (1,1,1), (10,10,10)) t = C.newPyTree(['Base',a,b]) C.convertPyTree2File(t, 'test.cgns') Cmpi.barrier() # Reread in parallel t = Cmpi.convertFile2SkeletonTree('test.cgns') (t, dic) = Distributor2.distribute(t, NProc=Cmpi.size, algorithm='fast') t = Cmpi.readZones(t, 'test.cgns', rank=Cmpi.rank) t = Cmpi.convert2PartialTree(t) t = Cmpi.allgatherTree(t) # full tree on every processors if Cmpi.rank == 0: Internal.printTree(t)
Actions
- Converter.Mpi.trace(text, cpu=True, mem=True, fileName='stdout')
Enable to monitor CPU usage and memory usage for each node/process. If fileName=”stdout”, information is written to standard output with the processor number. If fileName is another name, information is written in “filenameXXX” files, one for each process. If filename does not have a format for the process number and/or an file extension, it is appended “%03d” and “.out”, respectively. If cpu=True, time elapsed since the previous call to “trace” by this node is written. If mem=True, the current usage of memory of each process is written.
- Parameters:
text (string) – text to write
cpu (boolean) – True to write cpu usage information
mem (boolean) – True to write memory usage information
fileName (string) – name of the file to write information to or “stdout”
Example of use:
# - trace (pyTree) - import Generator.PyTree as G import Converter.Mpi as Cmpi Cmpi.trace('>>> Start', fileName="stdout") a = G.cart((0,0,0), (1,1,1), (100,100,100)) Cmpi.trace('>>> After cart', fileName="stdout") #>> 0: >>> Start [0 secs][8.5 MB] #>> 0: >>> After cart [0.0060176 secs][9.2 MB]
- Converter.Mpi.center2Node(t, var=None, cellNType=0, graph=None)
Perform a center to node conversion for a distributed tree. If var is set, var can be a field name or a container name (Internal.__FlowSolutionNodes__, Internal.__FlowSolutionCenters__, …). Then, center2Node is performed in the given field. Otherwise, the zone with its coordinates is moved to node.
- Parameters:
t ([pyTree]) – input tree
- Return type:
merged and gathered tree (identical on all processors)
Example of use:
# - center2Node distributed - import Converter.PyTree as C import Distributor2.PyTree as Distributor2 import Converter.Mpi as Cmpi import Connector.PyTree as X import Generator.PyTree as G # Create test case N = 11 t = C.newPyTree(['Base']) pos = 0 for i in range(N): a = G.cart( (pos,0,0), (1,1,1), (10+i, 10, 10) ) pos += 10 + i - 1 t[2][1][2].append(a) t = C.initVars(t, '{centers:Density} = {CoordinateX} + {CoordinateY}') t = X.connectMatch(t) if Cmpi.rank == 0: C.convertPyTree2File(t, 'in.cgns') Cmpi.barrier() # Reread in parallel sk = Cmpi.convertFile2SkeletonTree('in.cgns') (sk, dic) = Distributor2.distribute(sk, NProc=Cmpi.size, algorithm='gradient0', useCom='match') a = Cmpi.readZones(sk, 'in.cgns', rank=Cmpi.rank) # center2Node a = Cmpi.center2Node(a, 'centers:Density') # a is now a partial tree a = C.rmVars(a, 'centers:Density') # Rebuild full tree in file Cmpi.convertPyTree2File(a, 'out.cgns')