The chemistry solver
verifiedAgainst f1eaefe · verifiedOn 2026-09-10 · the automated staleness banner is planned, not built.
In one paragraph
Section titled “In one paragraph”VoxelSandbox.Chemistry is a plain-C# control-volume solver: given a VesselCharge, temperature, volume, baked reaction definitions, and a supplied energy increment, it proposes one fixed simulation tick and either returns the complete next state or refuses it. VesselSolver is the transaction owner. It validates the whole request before chemistry moves, evaluates every reaction from the same charge snapshot, projects their competing extents together, resolves reaction and applied heat through any phase boundary, then verifies pressure and mass conservation. The result is deliberately independent of Unity and host math libraries, so the same state and inputs produce the same calculation in an EditMode fixture, an in-world vessel, or a future authoritative server.
The pieces
Section titled “The pieces”| Symbol | File | Responsibility |
|---|---|---|
VesselSolver |
Chemistry/VesselSolver.cs:81 |
Owns the all-or-nothing tick: validation, canonical reaction order, coupled proposal/application, thermal resolution, pressure and mass checks |
ReactionEvaluator.TryEvaluate |
Chemistry/ReactionEvaluator.cs:94 |
Forms temperature-corrected thermodynamics, Q, Arrhenius rate, surface term, and signed net rate for one reaction at one charge snapshot |
DeterministicSubstepping |
Chemistry/DeterministicSubstepping.cs:10 |
Converts the predicted limiting-reagent fraction into an equal-duration power-of-two count from 1 to 64 |
ReactionExtentMath |
Chemistry/ReactionExtentMath.cs:21 |
Clamps ξ = r·dt by the limiting consumed species, then preflights and applies Δn = ν·ξ without a partial mutation |
PhaseTransitionMath |
Chemistry/PhaseTransition.cs:81 |
Transfers moles at a solid/liquid or liquid/gas boundary and returns residual energy for the next thermal step |
DeterministicMath |
Chemistry/DeterministicMath.cs:12 |
Fixed-coefficient IEEE-754 implementations of Exp, Ln, Pow, and Sqrt, with no System.Math or Unity dependency |
ProcessTimeScale |
Chemistry/ProcessTimeScale.cs:10 |
One global policy: 20 simulation ticks/s and a 60× multiplier on kinetic rates only |
The flow
Section titled “The flow”flowchart TD INPUT["VesselCharge + T + V\ntick input"] VALIDATE["VesselSolver\nvalidate and canonicalise"] STEPS["DeterministicSubstepping\npower-of-two substeps"] EVAL["ReactionEvaluator\nthermo, Q, kinetics"] PROJECT["VesselSolver\ncoupled extent projection"] EXTENT["ReactionExtentMath\nclamp then apply Δn"] THERMAL["VesselSolver\nreaction + applied energy"] PHASE["PhaseTransitionMath\nlatent heat at boundary"] RESULT["VesselTickResult\nT, pressure, heat, mass drift"] EVENTS["ScienceEventBuffer\nappend after accepted tick"] INPUT --> VALIDATE --> STEPS --> EVAL --> PROJECT --> EXTENT --> THERMAL --> PHASE --> EVAL PHASE --> RESULT RESULT --> EVENTS
VesselSolver.TryAdvanceTick (VesselSolver.cs:106) exposes convenience overloads, including an event-recording path and a phase-aware path. All lead to TryAdvanceTickCore (:215). Before entering the substep loop, it rejects bad inputs, a reaction set larger than 128, unsorted/invalid phase-transition data, invalid event capacity, and a duplicate reaction id. It measures initial mass and ideal-gas pressure before changing the charge.
For a full tick, ProcessTimeScale.ProcessSecondsPerSimulationTick is 3 process seconds: 60 / 20. TryChooseSubstepCount predicts the most demanding reaction and DeterministicSubstepping.TryCalculateSubstepCount quantises upward to 1, 2, 4, ..., 64, so a normal substep changes its limiting reagent by at most 5%. The thermal-energy cap adds a second bound: aggregate reaction heat may move temperature by at most 10% in one substep. Both limits are stated approximations, not hidden physical claims.
Each substep begins with ReactionEvaluator.TryEvaluate (ReactionEvaluator.cs:94). It uses ideal-gas partial pressure for gas activity and unit activity for pure solids/liquids, produces ΔrH, ΔrG, K, Q, the Arrhenius constant, and r = k·Πaᵢ^orderᵢ·S·(1 − Q/K). A missing reactant in the intended direction is a valid state that makes CanProceed false; missing catalogue thermodynamics or an out-of-range Shomate evaluation is an error.
The tick does not apply reactions one at a time. TryAdvanceCoupledReactionSubstep (VesselSolver.cs:443) derives every proposed extent from one snapshot, then finds one conservative scale for shared-reagent availability and the aggregate heat budget. Only then does ReactionExtentMath.TryApplyExtent (ReactionExtentMath.cs:93) mutate the charge. The three-pass apply validates every resulting quantity, reserves any trace-ledger capacity, and finally writes all species; a rejected extent leaves no half-reaction behind.
Heat is then resolved in sequence. TryResolveThermalEnergy (VesselSolver.cs:773) first heats or cools to the next reachable transition temperature, asks PhaseTransitionMath.TryResolveAtTransition (PhaseTransition.cs:88) to spend latent heat at constant temperature, then carries remaining energy onward. A substep therefore cannot jump across melting or boiling. Once all substeps finish, the solver calculates post-tick pressure, compares mass before/after against its absolute-plus-relative tolerance, and only then appends science events.
Invariants you must not break
Section titled “Invariants you must not break”- A tick is a transaction. Validate capacity and inputs before mutation; preflight an extent before adjustment; record
ScienceEvents only after mass conservation passes. An expected invalid simulation request returnsfalsewith aVesselTickResult.Error, not a partly-updated charge. - Reaction order cannot be catalogue order.
TryBuildCanonicalReactionOrder(VesselSolver.cs:649) sorts on physical definition fields and treats the id only as the final tie-breaker. Renumbering a reaction must not change its chemistry. - Competing reactions read one state. Keep the snapshot proposal plus one shared scale in the coupled-substep path. Sequentially applying one reaction before evaluating its neighbour would make results depend on ordering and can overspend a shared reagent.
- No negative moles. All callers must clamp through
ReactionExtentMath.TryClampExtent(ReactionExtentMath.cs:30);TryApplyExtentdeliberately proves that invariant again before writing. - Only kinetics are accelerated. The 60×
ProcessRateMultiplieris applied toReactionEvaluator’s net rate. Do not scale $K$, pressure, heat capacity, voltage, or energy a second time. - Do not reintroduce host math.
DeterministicMathexists because save determinism cannot rest on a platformlibm. New solver transcendental work must use it and preserve fixed evaluation order. - Phase boundaries consume energy before temperature changes. Keep transitions in ascending stable key order and retain the heat-to-boundary → latent heat → residual-energy sequence.
Where it connects
Section titled “Where it connects”- Upstream:
VoxelSandbox.Databakes authoredSubstanceDefinitionandReactionDefinitionassets into theSubstanceTableandChemicalReaction[]that this assembly accepts. See Catalogues & authoring. - Downstream:
VoxelSandbox.Industry’sVesselDomainowns aScienceVesselRun, drives it at the 20 Hz simulation cadence from wall-clock deltas, and supplies environment heat as the solver’s applied-energy input. The UnityVesselRuntimeis just itsMonoBehaviourbridge. - Evidence and tests:
Assets/_Game/Tests/EditMode/Chemistry/VesselSolverTests.cs,DeterministicSubsteppingTests.cs,ReactionExtentMathTests.cs, andThermalEnergyMathTests.csexercise the transaction, substep, non-negative-moles, and latent-heat contracts. The thermodynamic and kinetic evidence belongs inDocs/CHEMISTRY_FOUNDATIONS.md§28.4–28.9 andDocs/CHEMISTRY_SOURCES.md.
See also
Section titled “See also”- Catalogues & authoring — the Unity authoring layer that creates the solver’s immutable inputs.
- Add a scientific route — a worked example of adding a source-backed substance/reaction without inventing kinetics.
Docs/MASTERPLAN.md§28.4–28.9 and §36.1;Docs/CHEMISTRY_FOUNDATIONS.md§28.7 for the tick’s first-law and reaction-extent contracts.
User-contributed notes
Corrections, clarifications, and practical tips for this page. Anonymous is fine — a name is optional. Basic Markdown works:
**bold**,*italic*,`code`, and links.Notes policy
Notes are lightly filtered for spam and may be edited or removed. Keep them about this page — no support requests, no personal data, nothing you would not publish. Links are limited and marked
nofollow.