use std::f64::consts::E;
const ELL01: [f64; 12] = [6.5, 7.5, 9., 12.8, 13.9, 13.9, 12.4, 10.9, 9.4, 8., 7., 6.];
const ELL02: [f64; 12] = [7.9, 8.4, 8.9, 9.5, 9.9, 10.2, 10.1, 9.7, 9.1, 8.6, 8.1, 7.8];
const ELL03: [f64; 12] = [10.1, 9.6, 9.1, 8.5, 8.1, 7.8, 7.9, 8.3, 8.9, 9.4, 9.9, 10.2];
const ELL04: [f64; 12] = [
11.5, 10.5, 9.2, 7.9, 6.8, 6.2, 6.5, 7.4, 8.7, 10., 11.2, 11.8,
];
pub fn duff_moisture_code(
prev_dmc: f64,
temp: f64,
rh: f64,
precip: f64,
lat: f64,
mon: usize,
lat_adjustment: Option<bool>,
) -> f64 {
let lat_adjustment = lat_adjustment.unwrap_or(true);
let index = (mon).clamp(1, 12) - 1;
let temp = temp.max(-1.1);
let rk = 1.894 * (temp + 1.1) * (100. - rh) * ELL01[index] * 1e-4;
let rk = if lat_adjustment {
if lat <= 30. && lat > 10. {
1.894 * (temp + 1.1) * (100. - rh) * ELL02[index] * 1e-4
} else if lat <= -10. && lat > -30. {
1.894 * (temp + 1.1) * (100. - rh) * ELL03[index] * 1e-4
} else if (-90. ..=-30.).contains(&lat) {
1.894 * (temp + 1.1) * (100. - rh) * ELL04[index] * 1e-4
} else if lat <= 10. && lat > -10. {
1.894 * (temp + 1.1) * (100. - rh) * 9. * 1e-4
} else {
rk
}
} else {
rk
};
let pr = if precip <= 1.5 {
prev_dmc
} else {
let ra = precip;
let rw = 0.92 * ra - 1.27;
let wmi = 20. + 280. / E.powf(0.023 * prev_dmc);
let b = if prev_dmc <= 33. {
100. / (0.5 + 0.3 * prev_dmc)
} else if prev_dmc <= 65. {
14. - 1.3 * prev_dmc.ln()
} else {
6.2 * prev_dmc.ln() - 17.2
};
let wmr = wmi + 1000. * rw / (48.77 + b * rw);
43.43 * (5.6348 - (wmr - 20.).ln())
};
let pr = pr.max(0.);
(pr + rk).max(0.)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_util::precision_f64;
#[derive(Debug, serde::Deserialize)]
struct TestRow {
prev_dmc: f64,
temp: f64,
rh: f64,
precip: f64,
lat: f64,
mon: usize,
lat_adjustment: Option<bool>,
dmc: f64,
}
#[test]
fn test_duff_moisture_code() -> Result<(), Box<dyn std::error::Error>> {
let fixture = std::fs::File::open("./tests/fixtures/dmc.csv")?;
let mut rdr = csv::Reader::from_reader(fixture);
for result in rdr.deserialize() {
let record: TestRow = result?;
let dmc = duff_moisture_code(
record.prev_dmc,
record.temp,
record.rh,
record.precip,
record.lat,
record.mon,
record.lat_adjustment,
);
assert_eq!(precision_f64(dmc, 4), record.dmc);
}
Ok(())
}
}