Skip to content

Commit

Permalink
Dispersion equivalence (#43)
Browse files Browse the repository at this point in the history
## 🚨 Checklist
- [x] Read and follow the [Contributing
Guidelines](https://github.com/cmichelenstrofer/.github/blob/main/CONTRIBUTING.md).
- [x] Provide a more detailed description below, including [referencing
or closing the relevant
issue(s)](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue)

💔 Thanks!

## PR Description
  - Closes #42.
- Adds an equivalence to extend `Periodic` by using a specific,
user-supplied, dispersion-relation
  - This allows converting between temporal and spatial frequencies
  • Loading branch information
cmichelenstrofer authored Dec 11, 2023
1 parent 4924035 commit 6d96784
Show file tree
Hide file tree
Showing 7 changed files with 274 additions and 76 deletions.
1 change: 1 addition & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[deps]
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
DocumenterCitations = "daee34ce-89f3-4625-b898-19384cb65244"
Roots = "f2b01f46-fcfa-551c-844a-d8ac1e96c665"
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"
UnitfulAngles = "6fb2a4bd-7999-5318-a3b2-8ad61056cd98"

Expand Down
7 changes: 5 additions & 2 deletions docs/src/guide/derived.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ DimensionfulAngles.AngularPeriod
DimensionfulAngles.AngularWavenumber
```

## Periodic equivalence
## Periodic and Dispersion equivalences
For periodic responses there are several analogous ways to measure the repeat period: period `T` (`𝐓`, `s`), frequency `f` (`1/𝐓`, `Hz=1/s`), or angular frequency `ω` (`𝐀/𝐓`, `rad/s`).
These are [related by](https://en.wikipedia.org/wiki/Angular_frequency)

Expand All @@ -97,10 +97,13 @@ Additionally an angular period and angular wavelength can be defined analogously
![Diagram showing graphically the relationships between the various properties of harmonic waves: frequency, period, wavelength, angular frequency, and wavenumber.](../assets/Commutative_diagram_of_harmonic_wave_properties.svg)\
*image-source: Waldir, CC BY-SA 4.0 <https://creativecommons.org/licenses/by-sa/4.0>, via Wikimedia Commons*

*DimensionfulAngles.jl* provides [`Periodic`](@ref) a [UnitfulEquivalences.jl](https://sostock.github.io/UnitfulEquivalences.jl/stable/) `Equivalence` to convert between temporal or spatial period, frequency, angular frequency, and angular period of a periodic response.
*DimensionfulAngles.jl* provides [`Periodic`](@ref), a [UnitfulEquivalences.jl](https://sostock.github.io/UnitfulEquivalences.jl/stable/) `Equivalence` to convert between temporal or spatial period, frequency, angular frequency, and angular period of a periodic response.

It also provides [`Dispersion`](@ref), which extends [`Periodic`](@ref) to convert between temporal and spatial values using a specific [dispersion relation](https://en.wikipedia.org/wiki/Dispersion_relation) (or equivalently a [phase velocity](https://en.wikipedia.org/wiki/Phase_velocity) as used in the image above)).

```@docs
DimensionfulAngles.Periodic
DimensionfulAngles.Dispersion
```

## [Syntax](@id derived_syntax)
Expand Down
32 changes: 19 additions & 13 deletions src/DimensionfulAngles.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,29 +28,34 @@ module DimensionfulAngles

# using Base: Base # extend: see `base.jl` for full list of functions extended
using Unitful: Unitful # extend: has_unit_spacing, uconvert
using Unitful: cd as cdᵤ, lm, lx, m, minute, promotion, rad, rpm, rps, s, sr, 𝐉, 𝐋, 𝐓, °
using Unitful: cd as cdᵤ, Hz, lm, lx, m, minute, promotion, rad, rpm, rps, s, sr, 𝐉, 𝐋, 𝐓, °
using Unitful: ContextUnits, Dimension, Dimensions, DimensionlessQuantity, FixedUnits
using Unitful: FreeUnits, Frequency, FrequencyFreeUnits, Length, MixedUnits, NoDims
using Unitful: NoUnits, Number, Quantity, Time, Unitlike, Unit, Units, Wavenumber
using Unitful: @dimension, @refunit, @derived_dimension, @unit
using Unitful: dimension, register, uconvert, unit, ustrip
using UnitfulEquivalences: Equivalence, @eqrelation
using UnitfulEquivalences: UnitfulEquivalences # extend: edconvert
using UnitfulEquivalences: dimtype, edconvert, Equivalence, @eqrelation
using UnitfulAngles: turn, doubleTurn, halfTurn, quadrant, sextant, octant, clockPosition
using UnitfulAngles: hourAngle, compassPoint, hexacontade, brad, diameterPart, grad
using UnitfulAngles: arcminute, arcsecond
using UnitfulAngles: mas, μas, pas

export @ua_str
export θ₀
export Periodic
export Periodic, Dispersion
export sexagesimal, show_sexagesimal
# export 𝐀, Angle, AngleUnits, AngleFreeUnits
# export SolidAngle, SolidAngleUnits, SolidAngleFreeUnits
# export AngularVelocity, AngularVelocityUnits, AngularVelocityFreeUnits
# export AngularAcceleration, AngularAccelerationUnits, AngularAccelerationFreeUnits
# export AngularPeriod, AngularPeriodUnits, AngularPeriodFreeUnits
# export AngularWavenumber, AngularWavenumberUnits, AngularWavenumberFreeUnits
# export AngularWavelength, AngularWavelengthUnits, AngularWavelengthFreeUnits

# unexported:
# (all angle and derived units, including prefixed units)
# 𝐀, Angle, AngleUnits, AngleFreeUnits
# SolidAngle, SolidAngleUnits, SolidAngleFreeUnits
# AngularVelocity, AngularVelocityUnits, AngularVelocityFreeUnits
# AngularAcceleration, AngularAccelerationUnits, AngularAccelerationFreeUnits
# AngularPeriod, AngularPeriodUnits, AngularPeriodFreeUnits
# AngularWavenumber, AngularWavenumberUnits, AngularWavenumberFreeUnits
# AngularWavelength, AngularWavelengthUnits, AngularWavelengthFreeUnits


# Dimension
"""
Expand Down Expand Up @@ -145,12 +150,13 @@ julia> 2.1ua"rad" / θ₀
const θ₀ = (1 // 1)radᵃ

# Other functionalities.
include("units.jl") # Other units of angle.
include("derived.jl") # Units and functionalities for derived dimensions.
include("units.jl") # Define other units of angle.
include("derived.jl") # Define derived units and dimensions.
include("convert.jl") # Convert to/from `Unitful`
include("uamacro.jl") # String macro for using dimensionful units.
include("base.jl") # Extend Base functions to work with angular quantities.
include("defaults.jl") # Submodule to flood workspace with unit types.
include("defaults.jl") # Submodule to flood workspace with unit types.
include("equivalences.jl") # `UnitfulEquivalences` containing angular dimensions

# Register new units and dimensions with Unitful.jl.
const localpromotion = copy(promotion)
Expand Down
62 changes: 1 addition & 61 deletions src/derived.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Units and functionalities for derived dimensions of Angle.
# Derived units and dimensions of Angle.

# Solid angle
@derived_dimension SolidAngle (𝐀*𝐀) true
Expand Down Expand Up @@ -80,63 +80,3 @@ See also [`DimensionfulAngles.radᵃ`](@ref).
@derived_dimension AngularWavenumber (𝐀*𝐋^-1) true
@derived_dimension AngularWavelength (𝐋*𝐀^-1) true
@derived_dimension AngularPeriod (𝐓*𝐀^-1) true

# periodic equivalence for both temporal and spatial frequency
"""
Periodic()
Equivalence to convert between temporal or spatial period, frequency, and
[angular frequency](https://en.wikipedia.org/wiki/Angular_frequency)
according to the relations ``f = ω/2π = 1/T``, where
- ``f`` is the (temporal) frequency,
- ``ω`` is the (temporal) angular frequency,
- ``T`` is the (temporal) period,
- ``T̄`` is the (temporal) angular period,
and ``ν = k/2π = 1/λ = 1/(2πλ̄)``, where
- ``ν`` is the (spatial) frequency (linear wavenumber),
- ``k`` is the (spatial) angular frequency (angular wavenumber),
- ``λ`` is the (spatial) period (linear wavelength), and
- ``λ̄`` is the (spatial) angular period (angular wavelength).
# Example
```jldoctest; filter = r"(\\d*).(\\d{1,10})\\d+" => s"\\1.\\2"
julia> using Unitful
julia> using DimensionfulAngles
julia> uconvert(u"s", 10u"Hz", Periodic())
0.1 s
julia> uconvert(u"radᵃ/s", 1u"Hz", Periodic())
6.283185307179586 rad s⁻¹
```
"""
struct Periodic <: Equivalence end
# temporal
@eqrelation Periodic (AngularVelocity / Frequency = 2π * radᵃ)
@eqrelation Periodic (Frequency * Time = 1)
@eqrelation Periodic (AngularVelocity * Time = 2π * radᵃ)
@eqrelation Periodic (AngularVelocity * AngularPeriod = 1)
@eqrelation Periodic (Time / AngularPeriod = 2π * radᵃ)
@eqrelation Periodic (AngularPeriod * Frequency = 1/(2π * radᵃ))
# spatial
@eqrelation Periodic (AngularWavenumber / Wavenumber = 2π * radᵃ)
@eqrelation Periodic (Wavenumber * Length = 1)
@eqrelation Periodic (AngularWavenumber * Length = 2π * radᵃ)
@eqrelation Periodic (AngularWavenumber * AngularWavelength = 1)
@eqrelation Periodic (Length / AngularWavelength = 2π * radᵃ)
@eqrelation Periodic (AngularWavelength * Wavenumber = 1/(2π * radᵃ))
# default to `uconvert` behavior, temporal
@eqrelation Periodic (Frequency / Frequency = 1)
@eqrelation Periodic (Time / Time = 1)
@eqrelation Periodic (AngularVelocity / AngularVelocity = 1)
@eqrelation Periodic (AngularPeriod / AngularPeriod = 1)
# default to `uconvert` behavior, spatial
@eqrelation Periodic (Wavenumber / Wavenumber = 1)
@eqrelation Periodic (Length / Length = 1)
@eqrelation Periodic (AngularWavenumber / AngularWavenumber = 1)
@eqrelation Periodic (AngularWavelength / AngularWavelength = 1)
200 changes: 200 additions & 0 deletions src/equivalences.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
# equivalences containing angular dimensions

# periodic equivalence for both temporal and spatial frequency
"""
Periodic()
Equivalence to convert between temporal or spatial period, frequency,
[angular frequency](https://en.wikipedia.org/wiki/Angular_frequency), and angular period.
These quantities are related by ``f = ω/2π = 1/T = 1/(2πT̅)``, where
- ``f`` is the (temporal) frequency,
- ``ω`` is the (temporal) angular frequency,
- ``T`` is the (temporal) period,
- ``T̄`` is the (temporal) angular period,
and ``ν = k/2π = 1/λ = 1/(2πλ̄)``, where
- ``ν`` is the (spatial) frequency (linear wavenumber),
- ``k`` is the (spatial) angular frequency (angular wavenumber),
- ``λ`` is the (spatial) period (linear wavelength), and
- ``λ̄`` is the (spatial) angular period (angular wavelength).
See also [`DimensionfulAngles.Dispersion`](@ref)
# Example
```jldoctest; filter = r"(\\d*).(\\d{1,10})\\d+" => s"\\1.\\2"
julia> using Unitful
julia> using DimensionfulAngles
julia> uconvert(u"s", 10u"Hz", Periodic())
0.1 s
julia> uconvert(u"radᵃ/s", 1u"Hz", Periodic())
6.283185307179586 rad s⁻¹
```
"""
struct Periodic <: Equivalence end
# temporal
@eqrelation Periodic (AngularVelocity / Frequency = 2π * radᵃ)
@eqrelation Periodic (Frequency * Time = 1)
@eqrelation Periodic (AngularVelocity * Time = 2π * radᵃ)
@eqrelation Periodic (AngularVelocity * AngularPeriod = 1)
@eqrelation Periodic (Time / AngularPeriod = 2π * radᵃ)
@eqrelation Periodic (AngularPeriod * Frequency = 1/(2π * radᵃ))
# spatial
@eqrelation Periodic (AngularWavenumber / Wavenumber = 2π * radᵃ)
@eqrelation Periodic (Wavenumber * Length = 1)
@eqrelation Periodic (AngularWavenumber * Length = 2π * radᵃ)
@eqrelation Periodic (AngularWavenumber * AngularWavelength = 1)
@eqrelation Periodic (Length / AngularWavelength = 2π * radᵃ)
@eqrelation Periodic (AngularWavelength * Wavenumber = 1/(2π * radᵃ))
# default to `uconvert` behavior, temporal
@eqrelation Periodic (Frequency / Frequency = 1)
@eqrelation Periodic (Time / Time = 1)
@eqrelation Periodic (AngularVelocity / AngularVelocity = 1)
@eqrelation Periodic (AngularPeriod / AngularPeriod = 1)
# default to `uconvert` behavior, spatial
@eqrelation Periodic (Wavenumber / Wavenumber = 1)
@eqrelation Periodic (Length / Length = 1)
@eqrelation Periodic (AngularWavenumber / AngularWavenumber = 1)
@eqrelation Periodic (AngularWavelength / AngularWavelength = 1)


# periodic equivalence with a specific dispersion relation relating temporal and spatial
# frequencies
"""
Dispersion(; dispersion=nothing, dispersion_inverse=nothing)
Equivalence to convert between temporal and spatial frequencies using a specific
[dispersion relation](https://en.wikipedia.org/wiki/Dispersion_relation).
This extends the Periodic() equivalence to convert between spatial and temporal quantities
based on the provided dispersion relation.
See also [`DimensionfulAngles.Periodic`](@ref).
# Example
```jldoctest; filter = r"(\\d*).(\\d{1,10})\\d+" => s"\\1.\\2"
julia> using DimensionfulAngles, Unitful
julia> g = Unitful.gn # gravitational acceleration
9.80665 m s⁻²
julia> deepwater = Dispersion(
dispersion = (k -> √(g*k*θ₀)), dispersion_inverse = (ω -> ω^2/(g*θ₀))
);
julia> uconvert(u"radᵃ/mm", 1.0u"Hz", deepwater)
0.004025678249387654 rad mm⁻¹
```
Some dispersion relations do not have an expressible inverse.
In such cases using `Roots.jl` might be beneficial.
For example, here is how we could use the
linear [water wave dispersion](https://en.wikipedia.org/wiki/Dispersion_(water_waves))
without deep water approximation:
```jldoctest; filter = r"(\\d*).(\\d{1,10})\\d+" => s"\\1.\\2"
julia> using DimensionfulAngles, Unitful, Roots
julia> g = Unitful.gn # gravitational acceleration
9.80665 m s⁻²
julia> k0 = (2π)u"radᵃ/m" # initial guess: 1m wavelength
6.283185307179586 rad m⁻¹
julia> h = 0.5u"m" # water depth
0.5 m
julia> waterwaves = Dispersion(
dispersion = (k -> √(k*θ₀*g*tanh(k*h/θ₀))),
dispersion_inverse = (ω -> solve(ZeroProblem(k -> k - ω^2/(g*tanh(k*h/θ₀))/θ₀, k0)))
);
julia> uconvert(u"Hz", 0.004025678249387654u"radᵃ/mm", waterwaves)
0.9823052153509486 Hz
julia> h = (Inf)u"m" # water depth
Inf m
julia> waterwaves = Dispersion(
dispersion = ( k -> √(k*θ₀*g*tanh(k*h/θ₀)) ),
dispersion_inverse = (ω -> solve(ZeroProblem(k -> k - ω^2/(g*tanh(k*h/θ₀))/θ₀, k0)))
);
julia> uconvert(u"Hz", 0.004025678249387654u"radᵃ/mm", waterwaves) ≈ 1u"Hz"
true
```
"""
struct Dispersion <: Equivalence
dispersion::Union{Function, Nothing}
dispersion_inverse::Union{Function, Nothing}

function Dispersion(;
dispersion::Union{Function, Nothing}=nothing,
dispersion_inverse::Union{Function, Nothing}=nothing
)
return new(dispersion, dispersion_inverse)
end
end

const _temporal_frequency_dims = [Time, Frequency, AngularPeriod, AngularVelocity]
const _spatial_frequency_dims = [Length, Wavenumber, AngularWavelength, AngularWavenumber]
const _temporal_frequency_units = Dict(
Time => s, Frequency => Hz, AngularPeriod => s/radᵃ, AngularVelocity => radᵃ/s)
const _spatial_frequency_units = Dict(
Length => m, Wavenumber => 1/m, AngularWavelength => m/radᵃ, AngularWavenumber => radᵃ/m
)

# use all the equivalences in Periodic
for T1 _temporal_frequency_dims, T2 _temporal_frequency_dims
@eval begin
function UnitfulEquivalences.edconvert(d::dimtype($T1), x::$T2, ::Dispersion)
return edconvert(d, x, Periodic())
end
end
end

for T1 _spatial_frequency_dims, T2 _spatial_frequency_dims
@eval begin
function UnitfulEquivalences.edconvert(d::dimtype($T1), x::$T2, ::Dispersion)
return edconvert(d, x, Periodic())
end
end
end

# add new equivalences between temporal <-> spatial frequencies
for D_in _spatial_frequency_dims, D_out _temporal_frequency_dims
@eval begin
function UnitfulEquivalences.edconvert(
::dimtype($D_out), x::$D_in, equivalence::Dispersion
)
isnothing(equivalence.dispersion) && throw(ArgumentError(
"`dispersion` function not defined"))
k = uconvert(radᵃ/m, x, Periodic())
ω = equivalence.dispersion(k)
u = _temporal_frequency_units[$D_out]
return uconvert(u, ω, Periodic())
end
end
end

for D_in _temporal_frequency_dims, D_out _spatial_frequency_dims
@eval begin
function UnitfulEquivalences.edconvert(
::dimtype($D_out), x::$D_in, equivalence::Dispersion
)
isnothing(equivalence.dispersion_inverse) && throw(ArgumentError(
"`dispersion_inverse` function not defined"))
ω = uconvert(radᵃ/s, x, Periodic())
k = equivalence.dispersion_inverse(ω)
u = _spatial_frequency_units[$D_out]
return uconvert(u, k, Periodic())
end
end
end
1 change: 1 addition & 0 deletions test/Project.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[deps]
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
Roots = "f2b01f46-fcfa-551c-844a-d8ac1e96c665"
SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"
Expand Down
Loading

0 comments on commit 6d96784

Please sign in to comment.