Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion docs/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ with quantities are `ustrip` and `dimension`:
```@docs
ustrip
dimension
unit
isunitless
isdimensionless
upreferred
```

## Accessing dimensions
Expand All @@ -25,7 +29,7 @@ uamount
```@autodocs
Modules = [DynamicQuantities]
Pages = ["utils.jl"]
Filter = t -> !(t in [ustrip, dimension, ulength, umass, utime, ucurrent, utemperature, uluminosity, uamount])
Filter = t -> !(t in [ustrip, dimension, unit, isunitless, isdimensionless, upreferred, ulength, umass, utime, ucurrent, utemperature, uluminosity, uamount])
```

## Internals
Expand Down
2 changes: 1 addition & 1 deletion src/DynamicQuantities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export AbstractDimensions, Dimensions, NoDims
export AbstractSymbolicDimensions, SymbolicDimensions, SymbolicDimensionsSingleton
export QuantityArray
export DimensionError
export ustrip, dimension, uexpand, uconvert, ustripexpand
export ustrip, dimension, unit, isunitless, isdimensionless, upreferred, uexpand, uconvert, ustripexpand
export ulength, umass, utime, ucurrent, utemperature, uluminosity, uamount
export uparse, @u_str, sym_uparse, @us_str, @register_unit, aff_uparse, @ua_str

Expand Down
4 changes: 2 additions & 2 deletions src/symbolic_dimensions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ module SymbolicUnits

const SYMBOLIC_UNIT_VALUES = WriteOnceReadMany{Vector{DEFAULT_SYMBOLIC_QUANTITY_TYPE}}()

