# FlexUnits.jl The flexible units package

**URL:** https://discourse.julialang.org/t/flexunits-jl-the-flexible-units-package/132528
**Category:** Package Announcements
**Tags:** package, announcement, unitful
**Created:** [September 20, 2025, 6:42pm UTC](https://discourse.julialang.org/t/flexunits-jl-the-flexible-units-package/132528 "2025-09-20T18:42:16Z")
**Posts on this page:** 6
**Page:** 1

<div class="post-metadata">

### Author: ![Deduction42](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/deduction42/32/9206_2.png) [@Deduction42](https://discourse.julialang.org/u/Deduction42)
#### Post date: [September 20, 2025, 6:42pm UTC](https://discourse.julialang.org/t/flexunits-jl-the-flexible-units-package/132528/1 "2025-09-20T18:42:16Z")

</div>

# [FlexUnits.jl](https://github.com/Deduction42/FlexUnits.jl)

[![Aqua QA](https://global.discourse-cdn.com/julialang/original/3X/e/4/e4b7e78f38090b8dc2b392b3788d3a74cd09d727.svg)](https://github.com/JuliaTesting/Aqua.jl)  
[![Build Status](https://global.discourse-cdn.com/julialang/original/3X/9/c/9c5043a4204370d027b108e6b8348f2f60107e7a.svg)](https://github.com/Deduction42/FlexUnits.jl/actions/workflows/CI.yml?query=branch%3Amain)  
[![Coverage Status](https://global.discourse-cdn.com/julialang/original/3X/f/a/faf7ffb2d354f4e7e5dc56649fac90ca6a752220.svg)](https://coveralls.io/github/Deduction42/FlexUnits.jl?branch=main)

A units package that uses a very similar mechanism for unit tracking as DynamicQuantities.jl, but has an implementation that is more similar to Unitful.jl. The main difference comes from design decisions centered around flexibility and ambiguity avoidance, which are issues that sometimes come up when I build industrial solutions with existing units packages.

The main design choice that enables flexibility comes from its implementation which is similar to DynamicQuantities.jl, where the fundamental building block is a `Dimension` object that tracks the (SI) dimensions of the unit. In this package, one level above a `Dimension` is an `AffineUnit` which adds a scale and an offset and can represent all commonly used units (except logscale units like decibels).

Another core design choice is to eagerly convert all values to SI units when applying mathematical operations. This is both for simplicity and performance, as all dimensions only have one SI unit representation (simplicity) and no conversion factors need to occur between units (performance). This also means that if a single quantity is used in multiple calculations, a performance boost can be achieved by calling `x_si = ubase(x)` and using that value, as conversions don’t need to be repeated. Results can always be converted back to desired units, and if the conversion fails due to a dimension mismatch, a helpful error is given with respect to the dimensional difference.

Another degree of flexibility is achieved in how it is unopinionated with respect to unit registries; you can easily define your own registry and use it for macros as opposed to the `.UnitRegistry` module (the original UnitRegistry module is only ~30 lines of code). This can be helpful if there is a conflict in the symbolic unit representation (which can happen when applying a single solution codebase to multiple clients). Because of this, the default registry isn’t automatically exported. In order to use it, UnitRegistry must be explicitly imported in order to use the common string macros:

```julia-auto
using FlexUnits.jl, .UnitRegistry

```

Finally, all `Quantity` instances are generic and do not subtype into `Real` or even `Number`. This allows me to have generic unitful behavior on custom objects or Number types without running into ambiguities.

#### Major differences between FlexUnits.jl and DynamicQuantities.jl

1. Fully supports affine units (like °C and °F) and can potentially support logarithmic units (like dB) in a separate registry
2. The string macro `u_str` and parsing function `uparse` are not automatically exported (allowing users to export their own registries)
3. Easier to build your own unit registry (allowing differnet behaviour for `u_str` and `uparse`)
4. While units are pretty-printed by default, you can enable parseable unit outputs by setting `pretty_print_units(false)` which is useful for outputting unit information in JSON
5. More closely resembles the Unitful.jl API in many ways
6. No symbolic units (everything eagerly evaluates to SI units)
7. The function `uexpand` is replaced by `ubase`
8. There is no `Quantity` type that subtypes to `Number` or `Real`. A `Quantity` in FlexUnits.jl is equivalent to a `GenericQuantity` in DynamicQuantities.jl This is a deliberate design decision to avoid ambiguities on numerical operations with user-defined types.

#### Major differences between FlexUnits.jl and Unitful.jl

1. Units are not specialized (u"m/s" returns the same concrete type as u"°C") which is much faster when units cannot be inferred
2. The string macro `u_str` and parsing function `uparse` are not automatically exported (allowing users to export their own registries)
3. Units are not dynamically tracked, quantities are displayed as though `upreferred` was called on them
4. The function `upreferred` is replaced by `ubase` which converts to raw dimensions (like SI) which are not configurable
5. Operations on affine units do not produce errors (due to automatic conversion to dimensional form). **This may may yield unituitive (but more consistent) results**.
6. Unit registries are much simpler; a registry is simply a dict of units, all of the same type, living inside a module. Custom registries inside user-defined modules are not neccessary, but are still supported.
7. `Quantity` in FlexUnits.jl does not subtype to `Number` in order to support more value types (such as a Distribution or Array)

---

<div class="post-metadata">

### Author: ![juliohm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/juliohm/32/215266_2.png) [@juliohm](https://discourse.julialang.org/u/juliohm)
#### Post date: [May 9, 2026, 1:31pm UTC](https://discourse.julialang.org/t/flexunits-jl-the-flexible-units-package/132528/2 "2026-05-09T13:31:34Z")

</div>

That is awesome @Deduction42 ! 💯 :juliaspinner:

What are your recommendations for porting packages from Unitful.jl to FlexUnits.jl?

We chose Unitful.jl because we needed to dispatch on specific quantities like

```julia
const Len{T} = Quantity{T,u"𝐋"}
const Met{T} = Quantity{T,u"𝐋",typeof(m)}
const Deg{T} = Quantity{T,NoDims,typeof(°)}
const Rad{T} = Quantity{T,NoDims,typeof(rad)}

```

Can we do something similar with FlexUnits.jl?

---

<div class="post-metadata">

### Author: ![Deduction42](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/deduction42/32/9206_2.png) [@Deduction42](https://discourse.julialang.org/u/Deduction42)
#### Post date: [May 9, 2026, 2:24pm UTC](https://discourse.julialang.org/t/flexunits-jl-the-flexible-units-package/132528/3 "2026-05-09T14:24:58Z")

</div>

I do that all the time with dimensions actually. FlexUnits provides the `@D_str` macro to specify a dimension type. However, you can’t statically specify units in FlexUnits (this maintains flexibility, prevents over-specialization, and reduces compile times), units will be automatically converted to SI fundamental units.

```julia
display_simplified_units(true)

const Pressure{T} = Quantity{T, D"psi"} #Only uses dimensions of psi, converts to Pa
const Temperature{T} = Quantity{T, D"°C"} #Only uses dimensisons of Celsius, converts to K

julia> Pressure{Float64}(5u"psi")
34473.8 Pa

julia> Temperature{Float64}(5u"°C")
278.15 K

```

Now forcing everything to be SI units seems a bit clunky, but in a lot of my projects, I’m applying the same algorithm to different customers that use different units (especially in Canada where we’re notoriously inconsistent). One customer might have pressure in kPa while others have ounces per square inch; so I don’t know units, just dimensions. This is problematic for Unitful, because in order for a type to be concrete, you need dimensions AND units. Thus any object that contains your “Len” object above will take a dynamic dispatch performance hit, but “Met” will not.

```julia-auto
julia> isconcretetype(Len{Float64})
false

julia> isconcretetype(Met{Float64})
true

```

However for the FlexUnits types, all you need in order to make the type concrete is the dimension

```julia-auto
julia> isconcretetype(Temperature{Float64})
true

```

This allows me to achieve compiled performance with less information upfront. In reality, dimensions are more important than units. Resolving dimensions takes much more computational effort than scaling and offsetting; moreover, if you’re using base SI units, you don’t have to scale or offset anything. If you know the dimension, you can get any unit you want.

---

<div class="post-metadata">

### Author: ![juliohm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/juliohm/32/215266_2.png) [@juliohm](https://discourse.julialang.org/u/juliohm)
#### Post date: [May 9, 2026, 2:36pm UTC](https://discourse.julialang.org/t/flexunits-jl-the-flexible-units-package/132528/4 "2026-05-09T14:36:49Z")

</div>

It makes a lot of sense to dispatch on dimensions only. I will explore this idea.

Other than that, I think we only rely on `ustrip` and `unit`, which I assume exist in FlexUnits.jl as well. If you would like to help with PRs, CoordRefSystems.jl and Meshes.jl are the most challenging candidates I have in mind.

Let’s see if the port Unitful.jl → FlexUnits.jl brings some improvements 🙂

---

<div class="post-metadata">

### Author: ![Deduction42](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/deduction42/32/9206_2.png) [@Deduction42](https://discourse.julialang.org/u/Deduction42)
#### Post date: [May 9, 2026, 5:10pm UTC](https://discourse.julialang.org/t/flexunits-jl-the-flexible-units-package/132528/5 "2026-05-09T17:10:34Z")

</div>

FlexUnits tries to match the Unitful API as much as possible. It includes `ustrip` and `units`, and you can use them in similar manners. The main difference is object types:

- Quantity{T,U}, where U can be a dimension or a unit (AbstractUnitLike)
- Units{D,T} where D is a dimension and T is a transform (usually AffineTransform{Float64}) the transform is a callable object that converts the units to SI base units (the transform is under the “tobase” field, and can be accessed via the `tobase` function). When converting back to units, the inverse of the transform is called. Calling `FlexUnits.tobase` on a Dimension produces a `NoTransform`. Logarithmic units have an `ExpAffTransform`.
- Dimensions{FixRat32} is a dynamic dimension, you can use Quantity{T,Dimensions{FixRat32}} if you don’t know the dimensions beforehand. This is useful for matrices or dictionaries that have heterogeneous units.

Most operations give you dimensional quantities as output `Quantity{T, <:AbstractDimLike}`. Quantities with units usually pop up when you use `uconvert`. Also, make sure you use `using .UnitRegistry` to enable string macros and `uparse` etc.

---

<div class="post-metadata">

### Author: ![Deduction42](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/deduction42/32/9206_2.png) [@Deduction42](https://discourse.julialang.org/u/Deduction42)
#### Post date: [May 12, 2026, 2:36pm UTC](https://discourse.julialang.org/t/flexunits-jl-the-flexible-units-package/132528/6 "2026-05-12T14:36:49Z")

</div>

Let me know if you’re having any problems with your port. I have some more examples with dispatch patterns [up in the documentation](https://deduction42.github.io/FlexUnits.jl/stable/dispatch/) now. There’s also a differential equation solver example in advanced examples that uses a clever dispatch pattern to make a vector that is unitful when fields are accessed but produces raw numerical values when indexed by number (so that the solver doesn’t require knowing units).
