use crate::core::inplace::BoxDraw;
use crate::core::model::{ModelInputs, ModelResult};
use crate::units::SrsError;
use petekstatic::uncertainty::{run, Distribution};
use petektools::units::m3_to_mcm;
fn draw_at(inputs: &ModelInputs, mut sample: impl FnMut(Distribution) -> f64) -> BoxDraw {
BoxDraw {
area_m2: sample(inputs.area_m2),
gross_height_m: sample(inputs.gross_height_m),
top_depth_m: inputs.top_depth_m,
contact_depth_m: inputs.contact_depth_m,
porosity: sample(inputs.porosity),
net_to_gross: sample(inputs.net_to_gross),
water_saturation: sample(inputs.water_saturation),
fvf: sample(inputs.fvf),
}
}
pub fn run_model(inputs: &ModelInputs, n: usize, seed: u64) -> Result<ModelResult, SrsError> {
let p50 = draw_at(inputs, |d| d.quantile(0.5));
p50.validate(inputs.fluid)?;
let deterministic_in_place = p50.in_place(inputs.fluid);
let deterministic_grv_mcm = m3_to_mcm(p50.grv_m3());
let fluid = inputs.fluid;
let realizations = run(n, seed, |rng| {
let d = draw_at(inputs, |dist| dist.sample(rng));
d.in_place(fluid)
});
let summary = realizations.summary()?;
Ok(ModelResult {
fluid,
deterministic_in_place,
deterministic_grv_mcm,
summary,
realizations: n,
samples: realizations.values,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::inplace::Fluid;
use petekstatic::grid::Dims;
fn oil_model() -> ModelInputs {
ModelInputs {
area_m2: Distribution::triangular(320_000.0, 400_000.0, 520_000.0).unwrap(),
gross_height_m: Distribution::triangular(12.0, 15.0, 20.0).unwrap(),
porosity: Distribution::Constant(0.25),
net_to_gross: Distribution::Constant(0.8),
water_saturation: Distribution::Constant(0.3),
fvf: Distribution::Constant(1.25),
fluid: Fluid::Oil,
dims: Dims::new(10, 10, 5).unwrap(),
top_depth_m: 1500.0,
contact_depth_m: 2743.0,
aspect_ratio: 1.0,
}
}
#[test]
fn deterministic_matches_p50_draw() {
let m = oil_model();
let r = run_model(&m, 10_000, 1).unwrap();
assert!((r.deterministic_in_place - r.summary.p50).abs() / r.summary.p50 < 0.03);
}
#[test]
fn percentiles_are_ordered_and_reproducible() {
let m = oil_model();
let a = run_model(&m, 20_000, 42).unwrap();
let b = run_model(&m, 20_000, 42).unwrap();
assert!(a.summary.p90 < a.summary.p50 && a.summary.p50 < a.summary.p10);
assert_eq!(a.summary.p50, b.summary.p50);
assert_eq!(a.summary.p10, b.summary.p10);
}
#[test]
fn deterministic_grv_is_positive() {
let r = run_model(&oil_model(), 100, 7).unwrap();
assert!(r.deterministic_grv_mcm > 0.0);
assert!((4.0..9.0).contains(&r.deterministic_grv_mcm));
}
#[test]
fn zero_realizations_is_a_typed_error() {
assert!(run_model(&oil_model(), 0, 1).is_err());
}
#[test]
fn non_physical_inputs_are_typed_errors() {
let mut bad_phi = oil_model();
bad_phi.porosity = Distribution::Constant(-0.1); assert!(run_model(&bad_phi, 1000, 1).is_err());
let mut bad_sw = oil_model();
bad_sw.water_saturation = Distribution::Constant(1.2); assert!(run_model(&bad_sw, 1000, 1).is_err());
let mut bad_fvf = oil_model();
bad_fvf.fvf = Distribution::Constant(0.0); assert!(run_model(&bad_fvf, 1000, 1).is_err());
}
}