Files
astrokit/benches/reference_scenarios.rs
T

131 lines
4.6 KiB
Rust

//! The scenarios the Orekit fixtures were generated from, timed.
//!
//! The same force compositions over the same arcs, so the figures sit beside the ones
//! `scripts/generate-orekit-fixtures.sh --time` reports for Orekit.
//!
//! The tolerances are not literally equal — Orekit is asked for a nanometer of position
//! accuracy and this runs at `Accuracy::High` — so before reading a ratio, check that the
//! two took a comparable number of steps. Measured once, they do: between 1752 and 2473
//! here against 1932 and 2092 there, within a quarter of each other across every
//! scenario. A ratio of times is therefore close to a ratio of per-step cost, which is
//! the thing worth comparing. If a future scenario's step counts diverge, its timing
//! ratio means nothing until they are brought back together.
//!
//! One scenario is not like for like however the steps come out. Orekit's radiation
//! pressure registers eclipse event detectors, so its integrator locates the shadow
//! boundaries by root finding; this crate integrates straight through them. That is work
//! Orekit does and this does not, and it is arguably the better treatment, so the
//! radiation row is not purely a speed difference.
use std::path::Path;
use std::sync::Arc;
use astrokit::kernels;
use astrokit::prelude::*;
use criterion::{criterion_group, criterion_main, Criterion};
use nalgebra::Vector3;
use std::hint::black_box;
const ARC: f64 = 86400.0;
fn almanac() -> Option<Arc<anise::almanac::Almanac>> {
let directory = kernels::directory()
.unwrap_or_else(|| Path::new(env!("CARGO_MANIFEST_DIR")).join("data/kernels"));
kernels::load(&directory).ok().map(Arc::new)
}
fn bench(c: &mut Criterion) {
let Some(almanac) = almanac() else {
eprintln!("skipped: kernels not fetched, run scripts/fetch-kernels.sh");
return;
};
let epoch = Epoch::from_gregorian_utc_hms(2026, 1, 1, 0, 0, 0);
let high = Orbit
.with_position(Vector3::new(6.978e6, 0.0, 0.0))
.with_velocity(Vector3::new(0.0, 7.56e3, 0.0));
// Drag is measured lower down, where there is an atmosphere worth integrating.
let low = Orbit
.with_position(Vector3::new(6.778e6, 0.0, 0.0))
.with_velocity(Vector3::new(0.0, 7.668e3, 0.0));
let vehicle = Spacecraft::cannonball(500.0, 5.0, 2.2, 1.3);
let scenarios: Vec<(&str, State<f64, 6, Orbit>, Vec<Dynamics>)> = vec![
("two-body", high, vec![TwoBody { body: Body::EARTH }]),
(
"gravity-8",
high,
vec![Gravity {
field: GravityModel::Egm96,
degree: 8,
order: 8,
}],
),
(
"gravity-20",
high,
vec![Gravity {
field: GravityModel::Egm96,
degree: 20,
order: 20,
}],
),
(
"third-body",
high,
vec![
TwoBody { body: Body::EARTH },
ThirdBody { body: Body::MOON },
ThirdBody { body: Body::SUN },
],
),
(
"radiation",
high,
vec![TwoBody { body: Body::EARTH }, Sunshine],
),
(
"drag-harris-priester",
low,
vec![
TwoBody { body: Body::EARTH },
Drag {
density: DensityModel::harris_priester(),
},
],
),
#[cfg(feature = "msis")]
(
"drag-msis",
low,
vec![
TwoBody { body: Body::EARTH },
Drag {
density: DensityModel::Nrlmsise00,
},
],
),
];
let mut group = c.benchmark_group("reference scenarios");
// A day-long arc takes milliseconds, so the default hundred samples would make this
// the longest thing in the repository for no extra resolution.
group.sample_size(20);
for (name, initial, forces) in &scenarios {
group.bench_function(*name, |b| {
b.iter(|| {
let mut propagation = Propagation::new(epoch, *initial, forces)
.frame(Frame::EARTH_J2000)
.spacecraft(vehicle.clone())
.almanac(almanac.clone())
.duration(Duration::from_seconds(ARC))
.accuracy(Accuracy::High);
black_box(propagation.run().states.len())
})
});
}
group.finish();
}
criterion_group!(benches, bench);
criterion_main!(benches);