moonbit-thermochem

Thermochemical property calculations for MoonBit, including NASA polynomials, reaction enthalpy, and flame-temperature estimates.

thermochemistry
nasa-polynomial
combustion
enthalpy
moonbit
moon add Lxxbv/moonbit-thermochem@0.1.1
Download zip
Author
Version
0.1.1
License
Apache-2.0
Last updated
15 days ago
Downloads
5
README

#moonbit-thermochem

Thermochemical property calculations for MoonBit.

Status: Work in progress.

This package evaluates NASA7 heat-capacity, enthalpy, and entropy polynomials; defines species and reactions; parses common reaction equations; checks elemental balance; and includes a small, documented data set for combustion and ammonia-synthesis examples. Values use SI molar units by default: J/mol/K for heat capacity and entropy, and J/mol for enthalpy.

moon add Lxxbv/moonbit-thermochem

The core examples are checked as part of this checkout. Examples using the separate data and parser packages are marked nocheck: MoonBit README doc tests compile the root package and do not load sibling package imports.

#Look Up A Species

The bundled data set contains CH4, O2, N2, CO2, H2O, CO, H2, and NH3 gas species.

let methane = @data.get_species("CH4")
inspect(methane.name, content="CH4")

#Evaluate Heat Capacity

Species::cp_molar selects the NASA7 segment covering the requested temperature. It raises ThermoError::NoThermoSegment outside the species data range.

///|
test "evaluate a NASA7 heat capacity" {
let segment = Nasa7Segment::new(
range=TemperatureRange::new(lower=200.0, upper=3500.0),
a1=3.5,
a2=0.0,
a3=0.0,
a4=0.0,
a5=0.0,
a6=0.0,
a7=0.0,
)
let hydrogen = Species::new(
name="H2",
formula=parse_formula("H2"),
phase=Phase::Gas,
molar_mass=2.016,
formation_enthalpy=0.0,
model=ThermoModel::Nasa7([segment]),
)
inspect(hydrogen.cp_molar(temperature=1200.0) > 0.0, content="true")
}

#Calculate Reaction Enthalpy

Reaction enthalpy is the stoichiometric product enthalpy minus the stoichiometric reactant enthalpy at the same temperature.

///|
test "calculate reaction enthalpy" {
let formula = parse_formula("H2")
let reactant = Species::new(
name="H2",
formula~,
phase=Phase::Gas,
molar_mass=2.016,
formation_enthalpy=0.0,
model=ThermoModel::ConstantEnthalpy(10.0),
)
let product = Species::new(
name="H2O",
formula=parse_formula("H2O"),
phase=Phase::Gas,
molar_mass=18.015,
formation_enthalpy=0.0,
model=ThermoModel::ConstantEnthalpy(4.0),
)
let reaction = Reaction::new(
label="H2 -> H2O",
reactants=[StoichTerm::new(species=reactant, coefficient=1.0)],
products=[StoichTerm::new(species=product, coefficient=1.0)],
)
inspect(reaction.enthalpy(temperature=298.15), content="-6")
}

#Parse And Check Reactions

The root package can parse simple stoichiometric equations with integer or decimal coefficients, then report elemental balance before a reaction is bound to a species data set.

///|
test "parse and check reaction balance" {
let parsed = parse_reaction_equation(
"CH4 + 2 O2 + 7.52 N2 -> CO2 + 2 H2O + 7.52 N2",
)
inspect(parsed.is_elementally_balanced(), content="true")
inspect(parsed.element_balance().length(), content="4")
}

#Formula And Element Utilities

Formula parsing combines repeated symbols and can compute molar mass, per element mass contribution, and mass fraction from the built-in periodic table helpers. The element catalog also exposes period, group, block, standard phase, and a natural-abundance flag for lightweight validation and reporting.

///|
test "formula mass and unit helpers" {
let methane = parse_formula("CH4")
inspect(methane.molar_mass() > 16.0, content="true")
inspect(methane.mass_fraction("C") > 0.7, content="true")
inspect(element_group("O"), content="16")
inspect(j_per_mol_to_kj_per_mol(12500.0), content="12.5")
}

#Parse Compact NASA7 Data

The parser accepts a compact, whitespace-separated three-line record: a header followed by LOW and HIGH rows with seven coefficients each. The species name must also be a parseable chemical formula.

let text =
#|H2 G 200.0 1000.0 3500.0
#|LOW 2.34433112 0.00798052075 -0.000019478151 0.0000000201572094 -0.00000000000737611761 -917.935173 0.683010238
#|HIGH 3.3372792 -0.000049402473 0.000000499456778 -0.00000000017958886 0.000000000000020025227 -950.158922 -3.20502331
#|
let species = @parser.parse_nasa7_thermo(text)
inspect(species[0].name, content="H2")

#CLI Demo

Run the deterministic examples from a checkout:

moon run cmd/thermochem -- species CH4 1200 moon run cmd/thermochem -- reaction methane-combustion 298.15 moon run cmd/thermochem -- flame methane-air 298.15 moon run cmd/thermochem -- ammonia 700

For data provenance and scope, see data sources. See design notes for package boundaries and numerical methods.

#
ThermoError

pub(all) suberror ThermoError {
InvalidTemperatureRange(lower~ : Double, upper~ : Double)
TemperatureOutOfRange(value~ : Double, lower~ : Double, upper~ : Double)
NoThermoSegment(species~ : String, temperature~ : Double)
MissingSpecies(name~ : String)
UnknownElement(symbol~ : String)
ParseError(line~ : Int, column~ : Int, message~ : String)
SolverFailed(message~ : String)
} derive(Eq,
Debug
)

#
ElementBalance

pub(all) struct ElementBalance {
symbol : String
reactant_atoms : Double
product_atoms : Double
difference : Double
} derive(Eq,
Debug
)

#
ElementBalance::is_balanced

fn ElementBalance::is_balanced(self : ElementBalance, tolerance? : Double) -> Bool

#
ElementCount

pub(all) struct ElementCount {
symbol : String
count : Int
} derive(Eq,
Debug
)

#
ElementInfo

pub(all) struct ElementInfo {
symbol : String
name : String
atomic_number : Int
atomic_weight : Double
} derive(Eq,
Debug
)

#
ElementMass

pub(all) struct ElementMass {
symbol : String
count : Int
atomic_weight : Double
total_mass : Double
mass_fraction : Double
} derive(Eq,
Debug
)

#
EnergyUnit

pub(all) enum EnergyUnit {
JoulePerMol
KilojoulePerMol
} derive(Eq,
Debug
)

#
Formula

pub(all) struct Formula {
elements : Array[ElementCount]
} derive(Eq,
Debug
)

#
Formula::count

fn Formula::count(self : Formula, symbol : String) -> Int

#
Formula::element_mass

fn Formula::element_mass(self : Formula, symbol : String) -> Double raise ThermoError

#
Formula::element_masses

fn Formula::element_masses(self : Formula) -> Array[ElementMass] raise ThermoError

#
Formula::mass_fraction

fn Formula::mass_fraction(self : Formula, symbol : String) -> Double raise ThermoError

#
Formula::molar_mass

fn Formula::molar_mass(self : Formula) -> Double raise ThermoError

#
Formula::to_formula_string

fn Formula::to_formula_string(self : Formula) -> String

#
HeatCapacityUnit

pub(all) enum HeatCapacityUnit {
JoulePerMolKelvin
KilojoulePerMolKelvin
} derive(Eq,
Debug
)

#
Mixture

pub(all) struct Mixture {
amounts : Array[SpeciesAmount]
} derive(Eq,
Debug
)

#
Mixture::new

fn Mixture::new(amounts~ : Array[SpeciesAmount]) -> Mixture

