uom icon indicating copy to clipboard operation
uom copied to clipboard

How to use type aliases for new/combined quantities

Open anwarhahjjeffersongeorge opened this issue 4 years ago • 8 comments

To make a new combined Quantity representing a change in mass concentration per time, that works with SI, one can do something like:

type MassConcentrationRate = Quantity<uom::si::ISQ<N3, P1, N1, Z0, Z0, Z0, Z0>, uom::si::SI<f64>, f64>;

That's fine. From there the alias works in function signatures alongside existing uom quantities, like:

/// Calculate the frequency of mass concentration change
pub fn calculate_frequency (
  c_x: &MassConcentration,
  d_cx_dt: &MassConcentrationRate,
) -> Frequency {
  *d_cx_dt / *c_x
}

But to instantiate that type (EDIT: while maintaining unit checking), it's necessary to use other types; as so:

let d_cx_dt: MassConcentrationRate 
  = MassConcentration::new::<kilogram_per_liter>(30.0) * Frequency::new::<hertz>(1.0);

Then it can be used:

let c_x = MassConcentration::new::<kilogram_per_liter>(30.0);
let freq = calculate_frequency(&c_x, &d_cx_dt);

This all works fine, but:

  1. Is it the idomatic/intended way to accomplish this? EDIT: It seems like even though one can use the Quantity struct definition with fields, that feels like going against the point.
  2. Is it possible to use the quantity macro without defining a new entire system of units? That did not seem to work.
  3. Should PRs even be opened to add in new quantities like this whose use case might not be common?

  1. This is probably the best way currently without adding a new quantity. The * between should be optimized away at compile time and it's likely more clear than using the type constructor: let d_cx_dt = MassConcentrationRate { dimension: PhantomData, units: PhantomData, value: 30.0, }; as this method implicitly uses the base units.
  2. The quantity! macro can't be used outside the system! macro currently. I attempted to do this last year and ran into a couple roadblocks and stopped.
  3. PRs, even for uncommon quantities, are always appreciated.

iliekturtles avatar Feb 22 '21 13:02 iliekturtles

If defining a custom type alias in this way, what's the best way to go about defining a unit for the new quantity type?

I tried using the unit! macro, but I get the error unresolved import [my-new-type-quantity

image

  --> orbits/src/units.rs:5:1
   |
5  | / unit! {
6  | |     system: uom::si;
7  | |     quantity: GravitationalParamter;
8  | |
9  | |     @meters_cubed_per_seconds_squared: 1.0; "m³ / s²", "", "";
10 | | }
   | |_^ no external crate `GravitationalParamter`
   |
   = note: this error originates in the macro `unit` (in Nightly builds, run with -Z macro-backtrace for more info)

at this point am I better off defining my own system?

Is there a guide or example somewhere on how to extend your SI system without needing to redefine the entire thing with my additions?

mia-n avatar Nov 10 '23 03:11 mia-n

I found an extremely stupid hack/workaround. I defined a const

pub const ONE_METER_CUBED_PER_SECOND_SQUARED: GravitationalParameter = (Length::new::<meter>(1.0)).powi(P3::new()) * (Time::new::<second>(1.0)).powi(N2::new());

let mu: GravitationalParamter = 3.986e14 * ONE_METER_CUBED_PER_SECOND_SQUARED;

but, I mean, I kind of hate that. Surely there's a better way.

Right?

mia-n avatar Nov 10 '23 04:11 mia-n

@mia-n FWIW I haven't looked at this in a while, and I forgot to close the issue.

If defining a custom type alias in this way, what's the best way to go about defining a unit for the new quantity type?

I tried using the unit! macro, but I get the error unresolved import [my-new-type-quantity

image

  --> orbits/src/units.rs:5:1
   |
5  | / unit! {
6  | |     system: uom::si;
7  | |     quantity: GravitationalParamter;
8  | |
9  | |     @meters_cubed_per_seconds_squared: 1.0; "m³ / s²", "", "";
10 | | }
   | |_^ no external crate `GravitationalParamter`
   |
   = note: this error originates in the macro `unit` (in Nightly builds, run with -Z macro-backtrace for more info)

It seems like the macro invocation should work, but there might just be a typo: GravitationalParamterGravitationalParameter.

I found an extremely stupid hack/workaround. I defined a const

pub const ONE_METER_CUBED_PER_SECOND_SQUARED: GravitationalParameter = (Length::new::<meter>(1.0)).powi(P3::new()) * (Time::new::<second>(1.0)).powi(N2::new());

let mu: GravitationalParamter = 3.986e14 * ONE_METER_CUBED_PER_SECOND_SQUARED;

but, I mean, I kind of hate that. Surely there's a better way.

Right?

This workaround could be rewritten as a macro to make it easier if you have to do it a lot. However, I don't know if that's any better or less effort than using the unit macro if you can get that working.

Ah that was a silly mistake when re-writing this to take a picture, thank you for catching that. With it fixed, I do still get almost (but not quite) the same error:

error[E0432]: unresolved import `__quantity`
  --> orbits/src/units.rs:11:1
   |
11 | / unit! {
12 | |     system: uom::si;
13 | |     quantity: GravitationalParameter;
14 | |
15 | |     @meter_cubed_per_second_squared: prefix!(none);
16 | |     "m³ / s²", "meter cubed per second squared", "meters cubed per second squared";
17 | | }
   | |_^ `__quantity` is a type alias, not a module
   |
   = note: this error originates in the macro `unit` (in Nightly builds, run with -Z macro-backtrace for more info)

mia-n avatar Nov 10 '23 15:11 mia-n

I think it's because unit (here) is expecting quantity to be a module? I'm not super sure, I'm kind of new to rust and stumbling my way through this.

mia-n avatar Nov 10 '23 15:11 mia-n

I think it's because unit (here) is expecting quantity to be a module? I'm not super sure, I'm kind of new to rust and stumbling my way through this.

@mia-n If you check here maybe you can use the quantity macro to implement the GravitationalParameter quantity?

The documentation for the unit macro says you're on the right track that quantity is looking for a path to the module where quantity was invoked.

I'm not sure from your use case whether you want a unit or a quantity here.

Also, FYI sometimes if you're stuck on a macro invocation, you can use cargo-expand, but it needs nightly rust to be installed too.