///|
pub(all) struct ElementBalance {
symbol : String
reactant_atoms : Double
product_atoms : Double
difference : Double
} derive(Debug, Eq)
///|
fn abs_double(value : Double) -> Double {
if value < 0.0 {
-value
} else {
value
}
}
///|
fn include_symbol(symbols : Array[String], symbol : String) -> Unit {
for existing in symbols {
if existing == symbol {
return
}
}
symbols.push(symbol)
}
///|
fn include_formula_symbols(symbols : Array[String], formula : Formula) -> Unit {
for element in formula.elements {
include_symbol(symbols, element.symbol)
}
}
///|
fn parsed_symbols(reaction : ParsedReaction) -> Array[String] {
let symbols : Array[String] = []
for term in reaction.reactants {
include_formula_symbols(symbols, term.formula)
}
for term in reaction.products {
include_formula_symbols(symbols, term.formula)
}
symbols
}
///|
fn reaction_symbols(reaction : Reaction) -> Array[String] {
let symbols : Array[String] = []
for term in reaction.reactants {
include_formula_symbols(symbols, term.species.formula)
}
for term in reaction.products {
include_formula_symbols(symbols, term.species.formula)
}
symbols
}
///|
fn parsed_side_atoms(
terms : Array[ParsedStoichTerm],
symbol : String,
) -> Double {
let mut total = 0.0
for term in terms {
total = total + term.coefficient * term.formula.count(symbol).to_double()
}
total
}
///|
fn reaction_side_atoms(terms : Array[StoichTerm], symbol : String) -> Double {
let mut total = 0.0
for term in terms {
total = total +
term.coefficient * term.species.formula.count(symbol).to_double()
}
total
}
///|
pub fn ElementBalance::is_balanced(
self : ElementBalance,
tolerance? : Double = 1.0e-9,
) -> Bool {
abs_double(self.difference) <= tolerance
}
///|
pub fn ParsedReaction::element_balance(
self : ParsedReaction,
) -> Array[ElementBalance] {
let balances : Array[ElementBalance] = []
for symbol in parsed_symbols(self) {
let reactant_atoms = parsed_side_atoms(self.reactants, symbol)
let product_atoms = parsed_side_atoms(self.products, symbol)
balances.push({
symbol,
reactant_atoms,
product_atoms,
difference: product_atoms - reactant_atoms,
})
}
balances
}
///|
pub fn ParsedReaction::is_elementally_balanced(
self : ParsedReaction,
tolerance? : Double = 1.0e-9,
) -> Bool {
for balance in self.element_balance() {
if !balance.is_balanced(tolerance~) {
return false
}
}
true
}
///|
pub fn Reaction::element_balance(self : Reaction) -> Array[ElementBalance] {
let balances : Array[ElementBalance] = []
for symbol in reaction_symbols(self) {
let reactant_atoms = reaction_side_atoms(self.reactants, symbol)
let product_atoms = reaction_side_atoms(self.products, symbol)
balances.push({
symbol,
reactant_atoms,
product_atoms,
difference: product_atoms - reactant_atoms,
})
}
balances
}
///|
pub fn Reaction::is_elementally_balanced(
self : Reaction,
tolerance? : Double = 1.0e-9,
) -> Bool {
for balance in self.element_balance() {
if !balance.is_balanced(tolerance~) {
return false
}
}
true
}