use serde::{Deserialize, Serialize};
use crate::{
models::masw::{Masw, MaswExp},
validation::ValidationError,
};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VsLayerData {
pub thickness: f64,
pub vs: f64,
pub h_over_vs: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VsSoilClassificationResult {
pub layers: Vec<VsLayerData>,
pub sum_h_over_vs: f64,
pub vs_30: f64,
pub soil_class: String,
}
pub fn validate_input(masw: &Masw) -> Result<(), ValidationError> {
masw.validate(&["thickness", "vs"])?;
Ok(())
}
pub fn compute_vs_30(masw_exp: &MaswExp) -> Vec<VsLayerData> {
let mut remaining_depth = 30.0;
let mut result = Vec::new();
for layer in &masw_exp.layers {
if remaining_depth <= 0.0 {
break;
}
let thickness = layer.thickness.unwrap().min(remaining_depth);
let vs = layer.vs.unwrap();
if vs <= 0.0 {
continue; }
let h_over_vs = thickness / vs;
result.push(VsLayerData {
thickness,
vs,
h_over_vs,
});
remaining_depth -= thickness;
}
result
}
pub fn calc_lsc_by_vs(masw: &mut Masw) -> Result<VsSoilClassificationResult, ValidationError> {
validate_input(masw)?;
let mut masw_exp = masw.get_idealized_exp("idealized".to_string());
masw_exp.calc_depths();
let vs_layers = compute_vs_30(&masw_exp);
let sum_h_over_vs: f64 = vs_layers.iter().map(|l| l.h_over_vs).sum();
let depth = masw_exp.layers.last().unwrap().depth.unwrap().min(30.);
let vs_30 = if sum_h_over_vs > 0.0 {
depth / sum_h_over_vs
} else {
0.0
};
let soil_class = match vs_30 {
c if c > 1500.0 => "ZA",
c if c >= 760.0 => "ZB",
c if c >= 360.0 => "ZC",
c if c >= 180.0 => "ZD",
_ => "ZE",
}
.to_string();
Ok(VsSoilClassificationResult {
layers: vs_layers,
sum_h_over_vs,
vs_30,
soil_class,
})
}