#
Nasa7Segment

pub(all) struct Nasa7Segment {
range : TemperatureRange
a1 : Double
a2 : Double
a3 : Double
a4 : Double
a5 : Double
a6 : Double
a7 : Double
} derive(Eq,
Debug
)

#
Nasa7Segment::cp_over_r

fn Nasa7Segment::cp_over_r(self : Nasa7Segment, temperature : Double) -> Double raise ThermoError

#
Nasa7Segment::h_over_rt

fn Nasa7Segment::h_over_rt(self : Nasa7Segment, temperature : Double) -> Double raise ThermoError

#
Nasa7Segment::new

fn Nasa7Segment::new(range~ : TemperatureRange, a1~ : Double, a2~ : Double, a3~ : Double, a4~ : Double, a5~ : Double, a6~ : Double, a7~ : Double) -> Nasa7Segment

#
Nasa7Segment::s_over_r

fn Nasa7Segment::s_over_r(self : Nasa7Segment, temperature : Double) -> Double raise ThermoError

#
ParsedReaction

pub(all) struct ParsedReaction {
label : String
reactants : Array[ParsedStoichTerm]
products : Array[ParsedStoichTerm]
} derive(Eq,
Debug
)

#
ParsedReaction::element_balance

fn ParsedReaction::element_balance(self : ParsedReaction) -> Array[ElementBalance]

#
ParsedReaction::is_elementally_balanced

fn ParsedReaction::is_elementally_balanced(self : ParsedReaction, tolerance? : Double) -> Bool

#
ParsedReaction::to_reaction

fn ParsedReaction::to_reaction(self : ParsedReaction, lookup : (String) -> Species raise ThermoError) -> Reaction raise ThermoError

#
ParsedStoichTerm

pub(all) struct ParsedStoichTerm {
name : String
formula : Formula
coefficient : Double
} derive(Eq,
Debug
)

#
Phase

pub(all) enum Phase {
Gas
Liquid
Solid
} derive(Eq,
Debug
)

#
Reaction

pub(all) struct Reaction {
label : String
reactants : Array[StoichTerm]
products : Array[StoichTerm]
} derive(Eq,
Debug
)

#
Reaction::element_balance

fn Reaction::element_balance(self : Reaction) -> Array[ElementBalance]

#
Reaction::enthalpy

fn Reaction::enthalpy(self : Reaction, temperature~ : Double) -> Double raise ThermoError

#
Reaction::is_elementally_balanced

fn Reaction::is_elementally_balanced(self : Reaction, tolerance? : Double) -> Bool

#
Reaction::new

fn Reaction::new(label~ : String, reactants~ : Array[StoichTerm], products~ : Array[StoichTerm]) -> Reaction

#
Species

pub(all) struct Species {
name : String
formula : Formula
phase : Phase
molar_mass : Double
formation_enthalpy : Double
model : ThermoModel
} derive(Eq,
Debug
)

#
Species::cp_molar

fn Species::cp_molar(self : Species, temperature~ : Double) -> Double raise ThermoError

#
Species::enthalpy_molar

fn Species::enthalpy_molar(self : Species, temperature~ : Double) -> Double raise ThermoError

#
Species::entropy_molar

fn Species::entropy_molar(self : Species, temperature~ : Double) -> Double raise ThermoError

#
Species::new

fn Species::new(name~ : String, formula~ : Formula, phase~ : Phase, molar_mass~ : Double, formation_enthalpy~ : Double, model~ : ThermoModel) -> Species

#
SpeciesAmount

pub(all) struct SpeciesAmount {
species : Species
amount : Double
} derive(Eq,
Debug
)

#
SpeciesAmount::new

fn SpeciesAmount::new(species~ : Species, amount~ : Double) -> SpeciesAmount

#
StoichTerm

pub(all) struct StoichTerm {
species : Species
coefficient : Double
} derive(Eq,
Debug
)

