Some more updates as of v0.10
New typing system
There are now multiple quantity types which act identically, but let you work with different subtypes:
Quantity <: AbstractQuantity <: Number
- The default quantity returned by
0.5u"m/s"
, which is subtyped toNumber
.
- The default quantity returned by
GenericQuantity <: AbstractGenericQuantity <: Any
- Do you need to put units on some non-numerical type? Then you want
GenericQuantity
- Do you need to put units on some non-numerical type? Then you want
RealQuantity <: AbstractRealQuantity <: Real
- Many packages throughout Julia require
Real
input. Although physical quantities are not technically real, it can still be useful for compatibility to pass them as if they were.RealQuantity
lets you do that.
- Many packages throughout Julia require
- All three of these are of course compatible with
QuantityArray
, and can take anyAbstractDimensions
to store the dimensions.
julia> x = 0.5u"m/s"
0.5 m s⁻¹
julia> x_re = RealQuantity(x)::Real
0.5 m s⁻¹
julia> y_gen = GenericQuantity("hello", length=1, time=-1)
(hello) m s⁻¹
We also get automatic promotion:
julia> x = RealQuantity(0.5u"m/s")
0.5 m s⁻¹
julia> y = x * (1 + 2im)
(0.5 + 1.0im) m s⁻¹
julia> typeof(y)
Quantity{ComplexF64, Dimensions{DynamicQuantities.FixedRational{Int32, 25200}}}
the behavior of which can be overloaded for any custom hierarchy of quantity types.
Thanks to Guarav Arya for his help on this.
New methods
Many other numerical methods which require dimensionless numbers, such as exp
or sin
, can now take a quantity as input (required because DynamicQuantities.Quantity
can be dimensionless - it doesn’t automatically convert to Float64
due to the potential for type instability).
The overloaded method for dimensionless functions will simply check if the quantity is dimensionless or not, and then evaluate:
julia> x = 0.5u"m"
0.5 m
julia> y = 10u"Constants.Mpc"
3.085677581491367e23 m
julia> exp(x/y)
1.0
This means we can also have ranges of quantities now (via Base.rem
)
julia> for x in 0u"km":1.5u"km":10u"km"
@show x^2
end
x ^ 2 = 0.0 m²
x ^ 2 = 2.25e6 m²
x ^ 2 = 9.0e6 m²
x ^ 2 = 2.025e7 m²
x ^ 2 = 3.6e7 m²
x ^ 2 = 5.625e7 m²
x ^ 2 = 8.1e7 m²
(Although this should probably use a QuantityArray
… PRs always appreciated )
Better perf
QuantityArray performance seems pretty solid now, and lets you wrap arrays of arbitrary data with quantities via GenericQuantity
. Here’s summation on a regular array compared to an array of physical quantities:
julia> struct Coords
x::Float64
y::Float64
end
julia> Base.:+(a::Coords, b::Coords) = Coords(a.x+b.x, a.y+b.y)
julia> Base.:*(a::Coords, b::Number) = Coords(a.x*b, a.y*b)
julia> Base.:*(a::Number, b::Coords) = Coords(a*b.x, a*b.y)
julia> @btime(
sum(array),
setup=(N=1000; array=[Coords(rand(), rand()) for i=1:N]
)
828.000 ns (0 allocations: 0 bytes)
Coords(509.5939555433635, 494.56896926222123)
julia> @btime(
sum(array),
setup=(N=1000; array=QuantityArray([GenericQuantity(Coords(rand(), rand()), dimension(u"m/s")) for i=1:N])
)
843.750 ns (0 allocations: 0 bytes)
(Coords(504.7718424677944, 482.0449323023619)) m s⁻¹
Thanks to all the contributors thus far