Repository navigation
Chemical equilibrium
equilibrate finds what a charge of substances becomes at a temperature and a pressure. It minimizes the Gibbs energy with every element conserved. The gases form one ideal mixture, and each condensed substance is a pure phase that can enter or leave.
This page is a reference for extensions/chemistry/equilibrium.mojo. For the rest of the library, see Process chemistry.
Give the candidates that can form, the charge, the temperature and the pressure:
from extensions.chemistry.catalog import (
carbon_dioxide, carbon_monoxide, graphite, oxygen,
)
from extensions.chemistry.equilibrium import Charge, equilibrate
from extensions.chemistry.quantities import Amount64, MOLE
from units.si import Pressure64
from units.temperature import Temperature64
var result = equilibrate(
[graphite(), carbon_monoxide(), carbon_dioxide(), oxygen()],
[Charge(graphite(), Amount64(2, MOLE)),
Charge(carbon_dioxide(), Amount64(1, MOLE))],
Temperature64(1000), Pressure64(1e5),
)
result.mole_fraction("CO(g)") # about 0.69An Equilibrium gives amount_of(name), gas_amount(), mole_fraction(name) and share(element, name). The share is the part of an element that ends in one substance.
The solver is the method of NASA CEA (Gordon and McBride, NASA RP-1311, 1994). A Newton iteration solves for the element potentials, the change in the total gas and the change in each condensed amount. The gas amounts follow from the potentials. The step is damped as CEA damps it, so a trace species cannot jump by many orders at once.
After each solution, a condensed phase with a negative amount leaves. Then the absent condensed phase with the most negative driving force enters. The search stops when no phase changes, or after max_phase_changes.
Some charges have element balances that are not independent. CaO, CaCO3 and CO2 are an example: the calcium and the carbon fix the oxygen. The solver drops the dependent balances by Gaussian elimination before it iterates. It then checks every balance of the result to 1e-9.
A candidate that holds an element the charge does not hold is left out. A condensed candidate whose data does not reach the temperature is left out too.
EquilibriumShare is a Constraint. It holds where the share of an element that ends in a product reaches a minimum. Add it to a window with met_intervals, as Add a constraint shows. For example, a chlorination can require that 99 % of the titanium leaves as TiCl4 gas.
The tests compare each result with a closed form that the solver does not use:
| Case | Closed form |
|---|---|
| Excess carbon in CO2 where K = 1 | The CO fraction x solves x^2 / (1 - x) = 1: 0.618034, to 1e-14 |
| Limestone in argon | CO2 at the decomposition pressure while calcite and lime coexist |
| H2 at 3000 K | x^2 / (1 - x) = K for the H fraction |
tools/check_chemistry_cantera.mojo compares four gas mixtures with Cantera's solver. Both read the same NASA data, and they agree to 1e-13 in mole fraction. CI does not install Cantera, so this is a developer check:
uv pip install --python .venv/bin/python cantera
PYTHONPATH=$(ls -d .venv/lib/python3*/site-packages) \
.venv/bin/mojo run -I . tools/check_chemistry_cantera.mojoCantera reads NASA files at a reference pressure of one atmosphere, but the NASA data is at one bar. The tool rebuilds each Cantera species at one bar first.
Gases are ideal, and condensed phases are pure. For a solution phase, see Binary solutions. Equilibrium says where a charge ends, not how fast it gets there. For rates, see Rates and transport.
Tutorials
How-to
- Install
- Run the checks
- GPU backend
- Coverage
- Measure examples
- Add a feature
- Add a scanned building
- Write documentation
Reference
- Scene graph
- Rotations
- Cameras
- Geometry
- Geometry addons
- Lofts and clipping groups
- Sculptor
- Procedural generators
- Meshes and assets
- Lines
- Points and sprites
- Helpers
- Scene objects
- Animated objects
- Raycasting
- Curves and paths
- Animation
- Skinning
- Materials
- Node materials
- TSL functions
- Lights
- Lighting addons
- Fog
- Textures
- Renderer
- Renderer hooks and material flags
- Rasterization
- GPU backend
- Render target
- Post-processing
- GPU computation
- Compute nodes
- Progressive light map
- Gaussian splats
- Voxel global illumination
- SVG renderer
- Image files
- Image decode queue
- Model files
- More model files
- Exporters
- Scene JSON
- Windowing and controls
- C ABI and hosts
- Math
- Norm consumers
- Math addons
- Units
- Coverage tool
- Commands
- Examples
- Benchmarks
- Extensions
- Shared physics
- Physics query snapshots
- Continuous collision
- Tick force restoration
- Water
- Warp drives
- Vegetation
- Village
- Streamline
- Animals
-
Buildings
- Numerics
- Building topology
- Building model
- IFC exchange
- Procedural towers
- Floor plans and interiors
- Frame analysis
- Shell analysis
- Building energy
- Construction kits
- Bearing walls
- Scanned buildings
- SOG files
- Compressed PLY files
- Scan parts
- Cleaning and orienting scans
- Walking through a scan
- Virtual scans
- Castle
- CARLA
- Femur
- Tibia
- Fibula
- Patella
- Knee
- Muscles
- Vessels
- Lymph
- Nerves
- Integument
- Leg
- Foot
- Pelvis
- Torso
- Arm
- Hand
- Head
- Genome
- Game humanoid
- Humanoid fidelity
- Mixamo animations
- Audio-aligned game faces
- Converted assets
- Mesh quality
- Garments
- Outfits and armor
- Cloth and spring bones
- Attire estimates
- Segment inertia
- Process chemistry
- Thermochemical data
- Chemical equilibrium
- Process maps
- Rates and transport
- Electrolysis and film color
- Binary solutions
- Flowsheets and routes
- Materials and coatings
- Crystals and figures
Explanation
- Software rasterizer
- Scene graph as an array
- Linear color
- Normals are not colors
- Perspective correction
- Fixed-point coverage
- Per-fragment shading
- Mipmaps
- Sorted transparency
- Types and checks
- Render hooks as a trait
- Shared CPU and GPU code
- Bands and threads
- PNG structure
- Extensions beside the port
- Process temperatures
- Scans as building models
- CUDA teardown hang
- Mojo compiler hang