Skip to content

Converting between angular frequency and frequency (e.g., rad/s to Hz) #6032

Open

Description

@namurphy

I just tried a couple of ways of converting from 'rad / s' to 'Hz' that gave me different behavior than I was expecting. Doing this conversion requires dividing by 2π, but the dimensionless_angles equivalency uses a factor of 1 instead of 2π. Here are some examples that confused me when I first tried them. The second example gets pretty close to the behavior I was expecting, except that it seems non-intuitive that 'cycle / s' cannot be converted to 'Hz'.

>>> from astropy import units as u
>>> angular_frequency = 1.0 * u.rad/u.s
>>> angular_frequency.to(u.Hz)
UnitConversionError: 'rad / s' (angular speed) and '1 / s' (frequency) are not convertible

>>> frequency = angular_frequency.to(u.cycle/u.s)   # a cycle is 2π radians
<Quantity 0.15915494309189535 cycle / s>
>>> frequency.to(u.Hz)
UnitConversionError: 'cycle / s' (angular speed) and 'Hz' (frequency) are not convertible

>>> u.set_enabled_equivalencies(u.dimensionless_angles())
>>> angular_frequency = 1.0 * u.rad/u.s
>>> angular_frequency.to(u.Hz)
<Quantity 1.0 Hz>

Should units allow conversion between angular frequency and frequency by default? Would it be better to add this as another equivalency along with dimensionless_angles? Or is the status quo preferred? Allowing this conversion by default would result in more readable code for me and I think this would be more intuitive, but I don't know if there are any adverse consequences.

Also, here's my code for a custom equivalency that gave me the expected behavior:

>>> angfreq_to_freq = [(u.rad/u.s, u.Hz, lambda x: x/(2*pi), lambda x: 2*pi*x)]
>>> angular_frequency = 1.0 * u.rad / u.s
>>> angular_frequency.to(u.Hz, equivalencies=angfreq_to_freq)
<Quantity 0.15915494309189535 Hz>

I tried this with astropy 1.3.2 and 2.0.dev18104. Related pull requests: #1160, #1161, #2544, #2559

Many thanks!

Activity

  1. pllim commented on May 10, 2017

    @pllim
    Member

    Maybe this was an oversight because u.spectral() is able to handle u.rad / u.m, so it only makes sense that it should also handle u.rad / u.s; @mhvk ?

  2. mhvk commented on May 10, 2017

    @mhvk
    Contributor

    @namurphy - thanks for the report. This actually a tricky one. As you will have seen from the long discussion in #1161, dimensionless_angles is meant in the physical sense, where radius * angle has most usefully units of length, not of length * angle. With dimensionless_angles one basically tells Quantity that Hz should be treated as equivalent to rad/s -- which is not what you want!

    What you seem to want instead is to treat Hz as cycle/s, for which your later equivalency works, or more simply:

    (1.*u.rad/u.s).to(u.Hz, equivalencies=[(u.cy/u.s, u.Hz)])
    # Quantity 0.15915494309189535 Hz>
    

    Now whether this should be a possible equivalency in astropy or not depends a bit on where exactly you are using this and whether this is likely a common usage. Is it in the context of spectroscopy? If so, maybe it could be added to u.spectral() -- but I have heard of wavenumber in linear and angular, but not of angular frequencies in that context.

  3. namurphy commented on May 10, 2017

    @namurphy
    ContributorAuthor

    Yes, dealing with dimensionless dimensions is definitely a tricky issue!

    This came up while I was calculating basic plasma parameters such as an ion or electron gyrofrequency. These are usually calculated as angular frequency in units of radians per second, with the corresponding frequency in inverse seconds being given as f = ω/2π. In wave physics applications the standard convention is to have angular frequency in radians per second be convertible to Hz, so I do expect this to be common usage.

    I don't know if u.spectral() is the best place for this, however, as that enables several other equivalencies that assume we're dealing with light. The f = ω/2π relation is much more general than that since it applies to any sinusoidal wave, which is part of the reason why I'm mostly thinking this should be the default behavior within units.

    I am also thinking about how units treats becquerels (where 1 Bq = 1 decay event per second). u.Bq.to(u.Hz) does give 1.0.

    Edited to add: In pint there is an issue called Add angular frequency which has a comment that says:

    But the problem is that hertz is the inverse of second. It is not a good idea to redefine it in the canonical list included with pint as it will break user expectations. Converting from frequency to angular frequency is not really a unit conversion problem. It is a relation between physical quantities. Both have the same units: 1/second.

    That's is fairly convincing, so now I'm thinking that the equivalency approach is better and that perhaps using dimensionless_angles is the way for me to go while manually keeping track of all of the factors of 2π. Hm....

  4. namurphy commented on May 10, 2017

    @namurphy
    ContributorAuthor

    But there's more... In plasma physics there are characteristic length scales that are given by a speed divided by an angular frequency (e.g., an ion inertial length is c/ωpi or the speed of light divided by the ion plasma frequency).

    >>> omega_pi = 1 *u.rad/u.s
    >>> c/omega_pi
    <Quantity 299792458.0 m / rad>
    >>> (c/omega_pi).to(u.m)
    UnitConversionError: 'm / rad' and 'm' (length) are not convertible

    For this, dimensionless_angles() would give the desired behavior and there should be no multiplication or division by 2π. Sheesh! I'm tempted to submit a pull request to set 2π=1...

  5. pllim commented on May 11, 2017

    @pllim
    Member

    @namurphy , you are welcome to submit a PR!

  6. namurphy commented on May 11, 2017

    @namurphy
    ContributorAuthor

    Sorry to keep circling back to this with such frequency but I'm grateful that you understand my degree of confusion!

    Part of my confusion came from not fully understanding the implications of dimensionless_angles(). I suggest that the first thing that we do is expand the documentation on dimensionless_angles() to include information on the potential pitfalls that I encountered, and more broadly on what it means for radians to be dimensionless. That should help allay confusion should anyone else try to use this in the same way that I did. Does that sound like a worthwhile thing to do? We could then postpone the discussion of whether or not another built-in equivalency would be worth it until after we add this additional documentation. I can probably work on this.

    One more potential pitfall:

    >>> (1*u.cycle/u.s).to(u.Hz, equivalencies=u.dimensionless_angles())  # expected to be 1 Hz
    <Quantity 6.283185307179586 Hz>
  7. mhvk commented on May 11, 2017

    @mhvk
    Contributor

    @namurphy - It may be an idea to add your specific example of cy/s to Hz to the documentation [1], as it is logical but not necessarily expected, and perhaps also give the way to solve it):

    (1.*u.cycle/u.s).to(u.Hz, equivalencies=[(u.cy/u.s, u.Hz)])
    # <Quantity 1.0 Hz>
    

    Do you want to make a PR?

    [1] http://docs.astropy.org/en/latest/units/equivalencies.html#angles-as-dimensionless-units

  8. namurphy commented on May 11, 2017

    @namurphy
    ContributorAuthor

    @mhvk - Sure! I'll aim to get to that in the next couple of days, while simultaneously trying to get myself unconfused about this. You solution is very clear and explicity, so I like it and will use that approach going forward. I'll also include it in the pull request.

    I'm presently a net negative contributor (minus three characters!) so I'm happy to make a positive contribution!

  9. virtualmarioe commented on Aug 12, 2019

    @virtualmarioe

    Hey @namurphy, this conversion is exactly what I was looking for in the context of neural population simulations. Thanks for raising the issue.

  10. added a commit that references this issue on Jul 10, 2020
  11. added a commit that references this issue on Jul 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions