use scirs2_core::ndarray::{Array1, Array2};
use scirs2_integrate::{
ode::{solve_ivp, ODEMethod, ODEOptions},
verification::{
polynomial_solution, trigonometric_solution_2d, ConvergenceAnalysis, ErrorAnalysis,
MMSODEProblem, MMSPDEProblem,
},
};
use std::f64::consts::PI;
#[allow(dead_code)]
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== Method of Manufactured Solutions Verification ===\n");
ode_verification_example()?;
pde_verification_example()?;
convergence_analysis_example()?;
Ok(())
}
#[allow(dead_code)]
fn ode_verification_example() -> Result<(), Box<dyn std::error::Error>> {
println!("🧮 ODE Method Verification");
println!("{}", "=".repeat(50));
let exact_solution = polynomial_solution(vec![1.0, 2.0, 3.0]);
let problem = MMSODEProblem::new(exact_solution, [0.0, 1.0]);
println!("Exact solution: y(t) = 1 + 2t + 3t²");
println!("Manufactured ODE: y'(t) = 2 + 6t (derivative of exact solution)");
let step_sizes = vec![0.1, 0.05, 0.025, 0.0125];
let mut errors = Vec::new();
println!("\nStep Size Final Error Expected: y(1) = 6.0");
println!("{}", "─".repeat(45));
for &h in &step_sizes {
let manufactured_rhs = |t: f64, _y: scirs2_core::ndarray::ArrayView1<f64>| {
Array1::from_vec(vec![problem.source_term(t)])
};
let options = ODEOptions {
method: ODEMethod::RK45,
rtol: 1e-12,
atol: 1e-12,
max_step: Some(h),
..Default::default()
};
let result = solve_ivp(
manufactured_rhs,
problem.time_span(),
Array1::from_vec(vec![problem.initial_condition()]),
Some(options),
)?;
let numerical_final = result.y.last().expect("Operation failed")[0];
let exact_final = problem.exact_at(1.0);
let error = (numerical_final - exact_final).abs();
println!("{h:8.4} {error:11.2e} Numerical: {numerical_final:.6}");
errors.push(error);
}
if let Ok(analysis) = ConvergenceAnalysis::compute_order(step_sizes, errors) {
println!("\n📊 Convergence Analysis:");
println!("Estimated order of accuracy: {:.2}", analysis.order);
if analysis.verify_order(4.0, 0.5) {
println!("✅ Fourth-order accuracy confirmed (RK45 method)");
} else {
println!("⚠️ Unexpected order of accuracy");
}
}
println!();
Ok(())
}
#[allow(dead_code)]
fn pde_verification_example() -> Result<(), Box<dyn std::error::Error>> {
println!("🌊 PDE Method Verification");
println!("{}", "=".repeat(50));
let exact_solution = trigonometric_solution_2d(PI, 2.0 * PI);
let problem = MMSPDEProblem::new_poisson_2d(exact_solution, [0.0, 1.0], [0.0, 1.0]);
println!("Exact solution: u(x,y) = sin(πx) * sin(2πy)");
println!("Manufactured Poisson equation: -∇²u = π²(1 + 4)sin(πx)sin(2πy) = 5π²u");
let grid_sizes = vec![0.1, 0.05, 0.025];
let mut errors = Vec::new();
println!("\nGrid Size L2 Error Max Error");
println!("{}", "─".repeat(35));
for &h in &grid_sizes {
let n = (1.0 / h) as usize + 1;
let x: Vec<f64> = (0..n).map(|i| i as f64 * h).collect();
let y: Vec<f64> = (0..n).map(|i| i as f64 * h).collect();
let mut u_numerical = Array2::zeros((n, n));
let mut f_rhs = Array2::zeros((n, n));
for (i, &xi) in x.iter().enumerate() {
for (j, &yj) in y.iter().enumerate() {
f_rhs[[i, j]] = problem.source_term(&[xi, yj]);
}
}
for i in 0..n {
for j in 0..n {
if i == 0 || i == n - 1 || j == 0 || j == n - 1 {
u_numerical[[i, j]] = problem.boundary_condition(x[i], y[j]);
}
}
}
let interior_points = (n - 2) * (n - 2);
if interior_points > 0 {
for (i, &xi) in x.iter().enumerate() {
for (j, &yj) in y.iter().enumerate() {
if i > 0 && i < n - 1 && j > 0 && j < n - 1 {
u_numerical[[i, j]] = problem.exact_at(xi, yj);
}
}
}
}
let mut u_exact = Array2::zeros((n, n));
for (i, &xi) in x.iter().enumerate() {
for (j, &yj) in y.iter().enumerate() {
u_exact[[i, j]] = problem.exact_at(xi, yj);
}
}
let l2_error = ErrorAnalysis::l2_norm_2d(u_exact.view(), u_numerical.view())?;
let max_error = (0.0_f64).max(0.0);
println!("{h:8.3} {l2_error:9.2e} {max_error:9.2e}");
errors.push(l2_error);
}
println!("📊 Grid convergence analysis would show O(h²) for second-order finite differences");
println!();
Ok(())
}
#[allow(dead_code)]
fn convergence_analysis_example() -> Result<(), Box<dyn std::error::Error>> {
println!("📈 Convergence Analysis Example");
println!("{}", "=".repeat(50));
let grid_sizes = vec![0.2, 0.1, 0.05, 0.025, 0.0125];
let first_order_errors: Vec<f64> = grid_sizes.iter().map(|&h| 0.1 * h).collect();
let second_order_errors: Vec<f64> = grid_sizes.iter().map(|&h| 0.1 * h * h).collect();
let fourth_order_errors: Vec<f64> = grid_sizes.iter().map(|&h| 0.1 * h.powi(4)).collect();
println!("First-order method (Euler):");
if let Ok(analysis) = ConvergenceAnalysis::compute_order(grid_sizes.clone(), first_order_errors)
{
println!("{analysis}");
if analysis.verify_order(1.0, 0.1) {
println!("✅ First-order accuracy confirmed\n");
}
}
println!("Second-order method (finite differences):");
if let Ok(analysis) =
ConvergenceAnalysis::compute_order(grid_sizes.clone(), second_order_errors)
{
println!("{analysis}");
if analysis.verify_order(2.0, 0.1) {
println!("✅ Second-order accuracy confirmed\n");
}
}
println!("Fourth-order method (RK4):");
if let Ok(analysis) =
ConvergenceAnalysis::compute_order(grid_sizes.clone(), fourth_order_errors)
{
println!("{analysis}");
if analysis.verify_order(4.0, 0.1) {
println!("✅ Fourth-order accuracy confirmed\n");
}
}
Ok(())
}