Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Question] Converting from linear to angular velocity #368

Open
spayne-pgh opened this issue Dec 20, 2024 · 3 comments
Open

[Question] Converting from linear to angular velocity #368

spayne-pgh opened this issue Dec 20, 2024 · 3 comments

Comments

@spayne-pgh
Copy link

spayne-pgh commented Dec 20, 2024

I'm looking at converting a linear m/s to angular rad/s when I know the radius of the rotating item but I'm stuck at trying to get the result of the division of m/s / m which, if you just consider unit labels, ends up being 1/s . With a contrived googletest example:

TEST(UnitsTest, MetersPerSecondToRadsPerSecond) {
    const auto speed = (au::meters / au::seconds)(100.0);
    const auto radius = au::meters(10.0);
    const auto angular_velocity = speed / radius;
    EXPECT_DOUBLE_EQ(angular_velocity.in(au::radians / au::seconds), 10.0);
}

I get the expected error that the conversion can't take place because the types aren't the same dimension:

In file included from units_test.cc:8:
In file included from external/_main~_repo_rules~au/au/code/au/units/meters.hh:20:
In file included from external/_main~_repo_rules~au/au/code/au/quantity.hh:20:
external/_main~_repo_rules~au/au/code/au/conversion_policy.hh:101:19: error: static assertion failed due to requirement 'HasSameDimension<au::Pow<au::Seconds, -1>, au::UnitProduct<au::Pow<au::Seconds, -1>, au::Radians>>::value': Can only convert same-dimension units
  101 |     static_assert(HasSameDimension<SourceUnit, TargetUnit>::value,
      |                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
external/_main~_repo_rules~au/au/code/au/quantity.hh:158:13: note: in instantiation of function template specialization 'au::implicit_rep_permitted_from_source_to_target<double, au::Pow<au::Seconds, -1>, au::QuantityMaker<au::UnitProduct<au::Pow<au::Seconds, -1>, au::Radians>>>' requested here
  158 |             implicit_rep_permitted_from_source_to_target<Rep>(unit, NewUnit{});
      |             ^
external/_main~_repo_rules~au/au/code/au/quantity.hh:189:20: note: in instantiation of function template specialization 'au::Quantity<au::Pow<au::Seconds, -1>, double>::as<au::QuantityMaker<au::UnitProduct<au::Pow<au::Seconds, -1>, au::Radians>>, void>' requested here
  189 |             return as(u).in(u);
      |                    ^
glyd/base/units_test.cc:18:39: note: in instantiation of function template specialization 'au::Quantity<au::Pow<au::Seconds, -1>, double>::in<au::QuantityMaker<au::UnitProduct<au::Pow<au::Seconds, -1>, au::Radians>>, void>' requested here
   18 |     EXPECT_DOUBLE_EQ(angular_velocity.in(au::radians / au::seconds), 10.0);

Is there an explicit way to do that conversion that I'm missing?

@spayne-pgh
Copy link
Author

I suppose I can fake getting the dimensionless units out of it by also multiplying by 1 second but it still feels like it's outside of the spirit of unit safety:

const auto angular_velocity = (au::radians / au::seconds)(speed / radius * au::seconds(1.0));

@chiphogg
Copy link
Contributor

I suppose I can fake getting the dimensionless units out of it by also multiplying by 1 second but it still feels like it's outside of the spirit of unit safety:

const auto angular_velocity = (au::radians / au::seconds)(speed / radius * au::seconds(1.0));

Indeed it is! We can rule this approach out pretty quickly. The right approach turns out to be simple, but it needs a little motivation.

The root cause of all this weirdness is that conventional equations are based on a kind of "natural units" system, but for angles.

In case you're not familiar with "natural units", one example is that high energy physicists like to choose units where constants like $c$ and $\hbar$ have the numeric value of $1$, and can therefore be omitted from equations --- $E^2 = p^2 c^2 + m^2 c^4$ becomes simply $E^2 = p^2 + m^2$. (That's why they end up measuring particle masses in units like $\text{MeV}$, which is of course really an energy unit.) All the equations still work fine... but only if you use the specific units that the simplified equations are based on.

With angles, we tend to choose units where a specific angle-dimensioned constant --- the "Cotes angle", or, simply, the "radian" --- has a numeric value of $1$. All the familiar equations "work", but only if you measure angles in radians.

Going from a natural-units equation to a "complete" equation is hard in general, because you're trying to reverse a lossy step. But the reward is worth it: you get an equation that is valid for any choice of angular units!

In this case, we can check out Table 1 of Quincey (2021), the best freely available paper outlining a proposal to fix the SI (Leonard (2021) is similarly excellent but is not freely available). We find the following excerpt:

Quantity Familiar Equation Complete Equation
tangential velocity $v = r \omega$ $v = r \omega / \Theta_C$

Since $\Theta_C = 1 \, \text{rad}$, this shows us what to do on the C++ side. A unit symbol is the right tool for the job, since symbols::rad already exists. (You could also use a constant, but you'd have to define it.) So let's try that unit test again:

TEST(UnitsTest, MetersPerSecondToRadsPerSecond) {
    using au::symbols::rad;
    //^^^^^^^^^^^^^^^^^^^^^
    const auto speed = (au::meters / au::seconds)(100.0);
    const auto radius = au::meters(10.0);
    const auto angular_velocity = speed * rad / radius;
    //                                 ^^^^^^
    EXPECT_DOUBLE_EQ(angular_velocity.in(au::radians / au::seconds), 10.0);
}

That should work!

P.S. When reading a conventional equation, I like to mentally append what I have taken to calling the "Quincey disclaimer", (from that same paper) to the equation:

...where $\Theta_C$, which has a value of $1 \, \text{rad}$, has been set equal to $1$ (so that the angle unit must be the radian, and the units on either side of the equation do not match).

This helps me make sense of the equation, and reminds me to be aware of its preconditions.

@chiphogg
Copy link
Contributor

Oh! And I forgot to mention #87. We are on the hook for providing thorough documentation for how to deal with dimensioned angles in a world that overwhelmingly pretends they are dimensionless --- including (as mentioned there) a "how-to" page that will give you tables of "familiar" and "complete" equations, so that it will become perfectly clear where to "stick the rad".

No firm commitments, but I hope/expect to be able to address this in 2025, some time after Aurora's first product launches.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants