altrios_core/
traits.rs

1use crate::imports::*;
2pub mod serde_api;
3pub use serde_api::*;
4
5///Standardizes conversion from smaller than usize types for indexing.
6pub trait Idx {
7    fn idx(self) -> usize;
8}
9
10#[duplicate_item(Self; [u8]; [u16])]
11impl Idx for Self {
12    fn idx(self) -> usize {
13        self.into()
14    }
15}
16
17impl Idx for u32 {
18    fn idx(self) -> usize {
19        self.try_into().unwrap()
20    }
21}
22
23impl Idx for Option<NonZeroU16> {
24    fn idx(self) -> usize {
25        self.map(u16::from).unwrap_or(0) as usize
26    }
27}
28
29/// Trait implemented for indexing types, specifically `usize`, to assist in
30/// converting them into an `Option<NonZeroUxxxx>`.
31///
32/// This is necessary because both the common type conversion trait ([From])
33/// and the types involved (e.g. `Option<NonZeroU16>` ) are from the
34/// standard library.  Rust does not allow for the combination of traits and
35/// types if both are from external libraries.
36///
37/// This trait is default implemented for [usize] to convert into any type that
38/// already implements [TryFrom]<[NonZeroUsize]>, which includes most of the
39/// NonZeroUxxxx types.
40///
41/// This approach will error on a loss of precision.  So, if the [usize] value
42/// does not fit into the `NonZero` type, then an [Error] variant is returned.
43///
44/// If the value is `0`, then an [Ok]\([None]) variant is returned.
45///
46/// Note that the base `NonZero` types already have a similar feature if
47/// converting from the same basic type (e.g. [u16] to [Option]<[NonZeroU16]>),
48/// where the type is guaranteed to fit, but just might be `0`.  If that is the
49/// use case, then just use the `new()` method, which returns the [None] variant
50/// if the value is `0`.
51///
52/// The intended usage is as follows:
53/// ```
54/// # use std::num::NonZeroU8;
55/// # use altrios_core::traits::TryFromIdx;
56///
57/// let non_zero : usize = 42;
58/// let good_val : Option<NonZeroU8> = non_zero.try_from_idx().unwrap();
59/// assert!(good_val == NonZeroU8::new(42));
60///
61/// let zero : usize = 0;
62/// let none_val : Option<NonZeroU8> = zero.try_from_idx().unwrap();
63/// assert!(none_val == None);
64///
65/// let too_big : usize = 256;
66/// let bad_val : Result<Option<NonZeroU8>, _> = too_big.try_from_idx();
67/// assert!(bad_val.is_err());
68/// ```
69pub trait TryFromIdx<T> {
70    type Error;
71
72    fn try_from_idx(&self) -> Result<Option<T>, Self::Error>;
73}
74
75impl<T> TryFromIdx<T> for usize
76where
77    T: TryFrom<NonZeroUsize>,
78{
79    type Error = <T as TryFrom<NonZeroUsize>>::Error;
80
81    fn try_from_idx(&self) -> Result<Option<T>, Self::Error> {
82        NonZeroUsize::new(*self).map_or(
83            // If value is a 0-valued usize, then we immediately return an
84            // Ok(None).
85            Ok(None),
86            // Otherwise we attempt to convert it from a NonZeroUsize into a
87            // different type with potentially smaller accuracy.
88            |val| {
89                T::try_from(val)
90                    // We wrap a valid result in Some
91                    .map(Some)
92            },
93        )
94    }
95}
96
97pub trait Linspace {
98    fn linspace(start: f64, stop: f64, n_elements: usize) -> Vec<f64> {
99        let n_steps = n_elements - 1;
100        let step_size = (stop - start) / n_steps as f64;
101        let v_norm: Vec<f64> = (0..=n_steps)
102            .collect::<Vec<usize>>()
103            .iter()
104            .map(|x| *x as f64)
105            .collect();
106        let v = v_norm.iter().map(|x| (x * step_size) + start).collect();
107        v
108    }
109}
110
111impl Linspace for Vec<f64> {}
112
113/// Provides method for checking if an instance of `Self` is equal to `Self::default`
114pub trait EqDefault: Default + PartialEq {
115    /// Checks if an instance of `Self` is equal to `Self::default`
116    fn eq_default(&self) -> bool {
117        *self == Self::default()
118    }
119}
120impl<T: Default + PartialEq> EqDefault for T {}
121
122#[derive(Default, Deserialize, Serialize, Debug, Clone, PartialEq)]
123/// Governs which side effect to trigger when setting mass
124pub enum MassSideEffect {
125    /// To be used when [MassSideEffect] is not applicable
126    #[default]
127    None,
128    /// Set the extensive parameter -- e.g. energy, power -- as a side effect
129    Extensive,
130    /// Set the intensive parameter -- e.g. specific power, specific energy -- as a side effect
131    Intensive,
132}
133
134impl TryFrom<String> for MassSideEffect {
135    type Error = anyhow::Error;
136    fn try_from(value: String) -> anyhow::Result<MassSideEffect> {
137        let mass_side_effect = match value.as_str() {
138            "None" => Self::None,
139            "Extensive" => Self::Extensive,
140            "Intensive" => Self::Intensive,
141            _ => {
142                bail!(format!(
143                    "`MassSideEffect` must be 'Intensive', 'Extensive', or 'None'. "
144                ))
145            }
146        };
147        Ok(mass_side_effect)
148    }
149}
150
151pub trait Mass {
152    /// Returns mass of Self, either from `self.mass` or
153    /// the derived from fields that store mass data. `Mass::mass` also checks that
154    /// derived mass, if Some, is same as `self.mass`.
155    fn mass(&self) -> anyhow::Result<Option<si::Mass>>;
156
157    /// Sets component mass to `mass`, or if `None` is provided for `mass`,
158    /// sets mass based on other component parameters (e.g. power and power
159    /// density, sum of fields containing mass)
160    fn set_mass(
161        &mut self,
162        new_mass: Option<si::Mass>,
163        side_effect: MassSideEffect,
164    ) -> anyhow::Result<()>;
165
166    /// Returns derived mass (e.g. sum of mass fields, or
167    /// calculation involving mass specific properties).  If
168    fn derived_mass(&self) -> anyhow::Result<Option<si::Mass>>;
169
170    /// Sets all fields that are used in calculating derived mass to `None`.
171    /// Does not touch `self.mass`.
172    fn expunge_mass_fields(&mut self);
173
174    /// Sets any mass-specific property with appropriate side effects
175    fn set_mass_specific_property(&mut self) -> anyhow::Result<()> {
176        // TODO: remove this default implementation when this method has been universally implemented.
177        // For structs without specific properties, return an error
178        Ok(())
179    }
180}
181
182/// Super trait to ensure that related traits are implemented together
183pub trait StateMethods: SetCumulative + SaveState + Step + CheckAndResetState {}
184
185/// Trait for setting cumulative values based on rate values
186pub trait SetCumulative {
187    /// Sets cumulative values based on rate values
188    fn set_cumulative<F: Fn() -> String>(&mut self, dt: si::Time, loc: F) -> anyhow::Result<()>;
189}
190
191/// Provides method that saves `self.state` to `self.history` and propagates to any fields with
192/// `state`
193pub trait SaveState {
194    /// Saves `self.state` to `self.history` and propagates to any fields with `state`
195    /// # Arguments
196    /// - `loc`: closure that returns file and line number where called
197    fn save_state<F: Fn() -> String>(&mut self, loc: F) -> anyhow::Result<()>;
198}
199
200/// Trait that provides method for incrementing `i` field of this and all contained structs,
201/// recursively
202pub trait Step {
203    /// Increments `i` field of this and all contained structs, recursively
204    /// # Arguments
205    /// - `loc`: closure that returns file and line number where called
206    fn step<F: Fn() -> String>(&mut self, loc: F) -> anyhow::Result<()>;
207}
208
209/// Provides methods for getting and setting the save interval
210pub trait HistoryMethods: SaveState {
211    /// Recursively sets save interval
212    /// # Arguments
213    /// - `save_interval`: time step interval at which to save `self.state` to `self.history`
214    fn set_save_interval(&mut self, save_interval: Option<usize>) -> anyhow::Result<()>;
215    /// Returns save interval for `self` but does not guarantee recursive consistency in nested
216    /// objects
217    fn save_interval(&self) -> anyhow::Result<Option<usize>>;
218    /// Remove all history
219    fn clear(&mut self);
220}