#保留的水相平衡支持包

    此目录保留早期水相物种分配、活度、矿物饱和指数(SI)、敏感性及参考数据验证。它与根包的“同源分样处理历史核对与零净转移检验”分开维护,有独立的 MoonBit 包和 CLI;根包及默认 cmd/main 不导入它。

    #功能与边界

    支持 Ca–C 碳酸盐、Ca–S 硫酸盐、Zn–水解与纯水有限案例。数值验证使用独立 Python 参考路线,不等于运行 PHREEQC 可执行程序或完成真实实验认证。SI 大于零仅说明模型内过饱和,不证明实际沉淀,也不计算沉淀量。

    这些功能使用成熟水相化学、平衡和数值方法,并与 Mooncakes 的 biogeochem、vle、extractor、thermochem 等存在领域或方法交集。它们不作为当前主任务的独有性或本期新增贡献依据;保留代码仍属于完整仓库,目录划分不会消除先例或替代赛事审查。

    #独立入口

    从项目根目录运行:

    python3 scripts/aqueous.py solve --fixture carbonate moon run equilibrium/cmd/main fixture-json carbonate

    本地工具链使用者可将 moon 替换为 .moon/bin/moon。根包使用 python3 scripts/aliquots.py,其输入为样瓶处理历史,不能与本包平衡输入混用。

    完整操作见 支持包指南、平衡场景、数据来源 和 第三方许可。历史规划保留在根目录早期规划书;当前主任务以 根 README 和 贡献说明 为准。

    ComponentTerm

    pub(all) struct ComponentTerm {
    component : Int
    coefficient : Double
    } derive(ToJson,
    Debug
    ,
    FromJson
    )

    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.

    ComponentTerm::to_json

    ElementDef

    pub(all) struct ElementDef {
    name : String
    total_mol_l : Double
    } derive(ToJson,
    Debug
    ,
    FromJson
    )

    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.

    ElementDef::to_json

    fn ElementDef::to_json(ElementDef) -> Json

    IterationRecord

    pub(all) struct IterationRecord {
    outer_iteration : Int
    inner_iterations : Int
    ionic_strength : Double
    max_scaled_residual : Double
    max_log_gamma_error : Double
    } derive(ToJson,
    Debug
    ,
    FromJson
    )

    MineralDef

    pub(all) struct MineralDef {
    name : String
    component_terms : Array[ComponentTerm]
    log_ksp : Double
    source : String
    } derive(ToJson,
    Debug
    ,
    FromJson
    )

    A mineral saturation expression. The MVP reports saturation only; it does not change the dissolved totals or perform precipitation phase switching.

    MineralDef::to_json

    fn MineralDef::to_json(MineralDef) -> Json

    MineralSensitivity

    pub(all) struct MineralSensitivity {
    mineral : String
    minus_si : Double
    base_si : Double
    plus_si : Double
    d_si_d_input : Double
    phase_boundary_ambiguous : Bool
    } derive(ToJson,
    Debug
    ,
    FromJson
    )

    Perturbation

    pub(all) struct Perturbation {
    kind : String
    element_index : Int
    step : Double
    } derive(ToJson,
    Debug
    ,
    FromJson
    )

    Central differences at fixed database, activity model and other inputs. kind is fixed_pH, alkalinity, element_total or total_scale. Units are pH, mol/L, mol/L, fraction; element_index is used only for element_total.

    Perturbation::to_json

    Problem

    pub(all) struct Problem {
    temperature_k : Double
    volume_l : Double
    elements : Array[ElementDef]
    species : Array[SpeciesDef]
    minerals : Array[MineralDef]
    hydrogen_component : Int
    fixed_p_h : Double?
    alkalinity_mol_l : Double?
    activity_model : String
    database_version : String
    thermodynamics : ThermodynamicData
    } derive(ToJson,
    Debug
    ,
    FromJson
    )

    An input water sample and its explicitly versioned species database.

    Problem::to_json

    fn Problem::to_json(Problem) -> Json

    Problem::to_repr

    SaturationResult

    pub(all) struct SaturationResult {
    mineral : String
    log_iap : Double
    log_ksp : Double
    saturation_index : Double
    state : String
    source : String
    } derive(ToJson,
    Debug
    ,
    FromJson
    )

    SensitivityReport

    pub(all) struct SensitivityReport {
    difference_method : String
    perturbation : Perturbation
    input_value : Double
    input_unit : String
    d_p_h_d_input : Double
    d_ionic_strength_d_input : Double
    species : Array[SpeciesSensitivity]
    minerals : Array[MineralSensitivity]
    base_result : SpeciationResult
    minus_result : SpeciationResult
    plus_result : SpeciationResult
    diagnostics : Array[String]
    } derive(ToJson,
    Debug
    ,
    FromJson
    )

    SensitivityRequest

    pub(all) struct SensitivityRequest {
    problem : Problem
    perturbation : Perturbation
    } derive(ToJson,
    Debug
    ,
    FromJson
    )

    SolveError

    pub(all) enum SolveError {
    InvalidInput(String)
    DatabaseInconsistent(String)
    InconsistentConstraints(String)
    DimensionMismatch(String)
    Stalled(String)
    MultipleSolutionsSuspected(String)
    ActivityModelOutOfRange(String)
    NegativeConcentration(String)
    } derive(
    Debug
    )

    Structured error states are kept separate so a caller can distinguish invalid data, an inconsistent constraint set, and numerical stagnation.

    SolveError::message

    fn SolveError::message(self : SolveError) -> String

    SolveOptions

    pub(all) struct SolveOptions {
    max_inner_iterations : Int
    max_outer_iterations : Int
    residual_tolerance : Double
    activity_tolerance : Double
    finite_difference_step : Double
    check_multiple_solutions : Bool
    } derive(ToJson,
    Debug
    ,
    FromJson
    )

    Numerical controls. All tolerances are dimensionless residual tolerances except activity_tolerance, which applies to log10 activity coefficients.

    SolveOptions::to_json

    SpeciationResult

    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 : Int
    outer_iterations : Int
    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(ToJson,
    Debug
    ,
    FromJson
    )

    SpeciesDef

    pub(all) struct SpeciesDef {
    name : String
    charge : Int
    element_stoich : Array[Double]
    component_terms : Array[ComponentTerm]
    base_component : Int?
    log_k : Double
    alkalinity : Double
    source : String
    } derive(ToJson,
    Debug
    ,
    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.

    SpeciesDef::to_json

    fn SpeciesDef::to_json(SpeciesDef) -> Json

    SpeciesResult

    pub(all) struct SpeciesResult {
    name : String
    concentration_mol_l : Double
    activity : Double
    log_activity : Double
    activity_coefficient : Double
    charge : Int
    primary : Bool
    source : String
    } derive(ToJson,
    Debug
    ,
    FromJson
    )

    SpeciesSensitivity

    pub(all) struct SpeciesSensitivity {
    name : String
    minus_mol_l : Double
    base_mol_l : Double
    plus_mol_l : Double
    d_concentration_d_input : Double
    d_log10_concentration_d_input : Double
    } derive(ToJson,
    Debug
    ,
    FromJson
    )

    ThermoRecord

    pub(all) struct ThermoRecord {
    id : String
    reaction : String
    source_reaction : String
    log_k : Double
    source_lines : String
    derivation : String
    } derive(ToJson,
    Debug
    ,
    FromJson
    )

    Constants refer to the reaction written in reaction, with dimensionless activities. A secondary species uses log(a_s) = log_k + sum(nu log(a_i)).

    ThermoRecord::to_json

    ThermodynamicData

    pub(all) struct ThermodynamicData {
    adaptation_notice : String
    version : String
    reference_url : String
    reference_sha256 : String
    source_standard_state : String
    solver_standard_state : String
    reference_temperature_k : Double
    min_temperature_k : Double
    max_temperature_k : Double
    pressure_atm : Double
    license : String
    license_url : String
    limitations : String
    records : Array[ThermoRecord]
    } derive(ToJson,
    Debug
    ,
    FromJson
    )

    A fixed dataset and the approximation used to apply it in this solver. The source is molal; this release uses c/(1 mol/L), with m approximately c for dilute water and unit solvent activity. No temperature extrapolation.

    ThermodynamicData::to_json

    builtin_thermodynamics

    fn builtin_thermodynamics() -> ThermodynamicData

    default_options

    fn default_options() -> SolveOptions

    demo_json

    fn demo_json() -> String

    demo_problem

    fn demo_problem() -> Problem

    Calcium-carbonate water, 2 mM Ca, 3 mM dissolved inorganic carbon, 3 meq/L alkalinity and 1 mM chloride. The counterion closes charge.

    fixture_json

    fn fixture_json(name : String) -> String

    hydrolysis_problem

    fn hydrolysis_problem() -> Problem

    Dilute zinc nitrate titrated with 19 micromol/L NaOH. All counterions are explicit; pH follows charge balance. No carbonate or other ligands.

    inspect_json

    fn inspect_json(input : String) -> String

    named_problem

    fn named_problem(name : String) -> Result[Problem, SolveError]

    problem_from_json

    fn problem_from_json(input : String) -> Result[Problem, SolveError]

    Decode a Problem from the documented JSON schema. Parsing and schema validation are separate so callers can report a useful location/type error before numerical work starts.

    problem_to_json

    fn problem_to_json(problem : Problem) -> String

    pure_water_problem

    fn pure_water_problem() -> Problem

    result_to_json

    fn result_to_json(result : SpeciationResult) -> String

    sensitivity

    fn sensitivity(problem : Problem, perturbation : Perturbation, options? : SolveOptions) -> Result[SensitivityReport, SolveError]

    Solve the centre and both perturbed inputs. Any failed solve is returned as an error; a local derivative does not prove behaviour beyond the step.

    sensitivity_json

    fn sensitivity_json(input : String) -> String

    solve

    fn solve(problem : Problem, options? : SolveOptions) -> Result[SpeciationResult, SolveError]

    Solve a validated finite aqueous system using damped finite-difference Newton iterations inside a Davies or Debye-Hückel activity fixed point.

    solve_json

    fn solve_json(input : String) -> String

    sulfate_problem

    fn sulfate_problem() -> Problem

    A closed, electroneutral 10 mM CaSO4 solution; includes bisulfate, CaSO4(aq), CaHSO4+ and CaOH+, with gypsum saturation diagnostics.