DynamicQuantities.jl v0.7.0: efficient and type-stable physical quantities

It’s completely orthogonal to astronomy, these are just fundamentally very different units. For example, in SI, lumen and candela aren’t the same unit – they are 1 lm = 1 cd·sr. And if one ignores angular units in conversions, they end up with 1 lm = 1 cd instead.

Sounds similar to what DimensionfulAngles.jl does. It would resolve the issue if everyone used these units instead of regular u"arcmin". Otherwise, you have some library function that returns W/arcmin^2, but downstream conversions cannot distinguish it from W.

The |> docstring says

|>(x, f)

Infix operator which applies function f to the argument x. This allows f(g(x)) to be
written x |> g |> f.

Does this mean units are now callable, or is this introducing a special case?

It’s just not really practical to start adding another field for all the dimensions people may care about. If you need another dimension that is not part of the SI base units, it’s easy enough to declare a custom struct. I don’t want to violate SI in the main package though.

1 Like

Only |> is defined, rather than making quantities themselves callable (which we thought would introduce a bunch of potential bugs)

Don’t see how disallowing conversion between lumen and candela would be a SI violation. That seems a pretty strong take IMO.
They are defined in SI as different units, used for different dimensions.

Anyway, I’ll stop here. I think I kinda explained my PoV and how this “ignore angular units” approach is fundamentally more error-prone by allowing conversion between incompatible units. Meanwhile, “distinguish angular units by default” covers strictly more usecases.

My only opinion here is that the package should respect the SI unit system. If 1 rad = 1 in SI that’s what the package does. Like you, I would also prefer to track angles in the base units, but I think standards are more important than my subjective view, so this is what I went with.

1 Like

The way I read that table of “derived units” in the “Derived units” section of that wiki article is that radian and steradian are official SI derived units. They are dimensionless, but they are not unitless.

Typical dimensions include length [L], mass [M], and time [T]. A radian has dimensions [L]/[L] = 1, so it is dimensionless. But it is still a unit!

In other words, the SI system requires keeping track of dimensionless units like radian and steradian.

3 Likes

In DynamicQuantities.jl, SymbolicDimensions (with us"...") are how you can represent derived SI units (and whatever other symbols you want). But a Dimensions (with u"...") exclusively stores SI base units.

julia> x = us"rad"  # Derived SI units
1.0 rad

julia> dimension(x).rad
1

julia> uexpand(x)  # Base SI units
1.0

julia> y = us"J"
1.0 J

julia> dimension(y).J
1

julia> uexpand(y)
1.0 m² kg s⁻²

Does that help?

1 Like

I haven’t used DynamicQuantities before, and I’ve barely used Unitful. So I don’t understand the output of dimension(y).J. Shouldn’t that be something like m² kg s⁻² or [L]² [M] [T]⁻²?

I suggest reviewing the DynamicQuantities documentation here for clarity on the key functions and unit handling. Happy to discuss further if needed.

1 rad = 1 is what the SI system defines, but it is not useful. It’s confusing. If someone is using a unit tracking system to track the rad they asking for some useful behavior.

I agree with aplavin’s argument that it’s much more error prone to ignore angular units by default than it is to prevent “SI allowed” conversions by default. I suggest either not including angular units (no loss of functionality, just multiply by 1 if you want a rad or an sr) or having separate angular unit (rad_si) to express the SI behavior. OR (and this is the best choice IMHO) requiring an explicit request to allow conversions that rely on rad=1. It doesn’t violate the SI to require this request, it just requires a bit of ceremony to engage with the single most confusing part of the SI.

