xplt module usage
pyfebiopt.xplt reads FEBio .xplt outputs and exposes them as views that
behave like sliceable arrays. The philosophy: keep FEBio’s shapes and let you
select by time, region, items, nodes, and components with the same vocabulary
everywhere.
Reading scheme (what you get)
File contents: header + dictionary (names/types/formats), mesh (domains, surfaces, nodesets), then states (time steps with nodal/element/surface data).
xplt("run.xplt")object exposes:meshwith domains, surfaces, nodesets (names and ids kept from FEBio).dictionarymirroring FEBio variable names/types/formats.resultsregistry keyed by variable, with views:NodeResultView/NodeRegionResultView(nodal, global or per-domain)ItemResultView(elements/faces, FMT_ITEM)MultResultView(per-element-node, FMT_MULT)RegionResultView(per-domain vectors, FMT_REGION)
Capabilities snapshot
Read all or selected steps:
readAllStates()orreadSteps([...]).Access mesh names and ids: domains, surfaces, nodesets.
Inspect times and dictionary:
xp.results.times(),xp.dictionary.Slice lazily with views; evaluation happens on
comp(...)oreval().Handle missing data: absent variables on a step return
NaNwith correct shape.
Quick start
Open the file (parses header, dictionary, mesh).
from pyfebiopt.xplt import xplt
xp = xplt("run.xplt")
print("Domains:", list(xp.mesh.domains.keys()))
print("Surfaces:", list(xp.mesh.surfaces.keys()))
print("Nodesets:", list(xp.mesh.nodesets.keys()))
Load states (all or a subset).
xp.readAllStates() # read everything
# xp.readSteps([0, -1]) # only first and last step
Inspect registry, times, and dictionary.
r = xp.results
print("Times:", r.times()) # numpy array of times
print("Variables:", list(xp.dictionary.keys())) # FEBio dictionary entries
print("Results repr:", r) # counts per storage kind
Core capabilities
Nodal (global)
Axes: (time, node, component).
U = r["displacement"] # NodeResultView
U_tip = U.time(-1).nodes(-1).comp("z")
U_fast = U[0, ":", "x"] # __getitem__ shorthand
U_base = U.nodeset("base").comp(":")
# boolean mask for nodes
mask = U.nodes(np.array([True, False, True]))
sub = mask.comp("x")
Nodal per-domain
Axes: (time, node_in_region, component).
Ur = r["displacement"].region("arteria") # NodeRegionResultView
print("Region nodes:", Ur.region_nodes()) # node ids in that domain
u_slice = Ur.time(0).nodes(slice(0, 10)).comp("y")
Element/face items (FMT_ITEM)
Axes: (time, item, component) where items are elements or faces.
S = r["von Mises"].domain("arteria") # ItemResultView on elements
s_last = S.time(-1).items([0, 5]).comp(":")
P = r["pressure"].surface("outlet") # ItemResultView on faces
p_mid = P.time(":").faces(":").comp(":")
# boolean mask for items
mask = np.array([True, False, True])
masked = S.items(mask).comp(":")
Per-element-node (FMT_MULT)
Axes: (time, item, enode, component) where enode is the position inside an element.
Q = r["strain"].domain("arteria") # MultResultView
q_block = Q.time(0).items(0).enodes(":").comp("xx")
Per-region vectors (FMT_REGION)
Axes: (time, component).
V = r["domain volume"].domain("arteria") # RegionResultView
v_all = V.time(":").comp(":")
Dictionary and metadata
xp.dictionarymirrors FEBio’s dictionary:{name: {"type": ..., "format": ...}}.xp.versionandxp.compressionhold header info.Variable names follow FEBio; component names depend on data type (
"x","xx", etc.).
Print helpers
print(r)gives a summary of how many variables exist per storage kind.print(r["displacement"])prints shape info and missing-step counts.list(r.node.keys())orlist(r.elem_item.keys())to discover variables by kind.
Selections and tokens
Use
":"as a string to mean “all” in__getitem__and selectors.Selectors accept integers, slices, numpy arrays, lists, or boolean masks.
Component tokens:
"x" "y" "z"for VEC3F,"xx" "yy" "zz"for MAT3FD,"xx" "yy" "zz" "xy" "yz" "xz"for MAT3FS, full 3x3 names for MAT3F, and Voigt-6 pairs ("xxyy") for TENS4FS.
Slicing cheatsheet
All selectors are chainable; the last call for an axis wins. __getitem__ is a
shorthand that mirrors the axis order of each view.
Time
view.time(idx) where idx is int/slice/list/ndarray/boolean mask/":".
U.time(0) # first state
U.time(slice(0, 3)) # first three
U[0, ":", ":"] # __getitem__ equivalent
Region
Nodal per-domain:
region(name)/domain(name)Element/face item or mult:
region(name)/domain(name)/surface(name)Region vectors:
region(name)
S = r["von Mises"].domain("arteria")
P = r["pressure"].surface("outlet")
Items/nodes/enodes
Nodal global:
nodes(...)ornodeset("name")Nodal per-domain:
nodes(...)(indices are local to that region),region_nodes()to inspect idsItem views:
items(...)/elems(...)/faces(...)Mult views:
items(...)+enodes(...)/nodes(...)for per-element-node positions
U.nodeset("base").comp(":") # nodeset by name
Ur = r["displacement"].region("arteria")
Ur.nodes(slice(0, 5)).comp(":") # first 5 nodes of that domain
Q.items([0, 2]).enodes(":").comp(":")# two elements, all element-nodes
Components
comp(...) is common to every view and evaluates the array. Pass an
integer, slice, iterable, numpy array, boolean mask, or component names. ":"
means “all”. Component name support depends on the FEBio data type:
VEC3F →
x y zMAT3FD →
xx yy zzMAT3FS →
xx yy zz xy yz xzMAT3F → full 3x3 row-major names
TENS4FS → Voigt-6 pairs like
xxyy(order-insensitive)
Shapes (by view)
NodeResultView:(time, node, component)NodeRegionResultView:(time, node_in_region, component)ItemResultView:(time, item, component)MultResultView:(time, item, enode, component)RegionResultView:(time, component)
Memory and missing data
Views are lazy and cast to
float32to stay compact.Missing data for a step/region returns arrays filled with
NaNbut with the correct shape, so vectorized code does not break. This happens when FEBio writes a variable only on some steps (e.g., contact variables defined for part of the analysis); the missing steps remain present asNaNplaceholders to keep array ranks stable.
Practical snippets
List all regions that have a given variable:
S = r["von Mises"]
print("Domains:", S.regions()) # works for ItemResultView and others
Compute a time history for a nodeset average:
U = r["displacement"]
base = xp.mesh.nodesets["base"]
Uz = U.nodes(base).comp("z") # shape: (time, nodes)
Uz_mean = Uz.mean(axis=1)
Grab the last state only for a heavy file:
xp = xplt("run.xplt")
xp.readSteps([-1])
stress = xp.results["stress"].domain("arteria").time(-1).comp("xx")