Xdmf3 Python API

From XdmfWeb
Jump to navigationJump to search

XdmfLogo1.gif

XDMF API

While use of the XDMF API is not necessary to produce or consume valid XDMF datasets, there are many convenience features that make it attractive.

All XDMF Objects are derived from XdmfItem.

XdmfDomain

To understand access to XDMF data, understanding of the XdmfDOM is critical. XDMF uses the libxml2 library to parse and generate XML documents. The Domain is an in-memory tree structure that represents the XML file. Nodes of the tree are added, deleted, queried and serialized.

A Domain can be created inside the code via the New function:

domain = XdmfDomain.New()

A reader can be used to produce a domain from an existing file:

reader = XdmfReader.New()
domain = reader.read("MyXMLFile.xmf")

Once the XML has been parsed into the Domain, the tree can be navigated and modified. The domain is the root of the Xdmf tree and by accessing its children via XdmfDomain::getUnstructuredGrid(unsigned int) and similar functions.

ungrid = domain.getUnstructuredGrid(0)

The same grid can also be fetched by name.

ungrid = domain.getUnstructuredGrid("Unstructured Grid")

Once you have the child object, that child has its own functions that can be used to modify or retrieve attributes or children.

originalName = ungrid.getName()
ungrid.setName("New Name")

When finished building and modifying the XdmfTree it can then be written out to file with an XdmfWriter visitor.

writer = XdmfWriter.New("outfile.xmf")
domain.accept(writer)

XdmfArray

The XdmfArray class is a self describing data structure. It contains descriptions for its internal number type, precision, and shape (rank and dimensions). Many XDMF classes require an XdmfArray as input to methods. The following C++ example demonstrates creating an interlaced XYZ array from three separate variables:


x = [1, 2, 3, ..]
y = [1, 2, 3, ..]
z = [1, 2, 3, ..]
dims = UInt32Vector()
dims.push_back(10)
dims.push_back(20)
dims.push_back(30)
total = 10 * 20 * 30
xyz = XdmfArray.New()
xyz.initialize(XdmfArrayType.Float64(), dims);    // KDim, JDim, IDim
xyz.insertAsFloat64(0, x, 0, total, 3, 1);  //       insert<T>(unsigned int index, T *Values,
                                 //                 unsigned int NumberOfValues, unsigned int ArrayStride=1,
                                 //                 unsigned int ValuesStride=1)
xyz.insertAsFloat64(1, y, 0, total, 3, 1);
xyz.insertAsFloat64(2, z, 0, total, 3, 1);


XdmfTime

When modifying the Xdmf Tree, XdmfTime objects can be added to grids in order to specify timestamps.

time = XdmfTime.New(5.0)
ungrid.setTime(time)


XdmfHDF

In XDMF, Light data is stored in XML while the Heavy data is typically stored in an HDF5 file. Passing an XdmfHDF5Writer to an array will write its contents to the specified hdf5 file. A controller object is created to handle the data description and allows the array to read the data back in if needed. The default write mode creates a new hdf5 dataspace for each written array. HDF5 write modes include:

  • Default
  • Overwrite
  • Append
  • Hyperslab
from Xdmf import *
Geometry = "-1.75 -1.25 0 -1.25 -1.25 0 -0.75 
Connectivity = "3 2 5 1 .
Values = "100 200 300 ..
heavywriter = XdmfHDF5Writer.New("example.h5")
# Geometry
GeometryArray = XdmfGeometry.New()
GeometryArray.insertAsFloat64(0, Geometry)
GeometryArray.accept(heavywriter)
# Connectivity
ConnectivityArray = XdmfTopology.New()
ConnectivityArray.insertAsFloat64(0, Connectivity)
ConnectivityArray.accept(heavywriter)
# Values
ValueArray = XdmfArray.New()
ValueArray.insertAsFloat64(0, Values)
ValueArray.accept(heavywriter)


When reading, if reading from an XML file where the arrays have the hdf5 data sets specified, controllers will automatically be created for all arrays with a heavy data description. To access the data in heavy data XdmfArray::read() must be called. If it is required to link heavy data manually a heavy data controller can be created manually and added to an array.

newPath = "File path to hdf5 file goes here"
newSetPath = "path to the set goes here"
readType = XdmfArrayType.Int32()
#Xdmf provides wrappers for C++ types
#in this case std::vector<unsigned int>
readStarts = UInt32Vector()
#Three dimensions, all starting at index 0
readStarts.push_back(0)
readStarts.push_back(0)
readStarts.push_back(0)
readStrides = UInt32Vector()
#Three dimensions, no skipping between index
readStrides.push_back(1)
readStrides.push_back(1)
readStrides.push_back(1)
readCounts = UInt32Vector()
#Three dimensions, reading 10 values from each
readCounts.push_back(10)
readCounts.push_back(10)
readCounts.push_back(10)
readDataSize = UInt32Vector()
#three dimensins, each with 20 maximum values
readDataSize.push_back(20)
readDataSize.push_back(20)
readDataSize.push_back(20)
controller = XdmfHDF5Controller.New(
               newPath,
               newSetPath,
               readType,
               readStarts,
               readStrides,
               readCounts,
               readDataSize)
array.insert(controller)
array.read()

Reading XDMF

Putting all of this together, assume Points.xmf is a valid XDMF XML file with a single Grid. Here is a Python example to read and print values.

reader = XdmfReader.New()
domain = reader.read("Points.xmf")
grid = domain.getUnstructuredGrid(0)
topology = grid.getTopology()
print "Values = ", topology.getValuesString()
geometry = grid.getGeometry()
print  "Geo Type =  ", geo.getType().getName(), " #  Points = ", geometry.getNumberOfPoints()
print  "Points =  ", geometry.getValuesString()

Writing XDMF

Using the insert() and set() methods, an XDMF dataset can be generated programmatically as well. An object tree mirroring the XML is built as Xdmf objects are attached to each other. Since this is handled via shared pointers the objects are not duplicated if there are multiple instances of that object in the tree and the user does not have to worry about memory cleanup.

root = XdmfDomain.New()
# Information
i = XdmfInformation.New() # Arbitrary Name=Value Facility
i.setName("Time")
i.setValue("0.0123")
root.insert(i) # XdmfDomain is the root of the tree
               # insert adds the information we just created as a leaf
# Dimensions
dimensions = XdmfArray.New()
dimensions.pushBackAsUInt32(10)
dimensions.pushBackAsUInt32(20)
dimensions.pushBackAsUInt32(30)
# Origin
origin = XdmfArray.New()
origin.pushBackAsUInt32(1)
origin.pushBackAsUInt32(2)
origin.pushBackAsUInt32(3)
# Brick Size
brick = XdmfArray.New()
brick.pushBackAsUInt32(1)
brick.pushBackAsUInt32(2)
brick.pushBackAsUInt32(3)
# Grid
g = XdmfRegularGrid.New(brick, dimensions, origin)
g.setName("Structured Grid")
root.Insert(g)
# Attribute
attr = XdmfAttribute.New()
attr.setName("Pressure")
attr.setAttributeCenter(XdmfAttributeCenter::Cell());
attr.setAttributeType(XdmfAttributeType::Scalar());
attr.initializeAsFloat64(10 * 20 * 30)
pressureVals = {0, 1, 2, 3, 4, 5, ...} 
attr.insert(0, pressureVals)
# The heavy data set name is determined by the writer if not set
g.insert(attr)
# Generate a writer
writer = XdmfWriter.New("output.xmf")
domain.accept(writer)