use crate::{
error::Result,
layers::{
cold_surface_temperature_layer, melting_freezing_energy_area,
warm_surface_temperature_layer, warm_temperature_layer_aloft, Layer,
},
precip_type::{is_drizzler, PrecipType},
sounding::Sounding,
};
use metfor::JpKg;
pub fn bourgouin_precip_type(snd: &Sounding) -> Result<PrecipType> {
let b_type = analyze_bourgouin_type(snd)?;
let mut p_type = match b_type {
BourgouinType::A {
ref cold_surface,
ref warm_layer_aloft,
} => bourgouin_type_a_precip_type(snd, cold_surface, warm_layer_aloft)?,
BourgouinType::B {
ref warm_sfc_layer,
ref warm_layer_aloft,
} => bourgouin_type_b_precip_type(snd, warm_sfc_layer, warm_layer_aloft)?,
BourgouinType::C { ref warm_sfc_layer } => {
bourgouin_type_c_precip_type(snd, warm_sfc_layer)?
}
BourgouinType::D => bourgouin_type_d_precip_type(snd),
};
if is_drizzler(snd) {
match b_type {
BourgouinType::A { .. } | BourgouinType::D => p_type = PrecipType::LightFreezingDrizzle,
BourgouinType::B { .. } => match p_type {
PrecipType::LightIcePellets | PrecipType::LightSnow => {
p_type = PrecipType::LightFreezingDrizzle
}
PrecipType::LightRain | PrecipType::LightRainAndSnow => {
p_type = PrecipType::LightDrizzle
}
_ => {}
},
BourgouinType::C { .. } => {}
}
}
Ok(p_type)
}
#[derive(Debug)]
#[allow(clippy::large_enum_variant)]
enum BourgouinType {
A {
cold_surface: Layer,
warm_layer_aloft: Layer,
},
B {
warm_sfc_layer: Layer,
warm_layer_aloft: Layer,
},
C {
warm_sfc_layer: Layer,
},
D,
}
fn analyze_bourgouin_type(snd: &Sounding) -> Result<BourgouinType> {
let warm_layers = warm_temperature_layer_aloft(snd)?;
let warm_layer_aloft: Option<Layer> = warm_layers.get(0).cloned();
let b_type = match warm_layer_aloft {
Some(warm_layer_aloft) => {
if let Some(cold_surface) = cold_surface_temperature_layer(snd, &warm_layers)? {
BourgouinType::A {
warm_layer_aloft,
cold_surface,
}
} else if let Some(warm_sfc_layer) = warm_surface_temperature_layer(snd)? {
BourgouinType::B {
warm_layer_aloft,
warm_sfc_layer,
}
} else {
unreachable!()
}
}
None => {
if let Some(warm_sfc_layer) = warm_surface_temperature_layer(snd)? {
BourgouinType::C { warm_sfc_layer }
} else {
BourgouinType::D
}
}
};
Ok(b_type)
}
fn bourgouin_type_a_precip_type(
snd: &Sounding,
cold_surface: &Layer,
warm_aloft: &Layer,
) -> Result<PrecipType> {
let positive_area = melting_freezing_energy_area(snd, warm_aloft)?;
debug_assert!(positive_area >= JpKg(0.0));
if positive_area < JpKg(2.0) {
return Ok(PrecipType::LightSnow);
}
let negative_area = melting_freezing_energy_area(snd, cold_surface)?;
debug_assert!(negative_area <= JpKg(0.0));
let threshold = JpKg(56.0) + positive_area * 0.66;
let p_type = if negative_area > threshold {
PrecipType::LightIcePellets
} else {
PrecipType::LightFreezingRain
};
Ok(p_type)
}
fn bourgouin_type_b_precip_type(
snd: &Sounding,
warm_surface: &Layer,
warm_aloft: &Layer,
) -> Result<PrecipType> {
let cold_layer_aloft = &Layer {
bottom: warm_surface.top,
top: warm_aloft.bottom,
};
let top_p_type = bourgouin_type_a_precip_type(snd, cold_layer_aloft, warm_aloft)?;
let p_type = match top_p_type {
PrecipType::LightSnow | PrecipType::LightIcePellets => {
bourgouin_type_c_precip_type(snd, warm_surface)?
}
_ => PrecipType::LightRain,
};
Ok(p_type)
}
fn bourgouin_type_c_precip_type(snd: &Sounding, warm_surface: &Layer) -> Result<PrecipType> {
let melting_energy = melting_freezing_energy_area(snd, warm_surface)?;
debug_assert!(melting_energy >= JpKg(0.0));
let p_type = if melting_energy < JpKg(5.6) {
PrecipType::LightSnow
} else if melting_energy > JpKg(13.2) {
PrecipType::LightRain
} else {
PrecipType::LightRainAndSnow
};
Ok(p_type)
}
fn bourgouin_type_d_precip_type(_snd: &Sounding) -> PrecipType {
PrecipType::LightSnow
}