#
StoichTerm::new

fn StoichTerm::new(species~ : Species, coefficient~ : Double) -> StoichTerm

#
TemperatureRange

pub(all) struct TemperatureRange {
lower : Double
upper : Double
} derive(Eq,
Debug
)

#
TemperatureRange::contains

fn TemperatureRange::contains(self : TemperatureRange, temperature : Double) -> Bool

#
TemperatureRange::new

fn TemperatureRange::new(lower~ : Double, upper~ : Double) -> TemperatureRange raise ThermoError

#
TemperatureRange::validate

fn TemperatureRange::validate(self : TemperatureRange, temperature : Double) -> Unit raise ThermoError

#
TemperatureUnit

pub(all) enum TemperatureUnit {
Kelvin
Celsius
} derive(Eq,
Debug
)

#
ThermoModel

pub(all) enum ThermoModel {
Nasa7(Array[Nasa7Segment])
ConstantEnthalpy(Double)
} derive(Eq,
Debug
)

#
adiabatic_flame_temperature

fn adiabatic_flame_temperature(reaction : Reaction, initial_temperature~ : Double, lower_bound? : Double, upper_bound? : Double) -> Double raise ThermoError

Solves the simplified adiabatic heat balance for final product temperature.

#
atomic_number

fn atomic_number(symbol : String) -> Int raise ThermoError

#
atomic_weight

fn atomic_weight(symbol : String) -> Double raise ThermoError

#
celsius_to_kelvin

fn celsius_to_kelvin(value : Double) -> Double

#
convert_molar_energy

fn convert_molar_energy(value : Double, from~ : EnergyUnit, to~ : EnergyUnit) -> Double

#
convert_molar_heat_capacity

fn convert_molar_heat_capacity(value : Double, from~ : HeatCapacityUnit, to~ : HeatCapacityUnit) -> Double

#
convert_temperature

fn convert_temperature(value : Double, from~ : TemperatureUnit, to~ : TemperatureUnit) -> Double

#
element_block

fn element_block(symbol : String) -> String raise ThermoError

#
element_group

fn element_group(symbol : String) -> Int raise ThermoError

#
element_info

fn element_info(symbol : String) -> ElementInfo raise ThermoError

#
element_is_naturally_abundant

fn element_is_naturally_abundant(symbol : String) -> Bool raise ThermoError

#
element_name

fn element_name(symbol : String) -> String raise ThermoError

#
element_period

fn element_period(symbol : String) -> Int raise ThermoError

#
element_standard_phase

fn element_standard_phase(symbol : String) -> String raise ThermoError

#
is_known_element

fn is_known_element(symbol : String) -> Bool

#
j_per_mol_k_to_kj_per_mol_k

fn j_per_mol_k_to_kj_per_mol_k(value : Double) -> Double

#
j_per_mol_to_kj_per_mol

fn j_per_mol_to_kj_per_mol(value : Double) -> Double

#
kelvin_to_celsius

fn kelvin_to_celsius(value : Double) -> Double

#
kj_per_mol_k_to_j_per_mol_k

fn kj_per_mol_k_to_j_per_mol_k(value : Double) -> Double

#
kj_per_mol_to_j_per_mol

fn kj_per_mol_to_j_per_mol(value : Double) -> Double

#
parse_formula

fn parse_formula(text : String) -> Formula raise ThermoError

#
parse_reaction_equation

fn parse_reaction_equation(text : String) -> ParsedReaction raise ThermoError

#
solve_bisection

fn solve_bisection(lower~ : Double, upper~ : Double, tolerance~ : Double, max_iterations~ : Int, f : (Double) -> Double raise ThermoError) -> Double raise ThermoError

Solves a bracketed scalar root by bisection.

Powered by MoonBit

Site sourceReport issuePackagesBuild queueSkillsStatistics

© 2026 mooncakes.io