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

Addition of degree + radian is unitless #537

Open
bc0n opened this issue May 10, 2022 · 5 comments
Open

Addition of degree + radian is unitless #537

bc0n opened this issue May 10, 2022 · 5 comments
Labels
dimensionless dimensionless quantities/units (mm/m, rad, …) and their interaction with plain numbers

Comments

@bc0n
Copy link

bc0n commented May 10, 2022

using Unitful
a = 180u"°" + 3.141u"rad"
@show unit(a)    # blank
@show typeof(a) # Float64

I expect to have to explicitly ustrip() when I want a unitless quantity.
This behavior makes certain operations type-unstable, which the docs describe and dismiss.
Much of the point of having typed functions goes away if I also have to accept floats because their input units were automatically, silently canceled.
UnitfulAngles.jl does not fix this.

@sostock
Copy link
Collaborator

sostock commented Jun 10, 2022

Just to clarify, which of the following is the issue here:

  • that Unitful.jl returns plain numbers whenever the result of an operation would be a quantity with units NoUnits or
  • that the result of adding a quantity with units rad and a quantity with units ° does not have units rad or °, but NoUnits?

This behavior makes certain operations type-unstable, which the docs describe and dismiss.

What operation is type-unstable because of this?

Much of the point of having typed functions goes away if I also have to accept floats because their input units were automatically, silently canceled.

Yes, it is unfortunate that dispatching on DimensionlessQuantity is not useful because of this behavior. Dispatching on Union{DimensionlessQuantity,Real} may be an option if you only expect real numbers. Unfortunately, Union{DimensionlessQuantity,Number} would also match dimensionful quantities, since AbstractQuantity <: Number.

@bc0n
Copy link
Author

bc0n commented Jun 15, 2022

Thanks for the follow-up, I'd like to say both are the same issue, which is that Radians are sometimes treated as an angle and sometimes number and this inconsistency leads to confusion.
The type instability is simply:

ulia> Angle{T} = Union{Quantity{T,NoDims,typeof(u"rad")}, Quantity{T,NoDims,typeof(u"°")}} where T
Union{Quantity{T, NoDims, Unitful.FreeUnits{(rad,), NoDims, nothing}}, Quantity{T, NoDims, Unitful.FreeUnits{(°,), NoDims, nothing}}} where T

julia> g(a::Angle) = @show a
g (generic function with 1 method)

julia> g(1°)
a = 1°
1°

julia> g(1u"rad")
a = 1 rad
1 rad

julia> g(1°+2°)
a = 3°
3°

julia> g(1u"rad" + 2u"rad")
a = 3 rad
3 rad

julia> g(1° + 2u"rad")
ERROR: MethodError: no method matching g(::Float64)
Closest candidates are:
  g(::Union{Quantity{T, NoDims, Unitful.FreeUnits{(rad,), NoDims, nothing}}, Quantity{T, NoDims, Unitful.FreeUnits{(°,), NoDims, nothing}}} where T) at REPL[49]:1
Stacktrace:
 [1] top-level scope
   @ REPL[54]:1

Again I think the value of unit systems is that they help to distinguish variables and guide users in creating consistent, physically-intelligible models. While angles are, I guess, a mathematical convention rather than traceable to a physical quantity, it strains the conceptual model to define

julia> AngleOrNumber = Union{Angle, Real}
Union{Real, Union{Quantity{T, NoDims, Unitful.FreeUnits{(rad,), NoDims, nothing}}, Quantity{T, NoDims, Unitful.FreeUnits{(°,), NoDims, nothing}}} where T}

julia> typeof(1) <: AngleOrNumber
true

julia> typeof(1°) <: AngleOrNumber
true

That is I would prefer to treat angles as a fully-contrived unit that is not mapped to dimensionless/Real at all. Alternately, a fallback promotion from Real to Radian might enable g(1) to succeed?

@sostock sostock added the dimensionless dimensionless quantities/units (mm/m, rad, …) and their interaction with plain numbers label Dec 14, 2022
@cmichelenstrofer
Copy link
Contributor

@bc0n checkout DimensionfulAngles.jl, it treats angles as a dimension.

@RomeoV
Copy link

RomeoV commented Sep 13, 2023

Here's a two-line hack I used to solve this problem:

AngularUnits = Union{typeof(unit(1.0°)), typeof(unit(1.0rad))};
# without this there's a bug when adding different angular units...
Unitful.promote_unit(lhs::T, rhs::S) where {T<:AngularUnits, S<:AngularUnits} = rad

This always returns the result of addition/subtraction in rad. Actually I would have preferred

Unitful.promote_unit(lhs::T, rhs::S) where {T<:AngularUnits, S<:AngularUnits} = lhs

but that somehow yields a Stackoverflow error.

@RomeoV
Copy link

RomeoV commented Sep 13, 2023

Btw I think it would be really good if this issue would be dealt with properly, or at least throw an error of some kind.

I had a pretty painful debugging session trying to find this, especially since ustrip(\degree, myangle) doesn't complain if myangle is already a Float64. So for me the typed angle became a regular Float, further angle addition was incorrect, and the numerical error propagated silently from the simulation into the optimizatio, resulting in misleading numerical results without any warnings / errors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dimensionless dimensionless quantities/units (mm/m, rad, …) and their interaction with plain numbers
Projects
None yet
Development

No branches or pull requests

4 participants