Converter User Manual¶
Running the converter tests¶
Tests are diff-based: the converter produces a deterministic OOFEM .in file which is compared against a committed reference.in.
Build the converter first (from your build directory):
cd ~/build/oofem
make converter.exec
Run all converter tests:
ctest -R test_converter
Run a single test by name:
ctest -R test_converter_3DCylinderQhull -V
The -V flag prints the full output, which is useful for diagnosing failures.
Test layout¶
Test directories are named <Case><Mesher> so the mesher path is obvious from the directory name (e.g. 2DPlateT3D, 3DFPZQhull). Each test lives in src/converter/tests/<TestName>/ and contains:
File |
Purpose |
|---|---|
|
Control file template (also the OOFEM input template) |
|
Committed golden output to diff against |
|
Qhull mesh input (qhull path) |
|
T3D mesh input (t3d path) |
|
Shell script that runs the full mesh / random-field pipeline (informational, not invoked by ctest) |
|
Genran control file. Present in tests whose |
Updating a reference file¶
After intentionally changing converter output, regenerate the reference from the test directory:
cd src/converter/tests/3DCylinderQhull
~/build/oofem/src/converter/converter.exec control.in mesh.nodes mesh.voronoi
cp oofem.in reference.in
Then re-run the test to confirm it passes.
Running the converter¶
The converter needs to know which mesher produced the input. Specify it with either the --mesher (or -m) CLI flag, or with a #@mesher t3d|qhull directive in control.in. The CLI flag wins if both are present.
# T3D mesh
converter.exec --mesher t3d control.in mesh.t3d
# Qhull mesh (nodes + voronoi)
converter.exec --mesher qhull control.in mesh.nodes mesh.voronoi
# Qhull mesh (nodes + delaunay + voronoi) - legacy 3-file form
converter.exec --mesher qhull control.in mesh.nodes mesh.delaunay mesh.voronoi
If control.in already declares the mesher (e.g. all the converter tests do), the flag can be omitted:
converter.exec control.in mesh.t3d
converter.exec control.in mesh.nodes mesh.voronoi
Output is always written to oofem.in in the working directory.
Control file #@ directives¶
The control file is an OOFEM .in template. The converter reads it line by line. Lines that start with #@ are directives - they either configure the converter or mark an injection point for mesh-derived content, and are not written to the output. All other lines (including # comments and #% regression-check blocks) are passed through verbatim.
Parsing vs consumption¶
All directives go through a single parser (Grid::readControlRecords) regardless of which mesher produced the input. A directive’s applicability is a property of the consumers - only the qhull writers (give3DSMOutput, give3DTMOutput) look at #@notch / #@sphereinclusion / etc., and only the T3D writers look at #@BC / #@LOAD / etc. A directive from the “wrong” pipeline is silently ignored at write time, not rejected at parse time.
The two tables below split directives by the pipeline that consumes them. Directives in the qhull table have no effect in a T3D run, and vice versa.
Qhull-consumed directives¶
Consumed by give3DSMOutput / give3DTMOutput (the qhull writers) and by qhull-pipeline setup. Geometry directives set up the region and localiser; they are consumed, not written out. Parsed but not consumed when the mesher is T3D.
Directive |
Arguments |
Purpose |
|---|---|---|
|
|
Selects the output generator. Available types: |
|
|
Path to the transport (TM) control template; used only with |
|
|
Declares which mesher produced the input. Lets |
|
|
Nominal grain diameter - must match the |
|
|
Periodicity flags per axis (0 = non-periodic, 1 = periodic). For |
|
|
Out-of-plane thickness for 2D |
|
|
Random integer seed. Non-negative values are replaced with |
|
|
Defines a 3D box-shaped region. The |
|
|
2D analog of |
|
|
2D circular domain region - the converter counterpart of the generator’s |
|
|
Defines a cylindrical region with axis from |
|
|
Cylindrical inclusion with ITZ halo (axis as for |
|
|
Declares a straight fibre. The converter discretises it into reinforcement nodes at intersections with matrix Voronoi cells, builds |
|
|
Axis-aligned notch box. 2D form |
|
|
Spherical inclusion with ITZ halo. Effective radius is |
|
|
2D analog of |
|
|
Circular hole (void) - a first-class |
|
|
Straight-axis cylindrical inclusion with ITZ halo. The classification uses perpendicular distance from each endpoint to the infinite axis through the two line points; inclusion semantics match |
|
|
Bulk-load inclusions from a packing file produced by |
|
|
Per-material body load. Any element whose |
|
(no arguments) |
Toggle. When present, each emitted element gets |
|
|
Hydro-mechanical coupling. Hole forms (GraFahGalWhe15 thick-walled cylinder, transport -> mechanical) act on the rim of |
|
|
SM-only. Any inside Delaunay vertex on the specified face of the region’s bounding box ( |
|
|
SM-only. Like |
|
|
Tags every emitted node lying on the named face plane of region 1’s bounding box ( |
|
|
Tags every emitted boundary node lying on the edge of region ``<id>`` with |
|
|
TM-only. When |
|
|
SM-only. Any Delaunay line with either endpoint equal to the named controlvertex gets material |
|
|
Declares a specific mesh-node location whose nearest Delaunay-vertex id is exposed at write time via the inline placeholder |
|
(inline placeholder) |
Substituted on each non-directive line with the mapped OOFEM node id (compact in non-periodic mode, raw in periodic mode). Longer ids substitute first so that e.g. |
|
(inline placeholder, not a line directive) |
Substituted with the numeric id of the periodic control node before each non-directive line is written. Use anywhere the OOFEM template needs that id (e.g. |
|
(inline placeholder) |
Replaced with the |
|
(inline placeholder) |
Replaced with the single-node |
|
(marker line) |
Used by |
|
(marker line) |
Used by |
|
(no arguments) |
Opt in to writing the auxiliary POV-Ray rendering files ( |
|
(no arguments) |
Opt in to writing the ParaView |
T3D-consumed directives¶
Consumed by the T3D writers (giveOutputT3d / writeT3dNodesOofem / writeT3dElemsOofem). These directives describe BCs, loads, and section data attached to named T3D entities (vertices, curves, surfaces). Parsed but not consumed when the mesher is qhull.
Directive |
Arguments |
Purpose |
|---|---|---|
|
|
Dirichlet BC on a T3D entity. Generates |
|
|
Distributed load on a T3D entity. Emitted via |
|
|
Direction vector associated with an entity (used for shell/beam orientation). |
|
|
Shell/plate thickness for an entity. |
|
|
3D section data for shell/beam elements. |
|
|
Per-edge width scaling for shell elements. |
|
|
Element-name override. Entity form (T3D writer): |
|
|
Declare a T3D curve as an embedded reinforcement bar. The curve must be tagged with |
<entType> uses the numeric codes from Grid::entityTypeFromString: 1 vertex, 2 curve, 3 surface, 5 patch, 6 shell.
2dSM writer - 2D structural mechanics¶
#@grid 2dSM dispatches to Grid::give2DSMOutput, which emits one lattice2D element per Delaunay edge inside the rect (or latticeboundary2d for periodic-crossing edges). The OOFEM line format is
lattice2D <id> nodes 2 <n1> <n2> crossSect <m> mat <m>
gpCoords 2 <gx> <gy> width <w> thick <t>
with gpCoords the midpoint of the lattice element, width the length of the dual Voronoi cross-section edge, and thick the out-of-plane thickness from #@thickness. The companion test domain type is domain 2dlattice (or 2dLattice).
Periodic mode (any axis of #@perflag set) appends a CTLNODE whose coordinates are the specimen dimensions and whose DOFs hold the macro strains:
node <ctlNode> coords 2 <lx> <ly> dofidmask 3 31 32 42
Boundary-crossing Delaunay edges are emitted as
latticeboundary2d <id> nodes 3 <inside> <partner> <ctlNode>
crossSect <m> mat <m> gpCoords 2 <gx> <gy>
width <w> thick <t> location <code>
where <partner> is the periodic partner of the outside endpoint and <code> is the 1..8 compass-direction shift code consumed by Lattice2dBoundary::giveSwitches.
2dTM writer - 2D mass transport¶
#@grid 2dTM dispatches to Grid::give2DTMOutput. Mirrors 2dSM with Voronoi/Delaunay roles swapped: nodes are Voronoi vertices, each latticemt2D element is a Voronoi edge, the cross-section width is the length of the dual Delaunay edge:
latticemt2D <id> nodes 2 <n1> <n2> mat <m> dim 1
thick <t> width <w> gpCoords 2 <gx> <gy>
The companion domain type is domain 2dMassLatticeTransport. 2D TM periodicity is not implemented - OOFEM has no Lattice2dBoundary_mt element. Setting #@perflag periodic with #@grid 2dTM still emits latticemt2D for inside edges; boundary-crossing edges are dropped.
Voronoi-vertex projection at boundaries¶
For 2D and for delete-mode 3D notches, the converter projects Voronoi vertices that bound a crossing Voronoi edge onto the nearest face of the rect / notch (Grid::project2DVoronoiVerticesToBoundaries and project3DVoronoiVerticesToNotches). This serves two purposes:
Transport nodes land on the surface. TM elements that cross a boundary become elements with one inside endpoint and one on-the-boundary endpoint, matching the 3D outer-boundary behaviour of
Prism::modifyVoronoiCrossSection.SM cross-sections are clipped at the surface. The dual Voronoi edge length used as
lattice2Dwidthreflects the part inside the specimen rather than extending past it.
Selectivity matters: only Voronoi vertices that bound a crossing edge are projected. Vertices “deep outside” (only connected to other outside vertices) keep their qhull positions and are filtered out at emission time. A blanket “clamp every outside vertex” pass would collapse far-out vertices onto the same rect corner and create coincident TM nodes.
For notches, projection only fires for #@notch ... delete boxes - material-mode notches are pure material overrides and don’t represent a physical surface, so projecting Voronoi vertices for them would distort polygons without a coherent boundary to snap to.
3D periodic mass transport - latticemt3dboundary¶
#@grid 3dTM with #@perflag periodic emits latticemt3dboundary elements for boundary-crossing Voronoi edges. This requires the OOFEM-side Lattice3dboundary_mt element registered under the latticemt3dboundary keyword (in src/tm/Elements/LatticeElements/). The converter test tests/3DPerQhullTM/ exercises the full pipeline.
Random-field pipeline (mesh.sh + genran)¶
OOFEM InterpolatingFunction records can read a regular-grid random field from random.dat. To keep the full pipeline reproducible from the test directory, tests that consume random.dat ship a random.in genran control file, and mesh.sh runs genran between the qhull and converter steps:
~/build/oofem/src/generator/generator.exec mesh.in
mv nodes.dat mesh.nodes
qvoronoi p Fv < mesh.nodes > mesh.voronoi
~/Software/genran.git/genran.exe random.in random.dat
~/build/oofem/src/converter/converter.exec control.in mesh.nodes mesh.voronoi
Both random.dat and the stat.dat summary genran writes are gitignored - they’re build artefacts, regenerated from random.in each run. See tests/3DCylinderQhull/ for the canonical example.
Unified 3dSM writer - conventions for matrix, fibres, and links¶
#@grid 3dSM dispatches to a single writer (Grid::give3DSMOutput) that handles plain SM, periodic SM, and periodic SM with fibres, driven entirely by #@perflag and the presence of #@fibre records. The previously separate 3dFPZ, 3dFPZFibre, 3dFibreBenchmark, 3dGopSha, 3dTension, 3dSphere, and 3dCylinder grid types have been retired; their analyses are expressed entirely as control.in templates using #@notch / #@sphereinclusion / #@cylinderinclusion for per-region material overrides and #@controlvertex / #@CTL<id> for support/load points.
For the fibre case the writer emits three classes of element, all of them lattice3D family, distinguished by their cross-section reference. The control file is expected to declare three latticecs records and three materials in this exact slot order:
Slot |
Cross-section / material |
Element kind |
Geometry source |
|---|---|---|---|
1 |
|
matrix |
polygonal |
2 |
|
fibre segment |
circular cross-section, |
3 |
|
|
|
The counts header in control.in must read ncrosssect 3 nmat 3 ... whenever fibres are present (drop to 1 for plain SM). The writer prepends ndofman and nelem automatically.
Reinforcement-node ids are offset by the raw Delaunay-vertex count N_DelV: the i-th reinforcement node is written as node (N_DelV + i). The periodic control node id is N_DelV + N_reinf + 1, available throughout the template via the #@CTLNODE placeholder.
Unified 3dTM writer - plain and periodic¶
#@grid 3dTM dispatches to Grid::give3DTMOutput, which handles plain and periodic mass transport on the Voronoi dual mesh. When #@perflag has any periodic axis, the writer pins the first emitted Voronoi vertex (bc 1 1), emits latticemt3Dboundary for lines whose endpoints straddle a periodic face (with image-side endpoints swapped for their mirror partners and a location 2 ... suffix), and appends a control node with ndofs 3 dofIDmask 3 1 2 3 bc 3 1 1 2 - available in the template via #@CTLNODE. Non-periodic behaviour is unchanged (compact 1..N vertex ids, no control node, no boundary elements). Per-element material is resolved through #@notch / #@sphereinclusion / #@cylinderinclusion just like the SM writer, and #@bodyload / #@couplingflag apply symmetrically.
Adding a new analysis type¶
The long-term goal is that supporting a new analysis type means writing a new control.in, not touching C++. When adding a directive:
Register it in the unified
Grid::readControlRecordsparser. The parser is shared; there is no per-pipeline parser to edit.Wire it into the writer(s) that should consume it - one of the qhull writers (
give3DSMOutput/give3DTMOutput), the T3D writers, or both.Add it to the qhull-consumed or T3D-consumed table above, depending on where you wired it up. A directive consumed by both pipelines goes in a shared row - none exist today, but nothing in the parser prevents that.
Keep mesh.in and the #@prism/#@cylinder/#@fibre etc. lines in control.in mirrored so that readers can see the mesh parameters in both files. Prefer extending give3DSMOutput / give3DTMOutput over adding a new grid type.