hpr_aero/error.rs
1//! Error types for the aerodynamic models.
2
3use hpr_core::CoreError;
4use hpr_design::DesignError;
5use thiserror::Error;
6
7/// An error from the aerodynamic models: an input outside the models' domain, a design they can't
8/// describe, or an error from the design itself.
9#[derive(Debug, Clone, PartialEq, Error)]
10#[non_exhaustive]
11pub enum AeroError {
12 /// An input outside its domain, such as a negative span or a non-finite angle.
13 #[error("{what} is outside its domain: {value}")]
14 Domain {
15 /// What the value is.
16 what: &'static str,
17 /// The offending value.
18 value: f64,
19 },
20 /// A Mach number past the range of the model asked: `0 ≤ M < 5` for the normal force
21 /// ([`crate::model::NORMAL_FORCE_MACH_LIMIT`]) and the drag buildup
22 /// ([`crate::drag::BUILDUP_MACH_LIMIT`]); an override table takes any Mach number.
23 #[error("Mach {mach} is outside {model}'s range [0, {limit})")]
24 Mach {
25 /// The Mach number.
26 mach: f64,
27 /// The top of the model's range, which it doesn't reach.
28 limit: f64,
29 /// The model that refused it, such as "the drag buildup".
30 model: &'static str,
31 },
32 /// A part the models have no cited method for, such as tube fins.
33 #[error("no aerodynamic model: {0}")]
34 Unsupported(String),
35 /// A layout that doesn't hold together for the aerodynamic models, such as a fin set without
36 /// the radius of its body tube.
37 #[error("inconsistent layout: {0}")]
38 Layout(String),
39 /// An error in one component, with its id.
40 #[error("{id}: {source}")]
41 InComponent {
42 /// The component's id.
43 id: String,
44 /// The error.
45 source: Box<AeroError>,
46 },
47 /// An error from a drag model of a program's own ([`crate::custom::DragModel`]), as it gave
48 /// it: its own refusal, or one of hpr's it passed on.
49 #[error("drag model: {source}")]
50 DragModel {
51 /// The error.
52 source: Box<AeroError>,
53 },
54 /// An error from the design model, such as a profile whose volume integral fails.
55 #[error(transparent)]
56 Design(#[from] DesignError),
57 /// CSV text that doesn't read as a table, with its 1-based line (0 when the text has no
58 /// rows).
59 #[error("CSV line {line}: {message}")]
60 Csv {
61 /// The line, counting from 1.
62 line: usize,
63 /// What is wrong.
64 message: String,
65 },
66 /// A table that doesn't hold together, such as Mach numbers that don't increase.
67 #[error(transparent)]
68 Table(#[from] CoreError),
69}
70
71/// Checks that `value` is finite and positive (or non-negative when `allow_zero`).
72pub(crate) fn check_dimension(
73 what: &'static str,
74 value: f64,
75 allow_zero: bool,
76) -> Result<(), AeroError> {
77 let ok = value.is_finite() && (value > 0.0 || (allow_zero && value == 0.0));
78 if ok {
79 Ok(())
80 } else {
81 Err(AeroError::Domain { what, value })
82 }
83}
84
85/// Checks a Mach number in `[0, limit)` for `model`.
86pub(crate) fn check_mach(mach: f64, limit: f64, model: &'static str) -> Result<(), AeroError> {
87 if mach.is_finite() && (0.0..limit).contains(&mach) {
88 Ok(())
89 } else {
90 Err(AeroError::Mach { mach, limit, model })
91 }
92}