-
Notifications
You must be signed in to change notification settings - Fork 20
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
Create in-house units module #22
Conversation
Benchmark Results
Benchmark PlotsA plot of the benchmark results have been uploaded as an artifact to the workflow run for this PR. |
I'm debating whether to add c, the speed of light, as a derived unit. I'm hesitant because c feels more like a physical constant, and I wouldn't want to mislead people that physical constants are present: if c is included, people might think h (planck constant) is also included. But in actuality, h = hours. I think I won't include it, but just wanted to hear if others have thoughts. The other non-SI units I added are L (liters), bar, and eV - all of which are fairly common in scientific calculations. eV is technically a "constant" but due to its usage it feels more like a unit. (eV, year, and day are probably the only ones which can get away with being "units" despite technically being physical constants.) |
Quick update: after some good points made by @mcabbott, this PR now sets units to the type julia> typeof(u"1")
Quantity{Int64, DynamicQuantities.FixedRational{Int32, 25200}}
julia> typeof(u"km")
Quantity{Rational{Int64}, DynamicQuantities.FixedRational{Int32, 25200}}
julia> typeof(Float16(1.0)u"km")
Quantity{Float16, DynamicQuantities.FixedRational{Int32, 25200}}
julia> typeof(1f0u"km")
Quantity{Float32, DynamicQuantities.FixedRational{Int32, 25200}}
julia> typeof(1.0u"km")
Quantity{Float64, DynamicQuantities.FixedRational{Int32, 25200}} Edit: the one downside is that we can't store |
Hm, the one issue with this is you can get overflow errors for certain units: julia> u"fm^2"
ERROR: LoadError: OverflowError: 1000000000000000 * 1000000000000000 overflowed for type Int64 |
Reverted units back to Float64. |
I think we can merge this in a couple of days; let me know if there is any other feedback. |
@mcabbott Maybe something like this could be an option for the type of each unit? It's a Float64 that tries to demote itself whenever possible: struct WeakFloat <: AbstractFloat
x::Float64
end
Base.promote(x::WeakFloat, y::Float64) = (x.x, y)
Base.promote(x::WeakFloat, y::AbstractFloat) = (convert(typeof(y), x.x), y)
Base.promote(x::WeakFloat, y) = promote(x.x, y)
Base.promote(x, y::WeakFloat) = reverse(promote(y, x))
function Base.promote(x::WeakFloat, y::Rational{R}) where {R}
# If under eps * 1e5, we promote to a float:
abs(x.x) < eps(Float64) * 1e5 && return promote(x.x, y)
return (rationalize(R, x.x), y)
end
function Base.promote(x::WeakFloat, y::Integer)
abs(x.x) < eps(Float64) * 1e5 && return promote(x.x, y)
let r=rationalize(typeof(y), x.x)
return isinteger(r) ? (convert(typeof(y), r), y) : promote(r, y)
end
end Which gives you: julia> 2 * WeakFloat(100.0)
200
julia> (2//1) * WeakFloat(0.5)
1//1
julia> WeakFloat(1e-5) * 10000
1//10
julia> WeakFloat(1e-20) * 20
2.0e-19 The downside is that this is type unstable: the promoted type varies depending on how close the float is to 0. |
Thanks for the reviews, everyone! |
This adds an in-house units module, so now you can parse a wide variety of base and derived SI units without relying on Unitful.jl. It includes a few non SI units: min, h, day, year - but I purposefully didn't go too far beyond that, to avoid potential namespace ambiguities. I did not include any Gaussian units (like
erg
- which is used in astronomy), because Gaussian units require different formulas and I feel like mixing the unit systems might lead to confusion.This PR also makes it so that all physical quantities are displayed in SI base units "m¹ᐟ² kg¹ mol³", rather than the unevaluated "𝐋 ¹ᐟ² 𝐌 ¹ 𝐍 ³" representation, so representation is more obvious.
Here are all the available units and their prefixes:
I tried to add all of the common units. For physical constants or other weird unit systems, I think it is best we make the user define them themselves, to ensure they know what they are doing, and avoid potential namespace collisions.
You can access these units with
uparse
or@u_str
, as in Unitful. For example:You can still convert from Unitful.jl if you need to, but I think this is a more robust option.
What do you think? @oscardssmith @j-fu @odow
TODO:
upreferred
.