use super::SimpleReactorIVP::SimpleReactorTask;
use crate::ReactorsBVP::SimpleReactorBVP::ReactorError;
use RustedSciThe::symbolic::symbolic_engine::Expr;
use std::collections::HashMap;
impl SimpleReactorTask {
pub fn create_IVP_equations(&mut self) -> Result<(), ReactorError> {
let substances = &self.kindata.substances;
let n = substances.len();
if n == 0 {
return Err(ReactorError::MissingData(
"No substances found in kinetic data".to_string(),
));
}
let equations = self.kindata.vec_of_equations.clone();
let k = equations.len();
if k == 0 {
return Err(ReactorError::MissingData(
"No reactions found in kinetic data".to_string(),
));
}
let T_scale = self.scaling.T_scale;
let qm = Expr::Const(self.L.powi(2) / T_scale); let qc = Expr::Const(self.L.powi(2));
let (Ci_expr, Ci) = Expr::IndexedVars(n, "C");
let Ci: Vec<String> = Ci.iter().map(|var| var.replace("_", "")).collect();
let k_sym_vec = self.kindata.K_sym_vec.as_ref().ok_or_else(|| {
ReactorError::MissingData("Symbolic rate constants not calculated".to_string())
})?;
if k_sym_vec.len() != k {
return Err(ReactorError::InvalidConfiguration(
"Mismatch between number of reactions and rate constants".to_string(),
));
}
let G_react = &self.kindata.stecheodata.stecheo_reags;
let stoich_matrix = &self.kindata.stecheodata.stecheo_matrx;
if stoich_matrix.len() != k {
return Err(ReactorError::InvalidConfiguration(
"Stoichiometric matrix size mismatch".to_string(),
));
}
let Q: Vec<f64> = self.thermal_effects.clone();
if Q.len() != k {
return Err(ReactorError::InvalidConfiguration(
"Thermal effects vector size mismatch".to_string(),
));
}
let Pe_q = self.Pe_q;
let Mi = self.kindata.stecheodata.vec_of_molmasses.clone().unwrap();
let Mi: Vec<f64> = Mi.iter().map(|m| *m / 1000.0).collect();
let ro_m = Expr::Const(self.ro);
let mut map_eq_rate: HashMap<String, Expr> = HashMap::new();
let mut Rates: Vec<Expr> = Vec::new();
for j in 0..k {
let K_j = k_sym_vec[j].clone();
let mut rate_expr = K_j.clone();
for i in 0..n {
let powers_ji = G_react.get(j).and_then(|row| row.get(i)).ok_or_else(|| {
ReactorError::IndexOutOfBounds(format!("G_react[{}][{}] out of bounds", j, i))
})?;
let Powers_ji = Expr::Const(*powers_ji);
let ci_expr = Ci_expr.get(i).ok_or_else(|| {
ReactorError::IndexOutOfBounds(format!("Ci_expr[{}] out of bounds", i))
})?;
let Mi = Expr::Const(Mi[i].clone());
rate_expr = rate_expr * (ro_m.clone() * ci_expr.clone() / Mi).pow(Powers_ji);
}
rate_expr = rate_expr.simplify_();
map_eq_rate.insert(equations[j].clone(), rate_expr.clone());
Rates.push(rate_expr);
}
self.map_eq_rate = map_eq_rate;
let mut vec_of_unknowns: Vec<String> = Vec::new();
let mut vec_of_equations = Vec::new();
let mut map_of_equations: HashMap<String, (String, Expr)> = HashMap::new();
let q = Expr::Var("q".to_string());
vec_of_unknowns.push("Teta".to_string());
let lambda = Expr::Const(self.Lambda);
let RHS_Teta = q.clone() / lambda;
vec_of_equations.push(RHS_Teta.clone());
map_of_equations.insert("Teta".to_string(), ("Teta".to_string(), RHS_Teta));
vec_of_unknowns.push("q".to_string());
let mut Heat_prod = Expr::Const(0.0);
for j in 0..k {
let Qj = Expr::Const(Q[j]); Heat_prod = (Heat_prod + Qj * Rates[j].clone()).simplify_();
}
self.heat_release = Heat_prod.clone();
let Pe_q_expr = Expr::Const(Pe_q);
let RHS_q = q * Pe_q_expr - Heat_prod * qm;
vec_of_equations.push(RHS_q.clone());
map_of_equations.insert("q".to_string(), ("q".to_string(), RHS_q));
for i in 0..n {
let ci = Ci.get(i).ok_or_else(|| {
ReactorError::IndexOutOfBounds(format!("Ci[{}] out of bounds", i))
})?;
vec_of_unknowns.push(ci.clone());
let mut Wi = Expr::Const(0.0); for j in 0..k {
let stoich_coeff_val =
stoich_matrix
.get(j)
.and_then(|row| row.get(i))
.ok_or_else(|| {
ReactorError::IndexOutOfBounds(format!(
"stoich_matrix[{}][{}] out of bounds",
j, i
))
})?;
let stoich_coeff = Expr::Const(*stoich_coeff_val);
let rate_j = Rates[j].clone(); let Mi = Expr::Const(Mi[i]);
Wi = Wi + rate_j * stoich_coeff * Mi;
}
let RHS_i = (-Wi * qc.clone()).simplify_(); vec_of_equations.push(RHS_i.clone());
map_of_equations.insert(format!("{}_flux", substances[i]), (ci.clone(), RHS_i));
}
self.solver.eq_system = vec_of_equations;
self.solver.unknowns = vec_of_unknowns;
self.map_of_equations = map_of_equations;
Ok(())
}
pub fn set_solver_BC(&mut self) -> Result<(), ReactorError> {
let border_conditions: HashMap<String, f64> = self.boundary_condition.clone();
let map_of_equstions: HashMap<String, (String, Expr)> = self.map_of_equations.clone();
let mut BorderConditions: HashMap<String, (usize, f64)> = HashMap::new();
for (key, (variable, _)) in map_of_equstions.iter() {
if key.ends_with("_flux") {
let substance = key.strip_suffix("_flux").unwrap();
match border_conditions.get(&format!("{}_flux", substance)) {
Some(condition_value) => {
BorderConditions.insert(variable.clone(), (1, *condition_value));
}
None => {
BorderConditions.insert(variable.clone(), (1, 1e-10));
}
}
} else if !key.starts_with("Teta") && !key.starts_with("q") {
match border_conditions.get(key) {
Some(condition_value) => {
BorderConditions.insert(variable.clone(), (0, *condition_value));
}
None => {}
}
}
}
let dT = self.scaling.dT;
let T_scale = self.scaling.T_scale;
match border_conditions.get("T") {
Some(T0) => {
let Teta = (*T0 - dT) / T_scale; BorderConditions.insert("Teta".to_string(), (0, Teta)); }
None => {
return Err(ReactorError::MissingData(
"Initial temperature boundary condition 'T' not found".to_string(),
));
}
}
match border_conditions.get("q") {
Some(q_final) => {
BorderConditions.insert("q".to_string(), (1, *q_final)); }
None => {
BorderConditions.insert("q".to_string(), (1, 1e-10));
}
}
self.solver.BorderConditions = BorderConditions;
Ok(())
}
}