Skip to main content

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}