fastsim_core/
traits.rs

1use crate::error::Error;
2use crate::imports::*;
3
4pub trait Linspace {
5    /// Generate linearly spaced vec
6    /// # Arguments
7    /// - `start` - starting point
8    /// - `stop` - stopping point, inclusive
9    /// - `n_elements` - number of array elements
10    fn linspace(start: f64, stop: f64, n_elements: usize) -> Vec<f64> {
11        let n_steps = n_elements - 1;
12        let step_size = (stop - start) / n_steps as f64;
13        let v_norm: Vec<f64> = (0..=n_steps)
14            .collect::<Vec<usize>>()
15            .iter()
16            .map(|x| *x as f64)
17            .collect();
18        let v = v_norm.iter().map(|x| (x * step_size) + start).collect();
19        v
20    }
21}
22
23impl Linspace for Vec<f64> {}
24
25pub trait Min {
26    fn min(&self) -> anyhow::Result<f64>;
27}
28impl Min for &[f64] {
29    fn min(&self) -> anyhow::Result<f64> {
30        Ok(self.iter().fold(f64::INFINITY, |acc, curr| acc.min(*curr)))
31    }
32}
33impl Min for Vec<f64> {
34    fn min(&self) -> anyhow::Result<f64> {
35        self.as_slice().min()
36    }
37}
38impl Min for &[&f64] {
39    fn min(&self) -> anyhow::Result<f64> {
40        Ok(self.iter().fold(f64::INFINITY, |acc, curr| acc.min(**curr)))
41    }
42}
43impl Min for Vec<&f64> {
44    fn min(&self) -> anyhow::Result<f64> {
45        self.as_slice().min()
46    }
47}
48impl Min for &[Vec<f64>] {
49    fn min(&self) -> anyhow::Result<f64> {
50        self.iter()
51            .map(|v| v.min())
52            .try_fold(f64::INFINITY, |acc, x| Ok(acc.min(x?)))
53    }
54}
55impl Min for Vec<Vec<f64>> {
56    fn min(&self) -> anyhow::Result<f64> {
57        self.as_slice().min()
58    }
59}
60impl Min for &[Vec<Vec<f64>>] {
61    fn min(&self) -> anyhow::Result<f64> {
62        self.iter()
63            .map(|v| v.min())
64            .try_fold(f64::INFINITY, |acc, x| Ok(acc.min(x?)))
65    }
66}
67impl Min for Vec<Vec<Vec<f64>>> {
68    fn min(&self) -> anyhow::Result<f64> {
69        self.as_slice().min()
70    }
71}
72impl Min for Interpolator {
73    fn min(&self) -> anyhow::Result<f64> {
74        match self {
75            Interpolator::Interp0D(value) => Ok(*value),
76            Interpolator::Interp1D(..) => self.f_x()?.min(),
77            Interpolator::Interp2D(..) => self.f_xy()?.min(),
78            Interpolator::Interp3D(..) => self.f_xyz()?.min(),
79            Interpolator::InterpND(..) => Ok(self
80                .values()?
81                .iter()
82                .fold(f64::INFINITY, |acc, x| acc.min(*x))),
83        }
84    }
85}
86
87pub trait Max {
88    fn max(&self) -> anyhow::Result<f64>;
89}
90impl Max for &[f64] {
91    fn max(&self) -> anyhow::Result<f64> {
92        Ok(self
93            .iter()
94            .fold(f64::NEG_INFINITY, |acc, curr| acc.max(*curr)))
95    }
96}
97impl Max for Vec<f64> {
98    fn max(&self) -> anyhow::Result<f64> {
99        self.as_slice().max()
100    }
101}
102impl Max for &[&f64] {
103    fn max(&self) -> anyhow::Result<f64> {
104        Ok(self
105            .iter()
106            .fold(f64::NEG_INFINITY, |acc, curr| acc.max(**curr)))
107    }
108}
109impl Max for Vec<&f64> {
110    fn max(&self) -> anyhow::Result<f64> {
111        self.as_slice().max()
112    }
113}
114impl Max for &[Vec<f64>] {
115    fn max(&self) -> anyhow::Result<f64> {
116        self.iter()
117            .map(|v| v.max())
118            .try_fold(f64::NEG_INFINITY, |acc, x| Ok(acc.max(x?)))
119    }
120}
121impl Max for Vec<Vec<f64>> {
122    fn max(&self) -> anyhow::Result<f64> {
123        self.as_slice().max()
124    }
125}
126impl Max for &[Vec<Vec<f64>>] {
127    fn max(&self) -> anyhow::Result<f64> {
128        self.iter()
129            .map(|v| v.max())
130            .try_fold(f64::NEG_INFINITY, |acc, x| Ok(acc.max(x?)))
131    }
132}
133impl Max for Vec<Vec<Vec<f64>>> {
134    fn max(&self) -> anyhow::Result<f64> {
135        self.as_slice().max()
136    }
137}
138impl Max for Interpolator {
139    fn max(&self) -> anyhow::Result<f64> {
140        match self {
141            Interpolator::Interp0D(value) => Ok(*value),
142            Interpolator::Interp1D(..) => self.f_x()?.max(),
143            Interpolator::Interp2D(..) => self.f_xy()?.max(),
144            Interpolator::Interp3D(..) => self.f_xyz()?.max(),
145            Interpolator::InterpND(..) => Ok(self
146                .values()?
147                .iter()
148                .fold(f64::NEG_INFINITY, |acc, x| acc.max(*x))),
149        }
150    }
151}
152
153pub trait Init {
154    /// Specialized code to execute upon initialization.  For any struct with fields
155    /// that implement `Init`, this should propagate down the hierarchy.
156    fn init(&mut self) -> Result<(), Error> {
157        Ok(())
158    }
159}
160
161pub trait SerdeAPI: Serialize + for<'a> Deserialize<'a> + Init {
162    const ACCEPTED_BYTE_FORMATS: &'static [&'static str] = &[
163        #[cfg(feature = "yaml")]
164        "yaml",
165        #[cfg(feature = "json")]
166        "json",
167        #[cfg(feature = "toml")]
168        "toml",
169    ];
170    const ACCEPTED_STR_FORMATS: &'static [&'static str] = &[
171        #[cfg(feature = "yaml")]
172        "yaml",
173        #[cfg(feature = "json")]
174        "json",
175        #[cfg(feature = "toml")]
176        "toml",
177    ];
178    #[cfg(feature = "resources")]
179    const RESOURCE_PREFIX: &'static str = "";
180
181    /// Read (deserialize) an object from a resource file packaged with the `fastsim-core` crate
182    ///
183    /// # Arguments:
184    ///
185    /// * `filepath` - Filepath, relative to the top of the `resources` folder (excluding any relevant prefix), from which to read the object
186    #[cfg(feature = "resources")]
187    fn from_resource<P: AsRef<Path>>(filepath: P, skip_init: bool) -> Result<Self, Error> {
188        let filepath = Path::new(Self::RESOURCE_PREFIX).join(filepath);
189        let extension = filepath
190            .extension()
191            .and_then(OsStr::to_str)
192            .ok_or_else(|| {
193                Error::SerdeError(format!("File extension could not be parsed: {filepath:?}"))
194            })?;
195        let file = crate::resources::RESOURCES_DIR
196            .get_file(&filepath)
197            .ok_or_else(|| {
198                Error::SerdeError(format!("File not found in resources: {filepath:?}"))
199            })?;
200        Self::from_reader(&mut file.contents(), extension, skip_init)
201    }
202
203    /// Instantiates an object from a url.  Accepts yaml and json file types  
204    /// # Arguments  
205    /// - url: URL (either as a string or url type) to object  
206    ///
207    /// Note: The URL needs to be a URL pointing directly to a file, for example
208    /// a raw github URL.
209    #[cfg(feature = "web")]
210    fn from_url<S: AsRef<str>>(url: S, skip_init: bool) -> Result<Self, Error> {
211        let url = url::Url::parse(url.as_ref())?;
212        let format = url
213            .path_segments()
214            .and_then(|segments| segments.last())
215            .and_then(|filename| Path::new(filename).extension())
216            .and_then(OsStr::to_str)
217            .with_context(|| "Could not parse file format from URL: {url:?}")?;
218        let mut response = ureq::get(url.as_ref()).call()?.into_reader();
219        Self::from_reader(&mut response, format, skip_init)
220    }
221
222    /// Write (serialize) an object to a file.
223    /// Supported file extensions are listed in [`ACCEPTED_BYTE_FORMATS`](`SerdeAPI::ACCEPTED_BYTE_FORMATS`).
224    /// Creates a new file if it does not already exist, otherwise truncates the existing file.
225    ///
226    /// # Arguments
227    ///
228    /// * `filepath` - The filepath at which to write the object
229    ///
230    fn to_file<P: AsRef<Path>>(&self, filepath: P) -> Result<(), Error> {
231        let filepath = filepath.as_ref();
232        let extension = filepath
233            .extension()
234            .and_then(OsStr::to_str)
235            .ok_or_else(|| {
236                Error::SerdeError(format!("File extension could not be parsed: {filepath:?}"))
237            })?;
238        self.to_writer(
239            File::create(filepath).map_err(|err| Error::SerdeError(format!("{err}")))?,
240            extension,
241        )
242    }
243
244    /// Read (deserialize) an object from a file.
245    /// Supported file extensions are listed in [`ACCEPTED_BYTE_FORMATS`](`SerdeAPI::ACCEPTED_BYTE_FORMATS`).
246    ///
247    /// # Arguments:
248    ///
249    /// * `filepath`: The filepath from which to read the object
250    ///
251    fn from_file<P: AsRef<Path>>(filepath: P, skip_init: bool) -> Result<Self, Error> {
252        let filepath = filepath.as_ref();
253        let extension = filepath
254            .extension()
255            .and_then(OsStr::to_str)
256            .ok_or_else(|| {
257                Error::SerdeError(format!("File extension could not be parsed: {filepath:?}"))
258            })?;
259        let mut file = File::open(filepath)
260            .with_context(|| {
261                if !filepath.exists() {
262                    format!("File not found: {filepath:?}")
263                } else {
264                    format!("Could not open file: {filepath:?}")
265                }
266            })
267            .map_err(|err| Error::SerdeError(format!("{err}")))?;
268        Self::from_reader(&mut file, extension, skip_init)
269    }
270
271    /// Write (serialize) an object into anything that implements [`std::io::Write`]
272    ///
273    /// # Arguments:
274    ///
275    /// * `wtr` - The writer into which to write object data
276    /// * `format` - The target format, any of those listed in [`ACCEPTED_BYTE_FORMATS`](`SerdeAPI::ACCEPTED_BYTE_FORMATS`)
277    ///
278    fn to_writer<W: std::io::Write>(&self, mut wtr: W, format: &str) -> Result<(), Error> {
279        match format.trim_start_matches('.').to_lowercase().as_str() {
280            #[cfg(feature = "yaml")]
281            "yaml" | "yml" => serde_yaml::to_writer(wtr, self)
282                .map_err(|err| Error::SerdeError(format!("{err}")))?,
283            #[cfg(feature = "json")]
284            "json" => serde_json::to_writer(wtr, self)
285                .map_err(|err| Error::SerdeError(format!("{err}")))?,
286            #[cfg(feature = "toml")]
287            "toml" => {
288                let toml_string = self
289                    .to_toml()
290                    .map_err(|err| Error::SerdeError(format!("{err}")))?;
291                wtr.write_all(toml_string.as_bytes())
292                    .map_err(|err| Error::SerdeError(format!("{err}")))?;
293            }
294            _ => Err(Error::SerdeError(format!(
295                "Unsupported format {format:?}, must be one of {:?}",
296                Self::ACCEPTED_BYTE_FORMATS
297            )))?,
298        }
299        Ok(())
300    }
301
302    /// Deserialize an object from anything that implements [`std::io::Read`]
303    ///
304    /// # Arguments:
305    ///
306    /// * `rdr` - The reader from which to read object data
307    /// * `format` - The source format, any of those listed in [`ACCEPTED_BYTE_FORMATS`](`SerdeAPI::ACCEPTED_BYTE_FORMATS`)
308    ///
309    fn from_reader<R: std::io::Read>(
310        rdr: &mut R,
311        format: &str,
312        skip_init: bool,
313    ) -> Result<Self, Error> {
314        let mut deserialized: Self =
315            match format.trim_start_matches('.').to_lowercase().as_str() {
316                #[cfg(feature = "yaml")]
317                "yaml" | "yml" => serde_yaml::from_reader(rdr)
318                    .map_err(|err| Error::SerdeError(format!("{err}")))?,
319                #[cfg(feature = "json")]
320                "json" => serde_json::from_reader(rdr)
321                    .map_err(|err| Error::SerdeError(format!("{err}")))?,
322                #[cfg(feature = "msgpack")]
323                "msgpack" => rmp_serde::decode::from_read(rdr)
324                    .map_err(|err| Error::SerdeError(format!("{err}")))?,
325                #[cfg(feature = "toml")]
326                "toml" => {
327                    let mut buf = String::new();
328                    rdr.read_to_string(&mut buf)
329                        .map_err(|err| Error::SerdeError(format!("{err}")))?;
330                    Self::from_toml(buf, skip_init)
331                        .map_err(|err| Error::SerdeError(format!("{err}")))?
332                }
333                _ => Err(Error::SerdeError(format!(
334                    "Unsupported format {format:?}, must be one of {:?}",
335                    Self::ACCEPTED_BYTE_FORMATS,
336                )))?,
337            };
338        if !skip_init {
339            deserialized.init()?;
340        }
341        Ok(deserialized)
342    }
343
344    /// Write (serialize) an object into a string
345    ///
346    /// # Arguments:
347    ///
348    /// * `format` - The target format, any of those listed in [`ACCEPTED_STR_FORMATS`](`SerdeAPI::ACCEPTED_STR_FORMATS`)
349    ///
350    fn to_str(&self, format: &str) -> anyhow::Result<String> {
351        match format.trim_start_matches('.').to_lowercase().as_str() {
352            #[cfg(feature = "yaml")]
353            "yaml" | "yml" => self.to_yaml(),
354            #[cfg(feature = "json")]
355            "json" => self.to_json(),
356            #[cfg(feature = "toml")]
357            "toml" => self.to_toml(),
358            _ => bail!(
359                "Unsupported format {format:?}, must be one of {:?}",
360                Self::ACCEPTED_STR_FORMATS
361            ),
362        }
363    }
364
365    /// Read (deserialize) an object from a string
366    ///
367    /// # Arguments:
368    ///
369    /// * `contents` - The string containing the object data
370    /// * `format` - The source format, any of those listed in [`ACCEPTED_STR_FORMATS`](`SerdeAPI::ACCEPTED_STR_FORMATS`)
371    ///
372    fn from_str<S: AsRef<str>>(contents: S, format: &str, skip_init: bool) -> anyhow::Result<Self> {
373        Ok(
374            match format.trim_start_matches('.').to_lowercase().as_str() {
375                #[cfg(feature = "yaml")]
376                "yaml" | "yml" => Self::from_yaml(contents, skip_init)?,
377                #[cfg(feature = "json")]
378                "json" => Self::from_json(contents, skip_init)?,
379                #[cfg(feature = "toml")]
380                "toml" => Self::from_toml(contents, skip_init)?,
381                _ => bail!(
382                    "Unsupported format {format:?}, must be one of {:?}",
383                    Self::ACCEPTED_STR_FORMATS
384                ),
385            },
386        )
387    }
388
389    /// Write (serialize) an object to a JSON string
390    #[cfg(feature = "json")]
391    fn to_json(&self) -> anyhow::Result<String> {
392        Ok(serde_json::to_string(&self)?)
393    }
394
395    /// Read (deserialize) an object from a JSON string
396    ///
397    /// # Arguments
398    ///
399    /// * `json_str` - JSON-formatted string to deserialize from
400    ///
401    #[cfg(feature = "json")]
402    fn from_json<S: AsRef<str>>(json_str: S, skip_init: bool) -> anyhow::Result<Self> {
403        let mut json_de: Self = serde_json::from_str(json_str.as_ref())?;
404        if !skip_init {
405            json_de.init()?;
406        }
407        Ok(json_de)
408    }
409
410    /// Write (serialize) an object to a message pack
411    #[cfg(feature = "msgpack")]
412    fn to_msg_pack(&self) -> anyhow::Result<Vec<u8>> {
413        Ok(rmp_serde::encode::to_vec_named(&self)?)
414    }
415
416    /// Read (deserialize) an object from a message pack
417    ///
418    /// # Arguments
419    ///
420    /// * `msg_pack` - message pack object
421    ///
422    #[cfg(feature = "msgpack")]
423    fn from_msg_pack(msg_pack: &[u8], skip_init: bool) -> anyhow::Result<Self> {
424        let mut msg_pack_de: Self = rmp_serde::decode::from_slice(msg_pack)?;
425        if !skip_init {
426            msg_pack_de.init()?;
427        }
428        Ok(msg_pack_de)
429    }
430
431    /// Write (serialize) an object to a TOML string
432    #[cfg(feature = "toml")]
433    fn to_toml(&self) -> anyhow::Result<String> {
434        Ok(toml::to_string(&self)?)
435    }
436
437    /// Read (deserialize) an object from a TOML string
438    ///
439    /// # Arguments
440    ///
441    /// * `toml_str` - TOML-formatted string to deserialize from
442    ///
443    #[cfg(feature = "toml")]
444    fn from_toml<S: AsRef<str>>(toml_str: S, skip_init: bool) -> anyhow::Result<Self> {
445        let mut toml_de: Self = toml::from_str(toml_str.as_ref())?;
446        if !skip_init {
447            toml_de.init()?;
448        }
449        Ok(toml_de)
450    }
451
452    /// Write (serialize) an object to a YAML string
453    #[cfg(feature = "yaml")]
454    fn to_yaml(&self) -> anyhow::Result<String> {
455        Ok(serde_yaml::to_string(&self)?)
456    }
457
458    /// Read (deserialize) an object from a YAML string
459    ///
460    /// # Arguments
461    ///
462    /// * `yaml_str` - YAML-formatted string to deserialize from
463    ///
464    #[cfg(feature = "yaml")]
465    fn from_yaml<S: AsRef<str>>(yaml_str: S, skip_init: bool) -> anyhow::Result<Self> {
466        let mut yaml_de: Self = serde_yaml::from_str(yaml_str.as_ref())?;
467        if !skip_init {
468            yaml_de.init()?;
469        }
470        Ok(yaml_de)
471    }
472}
473
474impl<T: SerdeAPI> SerdeAPI for Vec<T> {}
475impl<T: Init> Init for Vec<T> {
476    fn init(&mut self) -> Result<(), Error> {
477        for val in self {
478            val.init()?
479        }
480        Ok(())
481    }
482}
483
484pub trait Diff<T> {
485    /// Returns vec of length `self.len() - 1` where each element in the returned vec at index i is
486    /// `self[i + 1] - self[i]`
487    fn diff(&self) -> Vec<T>;
488}
489
490impl<T: Clone + Sub<T, Output = T> + Default> Diff<T> for Vec<T> {
491    fn diff(&self) -> Vec<T> {
492        let mut v_diff: Vec<T> = vec![Default::default()];
493        v_diff.extend::<Vec<T>>(
494            self.windows(2)
495                .map(|vs| {
496                    let x = &vs[0];
497                    let y = &vs[1];
498                    y.clone() - x.clone()
499                })
500                .collect(),
501        );
502        v_diff
503    }
504}
505
506/// Provides method that saves `self.state` to `self.history` and propagates to any fields with
507/// `state`
508pub trait SaveState {
509    /// Saves `self.state` to `self.history` and propagates to any fields with `state`
510    /// # Arguments
511    /// - `loc`: closure that returns file and line number where called
512    fn save_state<F: Fn() -> String>(&mut self, loc: F) -> anyhow::Result<()>;
513}
514
515/// Provides methods for getting and setting the save interval
516pub trait HistoryMethods: SaveState {
517    /// Recursively sets save interval
518    /// # Arguments
519    /// - `save_interval`: time step interval at which to save `self.state` to `self.history`
520    fn set_save_interval(&mut self, save_interval: Option<usize>) -> anyhow::Result<()>;
521    /// Returns save interval for `self` but does not guarantee recursive consistency in nested
522    /// objects
523    fn save_interval(&self) -> anyhow::Result<Option<usize>>;
524    /// Remove all history
525    fn clear(&mut self);
526}
527
528/// Trait that provides method for incrementing `i` field of this and all contained structs,
529/// recursively
530pub trait Step {
531    /// Increments `i` field of this and all contained structs, recursively
532    /// # Arguments
533    /// - `loc`: closure that returns file and line number where called
534    fn step<F: Fn() -> String>(&mut self, loc: F) -> anyhow::Result<()>;
535}
536
537/// Provides method for checking if struct is default
538pub trait EqDefault: std::default::Default + PartialEq {
539    /// If `self` is default, returns true
540    fn eq_default(&self) -> bool {
541        *self == Self::default()
542    }
543}
544
545impl<T: Default + PartialEq> EqDefault for T {}
546
547/// Trait for setting cumulative values based on rate values
548pub trait SetCumulative {
549    /// Sets cumulative values based on rate values
550    fn set_cumulative(&mut self, dt: si::Time) -> anyhow::Result<()>;
551}
552
553#[cfg(test)]
554mod tests {
555    use super::*;
556
557    #[test]
558    fn test_linspace() {
559        assert_eq!(Vec::linspace(0., 2., 3), vec![0., 1., 2.]);
560    }
561
562    #[test]
563    fn test_max_for_vec_f64() {
564        assert_eq!(Vec::linspace(-10., 12., 5).max().unwrap(), 12.);
565    }
566    #[test]
567    fn test_min_for_vec_f64() {
568        assert_eq!(Vec::linspace(-10., 12., 5).min().unwrap(), -10.);
569    }
570
571    #[test]
572    fn test_diff() {
573        let diff = Vec::linspace(0., 2., 3).diff();
574        let ref_diff = vec![0., 1., 1.];
575        assert_eq!(diff, ref_diff);
576    }
577}