Expand description
§var_quantity
This crate is an extension of dyn_quantity and provides an interface for defining variable quantities whose value is a (pure) function of other quantities.
Feedback welcome!
Found a bug, missing docs, or have a feature request?
Please open an issue on GitHub.
As an example, let’s consider the eddy current losses in a conductive material
which are caused by sinusoidally changing magnetic fields. A simple model
could only take the magnetic flux density amplitude into account and a more
sophisticated model would also consider the field frequency. Using the
VarQuantity wrapper, both models can be used with the same interface:
use var_quantity::{IsQuantityFunction, VarQuantity, QuantityFunction,
DynQuantity, PredefUnit, Unit};
use var_quantity::uom::si::{f64::{Power, MagneticFluxDensity, Frequency},
power::watt, magnetic_flux_density::tesla, frequency::hertz};
// The serde annotations are just here because the doctests of this crate use
// the serde feature - they are not needed if the serde feature is disabled.
// Model 1: p = k * B^2
#[derive(Clone, serde::Deserialize, serde::Serialize, PartialEq)]
struct Model1(DynQuantity<f64>);
#[typetag::serde]
impl IsQuantityFunction for Model1 {
fn call(&self, conditions: &[DynQuantity<f64>]) -> DynQuantity<f64> {
let mut b = DynQuantity::new(0.0, PredefUnit::MagneticFluxDensity);
for factor in conditions.iter() {
if b.unit == factor.unit {
b = factor.clone();
}
}
return self.0 * b.powi(2);
}
fn dyn_eq(&self, other: &dyn IsQuantityFunction) -> bool {
(other as &dyn std::any::Any).downcast_ref::<Self>() == Some(self)
}
}
// Model 2: p = k * f^2 * B^2
#[derive(Clone, serde::Deserialize, serde::Serialize, PartialEq)]
struct Model2(DynQuantity<f64>);
#[typetag::serde]
impl IsQuantityFunction for Model2 {
fn call(&self, conditions: &[DynQuantity<f64>]) -> DynQuantity<f64> {
let mut b = DynQuantity::new(0.0, PredefUnit::MagneticFluxDensity);
let mut f = DynQuantity::new(0.0, PredefUnit::Frequency);
for factor in conditions.iter() {
if b.unit == factor.unit {
b = factor.clone();
}
if f.unit == factor.unit {
f = factor.clone();
}
}
return self.0 * f.powi(2) * b.powi(2);
}
fn dyn_eq(&self, other: &dyn IsQuantityFunction) -> bool {
(other as &dyn std::any::Any).downcast_ref::<Self>() == Some(self)
}
}
let k = DynQuantity::new(
1000.0,
Unit::from(PredefUnit::Power) / Unit::from(PredefUnit::MagneticFluxDensity).powi(2),
);
let model1: VarQuantity<Power> = VarQuantity::Function(
QuantityFunction::new(Box::new(Model1(k))).expect("output unit is watt"),
);
let k = DynQuantity::new(
2.0,
Unit::from(PredefUnit::Power)
/ Unit::from(PredefUnit::MagneticFluxDensity).powi(2)
/ Unit::from(PredefUnit::Frequency).powi(2),
);
let model2: VarQuantity<Power> = VarQuantity::Function(
QuantityFunction::new(Box::new(Model2(k))).expect("output unit is watt"),
);
// This function takes a variable quantity, the magnetic flux density and
// the frequency and calculates the losses
fn losses(model: &VarQuantity<Power>, b: MagneticFluxDensity, f: Frequency) -> Power {
return model.get(&[b.into(), f.into()]);
}
let b = MagneticFluxDensity::new::<tesla>(1.2);
let f = Frequency::new::<hertz>(20.0);
assert_eq!(losses(&model1, b, f).get::<watt>(), 1440.0);
assert_eq!(losses(&model2, b, f).get::<watt>(), 1152.0);The workflow to use the interface of this crate is as follows:
- Define the relation between input and output by implementing
IsQuantityFunctionfor the type representing a variable quantity (Model1andModel2in the previous example). The implementor is responsible for selecting the right quantities for his model from the giveconditions(for unary functions, the crate providesfilter_unary_functionto simplify this) and also for defining sensible defaults if the needed quantity is not given (in the example above, the default flux density and frequency was defined to zero). As explained in the serialization / deserialization section, the types must not be generic. - Create an type instance and box it as a trait object. The trait object
approach is necessary for two reasons:
- Reduce generic bloat (for example, when a material type is defined using
multiple
VarQuantityfor different properties, this could lead to dozens of generic parameters). - To allow for serialization and deserialization using the typetag crate.
- Reduce generic bloat (for example, when a material type is defined using
multiple
- Wrap the trait object in a
QuantityFunction. SinceIsQuantityFunctionworks with dynamic quantities, it needs to be tested whether the output fromIsQuantityFunction::callcan be converted to the statically typed quantityTusingTryFrom<DynQuantity<f64>>(in the example, the quantity types provided by the uom crate were used). This check is done in the constructorQuantityFunction::newand again inQuantityFunction::call, see the docstring ofQuantityFunction. - Wrap the
QuantityFunctioninVarQuantity::Function. The purpose of this enum is to offer an optimization for the important case of a constant quantity via its second variantVarQuantity::Constant. ItsVarQuantity::getmethod either returns the constant quantity directly or forwards toQuantityFunction::call.
§Predefined variable quantity models
Some variable quantity models are very common and therefore provided with this
crate. For example, model 1 from the introduction could also be realized
using the Polynomial struct from the unary module:
use var_quantity::{DynQuantity, PredefUnit, Unit};
use var_quantity::{unary::Polynomial, VarQuantity, QuantityFunction};
use var_quantity::uom::si::{f64::{Power, MagneticFluxDensity, Frequency},
power::watt, magnetic_flux_density::tesla, frequency::hertz};
// The input vector [a, b, c] is evaluated as ax² + bx + c. Here, b and c are
// zero, but still need to match unit-wise:
// [a] = W/T², [b] = W/T, [c] = W
// The output unit is [c] and the input unit is calculated as [c/b].
// [a] (and additional terms) can then be checked.
let a = DynQuantity::new(1000.0, Unit::from(PredefUnit::Power) / Unit::from(PredefUnit::MagneticFluxDensity).powi(2));
let b = DynQuantity::new(0.0, Unit::from(PredefUnit::Power) / Unit::from(PredefUnit::MagneticFluxDensity));
let c = DynQuantity::new(0.0, PredefUnit::Power);
let polynomial = Polynomial::new(vec![a, b, c]).expect("terms are checked during construction");
let model1: VarQuantity<Power> = VarQuantity::Function(
QuantityFunction::new(Box::new(polynomial)).expect("output unit is watt"),
);
// This function takes a variable quantity, the magnetic flux density and
// the frequency and calculates the losses
fn losses(model: &VarQuantity<Power>, b: MagneticFluxDensity, f: Frequency) -> Power {
return model.get(&[b.into(), f.into()]);
}
let b = MagneticFluxDensity::new::<tesla>(1.2);
let f = Frequency::new::<hertz>(20.0);
assert_eq!(losses(&model1, b, f).get::<watt>(), 1440.0);For a full list of available models, see the following modules:
unary: Models representing unary functions (single input).
§Serialization and deserialization
The serde integration is gated behind the serde feature flag.
All structs / enums in this crate implement serialization and deserialization.
See the docstrings of the individual types for details. The trait objects stored
within QuantityFunction are handled via typetag, which is why the the
implementors of IsQuantityFunction cannot be generic.
§Documentation
The full API documentation is available at https://docs.rs/var_quantity/0.3.1/var_quantity/.
Re-exports§
pub use typetag;pub use dyn_quantity;
Modules§
- error
- This module contains the various errors which can occur when dealing with
DynQuantityandUnit. - quantity
- This module contains the
DynQuantitystruct and code for: - unary
- This module contains unary functions which implement
IsQuantityFunction. - unit
- This module contains the
Unitstruct and supporting code. See the documentation string ofUnitfor more information. - uom
- Units of measurement is a crate that does automatic type-safe zero-cost dimensional analysis. You can create your own systems or use the pre-built International System of Units (SI) which is based on the International System of Quantities (ISQ) and includes numerous quantities (length, mass, time, …) with conversion factors for even more numerous measurement units (meter, kilometer, foot, mile, …). No more crashing your climate orbiter!
Structs§
- Clamped
Quantity - A wrapper around a type implementing
IsQuantityFunctiontrait object which clamps the output ofIsQuantityFunction::callusing the provided upper and lower limits. - DynQuantity
- This type represents a physical quantity via its numerical
valueand a unit of measurement (fieldexponents). The unit of measurement is not defined via the type system, but rather via the values of theUnit. This means that the unit of measurement is not fixed at compile time, but can change dynamically at runtime. - NotConvertible
From Complex F64 - Error describing a failed attempt to convert a
Complex<f64>into the typeVofDynQuantity<V>. - Parse
Error - Error representing a failed attempt to parse a string into a
DynQuantity. - Quantity
Function - A thin wrapper around a
Box<dyn IsQuantityFunction>trait object which provides some type checks for usage inVarQuantity. - Root
Error - Error representing a failed attempt to calculate the
nth root of anUnit. - Unit
- Struct representing a unit of measurement in the SI system via the exponents of
the base units. The unit is purely defined by the values of its fields, meaning
that it can change at runtime. The struct implements basic arithmetic functions
such as multiplication and division (via the
Mul,MulAssign,Div,DivAssigntraits), exponentiation (Unit::powi) and a fallible version of root calculation (Unit::try_nthroot). - Units
NotEqual - Error representing unequality of units.
Enums§
- Conversion
Error - Error describing a failed attempt to convert between different types representing quantities.
- Parse
Error Reason - The varying reasons parsing a string to a
DynQuantitycan fail. This struct is part ofParseError, which contains the information where the parsing failed. - Predef
Unit - An enum representing predefined
Units. - VarQuantity
- A quantity whose value can either be constant or a function of one or more other quantities.
Constants§
- SERIALIZE_
WITH_ UNITS - A thread-local, static variable which enables / disables serialization of
quantities with or without units. It is used within the functions
serialize_quantity,serialize_opt_quantity,serialize_angleandserialize_opt_angleas a thread-local context to decide whether a quantity should be serialized with or without its units. By default, its value isfalse, meaning that quantities are serialized without their units. Theserialize_with_unitsfunction sets it temporarily totrue, then performs the actual serialization, and afterwards resets it tofalseagain (return to default behaviour).
Traits§
- IsQuantity
- This is a marker trait which defines trait bounds for all types
Twhich can be used as “quantities” inVarQuantity<T>. It does not provide any methods and is auto-implemented for allTfulfilling the bounds, hence it is not necessary to ever import this trait. It is only public to make compiler error messages more helpful. - IsQuantity
Function - Trait used to construct variable quantities whose value is a (pure) function of other quantities.
- Unit
From Type - A trait to derive
Unitfrom a type. This trait bridges the gap between (external) types representing physical quantities (such as e.g. theQuantitytype from the uom crate) andUnit.
Functions§
- deserialize_
angle - Deserializes an angle from a valid
DynQuantityrepresentation (see docstring ofDynQuantity). The output value is always in radians. - deserialize_
opt_ angle - Like
deserialize_angle, but deserializes into anOption<f64>instead of af64. - deserialize_
opt_ quantity - Like
deserialize_quantity, but deserializes into anOption<T>instead of aTimplementingTryFrom<DynQuantity>. - deserialize_
opt_ vec_ of_ quantities - Like
deserialize_vec_of_quantities, but deserializes into anOption<Vec<T>>instead of aVec<T>. - deserialize_
quantity - Deserializes a type
TimplementingTryFrom<DynQuantity>from a validDynQuantityrepresentation (see docstring ofDynQuantity). - deserialize_
vec_ of_ quantities - Deserializes a vector of
Twhich implementsTryFrom<DynQuantity>from: - filter_
unary_ function - A helper function which filters the
conditionsfor a quantity with the typematch_for. If a matching quantity is found, it is used as argument forFand the result is returned. Otherwise, the result ofG()is returned. - serialize_
angle - Enables serialization of an angle into a string containing both the value and the “rad” unit.
- serialize_
opt_ angle - Like
serialize_angle, but serializes an [&Option<T>] instead of a&TimplementingInto<DynQuantity>. - serialize_
opt_ quantity - Like
serialize_quantity, but serializes an [&Option<T>] instead of a&TimplementingInto<DynQuantity>. - serialize_
quantity - Enables serialization of a quantity (any type implementing
Into<DynQuantity>) into a string containing both the value and the units. - serialize_
with_ units - A wrapper around a serialization function / closure which enables serialization with units.