5
5
[ ![ Dev] ( https://img.shields.io/badge/docs-dev-blue.svg )] ( https://symbolicml.org/DynamicQuantities.jl/dev/ )
6
6
[ ![ Build Status] ( https://github.com/SymbolicML/DynamicQuantities.jl/actions/workflows/CI.yml/badge.svg?branch=main )] ( https://github.com/SymbolicML/DynamicQuantities.jl/actions/workflows/CI.yml?query=branch%3Amain )
7
7
[ ![ Coverage] ( https://coveralls.io/repos/github/SymbolicML/DynamicQuantities.jl/badge.svg?branch=main )] ( https://coveralls.io/github/SymbolicML/DynamicQuantities.jl?branch=main )
8
+ [ ![ Aqua QA] ( https://raw.githubusercontent.com/JuliaTesting/Aqua.jl/master/badge.svg )] ( https://github.com/JuliaTesting/Aqua.jl )
8
9
9
10
</div >
10
11
11
12
DynamicQuantities defines a simple statically-typed ` Quantity ` type for Julia.
12
13
Physical dimensions are stored as a * value* , as opposed to a parametric type, as in [ Unitful.jl] ( https://github.com/PainterQubits/Unitful.jl ) .
13
- This is done to allow for calculations where physical dimensions are not known at compile time .
14
+ This can greatly improve both runtime performance, by avoiding type instabilities, and startup time, as it avoids overspecializing methods .
14
15
15
16
- [ Performance] ( #performance )
16
17
- [ Usage] ( #usage )
18
+ - [ Constants] ( #constants )
19
+ - [ Symbolic Units] ( #symbolic-units )
20
+ - [ Arrays] ( #arrays )
21
+ - [ Unitful] ( #unitful )
17
22
- [ Types] ( #types )
18
23
- [ Vectors] ( #vectors )
19
24
@@ -25,22 +30,22 @@ when the compiler cannot infer dimensions in a function:
25
30
``` julia
26
31
julia> using BenchmarkTools, DynamicQuantities; import Unitful
27
32
28
- julia> dyn_uni = 0.2 u " m^0.5 * kg * mol^3 "
29
- 0.2 m¹ᐟ² kg mol³
33
+ julia> dyn_uni = 0.2 u " m/s "
34
+ 0.2 m s⁻¹
30
35
31
36
julia> unitful = convert (Unitful. Quantity, dyn_uni)
32
- 0.2 kg m¹ᐟ² mol³
37
+ 0.2 m s⁻¹
33
38
34
39
julia> f (x, i) = x ^ i * 0.3 ;
35
40
36
41
julia> @btime f ($ dyn_uni, 1 );
37
- 8.759 ns (0 allocations: 0 bytes)
42
+ 2.708 ns (0 allocations: 0 bytes)
38
43
39
44
julia> @btime f ($ unitful, 1 );
40
- 30.083 μs (42 allocations: 1.91 KiB)
45
+ 2.597 μs (30 allocations: 1.33 KiB)
41
46
```
42
47
43
- ** ( Note the μ and n.) **
48
+ ** Note the μ and n: this is a 1000x speedup! **
44
49
Here, the DynamicQuantities quantity object allows the compiler to build a function that is type stable,
45
50
while the Unitful quantity object, which stores its dimensions in the type, requires type inference at runtime.
46
51
@@ -51,10 +56,10 @@ then you can get better speeds with Unitful:
51
56
julia> g (x) = x ^ 2 * 0.3 ;
52
57
53
58
julia> @btime g ($ dyn_uni);
54
- 10.051 ns (0 allocations: 0 bytes)
59
+ 1.791 ns (0 allocations: 0 bytes)
55
60
56
61
julia> @btime g ($ unitful);
57
- 2.000 ns (0 allocations: 0 bytes)
62
+ 1.500 ns (0 allocations: 0 bytes)
58
63
```
59
64
60
65
While both of these are type stable,
@@ -147,16 +152,113 @@ julia> ustrip(x)
147
152
0.2
148
153
```
149
154
150
- ### Unitful
155
+ ### Constants
156
+
157
+ There are a variety of physical constants accessible
158
+ via the ` Constants ` submodule:
159
+
160
+ ``` julia
161
+ julia> Constants. c
162
+ 2.99792458e8 m s⁻¹
163
+ ```
164
+
165
+ These can also be used inside the ` u"..." ` macro:
166
+
167
+ ``` julia
168
+ julia> u " Constants.c * Hz"
169
+ 2.99792458e8 m s⁻²
170
+ ```
171
+
172
+ For the full list, see the [ docs] ( https://symbolicml.org/DynamicQuantities.jl/dev/constants/ ) .
173
+
174
+
175
+ ### Symbolic Units
176
+
177
+ You can also choose to not eagerly convert to SI base units,
178
+ instead leaving the units as the user had written them.
179
+ For example:
180
+
181
+ ``` julia
182
+ julia> q = 100 us " cm * kPa"
183
+ 100.0 cm kPa
184
+
185
+ julia> q^ 2
186
+ 10000.0 cm² kPa²
187
+ ```
188
+
189
+ You can convert to regular SI base units with
190
+ ` uexpand ` :
191
+
192
+ ``` julia
193
+ julia> uexpand (q^ 2 )
194
+ 1.0e6 kg² s⁻⁴
195
+ ```
196
+
197
+ This also works with constants:
198
+
199
+ ``` julia
200
+ julia> x = us " Constants.c * Hz"
201
+ 1.0 Hz c
202
+
203
+ julia> x^ 2
204
+ 1.0 Hz² c²
205
+
206
+ julia> uexpand (x^ 2 )
207
+ 8.987551787368176e16 m² s⁻⁴
208
+ ```
209
+
210
+ You can also convert a quantity in regular base SI units to symbolic units with ` uconvert ` :
211
+ ``` julia
212
+ julia> uconvert (us " nm" , 5e-9 u " m" ) # can also write 5e-9u"m" |> uconvert(us"nm")
213
+ 5.0 nm
214
+ ```
215
+
216
+ ### Arrays
151
217
152
- DynamicQuantities works with quantities that are exclusively
153
- represented by their SI base units. This gives us type stability
154
- and greatly improves performance.
218
+ For working with an array of quantities that have the same dimensions,
219
+ you can use a ` QuantityArray ` :
220
+
221
+ ``` julia
222
+ julia> ar = QuantityArray (rand (3 ), u " m/s" )
223
+ 3 - element QuantityArray (:: Vector{Float64} , :: Quantity{Float64, Dimensions{DynamicQuantities.FixedRational{Int32, 25200}}} ):
224
+ 0.2729202669351497 m s⁻¹
225
+ 0.992546340360901 m s⁻¹
226
+ 0.16863543422972482 m s⁻¹
227
+ ```
155
228
156
- However, performing calculations with physical dimensions
157
- is actually equivalent to working with a standardized unit system.
158
- Thus, you can use Unitful to parse units,
159
- and then use the DynamicQuantities->Unitful extension for conversion:
229
+ This ` QuantityArray ` is a subtype ` <:AbstractArray{Quantity{Float64,Dimensions{...}},1} ` ,
230
+ meaning that indexing a specific element will return a ` Quantity ` :
231
+
232
+ ``` julia
233
+ julia> ar[2 ]
234
+ 0.992546340360901 m s⁻¹
235
+
236
+ julia> ar[2 ] *= 2
237
+ 1.985092680721802 m s⁻¹
238
+
239
+ julia> ar[2 ] += 0.5 u " m/s"
240
+ 2.485092680721802 m s⁻¹
241
+ ```
242
+
243
+ This also has a custom broadcasting interface which
244
+ allows the compiler to avoid redundant dimension calculations,
245
+ relative to if you had simply used an array of quantities:
246
+
247
+ ``` julia
248
+ julia> f (v) = v^ 2 * 1.5 ;
249
+
250
+ julia> @btime $ f .(xa) setup= (xa = randn (100000 ) .* u " km/s" );
251
+ 109.500 μs (2 allocations: 3.81 MiB)
252
+
253
+ julia> @btime $ f .(qa) setup= (xa = randn (100000 ) .* u " km/s" ; qa = QuantityArray (xa));
254
+ 50.917 μs (3 allocations: 781.34 KiB)
255
+ ```
256
+
257
+ So we can see the ` QuantityArray ` version saves on both time and memory.
258
+
259
+ ### Unitful
260
+
261
+ DynamicQuantities allows you to convert back and forth from Unitful.jl:
160
262
161
263
``` julia
162
264
julia> using Unitful: Unitful, @u_str ; import DynamicQuantities
@@ -180,28 +282,28 @@ true
180
282
## Types
181
283
182
284
Both a ` Quantity ` 's values and dimensions are of arbitrary type.
183
- By default, dimensions are stored as a ` DynamicQuantities. FixedRational{Int32,C}`
184
- object, which represents a rational number
285
+ By default, dimensions are stored as a ` Dimensions{ FixedRational{Int32,C} }`
286
+ object, whose exponents are stored as rational numbers
185
287
with a fixed denominator ` C ` . This is much faster than ` Rational ` .
186
288
187
289
``` julia
188
290
julia> typeof (0.5 u " kg" )
189
- Quantity{Float64, FixedRational{Int32, 25200 }
291
+ Quantity{Float64, Dimensions{ FixedRational{Int32, 25200 }} }
190
292
```
191
293
192
294
You can change the type of the value field by initializing with a value
193
295
explicitly of the desired type.
194
296
195
297
``` julia
196
298
julia> typeof (Quantity (Float16 (0.5 ), mass= 1 , length= 1 ))
197
- Quantity{Float16, FixedRational{Int32, 25200 }}
299
+ Quantity{Float16, Dimensions{ FixedRational{Int32, 25200 } }}
198
300
```
199
301
200
302
or by conversion:
201
303
202
304
``` julia
203
305
julia> typeof (convert (Quantity{Float16}, 0.5 u " m/s" ))
204
- Quantity{Float16, DynamicQuantities . FixedRational{Int32, 25200 }}
306
+ Quantity{Float16, Dimensions{ FixedRational{Int32, 25200 } }}
205
307
```
206
308
207
309
For many applications, ` FixedRational{Int8,6} ` will suffice,
@@ -213,9 +315,9 @@ the type you wish to use as the second argument to `Quantity`:
213
315
``` julia
214
316
julia> using DynamicQuantities
215
317
216
- julia> R8 = DynamicQuantities. FixedRational{Int8,6 };
318
+ julia> R8 = Dimensions{ DynamicQuantities. FixedRational{Int8,6 } };
217
319
218
- julia> R32 = DynamicQuantities. FixedRational{Int32,2 ^ 4 * 3 ^ 2 * 5 ^ 2 * 7 }; # Default
320
+ julia> R32 = Dimensions{ DynamicQuantities. FixedRational{Int32,2 ^ 4 * 3 ^ 2 * 5 ^ 2 * 7 } }; # Default
219
321
220
322
julia> q8 = [Quantity (randn (), R8, length= rand (- 2 : 2 )) for i in 1 : 1000 ];
221
323
@@ -229,30 +331,3 @@ julia> @btime f($q8);
229
331
julia> @btime f ($ q32);
230
332
8.417 μs (2 allocations: 39.11 KiB)
231
333
```
232
-
233
- ## Vectors
234
-
235
- There is not a separate class for vectors, but you can create units
236
- like so:
237
-
238
- ``` julia
239
- julia> randn (5 ) .* u " m/s"
240
- 5 - element Vector{Quantity{Float64, DynamicQuantities. FixedRational{Int32, 25200 }}}:
241
- 1.1762086954956399 m s⁻¹
242
- 1.320811324040591 m s⁻¹
243
- 0.6519033652437799 m s⁻¹
244
- 0.7424822374423569 m s⁻¹
245
- 0.33536928068133726 m s⁻¹
246
- ```
247
-
248
- Because it is type stable, you can have mixed units in a vector too:
249
-
250
- ``` julia
251
- julia> v = [Quantity (randn (), mass= rand (0 : 5 ), length= rand (0 : 5 )) for _= 1 : 5 ]
252
- 5 - element Vector{Quantity{Float64, DynamicQuantities. FixedRational{Int32, 25200 }}}:
253
- 0.4309293892461158 kg⁵
254
- 1.415520139801276
255
- 1.2179414706524276 m³ kg⁴
256
- - 0.18804207255117408 m³ kg⁵
257
- 0.52123911329638 m³ kg²
258
- ```
0 commit comments