-
Notifications
You must be signed in to change notification settings - Fork 123
Expand file tree
/
Copy pathtypes.jl
More file actions
300 lines (258 loc) · 10.6 KB
/
types.jl
File metadata and controls
300 lines (258 loc) · 10.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
"""
abstract type Unitlike end
Represents units or dimensions. Dimensions are unit-like in the sense that they are
not numbers but you can multiply or divide them and exponentiate by rationals.
"""
abstract type Unitlike end
"""
struct Dimension{D}
power::Rational{Int}
end
Description of a dimension. The name of the dimension `D` is a symbol, e.g.
`:Length`, `:Time`, `:Mass`, etc.
`Dimension{D}` objects are collected in a tuple, which is used for the type
parameter `N` of a [`Dimensions{N}`](@ref) object.
"""
struct Dimension{D}
power::Rational{Int}
end
@inline name(x::Dimension{D}) where {D} = D
@inline power(x::Dimension) = x.power
"""
struct Dimensions{N} <: Unitlike
Instances of this object represent dimensions, possibly combinations thereof.
"""
struct Dimensions{N} <: Unitlike end
const NoDims = Dimensions{()}()
"""
struct Unit{U,D}
tens::Int
power::Rational{Int}
end
Description of a physical unit, including powers-of-ten prefixes and powers of
the unit. The name of the unit is encoded in the type parameter `U` as a symbol,
e.g. `:Meter`, `:Second`, `:Gram`, etc. The type parameter `D` is a [`Dimensions{N}`](@ref)
object, for instance `Unit{:Meter, 𝐋}` or `Unit{:Liter, 𝐋^3}`. Note that the dimension
information refers to the unit, not powers of the unit.
`Unit{U,D}` objects are almost never explicitly manipulated by the user. They
are collected in a tuple, which is used for the type parameter `N` of a
[`Units{N,D,A}`](@ref) object.
"""
struct Unit{U,D}
tens::Int
power::Rational{Int}
end
@inline name(x::Unit{U}) where {U} = U
@inline tens(x::Unit) = x.tens
@inline power(x::Unit) = x.power
"""
dimension(x::Unit)
Returns a [`Unitful.Dimensions`](@ref) object describing the given unit `x`.
"""
@inline dimension(u::Unit{U,D}) where {U,D} = D^u.power
struct Affine{T} end
"""
abstract type Units{N,D,A} <: Unitlike end
Abstract supertype of all units objects, which can differ in their implementation details.
`A` is a translation for affine quantities; for non-affine quantities it is `nothing`.
"""
abstract type Units{N,D,A} <: Unitlike end
affinetranslation(::Units{N,D,Affine{T}}) where {N,D,T} = T
affinetranslation(::Units{N,D,nothing}) where {N,D} = false
"""
genericunit(::Units)
Given e.g. a `FreeUnits{N,D,A}`, `ContextUnits{N,D,P,A}`, or `FixedUnits{N,D,A}` object,
return the type `Units{N,D,A}`.
"""
genericunit(::Units{N,D,A}) where {N,D,A} = Units{N,D,A}
"""
struct FreeUnits{N,D,A} <: Units{N,D,A}
Instances of this object represent units, possibly combinations thereof. These behave like
units have behaved in previous versions of Unitful, and provide a basic level of
functionality that should be acceptable to most users. See
[Basic promotion mechanisms](@ref) in the docs for details.
Example: the unit `m` is actually a singleton of type
`Unitful.FreeUnits{(Unitful.Unit{:Meter, 𝐋}(0, 1//1),), 𝐋, nothing}`.
After dividing by `s`, a singleton of type
`Unitful.FreeUnits{(Unitful.Unit{:Meter, 𝐋}(0, 1//1), Unitful.Unit{:Second, 𝐓}(0, -1//1)), 𝐋/𝐓, nothing}` is returned.
"""
struct FreeUnits{N,D,A} <: Units{N,D,A} end
FreeUnits{N,D}() where {N,D} = FreeUnits{N,D,nothing}()
FreeUnits(::Units{N,D,A}) where {N,D,A} = FreeUnits{N,D,A}()
const NoUnits = FreeUnits{(), NoDims}()
(y::FreeUnits)(x) = uconvert(y,x)
"""
struct ContextUnits{N,D,P,A} <: Units{N,D,A}
Instances of this object represent units, possibly combinations thereof.
It is in most respects like `FreeUnits{N,D,A}`, except that the type parameter `P` is
again a `FreeUnits{M,D}` type that specifies a preferred unit for promotion.
See [Advanced promotion mechanisms](@ref) in the docs for details.
"""
struct ContextUnits{N,D,P,A} <: Units{N,D,A} end
function ContextUnits(x::Units{N,D,A}, y::Units) where {N,D,A}
D !== dimension(y) && throw(DimensionError(x,y))
ContextUnits{N,D,typeof(FreeUnits(y)),A}()
end
ContextUnits{N,D,P}() where {N,D,P} = ContextUnits{N,D,P,nothing}()
ContextUnits(u::Units{N,D,A}) where {N,D,A} =
ContextUnits{N,D,typeof(FreeUnits(upreferred(u))),A}()
(y::ContextUnits)(x) = uconvert(y,x)
"""
struct FixedUnits{N,D,A} <: Units{N,D,A} end
Instances of this object represent units, possibly combinations thereof.
These are primarily intended for use when you would like to disable automatic unit
conversions. See [Advanced promotion mechanisms](@ref) in the docs for details.
"""
struct FixedUnits{N,D,A} <: Units{N,D,A} end
FixedUnits{N,D}() where {N,D} = FixedUnits{N,D,nothing}()
FixedUnits(::Units{N,D,A}) where {N,D,A} = FixedUnits{N,D,A}()
"""
abstract type AbstractQuantity{T,D,U} <: Number end
Represents a generic quantity type, whose dimensions and units are specified in
the type signature. The dimensions and units are allowed to be the empty set,
in which case a dimensionless, unitless number results.
The type parameter `T` represents the numeric backing type. The type parameters
`D :: ` [`Unitful.Dimensions`](@ref) and `U <: ` [`Unitful.Units`](@ref).
Of course, the dimensions follow from the units, but the type parameters are
kept separate to permit convenient dispatch on dimensions.
"""
abstract type AbstractQuantity{T,D,U} <: Number end
"""
struct Quantity{T,D,U} <: AbstractQuantity{T,D,U}
A concrete subtype of [`Unitful.AbstractQuantity`](@ref).
The type parameter `T` represents the numeric backing type. The type parameters
`D :: ` [`Unitful.Dimensions`](@ref) and `U <: ` [`Unitful.Units`](@ref).
"""
struct Quantity{T,D,U} <: AbstractQuantity{T,D,U}
val::T
Quantity{T,D,U}(v::Number) where {T,D,U} = new{T,D,U}(v)
Quantity{T,D,U}(v::Quantity) where {T,D,U} = convert(Quantity{T,D,U}, v)
end
# Field-only constructor
Quantity{<:Any,D,U}(val) where {D,U} = Quantity{typeof(val),D,U}(val)
constructorof(::Type{Unitful.Quantity{_,D,U}}) where {_,D,U} =
Unitful.Quantity{T,D,U} where T
"""
DimensionlessUnits{U}
Useful for dispatching on [`Unitful.Units`](@ref) types that have no dimensions.
Example:
```jldoctest
julia> isa(Unitful.rad, DimensionlessUnits)
true
"""
const DimensionlessUnits{U} = Units{U, NoDims}
"""
AffineUnits{N,D,A} = Units{N,D,A} where A<:Affine
Useful for dispatching on unit objects that indicate a quantity should affine-transform
under unit conversion, like absolute temperatures. Not exported.
"""
const AffineUnits{N,D,A} = Units{N,D,A} where A<:Affine
"""
ScalarUnits{N,D} = Units{N,D,nothing}
Useful for dispatching on unit objects that indicate a quantity should transform in the
usual scalar way under unit conversion. Not exported.
"""
const ScalarUnits{N,D} = Units{N,D,nothing}
"""
DimensionlessQuantity{T,U} = AbstractQuantity{T, NoDims, U}
Useful for dispatching on [`Unitful.Quantity`](@ref) types that may have units
but no dimensions. (Units with differing power-of-ten prefixes are not canceled
out.)
Example:
```jldoctest
julia> isa(1.0u"mV/V", DimensionlessQuantity)
true
```
"""
const DimensionlessQuantity{T,U} = AbstractQuantity{T, NoDims, U}
"""
AffineQuantity{T,D,U} = AbstractQuantity{T,D,U} where U<:AffineUnits
Useful for dispatching on quantities that affine-transform under unit conversion, like
absolute temperatures. Not exported.
"""
const AffineQuantity{T,D,U} = AbstractQuantity{T,D,U} where U<:AffineUnits
"""
ScalarQuantity{T,D,U} = AbstractQuantity{T,D,U} where U<:ScalarUnits
Useful for dispatching on quantities that transform in the usual scalar way under unit
conversion. Not exported.
"""
const ScalarQuantity{T,D,U} = AbstractQuantity{T,D,U} where U<:ScalarUnits
"""
struct LogInfo{N,B,P}
Describes a logarithmic unit. Type parameters include:
- `N`: The name of the logarithmic unit, e.g. `:Decibel`, `:Neper`.
- `B`: The base of the logarithm.
- `P`: A prefactor to multiply the logarithm when the log is of a power ratio.
"""
struct LogInfo{N,B,P} end
"""
abstract type LogScaled{L<:LogInfo} <: Number end
Abstract supertype of [`Unitful.Level`](@ref) and [`Unitful.Gain`](@ref). It is only
used in promotion to put levels and gains onto a common log scale.
"""
abstract type LogScaled{L<:LogInfo} <: Number end
const RealOrRealQuantity = Union{Real, AbstractQuantity{<:Real}}
"""
struct Level{L, S, T<:Union{Real, AbstractQuantity{<:Real}}} <: LogScaled{L}
A logarithmic scale-based level. Details about the logarithmic scale are encoded in
`L <: LogInfo`. `S` is a reference quantity for the level, not a type. This type has one
field, `val::T`, and the log of the ratio `val/S` is taken. This type differs from
[`Unitful.Gain`](@ref) in that `val` is a linear quantity.
"""
struct Level{L, S, T<:RealOrRealQuantity} <: LogScaled{L}
val::T
function Level{L,S,T}(x) where {L,S,T}
S isa ReferenceQuantity || throw(DomainError(S, "Reference quantity must be real."))
dimension(S) != dimension(x) && throw(DimensionError(S,x))
return new{L,S,T}(x)
end
end
Level{L,S}(val::Number) where {L,S} = Level{L,S,real(typeof(val))}(val)
Level{L,S}(val::RealOrRealQuantity) where {L,S} = Level{L,S,typeof(val)}(val)
"""
struct Gain{L, S, T<:Real} <: LogScaled{L}
A logarithmic scale-based gain or attenuation factor. This type has one field, `val::T`.
For example, given a gain of `20dB`, we have `val===20`. This type differs from
[`Unitful.Level`](@ref) in that `val` is stored after computing the logarithm.
"""
struct Gain{L, S, T<:Real} <: LogScaled{L}
val::T
end
"""
struct MixedUnits{T<:LogScaled, U<:Units}
Struct for representing mixed logarithmic / linear units. Primarily useful as an
intermediate for `uconvert`. `T` is `<: Level` or `<: Gain`.
"""
struct MixedUnits{T<:LogScaled, U<:Units}
units::U
end
MixedUnits{T}() where {T} = MixedUnits{T, typeof(NoUnits)}(NoUnits)
MixedUnits{T}(u::Units) where {T} = MixedUnits{T,typeof(u)}(u)
(y::MixedUnits)(x) = uconvert(y,x)
# For logarithmic quantities
struct IsRootPowerRatio{S,T}
val::T
end
IsRootPowerRatio{S}(x) where {S} = IsRootPowerRatio{S, typeof(x)}(x)
Base.show(io::IO, ::MIME"text/plain", x::IsRootPowerRatio{S}) where {S} =
print(io, ifelse(S, "root-power ratio", "power ratio"), " with reference ", x.val)
const PowerRatio{T} = IsRootPowerRatio{false,T}
const RootPowerRatio{T} = IsRootPowerRatio{true,T}
@inline unwrap(x::IsRootPowerRatio) = x.val
@inline unwrap(x) = x
const ReferenceQuantity =
Union{RealOrRealQuantity, IsRootPowerRatio{S, <:RealOrRealQuantity} where S}
"""
reflevel(x::Level{L,S})
reflevel(::Type{Level{L,S}})
reflevel(::Type{Level{L,S,T}})
Returns the reference level, e.g.
```jldoctest
julia> reflevel(3u"dBm")
1 mW
```
"""
function reflevel end
@inline reflevel(x::Level{L,S}) where {L,S} = unwrap(S)
@inline reflevel(::Type{Level{L,S}}) where {L,S} = unwrap(S)
@inline reflevel(::Type{Level{L,S,T}}) where {L,S,T} = unwrap(S)