use super::{Layer, Layers};
use crate::{
error::{AnalysisError, Result},
sounding::Sounding,
};
use itertools::{izip, Itertools};
use metfor::HectoPascal;
pub fn inversions(snd: &Sounding, top_p: HectoPascal) -> Result<Layers> {
let t_profile = snd.temperature_profile();
let p_profile = snd.pressure_profile();
if t_profile.is_empty() || p_profile.is_empty() {
return Err(AnalysisError::MissingProfile);
}
izip!(0usize.., p_profile, t_profile)
.filter(|&(_, p, t)| p.is_some() && t.is_some())
.map(|(i, p, t)| (i, p.unpack(), t.unpack()))
.take_while(|&(_, p, _)| p >= top_p)
.map(|(i, _, t)| (i, t))
.tuple_windows::<(_, _)>()
.map(|((i0, t0), (i1, t1))| {
if t1 > t0 {
LayerType::Inversion(i0, i1)
} else {
LayerType::NonInversion
}
})
.scan(
(None, None),
|layers: &mut (Option<usize>, Option<usize>), layer: LayerType| {
let bottom: &mut Option<usize> = &mut layers.0;
let top: &mut Option<usize> = &mut layers.1;
match layer {
LayerType::Inversion(i0, i1) => {
if bottom.is_none() {
*bottom = Some(i0);
}
*top = Some(i1);
Some(None)
}
LayerType::NonInversion => {
if let (Some(i0), Some(i1)) = (bottom.take(), top.take()) {
Some(Some((i0, i1)))
} else {
Some(None)
}
}
}
},
)
.flatten()
.map(|(i0, i1)| {
snd.data_row(i0)
.and_then(|bottom| snd.data_row(i1).map(|top| Layer { bottom, top }))
})
.map(|opt| opt.ok_or(AnalysisError::InvalidInput))
.collect()
}
pub fn sfc_based_inversion(snd: &Sounding) -> Result<Option<Layer>> {
let t_profile = snd.temperature_profile();
let p_profile = snd.pressure_profile();
let h_profile = snd.height_profile();
if t_profile.is_empty() || p_profile.is_empty() || h_profile.is_empty() {
return Err(AnalysisError::MissingProfile);
}
let sfc_layer = izip!(0usize.., p_profile, h_profile, t_profile)
.filter(|(_, p, h, t)| p.is_some() && h.is_some() && t.is_some())
.map(|(i, p, _, t)| (i, p.unpack(), t.unpack()))
.take_while(|(_, p, _)| *p > HectoPascal(690.0))
.tuple_windows::<(_, _)>()
.map(|((i0, _, t0), (i1, _, t1))| {
if t1 > t0 {
LayerType::Inversion(i0, i1)
} else {
LayerType::NonInversion
}
})
.take_while(|lyr| {
std::mem::discriminant(lyr) == std::mem::discriminant(&LayerType::Inversion(0, 0))
})
.map(|lyr| match lyr {
LayerType::Inversion(i0, i1) => (i0, i1),
LayerType::NonInversion => unreachable!(),
})
.fold(None, |combined_layers, (i0, i1)| match combined_layers {
None => Some((i0, i1)),
Some((bottom, _top)) => Some((bottom, i1)),
}) .and_then(|(bottom_i, top_i)| {
let bottom = snd.data_row(bottom_i)?;
let top = snd.data_row(top_i)?;
Some(Layer { bottom, top })
});
Ok(sfc_layer)
}
enum LayerType {
Inversion(usize, usize),
NonInversion,
}