use scirs2_core::ndarray::{array, ArrayView1};
use scirs2_integrate::ode::{solve_ivp, ODEMethod, ODEOptions};
use std::fs::File;
use std::io::Write;
#[allow(dead_code)]
fn main() {
println!("LSODA Method Switching Visualization Example");
println!("-------------------------------------------");
println!("This example focuses on collecting detailed information about when and why");
println!("LSODA switches between methods, to help understand its behavior");
println!("The output is formatted to be easily parsed for visualization");
println!("\nRunning stiffness wave example");
println!("This system's stiffness oscillates between stiff and non-stiff regions");
let stiffness_wave = |t: f64, y: ArrayView1<f64>| {
let stiffness = 500.0 + 500.0 * (t * 0.5).sin();
array![-stiffness * y[0], -y[1]]
};
let result = solve_ivp(
stiffness_wave,
[0.0, 40.0], array![1.0, 1.0],
Some(ODEOptions {
method: ODEMethod::LSODA,
rtol: 1e-4,
atol: 1e-6,
max_steps: 10000,
h0: Some(0.1),
min_step: Some(1e-6),
..Default::default()
}),
);
match result {
Ok(res) => {
println!("Integration successful!");
println!(
"Steps: {}, Function evaluations: {}",
res.n_steps, res.n_eval
);
println!("Accepted: {}, Rejected: {}", res.n_accepted, res.n_rejected);
if let Some(msg) = &res.message {
println!("{msg}");
}
let mut file = File::create("lsoda_visualization_data.csv").expect("Operation failed");
writeln!(&mut file, "t,y1,y2,stiffness,h,method").expect("Operation failed");
let method_names = ["unknown", "Adams", "BDF"];
for i in 0..res.t.len() {
let t = res.t[i];
let stiffness = 500.0 + 500.0 * (t * 0.5).sin();
let method_idx = 0;
writeln!(
&mut file,
"{:.6},{:.10e},{:.10e},{:.6},{:.10e},{}",
t,
res.y[i][0],
res.y[i][1],
stiffness,
if i > 0 { res.t[i] - res.t[i - 1] } else { 0.1 },
method_names[method_idx]
)
.expect("Operation failed");
}
println!("\nData saved to lsoda_visualization_data.csv");
println!("This file contains t, y values, stiffness, and step size info");
println!("You can use this data to visualize the solution and method switching");
let mut readme =
File::create("lsoda_visualization_plotting_guide.txt").expect("Operation failed");
writeln!(
&mut readme,
"LSODA Method Switching Visualization Guide\n\
=========================================\n\
\n\
The data files contain detailed information about the LSODA solution\n\
including times, function values, stiffness measures, and more.\n\
\n\
To visualize the method switching behavior, you can use tools like\n\
Python with matplotlib, gnuplot, or any other plotting software.\n\
\n\
Python Example:\n\
```python\n\
import numpy as np\n\
import matplotlib.pyplot as plt\n\
\n\
# Load the data\n\
data = np.loadtxt('lsoda_visualization_data.csv', delimiter=',', skiprows=1)\n\
t = data[:, 0]\n\
y1 = data[:, 1]\n\
y2 = data[:, 2]\n\
stiffness = data[:, 3]\n\
step_size = data[:, 4]\n\
\n\
# Create a figure with multiple subplots\n\
fig, axes = plt.subplots(3, 1, figsize=(10, 12), sharex=True)\n\
\n\
# Plot solution\n\
axes[0].semilogy(t, np.abs(y1), label='y1 (variable stiffness)')\n\
axes[0].semilogy(t, np.abs(y2), label='y2 (reference)')\n\
axes[0].set_ylabel('Solution (log scale)')\n\
axes[0].legend()\n\
axes[0].grid(True)\n\
\n\
# Plot stiffness parameter\n\
axes[1].plot(t, stiffness)\n\
axes[1].set_ylabel('Stiffness Parameter')\n\
axes[1].grid(True)\n\
\n\
# Plot step size\n\
axes[2].semilogy(t, step_size)\n\
axes[2].set_ylabel('Step Size (log scale)')\n\
axes[2].set_xlabel('Time')\n\
axes[2].grid(True)\n\
\n\
plt.tight_layout()\n\
plt.savefig('lsoda_method_switching_visualization.png')\n\
plt.show()\n\
```\n\
\n\
What to Look For:\n\
----------------\n\
1. Stiffness parameter: Higher values indicate stiffer regions\n\
2. Step size changes: LSODA typically takes larger steps in smooth regions\n\
and smaller steps in rapidly changing regions\n\
3. Correlation between stiffness and step size: In stiff regions with BDF method,\n\
the solver can sometimes take larger steps than Adams would allow\n\
4. Method switching: If you enhanced the solver to output method information,\n\
you'll see transitions between Adams and BDF methods\n\
\n\
For optimal visualization of LSODA's behavior, consider enhancing the solver\n\
to output additional diagnostic information during integration."
)
.expect("Operation failed");
println!("A plotting guide has been saved to lsoda_visualization_plotting_guide.txt");
}
Err(e) => {
println!("Integration failed: {e}");
}
}
println!("\nRunning Robertson chemical reaction example");
println!("This is a standard test case for stiff ODE solvers");
let robertson = |_t: f64, y: ArrayView1<f64>| {
let k1 = 0.04;
let k2 = 3.0e7;
let k3 = 1.0e4;
array![
-k1 * y[0] + k3 * y[1] * y[2],
k1 * y[0] - k2 * y[1].powi(2) - k3 * y[1] * y[2],
k2 * y[1].powi(2)
]
};
let result = solve_ivp(
robertson,
[0.0, 1000.0], array![1.0, 0.0, 0.0],
Some(ODEOptions {
method: ODEMethod::LSODA,
rtol: 1e-4,
atol: 1e-7, max_steps: 10000,
..Default::default()
}),
);
match result {
Ok(res) => {
println!("Integration successful!");
println!(
"Steps: {}, Function evaluations: {}",
res.n_steps, res.n_eval
);
println!("Accepted: {}, Rejected: {}", res.n_accepted, res.n_rejected);
if let Some(msg) = &res.message {
println!("{msg}");
}
let mut file = File::create("robertson_solution.csv").expect("Operation failed");
writeln!(&mut file, "t,y1,y2,y3,step_size").expect("Operation failed");
for i in 0..res.t.len() {
let step_size = if i > 0 { res.t[i] - res.t[i - 1] } else { 0.0 };
writeln!(
&mut file,
"{:.8e},{:.8e},{:.8e},{:.8e},{:.8e}",
res.t[i], res.y[i][0], res.y[i][1], res.y[i][2], step_size
)
.expect("Operation failed");
}
println!("\nSolution saved to robertson_solution.csv");
println!("Note the characteristic behavior:");
println!("- y₁ decreases from 1.0 to ~0.0 (most of it converts to y₃)");
println!("- y₂ rapidly increases then slowly decreases (intermediate species)");
println!("- y₃ increases from 0.0 toward 1.0 (end product)");
println!("- The problem is very stiff in the initial transient phase");
println!("- LSODA should switch to BDF method during the stiff phase");
}
Err(e) => {
println!("Integration failed: {e}");
}
}
println!("\nAdvanced Method Switching Analysis");
println!("---------------------------------");
println!("To truly visualize LSODA's method switching behavior, the solver would");
println!("need to be enhanced to record and output the following for each step:");
println!("1. The active method (Adams or BDF) used at each successful step");
println!("2. Stiffness detection metrics:");
println!(" - Relative step size (h / abs(t))");
println!(" - Acceptance ratio and recent rejection rate");
println!(" - Step size relative to minimum (h / min_step)");
println!("3. Method switching events with reason for switching");
println!("4. Order adaptivity information (changing formula order within a method)");
println!();
println!("This information would enable creation of detailed visualizations showing:");
println!("- When and why the solver switches methods");
println!("- How the solution, stiffness, and step size correlate");
println!("- The efficiency gains of adaptive method switching");
}