Result file layout¶
PhAST stores the result of one run in one file, result.vtkhdf. The file is
an HDF5 file in the VTKHDF layout, so ParaView opens it directly, and it
carries the arrays, the load history, the mesh, the materials, the loading
and the resolved input that a later analysis needs. This page is the
specification of that layout. Result files explains how to
write, read, check and draw them.
The layout has version 2.0.0. Version 2.1.0 adds optional items for forward
runs (section 8); every 2.0 file remains valid. The machine-readable schema
is src/phast/io/result/schema/phast_result_2.0.0.yaml (and 2.1.0). The
writer embeds the schema it used in the file, in meta/schema_yaml, and the
validator checks a file against the schema of its own version.
The keywords MUST, SHOULD and MAY have their usual meaning. In this page
n_steps is the number of stored steps, N the number of nodes and E the
number of cells.
1. Principles¶
One file per run, float64 state, one writer. The file is a valid VTKHDF 2.x file: ParaView 5.13 and later open it without a sidecar file. Groups beside
/VTKHDFhold what VTK has no place for. The VTKHDF specification permits such groups and ParaView ignores them.Arrays that ParaView shows are stored once, inside
/VTKHDF, in its own layout. The other groups refer to them by HDF5 soft links that point into/VTKHDF, never out of it. A stored file contains no virtual datasets: a partial copy can turn them into silent zeros, they cannot grow while the run appends, and HDF5 single-writer mode does not support them.Values are stored where the solver keeps them. History variables (the crack driving history
H, plastic and hardening variables, cohesive damage) are stored at the solver’s own location, per quadrature point when the solver keeps them per point, because averaging them makes a later recomputation inexact. Derived outputs (stress, strain, energy densities) are a choice.Every value carries its meaning: location, component order, units, and, for derived values, the method that produced them.
Views are generated from the file and are never written as second copies: the ParaView view is the file itself, and figures are made by a command.
Run folders and file names are short and ASCII. Files hold no absolute paths.
The unit system is mm, MPa, N and s (unit_system = "mm-MPa-N-s"), the same
as the solver. Energies are in mJ, forces in N, stresses in MPa.
Four fixed choices apply to every file.
The extension is
.vtkhdf, which ParaView opens directly.The file is not compressed while the run appends to it. The archive command (
phast result finalize --archive) rewrites a closed file with gzip level 4, shuffle and fletcher32.Stress and strain stay in the ParaView view, at every stored step, while the estimated size of the file is below 4 GB. Above that size the runner moves them to
/derived(section 3).The result file is a record of the run and not a checkpoint. It supports an exact recomputation of the derived fields, but it is not a substitute for a checkpoint from which a run is restarted.
2. Normative layout¶
result.vtkhdf
├─ attrs (root)
│ phast_schema = "phast.api.result" phast_schema_version = "2.0.0"
│ dim = 2 | 3 analysis = plane_strain | plane_stress | 3d
│ history_coupling = fresh | lagged profile = setup | run
│ run_id, parent_run_id (identity; the parent is the run this one continues or repeats)
│ history_update = hard_max | softmax | smooth_max | log_smooth | custom_subgrad
│ history_update_beta (smoothing parameter, or absent)
│ kinematics = small | finite mesh_varies = false (true: layout 2.1, section 8.6)
│ index_basis = increment (rows of /history are load increments, not solver steps)
│ unit_system = "mm-MPa-N-s" status = running | complete | time_limited | failed | recovered
│ created, finished (ISO 8601 with time zone)
├─ VTKHDF/ (VTK view and the canonical storage of shown arrays)
│ attrs: Version = [2, 2], Type = UnstructuredGrid | ImageData
│ UnstructuredGrid: NumberOfPoints, NumberOfCells, NumberOfConnectivityIds,
│ Points (N, 3), Connectivity, Offsets, Types
│ ImageData: attrs WholeExtent, Origin, Spacing, Direction
│ PointData/<name> UnstructuredGrid: (n_steps * N[, c]); ImageData: (n_steps, nz+1, ny+1, nx+1[, c])
│ CellData/<name> UnstructuredGrid: (n_steps * E[, c]); ImageData: (n_steps, nz, ny, nx[, c])
│ Steps/ attr NSteps; Values, PartOffsets, NumberOfParts, PointOffsets,
│ CellOffsets, ConnectivityIdOffsets, PointDataOffsets/<name>,
│ CellDataOffsets/<name>
├─ mesh/
│ cell_blocks/<block>/ attrs: cell_type (T3, T6, Q4, Q8, Q9, H8, TET4, TET10),
│ vtk_type, node_order ("vtk" or a permutation dataset)
│ cells index range of this block inside /VTKHDF cells
│ quadrature/ points (n_qp, ref_dim), weights (n_qp); attrs rule, point_order
│ grid/ ImageData runs: shape (nx, ny, nz), spacing, origin; attr index_order = "x fastest"
│ node_sets/<name> int64 node indices
│ face_sets/<name> (n, 2) int64 pairs (cell, local face)
│ graph/edge_index (2, n_edges) int64, both directions, from the cell edges of every cell type
├─ materials/
│ phase_id soft link to /VTKHDF/CellData/phase_id
│ phases/<id>/ attrs: name, elastic_model (isotropic | orthotropic | transversely_isotropic | stiffness),
│ constants (named attrs, or a "stiffness" dataset 6x6), fracture_law
│ (AT1 | AT2 | PFCZM | none), Gc, strength, ell, and the units of each
│ orientation optional soft link to /VTKHDF/CellData/orientation (E, 9), local axes as rows
├─ interfaces/<name>/ cohesive blocks: connectivity, quadrature, law, parameters, and their fields
├─ loading/
│ bc/<k>/ attrs: set, kind (displacement | traction | symmetry | periodic), components,
│ value = constant or the name of a column in /history
│ periodic/ attrs: axes, controlled, free, fixed_zero
├─ history/ one row per converged increment, whether or not fields were stored for it;
│ every column carries a units attribute
│ increment, time (accumulated, never step*dt), dt, load_parameter, load_factor
│ step_index row in /VTKHDF/Steps, or -1 when no fields were stored for this increment
│ damage_solved 1 when the damage equation was solved in this increment
│ cutbacks, stagger_iterations, mechanics_iterations, residual
│ reaction/<set> (n_inc, 3), signed; attrs components, units, sign_convention
│ macro_stress/<c>, macro_strain/<c>, mean_strain/<c>
│ energy/<term> open list, each with attr method: external_work, elastic, fracture, kinetic,
│ plastic_work, cohesive_dissipation, total, balance_residual, balance_relative
├─ state/ soft links to the arrays in /VTKHDF: displacement, damage, H, velocity,
│ acceleration, damage_lower_bound (d_prev), external_force (f_ext)
├─ quadrature/<block>/<name> raw per-point arrays, (n_steps, E_block, n_qp[, c]); only when n_qp > 1
│ and the field is not already shown per point in /VTKHDF
├─ derived/<name>/ optional arrays on another cadence than the stored steps: values
│ (n_stored, ...) and step_index (one entry per increment, -1 when absent);
│ never inside /VTKHDF
└─ meta/
config_resolved_yaml scalar UTF-8 string dataset, attr sha256
schema_yaml the schema this file was written against
attrs: software, software_version (git revision and a dirty flag), dependencies (torch, h5py, HDF5),
machine, slurm_job_id, command_line, solver_dtype, tolerances
2.1 Field attributes¶
Every field dataset has these attributes.
Attribute |
Values |
|---|---|
|
|
|
comma-separated names in stored order, for example |
|
a unit string; |
|
|
|
stress and strain under |
|
derived fields only: |
|
strain-like tensors: |
2.2 Conventions¶
Profiles: a
setupfile is arunfile before its first increment./VTKHDF/Stepsexists withNSteps = 0and empty step datasets, so the same file grows into a run by appending. ParaView shows its mesh and static arrays.Categorical columns are stored as int8 codes with an attribute
codes, a JSON object that maps each code to its name, so that every reader sees numeric arrays.Write order and interruption: the rows of a new step are appended first, then the
Stepsoffsets andValues, thenNSteps, then thehistory/rows, and then the file is flushed. A file that is killed at any point of an increment opens in ParaView with the previous complete steps.Points are stored with 3 coordinates; 2D runs have z = 0.
Vectors in
/VTKHDFhave 3 components and 2D runs store z = 0. ParaView loads a 2-component vector but warps it incorrectly and gives no warning.Result.field()returns arrays in the native dimension.Symmetric tensors use one order everywhere: in 3D
xx, yy, zz, yz, xz, xy; in 2Dxx, yy, xy, withzzstored as a separate field (stress_zz) for plane strain. Strain shear is the engineering shear (gamma = 2 epsilon). A solver that uses another order converts when it writes. ParaView reads these arrays as plain multi-component arrays with no tensor role and no component names, so VTK tensor filters must not be applied to them directly.Quadrature points follow the
point_orderattribute, for exampleeta outer, xi inner, minus then plusfor Q4.The time axis inside
/VTKHDFis shared by every array in it. An array is inside/VTKHDFonly if it exists at every stored step. Repeated offsets are used only for static data (mesh,phase_id,orientation).Static arrays in ImageData have a leading time axis of length 1 and zero offsets, because ParaView requires the time axis. In a
setupfile (NSteps = 0) they have no time axis: ParaView reads a file with fewer than two steps as data without steps and refuses arrays that carry one. For the same reason ParaView 5.13 shows no arrays of an ImageData run with exactly one stored step. Such a file is valid andphast.load_resultreads it, and the validator warns about it.Field names contain no
/or..
2.3 Where each value is stored¶
Cell type |
Points per cell |
Stored |
|---|---|---|
T3, TET4 |
1 |
per cell in |
Q4 |
4 |
history per point: |
H8 |
8 |
history per point: |
T6, Q8, Q9, TET10 |
3 to 9 |
as Q4 |
Cohesive |
points along the interface |
|
Interface cells (/interfaces/<name>/connectivity, cell_type COH2 in 2D and COH4 in 3D) list the nodes of the first side
first and the nodes of the second side after them. The first side is the side the solver calls top (CohesiveElement.nodes_top: the
side the normal n = (-t_y, t_x) points to); a COH2 row is top0, top1, bottom1, bottom0, a COH4 row the four nodes of the top face
and then the four of the bottom face in the same order. A drawing of an interface uses its first side. The 2D order is tested against the
elements of insert_cohesive_layer; the 3D order is the same rule and has no test on solver elements yet.
phast.io.result.interface_connectivity builds the 2D rows from CohesiveElement records.
History variables per point go inside /VTKHDF as multi-component cell
arrays, so that they share the offsets and the truncation rule of the other
arrays and survive a copy of /VTKHDF alone. Derived tensors per point (stress
at 8 points with 6 components) go to /quadrature/, and their cell means go to
/VTKHDF with derived_method set.
2.4 History variable and damage¶
The root attribute history_coupling states how damage and history are
coupled: fresh (the history is updated from the current displacement and the
damage is then solved, within the same increment) or lagged (the damage of
increment i uses the history of the previous mechanics solve).
His always the history after the increment, the state carried forward.H_damage_inputis the history that the damage solve of the increment used. It MUST be stored whenhistory_coupling = laggedor when the damage is solved only every k-th increment. It MAY be omitted forfreshcoupling with a damage solve in every increment, where it equalsH. Readers never infer one from the other.history/damage_solvedmarks the increments in which the damage equation was solved (explicit dynamics withdamage_every > 1skips some).
3. Output settings¶
output:
result:
every: 1 # stored steps; 1 = every increment
fields: [displacement, damage, H, stress, strain]
derived_location: cell # cell | quadrature_point
derived_every: 1 # 1 = in the ParaView view; N > 1 = in /derived
size_limit_gb: 4 # above the estimate, derived fields move to /derived
layout: 2.0 # 2.1 adds the items of section 8 that the runner has data for
crack_tips: false # true or {threshold: 0.5, direction: x}; implies layout 2.1
Each simulation type supplies defaults, the user overrides them, and the
writer resolves the final settings into meta/config_resolved_yaml.
Simulation type |
Stored by default |
|
|---|---|---|
brittle phase field, T3 |
u, d, H, stress, strain |
cell (exact) |
brittle phase field, Q4, H8 |
u, d, H per point, stress, strain |
cell, volume-weighted mean |
plasticity |
u, d, plastic variables per point, stress |
quadrature_point |
cohesive interfaces |
u, interface opening, cohesive damage per point |
interface_point |
explicit dynamics |
u, v, a, d, H |
cell |
periodic voxel cells |
u, d, H per point, stress, strain |
cell; macro stress and strain in |
The writer refuses settings that would drop a history variable or a state variable, and it reports when a per-point request is satisfied exactly by cell values (one-point cells). The sizes below show why derived fields can move out of the ParaView view: a 2D mesh of 100,000 T3 cells stores 2.4 MB of primary state per step and 4.8 MB of stress and strain cell means per step, and a 48 by 48 by 48 voxel cell with H at 8 points stores 10.8 MB and 10.6 MB.
The 2D YAML runner (phast run, implemented in
src/phast/io/config/result_output.py) uses the settings as follows.
output.resultistrueby default and writes<output_dir>/result.vtkhdfwith the defaults above. A mapping overrides the keys above, andfalsewrites no file. Unknown keys, andfieldslists that omitdisplacement,damage,H(and, for explicit dynamics,velocityandacceleration), are refused.every: the quasi-static default is 1 and the explicit-dynamics default isoutput.h5_every. Fields are stored at increments0, every, 2*every, ..., at the last increment and at a time-limit stop. Wheneveryis not given and the stored state alone is estimated abovesize_limit_gb, the runner multiplieseveryuntil the estimate fits and prints the value.derived_every: N > 1stores the derived fields in/derivedevery N-th increment.derived_location: quadrature_point(Q4 only) also writesquadrature/Q4/stressandquadrature/Q4/strainand needsderived_every: 1.stress_zzis written for plane strain with the splitsisotropic(nu*(stress_xx + stress_yy)),amor,star_convex,spectralandspectral_stress. It is the out-of-plane stress of the same degraded law witheps_zz = 0, obtained from the 3D Lame parameters and a third principal strain of zero. Understress_degradation: fullthe isotropic expression applies. Plane stress hassigma_zz = 0, so it is not stored.fieldsmay also listdamage_lower_bound(state, node, unit1): the damage at the start of the increment, which is the lower bound that the irreversibility constraint applied in its damage solve. It differs fromdamageby the change of the increment, and it cannot be recovered from the stored steps whenevery > 1. It is not in the default list.H_damage_inputis written for explicit dynamics withdamage_every > 1: the history used by the most recent damage solve.Quasi-static runs use a pseudo-time of one unit per load increment (
history/timeandhistory/dthave unit1).load_parameteris the prescribed displacement of the first non-zeroprescribecondition (mm), or the load factor when there is none;load_factoris stored separately.Explicit runs store the accumulated physical time (s) and the time step that each increment used. The load factor of an increment is the value the run applied. Under
adaptive_dtthe runner evaluates the load ramp at the nominal timestep * dt, so the storedload_factorfollows that nominal time whilehistory/timefollows the accumulated step sizes.loading/bc/<k>(key<index>_<node set>) carriesset,kind,componentsandvalue. Prescribed displacements and tractions havevalue = load_factorand the condition’s value asscale. In explicit runs with a time-dependent traction ramp each traction hasvalue = traction_factor/<k>, a history column with its ramp factor.pf_dirichletentries (prescribed damage) go toloading/damage_bc/<k>.history/reaction/<set>is written for every node set with a fix, prescribe, symmetry or rigid-connector condition and foroutput.reaction_node_set. In explicit runs it excludes inertia and damping forces, which itsmethodattribute states.For runs with applied tractions,
history/energy/external_workis the accumulated work of the applied traction force with the left-point rulef_n . (u_(n+1) - u_n).balance_residualis the total energy minus the total energy after the first increment minus that work, andbalance_relativedivides it by the largest of the three magnitudes.The runner does not write the writer’s own CSV copies of the history (
sidecars=False): its existing CSV files keep their names and columns.
4. Writer rules¶
The writer creates
<name>.partial. After the final close it reopens the file, validates it, renames it atomically and writes<name>.sha256. Write on local disk. The writer warns when the target folder looks like a synchronised folder, because a synchronisation client can lock or partly upload an open HDF5 file.Every group, attribute and the resolved configuration are created before the first increment.
Increments are appended one at a time: field rows first, then the
Stepsoffsets andValues, thenNSteps, then thehistory/rows, thenflush(). A reader never sees offsets for a step whose rows are missing.File format bounds are
libver=("earliest", "v114"), never"latest". The namev114exists only in an h5py built against HDF5 1.14 (the wheels of h5py 3.13 and later); older builds stop with aKeyErrorwhen the file is opened for writing. With h5py 3.16 the latest bound writes HDF5 2.0 structures that the HDF5 1.13 to 1.14 library inside ParaView may not read, and a killed writer leaves a file that does not open.Chunks: one stored step per chunk along the time axis (
NorErows for UnstructuredGrid, one full grid for ImageData);history/columns in chunks of 1024 rows, resizable along time.Filters while running: none. The archive command applies gzip level 4, shuffle and fletcher32. Scale-offset and other lossy filters are never used.
Every dataset is created with an explicit dtype. Fields with
kind = stateorkind = history, mesh coordinates, material values andhistory/columns are float64. Fields withkind = derivedMAY be float32, because they can be recomputed from the float64 state. Indices are int64 and phase ids uint8. A reader that needs float32 converts while reading; no second copy is written.Time-limit stop: the signal handler only sets a flag. The run loop finishes the current increment, flushes, sets
status = time_limitedandfinished, closes the file, and the process exits with code 11 (TIME_LIMIT_EXIT_CODE).Locking is best effort. Other processes do not open the file while it is running; opening a file that is being written can fail with errno 35.
Portability on macOS, Linux and Windows: readers depend only on h5py and NumPy; only the filters built into every HDF5 library are used (none, gzip, shuffle, fletcher32), never plugin codecs; files hold no absolute paths (references to other files are relative paths or SHA-256 values); all text is UTF-8.
Resume (
ResultWriter.resume): a run killed or stopped by the scheduler continues in the same file from a checkpoint increment. The source is the.partialfile of a killed run or the closed file with statustime_limited,failedorrecovered; a file closedcomplete, a changing mesh (mesh_varies), anotherrun_idor mesh, another field list, a checkpoint increment that is not among the history rows, and stored steps missing below it are refused (ResumeRefused) before anything is changed. History rows after the checkpoint increment, the stored steps that only they refer to, and the long tables (crack_tips,substeps) after it are cut, the file is renamed to.partial,statusbecomesrunning,finishedandstatus_reasonare removed, and the resume is recorded in the root attributeresume_countand the JSON listresumesin the attributes ofmeta(time, increment, rows and steps kept and cut, previous status and SHA-256, scheduler job number). Both are outside the validated vocabulary and ignored by the validator. The bytes before the cut are kept as<file>.pre-resume-<k>.bak. A finished file equals an uninterrupted one in every dataset and attribute but these records and the wall-clock history columns. The code that records checkpoints must publish a checkpoint only after the history row of its step is in the file, so that a checkpoint is never ahead of the file.
5. Validator¶
phast result validate <file> reports findings of three levels.
ERROR: a missing required group or attribute; an unknown
phast_schema_versionmajor; an array shorter than the offsets implied byNSteps; a field withoutlocation,components,unitsorkind; a dangling soft link (ParaView drops the array that follows a dangling link without a message); astateorhistoryfield, coordinate or material value that is not float64; a dataset that uses a plugin filter; an absolute path stored as a reference; a virtual dataset;status = runningon a file that is not on scratch storage.WARN: history stored as a cell mean while the solver keeps it per point;
history/step_indexwith gaps that the configuration does not explain; a vector in/VTKHDFwith 2 components.INFO: optional groups that are absent.
The precise rules are as follows. The validator section of the schema holds
the lists.
Array length: a time-indexed array MUST hold at least the rows implied by the offsets of the
NStepsstored steps (offset[NSteps-1] + Nor+ E; for ImageDataoffset[NSteps-1] + 1along time). Rows beyond them, left by an interrupted append, are allowed.Steps/Valuesand every offset dataset hold at leastNStepsentries.Field attributes:
location,components,unitsandkindare required on every field (section 2.1). A field is every dataset in/VTKHDF/PointDataand/VTKHDF/CellDataand any other dataset that carries akindattribute. In/VTKHDFthe number of names incomponentsequals the number of stored components.Precision:
phase_idmay be uint8 or int8. In/history, floating columns are float64. Integer columns are allowed only for counters, indices and flags (increment,step_index,damage_solved,cutbacks,stagger_iterations,mechanics_iterations,mesh_index) and for categorical columns withcodes. Every/historydataset hasunits.References: string attributes and datasets whose names end in
path,manifestorfileare file references. An absolute POSIX, Windows drive, UNC orfile:value is an ERROR. Soft-link targets and HDF5 paths that resolve inside the file are not file references.Status: the validator cannot see where a file lives.
status = runningis an ERROR unless the caller states that the file is on scratch storage (--running-ok).Also an ERROR:
mesh_varies = truein a 2.0 file (2.1 allows it, section 8.6);profile = setupwithNStepsother than 0;history_coupling = laggedwithoutH_damage_inputin/VTKHDF; a soft link that points outside/VTKHDF; an external link.history/step_indexgaps are explained when the stored increments are evenly spaced (the cadenceoutput.result.everyfrommeta/config_resolved_yaml, or the first gap when the configuration does not state it). The last stored increment may come earlier.
Versioning: MAJOR.MINOR.PATCH. Minor versions only add optional items, and
readers accept any minor version of their major. The validator checks a file
with the schema of its own phast_schema_version (2.0.x with 2.0.0, 2.1.x
with 2.1.0, a later minor version with the newest schema it knows), so a 2.0
file receives the same findings that it received before 2.1 existed.
Recovery: phast result recover <file> works on a copy. It clears the
open-for-write flag (h5clear -s, or a read-write open with h5py, which is
enough for files written with the libver bounds of section 4 because their
superblock has no such flag), trims every time-indexed dataset to the longest
consistent step count, and sets status = recovered. The step count kept is
the smallest of NSteps, the length of every Steps dataset and the number of
steps whose rows are complete in every array. history/ keeps the rows up to
the last increment whose fields are kept, and derived/ and the long tables of
section 8 are trimmed to match. The root attribute recovered_from_steps
records NSteps before trimming, and finished the time of recovery.
6. Reader¶
phast.load_result(path) accepts a run directory, a layout-2.x file of any
name, or a run directory of the older formats. The methods that describe a
layout-2.x file are result.mesh_arrays(), result.step_index(),
result.materials(), result.boundary_conditions(),
result.quadrature(name, step), result.field_attributes(name),
result.history_attributes(), result.history_columns(),
result.source_files(), result.file_metadata(), result.steps(),
result.times() and result.n_steps(). Layout 2.1 adds result.mesh_versions(),
result.step_mesh(), result.crack_tips(), result.validation() and
result.initial_conditions(), and an optional step argument of
result.mesh_arrays(step) (section 8). The field_attributes,
history_attributes and history_columns methods return empty results for the
older formats, which store no attributes. The other methods listed here raise
ResultLoadError for the older formats.
Reader conventions:
In a directory,
result.vtkhdfis read in preference to any older store.run_manifest.jsonandrun_metadata.jsonare optional when it exists. Without either JSON file,metadata()returns the root attributes and themeta/attributes.file_metadata()returns them in every case.stepis the index of a stored step in/VTKHDF/Steps; negative values count from the end. Rows are located through the offsets.NStepsis authoritative, so rows of an incomplete step are never returned.result.steps()lists the valid values.field()returns 2D vectors with 2 components. UnstructuredGrid arrays are(N[, c])or(E[, c]), and history per point is(E, n_qp). ImageData arrays are(nz + 1, ny + 1, nx + 1[, c])or(nz, ny, nx[, c]), x varying fastest. Static arrays are returned at any step. A/derivedarray is returned for a stored step only whenhistory/step_indexmaps the step to an increment that has a row; otherwiseResultLoadErroris raised.field_names()lists reference names (Happears ashistory_field, as for the older formats); the stored names remain valid arguments.history(name)keeps the CSV files under their names and adds, for names no CSV provides, tables built from/history:history(columns of a group joined with_, vector columns split by component, for examplereaction_top_y),energy, and the long tablescrack_tipsandsubstepswhen the file has them.history_columns()returns the/historydatasets as arrays.
7. Figures¶
phast figures <run> [-o OUT] [--steps first,peak,last] [--dpi N] [--fields ...] [--deform-scale F|auto] (also python -m phast.io.result.figures) draws
the standard figures of a layout-2.x result from the file alone, with no
run-specific code. It writes, below 2000 px on both axes, a setup sheet, field
and component sheets at the chosen stored steps (default: the first, the step
nearest the largest load-like history value, and the last), a sheet of damage
across those steps and an evolution sheet, with manifest.json (kind, steps,
fields and the SHA-256 of the source files) beside them. Field maps are laid out
about 1900 px wide; when they need more than two rows, state and history fields
and derived fields go on separate sheets. What a file does not store is stated
in a footnote and is not raised as an error. The figures use the STIX serif
font.
8. Layout 2.1 additions¶
Version 2.1.0 adds optional items for forward runs. Its schema is a full copy of
the 2.0.0 schema plus the items below, so a file embeds the complete schema it
was written against. Nothing in 2.0 changes: every 2.0 file is a valid 2.1 file,
except that mesh_varies = true is accepted only from 2.1 on.
8.1 Version rules¶
The writer writes 2.0.0 until the first item of this section is used. At that point it sets
phast_schema_version = "2.1.0"and replacesmeta/schema_yamlby the 2.1 text, both before the item is written, and records the items in thelayout_2_1_featuresattribute of/meta. A file that is interrupted afterwards is therefore checked by the rules of its own version.The validator uses the schema of the version of the file (section 5). The notes about optional groups (INFO) of a 2.1 file include
/initial_conditionsand/meta/validation.The long tables below
/history(crack_tipsandsubsteps) have one row per event, and the datasets of one table have the same length. Their rows refer to rows of/history/increment. The caller adds the rows of an increment afterResultWriter.appendof that increment and then flushes, so that an interrupted file never holds table rows for an increment without a history row; a run killed between the two loses only the table rows of its last increment.
8.2 Crack tips and crack speed¶
history/crack_tips/ has one row per tip and increment, in increment order
(the tips of one increment in order of tip_id). An increment without a crack
has no row.
Dataset |
Type and unit |
Content |
|---|---|---|
|
int64, |
row of |
|
int64, |
0, 1, … within the increment |
|
float64 |
|
|
float64, |
damage value that defines the tip, in (0, 1] |
|
float64 |
derived; added when the file is closed |
The speed is derived from the stored positions. Each coordinate of a tip is
smoothed along the rows of that tip with a Savitzky-Golay filter (window 5,
order 2; unchanged when the tip has fewer than five rows). The speed of a row is
the length of the backward difference of the smoothed position divided by the
difference of history/time between the two rows, and the first row of a tip
has no speed (NaN). Smoothing the position before differentiating keeps the jump
of one element width between snapshots from appearing as a speed spike. The
dataset carries fit_method, fit_window, fit_polyorder and derived_from;
its unit follows the time unit (mm per unit of pseudo-time in quasi-static
runs, whose history/time has unit 1). A file that was closed before the
speed was written has no speed dataset. Result.crack_tips() then computes it
with the same fit and says so (speed_source). Recovery removes speed when it
trims rows.
The 2D runner finds the tip with physics.fracture_mechanics.find_crack_tip
(edge interpolation of the isoline d = threshold, farthest point along x,
-x or y). It follows one tip on T3 cells; the table itself holds any number
of tips.
8.3 Per-solve records¶
history/substeps/ has one row per damage solve: increment, call (the index
of the solve within the increment, from 0) and the columns of the damage
solver’s record: damage_increment, cg_iterations, cg_residual,
cg_rhs_norm, cg_initial_residual, cg_residual_reference,
cg_relative_residual, cg_converged, kkt_max, relative_kkt_max,
projected_relative_residual, projected_relative_residual_max, damage_min,
damage_max, min_increment, bounds_satisfied, irreversibility_satisfied,
walltime_s (s), cooldown_remaining, retry_counter, amg_eligible,
rebuild_attempted and rebuild_succeeded. Counters are int64, flags are int8
(0 or 1) and the other columns are float64. Text values
(requested_preconditioner, effective_preconditioner, amg_skip_reason,
fallback_target, fallback_reason) are int8 codes with a codes attribute
(section 2.2) that grows with the names that occur.
The writer and the validator support this table (ResultWriter.append_substep).
The 2D runner does not write it, because the damage solver does not report the
record of each solve.
8.4 Velocity conditions, initial conditions, wave speeds¶
loading/bc/<k>acceptskind = velocity(withcomponentsandvalue, a velocity inmm/sor the name of a history column, andvalue_units). For the velocity-impact ramp the runner records the prescribed velocity:valueis the boundary value times the impact velocityv0in mm/s,ramp_type = velocity_impact,t_ramp(s), anddisplacementdescribes the displacement that the solver imposes,u = v0 t^2 / (2 t_ramp)fort < t_rampandu = v0 (t - t_ramp / 2)afterwards. The validator requireskindto be one ofdisplacement,traction,symmetry,periodic,velocity.initial_conditions/holds the state at the start of the first increment, on the initial mesh: datasets nameddisplacement,velocity,acceleration,damage,Hordamage_lower_bound, each with the field attributes of section 2.1 (kind = stateorhistory, float64). The attributespecification(JSON) of an array records how it was set. Fordamagethe runner records the pre-seeded regions (preseed_notch_nodesetsandpreseed_damage). A pre-seeded notch is imposed as a large history value and one damage solve, so the stored damage is the state after that solve. The validator checks the length against the mesh.materials/phases/<id>may carryc_p,c_sandc_R(unitmm/s, listed in theunitsattribute) next torho(t/mm^3): the P-wave speedsqrt((lam + 2 mu) / rho)with the Lame parameter of the analysis (plane stress:E nu / (1 - nu^2)), the shear-wave speedsqrt(mu / rho)and the Rayleigh speedc_s (0.862 + 1.14 nu) / (1 + nu). For an isotropic phase withEandnuthe validator checks each against these formulas to a relative difference of 1e-6.
8.5 Validation results¶
meta/validation/ holds the comparisons that a run was judged by, one row each:
name (text), value (float64; NaN for a condition that is either met or not),
tolerance (float64; NaN when there is none), passed (int8, 0 or 1) and,
optionally, units and description (text). The attribute overall_passed is
1 when every row passed (the validator checks that it agrees with the rows), and
source names the summary that the rows come from by a relative file name. The
group is written once, at any time before the file closes
(ResultWriter.write_validation).
8.6 Meshes that change during a run¶
mesh_varies = true is allowed for UnstructuredGrid runs. The geometry follows
the time-dependent layout of VTKHDF, with one part for each mesh version.
NumberOfPoints,NumberOfCellsandNumberOfConnectivityIdshave one entry per mesh version.Points,Connectivity(point numbers local to the version),Offsets(cells + 1 entries per version, each starting at 0) andTypesare the versions concatenated.Each stored step names its mesh:
Steps/NumberOfParts = 1,PartOffsets[t]is the version, andPointOffsets[t],CellOffsets[t]andConnectivityIdOffsets[t]are the first point, cell and connectivity entry of that version. Steps on the same version repeat the same offsets. Array rows are stored per step as before. The offset of a step inPointDataOffsetsandCellDataOffsetsis the number of rows stored before it, and a step holds the points or cells of its version. An array that is static on each mesh (phase_id,orientation) holds the rows of each version once, and the offsets of the steps point at the rows of their version.history/mesh_index(int64,1) gives the version in force at each increment, including increments that store no fields. It is non-decreasing and agrees withPartOffsetsat the stored steps. A mesh is registered at the increment where it first applies, and the fields stored at that increment are on the new mesh.Version 0 uses the groups of section 2 (
mesh/cell_blocks,mesh/node_sets,mesh/graph, …). Versionk >= 1has the same sub-groups inmesh/versions/<k>/, with attributesn_points,n_cellsandfirst_incrementand the maps to the previous version:cell_parent(E_k,)(the cell of versionk - 1that each cell came from, -1 for none),node_origin(N_k,)(the node of versionk - 1, -1 for a new node) andnode_parents(N_k, 2)(the two nodes of versionk - 1whose mean is a new node, -1 for existing nodes). A conforming bisection or a quadrilateral split produces these maps directly (refinement_mapsconverts the dictionaries ofphast.solvers.adaptive).A non-conforming mesh stores its hanging-node constraints in
hanging_nodes/of its version (mesh/hanging_nodesfor version 0):constrained_nodes(n,),master_nodes(n, m)andweights(n, m)(float64, each row summing to 1). The value at a constrained node is the weighted sum of the values at its master nodes, and a master node is not itself constrained.Arrays of a fixed shape cannot follow the mesh. With
mesh_variesthere are noquadrature/,derived/orinterfaces/arrays, and every field is stored inside/VTKHDF(per-point history as a multi-component cell array, section 2.3). An ImageData grid is fixed and cannot change.The validator checks the counts and offsets of every version, the maps (ranges, shapes, one origin per node), the hanging-node constraints, the node sets against the node count of their version,
mesh_index, and that the rows suffice for the mesh of each stored step. Recovery trims the arrays to the rows of the mesh version of each kept step.The reader returns each stored step on its own mesh:
result.field(name, step)has the size of the version of that step,result.mesh_arrays(step)returns that version (withmesh_versionand the maps),result.step_mesh()lists the version of every step andresult.mesh_versions()lists the versions.
ParaView 5.13 shows such a file with the points, cells and arrays of each step. The 2D YAML runner does not refine the mesh, so it stops the file at a mesh change and continues with its other outputs. The writer and the format are what a solver loop that refines the mesh needs.
8.7 Not part of layout 2.x¶
One-dimensional runs (line cells) are outside the layout:
dimis 2 or 3. The writer refusesdim = 1, one-column points and line cell types, and the validator reports a file whosedimis 1 with the message “one-dimensional runs (line cells) are outside layout 2.x: dim must be 2 or 3”.J-integral and stress-intensity columns of
/historyare not defined. They can be added as optionalhistory/columns when a run records them.
8.8 Settings of the 2D runner¶
output.result.layout: 2.1 (default 2.0) records the initial state with the
pre-seeded damage, the wave speeds of the material and, for the velocity-impact
ramp, the prescribed velocity. output.result.crack_tips: true or {threshold: 0.5, direction: x} writes the crack-tip table of a T3 run and implies 2.1. The
numerical results of a run do not depend on these settings.