Skip to content

Add uconvert and chemistry example #48

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Oct 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,12 @@ julia> expand_units(x^2)
8.987551787368176e16 m² s⁻⁴
```

You can also convert a quantity in regular base SI units to symbolic units with `uconvert`:
```julia
julia> uconvert(us"nm", 5e-9u"m") # can also write 5e-9u"m" |> uconvert(us"nm")
5.0 nm
```

### Arrays

For working with an array of quantities that have the same dimensions,
Expand Down
1 change: 1 addition & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ makedocs(;
),
pages=[
"Home" => "index.md",
"Examples" => "examples.md",
"Utilities" => "api.md",
"Units" => "units.md",
"Constants" => "constants.md",
Expand Down
35 changes: 35 additions & 0 deletions docs/src/examples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Toy Examples with Code

## 1. Solving a Chemistry Homework Problem

On your chemistry homework, you are faced with the following problem on the photoelectric effect[^1]:

[^1]: Attribution: [MIT OCW](https://ocw.mit.edu/courses/5-111sc-principles-of-chemical-science-fall-2014/resources/mit5_111f14_lec04soln/)

> In a photoelectric effect experiment, electrons are ejected from a titanium surface (work function ``\Phi = 4.33\mathrm{eV}``) following irradition with UV light.
> 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.

Let's solve this problem with `DynamicQuantities.jl`!
```jldoctest examples
julia> using DynamicQuantities

julia> using DynamicQuantities.Constants: h, m_e

julia> Φ = 4.33u"Constants.eV" # work function
6.93742482522e-19 m² kg s⁻²

julia> E = 7.2e-19u"J" # incident energy
7.2e-19 m² kg s⁻²

julia> p = sqrt(2 * m_e * (E - Φ)) # momentum of ejected electrons
2.1871890716439906e-25 m kg s⁻¹

julia> λ = h / p # wavelength of ejected electrons
3.029491247878056e-9 m

julia> uconvert(us"nm", λ) # return answer in nanometers
3.0294912478780556 nm
```
Since units are automatically propagated, we can verify the dimension of our answer and all intermediates.
Also, using `DynamicQuantities.Constants`, we were able to obtain the (dimensionful!) values of all necessary constants without typing them ourselves.

6 changes: 6 additions & 0 deletions docs/src/symbolic_units.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,9 @@ To convert a quantity to its regular base SI units, use `expand_units`:
```@docs
expand_units
```

To convert a quantity in regular base SI units to corresponding symbolic units, use `uconvert`:

```@docs
uconvert
```
2 changes: 1 addition & 1 deletion src/DynamicQuantities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export AbstractQuantity, AbstractDimensions
export Quantity, Dimensions, SymbolicDimensions, QuantityArray, DimensionError
export ustrip, dimension
export ulength, umass, utime, ucurrent, utemperature, uluminosity, uamount
export uparse, @u_str, sym_uparse, @us_str, expand_units
export uparse, @u_str, sym_uparse, @us_str, expand_units, uconvert

include("fixed_rational.jl")
include("types.jl")
Expand Down
26 changes: 25 additions & 1 deletion src/symbolic_dimensions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,37 @@ end

Expand the symbolic units in a quantity to their base SI form.
In other words, this converts a `Quantity` with `SymbolicDimensions`
to one with `Dimensions`.
to one with `Dimensions`. The opposite of this function is `uconvert`,
for converting to specific symbolic units, or `convert(Quantity{<:Any,<:SymbolicDimensions}, q)`,
for assuming SI units as the output symbols.
"""
function expand_units(q::Q) where {T,R,D<:SymbolicDimensions{R},Q<:AbstractQuantity{T,D}}
return convert(constructor_of(Q){T,Dimensions{R}}, q)
end
expand_units(q::QuantityArray) = expand_units.(q)

"""
uconvert(qout::AbstractQuantity{<:Any, <:SymbolicDimensions}, q::AbstractQuantity{<:Any, <:Dimensions})

Convert a quantity `q` with base SI units to the symbolic units of `qout`, for `q` and `qout` with compatible units.
Mathematically, the result has value `q / expand_units(qout)` and units `dimension(qout)`.
"""
function uconvert(qout::AbstractQuantity{<:Any, <:SymbolicDimensions}, q::AbstractQuantity{<:Any, <:Dimensions})
@assert isone(ustrip(qout)) "You passed a quantity with a non-unit value to uconvert."
qout_expanded = expand_units(qout)
dimension(q) == dimension(qout_expanded) || throw(DimensionError(q, qout_expanded))
new_val = ustrip(q) / ustrip(qout_expanded)
new_dim = dimension(qout)
return new_quantity(typeof(q), new_val, new_dim)
end

"""
uconvert(qout::AbstractQuantity{<:Any, <:SymbolicDimensions})

Create a function that converts an input quantity `q` with base SI units to the symbolic units of `qout`, i.e
a function equivalent to `q -> uconvert(qout, q)`.
"""
uconvert(qout::AbstractQuantity{<:Any, <:SymbolicDimensions}) = Base.Fix1(uconvert, qout)

Base.copy(d::SymbolicDimensions) = SymbolicDimensions(copy(getfield(d, :nzdims)), copy(getfield(d, :nzvals)))
function Base.:(==)(l::SymbolicDimensions, r::SymbolicDimensions)
Expand Down
24 changes: 24 additions & 0 deletions test/unittests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,30 @@ end
@test_throws "rad is not available as a symbol" sym5.rad
end

@testset "uconvert" begin
@test uconvert(us"nm", 5e-9u"m") ≈ (5e-9u"m" |> uconvert(us"nm")) ≈ 5us"nm"
@test_throws DimensionError uconvert(us"nm * J", 5e-9u"m")

q = 1.5u"Constants.M_sun"
qs = uconvert(us"Constants.M_sun", 5.0 * q)
@test qs ≈ 7.5us"Constants.M_sun"
@test dimension(qs)[:kg] == 0
@test dimension(qs)[:g] == 0
@test dimension(qs)[:M_sun] == 1
@test expand_units(qs) ≈ 5.0 * q

# Refuses to convert to non-unit quantities:
@test_throws AssertionError uconvert(1.2us"m", 1.0u"m")
VERSION >= v"1.8" &&
@test_throws "You passed a quantity" uconvert(1.2us"m", 1.0u"m")

# Different types require converting both arguments:
q = convert(Quantity{Float16}, 1.5u"g")
qs = uconvert(convert(Quantity{Float16}, us"g"), 5 * q)
@test typeof(qs) <: Quantity{Float16,<:SymbolicDimensions{<:Any}}
@test qs ≈ 7.5us"g"
end

@testset "Test ambiguities" begin
R = DEFAULT_DIM_BASE_TYPE
x = convert(R, 10)
Expand Down