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

**URL:** <https://discourse.julialang.org/t/dynamicquantities-jl-v0-7-0-efficient-and-type-stable-physical-quantities/103709>\
**Category:** Package Announcements\
**Tags:** package, physics, unitful\
**Created:** [September 9, 2023, 11:48pm UTC](https://discourse.julialang.org/t/dynamicquantities-jl-v0-7-0-efficient-and-type-stable-physical-quantities/103709 "2023-09-09T23:48:08Z")\
**Posts on this page:** 9\
**Page:** 3

<div class="post-metadata">

**Author:** ![Jake](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jake/32/46007_2.png) [@Jake](https://discourse.julialang.org/u/Jake)\
**Post date:** [March 12, 2024, 8:30pm UTC](https://discourse.julialang.org/t/dynamicquantities-jl-v0-7-0-efficient-and-type-stable-physical-quantities/103709/41 "2024-03-12T20:30:14Z")

</div>

Not that MathCad is the authoritative standard (it isn’t, in fact some of its unit handling is just plain wrong by SI standards), but because it is on my computer and I am familiar with it, I took a look at how it handles angles. If you want to try it there is a [trial version available](https://www.mathcad.com/en/try-and-buy/mathcad-express-free-download) that will revert to MathCad Express after 30 days. This will not impact its units handling.

I have much more experience with its units handling than with the Julia packages and found it quite useful. For angles it has degrees, DMS, radian, revolution and steradian as angular units. The DMS is a function that converts the angle to radians and displays it unitless. So it seems it does things similar to DynamicQuantities from what I gather from this post.

The conundrum is that if I have an angle I would like to keep it as an angle but as soon as I multiply it by a radius to get the distance around a circle, I would like it to drop the angle unit and just keep the distance unit.

There is a similar problem with frequency. Frequency is normally given as Hz (cycles/s) rather than rad/s, though the later is more natural unit wise at least in the vibration world where I work.

I wish you the best in sorting this all out and think you have done a good job thinking through the issues.

---

<div class="post-metadata">

**Author:** ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)\
**Post date:** [March 12, 2024, 8:45pm UTC](https://discourse.julialang.org/t/dynamicquantities-jl-v0-7-0-efficient-and-type-stable-physical-quantities/103709/42 "2024-03-12T20:45:37Z")

</div>

There is a lot of writing on this topic. I don’t have a summary, but it seems probably worth reviewing what the specialists say.

> **[Angles in the SI: a detailed proposal for solving the problem](https://arxiv.org/abs/2108.05704)**
>
> A recent Letter proposed changing the dimensionless status of the radian and steradian within the SI, while allowing the continued use of the convention to set the angle 1 radian equal to the number 1 within equations, providing this is done...

> **[Angles are inherently neither length ratios nor dimensionless](https://arxiv.org/abs/1909.08389)**
>
> The status of angles within The International System of Units (SI) has long been a source of controversy and confusion. We address one specific but crucial issue, putting the case that the idea of angles necessarily being length ratios, and hence...

> **[On the dimension of angles and their units](https://arxiv.org/abs/2203.12392)**
>
> We examine implications of angles having their own dimension, in the same sense as do lengths, masses, {\\it etc.} The conventional practice in scientific applications involving trigonometric or exponential functions of angles is to assume that the...

> **[Comment on 'Angles in the SI: treating the radian as an independent, unhidden...](https://arxiv.org/abs/2011.08666)**
>
> In the paper by P. Quincey (2020) \[1\], the author claims that angles are not dimensionless quantities and that the radian should be a new independent base unit. However, the claim is not supported and the proposed redefinition will cause several...

[Comment on ‘Dimensionless units in the SI’](https://iopscience.iop.org/article/10.1088/0026-1394/52/4/613)

[Comment on ‘On the units radian and cycle for the quantity plane angle’](https://iopscience.iop.org/article/10.1088/0026-1394/53/6/1281/pdf)

---

<div class="post-metadata">

**Author:** ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)\
**Post date:** [March 13, 2024, 11:31am UTC](https://discourse.julialang.org/t/dynamicquantities-jl-v0-7-0-efficient-and-type-stable-physical-quantities/103709/43 "2024-03-13T11:31:59Z")

</div>

Did you read these, or just found some links from a google search? If you actually read them, a summary would be helpful. (I’m not actively looking to change the definition so have no reason to do a literature review myself)

My main takeaway from skimming these papers is that it’s far from a settled debate. So I’ll stick with `1 rad = 1` in `Dimensions` for now since it’s what the SI says. If you want to track angles you can use `SymbolicDimensions` (built-in) or the `AngleDimensions` from above.

---

<div class="post-metadata">

**Author:** ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)\
**Post date:** [March 13, 2024, 1:22pm UTC](https://discourse.julialang.org/t/dynamicquantities-jl-v0-7-0-efficient-and-type-stable-physical-quantities/103709/44 "2024-03-13T13:22:14Z")

</div>

Quick update, I just added `AngleDimensions` to the docs:

> [@](#):
>
> ## 3. Using dimensional angles
> 
> Say that we wish to track angles as a unit, rather than assume the SI convention that `1 rad = 1`. We can do this by creating a new struct to track dimensions:
> 
> ```julia
> using DynamicQuantities
> 
> struct AngleDimensions{R} <: AbstractDimensions{R}
> length::R
> mass::R
> time::R
> current::R
> temperature::R
> luminosity::R
> amount::R
> angle::R
> end
> 
> ```
> 
> Simply by inheriting from `AbstractDimensions`, we get all the constructors and operations as defined on `Dimensions`:
> 
> ```julia
> julia> x = Quantity(1.0, AngleDimensions(length=1, angle=-1))
> 1.0 m angle⁻¹
> 
> ```
> 
> However, perhaps we want to set the default `angle` dimension as `rad`. We can do this by defining a method for `dimension_name`:
> 
> ```julia
> import DynamicQuantities: DynamicQuantities as DQ
> 
> 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
> 
> ```
> 
> This gives us the following behavior:
> 
> ```julia
> julia> x = Quantity(1.0, AngleDimensions(length=1, angle=-1))
> 1.0 m rad⁻¹
> 
> ```
> 
> Next, say that we are working with existing quantities defined using standard `Dimensions`. We want to promote these to our new `AngleDimensions` type.
> 
> For this, we define two functions: `promote_rule` and a constructor for `AngleDimensions` from regular `Dimensions`:
> 
> ```julia
> function Base.promote_rule(::Type{AngleDimensions{R1}}, ::Type{Dimensions{R2}}) where {R1,R2}
> return AngleDimensions{promote_type(R1, R2)}
> end
> function Base.convert(::Type{Quantity{T,AngleDimensions{R}}}, q::Quantity{<:Any,<:Dimensions}) where {T,Din,R}
> val = ustrip(q)
> d = dimension(q)
> return Quantity(
> T(val),
> AngleDimensions{R}(;
> d.length, d.mass, d.time, d.current, d.temperature, d.luminosity, d.amount, angle=zero(R)
> )
> )
> end
> 
> ```
> 
> This means that whenever a `Quantity{<:Any,<:Dimensions}` interacts with a `Quantity{<:Any,<:AngleDimensions}`, the result will be a `Quantity{<:Any,<:AngleDimensions}`, and we will initialize the angle dimension to 0. (Code not given for `SymbolicDimensions`; you will probably want to treat the angles in symbolic units explicitly, so that `us"rad"` is correctly tracked.)
> 
> Let’s define a constant for `rad`:
> 
> ```julia
> julia> const rad = Quantity(1.0, AngleDimensions(angle = 1))
> 1.0 rad
> 
> ```
> 
> and use it in a calculation:
> 
> ```julia
> julia> x = 2rad
> 2.0 rad
> 
> julia> y = 10u"min"
> 600.0 s
> 
> julia> angular_velocity = x / y
> 0.0033333333333333335 s⁻¹ rad
> 
> ```
> 
> which as we can see, automatically promotes to `AngleDimensions`.
> 
> **However, note the following:** If existing code uses `rad` as a unit without tracking  
> it with `AngleDimensions`, you will need to explicitly add the missing dimensions. For this reason, if you decide to take this approach to tracking units, you probably want to use `AngleDimensions` throughout your codebase, rather than mixing them.

Obviously there’s no solution that will make everybody happy but I think it’s nice to lean into the core language features (like a plain `struct` for custom dimensions) to let people extend things as they see fit.

---

<div class="post-metadata">

**Author:** ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)\
**Post date:** [May 11, 2024, 4:39pm UTC](https://discourse.julialang.org/t/dynamicquantities-jl-v0-7-0-efficient-and-type-stable-physical-quantities/103709/45 "2024-05-11T16:39:36Z")

</div>

Eh, why not… Let’s just have statically-typed units in DynamicQuantities.jl as well!

> <https://github.com/SymbolicML/DynamicQuantities.jl/pull/136>
>
> I understand this goes against the main point of this package, but it turned out… to be extremely easy to stick in a \`StaticDimensions\` interface.
> 
> The trick is to store a regular dynamic \`Dimensions\` object (immutable struct) as one of the type parameters:
> 
> \`\`\`julia
> struct StaticDimensions{R,D\<:AbstractDimensions{R},dim} \<: AbstractStaticDimensions{R,D,dim}
> # (No fields)
> 
> StaticDimensions(d::AbstractDimensions) = new{eltype(d),typeof(d),d}()
> end
> \`\`\`
> 
> Promotion rules work as you'd expect with Unitful:
> 
> \`\`\`julia
> julia\> using DynamicQuantities; using DynamicQuantities: StaticDimensions
> 
> julia\> x = Quantity{Float64,StaticDimensions}.(\[1.0u"m", 1.0u"km", 10us"Constants.Mpc"\])
> 3-element Vector{Quantity{Float64, StaticDimensions{FixedRational{Int32, 25200}, Dimensions{FixedRational{Int32, 25200}}, m}}}:
> 1.0 m
> 1000.0 m
> 3.085677581491367e23 m
> \`\`\`
> 
> Where you can see it correctly promotes the dimensions (even symbolic dimensions).
> 
> We can see that units can be inferred:
> 
> \`\`\`julia
> julia\> x = Quantity{Float64,StaticDimensions}(10u"km")
> 10000.0 m
> 
> julia\> y = Quantity{Float64,StaticDimensions}(1u"h")
> 3600.0 s
> 
> julia\> @inferred x / y
> 2.7777777777777777 m s⁻¹
> 
> julia\> typeof(x / y)
> Quantity{Float64, StaticDimensions{FixedRational{Int32, 25200}, Dimensions{FixedRational{Int32, 25200}}, m s⁻¹}}
> \`\`\`
> 
> (See the \`m s⁻¹\` at the end of the type!)
> 
> \---
> 
> If anyone is interested, would love help in making this infer as well as Unitful. It then would make it really easy to switch between static and dynamic quantities – \`Dimensions\` for initial work, and then \`StaticDimensions\` whenever you are faced with optimizing a fast SIMD loop.
> 
> Any of @yingboma @chrisrackauckas @devmotion @aplavin, would you be up to help with improving this? I think the basics are ready, we would just need to ensure that inference is as good as Unitful.

Obviously it seems to be against the main use-case of the package, but it turned out to be very simple to add this, as you can just rely on the regular `AbstractDimensions` operations, but in typed space.

For example:

```julia
julia> using DynamicQuantities; using DynamicQuantities: StaticDimensions

julia> x = Quantity{Float64,StaticDimensions}(10u"km")
10000.0 m

julia> y = Quantity{Float64,StaticDimensions}(1u"h")
3600.0 s

julia> @inferred x / y
2.7777777777777777 m s⁻¹

julia> typeof(x / y)
Quantity{Float64, StaticDimensions(m s⁻¹::Dimensions{FixedRational{Int32, 25200}})}

```

So you can see it actually infers the `m s⁻¹`, just like Unitful.jl would.

It’s probably slow and not as good at inference as Unitful.jl right now. Which is why I would love people’s help in optimizing this!

Once it’s done, it should be very easy to switch back and forth between statically- and dynamically-typed units.

---

<div class="post-metadata">

**Author:** ![RGonTheNoble](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rgonthenoble/32/209422_2.png) [@RGonTheNoble](https://discourse.julialang.org/u/RGonTheNoble)\
**Post date:** [May 22, 2024, 4:03pm UTC](https://discourse.julialang.org/t/dynamicquantities-jl-v0-7-0-efficient-and-type-stable-physical-quantities/103709/46 "2024-05-22T16:03:06Z")

</div>

Is there any way we could register units (or even symbolic units) that have offsets, such as

```julia
@register_unit °C 1.0u"K" + 273.15

```

or

```julia
@register_unit kPag 1.0u"kPa" + 101.3

```

If I have this, I can efficiently store and apply arbitrary units in a vector/dictionary to map to any arbitrary process measurement (because a lot of process units have offsets, especially pressure and temperature).

---

<div class="post-metadata">

**Author:** ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)\
**Post date:** [May 22, 2024, 7:29pm UTC](https://discourse.julialang.org/t/dynamicquantities-jl-v0-7-0-efficient-and-type-stable-physical-quantities/103709/47 "2024-05-22T19:29:01Z")

</div>

There are no offset units, sorry. I think the maintenance burden of offset units is too high relative to the demand (you are the first to ask) for me to consider putting time into it, as it would require rewriting a lot of stuff. At its core the package is still very simple and lightweight and I’d like to keep it this way to avoid the implementation complexity of Unitful.

But do check out [Examples · DynamicQuantities.jl](https://symbolicml.org/DynamicQuantities.jl/dev/examples/#3.-Using-dimensional-angles) for writing custom dimensions, that might be an option for you.

---

<div class="post-metadata">

**Author:** ![RGonTheNoble](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rgonthenoble/32/209422_2.png) [@RGonTheNoble](https://discourse.julialang.org/u/RGonTheNoble)\
**Post date:** [May 22, 2024, 8:20pm UTC](https://discourse.julialang.org/t/dynamicquantities-jl-v0-7-0-efficient-and-type-stable-physical-quantities/103709/48 "2024-05-22T20:20:52Z")

</div>

I definitely wouldn’t recommend having offset units as part of a core type to do actual math on, because it messes up division and even multiplication when not differenced. But would it be hard to extend something like SymbolicUnits to represent something that gets converted to the main Quantity type upon creation and then convert back if desired as output?

---

<div class="post-metadata">

**Author:** ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)\
**Post date:** [August 27, 2024, 3:21pm UTC](https://discourse.julialang.org/t/dynamicquantities-jl-v0-7-0-efficient-and-type-stable-physical-quantities/103709/49 "2024-08-27T15:21:50Z")

</div>

Happy to share that DynamicQuantities.jl v1.0.0 is now released! This release is identical to 0.14.3. The v1 simply indicates I consider the API stable.

[Previous page](https://discourse.julialang.org/t/dynamicquantities-jl-v0-7-0-efficient-and-type-stable-physical-quantities/103709.md?page=2)
