hpr_atmos/error.rs
1//! Error types for the atmosphere and wind models.
2
3use thiserror::Error;
4
5/// What a profile with a column on only some levels should do: the hint that ends
6/// [`AtmosError::IncompleteColumn`]'s message, so a program can show it apart from the fact
7/// (`hpr` writes it as a `help:` line).
8pub const INCOMPLETE_COLUMN_HINT: &str = "give it on every level or none";
9
10/// An error from an atmosphere or wind model: an input outside a model's domain, or a profile
11/// that can't be built.
12#[derive(Debug, Clone, PartialEq, Error)]
13#[non_exhaustive]
14pub enum AtmosError {
15 /// An input outside a model's domain, such as a negative pressure or a non-finite height.
16 #[error("{what} is outside its domain: {value}")]
17 Domain {
18 /// What the value is.
19 what: &'static str,
20 /// The offending value.
21 value: f64,
22 },
23 /// A sounding profile or a layered wind was given no levels.
24 #[error("a profile or wind table needs at least one level")]
25 NoLevels,
26 /// A gust field has no samples.
27 #[error("a gust field needs at least one sample")]
28 EmptyGustField,
29 /// Profile heights must strictly increase.
30 #[error("profile heights must strictly increase; level {index} does not")]
31 HeightsNotIncreasing {
32 /// Index of the first level whose height is not above its predecessor's.
33 index: usize,
34 },
35 /// An optional column (humidity or wind) is given on some levels but not on this one.
36 #[error("level {index} has no {column}, but other levels do; {INCOMPLETE_COLUMN_HINT}")]
37 IncompleteColumn {
38 /// Index of the level without the value.
39 index: usize,
40 /// The column.
41 column: &'static str,
42 },
43 /// A sounding level's pressure is not below the pressure of the level beneath it. Pressure
44 /// must fall with height; a pressure in hPa given as Pa is the usual cause.
45 #[error(
46 "sounding level {index} has pressure {pressure_pa} Pa, not below {below_pa} Pa beneath it"
47 )]
48 PressureNotDecreasing {
49 /// Index of the level.
50 index: usize,
51 /// Its pressure, Pa.
52 pressure_pa: f64,
53 /// The pressure of the level below, given or filled in, Pa.
54 below_pa: f64,
55 },
56 /// The lowest level of a sounding must give its pressure; the levels above may be filled in
57 /// hydrostatically.
58 #[error("the lowest sounding level must give its pressure")]
59 MissingBasePressure,
60}
61
62/// Returns `value` if it is finite, or [`AtmosError::Domain`] naming `what`.
63pub(crate) fn finite(what: &'static str, value: f64) -> Result<f64, AtmosError> {
64 if value.is_finite() {
65 Ok(value)
66 } else {
67 Err(AtmosError::Domain { what, value })
68 }
69}
70
71/// Returns `value` if it is finite and strictly positive, or [`AtmosError::Domain`].
72pub(crate) fn positive(what: &'static str, value: f64) -> Result<f64, AtmosError> {
73 if value.is_finite() && value > 0.0 {
74 Ok(value)
75 } else {
76 Err(AtmosError::Domain { what, value })
77 }
78}