use crate::symbolic::symbolic_engine::Expr;
use crate::symbolic::symbolic_functions::Jacobian;
use log::info;
use nalgebra::{DMatrix, DVector, Matrix};
impl Jacobian {
pub fn generate_NR_solver_for_Radau(
&mut self,
eq_system: Vec<Expr>,
values: Vec<String>,
parameters: Vec<String>,
arg: String,
) {
self.set_vector_of_functions(eq_system);
self.set_variables(values.iter().map(|x| x.as_str()).collect());
self.calc_jacobian();
self.find_bandwidths();
let ncols = self.symbolic_jacobian.len();
let nrows = self.symbolic_jacobian[0].len();
assert!(nrows == ncols);
self.jacobian_generate_IVP_Radau(arg.as_str(), values.clone(), parameters.clone());
let values_str = values.iter().map(|x| x.as_str()).collect::<Vec<&str>>();
let parameters_str = parameters.iter().map(|x| x.as_str()).collect::<Vec<&str>>();
self.lambdify_funcvector_with_parameters(
arg.as_str(),
values_str.clone(),
parameters_str.clone(),
);
self.vector_funvector_with_parameters_DVector(
arg.as_str(),
values_str.clone(),
parameters_str.clone(),
);
}
pub fn jacobian_generate_IVP_Radau(
&mut self,
arg: &str,
variable_str: Vec<String>,
parameters: Vec<String>,
) {
let symbolic_jacobian = self.symbolic_jacobian.clone();
let symbolic_jacobian_rc = symbolic_jacobian.clone();
let vector_of_functions_len = self.vector_of_functions.len();
let vector_of_variables_len = self.vector_of_variables.len();
let bandwidth = self.bandwidth;
let new_jac = Jacobian::calc_jacobian_fun_with_parameters(
symbolic_jacobian_rc,
vector_of_functions_len,
vector_of_variables_len,
variable_str.iter().map(|s| s.to_string()).collect(),
parameters.iter().map(|s| s.to_string()).collect(),
arg.to_string(),
bandwidth.unwrap(),
);
self.function_jacobian_IVP_DMatrix = new_jac;
}
pub fn calc_jacobian_fun_with_parameters(
jac: Vec<Vec<Expr>>,
vector_of_functions_len: usize,
vector_of_variables_len: usize,
variable_str: Vec<String>,
parameters: Vec<String>,
arg: String,
bandwidth: (usize, usize),
) -> Box<dyn Fn(f64, &DVector<f64>) -> DMatrix<f64>> {
let mut variables_and_parameters: Vec<String> = variable_str.clone();
variables_and_parameters.extend(parameters.clone());
let (kl, ku) = bandwidth;
Box::new(move |x: f64, v: &DVector<f64>| -> DMatrix<f64> {
let mut new_function_jacobian: DMatrix<f64> =
DMatrix::zeros(vector_of_functions_len, vector_of_variables_len);
let variales_and_parameters: Vec<&str> = variables_and_parameters
.iter()
.map(|s| s.as_str())
.collect();
for i in 0..vector_of_functions_len {
let (right_border, left_border) = if kl == 0 && ku == 0 {
let right_border = vector_of_variables_len;
let left_border = 0;
(right_border, left_border)
} else {
let right_border = std::cmp::min(i + ku + 1, vector_of_variables_len);
let left_border = if i as i32 - (kl as i32) - 1 < 0 {
0
} else {
i - kl - 1
};
(right_border, left_border)
};
for j in left_border..right_border {
let partial_func = Expr::lambdify_IVP_owned(
jac[i][j].clone(),
arg.as_str(),
variales_and_parameters.clone(),
);
let v_vec: Vec<f64> = v.iter().cloned().collect();
new_function_jacobian[(i, j)] = if jac[i][j].clone() == Expr::Const(0.0) {
0.0
} else {
partial_func(x, v_vec.clone())
};
}
}
new_function_jacobian
})
}
pub fn lambdify_funcvector_with_parameters(
&mut self,
arg: &str,
variable_str: Vec<&str>,
parameters: Vec<&str>,
) {
let mut variable_and_parameters: Vec<&str> = variable_str.clone();
variable_and_parameters.extend(parameters.clone());
println!("variable_and_parameters = {:?} \n", variable_and_parameters);
let mut result: Vec<Box<dyn Fn(f64, Vec<f64>) -> f64>> = Vec::new();
for func in self.vector_of_functions.clone() {
let func = Expr::lambdify_IVP_owned(func, arg, variable_and_parameters.clone());
result.push(func);
}
self.lambdified_functions_IVP = result;
}
pub fn vector_funvector_with_parameters_DVector(
&mut self,
arg: &str,
variable_str: Vec<&str>,
parameters: Vec<&str>,
) {
let vector_of_functions = &self.vector_of_functions;
fn f(
vector_of_functions: Vec<Expr>,
arg: String,
variable_and_parameters: Vec<String>,
) -> Box<dyn Fn(f64, &DVector<f64>) -> DVector<f64> + 'static> {
Box::new(move |x: f64, v: &DVector<f64>| -> DVector<f64> {
let mut result: DVector<f64> = DVector::zeros(vector_of_functions.len());
for (i, func) in vector_of_functions.iter().enumerate() {
let func = Expr::lambdify_IVP_owned(
func.to_owned(),
arg.as_str(),
variable_and_parameters
.iter()
.map(|s| s.as_str())
.collect::<Vec<_>>()
.clone(),
);
result[i] = func(x, v.iter().cloned().collect());
}
result
}) } let mut variable_and_parameters = variable_str.clone();
variable_and_parameters.extend(parameters.clone());
self.lambdified_functions_IVP_DVector = f(
vector_of_functions.to_owned(),
arg.to_string(),
variable_and_parameters
.clone()
.iter()
.map(|s| s.to_string())
.collect(),
);
}
}
pub struct RadauNewton {
pub eq_system: Vec<Expr>, pub k_values: DVector<f64>, pub k_variables: Vec<String>, pub parameters: Vec<String>, pub arg: String, pub tolerance: f64,
pub max_iterations: usize,
pub max_error: f64,
pub result: Option<DVector<f64>>, pub jacobian_symbolic: Option<Vec<Vec<Expr>>>, pub y_n: DVector<f64>, pub h: f64, pub t_n: f64, pub n_vars: usize, pub n_stages: usize, pub parallel: bool,
pub fun: Box<dyn Fn(f64, &DVector<f64>) -> DVector<f64>>,
pub jac: Option<Box<dyn FnMut(f64, &DVector<f64>) -> DMatrix<f64>>>,
pub n: usize,
}
impl RadauNewton {
pub fn new(
eq_system: Vec<Expr>,
k_variables: Vec<String>,
initial_guess: Option<DVector<f64>>,
parameters: Vec<String>,
arg: String,
tolerance: f64,
max_iterations: usize,
n_vars: usize,
n_stages: usize,
) -> RadauNewton {
let initial_guess = initial_guess.unwrap_or_else(|| DVector::zeros(k_variables.len()));
RadauNewton {
eq_system,
k_values: initial_guess.clone(),
k_variables: k_variables.clone(),
parameters,
arg,
tolerance,
max_iterations,
max_error: 1e-3,
result: None,
jacobian_symbolic: None,
y_n: DVector::zeros(n_vars),
h: 0.0,
t_n: 0.0,
n_vars,
n_stages,
parallel: false,
fun: Box::new(|_t, y| y.clone()),
jac: None,
n: k_variables.len(),
}
}
pub fn eq_generate(&mut self) {
info!("Generating Radau Newton equations and jacobian");
let mut jacobian_instance = Jacobian::new();
match self.parallel {
true => {
println!("\n Parallel version enabled \n");
jacobian_instance.generate_NR_solver_for_Radau_parallel(
self.eq_system.clone(),
self.k_variables.clone(),
self.parameters.clone(),
self.arg.clone(),
);
}
false => {
jacobian_instance.generate_NR_solver_for_Radau(
self.eq_system.clone(),
self.k_variables.clone(),
self.parameters.clone(),
self.arg.clone(),
);
}
}
self.jacobian_symbolic = Some(jacobian_instance.symbolic_jacobian.clone());
let fun: Box<dyn Fn(f64, &DVector<f64>) -> DVector<f64>> =
jacobian_instance.lambdified_functions_IVP_DVector;
let jac = jacobian_instance.function_jacobian_IVP_DMatrix;
let jac_wrapped: Box<dyn FnMut(f64, &DVector<f64>) -> DMatrix<f64>> =
Box::new(move |t: f64, y: &DVector<f64>| -> DMatrix<f64> { jac(t, &y) });
self.fun = fun;
self.jac = Some(jac_wrapped);
self.n = self.eq_system.len();
info!("Radau Newton functions generated successfully");
}
pub fn set_parameters(&mut self, y_n: DVector<f64>, h: f64, t_n: f64) {
self.y_n = y_n;
self.h = h;
self.t_n = t_n;
info!(
"Setting Radau Newton parameters: t_n = {:.6}, h = {:.6}",
t_n, h
);
}
pub fn set_parallel(&mut self, parallel: bool) {
self.parallel = parallel;
}
pub fn set_initial_guess(&mut self, guess: DVector<f64>) {
self.k_values = guess;
}
pub fn iteration(&mut self) -> DVector<f64> {
let t = self.t_n;
let y_n: DVector<f64> = self.y_n.clone();
let k_vector: DVector<f64> = self.k_values.clone();
info!("Radau Newton iteration: k_vector = {:?}", k_vector);
let mut param_vector: DVector<f64> = k_vector.clone();
param_vector.extend(y_n.iter().cloned());
let param_vector: DVector<f64> = param_vector.push(self.h);
let f = (self.fun)(t, ¶m_vector);
let new_j = &self.jac.as_mut().unwrap()(t, ¶m_vector);
let lu = new_j.clone().lu();
let neg_f = -1.0 * f;
let delta_k = lu.solve(&neg_f).expect("Jacobian should be invertible");
info!("Radau Newton iteration: step = {:?}", delta_k);
let new_k = k_vector + delta_k;
new_k
}
pub fn solve(&mut self) -> Option<DVector<f64>> {
info!(
"Starting Radau Newton solve with {} unknowns",
self.k_values.len()
);
let mut k = self.k_values.clone();
let mut iteration_count = 0;
while iteration_count < self.max_iterations {
info!("Radau Newton iteration {}", iteration_count);
self.k_values = k.clone();
let k_new = self.iteration();
info!(
"Radau Newton iteration {}: k_new = {:?}",
iteration_count, k_new
);
let delta_k = &k_new - &k;
let error = Matrix::norm(&delta_k);
info!(
"Radau Newton iteration {}: error = {:.2e}",
iteration_count, error
);
info!("end of iteration {} \n", iteration_count);
if error < self.tolerance {
info!("Radau Newton converged in {} iterations", iteration_count);
self.result = Some(k_new.clone());
self.max_error = error;
return Some(k_new);
}
k = k_new;
iteration_count += 1;
}
info!(
"Radau Newton failed to converge after {} iterations",
self.max_iterations
);
None
}
pub fn get_result(&self) -> Option<DVector<f64>> {
self.result.clone()
}
}
#[cfg(test)]
mod tests2 {
use super::*;
use approx::assert_relative_eq;
use nalgebra::DVector;
#[test]
fn test_generate_nr_solver_for_radau_basic() {
let mut jacobian = Jacobian::new();
let eq1 = Expr::parse_expression("K01 - y");
let eq_system = vec![eq1];
let values = vec!["K01".to_string()]; jacobian.vector_of_functions = eq_system;
jacobian.set_variables(values.iter().map(|x| x.as_str()).collect());
jacobian.calc_jacobian();
jacobian.find_bandwidths();
info!("Jacobian: {:?}", jacobian.symbolic_jacobian);
}
#[test]
fn test_generate_nr_solver_for_radau_multiple_stages() {
let mut jacobian = Jacobian::new();
let eq1 = Expr::parse_expression("K00 - y0 - h*K10");
let eq2 = Expr::parse_expression("K10 - y0 - h*K00");
let eq_system = vec![eq1, eq2];
let values = vec!["K00".to_string(), "K10".to_string()]; let parameters = vec!["y0".to_string(), "h".to_string()]; let arg = "t".to_string();
jacobian.vector_of_functions = eq_system;
jacobian.set_variables(values.iter().map(|x| x.as_str()).collect());
jacobian.calc_jacobian();
jacobian.find_bandwidths();
info!("sym jac");
for (i, row) in jacobian.symbolic_jacobian.iter().enumerate() {
for (j, col) in row.iter().enumerate() {
print!("\n({}, {}), \n {} ", i, j, col);
}
}
jacobian.jacobian_generate_IVP_Radau(arg.as_str(), values.clone(), parameters.clone());
let J_fun = jacobian.function_jacobian_IVP_DMatrix.as_ref();
let K_values = DVector::from_vec(vec![0.0, 0.0]);
let h = 0.0;
let y_0 = 0.0;
let parameters_val = vec![h, y_0];
let mut values_and_parameters = K_values.clone();
values_and_parameters.extend(parameters_val);
info!("values and parameters = {:?} \n", values_and_parameters);
let J = J_fun(0.0, &values_and_parameters);
info!("J = {:?}", J.clone());
assert_eq!(J.shape(), (2, 2));
assert_eq!(J.data.as_vec().to_owned(), vec![1.0, 0.0, 0.0, 1.0]);
let values_str = values.iter().map(|x| x.as_str()).collect::<Vec<&str>>();
let parameters_str = parameters.iter().map(|x| x.as_str()).collect::<Vec<&str>>();
jacobian.vector_funvector_with_parameters_DVector(
arg.as_str(),
values_str.clone(),
parameters_str.clone(),
);
let result = (jacobian.lambdified_functions_IVP_DVector)(0.0, &values_and_parameters);
info!("result = {:?} \n", result);
}
#[test]
fn test_generate_nr_solver_for_radau_multiple_variables() {
let mut jacobian = Jacobian::new();
let eq1 = Expr::parse_expression("K00 - y0");
let eq2 = Expr::parse_expression("K01 - y1");
let eq_system = vec![eq1, eq2];
let values = vec!["K00".to_string(), "K01".to_string()]; let parameters = vec!["y0".to_string(), "y1".to_string()];
let arg = "t".to_string();
jacobian.generate_NR_solver_for_Radau(eq_system, values, parameters, arg);
assert_eq!(jacobian.lambdified_functions_IVP.len(), 2);
}
#[test]
fn test_jacobian_generate_ivp_radau() {
let mut jacobian = Jacobian::new();
let eq1 = Expr::parse_expression("K00 - y0");
jacobian.set_vector_of_functions(vec![eq1]);
jacobian.set_variables(vec!["K00"]);
jacobian.calc_jacobian();
jacobian.find_bandwidths();
let values = vec!["K00".to_string()];
let parameters = vec!["y0".to_string(), "h".to_string()];
jacobian.jacobian_generate_IVP_Radau("t", values, parameters);
let jac_fn = &jacobian.function_jacobian_IVP_DMatrix;
let test_vars = DVector::from_vec(vec![1.0, 2.0, 0.1]); let jac_result = jac_fn(0.0, &test_vars);
assert_eq!(jac_result.nrows(), 1);
assert_eq!(jac_result.ncols(), 1);
assert_relative_eq!(jac_result[(0, 0)], 1.0, epsilon = 1e-10);
}
#[test]
fn test_calc_jacobian_fun_with_parameters() {
let jac_symbolic = vec![
vec![Expr::Const(1.0), Expr::Const(0.0)], vec![Expr::Const(0.0), Expr::Const(1.0)], ];
let variable_str = vec!["K00".to_string(), "K10".to_string()];
let parameters = vec!["y0".to_string(), "h".to_string()];
let arg = "t".to_string();
let bandwidth = (0, 0);
let jac_fn = Jacobian::calc_jacobian_fun_with_parameters(
jac_symbolic,
2, 2, variable_str,
parameters,
arg,
bandwidth,
);
let test_vars = DVector::from_vec(vec![1.0, 2.0, 3.0, 0.1]); let jac_result = jac_fn(0.0, &test_vars);
assert_eq!(jac_result.nrows(), 2);
assert_eq!(jac_result.ncols(), 2);
assert_relative_eq!(jac_result[(0, 0)], 1.0, epsilon = 1e-10);
assert_relative_eq!(jac_result[(0, 1)], 0.0, epsilon = 1e-10);
assert_relative_eq!(jac_result[(1, 0)], 0.0, epsilon = 1e-10);
assert_relative_eq!(jac_result[(1, 1)], 1.0, epsilon = 1e-10);
}
#[test]
fn test_lambdify_funcvector_with_parameters() {
let mut jacobian = Jacobian::new();
let eq1 = Expr::parse_expression("K00 - y0");
let eq2 = Expr::parse_expression("K10 - y1");
jacobian.set_vector_of_functions(vec![eq1, eq2]);
let variable_str = vec!["K00", "K10"];
let parameters = vec!["y0", "y1", "h"];
jacobian.lambdify_funcvector_with_parameters("t", variable_str, parameters);
assert_eq!(jacobian.lambdified_functions_IVP.len(), 2);
let test_values = vec![1.0, 2.0, 3.0, 4.0, 0.1]; let result1 = jacobian.lambdified_functions_IVP[0](0.0, test_values.clone());
let result2 = jacobian.lambdified_functions_IVP[1](0.0, test_values.clone());
assert_relative_eq!(result1, -2.0, epsilon = 1e-10);
assert_relative_eq!(result2, -2.0, epsilon = 1e-10);
}
#[test]
fn test_vector_funvector_with_parameters_dvector() {
let mut jacobian = Jacobian::new();
let eq1 = Expr::parse_expression("K00 - y0");
let eq2 = Expr::parse_expression("K10 - y1");
jacobian.set_vector_of_functions(vec![eq1, eq2]);
let variable_str = vec!["K00", "K10"];
let parameters = vec!["y0", "y1", "h"];
jacobian.vector_funvector_with_parameters_DVector("t", variable_str, parameters);
let test_vars = DVector::from_vec(vec![1.0, 2.0, 3.0, 4.0, 0.1]); let result = (jacobian.lambdified_functions_IVP_DVector)(0.0, &test_vars);
assert_eq!(result.len(), 2);
assert_relative_eq!(result[0], -2.0, epsilon = 1e-10); assert_relative_eq!(result[1], -2.0, epsilon = 1e-10); }
#[test]
fn test_vector_funvector_with_parameters_dvector2() {
let mut jacobian = Jacobian::new();
let eq1 = Expr::parse_expression("K00 - h*y0");
let eq2 = Expr::parse_expression("K10 - h*y1");
jacobian.set_vector_of_functions(vec![eq1, eq2]);
let variable_str = vec!["K00", "K10"];
let parameters = vec!["y0", "y1", "h"];
jacobian.vector_funvector_with_parameters_DVector("t", variable_str, parameters);
let test_vars = DVector::from_vec(vec![1.0, 2.0, 3.0, 4.0, 1.0]); let result = (jacobian.lambdified_functions_IVP_DVector)(0.0, &test_vars);
assert_eq!(result.len(), 2);
assert_relative_eq!(result[0], -2.0, epsilon = 1e-10); assert_relative_eq!(result[1], -2.0, epsilon = 1e-10); }
#[test]
fn test_radau_system_with_time_dependency() {
let mut jacobian = Jacobian::new();
let eq1 = Expr::parse_expression("K00 - t*y0");
let eq_system = vec![eq1];
let values = vec!["K00".to_string()];
let parameters = vec!["y0".to_string(), "h".to_string()];
let arg = "t".to_string();
jacobian.generate_NR_solver_for_Radau(eq_system, values, parameters, arg);
let test_vars1 = DVector::from_vec(vec![2.0, 1.0, 0.1]); let result1 = (jacobian.lambdified_functions_IVP_DVector)(1.0, &test_vars1);
assert_relative_eq!(result1[0], 1.0, epsilon = 1e-10);
let result2 = (jacobian.lambdified_functions_IVP_DVector)(2.0, &test_vars1);
assert_relative_eq!(result2[0], 0.0, epsilon = 1e-10);
}
#[test]
fn test_radau_system_with_step_size_dependency() {
let mut jacobian = Jacobian::new();
let eq1 = Expr::parse_expression("K00 - h*y0");
let eq_system = vec![eq1];
let values = vec!["K00".to_string()];
let parameters = vec!["y0".to_string(), "h".to_string()];
let arg = "t".to_string();
jacobian.generate_NR_solver_for_Radau(eq_system, values, parameters, arg);
let test_vars1 = DVector::from_vec(vec![1.0, 2.0, 0.1]); let result1 = (jacobian.lambdified_functions_IVP_DVector)(0.0, &test_vars1);
assert_relative_eq!(result1[0], 0.8, epsilon = 1e-10);
let test_vars2 = DVector::from_vec(vec![1.0, 2.0, 0.5]); let result2 = (jacobian.lambdified_functions_IVP_DVector)(0.0, &test_vars2);
assert_relative_eq!(result2[0], 0.0, epsilon = 1e-10);
}
#[test]
fn test_radau_jacobian_with_coupling() {
let mut jacobian = Jacobian::new();
let eq1 = Expr::parse_expression("K00 - K10");
let eq2 = Expr::parse_expression("K10 - K00");
jacobian.set_vector_of_functions(vec![eq1, eq2]);
jacobian.set_variables(vec!["K00", "K10"]);
jacobian.calc_jacobian();
jacobian.find_bandwidths();
let values = vec!["K00".to_string(), "K10".to_string()];
let parameters = vec!["y0".to_string(), "h".to_string()];
jacobian.jacobian_generate_IVP_Radau("t", values.clone(), parameters.clone());
info!("residual: {:?}", jacobian.vector_of_functions);
let test_vars = DVector::from_vec(vec![1.0, 2.0, 3.0, 0.1]); jacobian.vector_funvector_with_parameters_DVector(
"t",
values.iter().map(|x| x.as_str()).collect(),
parameters.iter().map(|x| x.as_str()).collect(),
);
let result = (jacobian.lambdified_functions_IVP_DVector)(0.0, &test_vars);
assert_eq!(result.len(), 2);
info!("result: {:?}", result);
assert_relative_eq!(result[0], -1.0, epsilon = 1e-10); assert_relative_eq!(result[1], 1.0, epsilon = 1e-10); }
}
#[cfg(test)]
mod tests_radau_newton {
use super::*;
use approx::assert_relative_eq;
use nalgebra::DVector;
#[test]
fn test_radau_newton_new() {
let eq1 = Expr::parse_expression("K00 - y0");
let eq_system = vec![eq1];
let k_variables = vec!["K00".to_string()];
let parameters = vec!["y0".to_string(), "h".to_string()];
let arg = "t".to_string();
let radau_newton = RadauNewton::new(
eq_system,
k_variables,
None,
parameters,
arg,
1e-6,
50,
1,
1,
);
assert_eq!(radau_newton.k_variables.len(), 1);
assert_eq!(radau_newton.n_vars, 1);
assert_eq!(radau_newton.n_stages, 1);
assert_eq!(radau_newton.parameters.len(), 2);
}
#[test]
fn test_radau_newton_simple_solve() {
let eq1 = Expr::parse_expression("K00 - y0");
let eq_system = vec![eq1];
let k_variables = vec!["K00".to_string()];
let parameters = vec!["y0".to_string(), "h".to_string()];
let arg = "t".to_string();
let mut radau_newton = RadauNewton::new(
eq_system,
k_variables,
None,
parameters,
arg,
1e-10,
50,
1,
1,
);
radau_newton.eq_generate();
let y_n = DVector::from_vec(vec![2.0]);
radau_newton.set_parameters(y_n, 0.1, 0.0);
radau_newton.set_initial_guess(DVector::from_vec(vec![0.0]));
let result = radau_newton.solve();
assert!(result.is_some());
let k_solution = result.unwrap();
assert_eq!(k_solution.len(), 1);
assert_relative_eq!(k_solution[0], 2.0, epsilon = 1e-8);
}
#[test]
fn test_radau_newton_two_variables() {
let eq1 = Expr::parse_expression("K00 - y0");
let eq2 = Expr::parse_expression("K01 - y1");
let eq_system = vec![eq1, eq2];
let k_variables = vec!["K00".to_string(), "K01".to_string()];
let parameters = vec!["y0".to_string(), "y1".to_string(), "h".to_string()];
let arg = "t".to_string();
let mut radau_newton = RadauNewton::new(
eq_system,
k_variables,
None,
parameters,
arg,
1e-10,
50,
2,
1,
);
radau_newton.eq_generate();
let y_n = DVector::from_vec(vec![1.5, 3.0]);
radau_newton.set_parameters(y_n, 0.1, 0.0);
radau_newton.set_initial_guess(DVector::from_vec(vec![0.0, 0.0]));
let result = radau_newton.solve();
assert!(result.is_some());
let k_solution = result.unwrap();
assert_eq!(k_solution.len(), 2);
assert_relative_eq!(k_solution[0], 1.5, epsilon = 1e-8);
assert_relative_eq!(k_solution[1], 3.0, epsilon = 1e-8);
}
#[test]
fn test_radau_newton_with_step_size() {
let eq1 = Expr::parse_expression("K00 - h*y0");
let eq_system = vec![eq1];
let k_variables = vec!["K00".to_string()];
let parameters = vec!["y0".to_string(), "h".to_string()];
let arg = "t".to_string();
let mut radau_newton = RadauNewton::new(
eq_system,
k_variables,
None,
parameters,
arg,
1e-10,
50,
1,
1,
);
radau_newton.eq_generate();
let y_n = DVector::from_vec(vec![2.0]);
radau_newton.set_parameters(y_n, 0.5, 0.0);
radau_newton.set_initial_guess(DVector::from_vec(vec![0.0]));
let result = radau_newton.solve();
assert!(result.is_some());
let k_solution = result.unwrap();
assert_relative_eq!(k_solution[0], 1.0, epsilon = 1e-8);
}
#[test]
fn test_radau_newton_with_time_dependency() {
let eq1 = Expr::parse_expression("K00 - t*y0");
let eq_system = vec![eq1];
let k_variables = vec!["K00".to_string()];
let parameters = vec!["y0".to_string(), "h".to_string()];
let arg = "t".to_string();
let mut radau_newton = RadauNewton::new(
eq_system,
k_variables,
None,
parameters,
arg,
1e-10,
50,
1,
1,
);
radau_newton.eq_generate();
let y_n = DVector::from_vec(vec![3.0]);
radau_newton.set_parameters(y_n, 0.1, 2.0);
radau_newton.set_initial_guess(DVector::from_vec(vec![0.0]));
let result = radau_newton.solve();
assert!(result.is_some());
let k_solution = result.unwrap();
assert_relative_eq!(k_solution[0], 6.0, epsilon = 1e-8);
}
#[test]
fn test_radau_newton_coupled_system() {
let eq1 = Expr::parse_expression("K00 - (K10 + y0)");
let eq2 = Expr::parse_expression("K10 - y1");
let eq_system = vec![eq1, eq2];
let k_variables = vec!["K00".to_string(), "K10".to_string()];
let parameters = vec!["y0".to_string(), "y1".to_string(), "h".to_string()];
let arg = "t".to_string();
let mut radau_newton = RadauNewton::new(
eq_system,
k_variables,
Some(DVector::from_vec(vec![20.0, 20.0])),
parameters,
arg,
1e-10,
50,
2,
2,
);
radau_newton.eq_generate();
let sym_jac: &Vec<Vec<Expr>> = &radau_newton.jacobian_symbolic.clone().unwrap().clone();
info!("sym_jac: {:?}", sym_jac);
let y_n = DVector::from_vec(vec![1.0, 50.0]);
radau_newton.set_parameters(y_n.clone(), 0.1, 0.0);
let result = radau_newton.solve();
assert!(result.is_some());
let k_solution = result.unwrap();
info!("solution is: {:?}", k_solution);
assert_eq!(k_solution.len(), 2);
let res = k_solution[0] - k_solution[1] - y_n[0];
info!("residual: {:?}", res);
let res2 = k_solution[1] - y_n[1];
info!("residual: {:?}", res2);
assert_relative_eq!(k_solution[1], 50.0, epsilon = 1e-8); assert_relative_eq!(k_solution[0], 51.0, epsilon = 1e-8); }
#[test]
fn test_radau_newton_nonlinear_system() {
let eq1 = Expr::parse_expression("K00*K00 - y0");
let eq_system = vec![eq1];
let k_variables = vec!["K00".to_string()];
let parameters = vec!["y0".to_string(), "h".to_string()];
let arg = "t".to_string();
let mut radau_newton = RadauNewton::new(
eq_system,
k_variables,
None,
parameters,
arg,
1e-10,
50,
1,
1,
);
radau_newton.eq_generate();
let y_n = DVector::from_vec(vec![4.0]);
radau_newton.set_parameters(y_n, 0.1, 0.0);
radau_newton.set_initial_guess(DVector::from_vec(vec![1.5]));
let result = radau_newton.solve();
assert!(result.is_some());
let k_solution = result.unwrap();
assert_relative_eq!(k_solution[0], 2.0, epsilon = 1e-8);
}
#[test]
fn test_radau_newton_parameter_changes() {
let eq1 = Expr::parse_expression("K00 - y0");
let eq_system = vec![eq1];
let k_variables = vec!["K00".to_string()];
let parameters = vec!["y0".to_string(), "h".to_string()];
let arg = "t".to_string();
let mut radau_newton = RadauNewton::new(
eq_system,
k_variables,
None,
parameters,
arg,
1e-10,
50,
1,
1,
);
radau_newton.eq_generate();
let y_n1 = DVector::from_vec(vec![1.0]);
radau_newton.set_parameters(y_n1, 0.1, 0.0);
radau_newton.set_initial_guess(DVector::from_vec(vec![0.0]));
let result1 = radau_newton.solve();
assert!(result1.is_some());
assert_relative_eq!(result1.unwrap()[0], 1.0, epsilon = 1e-8);
let y_n2 = DVector::from_vec(vec![5.0]);
radau_newton.set_parameters(y_n2, 0.1, 0.0);
radau_newton.set_initial_guess(DVector::from_vec(vec![0.0]));
let result2 = radau_newton.solve();
assert!(result2.is_some());
assert_relative_eq!(result2.unwrap()[0], 5.0, epsilon = 1e-8);
}
#[test]
fn test_radau_newton_convergence_failure() {
let eq1 = Expr::parse_expression("K00*K00*K00 - y0");
let eq_system = vec![eq1];
let k_variables = vec!["K00".to_string()];
let parameters = vec!["y0".to_string(), "h".to_string()];
let arg = "t".to_string();
let mut radau_newton = RadauNewton::new(
eq_system,
k_variables,
None,
parameters,
arg,
1e-10,
5, 1,
1,
);
radau_newton.eq_generate();
let y_n = DVector::from_vec(vec![8.0]);
radau_newton.set_parameters(y_n, 0.1, 0.0);
radau_newton.set_initial_guess(DVector::from_vec(vec![100.0]));
let result = radau_newton.solve();
assert!(result.is_none());
}
}