pub const ACRE_TO_FT2: f64 = 43_560.0;
pub const FT3_PER_BBL: f64 = 5.614_583_333_333_333;
pub const FT_TO_M: f64 = 0.304_8;
pub const M3_PER_BBL: f64 = 0.158_987_294_928;
pub const BAR_PER_PSI: f64 = 0.068_947_572_931_683_6;
pub const MD_TO_M2: f64 = 9.869_233e-16;
#[must_use]
pub fn acres_to_ft2(acres: f64) -> f64 {
acres * ACRE_TO_FT2
}
#[must_use]
pub fn acre_ft_to_ft3(acre_ft: f64) -> f64 {
acre_ft * ACRE_TO_FT2
}
#[must_use]
pub fn ft3_to_acre_ft(ft3: f64) -> f64 {
ft3 / ACRE_TO_FT2
}
#[must_use]
pub fn ft3_to_rb(ft3: f64) -> f64 {
ft3 / FT3_PER_BBL
}
#[must_use]
pub fn rb_to_ft3(bbl: f64) -> f64 {
bbl * FT3_PER_BBL
}
#[must_use]
pub fn degf_to_degr(degf: f64) -> f64 {
degf + 459.67
}
#[must_use]
pub fn ft_to_m(ft: f64) -> f64 {
ft * FT_TO_M
}
#[must_use]
pub fn m_to_ft(m: f64) -> f64 {
m / FT_TO_M
}
#[must_use]
pub fn m3_to_bbl(m3: f64) -> f64 {
m3 / M3_PER_BBL
}
#[must_use]
pub fn bbl_to_m3(bbl: f64) -> f64 {
bbl * M3_PER_BBL
}
#[must_use]
pub fn psi_to_bar(psi: f64) -> f64 {
psi * BAR_PER_PSI
}
#[must_use]
pub fn bar_to_psi(bar: f64) -> f64 {
bar / BAR_PER_PSI
}
#[must_use]
pub fn md_to_m2(md: f64) -> f64 {
md * MD_TO_M2
}
#[must_use]
pub fn m2_to_md(m2: f64) -> f64 {
m2 / MD_TO_M2
}
pub const M3_PER_MCM: f64 = 1.0e6;
pub const SM3_PER_MSM3: f64 = 1.0e6;
pub const SM3_PER_BCM: f64 = 1.0e9;
pub const SCF_PER_SM3: f64 = 35.314_666_721_488_59;
pub const SM3_PER_STB: f64 = M3_PER_BBL;
pub const M2_PER_KM2: f64 = 1.0e6;
#[must_use]
pub fn m3_to_mcm(m3: f64) -> f64 {
m3 / M3_PER_MCM
}
#[must_use]
pub fn mcm_to_m3(mcm: f64) -> f64 {
mcm * M3_PER_MCM
}
#[must_use]
pub fn m3_to_msm3(m3: f64) -> f64 {
m3 / SM3_PER_MSM3
}
#[must_use]
pub fn msm3_to_m3(msm3: f64) -> f64 {
msm3 * SM3_PER_MSM3
}
#[must_use]
pub fn m3_to_bcm(m3: f64) -> f64 {
m3 / SM3_PER_BCM
}
#[must_use]
pub fn bcm_to_m3(bcm: f64) -> f64 {
bcm * SM3_PER_BCM
}
#[must_use]
pub fn scf_to_sm3(scf: f64) -> f64 {
scf / SCF_PER_SM3
}
#[must_use]
pub fn sm3_to_scf(sm3: f64) -> f64 {
sm3 * SCF_PER_SM3
}
#[must_use]
pub fn stb_to_sm3(stb: f64) -> f64 {
stb * SM3_PER_STB
}
#[must_use]
pub fn sm3_to_stb(sm3: f64) -> f64 {
sm3 / SM3_PER_STB
}
#[must_use]
pub fn km2_to_m2(km2: f64) -> f64 {
km2 * M2_PER_KM2
}
#[must_use]
pub fn m2_to_km2(m2: f64) -> f64 {
m2 / M2_PER_KM2
}
#[must_use]
pub fn format_volume(v_m3: f64) -> String {
let a = v_m3.abs();
if a >= SM3_PER_BCM {
format!("{:.1} bcm", v_m3 / SM3_PER_BCM)
} else if a >= M3_PER_MCM {
format!("{:.1} mcm", v_m3 / M3_PER_MCM)
} else {
format!("{v_m3:.1} m³")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn acre_roundtrips_through_ft3() {
assert!((acre_ft_to_ft3(1.0) - 43_560.0).abs() < 1e-9);
assert!((ft3_to_acre_ft(43_560.0) - 1.0).abs() < 1e-12);
}
#[test]
fn barrel_roundtrips() {
let v = 12_345.6_f64;
assert!((rb_to_ft3(ft3_to_rb(v)) - v).abs() < 1e-6);
}
#[test]
fn rankine_offset() {
assert!((degf_to_degr(60.0) - 519.67).abs() < 1e-9);
}
#[test]
fn feet_metres_roundtrip_and_known() {
assert!((ft_to_m(1.0) - 0.3048).abs() < 1e-15);
assert!((ft_to_m(100.0) - 30.48).abs() < 1e-12);
assert!((m_to_ft(0.3048) - 1.0).abs() < 1e-12);
assert!((m_to_ft(ft_to_m(1234.5)) - 1234.5).abs() < 1e-9);
}
#[test]
fn cubic_metres_barrels_known() {
assert!((bbl_to_m3(1.0) - 0.158_987_294_928).abs() < 1e-15);
assert!((m3_to_bbl(0.158_987_294_928) - 1.0).abs() < 1e-12);
assert!((m3_to_bbl(1000.0) - 6_289.810_770_432_105).abs() < 1e-6);
assert!((bbl_to_m3(m3_to_bbl(42.0)) - 42.0).abs() < 1e-9);
}
#[test]
fn psi_bar_known() {
assert!((psi_to_bar(1.0) - 0.068_947_572_931_683_6).abs() < 1e-15);
assert!((bar_to_psi(1.0) - 14.503_773_773_022_1).abs() < 1e-6);
assert!((bar_to_psi(psi_to_bar(2500.0)) - 2500.0).abs() < 1e-9);
}
#[test]
fn millidarcy_square_metres_known() {
assert!((md_to_m2(1.0) - 9.869_233e-16).abs() < 1e-30);
assert!((m2_to_md(9.869_233e-16) - 1.0).abs() < 1e-9);
assert!((m2_to_md(md_to_m2(150.0)) - 150.0).abs() < 1e-9);
}
#[test]
fn si_report_scales_known() {
assert_eq!(m3_to_mcm(2.5e6), 2.5);
assert_eq!(mcm_to_m3(2.5), 2.5e6);
assert_eq!(m3_to_msm3(3.0e6), 3.0);
assert_eq!(msm3_to_m3(3.0), 3.0e6);
assert_eq!(m3_to_bcm(4.0e9), 4.0);
assert_eq!(bcm_to_m3(4.0), 4.0e9);
assert!((m3_to_mcm(mcm_to_m3(7.25)) - 7.25).abs() < 1e-12);
assert!((m3_to_bcm(bcm_to_m3(1.5)) - 1.5).abs() < 1e-12);
}
#[test]
fn scf_sm3_geometric_factor() {
assert!((sm3_to_scf(1.0) - 35.314_666_721_488_59).abs() < 1e-9);
assert!((scf_to_sm3(35.314_666_721_488_59) - 1.0).abs() < 1e-12);
assert!((scf_to_sm3(sm3_to_scf(123.4)) - 123.4).abs() < 1e-9);
}
#[test]
fn stb_sm3_shares_barrel_volume() {
assert_eq!(SM3_PER_STB, M3_PER_BBL);
assert!((stb_to_sm3(1.0) - 0.158_987_294_928).abs() < 1e-15);
assert!((sm3_to_stb(stb_to_sm3(1000.0)) - 1000.0).abs() < 1e-9);
}
#[test]
fn km2_m2_area_scale_known() {
assert_eq!(M2_PER_KM2, 1.0e6);
assert_eq!(km2_to_m2(2.5), 2.5e6);
assert_eq!(m2_to_km2(2.5e6), 2.5);
assert!((m2_to_km2(km2_to_m2(7.25)) - 7.25).abs() < 1e-12);
}
#[test]
fn format_volume_picks_scale() {
assert_eq!(format_volume(12_400_000.0), "12.4 mcm");
assert_eq!(format_volume(4_000_000_000.0), "4.0 bcm");
assert_eq!(format_volume(950.0), "950.0 m³");
}
}