I think you may be heartened to know that it’s not just random internet people who are arguing for unitful radians. Many scientists including those from national metrology institutes that are responsible for providing standards to “realize” the SI units also argue for unitful radians. Many physicists argue that only the minimum set of fundamental base units should be included in the, which would exclude the mol (just a number, 10 isn’t a unit so why is the mol?) and the candela (which is about human perception of light rather than light itself). so why are you only seeing complaints about rad when the candela and mol is also controversial in the SI? Because we don’t actually want a super principled unit implementation, they want something useful, and despite their flaws, the mol and candela are useful, while the unitless rad is not. Below are some papers criticizing the SI, 4 on topic of unitful radians and one on the candela.

  1. Proposal for the dimensionally consistent treatment of angle and solid angle by the International System of Units (SI) This author is from BIPM, which until the redefinition of the SI had the artifact standards which defined the meter and the kilogram for the entire world. He argues for a angle base unit.
  2. Physiological Units in the SI argues for a new concept of “physiological derived unit” so that the candela would no longer be a base unit.
  3. Angles in the SI: treating the radian as an independent, unhidden unit does not require the redefinition of the term ‘frequency’ or the unit hertz Also arguing for an angle base unit.
  4. A proposal to classify the radian as a base unit in the SI Nobel prize winner William Phillips from NIST, the US institute of Standards and Technology argues that the radian should be a base unit.
  5. Proposal for the dimensionally consistent treatment of angle and solid angle by the International System of Units

I strongly encourage you to reconsider that “doing exactly what the SI says” is the best choice, instead do what is useful to your users.

4 Likes

@ggggggggg why did you delete your post? It looked like you had some good references. I think having concrete papers to point to (like you had) on modifications to SI is very good and makes me want to reconsider my opinion :slightly_smiling_face: Especially if someone has worked out all the issues and found it can be made robust.

p.s., another alternative is to have the AngleDimensions I suggested above as a built-in

julia> struct AngleDimensions{R} <: AbstractDimensions{R}
           length::R
           mass::R
           time::R
           current::R
           temperature::R
           luminosity::R
           amount::R
           angle::R
       end

and set up promotion rules so that if it collides with any regular Dimensions object, it converts it into the AngleDimensions. It’s a lot of work though so I won’t work on this myself. But if someone cares enough about this to actually contribute it, I’d welcome it.

I have not reviewed the Wikipedia article, but my favourite source for information on SI is NIST. I assume that Wikipedia is heavily based on this document.

Strain is similar to angle in that it is determined as ΔL/L which is also unitless but often given as μm/m.

Another is that the units of energy (work) and torque are the same in base units. Both are N * m, but are fundamentally different.

My favourite is fuel consumption. Since I am from Canada we use liter/(100 km) which becomes L^3/L or L^2. So I can give my car’s fuel economy in hectares , or acres if you prefer.

Units are fun, I like the idea of using SI as a reference and NIST is a good place to go for this reference.

1 Like

I undeleted it since you seem happy about it. The reason I deleted it was that I was about to write a follow up post, and it felt like I was going to pile on and it might be overall discouraging. If you’re into it, then I do have more input I’d like to share

The suggestion to use symbolic units to “avoid” 1 rad = 1 feels worse than not having it to me, since it dissapears as soon as you interact with normal units. I haven’t wrapped my head around your goals with symbolic units fully, and I find their behavior quite surprising. For example I can’t add m and mm when they’re symbolic which gives this very odd error message
1us"m"+1us"mm" = DimensionError: 1.0 m and 1.0 mm have incompatible dimensions
nor can I add rad and sr which is something the docs suggest is a plus
1us"rad" +1us"sr"=DimensionError: 1.0 rad and 1.0 sr have incompatible dimensions
but I can easily add them if I change just one character
1us"rad"+1u"sr"=2.0
and that ease of changing behavior feels very footgunny to me, though I haven’t come up with any examples outside of rad where it is really problematic.

Do you have any plans to support displaying the working units. Eg 1u"nm"*1u"eV"=1 eV nm until at some point it’s requested to be reduced to base or unified with another compatible unit?

