Skip to content

Commit d868579

Browse files
authored
Merge branch 'abstract-types-2' into constants
2 parents b8f3af3 + a365da0 commit d868579

File tree

8 files changed

+80
-17
lines changed

8 files changed

+80
-17
lines changed

.github/workflows/CI.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ jobs:
3737
run: |
3838
julia --color=yes --project=. -e 'import Pkg; Pkg.add("Coverage")'
3939
julia --color=yes --threads=auto --check-bounds=yes --depwarn=yes --code-coverage=user --project=. -e 'import Pkg; Pkg.test(coverage=true)'
40+
DQ_TEST_UPREFERRED=true julia --color=yes --threads=auto --check-bounds=yes --depwarn=yes --code-coverage=user --project=. -e 'import Pkg; Pkg.test(coverage=true)'
4041
julia --color=yes --project=. coverage.jl
4142
- name: "Coveralls"
4243
uses: coverallsapp/github-action@v2

ext/DynamicQuantitiesUnitfulExt.jl

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,28 @@ else
1010
import ..Unitful: @u_str
1111
end
1212

13-
# This lets the user override the preferred units:
14-
function unitful_equivalences()
15-
si_units = (length=u"m", mass=u"kg", time=u"s", current=u"A", temperature=u"K", luminosity=u"cd", amount=u"mol")
13+
function get_si_units()
14+
return (length=u"m", mass=u"kg", time=u"s", current=u"A", temperature=u"K", luminosity=u"cd", amount=u"mol")
15+
end
16+
17+
function validate_upreferred()
18+
si_units = get_si_units()
1619
for k in keys(si_units)
1720
if Unitful.upreferred(si_units[k]) !== si_units[k]
1821
error("Found custom `Unitful.preferunits`. This is not supported when interfacing Unitful and DynamicQuantities: you must leave the default `Unitful.upreferred`, which is the SI base units.")
1922
end
2023
end
24+
return true
25+
end
26+
27+
function unitful_equivalences()
28+
si_units = get_si_units()
2129
return NamedTuple((k => si_units[k] for k in keys(si_units)))
2230
end
2331

2432
Base.convert(::Type{Unitful.Quantity}, x::DynamicQuantities.Quantity) =
2533
let
34+
validate_upreferred()
2635
cumulator = DynamicQuantities.ustrip(x)
2736
dims = DynamicQuantities.dimension(x)
2837
equiv = unitful_equivalences()
@@ -42,9 +51,10 @@ Base.convert(::Type{DynamicQuantities.Quantity{T,D}}, x::Unitful.Quantity) where
4251
return DynamicQuantities.Quantity(convert(T, value), dimension)
4352
end
4453

45-
Base.convert(::Type{DynamicQuantities.Dimensions}, d::Unitful.Dimensions) = convert(DynamicQuantities.Dimensions{DynamicQuantities.DEFAULT_DIM_TYPE}, d)
54+
Base.convert(::Type{DynamicQuantities.Dimensions}, d::Unitful.Dimensions) = convert(DynamicQuantities.DEFAULT_DIM_TYPE, d)
4655
Base.convert(::Type{DynamicQuantities.Dimensions{R}}, d::Unitful.Dimensions{D}) where {R,D} =
4756
let
57+
validate_upreferred()
4858
cumulator = DynamicQuantities.Dimensions{R}()
4959
for dim in D
5060
dim_symbol = _map_dim_name_to_dynamic_units(typeof(dim))

