use crate::numerical::ODE_api2::{SolverParam, SolverType, UniversalODESolver};
use crate::numerical::ShootingBVP::Shooting_simple::{
BoundaryCondition, BoundaryConditionType, BoundaryValueProblem, RK45_ivp_solver_and_mesh,
ShootingMethodResult, ShootingMethodSolver,
};
use crate::symbolic::symbolic_engine::Expr;
use log::{debug, info};
use nalgebra::{DMatrix, DVector};
use simplelog::*;
use std::collections::HashMap;
pub struct BVPShooting {
pub eq_vec: Vec<Expr>,
pub values: Vec<String>,
pub arg: String,
pub BoundaryConditions: HashMap<String, Vec<(usize, f64)>>,
pub solver: ShootingMethodSolver,
pub ivp_solver: SolverType,
pub solver_params: HashMap<String, SolverParam>,
pub borders: (f64, f64),
pub solution: DMatrix<f64>,
pub full_sol: ShootingMethodResult,
}
impl BVPShooting {
pub fn new(
eq_vec: Vec<Expr>,
values: Vec<String>,
arg: String,
BoundaryConditions: HashMap<String, Vec<(usize, f64)>>,
borders: (f64, f64),
) -> Self {
BVPShooting {
eq_vec,
values,
arg,
solver: ShootingMethodSolver::new(),
ivp_solver: SolverType::BDF,
solver_params: HashMap::new(),
BoundaryConditions: BoundaryConditions,
borders: borders,
solution: DMatrix::zeros(0, 0),
full_sol: ShootingMethodResult::default(),
}
}
pub fn set_parameters(&mut self, ivp_solver: SolverType, params: HashMap<String, SolverParam>) {
self.ivp_solver = ivp_solver;
self.solver_params = params;
}
pub fn generate_eq_closure(&self) -> Box<dyn Fn(f64, &DVector<f64>) -> DVector<f64>> {
let eq_vec = self.eq_vec.clone();
let values = self.values.clone();
let arg = self.arg.clone();
let n = eq_vec.len();
let residual = Box::new(move |t: f64, y: &DVector<f64>| -> DVector<f64> {
let mut args = Vec::with_capacity(1 + n);
args.push(t);
args.extend(y.iter().cloned());
let mut res = DVector::zeros(n);
let mut all_var_names = vec![arg.as_str()];
all_var_names.extend(values.iter().map(|s| s.as_str()));
for i in 0..n {
res[i] = eq_vec[i].eval_expression(all_var_names.as_slice(), &args);
}
res
});
residual
}
pub fn BC_create(&self) -> (BoundaryCondition, BoundaryCondition) {
let values = &self.values;
let map_of_bc = &self.BoundaryConditions;
let mut left_bc: Option<BoundaryCondition> = None;
let mut right_bc: Option<BoundaryCondition> = None;
for var_name in values {
if let Some(conditions) = map_of_bc.get(var_name) {
let var_idx = values.iter().position(|v| v == var_name).unwrap();
for (boundary_idx, value) in conditions {
let bc_type: BoundaryConditionType = if var_idx == 0 {
BoundaryConditionType::Dirichlet } else {
BoundaryConditionType::Neumann };
let bc = BoundaryCondition {
value: *value,
bc_type: bc_type,
};
if *boundary_idx == 0 {
if left_bc.is_none() {
left_bc = Some(bc);
}
} else {
if right_bc.is_none() {
right_bc = Some(bc);
}
}
}
}
}
let left = left_bc.unwrap_or(BoundaryCondition {
value: 0.0,
bc_type: BoundaryConditionType::Dirichlet,
});
let right = right_bc.unwrap_or(BoundaryCondition {
value: 0.0,
bc_type: BoundaryConditionType::Dirichlet,
});
debug!("Left BC: value={}, type={:?}", left.value, left.bc_type);
debug!("Right BC: value={}, type={:?}", right.value, right.bc_type);
(left, right)
}
pub fn simple_solve(
&mut self,
initial_guess: f64,
tolerance: f64,
max_iterations: usize,
step_size: f64,
) {
let ode_system = self.generate_eq_closure();
let (a, b) = self.borders;
let (left_bc, right_bc) = self.BC_create();
let problem = BoundaryValueProblem::new(ode_system, a, b, left_bc, right_bc);
let mut solver = ShootingMethodSolver {
initial_guess,
tolerance,
max_iterations,
step_size,
result: ShootingMethodResult::default(),
};
let result = solver.solve(&problem, |x0, y0, x_end, h| {
RK45_ivp_solver_and_mesh(x0, y0, x_end, h, &problem.ode_system)
});
match result {
Ok(result_struct) => {
self.full_sol = result_struct.clone();
let solution = result_struct.y;
self.solution = solution;
}
Err(e) => {
println!("Error: {}", e);
}
}
}
pub fn solve_with_certain_ivp(
&mut self,
initial_guess: f64,
tolerance: f64,
max_iterations: usize,
step_size: f64,
ivp_solver: SolverType,
params: HashMap<String, SolverParam>,
) {
let ode_system = self.generate_eq_closure();
let (t0, tend) = self.borders;
let (left_bc, right_bc) = self.BC_create();
let problem = BoundaryValueProblem::new(ode_system, t0, tend, left_bc, right_bc);
let ivp_closure = Self::creating_IVP_closure(
self.eq_vec.clone(),
self.values.clone(),
self.arg.clone(),
ivp_solver.clone(),
params,
);
let mut solver = ShootingMethodSolver {
initial_guess,
tolerance,
max_iterations,
step_size,
result: ShootingMethodResult::default(),
};
let result = solver.solve(&problem, |x0, y0, x_end, h| {
let (y_matrix, x_vec) = ivp_closure(x0, y0, x_end, h);
(y_matrix, x_vec)
});
match result {
Ok(result_struct) => {
self.full_sol = result_struct.clone();
let solution = result_struct.y;
self.solution = solution;
}
Err(e) => {
println!("Error: {}", e);
}
}
}
pub fn get_solution(&self) -> ShootingMethodResult {
return self.full_sol.clone();
}
pub fn get_x(&self) -> DVector<f64> {
return self.full_sol.x_mesh.clone();
}
pub fn get_y(&self) -> DMatrix<f64> {
return self.full_sol.y.clone();
}
pub fn IVP_solver(
eq_system: Vec<Expr>,
values: Vec<String>,
arg: String,
t0: f64,
y0: DVector<f64>,
t_bound: f64,
solver_type: SolverType,
params: HashMap<String, SolverParam>,
step: f64,
) -> Result<(DVector<f64>, DMatrix<f64>), String> {
let mut solver =
UniversalODESolver::new(eq_system, values, arg, solver_type, t0, y0, t_bound);
let mut params = params;
let add_step_size = HashMap::from([("step_size".to_string(), SolverParam::Float(step))]);
params.extend(add_step_size);
info!("solving with params {:?}", params);
solver.set_parameters(params);
solver.initialize();
solver.solve();
let (x, y) = solver.get_result();
match (x, y) {
(Some(x_vec), Some(y_mat)) => Ok((x_vec, y_mat)),
_ => Err("IVP solver failed to produce results".to_string()),
}
}
pub fn creating_IVP_closure(
eq_system: Vec<Expr>,
values: Vec<String>,
arg: String,
solver_type: SolverType,
params: HashMap<String, SolverParam>,
) -> Box<dyn Fn(f64, DVector<f64>, f64, f64) -> (DMatrix<f64>, DVector<f64>)> {
let f: Box<dyn Fn(f64, DVector<f64>, f64, f64) -> (DMatrix<f64>, DVector<f64>) + '_> =
Box::new(
move |t0: f64, ivp_initial_condition: DVector<f64>, t_end, step| {
info!("\n ivp_initial_condition: {:}", ivp_initial_condition);
match Self::IVP_solver(
eq_system.clone(),
values.clone(),
arg.clone(),
t0,
ivp_initial_condition,
t_end,
solver_type.clone(),
params.clone(),
step,
) {
Ok((x_vec, y_mat)) => {
let y_mat = y_mat.transpose();
(y_mat, x_vec)
}
Err(e) => {
eprintln!("IVP solver error: {}", e);
panic!("IVP solver failed: {}", e);
}
}
},
);
f
}
}
#[allow(dead_code)]
fn init_logger() {
let _ = SimpleLogger::init(LevelFilter::Debug, Config::default());
}