use zhl16::{
BodyModelError, BodyState, Gas, GasMixture, GasMixtureError, Pressure, PressureUnit,
QuantityError, Time, TimeUnit,
};
fn assert_close(actual: f64, expected: f64, epsilon: f64) {
let diff = (actual - expected).abs();
assert!(
diff <= epsilon,
"expected {actual} to be within {epsilon} of {expected}"
);
}
fn assert_bar(actual: Pressure, expected: f64) {
assert_close(actual.to(PressureUnit::Bar), expected, 1E-12);
}
fn gas_mixture(oxygen_fraction: f64, helium_fraction: f64) -> GasMixture {
GasMixture::new(oxygen_fraction, helium_fraction).expect("valid gas mixture")
}
fn pressure(value: f64, unit: PressureUnit) -> Pressure {
Pressure::new(value, unit).expect("valid pressure")
}
fn time(value: f64, unit: TimeUnit) -> Time {
Time::new(value, unit).expect("valid time")
}
#[test]
fn accepts_trimix_as_oxygen_and_helium_and_derives_inert_fractions() {
let trimix_10_20 = gas_mixture(0.10, 0.20);
assert_close(trimix_10_20.oxygen_fraction(), 0.10, 1E-12);
assert_close(trimix_10_20.helium_fraction(), 0.20, 1E-12);
assert_close(trimix_10_20.nitrogen_fraction(), 0.70, 1E-12);
assert_close(trimix_10_20.inert_fraction(Gas::Nitrogen), 0.70, 1E-12);
assert_close(trimix_10_20.inert_fraction(Gas::Helium), 0.20, 1E-12);
}
#[test]
fn accepts_gas_operating_pressure_calculations() {
let nitrox_32 = gas_mixture(0.32, 0.0);
let trimix_18_45 = gas_mixture(0.18, 0.45);
assert_bar(
nitrox_32
.max_operational_pressure(pressure(1.4, PressureUnit::Bar))
.expect("positive PPO2"),
4.375,
);
assert_bar(
trimix_18_45.equivalent_narcotic_pressure(pressure(6.0, PressureUnit::Bar)),
3.3,
);
}
#[test]
fn rejects_gas_mixtures_with_typed_errors() {
assert_eq!(
GasMixture::new(0.80, 0.30),
Err(GasMixtureError::FractionSumExceedsOne)
);
assert_eq!(
GasMixture::new(-0.01, 0.0),
Err(GasMixtureError::NegativeOxygenFraction)
);
assert_eq!(
GasMixture::new(0.0, 1.0),
Err(GasMixtureError::ZeroOxygenFraction)
);
assert_eq!(
gas_mixture(0.32, 0.0).max_operational_pressure(pressure(0.0, PressureUnit::Bar)),
Err(GasMixtureError::NonPositiveCriticalPartialPressureOfOxygen)
);
}
#[test]
fn accepts_a_one_minute_trimix_exposure_and_updates_observable_tissues() {
let mut body = BodyState::new(pressure(0.79, PressureUnit::Bar), Pressure::zero());
let trimix_10_20 = gas_mixture(0.10, 0.20);
body.evolve(
pressure(2.0, PressureUnit::Bar),
&trimix_10_20,
time(60.0, TimeUnit::Seconds),
)
.expect("positive time step");
let compartments = body.compartments();
assert_eq!(compartments.len(), 16);
assert_bar(compartments[0].get_nitrogen_pressure(), 0.868479510077358);
assert_bar(compartments[0].get_helium_pressure(), 0.142854684350934);
assert_bar(compartments[15].get_nitrogen_pressure(), 0.790617948898247);
assert_bar(compartments[15].get_helium_pressure(), 0.001118888499687);
assert!(
compartments[0].get_nitrogen_pressure() > compartments[15].get_nitrogen_pressure(),
"fastest compartment should take up more nitrogen than slowest compartment"
);
assert!(
compartments[0].get_helium_pressure() > compartments[15].get_helium_pressure(),
"fastest compartment should take up more helium than slowest compartment"
);
}
#[test]
fn accepts_gradient_factor_queries_after_an_exposure() {
let mut body = BodyState::new(pressure(0.79, PressureUnit::Bar), Pressure::zero());
body.evolve(
pressure(2.0, PressureUnit::Bar),
&gas_mixture(0.10, 0.20),
time(60.0, TimeUnit::Seconds),
)
.expect("positive time step");
let compartments = body.compartments();
assert_bar(
compartments[0]
.safe_ambient_pressure(0.85)
.expect("valid gradient factor"),
-0.021558210308153,
);
assert_bar(
compartments[15]
.safe_ambient_pressure(0.85)
.expect("valid gradient factor"),
0.575978611468123,
);
assert_bar(
compartments[15]
.safe_ambient_pressure(1.0)
.expect("valid gradient factor"),
0.539226909247077,
);
assert_bar(
compartments[15]
.safe_ambient_pressure(0.0)
.expect("valid gradient factor"),
0.791736837397934,
);
}
#[test]
fn rejects_invalid_model_inputs_with_typed_errors() {
let mut body = BodyState::new(pressure(0.79, PressureUnit::Bar), Pressure::zero());
assert_eq!(
body.evolve(
pressure(2.0, PressureUnit::Bar),
&gas_mixture(0.10, 0.20),
Time::zero()
),
Err(BodyModelError::NonPositiveTimeStep)
);
assert_eq!(
body.compartments()[0].safe_ambient_pressure(-0.01),
Err(BodyModelError::InvalidGradientFactor)
);
assert_eq!(
body.compartments()[0].safe_ambient_pressure(1.01),
Err(BodyModelError::InvalidGradientFactor)
);
}
#[test]
fn accepts_public_quantity_conversions_needed_by_callers() {
assert_close(
time(1.0, TimeUnit::Hours).to(TimeUnit::Seconds),
3600.0,
1E-12,
);
assert_close(
pressure(1.0, PressureUnit::Atmosphere).to(PressureUnit::Bar),
1.01325,
1E-12,
);
assert_close(
pressure(2.0, PressureUnit::Bar).to(PressureUnit::Pascal),
200_000.0,
1E-12,
);
}
#[test]
fn rejects_nan_quantities_with_typed_errors() {
assert_eq!(
Pressure::new(f64::NAN, PressureUnit::Bar),
Err(QuantityError::NanValue)
);
assert_eq!(
Time::new(f64::NAN, TimeUnit::Seconds),
Err(QuantityError::NanValue)
);
}