1#[macro_export]
2macro_rules! eff_test_body {
3 ($component:ident, $eff_max:expr, $eff_min:expr, $eff_range:expr) => {
4 assert!(almost_eq($component.get_eff_max(), $eff_max, None));
5 assert!(almost_eq($component.get_eff_min(), $eff_min, None));
6 assert!(almost_eq($component.get_eff_range(), $eff_range, None));
7
8 $component.set_eff_max(0.9).unwrap();
9 assert!(almost_eq($component.get_eff_max(), 0.9, None));
10 assert!(almost_eq(
11 $component.get_eff_min(),
12 $eff_min * 0.9 / $eff_max,
13 None
14 ));
15 assert!(almost_eq(
16 $component.get_eff_range(),
17 $eff_range * 0.9 / $eff_max,
18 None
19 ));
20
21 $component.set_eff_range(0.2).unwrap();
22 assert!(almost_eq($component.get_eff_max(), 0.9, None));
23 assert!(almost_eq($component.get_eff_min(), 0.7, None));
24 assert!(almost_eq($component.get_eff_range(), 0.2, None));
25
26 $component.set_eff_range(0.98).unwrap();
27 assert!(almost_eq($component.get_eff_max(), 0.98, None));
28 assert!(almost_eq($component.get_eff_min(), 0.0, None));
29 assert!(almost_eq($component.get_eff_range(), 0.98, None));
30 };
31}
32
33#[macro_export]
34macro_rules! make_uom_cmp_fn {
35 ($name:ident) => {
36 paste! {
37 pub fn [<$name _uom>]<D, U>(
42 val1: &uom::si::Quantity<D, U, f64>,
43 val2: &uom::si::Quantity<D, U, f64>,
44 epsilon: Option<f64>,
45 ) -> bool
46 where
47 D: uom::si::Dimension + ?Sized,
48 U: uom::si::Units<f64> + ?Sized,
49 {
50 $name(val1.value, val2.value, epsilon)
51 }
52 }
53 };
54}
55
56#[macro_export]
57macro_rules! impl_efficiency_enum {
58 ($enum_ty:ty { $($variant:ident),+ $(,)? }) => {
59 impl From<f64> for $enum_ty {
60 fn from(value: f64) -> Self {
61 Self::Constant(ninterp::prelude::Interp0D(value))
62 }
63 }
64
65 impl Default for $enum_ty {
66 fn default() -> Self {
68 Self::from(1.0)
69 }
70 }
71
72 impl Interpolator<f64> for $enum_ty {
73 fn ndim(&self) -> usize {
74 ::paste::paste! {
75 match self {
76 $(Self::$variant([<$variant:snake _interp>]) => [<$variant:snake _interp>].ndim(),)+
77 }
78 }
79 }
80
81 fn validate(&self) -> Result<(), ninterp::error::ValidateError> {
83 ::paste::paste! {
84 match self {
85 $(Self::$variant([<$variant:snake _interp>]) => [<$variant:snake _interp>].validate(),)+
86 }
87 }?;
88
89 <Self as $crate::utils::interp::InterpolatorScanValues>::try_for_each_value::<ninterp::error::ValidateError, _>(self, |value| {
90 if value.is_nan() {
91 return Ok(());
92 }
93 if !(0.0..=1.0).contains(&value) {
94 return Err(ninterp::error::ValidateError::Other(
95 format!(
96 "Efficiency map contains out-of-range value ({value}); values must be in range [0, 1]"
97 )
98 .into(),
99 ));
100 }
101 Ok(())
102 })?;
103
104 Ok(())
105 }
106
107 fn interpolate(&self, point: &[f64]) -> Result<f64, ninterp::error::InterpolateError> {
109 let efficiency = ::paste::paste! {
110 match self {
111 $(Self::$variant([<$variant:snake _interp>]) => ninterp::prelude::Interpolator::interpolate([<$variant:snake _interp>], point),)+
112 }
113 }?;
114
115 if efficiency.is_nan() {
116 return Err(ninterp::error::InterpolateError::Other(
117 format!(
118 "Efficiency interpolation returned NaN at point {:?}; likely missing local map data",
119 point
120 )
121 .into(),
122 ));
123 }
124 if !efficiency.is_finite() {
125 return Err(ninterp::error::InterpolateError::Other(
126 format!(
127 "Efficiency interpolation returned non-finite value ({efficiency}) at point {:?}",
128 point
129 )
130 .into(),
131 ));
132 }
133 if efficiency <= 0.0 || efficiency > 1.0 {
134 return Err(ninterp::error::InterpolateError::Other(
135 format!(
136 "Efficiency interpolation returned out-of-range value ({efficiency}) at point {:?}; expected (0, 1]",
137 point
138 )
139 .into(),
140 ));
141 }
142
143 Ok(efficiency)
144 }
145
146 fn set_extrapolate(
147 &mut self,
148 extrapolate: Extrapolate<f64>,
149 ) -> Result<(), ninterp::error::ValidateError> {
150 ::paste::paste! {
151 match self {
152 $(Self::$variant([<$variant:snake _interp>]) => [<$variant:snake _interp>].set_extrapolate(extrapolate),)+
153 }
154 }
155 }
156 }
157
158 impl $crate::utils::interp::InterpolatorScanValues for $enum_ty {
159 fn try_for_each_value<E, F: FnMut(f64) -> Result<(), E>>(&self, f: F) -> Result<(), E> {
160 ::paste::paste! {
161 match self {
162 $(Self::$variant([<$variant:snake _interp>]) => [<$variant:snake _interp>].try_for_each_value(f),)+
163 }
164 }
165 }
166 }
167
168 impl Min<f64> for $enum_ty {
169 fn min(&self) -> anyhow::Result<&f64> {
170 ::paste::paste! {
171 match self {
172 $(Self::$variant([<$variant:snake _interp>]) => [<$variant:snake _interp>].min(),)+
173 }
174 }
175 }
176 }
177
178 impl Max<f64> for $enum_ty {
179 fn max(&self) -> anyhow::Result<&f64> {
180 ::paste::paste! {
181 match self {
182 $(Self::$variant([<$variant:snake _interp>]) => [<$variant:snake _interp>].max(),)+
183 }
184 }
185 }
186 }
187
188 impl Range<f64> for $enum_ty {
189 fn range(&self) -> anyhow::Result<f64> {
190 ::paste::paste! {
191 match self {
192 $(Self::$variant([<$variant:snake _interp>]) => [<$variant:snake _interp>].range(),)+
193 }
194 }
195 }
196 }
197
198 impl $crate::utils::interp::InterpolatorMutMethods for $enum_ty {
199 fn set_min(&mut self, min: f64, scaling: Option<$crate::utils::interp::ScalingMethods>) -> anyhow::Result<()> {
200 ::paste::paste! {
201 match self {
202 $(Self::$variant([<$variant:snake _interp>]) => [<$variant:snake _interp>].set_min(min, scaling),)+
203 }
204 }
205 }
206
207 fn set_max(&mut self, max: f64, scaling: Option<$crate::utils::interp::ScalingMethods>) -> anyhow::Result<()> {
208 ::paste::paste! {
209 match self {
210 $(Self::$variant([<$variant:snake _interp>]) => [<$variant:snake _interp>].set_max(max, scaling),)+
211 }
212 }
213 }
214
215 fn set_range(&mut self, range: f64) -> anyhow::Result<()> {
216 ::paste::paste! {
217 match self {
218 $(Self::$variant([<$variant:snake _interp>]) => [<$variant:snake _interp>].set_range(range),)+
219 }
220 }
221 }
222 }
223 };
224}
225
226#[macro_export]
227macro_rules! format_dbg {
229 ($dbg_expr:expr) => {
230 format!(
231 "[{}:{}] {}: {:?}",
232 file!(),
233 line!(),
234 stringify!($dbg_expr),
235 $dbg_expr
236 )
237 };
238 () => {
239 format!("[{}:{}]", file!(), line!())
240 };
241}
242
243#[macro_export]
244macro_rules! init_opt_default {
246 ($obj:ident, $fieldname:ident, $def_val:expr) => {
247 $obj.$fieldname = $obj.$fieldname.or(Some($def_val));
248 };
249}
250
251#[macro_export]
252macro_rules! timer {
254 ($code_block:expr) => {
255 #[cfg(feature = "timer")]
256 let now_and_then = Instant::now();
257 $code_block;
258 #[cfg(feature = "timer")]
259 println!(
260 "{}\nElapsed time: {} μs",
261 format_dbg!(),
262 now_and_then.elapsed().as_micros()
263 );
264 };
265}
266
267#[macro_export]
298macro_rules! impl_si_serialize_as {
299 ($prefix:ident, $qty_type:ty, $unit:path) => {
300 ::paste::paste! {
301 pub fn $prefix<S: ::serde::Serializer>(
303 val: &$qty_type,
304 s: S,
305 ) -> ::std::result::Result<S::Ok, S::Error> {
306 s.serialize_f64(val.get::<$unit>())
307 }
308
309 pub fn [<tracked_ $prefix>]<S: ::serde::Serializer>(
311 val: &$crate::utils::tracked_state::TrackedState<$qty_type>,
312 s: S,
313 ) -> ::std::result::Result<S::Ok, S::Error> {
314 s.serialize_f64(val.inner().get::<$unit>())
315 }
316
317 pub fn [<opt_ $prefix>]<S: ::serde::Serializer>(
319 val: &::std::option::Option<$qty_type>,
320 s: S,
321 ) -> ::std::result::Result<S::Ok, S::Error> {
322 match val {
323 Some(v) => s.serialize_some(&v.get::<$unit>()),
324 None => s.serialize_none(),
325 }
326 }
327
328 pub fn [<vec_ $prefix>]<S: ::serde::Serializer>(
330 val: &::std::vec::Vec<$qty_type>,
331 s: S,
332 ) -> ::std::result::Result<S::Ok, S::Error> {
333 use ::serde::ser::SerializeSeq;
334 let mut seq = s.serialize_seq(Some(val.len()))?;
335 for v in val {
336 seq.serialize_element(&v.get::<$unit>())?;
337 }
338 seq.end()
339 }
340
341 pub fn [<vec_tracked_ $prefix>]<S: ::serde::Serializer>(
343 val: &::std::vec::Vec<$crate::utils::tracked_state::TrackedState<$qty_type>>,
344 s: S,
345 ) -> ::std::result::Result<S::Ok, S::Error> {
346 use ::serde::ser::SerializeSeq;
347 let mut seq = s.serialize_seq(Some(val.len()))?;
348 for v in val {
349 seq.serialize_element(&v.inner().get::<$unit>())?;
350 }
351 seq.end()
352 }
353
354 pub fn [<tracked_opt_ $prefix>]<S: ::serde::Serializer>(
356 val: &$crate::utils::tracked_state::TrackedState<::std::option::Option<$qty_type>>,
357 s: S,
358 ) -> ::std::result::Result<S::Ok, S::Error> {
359 match val.inner() {
360 Some(v) => s.serialize_some(&v.get::<$unit>()),
361 None => s.serialize_none(),
362 }
363 }
364
365 pub fn [<opt_tracked_ $prefix>]<S: ::serde::Serializer>(
367 val: &::std::option::Option<$crate::utils::tracked_state::TrackedState<$qty_type>>,
368 s: S,
369 ) -> ::std::result::Result<S::Ok, S::Error> {
370 match val {
371 Some(v) => s.serialize_some(&v.inner().get::<$unit>()),
372 None => s.serialize_none(),
373 }
374 }
375
376 pub fn [<vec_tracked_opt_ $prefix>]<S: ::serde::Serializer>(
378 val: &::std::vec::Vec<$crate::utils::tracked_state::TrackedState<::std::option::Option<$qty_type>>>,
379 s: S,
380 ) -> ::std::result::Result<S::Ok, S::Error> {
381 use ::serde::ser::SerializeSeq;
382 let mut seq = s.serialize_seq(Some(val.len()))?;
383 for v in val {
384 match v.inner() {
385 Some(inner) => seq.serialize_element(&Some(inner.get::<$unit>()))?,
386 None => seq.serialize_element(&::std::option::Option::<f64>::None)?,
387 }
388 }
389 seq.end()
390 }
391 }
392 };
393}