pub use crate::vector::{Vector, Vec64};
pub use crate::mesh1d::Mesh1D;
pub use crate::banded::Banded;
pub use crate::matrix::Mat64;
pub use crate::traits::Number;
pub struct ODEBVP<T> {
pub max_iter: usize, pub tol: f64, pub delta: f64, pub solution: Mesh1D<T, f64>, }
impl<T: Clone + Number> ODEBVP<T> {
#[inline]
pub fn new( nodes: Vector<f64>, order: usize ) -> Self {
let max_iter: usize = 20;
let tol: f64 = 1.0e-8;
let delta: f64 = 1.0e-8;
let solution = Mesh1D::<T, f64>::new( nodes, order );
ODEBVP { max_iter, tol, delta, solution }
}
#[inline]
pub fn iterations(&mut self, iterations: usize ) {
self.max_iter = iterations;
}
#[inline]
pub fn tolerance(&mut self, tolerance: f64 ) {
self.tol = tolerance;
}
#[inline]
pub fn delta(&mut self, delta: f64 ) {
self.delta = delta;
}
}
impl ODEBVP<f64> {
pub fn solve(&mut self,
eqn: &dyn Fn(&Vec64, &f64) -> Vec64,
bc_left: &dyn Fn(&Vec64) -> Vector<Option<f64>>,
bc_right: &dyn Fn(&Vec64) -> Vector<Option<f64>>,
jacobian: Option<&dyn Fn(&Vec64, &f64) -> Mat64>
) -> Result<(), f64> {
let order = self.solution.nvars();
let n = self.solution.nodes().size();
let mut max_residual = 1.0;
let size = n * order;
let off_diag = 2 * order - 1;
let mut a_mat = Banded::<f64>::new( size, off_diag, off_diag, 0.0 );
let mut b_vec = Vec64::new( size, 0.0 );
for _iter in 0..self.max_iter {
self.assemble_matrix_problem( eqn, bc_left, bc_right, jacobian, &mut a_mat, &mut b_vec );
let sol = a_mat.solve( &b_vec );
for i in 0..n {
for j in 0..order {
self.solution[i][j] += sol[ i * order + j ];
}
}
max_residual = b_vec.norm_inf();
if max_residual <= self.tol {
return Ok( () )
}
}
Err( max_residual )
}
fn assemble_matrix_problem( &mut self,
eqn: &dyn Fn(&Vec64, &f64) -> Vec64,
bc_left: &dyn Fn(&Vec64) -> Vector<Option<f64>>,
bc_right: &dyn Fn(&Vec64) -> Vector<Option<f64>>,
jacobian: Option<&dyn Fn(&Vec64, &f64) -> Mat64>,
a: &mut Banded<f64>,
b: &mut Vec64
) {
let order = self.solution.nvars();
let n = self.solution.nodes().size();
a.fill( 0.0 ); b.assign( 0.0 ); let mut row = 0;
let bc_left_guess: Vec64 = self.solution.get_nodes_vars( 0 );
let bc_left_residual: Vector<Option<f64>> = bc_left( &bc_left_guess );
for i in 0..order {
if let Some( res ) = bc_left_residual[ i ] {
a[ ( row, i ) ] = 1.0;
b[ row ] = -res;
row += 1;
}
}
for i in 0..n-1 {
let x_i = self.solution.coord( i );
let x_i_1 = self.solution.coord( i + 1 );
let dx_i = x_i_1 - x_i;
let inv_dx_i = 1.0 / dx_i;
let x_i_half = 0.5 * ( x_i + x_i_1 );
let y_g_i = self.solution.get_nodes_vars( i );
let y_g_i_1 = self.solution.get_nodes_vars( i + 1 );
let y_g_i_half = 0.5 * ( &y_g_i + &y_g_i_1 );
let f_fun = eqn( &y_g_i_half, &x_i_half );
let mut jac = Mat64::new( order, order, 0.0 );
if let Some(jacobian_func) = jacobian {
jac = jacobian_func( &y_g_i_half, &x_i_half );
} else {
for alpha in 0..order {
for beta in 0..order {
let mut delta = Vec64::new( order, 0.0 );
delta[ beta ] = self.delta;
let y_g_i_half_star = &y_g_i_half + δ
let f_fun_star = eqn( &y_g_i_half_star, &x_i_half );
jac[( alpha, beta )] = ( f_fun_star[ alpha ] - f_fun[ alpha ] ) / self.delta;
}
}
}
let dy_g_i = inv_dx_i * ( &y_g_i_1 - &y_g_i );
let r = &f_fun - &dy_g_i;
let mut identity = Mat64::new( order, order, 0.0 );
identity.fill_diag( inv_dx_i );
let jac_half = &jac * 0.5;
let k = &identity - &jac_half;
let l = &-identity - &jac_half;
for n in 0..order {
for j in 0..order {
a[ ( row, ( i * order ) + j ) ] = l[( n, j )];
a[ ( row, ( ( i + 1 ) * order ) + j ) ] = k[( n, j )];
}
b[ row ] = r[ n ];
row += 1;
}
}
let bc_right_guess: Vec64 = self.solution.get_nodes_vars( n - 1 );
let bc_right_residual: Vector<Option<f64>> = bc_right( &bc_right_guess );
for i in 0..order {
if let Some( res ) = bc_right_residual[ i ] {
a[ ( row, ( ( n - 1 ) * order ) + i ) ] = 1.0;
b[ row ] = -res;
row += 1;
}
}
}
}