painterqubits / unitful.jl Goto Github PK
View Code? Open in Web Editor NEWPhysical quantities with arbitrary units
License: Other
Physical quantities with arbitrary units
License: Other
It would be nice to have the ability to transform from on unit system, say standard SI, to another, say atomic units, without having to specify the explicit units as with uconvert
.
I'm not sure exactly how to go about it. One way would be have a function
system = u"g * km * eV"
simplify(a, system)
We can then project units(simplify(a))
(simplified as in #34) onto the units in the second argument + an orthogonal subspace of units defined by: (i) just SI units, or (ii) the units of simplify(a * system)
over each individual dimension. I think I would go for (ii).
One caveat is users entering a unit system that is not linearly independent.
Another possibility is to define a new type with an appropriate *
operator. And then proceed in a fairly similar manner. The difference is that the type and associated arithmetic could be made to guarantee that the units are linearly independent. And uconvert(a, system)
could be used.
Comments and preferences?
@ajkeller34, unless I'm mistaken, the following functionality (simplify
) does not yet exist. Happy to try and implement it if it doesn't:
notsosimple = 1u"m/ft*kg"
simple = simplify(notsosimple)
@assert units(simple) === units(1u"kg")
It should be possible to determine which unit to use of m or ft, the same way it is done for +, viapromote_type
As mentioned in #13 and a few other issues, there is currently no way to squash:
7 MeV/keV
7e+3 (dimensionless)
Can you add a simplify(unitful_variable)
method (or something like that) to handle this?
Even if it wasn't robust or kind of wonky at first, I think it still gives a good addition moving forward
I defined a Liter unit based on 1000cm^3, and it seemed to work most of the time, but then failed on some operations. If I instead define it based on m^3, then it seems to work as expected. I added the following lines to Defaults.jl
to test this:
@unit L "L" Liter (1//1000)*m^3 true
@unit altL "altL" altLiter 1000*cm^3 true
@unit arb "arb" Arb 1nA true
And then did the following tests at the REPL that all work.
julia> a = 1u"altL"
1 altL
julia> a = 1u"maltL"
1 maltL
julia> dimension(a)
𝐋^3
But then trying to get back to a unitless number fails
julia> Float64(1u"altL/cm^3")
ERROR: DomainError:
Cannot raise an integer x to a negative power -n.
Make x a float by adding a zero decimal (e.g. 2.0^-n instead of 2^-n), or write 1/x^n, float(x)^-n, or (x//1)^-n.
in power_by_squaring(::Int64, ::Int64) at ./intfuncs.jl:118
in basefactorhelper(::Float64, ::Int64, ::Int64, ::Rational{Int64}) at /Users/oneilg/.julia/v0.5/Unitful/src/Unitful.jl:268
in map(::Unitful.#basefactor, ::Tuple{Unitful.Unit{:Meter},Unitful.Unit{:altLiter}}) at ./tuple.jl:93
in basefactor(...) at /Users/oneilg/.julia/v0.5/Unitful/src/Unitful.jl:276
in convfact(...) at /Users/oneilg/.julia/v0.5/Unitful/src/Conversion.jl:85
in convert at /Users/oneilg/.julia/v0.5/Unitful/src/Conversion.jl:183 [inlined]
in Float64(::Unitful.Quantity{Int64,Unitful.Dimensions{()},Unitful.Units{(Unitful.Unit{:Meter}(-2,-3//1),Unitful.Unit{:altLiter}(0,1//1)),Unitful.Dimensions{()}}}) at ./sysimg.jl:53
The version based on m^3 works for going to a unitless number
julia> Float64(1u"L/cm^3")
1000.0
And arb (based on nA) shows the same error as altL
julia> Float64(1u"arb/A")
ERROR: DomainError:
Cannot raise an integer x to a negative power -n.
Make x a float by adding a zero decimal (e.g. 2.0^-n instead of 2^-n), or write 1/x^n, float(x)^-n, or (x//1)^-n.
in power_by_squaring(::Int64, ::Int64) at ./intfuncs.jl:118
in basefactorhelper(::Float64, ::Int64, ::Int64, ::Rational{Int64}) at /Users/oneilg/.julia/v0.5/Unitful/src/Unitful.jl:268
in map(::Unitful.#basefactor, ::Tuple{Unitful.Unit{:Ampere},Unitful.Unit{:Arb}}) at ./tuple.jl:93
in basefactor(...) at /Users/oneilg/.julia/v0.5/Unitful/src/Unitful.jl:276
in convfact(...) at /Users/oneilg/.julia/v0.5/Unitful/src/Conversion.jl:85
in convert at /Users/oneilg/.julia/v0.5/Unitful/src/Conversion.jl:183 [inlined]
in Float64(::Unitful.Quantity{Int64,Unitful.Dimensions{()},Unitful.Units{(Unitful.Unit{:Ampere}(0,-1//1),Unitful.Unit{:Arb}(0,1//1)),Unitful.Dimensions{()}}}) at ./sysimg.jl:53
The first time I ran a code which used Unitful in the tests when it was not installed it was fine, but the second time I got:
INFO: Building Unitful
WARNING: `Pkg.build("Unitful.jl")` was run, but /home/crackauc/.julia/v0.5/Unitful/deps/Defaults.jl already exists. No action has been taken. If you encounter problems, you might consider backing up then deleting the existing file at /home/crackauc/.julia/v0.5/Unitful/deps/Defaults.jl, then running `Pkg.build("Unitful")` again.
which means that Unitful leaves something behind when it's removed. I think this is related to #33? @tkelman might want to chime in.
Useful in general relativity. As described in this blog post: http://blog.sigfpe.com/2016/08/dimensionful-matrices.html
These seem like they would be different from Array{Any}. Unable to check if any of this is currently possible as I am not running Julia v0.5.
With the physical dimension of _Time, and the base relative unit, the unit of _Second,
we define _Minute and _Hour as exact integer multiples of the relative unit.
Adopting a coarser granularity, and granularity based relative unit, the unit of _Month,
we define _Year and _Century as exact integer multiples of the relative unit.
When the situation allows, we prefer to work with granularity based relative unit and
units of measure naturally expressible as exact scalars, relative to the relative unit.
This is distinct from support for multiple systems of units, yet its support, cleanly realized,
may be expressed sharing much of the approach that allows work with different unit systems
without forcing each unit system to be a facade laid over the expression of SI unit values.
Have you developed some design/implementation insight just well-suited?
julia> x, y = 10m, 1
(10 m, 1)
julia> px, py = promote(x, y)
(10 m, 1)
julia> ppx, ppy = promote(px, py)
(10 m, 1)
julia> (typeof(y), typeof(py), typeof(ppy))
(Int64, Quantity{Int64, Dimensions:{}, Units:{}}, Int64)
Tip of the hat to @timholy for finding this gem. Guess I should add a damping term to my type oscillator.
Hi Andrew,
Is this a problem on my side or am I updating at an unlucky moment?
In the past I had forked the Unitful.jl repository, but the last couple of weeks I just added the oil-field units I needed in my application package. Would it be'safer' for me to go back to forking Unitful.jl?
Thanks and regards,
Rob
Details:
First I got:
julia> using Unitful
INFO: Precompiling module Unitful.
ERROR: LoadError: could not open file /Users/rob/.julia/v0.5/Unitful/deps/Defaults.jl
in include_from_node1(::String) at ./loading.jl:488
in include_from_node1(::String) at /Applications/Julia-0.5.app/Contents/Resources/julia/lib/julia/sys.dylib:?
in defaults() at /Users/rob/.julia/v0.5/Unitful/src/User.jl:261
in include_from_node1(::String) at ./loading.jl:488
in include_from_node1(::String) at /Applications/Julia-0.5.app/Contents/Resources/julia/lib/julia/sys.dylib:?
in macro expansion; at ./none:2 [inlined]
in anonymous at ./:?
in eval(::Module, ::Any) at ./boot.jl:234
in eval(::Module, ::Any) at /Applications/Julia-0.5.app/Contents/Resources/julia/lib/julia/sys.dylib:?
in process_options(::Base.JLOptions) at ./client.jl:239
in _start() at ./client.jl:318
in _start() at /Applications/Julia-0.5.app/Contents/Resources/julia/lib/julia/sys.dylib:?
while loading /Users/rob/.julia/v0.5/Unitful/src/Unitful.jl, in expression starting on line 878
ERROR: Failed to precompile Unitful to /Users/rob/.julia/lib/v0.5/Unitful.ji.
in compilecache(::String) at ./loading.jl:593
in require(::Symbol) at ./loading.jl:422
in require(::Symbol) at /Applications/Julia-0.5.app/Contents/Resources/julia/lib/julia/sys.dylib:?
Now I'm getting:
julia> using Unitful
INFO: Precompiling module Unitful.
ERROR: LoadError: could not open file /Users/rob/.julia/v0.5/Unitful/deps/Defaults.jl
in include_from_node1(::String) at ./loading.jl:488
in include_from_node1(::String) at /Applications/Julia-0.5.app/Contents/Resources/julia/lib/julia/sys.dylib:?
in defaults() at /Users/rob/.julia/v0.5/Unitful/src/User.jl:261
in include_from_node1(::String) at ./loading.jl:488
in include_from_node1(::String) at /Applications/Julia-0.5.app/Contents/Resources/julia/lib/julia/sys.dylib:?
in macro expansion; at ./none:2 [inlined]
in anonymous at ./:?
in eval(::Module, ::Any) at ./boot.jl:234
in eval(::Module, ::Any) at /Applications/Julia-0.5.app/Contents/Resources/julia/lib/julia/sys.dylib:?
in process_options(::Base.JLOptions) at ./client.jl:239
in _start() at ./client.jl:318
in _start() at /Applications/Julia-0.5.app/Contents/Resources/julia/lib/julia/sys.dylib:?
while loading /Users/rob/.julia/v0.5/Unitful/src/Unitful.jl, in expression starting on line 878
ERROR: Failed to precompile Unitful to /Users/rob/.julia/lib/v0.5/Unitful.ji.
in compilecache(::String) at ./loading.jl:593
in require(::Symbol) at ./loading.jl:422
in require(::Symbol) at /Applications/Julia-0.5.app/Contents/Resources/julia/lib/julia/sys.dylib:?
SIUnits.jl seemed to handle this pretty well. There's no superscript slash in Unicode, so printing rational exponents using Unicode only will be problematic.
Hi,
Unless I'm missing something, base units like m
and kg
are not available after upgrading to the latest master. I wiped the old version and did
Pkg.clone("https://github.com/ajkeller34/Unitful.jl.git")
Pkg.build("Unitful")
using Unitful
After that, I get e.g. ERROR: UndefVarError: kg not defined
. This on Julia 0.5-rc3.
Let me first express my appreciation for the package, looks really nice! I like very much the possibility to have units with rational exponents, maybe not really physical but useful in calculations. In addition, it appears to be much faster than SIUnits.jl
, yet it has more features (for what I can see).
I'd like to ask you whether it is possible to make this package compatible with Measurements.jl
, a library for uncertainty propagation. This package provides a new type, Measurement
, which is subtype of AbstractFloat
and takes AbstractFloat
numbers with uncertainty, then instructs most mathematical operations to correctly propagate the uncertainty to the result. You can read here the discussion which led to the final choice: JuliaPhysics/Measurements.jl#1
The main thing that Unitful.jl
should do in order to became compatible with Measurements.jl
is to make DimensionedQuantity
subtype of AbstractFloat
.
I understand the change I'm proposing is breaking, but it would be really cool to see error propagation for numbers with their units!
Hi,
Is there a way to simplify units that are the same, for example if I enter:
1J / (1N*m)
The result is:
1.0 m^-1 N^-1 J
I would have expected just 1.0
, like when I do 1J / (1J)
This was on 0.5-rc3 using the master branch, is it recommended to use the rewrite branch instead?
I'm rewriting Images.jl, which currently (sort of) supports encoding of information like the spacing between pixels with unit-ful quantitites (currently via SIUnits). In the rewrite (which will require julia 0.5), I'm struggling with whether to remain with SIUnits or switch to Unitful. The switch faces a number of issues:
promote_op
infrastructure has been dramatically simplified with (yay!) finally the ability to rely on inference for many operationsI guess I'm posting this largely to try to gauge your enthusiasm for updating Unitful and addressing these issues. Given the number of other packages I maintain/work on I probably don't have the bandwidth to make major changes to this package (nor to SIUnits), so I am trying to read the tea leaves to figure out what will become the future well-supported units package in Julia.
It'd be nice if we could dispatch to sind
. Noticed in #38 by @mweastwood.
.<
and .<=
cannot be used to compare two Quantity
objects on julia 0.5. This is not an issue for julia 0.6 where the dots are syntax for fused broadcasts.
Fix inbound.
Unitful.jl is incompatible with @fastmath
. Here's a small example showing how to get the StackOverflowError (requires that you have optimized Julia to your system, like re-built the system image, and I can only get this error on Linux but not Windows)
using Unitful
u = 1.5u"N"
halfΔt = 0.03125u"s"
tmp2 = .25u"N/s"
u+halfΔt*tmp2
@fastmath u+halfΔt*tmp2
SIUnits has the same error at Keno/SIUnits.jl#94
# calculate one second + one hour
# converting away from a base unit
1s + 1h
1.0002777777777778 h # inexactness introduced
# converting into a base unit
1s + 1h
3601 s # exactness preserved
The deps/Defaults.jl
file is not regenerated with each invocation of Pkg.build("Unitful")
if the file exists already. This is to avoid overwriting user defaults. However, in some cases the defaults need to be changed to be compliant with changes to code in the package. It would be nice if there were a mechanism to handle this so that the user doesn't have to clear out that file or merge their changes manually.
Unitful is an amazing package and seems to me to be the one superseding all the others - I'd like to consider it (after some further maturity) to get part of the Julia "standard packages" ecosystem.
I'm used to the unit system of CLHEP library used in high-energy physics codes (e.g. manual, git), where the workflow is following:
At the input, all values are multiplied by the input unit, all computations are done with units and at the output (or binary interface or ccall to a library), the variable is divided by the desired unit:
u = 15u"V"
d = 5u"mm"
E = u/d
outE = E/u"V/m"
>3.0 mm^-1 m
I'd like outE v to be an unitless value of 3000.
This would be much more readable than having to write upreferred(outE)
, or using convert and ustrip functionality. Compare the following easy readable code (as would be in CLHEP)
println("The field intensity is $(E/u"V/m") [V/m] in a gap of $(d/u"m") [m]")
>The field intensity is 3000.0 [V/m] in a gap of 0.005 [m]
with the current verbosity
println("The field intensity is $(ustrip(float(uconvert(u"V/m", E)))) [V/m] in a gap of $(ustrip(float(uconvert(u"m", d)))) [m]")
>The field intensity is 3000.0 [V/m] in a gap of 0.005 [m]
Is it possible to specialize somehow the /
operator in such a way that if dimensions of both operands are same, the units are correctly stripped, giving just a float number?
At least for cases where just different unit multiples of the same basic unit are uses, such as mm
and m
, etc.
Thank you for the advice.
Rather than throwing ErrorException
, should we make a dimensional mismatch exception type?
julia> using Unitful
julia> 1u"μm/m" < 1
ERROR: MethodError: no method matching isless(::Unitful.Quantity{Int64,Unitful.Dimensions{()},Unitful.Units{(Unitful.Unit{:Meter}(-6,1//1),Unitful.Unit{:Meter}(0,-1//1)),Unitful.Dimensions{()}}}, ::Int64)
Closest candidates are:
isless(::Char, ::Integer) at deprecated.jl:49
isless(::AbstractFloat, ::Real) at operators.jl:42
isless(::Real, ::Real) at operators.jl:75
...
in <(::Unitful.Quantity{Int64,Unitful.Dimensions{()},Unitful.Units{(Unitful.Unit{:Meter}(-6,1//1),Unitful.Unit{:Meter}(0,-1//1)),Unitful.Dimensions{()}}}, ::Int64) at ./operators.jl:63
Following the docs I tried:
julia> using Unitful
julia> @unit M "M" Molar 1u"mol/L" true
M
julia> 1M
1 M
julia> 1u"M"
ERROR: Symbol M could not be found in registered unit modules.
edit: Expected behavior is that 1M==1u"M"
My guess is that this issue actually arises from Base, but it seems worth having someone with more knowledge look at it first. The docs for quadgk
claim that it should work with any type that implements, +
, -
, multiplication by real values, and norm
; all of which seem to work with Quantity
.
julia> using Unitful
julia> quadgk(x->x*1u"mm",0,1)
ERROR: Unitful.DimensionError()
in convfact(...) at /Users/oneilg/.julia/v0.5/Unitful/src/Conversion.jl:79
in convert at /Users/oneilg/.julia/v0.5/Unitful/src/Conversion.jl:183 [inlined]
in Base.QuadGK.Segment(::Float64, ::Float64, ::Unitful.Quantity{Float64,Unitful.Dimensions{(Unitful.Dimension{:Length}(1//1),)},Unitful.Units{(Unitful.Unit{:Meter}(-3,1//1),),Unitful.Dimensions{(Unitful.Dimension{:Length}(1//1),)}}}, ::Unitful.Quantity{Float64,Unitful.Dimensions{(Unitful.Dimension{:Length}(1//1),)},Unitful.Units{(Unitful.Unit{:Meter}(-3,1//1),),Unitful.Dimensions{(Unitful.Dimension{:Length}(1//1),)}}}) at ./quadgk.jl:40
in evalrule(::##1#2, ::Float64, ::Float64, ::Array{Float64,1}, ::Array{Float64,1}, ::Array{Float64,1}, ::Base.LinAlg.#vecnorm) at ./quadgk.jl:79
in do_quadgk(::##1#2, ::Array{Float64,1}, ::Int64, ::Type{Float64}, ::Float64, ::Float64, ::Int64, ::Base.LinAlg.#vecnorm) at ./quadgk.jl:120
in #quadgk#15(::Array{Any,1}, ::Function, ::Function, ::Int64, ::Int64) at ./quadgk.jl:230
in quadgk(::Function, ::Int64, ::Int64) at ./quadgk.jl:225
There are some cases where I would like to disable automatic conversion of units, and instead throw an error. Is this something that you would consider? I don't actually know how one would go about implementing it though.
A related issue is when there isn't a fixed conversion between units, e.g. currencies. Is there a way I could define new units that would simply throw an error if I attempted to use them in an incompatible way?
Nicely improved -- thank you.
I don't know why Pkg.build does not just happen (Linux v0.5.0rc3).
# this works
julia> Pkg.clone("git://github.com/ajkeller34/Unitful.jl.git")
INFO: Cloning Unitful from git://github.com/ajkeller34/Unitful.jl.git
INFO: Computing changes...
INFO: No packages to install, update or remove
julia> Pkg.build("Unitful")
INFO: Building Unitful
INFO: Default units, dimensions, and logic are set in /home/jas/.julia/v0.5/Unitful/deps/Defaults.jl
julia> using Unitful
#
# this does not build by itself
#
julia> Pkg.rm("Unitful");Pkg.rm("Unitful");
julia> Pkg.clone("git://github.com/ajkeller34/Unitful.jl.git")
INFO: Cloning Unitful from git://github.com/ajkeller34/Unitful.jl.git
INFO: Computing changes...
INFO: No packages to install, update or remove
julia> using Unitful
INFO: Precompiling module Unitful...
ERROR: LoadError: could not open file /home/jas/.julia/v0.5/Unitful/deps/Defaults.jl
in include_from_node1(::String) at ./loading.jl:426
in defaults() at /home/jas/.julia/v0.5/Unitful/src/User.jl:219
in include_from_node1(::String) at ./loading.jl:426
in macro expansion; at ./none:2 [inlined]
in anonymous at ./<missing>:?
in eval(::Module, ::Any) at ./boot.jl:234
in process_options(::Base.JLOptions) at ./client.jl:239
in _start() at ./client.jl:318
while loading /home/jas/.julia/v0.5/Unitful/src/Unitful.jl, in expression starting on line 704
ERROR: Failed to precompile Unitful to /home/jas/.julia/lib/v0.5/Unitful.ji
in compilecache(::String) at ./loading.jl:505
in require(::Symbol) at ./loading.jl:364
# but forcing the build gets it working
julia> Pkg.build("Unitful")
INFO: Building Unitful
INFO: Default units, dimensions, and logic are set in /home/jas/.julia/v0.5/Unitful/deps/Defaults.jl
julia> using Unitful
julia> quit()
The docs don't specify a way to add a new conversion of this variety
julia> 1u"g" == 0.001u"kg"
false
julia> 0.001u"kg" == 1u"g"
true
Can this be handled by promotion somehow?
I'm trying to have a particle density (#Particles/volume
).
I just noticed nothing like that was in:
using Unitful
muladd(0.1u"s",0.16666666666666666u"N*s^-1",1.0u"N")
Unitful-based tests on OrdinaryDiffEq.jl (DifferentialEquations.jl) have been disabled due to this problem.
There are a number of decibel-based units in common use (dBm, dBW, dBV, ...). It seems like a lot of work to support them properly, but I would certainly use them.
Here is a surprising failed call to uconvert
. It works to convert to u"cm^-2"
but not to convert to u"1/cm^2"
.
julia> 1u"1/cm^2"==1u"cm^-2"
true
julia> uconvert(u"cm^-2",1u"fs*W/(eV*cm^2)")
6241.509125883258 cm^-2
julia> uconvert(u"1/cm^2",1u"fs*W/(eV*cm^2)")
ERROR: MethodError: no method matching uconvert(::Unitful.Quantity{Int64,Unitful.Dimensions{(Unitful.Dimension{:Length}(-2//1),)},Unitful.Units{(Unitful.Unit{:Meter}(-2,-2//1),),Unitful.Dimensions{(Unitful.Dimension{:Length}(-2//1),)}}}, ::Unitful.Quantity{Int64,Unitful.Dimensions{(Unitful.Dimension{:Length}(-2//1),)},Unitful.Units{(Unitful.Unit{:Meter}(-2,-2//1),Unitful.Unit{:Second}(-15,1//1),Unitful.Unit{:Watt}(0,1//1),Unitful.Unit{:eV}(0,-1//1)),Unitful.Dimensions{(Unitful.Dimension{:Length}(-2//1),)}}})
Closest candidates are:
uconvert{T,D,U}(::Unitful.Units{N,D}, ::Unitful.Quantity{T,D,U}) at /Users/oneilg/.julia/v0.5/Unitful/src/Conversion.jl:21
uconvert(::Unitful.Units{N,D}, ::Number) at /Users/oneilg/.julia/v0.5/Unitful/src/Conversion.jl:58
Is there a function to reduce to base units?
I can do
julia> uconvert(u"m",4u"m^3"/2u"cm^2")
20000.0 m
But only if I already know it has units of meters, but what if I want to just get to base units without knowing what they will be?
Is there a particular reason why there is no method for exp
with DimensionlessQuantity
argument?
I remember that you had requested change to Base to allow ranges to work correctly, I don't remember if those happened or not. But I think these are unrelated. Here is an example where collect(range)
is not even monotonic. The result is not always the same, suggesting this value is initialized. Also the behavior is different when the range is used in a comprehenision vs when used with the new vectorizing dot syntax.
julia> range = 0u"mm":0.001u"mm":5.4167u"mm"
0.0 mm:0.001 mm:5.416 mm
julia> collect(range)[end]
NaN mm #expected 5.416mm
julia> [r.val for r in range][end]
6.934269676051e-310 #expected 5.416
julia> getval(q)=q.val
julia> getval.(range)[end]
5.416 #expected 5.416
Here is an example where the range appears to end two value too soon.
julia> collect(0u"mm":0.001u"mm":5u"mm")[end]
4.998000000000004 mm #expected 5.0 mm
julia> length(0:0.001:5)-length(0u"mm":0.001u"mm":5u"mm")
2
Hi,
It would be useful to easily be able to strip units, e.g. for making arrays to pass to a plotting package, or for using linspace.
I know we can do a/unit(a)
, but that's a bit verbose since it requires writing the variable name twice. Would a function like this be an acceptable addition:
value(a) = a.val
I didn't find any example of products of units with u_str in the docs. There are lots of examples like 4u"m/s"
but it would be nice to see something like 3u"N*m"
. I kept trying 3u"N m"
expecting it to work.
`julia> typeof(u"ha")
Unitful.Units{(Unitful.Unit{:Are}(2,1//1),),Unitful.Dimensions{(Unitful.Dimension{:Length}(1//1),)}}
julia> typeof(u"a")
Unitful.Units{(Unitful.Unit{:Are}(0,1//1),),Unitful.Dimensions{(Unitful.Dimension{:Length}(2//1),)}}
`
The dimension of should be L^2 in both cases, I guess.
const ha = Unitful.Units{(Unitful.Unit{:Are}(2,1//1),), typeof(𝐋^2)}()
deps/build.jl: 85
Unitful.jl is testament to the fact that physical units are superbly useful, even for computation. In fact, I assert that nondimensional numbers are also superbly useful.
Typically I want to attach units to variables, so whatever units I start with, I can specify how to calculate things correctly with some set of mutually compatible units. Yet after converting inputs to the compatible units, I want the computer to do a nondimensional (often floating point) calculation. Now that there is Unitful, it can express the result in sensible and correct units too.
I would like to be able to dispatch functions specifically on nondimensional Number
s. I don't know how to do this, perhaps because I'm just learning Julia types and methods. I can make methods that dispatch on whether I pass a function, e.g., a ::Pressure
or a ::Temperature
, but there's no type syntax I know of to dispatch a method to operate particularly on a NonDimensionalNumber. It seems awkward because Quantity <: Number
, so that Number
includes nondimensional Numbers
and nondimensional and dimensional Unitful.Quantity
s. My desired NonDimensionalNumber abstract type would include Numbers
that are not Quantity
s, or at least not dimensional ones. Whether variables have this property can be assessed at run time, but not when the compiler dispatches using type.
julia> Quantity <: Number
true
julia> DimensionlessQuantity <: Quantity
true
julia> isa(1u"m/m",DimensionlessQuantity)
false
julia> isa(1,DimensionlessQuantity)
false
julia> isa(1//1,DimensionlessQuantity)
false
julia> isa(NoUnits(1u"m/μm"),DimensionlessQuantity)
false
There are lots of physically meaningful nondimensional numbers: e.g. ratios of things with the same dimensions. Some nondimensional numbers even have hallowed names like Reynolds and Rayleigh. Scientific computation has made itself very useful dealing entirely with nondimensional numbers, or at any rate not knowing the numbers were dimensional, leaving pesky unit conversions to humans.
Good work, I have recently been working on a package called EngUnits.jl. The package extent SIUnits.jl to handle input and output units more gracefully. My two primary concerns when designing the package was:
1*u"kN/m^2" == 1000u"Pa"
. This allow for having a lot of different units without exporting them from the package and avoids cases where the unit name is overwritten.1*Newton*1*Meter
, which is displayed as 1 kg m²s⁻²
. Instead the package displays it as J
, and the user can specify to get it displayed different by display_unit(u"J", "N m")
. The units can also be displayed with a certain prefix as: display_unit(u"MPa", "MPa")
. This will cause pressure to always be displayed in MPa
. The user can change the default display values by changing a setup file created during build time of the package.This package seem to be aiming for a versatile implementation of units. I hope some of the concerns, I have raised here can be considered, when furher improvements of this package are made. The string macro implementation, should be quite easy to port to this package. I will be happy to do it in a PR, if people find it appropriate. I do not know how easy it would be, to implement a similar output display system.
Hey, I was thinking of adding compatibility to this package in differential equations. I have already made the ODE parts fully compatible with SIUnits, though it seems there are limitations to that package which led me here. However, there are a few things I wanted to discuss that I couldn't find in the documentation:
sqrt
function work with Unitful units?\
and sparse matrix multiplication do? Those would be required for the PDE solvers. SIUnits runs into problems with \
since it calls CHOLMOD, either there needs to be a dispatch which strips the units and then puts them back on (and checks afterwards that the units work?). I'm not sure what goes wrong with SIUnits sprase matrix multiplication. I think it makes zeros that aren't unit'd and then the unit checks go wrong.Same for °F
. Better to convert °C
to an absolute scale like K
before taking the power, if it is the only unit on the quantity.
Not sure if better to convert °F
to °Ra
or K
since both are absolute scales, and °Ra
is more closely related to °F
...
Might be related to #9:
currently write
ing Unitful variables to a JLD file causes the current error:
ERROR: LoadError: LoadError: LoadError: type parameters with objects of type Tuple{Unitful.Dimension{:Length},Unitful.Dimension{:Mass},Unitful.Dimension{:Time}} are currently unsupported
From JLD.jl/src/JLD.jl, it looks like the following condition needs to be satisfied:
isbits(x) && parse(s) === x && !isa(x, Tuple)
Currently get this error:
Got an exception of type MethodError outside of a @test
MethodError: no method matching eps(::Unitful.Quantity{Float64,Unitful.Dimensions{(Unitful.Dimension{:Mass}(1//1),)},Unitful.Units{(Unitful.Unit{:Gram,Unitful.Dimensions{(Unitful.Dimension{:Mass}(1//1),)}}(3,1//1),),Unitful.Dimensions{(Unitful.Dimension{:Mass}(1//1),)}}})
Closest candidates are:
eps(!Matched::Date) at dates/types.jl:231
eps(!Matched::DateTime) at dates/types.jl:230
eps() at float.jl:499
edit: something along the lines (but as a test macro):
function is_within_error(v_actual, v_approx, rel_error=1e-4)
if v_actual == v_approx
return true
end
if ( v_actual == typeof(v_actual)(0) )
v_actual, v_approx = v_approx, v_actual
# or return false or throw an error?
end
relative_error = abs( v_actual - v_approx ) / abs(v_actual)
relative_error < rel_error
end
Hi Andrew,
I noticed that with this mornings build of Julia 0.5.0- Intrinsics.box on lines 54 and 55 of Unitful.jl needed to be replaced by Core.Intrinsics.box.
As I have added some oil field units it's a bit tricky to generate a PR, but if that would help I can do it.
Thanks for a great Pkg!
Rob
julia> import Base:in
julia> in(x::Quantity, y::Unitful.Units) = uconvert(y,x)
in (generic function with 28 methods)
julia> 3u"m"
3 m
julia> ans in u"cm"
300//1 cm
I'm not sure whether to encourage this sort of thing although it's awfully nice at the REPL.
Some linear algebra operations like \
have a hard time with unitful numbers. Take the following case:
a = [1.0u"N" 2.0u"N"
3.0u"N" 1.0u"N"]
b = [1.0u"N";3.0u"N"]
If you try \
it gives a dimension error. If you try *
then it uses the generic *
fallback and not BLAS. The same is true if you use:
A = rand(100,100)u"N"
b = rand(100)u"N"
I suggest trying something like, performing a dimensional analysis, strip the units, use the general *
or \
, and then re-apply units.
To do this properly, you might need a UnitfulArray
instead of an array of Unitful values. I am not sure if stripping the units on an array of Unitful numbers can be done without making a temporary.
I am interested in adding Unitful as a dependency for a package I maintain, but it is important that I am able to dispatch on units of angle. For example I have a 3-vector type, and it would be nice to provide constructors like (without units all of the arguments are Float64
and you don't get the convenience of having both constructors):
f(x::Unitful.Length, y::Unitful.Length, z::Unitful.Length) = ...
f(r::Unitful.Length, theta::Unitful.Angle, phi::Unitful.Angle) = ...
I am also going to argue that "Angle" should be a fundamental (not derived dimension). The idea that angles are "dimensionless" seems to originate from the idea that it is a length / length
(see table 3 http://physics.nist.gov/cuu/Units/units.html), but this is only correct in the small angle approximation. In general you need to use a trig function to get from an angle to something that is truly dimensionless. Or rather you need an inverse trig function to get an angle from something that is dimensionless.
Finally it would also be really cool if sin(1°)
dispatched to sind(1)
.
(see https://github.com/Keno/SIUnits.jl/blob/d25c43483828c954cd63c19cbe3ff209a614bbd1/src/nonsiunits.jl#L15-L22)
On master, you can now easily implement conversion between dimensions by multiplying by appropriate fundamental constants (e.g. energy = planck constant * frequency). Currently this is on a case-by-case basis, and is opt-in to avoid confusion:
julia> using Unitful
julia> import Unitful: uconvert, EnergyUnit, Frequency
julia> uconvert(e::EnergyUnit, f::Frequency) = uconvert(e, u"h"*f)
uconvert (generic function with 4 methods)
julia> 1u"GHz" |> u"eV"
4.135667662340165e-6 eV
It'd be cool if we could come up with a mechanism to implement this logic automatically given a set of fundamental constants, although this sort of thing would always need to be opt-in. Perhaps we could define a generalized uconvert
called dconvert
that attempts to convert quantities between dimensions using a set of fundamental constants when possible. The h
vs ħ
distinction would be super annoying.
This is really just a discussion topic, it's not something I think belongs in this package. If you don't find it interesting, just close it.
I'm mulling over the proper framework for expressing values recorded from scientific cameras. Such cameras typically report values as, e.g., UInt16
. The existing FixedPointNumbers
framework allows one to normalize these numbers to a 0-to-1 scale, where 1 means "saturated" (which is something quite important to keep track of---you want to know when your sensor has been maxed out). For example, a 12-bit camera might return values in the range 0x0000:0x0fff
, and representing those as N4f12
numbers (where 0x0fff
corresponds to 1) is a good choice because it places what would otherwise be arbitrary integers onto a common scale possessing genuine physical meaning.
However, scientific cameras often have another important feature: there's often a known conversion from "camera units" to "number of photons." This, too, is a very meaningful quantity, since the number of photons gives an estimate of the uncertainty in the intensity measurement (sqrt(N)
due to Poisson statistics).
So here we have a raw measurement, expressed in essentially arbitrary units, that might meaningfully be reinterpreted in two distinct ways. This sort of feels like physical units (we can convert microns to inches to light-picoseconds), but it also seems a bit different. It kind of seems to blur the lines between dimensionless numbers and dimensionful numbers. One of the reasons I'm thinking about Unitful is the very nice framework you've developed for custom units, but I think there are conceptual as well as practical issues here. Any particular insights on the right choice of framework for representing such numbers? I'm CCing @rsrock and @Cody-G to see if they have any interest in the discussion topic.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.