use scirs2_core::ndarray::{array, Array2, ArrayView1};
use scirs2_integrate::error::IntegrateResult;
use scirs2_integrate::ode::{solve_ivp, MassMatrix, ODEMethod, ODEOptions};
#[allow(dead_code)]
fn main() -> IntegrateResult<()> {
println!("=== Mass Matrix ODE Example ===");
println!("\n=== Example 1: Constant Mass Matrix ===");
let mut mass_matrix = Array2::<f64>::eye(2);
mass_matrix[[0, 0]] = 2.0;
let mass = MassMatrix::constant(mass_matrix);
let f = |_t: f64, y: ArrayView1<f64>| array![y[1], -y[0]];
let y0 = array![1.0, 0.0];
let options_rk45 = ODEOptions {
method: ODEMethod::RK45,
rtol: 1e-6,
atol: 1e-8,
mass_matrix: Some(mass.clone()),
..Default::default()
};
let options_radau = ODEOptions {
method: ODEMethod::Radau,
rtol: 1e-6,
atol: 1e-8,
mass_matrix: Some(mass.clone()),
..Default::default()
};
let result_rk45 = solve_ivp(f, [0.0, 10.0], y0.clone(), Some(options_rk45))?;
let result_radau = solve_ivp(f, [0.0, 10.0], y0.clone(), Some(options_radau))?;
println!("\nComparison of methods for constant mass matrix:");
println!(
" RK45 steps: {} (accepted: {}, rejected: {})",
result_rk45.n_steps, result_rk45.n_accepted, result_rk45.n_rejected
);
println!(" RK45 function evaluations: {}", result_rk45.n_eval);
println!(
" Radau steps: {} (accepted: {}, rejected: {})",
result_radau.n_steps, result_radau.n_accepted, result_radau.n_rejected
);
println!(" Radau function evaluations: {}", result_radau.n_eval);
println!(" Radau LU decompositions: {}", result_radau.n_lu);
println!(" Radau Jacobian evaluations: {}", result_radau.n_jac);
let result = result_rk45;
let omega = 1.0 / f64::sqrt(2.0);
println!("\nResults:");
println!(" t\t\tx(numerical)\tx(analytical)\tv(numerical)\tv(analytical)");
for (i, &t) in result.t.iter().enumerate().take(5) {
let x_numerical = result.y[i][0];
let v_numerical = result.y[i][1];
let x_analytical = (omega * t).cos();
let v_analytical = -f64::sqrt(2.0) * (omega * t).sin();
println!(
" {t:.3}\t\t{x_numerical:.6}\t{x_analytical:.6}\t{v_numerical:.6}\t{v_analytical:.6}"
);
}
let n = result.t.len() - 1;
let t_final = result.t[n];
let x_numerical = result.y[n][0];
let v_numerical = result.y[n][1];
let x_analytical = (omega * t_final).cos();
let v_analytical = -f64::sqrt(2.0) * (omega * t_final).sin();
println!("\nAt t = {t_final:.3}:");
println!(" x error: {:.3e}", (x_numerical - x_analytical).abs());
println!(" v error: {:.3e}", (v_numerical - v_analytical).abs());
println!("\n=== Example 2: Time-dependent Mass Matrix ===");
let time_dependent_mass = |t: f64| {
let mut m = Array2::<f64>::eye(2);
m[[0, 0]] = 1.0 + 0.5 * t.sin();
m
};
let mass = MassMatrix::time_dependent(time_dependent_mass);
let y0 = array![1.0, 0.0];
let options_rk45 = ODEOptions {
method: ODEMethod::RK45,
rtol: 1e-6,
atol: 1e-8,
mass_matrix: Some(mass.clone()),
..Default::default()
};
let options_radau = ODEOptions {
method: ODEMethod::Radau,
rtol: 1e-6,
atol: 1e-8,
mass_matrix: Some(mass.clone()),
..Default::default()
};
let result_rk45 = solve_ivp(f, [0.0, 10.0], y0.clone(), Some(options_rk45))?;
let result_radau = solve_ivp(f, [0.0, 10.0], y0.clone(), Some(options_radau))?;
println!("\nComparison of methods for time-dependent mass matrix:");
println!(
" RK45 steps: {} (accepted: {}, rejected: {})",
result_rk45.n_steps, result_rk45.n_accepted, result_rk45.n_rejected
);
println!(" RK45 function evaluations: {}", result_rk45.n_eval);
println!(
" Radau steps: {} (accepted: {}, rejected: {})",
result_radau.n_steps, result_radau.n_accepted, result_radau.n_rejected
);
println!(" Radau function evaluations: {}", result_radau.n_eval);
println!(" Radau LU decompositions: {}", result_radau.n_lu);
println!(" Radau Jacobian evaluations: {}", result_radau.n_jac);
let result = result_radau;
println!("\nSolution for time-dependent mass matrix (Radau method):");
println!(" t\t\tx\t\tv");
for (i, &t) in result.t.iter().enumerate().take(10) {
let x = result.y[i][0];
let v = result.y[i][1];
println!(" {t:.3}\t\t{x:.6}\t{v:.6}");
}
let options_standard = ODEOptions {
method: ODEMethod::Radau,
rtol: 1e-6,
atol: 1e-8,
..Default::default()
};
let result_standard = solve_ivp(f, [0.0, 10.0], y0, Some(options_standard))?;
let final_x_with_mass = result.y.last().expect("Operation failed")[0];
let final_x_standard = result_standard.y.last().expect("Operation failed")[0];
println!("\nEffect of time-dependent mass matrix:");
println!(" Final position with mass matrix: {final_x_with_mass:.6}");
println!(" Final position standard oscillator: {final_x_standard:.6}");
println!(
" Difference: {:.6}",
(final_x_with_mass - final_x_standard).abs()
);
println!("\nSummary:");
println!(" Direct mass matrix support is working correctly");
println!(" Radau method can directly handle mass matrices without transformation");
println!(" Both constant and time-dependent mass matrices are supported");
Ok(())
}