@@ -10,6 +10,7 @@ On your chemistry homework, you are faced with the following problem on the phot
10
10
> The energy of the incident UV light is `` 7.2 \cdot 10^{-19} \mathrm{J} `` per photon. Calculate the wavelength of the ejected electrons, in nanometers.
11
11
12
12
Let's solve this problem with ` DynamicQuantities.jl ` !
13
+
13
14
``` jldoctest examples
14
15
julia> using DynamicQuantities
15
16
@@ -30,6 +31,356 @@ julia> λ = h / p # wavelength of ejected electrons
30
31
julia> uconvert(us"nm", λ) # return answer in nanometers
31
32
3.0294912478780556 nm
32
33
```
34
+
33
35
Since units are automatically propagated, we can verify the dimension of our answer and all intermediates.
34
36
Also, using ` DynamicQuantities.Constants ` , we were able to obtain the (dimensionful!) values of all necessary constants without typing them ourselves.
35
37
38
+ ## 2. Projectile motion
39
+
40
+ Let's solve a simple projectile motion problem.
41
+ First load the ` DynamicQuantities ` module:
42
+
43
+ ``` julia
44
+ using DynamicQuantities
45
+ ```
46
+
47
+ Set up initial conditions as quantities:
48
+
49
+ ``` julia
50
+ y0 = 10 u " km"
51
+ v0 = 250 u " m/s"
52
+ θ = deg2rad (60 )
53
+ g = 9.81 u " m/s^2"
54
+ ```
55
+
56
+ Next, we use trig functions to calculate x and y components of initial velocity.
57
+ ` vx0 ` is the x component and
58
+ ` vy0 ` is the y component:
59
+
60
+ ``` julia
61
+ vx0 = v0 * cos (θ)
62
+ vy0 = v0 * sin (θ)
63
+ ```
64
+
65
+ Next, let's create a time vector from 0 seconds to 1.3 minutes.
66
+ Note that these are the same dimension (time), so it's fine to treat
67
+ them as dimensionally equivalent!
68
+
69
+ ``` julia
70
+ t = range (0 u " s" , 1.3 u " min" , length= 100 )
71
+ ```
72
+
73
+ Next, use kinematic equations to calculate x and y as a function of time.
74
+ ` x(t) ` is the x position at time t, and
75
+ ` y(t) ` is the y position:
76
+
77
+ ``` julia
78
+ x (t) = vx0* t
79
+ y (t) = vy0* t - 0.5 * g* t^ 2 + y0
80
+ ```
81
+
82
+ These are functions, so let's evaluate them:
83
+
84
+ ``` julia
85
+ x_si = x .(t)
86
+ y_si = y .(t)
87
+ ```
88
+
89
+ These are regular vectors of quantities
90
+ with ` Dimensions ` for physical dimensions.
91
+
92
+ Next, let's plot the trajectory.
93
+ First convert to km and strip units:
94
+
95
+ ``` julia
96
+ x_km = ustrip .(uconvert (us " km" ).(x_si))
97
+ y_km = ustrip .(uconvert (us " km" ).(y_si))
98
+ ```
99
+
100
+ Now, we plot:
101
+
102
+ ``` julia
103
+ plot (x_km, y_km, label= " Trajectory" , xlabel= " x [km]" , ylabel= " y [km]" )
104
+ ```
105
+
106
+ ## 3. Various Simple Examples
107
+
108
+ This section demonstrates miscellaneous examples of using ` DynamicQuantities.jl ` .
109
+
110
+ ### Conversion
111
+
112
+ Convert a quantity to have a new type for the value:
113
+
114
+ ``` julia
115
+ quantity = 1.5 u " m"
116
+
117
+ convert_q = Quantity {Float32} (quantity)
118
+
119
+ println (" Converted Quantity to Float32: " , convert_q)
120
+ ```
121
+
122
+ ### Array basics
123
+
124
+ Create a ` QuantityArray ` (an array of quantities with
125
+ the same dimension) by passing an array and a single quantity:
126
+
127
+ ``` julia
128
+ x = QuantityArray (randn (32 ), u " km/s" )
129
+ ```
130
+
131
+ or, by passing an array of individual quantities:
132
+
133
+ ``` julia
134
+ y = randn (32 )
135
+ y_q = QuantityArray (y .* u " m * cd / s" )
136
+ ```
137
+
138
+ We can take advantage of this being ` <:AbstractArray ` :
139
+
140
+ ``` julia
141
+ println (" Sum x: " , sum (x))
142
+ ```
143
+
144
+ We can also do things like setting a particular element:
145
+
146
+ ``` julia
147
+ y_q[5 ] = Quantity (5 , length= 1 , luminosity= 1 , time= - 1 )
148
+ println (" 5th element of y_q: " , y_q[5 ])
149
+ ```
150
+
151
+ We can get back the original array with ` ustrip ` :
152
+
153
+ ``` julia
154
+ println (" Stripped y_q: " , ustrip (y_q))
155
+ ```
156
+
157
+ This ` QuantityArray ` is useful for broadcasting:
158
+
159
+ ``` julia
160
+ f_square (v) = v^ 2 * 1.5 - v^ 2
161
+ println (" Applying function to y_q: " , sum (f_square .(y_q)))
162
+ ```
163
+
164
+ ### Fill
165
+
166
+ We can also make ` QuantityArray ` using ` fill ` :
167
+
168
+ ``` julia
169
+ filled_q = fill (u " m/s" , 10 )
170
+ println (" Filled QuantityArray: " , filled_q)
171
+ ```
172
+
173
+ ` fill ` works for 0 dimensional ` QuantityArray ` s as well:
174
+
175
+ ``` julia
176
+ empty_q = fill (u " m/s" , ())
177
+ println (" 0 dimensional QuantityArray: " , empty_q)
178
+ ```
179
+
180
+ ### Similar
181
+
182
+ Likewise, we can create a ` QuantityArray ` with the same properties as another ` QuantityArray ` :
183
+
184
+ ``` julia
185
+ qa = QuantityArray (rand (3 , 4 ), u " m" )
186
+
187
+ new_qa = similar (qa)
188
+
189
+ println (" Similar qa: " , new_qa)
190
+ ```
191
+
192
+ ### Promotion
193
+
194
+ Promotion rules are defined for ` QuantityArray ` s:
195
+
196
+ ``` julia
197
+ qarr1 = QuantityArray (randn (32 ), convert (Dimensions{Rational{Int32}}, dimension (u " km/s" )))
198
+ qarr2 = QuantityArray (randn (Float16, 32 ), convert (Dimensions{Rational{Int64}}, dimension (u " km/s" )))
199
+ ```
200
+
201
+ See what type they promote to:
202
+
203
+ ``` julia
204
+ println (" Promoted type: " , typeof (promote (qarr1, qarr2)))
205
+ ```
206
+
207
+ ### Array Concatenation
208
+
209
+ Likewise, we can take advantage of array concatenation,
210
+ which will ensure we have the same dimensions:
211
+
212
+ ``` julia
213
+ qarr1 = QuantityArray (randn (3 ) .* u " km/s" )
214
+ qarr2 = QuantityArray (randn (3 ) .* u " km/s" )
215
+ ```
216
+
217
+ Concatenate them:
218
+
219
+ ``` julia
220
+ concat_qarr = hcat (qarr1, qarr2)
221
+ println (" Concatenated QuantityArray: " , concat_qarr)
222
+ ```
223
+
224
+ ### Symbolic Units
225
+
226
+ We can use arbitrary ` AbstractQuantity ` and ` AbstractDimensions `
227
+ in a ` QuantityArray ` , including ` SymbolicDimensions ` :
228
+
229
+ ``` julia
230
+ z_ar = randn (32 )
231
+ z = QuantityArray (z_ar, us " Constants.M_sun * km/s" )
232
+ ```
233
+
234
+ Expand to standard units:
235
+
236
+ ``` julia
237
+ z_expanded = uexpand (z)
238
+ println (" Expanded z: " , z_expanded)
239
+ ```
240
+
241
+
242
+ ### GenericQuantity Construction
243
+
244
+ In addition to ` Quantity ` , we can also use ` GenericQuantity ` :
245
+
246
+
247
+ ``` julia
248
+ x = GenericQuantity (1.5 )
249
+ y = GenericQuantity (0.2 u " km" )
250
+ println (y)
251
+ ```
252
+
253
+ This ` GenericQuantity ` is subtyped to ` Any ` ,
254
+ rather than ` Number ` , and thus can also store
255
+ custom non-scalar types.
256
+
257
+ For example, we can work with ` Coords ` , and
258
+ wrap it in a single ` GenericQuantity ` type:
259
+
260
+ ``` julia
261
+ struct Coords
262
+ x:: Float64
263
+ y:: Float64
264
+ end
265
+
266
+ # Define arithmetic operations on Coords
267
+ Base.:+ (a:: Coords , b:: Coords ) = Coords (a. x + b. x, a. y + b. y)
268
+ Base.:- (a:: Coords , b:: Coords ) = Coords (a. x - b. x, a. y - b. y)
269
+ Base.:* (a:: Coords , b:: Number ) = Coords (a. x * b, a. y * b)
270
+ Base.:* (a:: Number , b:: Coords ) = Coords (a * b. x, a * b. y)
271
+ Base.:/ (a:: Coords , b:: Number ) = Coords (a. x / b, a. y / b)
272
+ ```
273
+
274
+ We can then build a ` GenericQuantity ` out of this:
275
+
276
+ ``` julia
277
+ coord1 = GenericQuantity (Coords (0.3 , 0.9 ), length= 1 )
278
+ coord2 = GenericQuantity (Coords (0.2 , - 0.1 ), length= 1 )
279
+ ```
280
+
281
+ and perform operations on these:
282
+
283
+ ``` julia
284
+ coord1 + coord2 |> uconvert (us " cm" )
285
+ # (Coords(50.0, 80.0)) cm
286
+ ```
287
+
288
+ The nice part about this is it only stores a single Dimensions
289
+ (or ` SymbolicDimensions ` ) for the entire struct!
290
+
291
+ ### GenericQuantity and Quantity Promotion
292
+
293
+ When we combine a ` GenericQuantity ` and a ` Quantity ` ,
294
+ the result is another ` GenericQuantity ` :
295
+
296
+ ``` julia
297
+ x = GenericQuantity (1.5f0 )
298
+ y = Quantity (1.5 , length= 1 )
299
+ println (" Promoted type of x and y: " , typeof (x * y))
300
+ ```
301
+
302
+ ### Custom Dimensions
303
+
304
+ We can create custom dimensions by subtyping to
305
+ ` AbstractDimensions ` :
306
+
307
+ ``` julia
308
+ struct MyDimensions{R} <: AbstractDimensions{R}
309
+ cookie:: R
310
+ milk:: R
311
+ end
312
+ ```
313
+
314
+ Many constructors and functions are defined on ` AbstractDimensions ` ,
315
+ so this can be used out-of-the-box.
316
+ We can then use this in a ` Quantity ` , and all operations will work as expected:
317
+
318
+ ``` julia
319
+ x = Quantity (1.5 , MyDimensions (cookie= 1 , milk= - 1 ))
320
+ y = Quantity (2.0 , MyDimensions (milk= 1 ))
321
+
322
+ x * y
323
+ ```
324
+
325
+ which gives us ` 3.0 cookie ` computed from a rate of ` 1.5 cookie milk⁻¹ ` multiplied
326
+ by ` 2.0 milk ` . Likewise, we can use these in a ` QuantityArray ` :
327
+
328
+ ``` julia
329
+ x_qa = QuantityArray (randn (32 ), MyDimensions (cookie= 1 , milk= - 1 ))
330
+
331
+ x_qa .^ 2
332
+ ```
333
+
334
+ ### Custom Quantities
335
+
336
+ We can also create custom dimensions by subtyping
337
+ to either ` AbstractQuantity ` (for ` <:Number ` ) or
338
+ ` AbstractGenericQuantity ` (for ` <:Any ` ):
339
+
340
+ ``` julia
341
+ struct MyQuantity{T,D} <: AbstractQuantity{T,D}
342
+ value:: T
343
+ dimensions:: D
344
+ end
345
+ ```
346
+
347
+ Since ` AbstractQuantity <: Number ` , this will also be a number.
348
+ Keep in mind that you must call these fields ` value ` and ` dimensions `
349
+ for ` ustrip(...) ` and ` dimension(...) ` to work. Otherwise, simply
350
+ redefine those.
351
+
352
+ We can use this custom quantity just like we would use ` Quantity ` :
353
+
354
+ ``` julia
355
+ q1 = MyQuantity (1.2 , Dimensions (length= - 2 ))
356
+ # prints as `1.2 m⁻²`
357
+
358
+ q2 = MyQuantity (1.5 , MyDimensions (cookie= 1 ))
359
+ # prints as `1.5 cookie`
360
+ ```
361
+
362
+ Including mathematical operations:
363
+
364
+ ``` julia
365
+ q2 ^ 2
366
+ # `2.25 cookie²`
367
+ ```
368
+
369
+ The main reason you would use a custom quantity is if you want
370
+ to change built-in behavior, or maybe have special methods for
371
+ different types of quantities.
372
+
373
+ Note that you can declare a method on ` AbstractQuantity ` , or
374
+ ` AbstractGenericQuantity ` to allow their respective inputs.
375
+
376
+ ** Note** : In general, you should probably
377
+ specialize on ` UnionAbstractQuantity ` which is
378
+ the union of these two abstract quantities, _ as well as any other future abstract quantity types_ ,
379
+ such as the planned ` AbstractRealQuantity ` .
380
+
381
+ ``` julia
382
+ function my_func (x:: UnionAbstractQuantity{T,D} ) where {T,D}
383
+ # value has type T and dimensions has type D
384
+ return x / ustrip (x)
385
+ end
386
+ ```
0 commit comments