use crate::{models::soil_profile::SoilProfile, validation::ValidationError};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CuLayerData {
pub thickness: f64,
pub cu: f64,
pub h_over_cu: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CuSoilClassificationResult {
pub layers: Vec<CuLayerData>,
pub sum_h_over_cu: f64,
pub cu_30: f64,
pub soil_class: String,
}
pub fn validate_input(soil_profile: &SoilProfile) -> Result<(), ValidationError> {
soil_profile.validate(&["thickness", "cu"])?;
Ok(())
}
pub fn compute_cu_30(profile: &SoilProfile) -> Vec<CuLayerData> {
let mut remaining_depth = 30.0;
let mut result = Vec::new();
for layer in &profile.layers {
if remaining_depth <= 0.0 {
break;
}
let thickness = layer.thickness.unwrap().min(remaining_depth);
let cu = layer.cu.unwrap_or(0.0);
if cu <= 0.0 {
continue; }
let h_over_cu = thickness / cu;
result.push(CuLayerData {
thickness,
cu,
h_over_cu,
});
remaining_depth -= thickness;
}
result
}
pub fn calc_lsc_by_cu(
soil_profile: &mut SoilProfile,
) -> Result<CuSoilClassificationResult, ValidationError> {
validate_input(soil_profile)?;
soil_profile.calc_layer_depths();
let cu_layers = compute_cu_30(soil_profile);
let sum_h_over_cu: f64 = cu_layers.iter().map(|l| l.h_over_cu).sum();
let depth = soil_profile.layers.last().unwrap().depth.unwrap().min(30.);
let cu_30 = if sum_h_over_cu > 0.0 {
depth / sum_h_over_cu
} else {
0.0
};
let soil_class = match cu_30 {
c if c > 25.0 => "ZC",
c if c >= 7.0 => "ZD",
_ => "ZE",
}
.to_string();
Ok(CuSoilClassificationResult {
layers: cu_layers,
sum_h_over_cu,
cu_30,
soil_class,
})
}