///|
/// A named analytical element and its total dissolved concentration.
///
/// Concentrations are mol/L. The solver never guesses a unit conversion;
/// callers must provide values in this unit.
pub(all) struct ElementDef {
name : String
total_mol_l : Double
} derive(Debug, ToJson, FromJson)
///|
pub extend ElementDef with @debug.Debug::{to_repr}
///|
pub extend ElementDef with @json.ToJson::{to_json}
///|
pub extend ElementDef with @json.FromJson::{from_json}
///|
/// A term in a mass-action or mineral dissolution expression.
///
/// `coefficient` is the signed stoichiometric exponent. A negative value is
/// allowed for an expression written in the opposite direction.
pub(all) struct ComponentTerm {
component : Int32
coefficient : Double
} derive(Debug, ToJson, FromJson)
///|
/// A charged aqueous species. Primary species point to one free component;
/// secondary species use `component_terms` and `log_k` to derive their
/// activity from the primary activities.
pub(all) struct SpeciesDef {
name : String
charge : Int32
element_stoich : Array[Double]
component_terms : Array[ComponentTerm]
base_component : Int32?
log_k : Double
alkalinity : Double
source : String
} derive(Debug, ToJson, FromJson)
///|
/// A mineral saturation expression. The MVP reports saturation only; it does
/// not change the dissolved totals or perform precipitation phase switching.
pub(all) struct MineralDef {
name : String
component_terms : Array[ComponentTerm]
log_ksp : Double
source : String
} derive(Debug, ToJson, FromJson)
///|
/// An input water sample and its explicitly versioned species database.
pub(all) struct Problem {
temperature_k : Double
volume_l : Double
elements : Array[ElementDef]
species : Array[SpeciesDef]
minerals : Array[MineralDef]
hydrogen_component : Int32
fixed_p_h : Double?
alkalinity_mol_l : Double?
activity_model : String
database_version : String
thermodynamics : ThermodynamicData
} derive(Debug, ToJson, FromJson)
///|
/// Numerical controls. All tolerances are dimensionless residual tolerances
/// except `activity_tolerance`, which applies to log10 activity coefficients.
pub(all) struct SolveOptions {
max_inner_iterations : Int32
max_outer_iterations : Int32
residual_tolerance : Double
activity_tolerance : Double
finite_difference_step : Double
check_multiple_solutions : Bool
} derive(Debug, ToJson, FromJson)
///|
pub fn default_options() -> SolveOptions {
{
max_inner_iterations: 80,
max_outer_iterations: 100,
residual_tolerance: 1.0e-12,
activity_tolerance: 1.0e-11,
finite_difference_step: 1.0e-5,
check_multiple_solutions: true,
}
}
///|
/// Structured error states are kept separate so a caller can distinguish
/// invalid data, an inconsistent constraint set, and numerical stagnation.
pub(all) enum SolveError {
InvalidInput(String)
DatabaseInconsistent(String)
InconsistentConstraints(String)
DimensionMismatch(String)
Stalled(String)
MultipleSolutionsSuspected(String)
ActivityModelOutOfRange(String)
NegativeConcentration(String)
} derive(Debug)
///|
pub fn SolveError::message(self : SolveError) -> String {
match self {
InvalidInput(message) => message
DatabaseInconsistent(message) => message
InconsistentConstraints(message) => message
DimensionMismatch(message) => message
Stalled(message) => message
MultipleSolutionsSuspected(message) => message
ActivityModelOutOfRange(message) => message
NegativeConcentration(message) => message
}
}
///|
pub(all) struct SpeciesResult {
name : String
concentration_mol_l : Double
activity : Double
log_activity : Double
activity_coefficient : Double
charge : Int32
primary : Bool
source : String
} derive(Debug, ToJson, FromJson)
///|
pub(all) struct SaturationResult {
mineral : String
log_iap : Double
log_ksp : Double
saturation_index : Double
state : String
source : String
} derive(Debug, ToJson, FromJson)
///|
pub(all) struct SpeciationResult {
status : String
temperature_k : Double
volume_l : Double
activity_model : String
p_h : Double
ionic_strength : Double
charge_residual_mol_l : Double
alkalinity_mol_l : Double
element_residuals_mol_l : Array[Double]
species : Array[SpeciesResult]
saturation : Array[SaturationResult]
activity_coefficients : Array[Double]
inner_iterations : Int32
outer_iterations : Int32
max_scaled_residual : Double
diagnostics : Array[String]
database_version : String
solver_options : SolveOptions
thermodynamics : ThermodynamicData
iteration_trace : Array[IterationRecord]
max_log_gamma_error : Double
} derive(Debug, ToJson, FromJson)