use crate::*;
use criterion::Criterion;
use core::f64::NAN;
use mathru::{
algebra::linear::{matrix::General, vector::Vector},
analysis::differential_equation::ordinary::solver::explicit::runge_kutta::adaptive::{
DormandPrince54, ProportionalControl,
},
analysis::differential_equation::ordinary::{ExplicitInitialValueProblemBuilder, ExplicitODE},
vector,
};
criterion_group!(
ode,
b_mathru_harmonic,
b_mathru_young_laplace
);
struct HarmonicOde {}
impl ExplicitODE<f64> for HarmonicOde {
fn ode(&self, _t: &f64, y: &Vector<f64>) -> Vector<f64> {
let a: General<f64> = General::new(2, 2, vec![0.0, 1.0, -1.0, 0.0]);
&a * y
}
}
struct CoupledHarmonicState {
x1: f64,
x2: f64,
v1: f64,
v2: f64,
}
impl From<[f64; 4]> for CoupledHarmonicState {
fn from(ar: [f64; 4]) -> Self {
CoupledHarmonicState {
x1: ar[0],
x2: ar[1],
v1: ar[2],
v2: ar[3],
}
}
}
impl From<CoupledHarmonicState> for [f64; 4] {
fn from(s: CoupledHarmonicState) -> Self {
[s.x1, s.x2, s.v1, s.v2]
}
}
struct YoungLaplace {
phi_0: f64,
x_contact: f64,
z_contact: f64,
startsign: f64,
}
impl ExplicitODE<f64> for YoungLaplace {
fn ode(&self, pmphi: &f64, q: &Vector<f64>) -> Vector<f64> {
let phi = -pmphi * self.phi_0.signum();
let q = q.clone().convert_to_vec();
let x = q[0];
let z = q[1];
#[allow(clippy::float_cmp)]
{
if (x * z - phi.sin()).signum() != self.startsign {
return vector![NAN; NAN];
}
}
let dxdphi = x * phi.cos() / (x * z - phi.sin()); let dzdphi = x * phi.sin() / (x * z - phi.sin()); vector![-self.phi_0.signum() * dxdphi; -self.phi_0.signum() * dzdphi]
}
}
impl YoungLaplace {
fn time_span(&self) -> (f64, f64) {
(-self.phi_0.abs(), 0.)
}
fn init_cond(&self) -> Vector<f64> {
#[allow(clippy::float_cmp)]
{
assert!(self.startsign == 1. || self.startsign == -1.); }
vector![self.x_contact; self.z_contact]
}
}
#[test]
fn compare_with_mathru() {
let phi_0 = -0.7653981633974483;
let x_contact = 0.34641208585537364;
let z_contact = 0.4145142430976436;
let h_0 = 0.09026305092159702;
let fac = 0.9;
let fac_min = 0.2; let fac_max = 10.0; let n_max = 1e9 as u32;
let abs_tol = 1e-6;
let rel_tol = 1e-3;
let solver = ProportionalControl::new(n_max, h_0, fac, fac_min, fac_max, abs_tol, rel_tol);
let ode = YoungLaplaceOde {
phi_0,
startsign: (x_contact * z_contact - phi_0.sin()).signum(),
};
let myres = solve_ivp(
&ode,
(-phi_0.abs(), 0.),
Coord([x_contact, z_contact]),
|_, _| true,
1e-6,
1e-3,
);
let ode = YoungLaplace {
phi_0,
x_contact,
z_contact,
startsign: (x_contact * z_contact - phi_0.sin()).signum(),
};
let problem = ExplicitInitialValueProblemBuilder::new(&ode, ode.time_span().0, ode.init_cond())
.t_end(ode.time_span().1)
.build();
let (_pm_phi, xz) = solver.solve(&problem, &DormandPrince54::default()).unwrap();
let myres_y = match myres {
IvpResult::FinalTimeReached(y) => y,
_ => panic!(),
};
let res = xz[xz.len() - 1].clone().convert_to_vec();
assert_approx_eq!(myres_y.0[0], res[0], 1e-3);
assert_approx_eq!(myres_y.0[1], res[1], 1e-3);
}
fn b_mathru_young_laplace(bench: &mut Criterion) {
let phi_0 = -0.7653981633974483;
let x_contact = 0.34641208585537364;
let z_contact = 0.4145142430976436;
let h_0 = 0.09026305092159702;
let fac = 0.9;
let fac_min = 0.2; let fac_max = 10.0; let n_max = 1e9 as u32;
let abs_tol = 1e-6;
let rel_tol = 1e-3;
let solver = ProportionalControl::new(n_max, h_0, fac, fac_min, fac_max, abs_tol, rel_tol);
let ode = YoungLaplace {
phi_0,
x_contact,
z_contact,
startsign: (x_contact * z_contact - phi_0.sin()).signum(),
};
let problem = ExplicitInitialValueProblemBuilder::new(&ode, ode.time_span().0, ode.init_cond())
.t_end(ode.time_span().1)
.build();
let method = DormandPrince54::default();
bench.bench_function("mathru young laplace", move |b| {
b.iter(|| solver.solve(&problem, &method).unwrap());
});
}
fn b_mathru_harmonic(bench: &mut Criterion) {
let h_0 = 0.09026305092159702;
let fac = 0.9;
let fac_min = 0.2;
let fac_max = 10.0;
let n_max = 1e9 as u32;
let abs_tol = 1e-6;
let rel_tol = 1e-3;
let solver = ProportionalControl::new(n_max, h_0, fac, fac_min, fac_max, abs_tol, rel_tol);
let ode = HarmonicOde {};
let problem = ExplicitInitialValueProblemBuilder::new(&ode, 0.0, vector![1.; 0.])
.t_end(10.0)
.build();
let method = DormandPrince54::default();
bench.bench_function("mathru harmonic", move |b| {
b.iter(|| solver.solve(&problem, &method).unwrap());
});
}