function update_symbolic_unit_values!(unit, symbolic_unit_values = SYMBOLIC_UNIT_VALUES)
@unstable function update_symbolic_unit_values!(unit, symbolic_unit_values = SYMBOLIC_UNIT_VALUES)
@eval begin
const $unit = constructorof(DEFAULT_SYMBOLIC_QUANTITY_TYPE)(
DEFAULT_VALUE_TYPE(1.0),
Expand All @@ -456,7 +456,7 @@ module SymbolicUnits
end
end

update_symbolic_unit_values!(w::WriteOnceReadMany) = update_symbolic_unit_values!.(w._raw_data)
@unstable update_symbolic_unit_values!(w::WriteOnceReadMany) = update_symbolic_unit_values!.(w._raw_data)
update_symbolic_unit_values!(UNIT_SYMBOLS)

# Non-eval version of `update_symbolic_unit_values!` for registering units in
Expand Down
24 changes: 21 additions & 3 deletions src/types.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using DispatchDoctor: @unstable

@static if VERSION <= v"1.11.0-"
@eval using Tricks: static_fieldnames
else
Expand Down Expand Up @@ -154,6 +156,22 @@ Base.getproperty(::NoDims{R}, ::Symbol) where {R} = zero(R)

const DEFAULT_DIMENSIONLESS_TYPE = NoDims{DEFAULT_DIM_BASE_TYPE}

"""
dimensionless

A dimensionless (unitless) quantity.

This is the default value returned by `dimension(::Number)`.
"""
const dimensionless = DEFAULT_DIMENSIONLESS_TYPE()

"""
NoUnits

Alias of [`dimensionless`](@ref), provided for Unitful.jl compatibility.
"""
const NoUnits = dimensionless

"""
Quantity{T<:Number,D<:AbstractDimensions} <: AbstractQuantity{T,D} <: Number

Expand Down Expand Up @@ -294,13 +312,13 @@ function with_type_parameters(::Type{<:RealQuantity}, ::Type{T}, ::Type{D}) wher
end

# The following functions should be overloaded for special types
function constructorof(::Type{T}) where {T<:Union{UnionAbstractQuantity,AbstractDimensions}}
@unstable function constructorof(::Type{T}) where {T<:Union{UnionAbstractQuantity,AbstractDimensions}}
return Base.typename(T).wrapper
end
function with_type_parameters(::Type{D}, ::Type{R}) where {D<:AbstractDimensions,R}
@unstable function with_type_parameters(::Type{D}, ::Type{R}) where {D<:AbstractDimensions,R}
return constructorof(D){R}
end
function with_type_parameters(::Type{Q}, ::Type{T}, ::Type{D}) where {Q<:UnionAbstractQuantity,T,D}
@unstable function with_type_parameters(::Type{Q}, ::Type{T}, ::Type{D}) where {Q<:UnionAbstractQuantity,T,D}
return constructorof(Q){T,D}
end

Expand Down
37 changes: 37 additions & 0 deletions src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,7 @@ julia> ustrip.(u"km", [1000u"m", 2000u"m"])
dimension(unit) == dimension(q) || throw(DimensionError(unit, q))
return ustrip(q) / ustrip(unit)
end
@inline ustrip(::Type{T}, unit::UnionAbstractQuantity, q::UnionAbstractQuantity) where {T} = convert(T, ustrip(unit, q))

"""
dimension(q::AbstractQuantity)
Expand All @@ -426,6 +427,42 @@ dimension(d::AbstractDimensions) = d
dimension(aq::AbstractArray{<:UnionAbstractQuantity}) = allequal(dimension.(aq)) ? dimension(first(aq)) : throw(DimensionError(aq[begin], aq[begin+1:end]))
dimension(_) = DEFAULT_DIMENSIONLESS_TYPE()

"""
isunitless(x)

Return `true` if `x` has no dimensions.

!!! note
Like Unitful.jl, `isunitless` is only defined for scalars. For arrays, use broadcasting:
`isunitless.(x)`.

See also [`isdimensionless`](@ref).
Comment on lines +434 to +439
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MilesCranmerBot not needed

"""
isunitless(x::Number) = iszero(dimension(x))
isunitless(x::AbstractGenericQuantity) = iszero(dimension(x))
isunitless(d::AbstractDimensions) = iszero(d)

"""
isdimensionless(x)

Alias for [`isunitless`](@ref).
"""
isdimensionless(x) = isunitless(x)

"""
unit(q)

Return the multiplicative unit quantity associated with `q`.
"""
unit(t::T) where {T} = oneunit(t)

"""
upreferred(x)

Compatibility no-op for Unitful.jl-like APIs.
"""
upreferred(x) = x

"""
ulength(q::AbstractQuantity)
ulength(q::AbstractGenericQuantity)
Expand Down
32 changes: 32 additions & 0 deletions test/unittests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,34 @@ end
@test ustrip(z) ≈ 60 * 60 * 24 * 365.25
@test z == uparse("yr")

@testset "Unitful-like API compatibility shims" begin
q = 3.0u"m"

@test isunitless(1.0)
@test isdimensionless(1.0)
@test !isunitless(q)
@test !isdimensionless(q)

# Match Unitful.jl: `isunitless`/`isdimensionless` are scalar predicates.
# For arrays, use broadcasting: `isunitless.(x)`.
@test_throws MethodError isunitless([1.0u"1", 2.0u"1"])
@test_throws MethodError isunitless([1.0u"m", 2.0u"m"])
@test_throws MethodError isunitless([1.0u"m", 2.0u"s"])
@test_throws MethodError isdimensionless([1.0u"m", 2.0u"s"])

@test unit(q) == 1.0u"m"
@test isunitless(unit(1))
@test isunitless(unit(Float64))
@test ustrip(unit(1)) == 1.0
@test ustrip(unit(Float64)) == 1.0

@test upreferred(q) === q
@test upreferred(2.0) == 2.0

@test uparse("m") == u"m"
@test uparse("km") == u"km"
end

# Test type stability of extreme range of units
@test typeof(u"1") == DEFAULT_QUANTITY_TYPE
@test typeof(u"1f0") == DEFAULT_QUANTITY_TYPE
Expand Down Expand Up @@ -1077,6 +1105,10 @@ end
@test ustrip(u"s", 1u"minute") == 60.0
@test ustrip(u"minute", 60u"s") == 1.0

# Typed conversions
@test ustrip(Float32, u"m", 3.0u"m") === Float32(3.0)
@test ustrip(Float64, u"km", 2500.0u"m") === 2.5

# Arrays
m_qarray = QuantityArray([1000.0, 2000.0], u"m")
@test ustrip(u"km", m_qarray) == [1.0, 2.0]
Expand Down