use crate::nonlinearity::AllenCahn;
struct ScalarGen {
a: f64,
}
impl GeneratorAction<f64> for ScalarGen {
fn dim(&self) -> usize { 1 }
fn apply_generator(&self, src: &[f64], dst: &mut [f64]) { dst[0] = self.a * src[0]; }
fn norm_bound(&self) -> f64 { self.a.abs() }
}
struct ZeroNl;
impl Nonlinearity<f64> for ZeroNl {
fn eval(&self, _u: &[f64], n_out: &mut [f64]) -> Result<(), SemiflowError> {
for x in n_out.iter_mut() { *x = 0.0; }
Ok(())
}
}
#[test]
fn etdrk4_step_linear_exact() {
let a = -1.0_f64;
let h = 0.1_f64;
let driver = Etdrk4::new(ScalarGen { a }, ZeroNl, h).unwrap();
let u = [1.0_f64];
let mut u_next = [0.0_f64];
let mut scratch = ScratchPool::new();
driver.step(&u, &mut u_next, &mut scratch).unwrap();
let exact = (a * h).exp(); assert!(
(u_next[0] - exact).abs() < 1e-12,
"linear exact: got {}, expected {exact}", u_next[0]
);
}
#[test]
fn etdrk4_integrate_matches_step() {
let op = ScalarGen { a: -0.5 };
let nl = AllenCahn::<f64>::new();
let h = 0.05;
let driver = Etdrk4::new(op, nl, h).unwrap();
let u = [0.3_f64];
let mut u_step = [0.0_f64];
let mut u_int = [0.0_f64];
let mut scratch = ScratchPool::new();
driver.step(&u, &mut u_step, &mut scratch).unwrap();
driver.integrate(&u, 1, &mut u_int, &mut scratch).unwrap();
assert!(
(u_step[0] - u_int[0]).abs() < 1e-15,
"step vs integrate(1): {} != {}", u_step[0], u_int[0]
);
}
#[test]
fn etdrk4_integrate_two_steps_vs_manual() {
let op = ScalarGen { a: -0.5 };
let nl = AllenCahn::<f64>::new();
let h = 0.02;
let driver = Etdrk4::new(op, nl, h).unwrap();
let u0 = [0.5_f64];
let mut scratch = ScratchPool::new();
let mut u1 = [0.0_f64];
let mut u2 = [0.0_f64];
driver.step(&u0, &mut u1, &mut scratch).unwrap();
driver.step(&u1, &mut u2, &mut scratch).unwrap();
let mut u_int = [0.0_f64];
driver.integrate(&u0, 2, &mut u_int, &mut scratch).unwrap();
assert!(
(u2[0] - u_int[0]).abs() < 1e-15,
"two steps vs integrate(2): {} != {}", u2[0], u_int[0]
);
}
#[test]
fn etdrk4_rejects_zero_dim() {
struct ZeroDimGen;
impl GeneratorAction<f64> for ZeroDimGen {
fn dim(&self) -> usize { 0 }
fn apply_generator(&self, _s: &[f64], _d: &mut [f64]) {}
fn norm_bound(&self) -> f64 { 0.0 }
}
let err = Etdrk4::new(ZeroDimGen, ZeroNl, 0.1_f64);
assert!(err.is_err(), "expected DomainViolation for dim=0");
}