src/math.jl

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,15 @@ Base.:+(l, r::AbstractQuantity) = iszero(dimension(r)) ? new_quantity(typeof(r),
2525
Base.:-(l::AbstractQuantity, r) = l + (-r)
2626
Base.:-(l, r::AbstractQuantity) = l + (-r)
2727

28-
_pow(l::AbstractDimensions, r) = map_dimensions(Base.Fix1(*, r), l)
29-
_pow(l::AbstractQuantity{T}, r) where {T} = new_quantity(typeof(l), ustrip(l)^r, _pow(dimension(l), r))
30-
_pow_as_T(l::AbstractQuantity{T}, r) where {T} = new_quantity(typeof(l), ustrip(l)^convert(T, r), _pow(l.dimensions, r))
31-
Base.:^(l::AbstractDimensions{R}, r::Integer) where {R} = _pow(l, r)
28+
# We don't promote on the dimension types:
29+
_pow(l::AbstractDimensions{R}, r::R) where {R} = map_dimensions(Base.Fix1(*, r), l)
3230
Base.:^(l::AbstractDimensions{R}, r::Number) where {R} = _pow(l, tryrationalize(R, r))
33-
Base.:^(l::AbstractQuantity{T,D}, r::Integer) where {T,R,D<:AbstractDimensions{R}} = _pow(l, r)
34-
Base.:^(l::AbstractQuantity{T,D}, r::Number) where {T,R,D<:AbstractDimensions{R}} = _pow_as_T(l, tryrationalize(R, r))
31+
Base.:^(l::AbstractQuantity{T,D}, r::Integer) where {T,R,D<:AbstractDimensions{R}} = new_quantity(typeof(l), ustrip(l)^r, dimension(l)^r)
32+
Base.:^(l::AbstractQuantity{T,D}, r::Number) where {T,R,D<:AbstractDimensions{R}} =
33+
let dim_pow = tryrationalize(R, r), val_pow = convert(T, dim_pow)
34+
# Need to ensure we take the numerical power by the rationalized quantity:
35+
return new_quantity(typeof(l), ustrip(l)^val_pow, dimension(l)^dim_pow)
36+
end
3537

3638
Base.inv(d::AbstractDimensions) = map_dimensions(-, d)
3739
Base.inv(q::AbstractQuantity) = new_quantity(typeof(q), inv(ustrip(q)), inv(dimension(q)))

src/utils.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ Base.show(io::IO, d::AbstractDimensions) =
8484
s = replace(s, r"\s*$" => "")
8585
print(io, s)
8686
end
87-
Base.show(io::IO, q::AbstractQuantity) = print(io, ustrip(q), " ", dimension(q))
87+
Base.show(io::IO, q::AbstractQuantity{<:Real}) = print(io, ustrip(q), " ", dimension(q))
88+
Base.show(io::IO, q::AbstractQuantity) = print(io, "(", ustrip(q), ") ", dimension(q))
8889

8990
function dimension_name(::AbstractDimensions, k::Symbol)
9091
default_dimensions = (length="m", mass="kg", time="s", current="A", temperature="K", luminosity="cd", amount="mol")

test/runtests.jl

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,15 @@ import Ratios: SimpleRatio
55
@eval Base.round(T, x::SimpleRatio) = round(T, x.num // x.den)
66
end
77

8-
@safetestset "Unitful.jl integration tests" begin
9-
include("test_unitful.jl")
10-
end
11-
12-
@safetestset "Unit tests" begin
13-
include("unittests.jl")
8+
if parse(Bool, get(ENV, "DQ_TEST_UPREFERRED", "false"))
9+
@safetestset "Test upreferred disallowed" begin
10+
include("test_ban_upreferred.jl")
11+
end
12+
else
13+
@safetestset "Unitful.jl integration tests" begin
14+
include("test_unitful.jl")
15+
end
16+
@safetestset "Unit tests" begin
17+
include("unittests.jl")
18+
end
1419
end

test/test_ban_upreferred.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
### These are tests we need to run with a fresh Julia runtime
2+
3+
import Unitful
4+
import Unitful: @u_str
5+
Unitful.preferunits(u"km")
6+
using Test
7+
import DynamicQuantities
8+
9+
x_unitful = 1.5u"km"
10+
x_dq = DynamicQuantities.Quantity(1500.0, length=1)
11+
12+
@test_throws ErrorException convert(DynamicQuantities.Quantity, x_unitful)
13+
@test_throws ErrorException convert(Unitful.Quantity, x_dq)

test/test_unitful.jl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,19 @@ for T in [DEFAULT_VALUE_TYPE, Float16, Float32, Float64], R in [DEFAULT_DIM_BASE
2222

2323
@test isapprox(convert(DynamicQuantities.Quantity{T,D}, x_unitful), x; atol=1e-6)
2424
@test risapprox(convert(Unitful.Quantity, convert(DynamicQuantities.Quantity{T,D}, x_unitful)), Unitful.upreferred(x_unitful); atol=1e-6)
25+
26+
@test typeof(convert(DynamicQuantities.Dimensions, Unitful.dimension(x_unitful))) == DynamicQuantities.Dimensions{DEFAULT_DIM_BASE_TYPE}
27+
end
28+
29+
module MyScaleUnit
30+
using Unitful
31+
@dimension(𝐒, "𝐒", Scale)
32+
@refunit(scale, "scale", Scale, 𝐒, false)
2533
end
34+
35+
Unitful.register(MyScaleUnit)
36+
37+
x = 1.0u"scale"
38+
@test typeof(x) <: Unitful.Quantity{Float64, MyScaleUnit.𝐒}
39+
@test_throws ErrorException convert(DynamicQuantities.Quantity, x)
40+
# These are not supported because there is no SI equivalency

test/unittests.jl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,15 @@ using Test
129129
@test abs(x) == abs(Quantity(1.2, length=2 // 5))
130130
end
131131

132+
@testset "Complex numbers" begin
133+
x = (0.5 + 0.5im) * u"km/s"
134+
@test string(x) == "(500.0 + 500.0im) m s⁻¹"
135+
@test typeof(x) == Quantity{Complex{Float64}, DEFAULT_DIM_TYPE}
136+
@test typeof(x^2) == Quantity{Complex{Float64}, DEFAULT_DIM_TYPE}
137+
@test x^2/u"km/s"^2 == Quantity(0.5im)
138+
@test x^2.5 (-5.088059320440205e6 + 1.2283661817565577e7im) * u"m^(5/2) * s^(-5/2)"
139+
end
140+
132141
@testset "Fallbacks" begin
133142
@test ustrip(0.5) == 0.5
134143
@test ustrip(ones(32)) == ones(32)
@@ -272,6 +281,13 @@ end
272281
@test typeof(convert(Quantity{Float16}, q)) == Quantity{Float16,Dimensions{Rational{Int16}}}
273282
@test convert(Quantity, q) === q
274283

284+
# Test that regular type promotion applies:
285+
q = Quantity(2, d)
286+
@test typeof(q) == Quantity{Int64,typeof(d)}
287+
@test typeof(q ^ 2) == Quantity{Int64,typeof(d)}
288+
@test typeof(0.5 * q) == Quantity{Float64,typeof(d)}
289+
@test typeof(inv(q)) == Quantity{Float64,typeof(d)}
290+
275291
# Automatic conversions via constructor:
276292
for T in [Float16, Float32, Float64, BigFloat], R in [DEFAULT_DIM_BASE_TYPE, Rational{Int16}, Rational{Int32}, SimpleRatio{Int}, SimpleRatio{SafeInt16}]
277293
D = Dimensions{R}

0 commit comments

Comments
 (0)