Core data structures¶
pyOFTools moves data through a pipeline. Every stage has a concrete Python type, and every type has a small, well-defined shape. This page lists them and shows how they connect.
Knowing these types is optional for writing a post-processor — the
builders module hides most of them — but it is essential for writing
custom nodes, reading tracebacks, or debugging why an aggregator rejected a
dataset.
The pipeline in one picture¶
graph LR
subgraph Source
Mesh["fvMesh / sampledSurface / sampledSet"]
end
subgraph "Geometry adapter"
Adapter["InternalMesh<br/>SurfaceMesh<br/>SetGeometry<br/>BoundaryMesh"]
end
subgraph "Dataset (field + geometry + mask/groups)"
DS["InternalDataSet<br/>SurfaceDataSet<br/>PointDataSet<br/>PatchDataSet"]
end
subgraph "Nodes"
Filter["Filter node<br/>(Box, Sphere, Threshold)"]
Binner["Binner<br/>(Directional)"]
Agg["Aggregator<br/>(Sum, Mean, VolIntegrate)"]
end
Result["AggregatedDataSet"]
Writer["TableWriter → CSV"]
Mesh --> Adapter
Adapter --> DS
DS -->|masked| Filter
Filter --> DS
DS -->|grouped| Binner
Binner --> DS
DS --> Agg
Agg --> Result
Result --> Writer
Read the arrows as “produces”. The dataset types in the middle are the
stable currency of the pipeline: filters and binners return a dataset of the
same kind they received, aggregators always return an AggregatedDataSet.
The four input datasets¶
All four share the same outer shape — name, field, geometry,
optional mask, optional groups — and differ only in the geometry they
carry. Pydantic BaseModel is used so nodes can declare dataset fields by
type and have them validated at construction.
Dataset |
Geometry protocol |
What the geometry carries |
Produced by |
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Boundary-field builders |
field is a pybFoam scalarField / vectorField / tensorField /
symmTensorField. Size matches the geometry (one entry per cell, face, or
sample point).
mask is an optional boolean array selecting which elements participate in
subsequent steps. Box and Sphere write to it; aggregators honour it.
groups is an optional integer array assigning each element to a bin.
Directional writes to it; aggregators reduce per-group, producing one
AggregatedData row per group.
The geometry protocols¶
Geometry is a Python Protocol, not a class hierarchy. Anything with the
right attributes satisfies it, so OpenFOAM objects wrap into the pipeline via
thin adapters (pure Python, no copies):
FvMeshInternalAdapter(mesh)— exposes cell centres and cell volumes from anfvMesh.SampledSurfaceAdapter(surface)— exposes face centres and face areas from asampledSurface(whatcreate_planeandcreate_iso_surfacereturn underneath).SampledSetAdapter(set)— exposes point positions and distances from asampledSet(underneath thecreate_*_setbuilders).
Once wrapped, nothing in the pipeline knows it is talking to an OpenFOAM
object — only to an InternalMesh, SurfaceMesh, etc. This is what lets
the same Mean node operate on volumes, surfaces, or lines.
The output dataset¶
AggregatedDataSet is the pipeline’s exit type. It has no geometry and no
mask — just a name and a list of AggregatedData rows. Each row carries:
value— a scalar, vector, or tensor result.group— the bin label(s) the value belongs to (Noneif ungrouped).group_name— the names of the grouping columns.
AggregatedDataSet.headers and AggregatedDataSet.grouped_values expose
the data in a CSV-ready shape; TableWriter consumes those directly.
Which node consumes which dataset¶
Most nodes are geometry-agnostic: Sum and Mean work on any dataset
because they only touch field, mask, and groups. A few need
specific geometry:
VolIntegraterequiresInternalMesh(needs cell volumes).SurfIntegraterequiresSurfaceMeshorBoundaryMesh(needs face areas).Directionalreadspositionsfrom any geometry, so it works on all four input dataset types.BoxandSpherereadpositionsand write tomask; they also work on all four.
A type mismatch surfaces as a Pydantic validation error at node construction
or a TypeError at compute() time, not as silent wrong results.
Lifetime and ownership¶
A
WorkFlowis rebuilt every time step insideTableWriter. The field data is read fresh from the running solver, so you see current values — not a stale snapshot.Geometry adapters hold references to the underlying OpenFOAM objects. Those live for the lifetime of the
fvMesh. Don’t cache an adapter past a mesh motion / topology change.Aggregation results (the
AggregatedDataSet) are plain Python numbers and lists. They are safe to pickle, compare in tests, or return from a function.
See also¶
WorkFlow internals — how
WorkFlowchains nodes and whatTableWriterdoes in parallel.Custom nodes — the
compute(dataset) -> datasetcontract you implement when you add a new node.