Skip to content

Commit 0c665e2

Browse files
authored
Merge pull request #48 from gaurav-arya/ag-example
Add `uconvert` and chemistry example
2 parents 7d229fd + 4c34768 commit 0c665e2

File tree

7 files changed

+98
-2
lines changed

7 files changed

+98
-2
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,12 @@ julia> expand_units(x^2)
207207
8.987551787368176e16 m² s⁻⁴
208208
```
209209

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-9u"m") # can also write 5e-9u"m" |> uconvert(us"nm")
213+
5.0 nm
214+
```
215+
210216
### Arrays
211217

212218
For working with an array of quantities that have the same dimensions,

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ makedocs(;
3939
),
4040
pages=[
4141
"Home" => "index.md",
42+
"Examples" => "examples.md",
4243
"Utilities" => "api.md",
4344
"Units" => "units.md",
4445
"Constants" => "constants.md",

docs/src/examples.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Toy Examples with Code
2+
3+
## 1. Solving a Chemistry Homework Problem
4+
5+
On your chemistry homework, you are faced with the following problem on the photoelectric effect[^1]:
6+
7+
[^1]: Attribution: [MIT OCW](https://ocw.mit.edu/courses/5-111sc-principles-of-chemical-science-fall-2014/resources/mit5_111f14_lec04soln/)
8+
9+
> In a photoelectric effect experiment, electrons are ejected from a titanium surface (work function ``\Phi = 4.33\mathrm{eV}``) following irradition with UV light.
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+
12+
Let's solve this problem with `DynamicQuantities.jl`!
13+
```jldoctest examples
14+
julia> using DynamicQuantities
15+
16+
julia> using DynamicQuantities.Constants: h, m_e
17+
18+
julia> Φ = 4.33u"Constants.eV" # work function
19+
6.93742482522e-19 m² kg s⁻²
20+
21+
julia> E = 7.2e-19u"J" # incident energy
22+
7.2e-19 m² kg s⁻²
23+
24+
julia> p = sqrt(2 * m_e * (E - Φ)) # momentum of ejected electrons
25+
2.1871890716439906e-25 m kg s⁻¹
26+
27+
julia> λ = h / p # wavelength of ejected electrons
28+
3.029491247878056e-9 m
29+
30+
julia> uconvert(us"nm", λ) # return answer in nanometers
31+
3.0294912478780556 nm
32+
```
33+
Since units are automatically propagated, we can verify the dimension of our answer and all intermediates.
34+
Also, using `DynamicQuantities.Constants`, we were able to obtain the (dimensionful!) values of all necessary constants without typing them ourselves.
35+

docs/src/symbolic_units.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,9 @@ To convert a quantity to its regular base SI units, use `expand_units`:
1919
```@docs
2020
expand_units
2121
```
22+
23+
To convert a quantity in regular base SI units to corresponding symbolic units, use `uconvert`:
24+
25+
```@docs
26+
uconvert
27+
```

src/DynamicQuantities.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export AbstractQuantity, AbstractDimensions
55
export Quantity, Dimensions, SymbolicDimensions, QuantityArray, DimensionError
66
export ustrip, dimension
77
export ulength, umass, utime, ucurrent, utemperature, uluminosity, uamount
8-
export uparse, @u_str, sym_uparse, @us_str, expand_units
8+
export uparse, @u_str, sym_uparse, @us_str, expand_units, uconvert
99

1010
include("fixed_rational.jl")
1111
include("types.jl")

src/symbolic_dimensions.jl

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,37 @@ end
9898
9999
Expand the symbolic units in a quantity to their base SI form.
100100
In other words, this converts a `Quantity` with `SymbolicDimensions`
101-
to one with `Dimensions`.
101+
to one with `Dimensions`. The opposite of this function is `uconvert`,
102+
for converting to specific symbolic units, or `convert(Quantity{<:Any,<:SymbolicDimensions}, q)`,
103+
for assuming SI units as the output symbols.
102104
"""
103105
function expand_units(q::Q) where {T,R,D<:SymbolicDimensions{R},Q<:AbstractQuantity{T,D}}
104106
return convert(constructor_of(Q){T,Dimensions{R}}, q)
105107
end
106108
expand_units(q::QuantityArray) = expand_units.(q)
107109

110+
"""
111+
uconvert(qout::AbstractQuantity{<:Any, <:SymbolicDimensions}, q::AbstractQuantity{<:Any, <:Dimensions})
112+
113+
Convert a quantity `q` with base SI units to the symbolic units of `qout`, for `q` and `qout` with compatible units.
114+
Mathematically, the result has value `q / expand_units(qout)` and units `dimension(qout)`.
115+
"""
116+
function uconvert(qout::AbstractQuantity{<:Any, <:SymbolicDimensions}, q::AbstractQuantity{<:Any, <:Dimensions})
117+
@assert isone(ustrip(qout)) "You passed a quantity with a non-unit value to uconvert."
118+
qout_expanded = expand_units(qout)
119+
dimension(q) == dimension(qout_expanded) || throw(DimensionError(q, qout_expanded))
120+
new_val = ustrip(q) / ustrip(qout_expanded)
121+
new_dim = dimension(qout)
122+
return new_quantity(typeof(q), new_val, new_dim)
123+
end
124+
125+
"""
126+
uconvert(qout::AbstractQuantity{<:Any, <:SymbolicDimensions})
127+
128+
Create a function that converts an input quantity `q` with base SI units to the symbolic units of `qout`, i.e
129+
a function equivalent to `q -> uconvert(qout, q)`.
130+
"""
131+
uconvert(qout::AbstractQuantity{<:Any, <:SymbolicDimensions}) = Base.Fix1(uconvert, qout)
108132

109133
Base.copy(d::SymbolicDimensions) = SymbolicDimensions(copy(getfield(d, :nzdims)), copy(getfield(d, :nzvals)))
110134
function Base.:(==)(l::SymbolicDimensions, r::SymbolicDimensions)

test/unittests.jl

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,30 @@ end
591591
@test_throws "rad is not available as a symbol" sym5.rad
592592
end
593593

594+
@testset "uconvert" begin
595+
@test uconvert(us"nm", 5e-9u"m") (5e-9u"m" |> uconvert(us"nm")) 5us"nm"
596+
@test_throws DimensionError uconvert(us"nm * J", 5e-9u"m")
597+
598+
q = 1.5u"Constants.M_sun"
599+
qs = uconvert(us"Constants.M_sun", 5.0 * q)
600+
@test qs 7.5us"Constants.M_sun"
601+
@test dimension(qs)[:kg] == 0
602+
@test dimension(qs)[:g] == 0
603+
@test dimension(qs)[:M_sun] == 1
604+
@test expand_units(qs) 5.0 * q
605+
606+
# Refuses to convert to non-unit quantities:
607+
@test_throws AssertionError uconvert(1.2us"m", 1.0u"m")
608+
VERSION >= v"1.8" &&
609+
@test_throws "You passed a quantity" uconvert(1.2us"m", 1.0u"m")
610+
611+
# Different types require converting both arguments:
612+
q = convert(Quantity{Float16}, 1.5u"g")
613+
qs = uconvert(convert(Quantity{Float16}, us"g"), 5 * q)
614+
@test typeof(qs) <: Quantity{Float16,<:SymbolicDimensions{<:Any}}
615+
@test qs 7.5us"g"
616+
end
617+
594618
@testset "Test ambiguities" begin
595619
R = DEFAULT_DIM_BASE_TYPE
596620
x = convert(R, 10)

0 commit comments

Comments
 (0)