I wouldn’t say I am necessarily ‘happy’ with it; more I just feel like the safest thing to do is go with the international standard. But I am very happy to be proven wrong – I just want the proof to be in the form of studies/papers rather than strong opinions and small code examples. My worry is that if I were to adopt some non-standard mixed SI system, it would introduce inconsistencies we haven’t thought of yet (but that SI has thought of – which is why they’ve made the choice of choosing slightly inconvenient unit system).

SI has put much more thought into units than any of us so I am basically just looking for a very strong evidence to switch (but I do not have any reason to prefer one over the other; I don’t really care either way).

This is the purpose of the symbolic units with us"..."

julia> using DynamicQuantities

julia> 1us"nm"*1us"Constants.eV"
1.0 nm eV

julia> ans |> uexpand
1.602176634e-28 m³ kg s⁻²

This is the intended behavior. It makes it clear to the user that they are adding units of different scale. (Also happy to reconsider this. I added it as @odow wanted it for JuMP).

If you want to get around this, you can just do:

  1. 1.5us"m" + (3us"mm" |> us"m"), or
  2. 1.5us"m" + (3us"mm" |> uexpand) which will convert to Dimensions, or
  3. just work in the base SI units with 1.5u"m" + 3u"mm".

Working in the base SI units is always faster in DynamicQuantities.jl because it only needs to track 7 numbers in an immutable struct, rather than two sparse arrays as in SymbolicDimensions

Here’s a full implementation of AngleDimensions. This is probably good to add as an example to the documentation for extending DynamicQuantities:

using DynamicQuantities
import DynamicQuantities: DynamicQuantities as DQ

struct AngleDimensions{R} <: AbstractDimensions{R}
    length::R
    mass::R
    time::R
    current::R
    temperature::R
    luminosity::R
    amount::R
    angle::R
end

# field -> string mapping
function DQ.dimension_name(::AngleDimensions, k::Symbol)
    default_dimensions = (
        length = "m",
        mass = "kg",
        time = "s",
        current = "A",
        temperature = "K",
        luminosity = "cd",
        amount = "mol",
        angle = "rad",
    )
    return get(default_dimensions, k, string(k))
end

function Base.promote_rule(::Type{AngleDimensions{R1}}, ::Type{Dimensions{R2}}) where {R1,R2}
    return AngleDimensions{promote_type(R1, R2)}
end
function (::Type{AngleDimensions{R}})(d::Dimensions) where {R}
    return AngleDimensions{R}(
        d.length, d.mass, d.time, d.current, d.temperature, d.luminosity, d.amount, zero(R),
    )
end

const rad = Quantity(1.0, AngleDimensions(angle = 1))

Then, we can do operations with rad as follows:

julia> θ = 0.02rad  # AngleDimensions-based
0.02 rad

julia> distance = 10u"km"  # Dimensions
10000.0 m

julia> θ * distance  # Promotes to AngleDimensions
200.0 m rad
1 Like

One more comment. I was re-reading some of the references, and many of them call out software specifically as being an area where rigorous treatment of radians is needed. For example from Ref 1 above:

“These seemingly ad hoc ‘rules’ for getting the ‘appropriate’ answer are at the core of the widespread confusion about angle (and solid angle), in general, and about the radian (and steradian), in particular. Since these ‘rules’ require human experience and ‘insight’ to apply, they do not translate easily into programing algorithms for computer-aided physics and engineering calculation software.”

I sympathize with the issue of angles. It is one of our pain points with Unitful.jl

Many other people also dislike the way Unitful.jl handles angles, precisely because of the SI rules. Examples include UnitfulAngles.jl and DimensionfulAngles.jl

It would be really nice to treat angles with a more practical set of rules, ideally allowing to differentiate between angles and raw unitless numbers.

The lack of explicit treatment of angles leads to our inability to represent things like 34° 24' 16'' with Unitful.jl or any other package. These are angles expressed as Degree Minute Second (DMS), yet another popular alternative to Radians or Degrees.

2 Likes