use scirs2_core::ndarray::{array, ArrayView1};
use scirs2_integrate::ode::{solve_ivp, ODEMethod, ODEOptions};
#[allow(dead_code)]
fn main() {
println!("LSODA Solver Example - Automatic Stiffness Detection");
println!("--------------------------------------------------");
println!("NOTE: The LSODA implementation is still experimental.");
println!(" This example will run a series of tests with different parameters");
println!(" to demonstrate when LSODA works well and when it struggles.");
println!("\nExponential Decay Example (Simple, Non-stiff)");
println!(" y' = -y, y(0) = 1, exact solution: y(t) = exp(-t)");
let decay = |_t: f64, y: ArrayView1<f64>| array![-y[0]];
let result = solve_ivp(
decay,
[0.0, 5.0],
array![1.0],
Some(ODEOptions {
method: ODEMethod::LSODA,
rtol: 1e-4,
atol: 1e-6,
max_steps: 1000,
h0: Some(0.1), min_step: Some(1e-3), ..Default::default()
}),
)
.unwrap_or_else(|e| {
println!("LSODA method failed unexpectedly on simple problem: {e}.");
println!("Using DOP853 as fallback...");
solve_ivp(decay, [0.0, 5.0], array![1.0], None).expect("Operation failed")
});
println!("Results for exponential decay:");
println!(" Solver method: {:?}", result.method);
println!(
" Steps: {}, Function evaluations: {}",
result.n_steps, result.n_eval
);
println!(
" Final value: {:.6}, Exact: {:.6}",
result.y.last().expect("Operation failed")[0],
(-5.0f64).exp()
);
println!(
" Error: {:.2e}",
(result.y.last().expect("Operation failed")[0] - (-5.0f64).exp()).abs()
);
if let Some(msg) = &result.message {
println!(" {msg}");
}
println!("\nVan der Pol Oscillator with Varying Mu Parameter");
println!("y'' - μ(1-y²)y' + y = 0");
println!("As system: y₀' = y₁, y₁' = μ(1-y₀²)y₁ - y₀");
println!("This problem changes from non-stiff to stiff as μ increases");
let mu_values = [1.0, 10.0, 100.0];
for &mu in &mu_values {
println!("\nSolving with μ = {mu}");
let van_der_pol = move |_t: f64, y: ArrayView1<f64>| {
array![y[1], mu * (1.0 - y[0].powi(2)) * y[1] - y[0]]
};
let result = solve_ivp(
van_der_pol,
[0.0, 10.0], array![2.0, 0.0],
Some(ODEOptions {
method: ODEMethod::LSODA,
rtol: 1e-3, atol: 1e-5, max_steps: 3000, h0: Some(0.1), min_step: Some(0.0001), ..Default::default()
}),
)
.unwrap_or_else(|e| {
println!("LSODA method failed with error: {e}.");
println!("Note: This is expected as LSODA implementation is still experimental.");
println!("Using a more stable solver (DOP853) instead...");
solve_ivp(
van_der_pol,
[0.0, 10.0],
array![2.0, 0.0],
Some(ODEOptions {
method: ODEMethod::DOP853,
rtol: 1e-4,
atol: 1e-6,
max_steps: 5000,
..Default::default()
}),
)
.expect("Operation failed")
});
println!(" Solver method: {:?}", result.method);
println!(" Steps: {}", result.n_steps);
println!(" Function evaluations: {}", result.n_eval);
println!(" Accepted steps: {}", result.n_accepted);
println!(" Rejected steps: {}", result.n_rejected);
println!(
" Final state: [{:.4}, {:.4}]",
result.y.last().expect("Operation failed")[0],
result.y.last().expect("Operation failed")[1]
);
println!(" Success: {}", result.success);
if let Some(msg) = &result.message {
println!(" {msg}");
}
}
println!("\nLSODA Method Analysis:");
println!("- LSODA automatically switches between Adams method (for non-stiff regions)");
println!(" and BDF method (for stiff regions) as needed during integration");
println!("- For simple problems like exponential decay, LSODA works well");
println!("- For complex problems, current implementation may struggle");
println!("- Larger initial step sizes (0.1) tend to work better than small ones");
println!("- Method switching should be visible in the statistics for stiff problems");
println!("\nRecommendations for using LSODA:");
println!("1. Start with larger initial step sizes (h0 ~ 0.1)");
println!("2. Use larger minimum step sizes (min_step ~ 1e-3)");
println!("3. For stiff problems, consider using BDF directly instead");
}