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 13 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/Project.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
[deps]
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
DynamicQuantities = "06fc5a27-2a28-4c7c-a15d-362465fb6821"
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
38 changes: 38 additions & 0 deletions docs/src/examples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Toy Examples with Code

```jldoctest examples
julia> using DynamicQuantities

```

## 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.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
29 changes: 28 additions & 1 deletion src/symbolic_dimensions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,40 @@ 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})
uconvert(ustr::String, 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)`.
For string input, `qout` is created by parsing `ustr` as a symbolic unit, i.e. `qout = sym_uparse(ustr)`.
"""
function uconvert(qout::AbstractQuantity{<:Any, <:SymbolicDimensions}, q::AbstractQuantity{<:Any, <:Dimensions})
qout_expanded = expand_units(qout)
dimension(q) == dimension(qout_expanded) || throw(DimensionError(q, qout_expanded))
return new_quantity(typeof(qout), ustrip(q) / ustrip(qout_expanded), dimension(qout))
end
uconvert(ustr::String, q::AbstractQuantity{<:Any, <:Dimensions}) = uconvert(sym_uparse(ustr), q)

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

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)`.
For string input, `qout` is created by parsing `ustr` as a symbolic unit, i.e. `qout = sym_uparse(ustr)`.
"""
uconvert(qout::AbstractQuantity{<:Any, <:SymbolicDimensions}) = Base.Fix1(uconvert, qout)
uconvert(ustr::String) = uconvert(sym_uparse(ustr))

Base.copy(d::SymbolicDimensions) = SymbolicDimensions(copy(getfield(d, :nzdims)), copy(getfield(d, :nzvals)))
function Base.:(==)(l::SymbolicDimensions, r::SymbolicDimensions)
Expand Down
3 changes: 3 additions & 0 deletions test/unittests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,9 @@ end
@test us"Constants.h" != us"h"
@test expand_units(us"Constants.h") == u"Constants.h"

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

# Actually expands to:
@test dimension(us"Constants.h")[:m] == 2
@test dimension(us"Constants.h")[:s] == -1
Expand Down