@@ -99,15 +99,16 @@ dimension_names(::Type{<:AbstractSymbolicDimensions}) = ALL_SYMBOLS
99
99
Base. propertynames (:: AbstractSymbolicDimensions ) = ALL_SYMBOLS
100
100
Base. getindex (d:: AbstractSymbolicDimensions , k:: Symbol ) = getproperty (d, k)
101
101
constructorof (:: Type{<:SymbolicDimensions} ) = SymbolicDimensions
102
- constructorof (:: Type{<:SymbolicDimensionsSingleton{R}} ) where {R} = SymbolicDimensionsSingleton{R}
102
+ constructorof (:: Type{<:SymbolicDimensionsSingleton} ) = SymbolicDimensionsSingleton
103
103
with_type_parameters (:: Type{<:SymbolicDimensions} , :: Type{R} ) where {R} = SymbolicDimensions{R}
104
104
with_type_parameters (:: Type{<:SymbolicDimensionsSingleton} , :: Type{R} ) where {R} = SymbolicDimensionsSingleton{R}
105
105
nzdims (d:: SymbolicDimensions ) = getfield (d, :nzdims )
106
106
nzdims (d:: SymbolicDimensionsSingleton ) = (getfield (d, :dim ),)
107
107
nzvals (d:: SymbolicDimensions ) = getfield (d, :nzvals )
108
108
nzvals (:: SymbolicDimensionsSingleton{R} ) where {R} = (one (R),)
109
- Base. eltype (:: AbstractSymbolicDimensions{R} ) where {R} = R
110
- Base. eltype (:: Type{<:AbstractSymbolicDimensions{R}} ) where {R} = R
109
+
110
+ # Need to construct with `R` if available, as can't figure it out otherwise:
111
+ constructorof (:: Type{<:SymbolicDimensionsSingleton{R}} ) where {R} = SymbolicDimensionsSingleton{R}
111
112
112
113
# Conversion:
113
114
function SymbolicDimensions (d:: SymbolicDimensionsSingleton{R} ) where {R}
@@ -170,23 +171,55 @@ uexpand(q::QuantityArray) = uexpand.(q)
170
171
Convert a quantity `q` with base SI units to the symbolic units of `qout`, for `q` and `qout` with compatible units.
171
172
Mathematically, the result has value `q / uexpand(qout)` and units `dimension(qout)`.
172
173
"""
173
- function uconvert (qout:: UnionAbstractQuantity{<:Any, <:AbstractSymbolicDimensions } , q:: UnionAbstractQuantity{<:Any, <:Dimensions} )
174
+ function uconvert (qout:: UnionAbstractQuantity{<:Any, <:SymbolicDimensions } , q:: UnionAbstractQuantity{<:Any, <:Dimensions} )
174
175
@assert isone (ustrip (qout)) " You passed a quantity with a non-unit value to uconvert."
175
176
qout_expanded = uexpand (qout)
176
177
dimension (q) == dimension (qout_expanded) || throw (DimensionError (q, qout_expanded))
177
178
new_val = ustrip (q) / ustrip (qout_expanded)
178
179
new_dim = dimension (qout)
179
180
return new_quantity (typeof (q), new_val, new_dim)
180
181
end
181
- function uconvert (qout:: UnionAbstractQuantity{<:Any,<:AbstractSymbolicDimensions } , q:: QuantityArray{<:Any,<:Any,<:Dimensions} )
182
+ function uconvert (qout:: UnionAbstractQuantity{<:Any,<:SymbolicDimensions } , q:: QuantityArray{<:Any,<:Any,<:Dimensions} )
182
183
@assert isone (ustrip (qout)) " You passed a quantity with a non-unit value to uconvert."
183
184
qout_expanded = uexpand (qout)
184
185
dimension (q) == dimension (qout_expanded) || throw (DimensionError (q, qout_expanded))
185
186
new_array = ustrip (q) ./ ustrip (qout_expanded)
186
187
new_dim = dimension (qout)
187
188
return QuantityArray (new_array, new_dim, quantity_type (q))
188
189
end
189
- # TODO : Method for converting SymbolicDimensions -> SymbolicDimensions
190
+
191
+ # Ensure we always do operations with SymbolicDimensions:
192
+ function uconvert (
193
+ qout:: UnionAbstractQuantity{T,<:SymbolicDimensionsSingleton{R}} ,
194
+ q:: Union {
195
+ <: UnionAbstractQuantity{<:Any,<:Dimensions} ,
196
+ <: QuantityArray{<:Any,<:Any,<:Dimensions} ,
197
+ },
198
+ ) where {T,R}
199
+ return uconvert (
200
+ convert (
201
+ with_type_parameters (
202
+ typeof (qout),
203
+ T,
204
+ with_type_parameters (SymbolicDimensions, R),
205
+ ),
206
+ qout,
207
+ ),
208
+ q,
209
+ )
210
+ end
211
+
212
+ # Allow user to convert SymbolicDimensions -> SymbolicDimensions
213
+ function uconvert (
214
+ qout:: UnionAbstractQuantity{<:Any,<:AbstractSymbolicDimensions{R}} ,
215
+ q:: Union {
216
+ <: UnionAbstractQuantity{<:Any,<:AbstractSymbolicDimensions} ,
217
+ <: QuantityArray{<:Any,<:Any,<:AbstractSymbolicDimensions} ,
218
+ },
219
+ ) where {R}
220
+ return uconvert (qout, uexpand (q))
221
+ end
222
+
190
223
191
224
"""
192
225
uconvert(qout::UnionAbstractQuantity{<:Any, <:AbstractSymbolicDimensions})
@@ -197,7 +230,7 @@ a function equivalent to `q -> uconvert(qout, q)`.
197
230
uconvert (qout:: UnionAbstractQuantity{<:Any,<:AbstractSymbolicDimensions} ) = Base. Fix1 (uconvert, qout)
198
231
199
232
Base. copy (d:: SymbolicDimensions ) = SymbolicDimensions (copy (nzdims (d)), copy (nzvals (d)))
200
- Base. copy (d:: SymbolicDimensionsSingleton ) = constructorof (d )(getfield (d, :dim ))
233
+ Base. copy (d:: SymbolicDimensionsSingleton ) = constructorof (typeof (d) )(getfield (d, :dim ))
201
234
202
235
function Base.:(== )(l:: AbstractSymbolicDimensions , r:: AbstractSymbolicDimensions )
203
236
nzdims_l = nzdims (l)
@@ -336,6 +369,7 @@ to enable pretty-printing of units.
336
369
module SymbolicUnits
337
370
338
371
import .. UNIT_SYMBOLS
372
+ import .. CONSTANT_SYMBOLS
339
373
import .. SymbolicDimensionsSingleton
340
374
import ... constructorof
341
375
import ... DEFAULT_SYMBOLIC_QUANTITY_TYPE
@@ -353,22 +387,34 @@ module SymbolicUnits
353
387
import .... DEFAULT_VALUE_TYPE
354
388
import .... DEFAULT_DIM_BASE_TYPE
355
389
390
+ const _SYMBOLIC_CONSTANT_VALUES = DEFAULT_SYMBOLIC_QUANTITY_TYPE[]
391
+
356
392
for unit in CONSTANT_SYMBOLS
357
- @eval const $ unit = constructorof (DEFAULT_SYMBOLIC_QUANTITY_TYPE)(
358
- DEFAULT_VALUE_TYPE (1.0 ),
359
- SymbolicDimensionsSingleton {DEFAULT_DIM_BASE_TYPE} ($ (QuoteNode (disambiguate_symbol (unit))))
360
- )
393
+ @eval begin
394
+ const $ unit = constructorof (DEFAULT_SYMBOLIC_QUANTITY_TYPE)(
395
+ DEFAULT_VALUE_TYPE (1.0 ),
396
+ SymbolicDimensionsSingleton {DEFAULT_DIM_BASE_TYPE} ($ (QuoteNode (disambiguate_symbol (unit))))
397
+ )
398
+ push! (_SYMBOLIC_CONSTANT_VALUES, $ unit)
399
+ end
361
400
end
401
+ const SYMBOLIC_CONSTANT_VALUES = Tuple (_SYMBOLIC_CONSTANT_VALUES)
362
402
end
363
403
import . Constants
364
404
import . Constants as SymbolicConstants
405
+ import . Constants: SYMBOLIC_CONSTANT_VALUES
365
406
407
+ const _SYMBOLIC_UNIT_VALUES = DEFAULT_SYMBOLIC_QUANTITY_TYPE[]
366
408
for unit in UNIT_SYMBOLS
367
- @eval const $ unit = constructorof (DEFAULT_SYMBOLIC_QUANTITY_TYPE)(
368
- DEFAULT_VALUE_TYPE (1.0 ),
369
- SymbolicDimensionsSingleton {DEFAULT_DIM_BASE_TYPE} ($ (QuoteNode (unit)))
370
- )
409
+ @eval begin
410
+ const $ unit = constructorof (DEFAULT_SYMBOLIC_QUANTITY_TYPE)(
411
+ DEFAULT_VALUE_TYPE (1.0 ),
412
+ SymbolicDimensionsSingleton {DEFAULT_DIM_BASE_TYPE} ($ (QuoteNode (unit)))
413
+ )
414
+ push! (_SYMBOLIC_UNIT_VALUES, $ unit)
415
+ end
371
416
end
417
+ const SYMBOLIC_UNIT_VALUES = Tuple (_SYMBOLIC_UNIT_VALUES)
372
418
373
419
374
420
"""
@@ -387,18 +433,50 @@ module SymbolicUnits
387
433
`Quantity(1.0, SymbolicDimensions, c=2, Hz=2)`. However, note that due to
388
434
namespace collisions, a few physical constants are automatically converted.
389
435
"""
390
- function sym_uparse (raw_string:: AbstractString )
391
- raw_result = eval (Meta. parse (raw_string))
392
- return copy (as_quantity (raw_result)):: DEFAULT_SYMBOLIC_QUANTITY_OUTPUT_TYPE
436
+ function sym_uparse (s:: AbstractString )
437
+ ex = map_to_scope (Meta. parse (s))
438
+ ex = :($ as_quantity ($ ex))
439
+ return copy (eval (ex)):: DEFAULT_SYMBOLIC_QUANTITY_OUTPUT_TYPE
393
440
end
394
441
395
442
as_quantity (q:: DEFAULT_SYMBOLIC_QUANTITY_OUTPUT_TYPE ) = q
396
443
as_quantity (x:: Number ) = convert (DEFAULT_SYMBOLIC_QUANTITY_OUTPUT_TYPE, x)
397
444
as_quantity (x) = error (" Unexpected type evaluated: $(typeof (x)) " )
445
+
446
+ function map_to_scope (ex:: Expr )
447
+ if ex. head == :call
448
+ ex. args[2 : end ] = map (map_to_scope, ex. args[2 : end ])
449
+ return ex
450
+ elseif ex. head == :. && ex. args[1 ] == :Constants
451
+ @assert ex. args[2 ] isa QuoteNode
452
+ return lookup_constant (ex. args[2 ]. value)
453
+ else
454
+ throw (ArgumentError (" Unexpected expression: $ex . Only `:call` and `:.` (for `SymbolicConstants`) are expected." ))
455
+ end
456
+ end
457
+ function map_to_scope (sym:: Symbol )
458
+ if sym in UNIT_SYMBOLS
459
+ return lookup_unit (sym)
460
+ elseif sym in CONSTANT_SYMBOLS
461
+ throw (ArgumentError (" Symbol $sym found in `SymbolicConstants` but not `SymbolicUnits`. Please access the `SymbolicConstants` module. For example, `u\" SymbolicConstants.$sym \" `." ))
462
+ else
463
+ throw (ArgumentError (" Symbol $sym not found in `SymbolicUnits` or `SymbolicConstants`." ))
464
+ end
465
+ end
466
+ function map_to_scope (ex)
467
+ return ex
468
+ end
469
+ function lookup_unit (ex:: Symbol )
470
+ i = findfirst (== (ex), UNIT_SYMBOLS):: Int
471
+ return as_quantity (SYMBOLIC_UNIT_VALUES[i])
472
+ end
473
+ function lookup_constant (ex:: Symbol )
474
+ i = findfirst (== (ex), CONSTANT_SYMBOLS):: Int
475
+ return as_quantity (SYMBOLIC_CONSTANT_VALUES[i])
476
+ end
398
477
end
399
478
400
- import . SymbolicUnits: sym_uparse
401
- import . SymbolicUnits: SymbolicConstants
479
+ import . SymbolicUnits: as_quantity, sym_uparse, SymbolicConstants, map_to_scope
402
480
403
481
"""
404
482
us"[unit expression]"
@@ -416,7 +494,9 @@ module. So, for example, `us"Constants.c^2 * Hz^2"` would evaluate to
416
494
namespace collisions, a few physical constants are automatically converted.
417
495
"""
418
496
macro us_str (s)
419
- return esc (SymbolicUnits. sym_uparse (s))
497
+ ex = map_to_scope (Meta. parse (s))
498
+ ex = :($ as_quantity ($ ex))
499
+ return esc (ex)
420
500
end
421
501
422
502
function Base. promote_rule (:: Type{SymbolicDimensionsSingleton{R1}} , :: Type{SymbolicDimensionsSingleton{R2}} ) where {R1,R2}
0 commit comments