|
SERiF v0.0.1a
Stellar Evolution and Rotation in Four Dimensions
|
Files | |
| archive.hpp | |
| components.hpp | |
| equations.hpp | |
| model.hpp | |
| numerics.hpp | |
| physics.hpp | |
| record.hpp | |
| serialize.hpp | |
| solver.hpp | |
| top.hpp | |
An archive contains the information supplied to pack(...). Each component describes its own scientific data through an external Serializer<T> specialization. The archive handles files, encoding, indexing and provenance. The model, computational graph and numerical algorithms do not depend on this module.
write and reader operations return std::expected<T, Error>. An error has a component path and an explanatory message. Check that result before using it. The sandbox demonstrates one way to keep error handling separate from the scientific workflow.
pack(solution) is valid too. A result supplies its available model, accepted physical coefficients, diagnostics, normalization factors/reference scales, and supported Newton checkpoint. It cannot supply a mesh, a preconditioner recipe or settings that the result never retained. Passing the configured discretization and solver options preserves those additional inputs. Custom prescribed inputs, fixed displacement and assembly options should also be supplied when used.
pack immediately owns encoded data. Changing a model or destroying its resources after packing does not change that snapshot. named("comparison_star", value) chooses a name when multiple objects have the same default role. Conflicting objects with the same name are an error; identical represented objects are deduplicated. An unregistered type appears as an unsupported entry containing an explanation. A partial archive remains useful for analysis, but does not claim full reconstruction.
Opening reads the fixed header and the ledger, not the coefficient or mesh payloads. node(path) returns an owned component subtree for inspection or a specialized reconstruction routine. read<T>(path) then applies Serializer<T>::load. Every selected section is length-checked and checksum-checked before use. validate() reads and checks all sections. A Reader owns an open stream and should not be shared between concurrent reads without external synchronization.
SERiF compatibility defaults to the same major version. Set ReadOptions::version_tolerance to exact to require the same version string or any for explicit recovery/inspection across major versions. These choices do not disable file-format checks, component-schema checks, coordinate validation or MFEM representation checks. The separate file-format version is currently 1.
Choose .format = Format::text for readable named sections, one number per line in array sections, and quoted strings. Text uses the classic locale and max_digits10; it preserves finite double values, including signed zero. Nonfinite doubles use bits: followed by their 16 hexadecimal IEEE-754 digits, preserving NaN payloads too. This is a sectioned scientific text format, not a rectangular CSV table.
The default binary encoding stores numerical arrays as little-endian fixed-width values. It never writes C++ structure padding, addresses, native size_t layout or virtual tables. Both formats represent the same component tree and preserve binary64 coefficient values exactly. A full model description is distinct from bitwise process memory: derived caches are rebuilt, and different hardware or library versions may produce different subsequent arithmetic.
tools/read_serif_archive.py is a standard-library-only data inspector for either format:
The first command lists available fields and their offsets. The second exports ordinary index,value CSV for analysis. This inspector does not instantiate physical models or apply the C++ model loader's version and formulation compatibility policy.
The first 512 bytes are an ASCII header beginning with SERIF_ARCHIVE 1. Its fields specify encoding, SERiF version, UTC write time, ledger offset/length, data offset, total file length, entry count and ledger checksum. Remaining header bytes are whitespace. All offsets are absolute byte offsets, including in text mode; files are always opened in binary stream mode to avoid newline translation.
The ledger starts immediately after the header. It has one row per component or field:
Paths and schema names are quoted. Offsets and byte lengths have 20 decimal digits, allowing the complete ledger size to be known before payload positions are assigned. Rows are sorted by path; parent components precede their fields. A component-directory row has no payload. Text-mode data sections have visible # path labels preceding the indexed payload. Binary payloads are contiguous.
| Kind | Representation |
|---|---|
| 0 | Component directory |
| 1 | Boolean, one byte in binary |
| 2 | Signed 64-bit integer |
| 3 | Unsigned 64-bit integer |
| 4 | IEEE-754 binary64 real |
| 5 | String bytes, UTF-8 by convention |
| 6 | Binary64 array |
| 7 | Signed 64-bit integer array |
| 8 | Unsigned 64-bit integer array |
Binary numeric values occupy eight bytes except booleans. Counts give array extents or unescaped string byte lengths; scalar counts are one. fnv1a64-v1 fingerprints detect accidental corruption and identify exact serialized resources. They are not cryptographic authentication. Readers enforce configurable index, payload, entry-count and nesting limits; raise those limits explicitly for unusually large models.
Writes use an exclusively created temporary file beside the destination. Only a complete, successfully closed file replaces the destination. A failed write retains the previous archive. This prevents partial publication; it is not a guarantee against filesystem or hardware failure after publication.
Every archive includes provenance: UTC time; SERiF version generated directly from Meson's project version through serif/config.hpp.in; compiler/build information; host OS, kernel, machine and architecture information where exposed; available library versions; author, comment and user metadata. The MFEM component adds the backend configuration, MFEM source information and exposed dependency versions. Runtime MPI information is queried only if MPI is already initialized. Missing platform facilities are not guessed.
Metadata keys use the same simple field-name alphabet as other component names: letters, digits, underscores, periods and hyphens. Author, comment and metadata values can contain Unicode, punctuation and line breaks. Record the unit convention explicitly: physical dimension tags do not identify SI, CGS or a particular code-unit conversion.
MFEM discretizations embed native reference meshes by default, including the separate logical mesh when deformation needs it. Use save_discretization(finite_elements, {.embed_mesh = false}) to retain mesh identities and representation metadata while obtaining native mesh text from a caller-supplied resolver during restoration. A mismatched supplied mesh is rejected. The mesh payload itself uses enough decimal digits to recover stored coordinates; coefficients are saved in their original global/reduced numbering, with space, support and signed element-numbering information for validation.
An observer may return SolveControl::stop. The request takes effect at a completed accepted boundary. result.checkpoint() contains the accepted state, original convergence reference, next line-search step, carried normalized correction and cumulative accepted history. It contains no live Jacobian, inverse or computational graph cache.
Save the checkpoint together with the model/discretization recipe, normalization, solver options and any additional physical inputs. After loading, resume_equilibrium(...) reconstructs and qualifies that state, rebuilds preparation, checks the represented residual against the saved boundary, and continues with the original convergence reference and iteration budget. Increasing the iteration budget is an explicit solver-option change.
Exact accepted-boundary checkpoint support currently covers StandardNewton, NormalizedResidualMetric and FGMRES. Other strategies continue to solve normally but explicitly report that their private history is not checkpointable. This is not yet a continuation-controller checkpoint. Restoring coefficients alone is a useful new initial guess, but does not preserve Newton history.
See tests/sandbox/serialization_sandbox.cpp for the complete save/load/resume example, COMPONENTS.md for the supported scientific descriptions and custom-type rules, and the MFEM serializer notes for backend reconstruction requirements.
Specialize Serializer<T> with a stable key, save(const T&) -> Node, and load(const Node&) -> Result<T>. object, field, read_object and member<T> compose existing serializers. Plain configuration aggregates can use StructSerializer<T> and an explicit member_field list. Keep physics validation with the component; existing validate() methods are reused during reconstruction. Schema versions default to one; an explicit Serializer<T>::version selects another supported version. When a schema changes, increase that version and add an explicit migration from the inspected old Node if needed. New formats do not require changing component serializers.
The TOP exporter is a separate format writer in this serialization module. It accepts a stellar model together with the mesh and accepted physical state that realize it. The MFEM sampling adapter remains an explicit include:
StellarModel describes physics; it deliberately does not contain a mesh or solution. No normalization factors should be applied before export: accepted state coefficients already represent physical quantities. Exporting an accepted state does not certify equilibrium convergence. The complete sandbox exports only after its solve converges:
Use --top-radial-points, --top-degree, --top-angular-points, and --top-azimuthal-points to control export sampling independently of the FE mesh. Defaults are 64 Chebyshev radial points, even degree 16, 48 Gauss-Legendre angular points and 8 azimuthal samples. The parent directory must exist and the destination must not already exist. A failed export preserves any existing destination, and a successful directory is published only after every file has been written.
The current adapter supports the density formulation with the Polytrope EOS, BarotropicDensityBalance, PrimalPoissonBalance, FixedAngularMomentum, a solved uniform angular velocity and a zero-pressure moving surface. Unsupported physics, finite-pressure boundaries, invalid geometry and material negative densities return errors. A future realistic-EOS/finite-pressure interface needs a corresponding TOP reader; this writer does not pretend those models are vacuum polytropes.
The adapter reconstructs deformation using the existing geometry graph, samples the actual deformed finite-element surface and density, and accounts for nonconforming true DOFs. It uses the prescribed rotation axis and center. The source mesh and state remain unchanged. Central density is sampled and total mass is integrated from the state. Enthalpy is derived from the EOS only while writing TOP's external format; it does not become a SERiF primary field.
TOP represents a 2D, equatorially symmetric background. The exporter averages over azimuth and equatorial reflection at common surface-fitted radial coordinates, and projects both geometry and enthalpy onto normalized spherical harmonics. This is a measured approximation to the 3D FE representation. The returned TopExportReport and metadata.txt record source/projected radii, physical scales, symmetry and surface-projection discrepancies, sampled enthalpy angular truncation, surface enthalpy, mapping validity and tiny negative-density roundoff clamps. Defaults reject symmetry and surface-projection errors above 1e-3 and surface enthalpy above 1e-6 of its central value. Enthalpy angular truncation is reported relative to central enthalpy, without a rejection limit; it is distinct from the symmetry discrepancy. Geometry and projection checks are sampled diagnostics, not global error bounds. These are export admissibility limits, not seismic accuracy targets. Negative density is clamped only within 128 machine epsilons of the central density; larger negatives are rejected.
Output files are lambda_eps, enthalpy_et_der, domains_boundaries, plus metadata.txt. Radial derivatives are with respect to TOP's mapping coordinate, computed from the projected Chebyshev samples. No Bernoulli reconstruction, extra equilibrium solve or interior smoothing is performed. Finite-element interpolation noise can therefore affect those derivatives and the modes; vary export resolution at fixed FE solution before attributing a frequency change to the stellar equilibrium. For n > 1, deriving enthalpy from FE density can amplify surface regularity defects; accepting an export does not establish derivative or mode convergence. The actual-solver integration benchmark currently covers n=1.
Use TOP's recovered poly_ester reader and eq_poly_ester_gamma2 for the selected n=1, Gamma1=2 benchmark. The files describe the equilibrium; the TOP equation module supplies Gamma1. TOP frequencies are angular frequencies divided by sqrt(4*pi*G*rho_c); angular_frequency_unit in the report supplies that physical conversion in the input model's unit system. Divide by 2*pi to obtain cyclic frequency.
The backend-independent overload in serif/utils/serialize/top.hpp accepts a TopPolytropeSource with physical surface-radius and enthalpy callbacks, allowing other sampling backends without an MFEM dependency. Analytic source and MFEM regressions are part of the serialization test suite. An optional test against the actual recovered TOP reader is available after building top_export_fixture:
That optional check requires TOP's compiled eq_poly_ester_gamma2 module. TOP is not a SERiF build dependency.