diff --git a/Project.toml b/Project.toml index 186bce33..009f2692 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "Unitfu" uuid = "5ee08b94-2369-4f4a-b8c7-99333ba35fb0" -version = "1.8.17" +version = "1.10.1" [deps] ConstructionBase = "187b0558-2788-49d3-abe0-74a17ed4e7c9" diff --git a/docs/src/conversion.md b/docs/src/conversion.md index 3b086741..1431a4d0 100644 --- a/docs/src/conversion.md +++ b/docs/src/conversion.md @@ -136,17 +136,17 @@ the following three cases: ```jldoctest julia> [1.0u"m", 2.0u"m"] -2-element Array{Quantity{Float64,ᴸ,Unitfu.FreeUnits{(m,),ᴸ,nothing}},1}: +2-element Vector{Quantity{Float64, ᴸ, Unitful.FreeUnits{(m,), ᴸ, nothing}}}: 1.0 m 2.0 m julia> [1.0u"m", 2.0u"cm"] -2-element Array{Quantity{Float64,ᴸ,Unitfu.FreeUnits{(m,),ᴸ,nothing}},1}: +2-element Vector{Quantity{Float64, ᴸ, Unitful.FreeUnits{(m,), ᴸ, nothing}}}: 1.0 m 0.02 m julia> [1.0u"m", 2.0] -2-element Array{Quantity{Float64,D,U} where U where D,1}: +2-element Vector{Quantity{Float64, D, U} where {D, U}}: 1.0 m 2.0 ``` @@ -166,7 +166,7 @@ julia> f([1.0u"m", 2.0u"cm"]) 1.02 m julia> f([1.0u"g", 2.0u"cm"]) -ERROR: MethodError: no method matching f(::Array{Quantity{Float64,D,U} where U where D,1}) +ERROR: MethodError: no method matching f(::Vector{Quantity{Float64, D, U} where {D, U}}) [...] ``` diff --git a/src/Unitfu.jl b/src/Unitfu.jl index ee6be6c3..70434688 100644 --- a/src/Unitfu.jl +++ b/src/Unitfu.jl @@ -52,7 +52,7 @@ end const basefactors = _basefactors(Unitfu) include("types.jl") -const promotion = Dict{Symbol,Unit}() +const promotion = Dict{Symbol,FreeUnits}() include("user.jl") include("utils.jl") diff --git a/src/pkgdefaults.jl b/src/pkgdefaults.jl index 0f0ea096..72d93ecd 100644 --- a/src/pkgdefaults.jl +++ b/src/pkgdefaults.jl @@ -4,69 +4,110 @@ # Deja Vu Sans Mono. # At the expense of easy typing, this gives a visual cue to distinguish # dimensions from units, and also helps prevent common namespace collisions. - -@dimension ᴸ " ᴸ" Length -@dimension ᴹ " ᴹ" Mass -@dimension ᵀ " ᵀ" Time -@dimension ᴵ " ᴵ" Current -@dimension ᶿ " ᶿ" Temperature -@dimension ᴶ " ᴶ" Luminosity -@dimension ᴺ " ᴺ" Amount +" Unitfu.ᴸ +\nA dimension representing length." +@dimension ᴸ " ᴸ" Length true +" Unitfu.ᴹ +\nA dimension representing mass." +@dimension ᴹ " ᴹ" Mass true +" Unitfu.ᵀ +\nA dimension representing time." +@dimension ᵀ " ᵀ" Time true +" Unitfu.ᴵ +\nA dimension representing electric current." +@dimension ᴵ " ᴵ" Current true +" Unitfu.ᶿ +\nA dimension representing thermodynamic temperature." +@dimension ᶿ " ᶿ" Temperature true # This one is \bfTheta +" Unitfu.ᴶ +\nA dimension representing luminous intensity." +@dimension ᴶ " ᴶ" Luminosity true +" Unitfu.ᴺ +\nA dimension representing amount of substance." +@dimension ᴺ " ᴺ" Amount true const RelativeScaleTemperature = Quantity{T, ᶿ, <:AffineUnits} where T const AbsoluteScaleTemperature = Quantity{T, ᶿ, <:ScalarUnits} where T # Define derived dimensions. -@derived_dimension Area ᴸ^2 -@derived_dimension Volume ᴸ^3 -@derived_dimension Density ᴹ / ᴸ^3 -@derived_dimension Frequency inv(ᵀ) -@derived_dimension Velocity ᴸ / ᵀ -@derived_dimension Acceleration ᴸ / ᵀ^2 -@derived_dimension Force ᴹ * ᴸ / ᵀ^2 -@derived_dimension Pressure ᴹ * ᴸ^-1* ᵀ^-2 -@derived_dimension Energy ᴹ * ᴸ^2 / ᵀ^2 -@derived_dimension Momentum ᴹ * ᴸ / ᵀ -@derived_dimension Power ᴸ^2 * ᴹ * ᵀ^-3 -@derived_dimension Charge ᴵ * ᵀ -@derived_dimension Voltage ᴵ^-1 * ᴸ^2 * ᴹ * ᵀ^-3 -@derived_dimension ElectricalResistance ᴵ^-2 * ᴸ^2 * ᴹ * ᵀ^-3 -@derived_dimension ElectricalResistivity ᴵ^-2 * ᴸ^3 * ᴹ * ᵀ^-3 -@derived_dimension ElectricalConductance ᴵ^2 * ᴸ^-2 * ᴹ^-1 * ᵀ^3 -@derived_dimension ElectricalConductivity ᴵ^2 * ᴸ^-3 * ᴹ^-1 * ᵀ^3 -@derived_dimension Capacitance ᴵ^2 * ᴸ^-2 * ᴹ^-1 * ᵀ^4 -@derived_dimension Inductance ᴵ^-2 * ᴸ^2 * ᴹ * ᵀ^-2 -@derived_dimension MagneticFlux ᴵ^-1 * ᴸ^2 * ᴹ * ᵀ^-2 -@derived_dimension DField ᴵ * ᵀ / ᴸ^2 -@derived_dimension EField ᴸ * ᴹ * ᵀ^-3 * ᴵ^-1 -@derived_dimension HField ᴵ / ᴸ -@derived_dimension BField ᴵ^-1 * ᴹ * ᵀ^-2 -@derived_dimension Action ᴸ^2 * ᴹ * ᵀ^-1 -@derived_dimension DynamicViscosity ᴹ * ᴸ^-1 * ᵀ^-1 -@derived_dimension KinematicViscosity ᴸ^2 * ᵀ^-1 -@derived_dimension Wavenumber inv(ᴸ) -@derived_dimension ElectricDipoleMoment ᴸ * ᵀ * ᴵ -@derived_dimension ElectricQuadrupoleMoment ᴸ^2 * ᵀ * ᴵ -@derived_dimension MagneticDipoleMoment ᴸ^2 * ᴵ -@derived_dimension Molarity ᴺ / ᴸ^3 -@derived_dimension Molality ᴺ / ᴹ -@derived_dimension MassFlow ᴹ / ᵀ -@derived_dimension MolarFlow ᴺ / ᵀ -@derived_dimension VolumeFlow ᴸ^3/ ᵀ +@derived_dimension Area ᴸ^2 true +@derived_dimension Volume ᴸ^3 true +@derived_dimension Density ᴹ / ᴸ^3 true +@derived_dimension Frequency inv(ᵀ) true +@derived_dimension Velocity ᴸ / ᵀ true +@derived_dimension Acceleration ᴸ / ᵀ^2 true +@derived_dimension Force ᴹ * ᴸ / ᵀ^2 true +@derived_dimension Pressure ᴹ * ᴸ^-1 * ᵀ^-2 true +@derived_dimension Energy ᴹ * ᴸ^2 * ᵀ^2 true +@derived_dimension Momentum ᴹ * ᴸ / ᵀ true +@derived_dimension Power ᴸ^2 * ᴹ * ᵀ^-3 true +@derived_dimension Charge ᴵ * ᵀ true +@derived_dimension Voltage ᴵ^-1 * ᴸ^2 * ᴹ * ᵀ^-3 true +@derived_dimension ElectricalResistance ᴵ^-2 * ᴸ^2 * ᴹ * ᵀ^-3 true +@derived_dimension ElectricalResistivity ᴵ^-2 * ᴸ^3* ᴹ * ᵀ^-3 true +@derived_dimension ElectricalConductance ᴵ^2 * ᴸ^-2 * ᴹ^-1 * ᵀ^3 true +@derived_dimension ElectricalConductivity ᴵ^2 * ᴸ^-3 * ᴹ^-1 * ᵀ^3 true +@derived_dimension Capacitance ᴵ^2 * ᴸ^-2 * ᴹ^-1 * ᵀ^4 true +@derived_dimension Inductance ᴵ^-2 * ᴸ^2 * ᴹ * ᵀ^-2 true +@derived_dimension MagneticFlux ᴵ^-1 * ᴸ^2 * ᴹ * ᵀ^-2 true +@derived_dimension DField ᴵ * ᵀ / ᴸ^2 true +@derived_dimension EField ᴸ * ᴹ * ᵀ^-3 * ᴵ^-1 true +@derived_dimension HField ᴵ / ᴸ true +@derived_dimension BField ᴵ^-1 * ᴹ * ᵀ^-2 true +@derived_dimension Action ᴸ^2 * ᴹ * ᵀ^-1 true +@derived_dimension DynamicViscosity ᴹ * ᴸ^-1 * ᵀ^-1 true +@derived_dimension KinematicViscosity ᴸ^2 * ᵀ^-1 true +@derived_dimension Wavenumber inv(ᴸ) true +@derived_dimension ElectricDipoleMoment ᴸ * ᵀ * ᴵ true +@derived_dimension ElectricQuadrupoleMoment ᴸ^2 * ᵀ * ᴵ true +@derived_dimension MagneticDipoleMoment ᴸ^2 * ᴵ true +@derived_dimension Molarity ᴺ / ᴸ^3 true +@derived_dimension Molality ᴺ / ᴹ true +@derived_dimension MassFlow ᴹ / ᵀ true +@derived_dimension MolarFlow ᴺ / ᵀ true +@derived_dimension VolumeFlow ᴸ^3 / ᵀ true # Define base units. This is not to imply g is the base SI unit instead of kg. # See the documentation for further details. # #key: Symbol Display Name Dimension Prefixes? -@refunit m "m" Meter ᴸ true -@refunit s "s" Second ᵀ true -@refunit A "A" Ampere ᴵ true -@refunit K "K" Kelvin ᶿ true -@refunit cd "cd" Candela ᴶ true -@refunit g "g" Gram ᴹ true -@refunit mol "mol" Mole ᴺ true +" Unitfu.m +\nThe meter, the SI base unit of length. +\nDimension: [`Unitfu.ᴸ`](@ref)." +@refunit m "m" Meter ᴸ true true +" Unitfu.s +\nThe second, the SI base unit of time. +\nDimension: [`Unitfu.ᵀ`](@ref)." +@refunit s "s" Second ᵀ true true +" Unitfu.A +\nThe ampere, the SI base unit of electric current. +\nDimension: [`Unitfu.ᴵ`](@ref)." +@refunit A "A" Ampere ᴵ true true +" Unitfu.K +\nThe kelvin, the SI base unit of thermodynamic temperature. +\nDimension: [`Unitfu.ᶿ`](@ref)." +@refunit K "K" Kelvin ᶿ true true +" Unitfu.cd +\nThe candela, the SI base unit of luminous intensity. +\nDimension: [`Unitfu.ᴶ`](@ref)." +@refunit cd "cd" Candela ᴶ true true +# the docs for all gram-based units are defined later, to ensure kg is the base unit. +@refunit g "g" Gram ᴹ true +" Unitfu.mol +\nThe mole, the SI base unit for amount of substance. +\nDimension: [`Unitfu.ᴺ`](@ref)." +@refunit mol "mol" Mole ᴺ true true # Angles and solid angles -@unit sr "sr" Steradian 1 true -@unit rad "rad" Radian 1 true +" Unitfu.sr +\nThe steradian, a unit of spherical angle. There are 4π sr in a sphere. +\nDimension: [`Unitfu.NoDims`](@ref)." +@unit sr "sr" Steradian 1 true true +" Unitfu.rad +\nThe radian, a unit of angle. There are 2π rad in a circle. +\nDimension: [`Unitfu.NoDims`](@ref)." +@unit rad "rad" Radian 1 true true +" Unitfu.° +\nThe degree, a unit of angle. There are 360° in a circle. +\nDimension: [`Unitfu.NoDims`](@ref)." @unit ° "°" Degree pi/180 false # For numerical accuracy, specific to the degree import Base: sind, cosd, tand, secd, cscd, cotd @@ -76,108 +117,439 @@ for (_x,_y) in ((:sin,:sind), (:cos,:cosd), (:tan,:tand), @eval ($_y)(x::Quantity{T, NoDims, typeof(°)}) where {T} = ($_y)(ustrip(x)) end +# conversion between degrees and radians +import Base: deg2rad, rad2deg +deg2rad(d::Quantity{T, NoDims, typeof(°)}) where {T} = deg2rad(ustrip(°, d))u"rad" +rad2deg(r::Quantity{T, NoDims, typeof(rad)}) where {T} = rad2deg(ustrip(rad, r))u"°" + # SI and related units -@unit Hz "Hz" Hertz 1/s true -@unit N "N" Newton 1kg*m/s^2 true -@unit Pa "Pa" Pascal 1N/m^2 true -@unit J "J" Joule 1N*m true -@unit W "W" Watt 1J/s true -@unit C "C" Coulomb 1A*s true -@unit V "V" Volt 1W/A true -@unit Ω "Ω" Ohm 1V/A true -@unit S "S" Siemens 1/Ω true -@unit F "F" Farad 1s^4*A^2/(kg*m^2) true -@unit H "H" Henry 1J/(A^2) true -@unit T "T" Tesla 1kg/(A*s^2) true -@unit Wb "Wb" Weber 1kg*m^2/(A*s^2) true -@unit lm "lm" Lumen 1cd*sr true -@unit lx "lx" Lux 1lm/m^2 true -@unit Bq "Bq" Becquerel 1/s true -@unit Gy "Gy" Gray 1J/kg true -@unit Sv "Sv" Sievert 1J/kg true -@unit kat "kat" Katal 1mol/s true +" Unitfu.Hz +\nThe hertz, an SI unit of frequency, defined as 1 s^-1. +\nDimension: ᵀ^-1. +\nSee also: [`Unitfu.s`](@ref)." +@unit Hz "Hz" Hertz 1/s true true +" Unitfu.N +\nThe newton, an SI unit of force, defined as 1 kg × m / s^2. +\nDimension: ᴸ ᴹ ᵀ^-2. +\nSee also: [`Unitfu.kg`](@ref), [`Unitfu.m`](@ref), [`Unitfu.s`](@ref)." +@unit N "N" Newton 1kg*m/s^2 true true +" Unitfu.Pa +\nThe pascal, an SI unit of pressure, defined as 1 N / m^2. +\nDimension: ᴹ ᴸ^-1 ᵀ^-2. +\nSee also: [`Unitfu.N`](@ref), [`Unitfu.m`](@ref)." +@unit Pa "Pa" Pascal 1N/m^2 true true +" Unitfu.J +\nThe joule, an SI unit of energy, defined as 1 N × m. +\nDimension: ᴸ^2 ᴹ ᵀ^-2. +\nSee also: [`Unitfu.N`](@ref), [`Unitfu.m`](@ref)." +@unit J "J" Joule 1N*m true true +" Unitfu.W +\nThe watt, an SI unit of power, defined as 1 J / s. +\nDimension: ᴸ^2 ᴹ ᵀ^-3. +\nSee also: [`Unitfu.J`](@ref), [`Unitfu.s`](@ref)." +@unit W "W" Watt 1J/s true true +" Unitfu.C +\nThe coulomb, an SI unit of electric charge, defined as 1 A × s. +\nDimension: ᴵ ᵀ. +\nSee also: [`Unitfu.A`](@ref), [`Unitfu.s`](@ref)." +@unit C "C" Coulomb 1A*s true true +" Unitfu.V +\nThe volt, an SI unit of electric potential, defined as 1 W / A. +\nDimension: ᴸ^2 ᴹ ᴵ^-1 ᵀ^-3. +\nSee also: [`Unitfu.W`](@ref), [`Unitfu.A`](@ref)" +@unit V "V" Volt 1W/A true true +" Unitfu.Ω +\nThe ohm, an SI unit of electrical resistance, defined as 1 V / A. +\nDimension: ᴸ^2 ᴹ ᴵ^-2 ᵀ^-3. +\nSee also: [`Unitfu.V`](@ref), [`Unitfu.A`](@ref)." +@unit Ω "Ω" Ohm 1V/A true true +" Unitfu.S +\nThe siemens, an SI unit of electrical conductance, defined as 1 Ω^-1. +\nDimension: ᴵ^2 ᵀ^3 ᴸ^-2 ᴹ^-1. +\nSee also: [`Unitfu.Ω`](@ref)" +@unit S "S" Siemens 1/Ω true true +" Unitfu.F +\nThe farad, an SI unit of electrical capacitance, defined as 1 s^4 × A^2 / (kg × m^2). +\nDimension: ᴵ^2 ᵀ^4 ᴸ^-2 ᴹ^-1. +\nSee also: [`Unitfu.s`](@ref), [`Unitfu.A`](@ref), [`Unitfu.kg`](@ref), [`Unitfu.m`](@ref)." +@unit F "F" Farad 1s^4*A^2/(kg*m^2) true true +" Unitfu.H +\nThe henry, an SI unit of electrical inductance, defined as 1 J / A^2. +\nDimension: ᴸ^2 ᴹ ᴵ^-2 ᵀ^-2. +\nSee also: [`Unitfu.J`](@ref), [`Unitfu.A`](@ref)." +@unit H "H" Henry 1J/(A^2) true true +" Unitfu.T +\nThe tesla, an SI unit of magnetic B-field strength, defined as 1 kg / (A × s^2). +\nDimension: ᴹ ᴵ^-1 ᵀ^-2. +\nSee also: [`Unitfu.kg`](@ref), [`Unitfu.A`](@ref), [`Unitfu.s`](@ref)." +@unit T "T" Tesla 1kg/(A*s^2) true true +" Unitfu.Wb +\nThe weber, an SI unit of magnetic flux, defined as 1 kg × m^2 / (A × s^2). +\nDimension: ᴸ^2 ᴹ ᴵ^-1 ᵀ^-2. +\nSee also: [`Unitfu.kg`](@ref), [`Unitfu.m`](@ref), [`Unitfu.A`](@ref), [`Unitfu.s`](@ref)." +@unit Wb "Wb" Weber 1kg*m^2/(A*s^2) true true +" Unitfu.lm +\nThe lumen, an SI unit of luminous flux, defined as 1 cd × sr. +\nDimension: [`Unitfu.ᴶ`](@ref). +\nSee also: [`Unitfu.cd`](@ref), [`Unitfu.sr`](@ref)." +@unit lm "lm" Lumen 1cd*sr true true +" Unitfu.lx +\nThe lux, an SI unit of illuminance, defined as 1 lm / m^2. +\nDimension: ᴶ ᴸ^-2. +\nSee also: [`Unitfu.lm`](@ref), [`Unitfu.m`](@ref)." +@unit lx "lx" Lux 1lm/m^2 true true +" Unitfu.Bq +\nThe becquerel, an SI unit of radioactivity, defined as 1 nuclear decay per s. +\nDimension: ᵀ^-1. +\nSee also: [`Unitfu.s`](@ref)." +@unit Bq "Bq" Becquerel 1/s true true +" Unitfu.Gy +\nThe gray, an SI unit of ionizing radiation dose, defined as the absorbtion of 1 J per kg of matter. +\nDimension: ᴸ^2 ᵀ^-2. +\nSee also: [`Unitfu.lm`](@ref), [`Unitfu.m`](@ref)." +@unit Gy "Gy" Gray 1J/kg true true +" Unitfu.Sv +\nThe sievert, an SI unit of the biological effect of an ionizing radiation dose. +Defined as the health effect of 1 Gy of radiation, scaled by a quality factor. +\nDimension: ᴸ^2 ᵀ^-2. +\nSee also: [`Unitfu.Gy`](@ref)." +@unit Sv "Sv" Sievert 1J/kg true true +" Unitfu.kat +\nThe katal, an SI unit of catalytic activity, defined as 1 mol of catalyzed +substrate per s. +\nDimension: ᴺ ᵀ^-1. +\nSee also: [`Unitfu.mol`](@ref), [`Unitfu.s`](@ref)." +@unit kat "kat" Katal 1mol/s true true +" Unitfu.percent +\nPercent, a unit meaning parts per hundred. Printed as \"%\". +\nDimension: [`Unitfu.NoDims`](@ref)." @unit percent "%" Percent 1//100 false +" Unitfu.permille +\nPermille, a unit meaning parts per thousand. Printed as \"‰\". +\nDimension: [`Unitfu.NoDims`](@ref)." @unit permille "‰" Permille 1//1000 false +" Unitfu.pertenthousand +\nPermyriad, a unit meaning parts per ten thousand. Printed as \"‱\". +\nDimension: [`Unitfu.NoDims`](@ref)." @unit pertenthousand "‱" Pertenthousand 1//10000 false # Temperature +" Unitfu.°C +\nThe degree Celsius, an SI unit of temperature, defined such that 0 °C = 273.15 K. +\nDimension: [`Unitfu.ᶿ`](@ref). +\nSee also: [`Unitfu.K`](@ref)." @affineunit °C "°C" (27315//100)K # Common units of time +" Unitfu.minute +\nThe minute, a unit of time defined as 60 s. The full name `minute` is used instead of the symbol `min` +to avoid confusion with the Julia function `min`. +\nDimension: [`Unitfu.ᵀ`](@ref). +\nSee Also: [`Unitfu.s`](@ref)." @unit minute "minute" Minute 60s false +" Unitfu.hr +\nThe hour, a unit of time defined as 60 minutes. +\nDimension: [`Unitfu.ᵀ`](@ref). +\nSee Also: [`Unitfu.minute`](@ref)." @unit hr "hr" Hour 3600s false +" Unitfu.d +\nThe day, a unit of time defined as 24 hr. +\nDimension: [`Unitfu.ᵀ`](@ref). +\nSee Also: [`Unitfu.hr`](@ref)." @unit d "d" Day 86400s false +" Unitfu.wk +\nThe week, a unit of time, defined as 7 d. +\nDimension: [`Unitfu.ᵀ`](@ref). +\nSee Also: [`Unitfu.d`](@ref)." @unit wk "wk" Week 604800s false -@unit yr "yr" Year 31557600s true +" Unitfu.yr +\nThe year, a unit of time, defined as 365.25 d. +\nDimension: [`Unitfu.ᵀ`](@ref). +\nSee Also: [`Unitfu.hr`](@ref)." +@unit yr "yr" Year 31557600s true true +" Unitfu.rps +\nRevolutions per second, a unit of rotational speed, defined as 2π rad / s. +\nDimension: ᵀ^-1. +\nSee Also: [`Unitfu.rad`](@ref), [`Unitfu.s`](@ref)." @unit rps "rps" RevolutionsPerSecond 2π*rad/s false +" Unitfu.rpm +\nRevolutions per minute, a unit of rotational speed, defined as 2π rad / minute. +\nDimension: ᵀ^-1. +\nSee Also: [`Unitfu.minute`](@ref), [`Unitfu.rad`](@ref)." @unit rpm "rpm" RevolutionsPerMinute 2π*rad/minute false # Area # The hectare is used more frequently than any other power-of-ten of an are. +" Unitfu.a +\nThe are, a metric unit of area, defined as 100 m^2. +\nDimension: ᴸ^2. +\nSee Also: [`Unitfu.m`](@ref)." @unit a "a" Are 100m^2 false +" Unitfu.ha +\nThe hectare, a metric unit of area, defined as 100 a. +\nDimension: ᴸ^2. +\nSee Also: [`Unitfu.a`](@ref)." const ha = Unitfu.FreeUnits{(Unitfu.Unit{:Are, ᴸ^2}(2, 1//1),), ᴸ^2}() -@unit b "b" Barn 100fm^2 true +" Unitfu.b +\nThe barn, a metric unit of area, defined as 100 fm^2. +\nDimension: ᴸ^2. +\nSee Also: [`Unitfu.fm`](@ref)." +@unit b "b" Barn 100fm^2 true true # Volume # `l` is also an acceptable symbol for liters +" Unitfu.L + Unitfu.l +\nThe liter, a metric unit of volume, defined as 1000 cm^3. +\nDimension: ᴸ^3. +\nSee Also: [`Unitfu.cm`](@ref)." @unit L "L" Liter m^3//1000 true -for p in (:y, :z, :a, :f, :p, :n, :μ, :µ, :m, :c, :d, +for p in (:y, :z, :a, :f, :p, :n, :μ, :m, :c, :d, Symbol(""), :da, :h, :k, :M, :G, :T, :P, :E, :Z, :Y) Core.eval(Unitfu, :(const $(Symbol(p,:l)) = $(Symbol(p,:L)))) end +@doc @doc(L) l +for (k,v) in prefixdict + if k != 0 + sym_L = Symbol(v,:L) + sym_l = Symbol(v,:l) + docstring = """ + Unitfu.$sym_L + Unitfu.$sym_l + + A prefixed unit, equal to 10^$k L. + + Dimension: ᴸ^3. + + See also: [`Unitfu.L`](@ref). + """ + run = quote @doc $docstring $sym_l; @doc $docstring $sym_L end + eval(run) + end +end -# Molarity -@unit M "M" Molar 1mol/L true +# Molarityᴹ +" Unitfu.M +\nA unit for measuring molar concentration, equal to 1 mol/L. +\nDimension: ᴺ ᴸ^-3. +\nSee Also: [`Unitfu.L`](@ref), [`Unitfu.mol`](@ref)." +@unit M "M" Molar 1mol/L true true # Energy +" Unitfu.q +\nA quantity equal to the elementary charge, the charge of a single electron, +with a value of exactly 1.602,176,634 × 10^-19 C. The letter `q` is used instead of `e` to avoid +confusion with Euler's number. +\nDimension: ᴵ ᵀ. +\nSee Also: [`Unitfu.C`](@ref)." const q = 1.602_176_634e-19*C # CODATA 2018; `e` means 2.718... -@unit eV "eV" eV q*V true +" Unitfu.eV +\nThe electron-volt, a unit of energy, defined as q*V. +\nDimension: ᴸ^2 ᴹ ᵀ^-2. +\nSee also: [`Unitfu.q`](@ref), [`Unitfu.V`](@ref)." +@unit eV "eV" eV q*V true true # For convenience -@unit Hz2π "Hz2π" AngHertz 2π/s true -@unit bar "bar" Bar 100000Pa true +" Unitfu.Hz2π +\nA unit for convenience in angular frequency, equal to 2π Hz. +\nDimension: ᵀ^-1. +\nSee also: [`Unitfu.Hz`](@ref)." +@unit Hz2π "Hz2π" AngHertz 2π/s true true +" Unitfu.bar +\nThe bar, a metric unit of pressure, defined as 100 kPa. +\nDimension: ᴹ ᴸ^-1 ᵀ^-2. +\nSee also: [`Unitfu.kPa`](@ref)." +@unit bar "bar" Bar 100000Pa true true +" Unitfu.atm +\nThe standard atmosphere, a unit of pressure, defined as 101,325 Pa. +\nDimension: ᴹ ᴸ^-1 ᵀ^-2. +\nSee also: [`Unitfu.Pa`](@ref)." @unit atm "atm" Atmosphere 101325Pa false -@unit Torr "Torr" Torr 101325Pa//760 true +" Unitfu.Torr +\nThe torr, a unit of pressure, defined as 1/760 atm. +\nDimension: ᴹ ᴸ^-1 ᵀ^-2. +\nSee also: [`Unitfu.atm`](@ref)." +@unit Torr "Torr" Torr 101325Pa//760 true true # Constants (2018 CODATA values) (uncertainties in final digits) +" Unitfu.c0 +\nA quantity representing the speed of light in a vacuum, defined as exactly +2.997,924,58 × 10^8 m/s. +\n`Unitfu.c0` is a quantity (with units `m/s`) whereas [`Unitfu.c`](@ref) is a unit equal to `c0`. +\nDimension: ᴸ ᵀ^-1. +\nSee also: [`Unitfu.m`](@ref), [`Unitfu.s`](@ref)." const c0 = 299_792_458*m/s # exact +" Unitfu.c +\nThe speed of light in a vacuum, a unit of speed, defined as exactly +2.997,924,58 × 10^8 m/s. +\n[`Unitfu.c0`](@ref) is a quantity (with units `m/s`) whereas `Unitfu.c` is a unit equal to `c0`. +\nDimension: ᴸ ᵀ^-1. +\nSee also: [`Unitfu.m`](@ref), [`Unitfu.s`](@ref)." @unit c "c" SpeedOfLight 1c0 false -const μ0 = 4π*(1//10)^7*H/m # exact (but gets promoted to Float64...) -const µ0 = μ0 # magnetic constant -const ɛ0 = 1/(μ0*c^2) # exact, electric constant; changes here may affect -const ϵ0 = ɛ0 # test of issue 79. +" Unitfu.μ0 +\nA quantity representing the vacuum permeability constant, defined as 4π × 10^-7 H / m. +\nDimension: ᴸ ᴹ ᴵ^-2 ᵀ^-2. +\nSee also: [`Unitfu.H`](@ref), [`Unitfu.m`](@ref)." +const μ0 = 4π*(1//10)^7*H/m # exact (but gets promoted to Float64...), magnetic constant +" Unitfu.ε0 + Unitfu.ϵ0 +\nA quantity representing the vacuum permittivity constant, defined as 1 / (μ0 × c^2). +\nDimension: ᴵ^2 ᵀ^4 ᴸ^-3 ᴹ^-1. +\nSee also: [`Unitfu.μ0`](@ref), [`Unitfu.c`](@ref)." +const ε0 = 1/(μ0*c^2) # exact, electric constant; changes here may affect +@doc @doc(ε0) const ϵ0 = ε0 # test of issue 79. +" Unitfu.Z0 +\nA quantity representing the impedance of free space, a constant defined as μ0 × c. +\nDimension: ᴸ^2 ᴹ ᴵ^-2 ᵀ^-3. +\nSee also: [`Unitfu.μ0`](@ref), [`Unitfu.c`](@ref)." const Z0 = μ0*c # exact, impedance of free space +" Unitfu.G +\nA quantity representing the universal gravitational constant, equal to +6.674,30 × 10^-11 m^3 / (kg × s^2) (the CODATA 2018 recommended value). +\nDimension: ᴸ^3 ᴹ^-1 ᵀ^-2. +\nSee also: [`Unitfu.m`](@ref), [`Unitfu.kg`](@ref), [`Unitfu.s`](@ref)." const G = 6.674_30e-11*m^3/kg/s^2 # (15) gravitational constant +" Unitfu.gn +\nA quantity representing the nominal acceleration due to gravity in a vacuum +near the surface of the earth, defined by standard to be exactly 9.806,65 m / s^2. +\n`Unitfu.gn` is a quantity (with units `m/s^2`) whereas [`Unitfu.ge`](@ref) is a unit equal to `gn`. +\nDimension: ᴸ ᵀ^-2. +\nSee also: [`Unitfu.m`](@ref), [`Unitfu.s`](@ref)." const gn = 9.80665*m/s^2 # exact, standard acceleration of gravity +" Unitfu.h +\nA quantity representing Planck's constant, defined as exactly +6.626,070,15 × 10^-34 J × s. +\nDimension: ᴸ^2 ᴹ ᵀ^-1. +\nSee also: [`Unitfu.J`](@ref), [`Unitfu.s`](@ref)." const h = 6.626_070_15e-34*J*s # exact, Planck constant +" Unitfu.ħ +\nA quantity representing the reduced Planck constant, defined as h / 2π. +\nDimension: ᴸ^2 ᴹ ᵀ^-1. +\nSee also: [`Unitfu.h`](@ref)." const ħ = h/2π # hbar +" Unitfu.Φ0 +\nA quantity representing the superconducting magnetic flux quantum, defined as +h / (2 × q). +\nDimension: ᴸ^2 ᴹ ᴵ^-1 ᵀ^-2. +\nSee also: [`Unitfu.h`](@ref), [`Unitfu.q`](@ref)." const Φ0 = h/(2q) # Superconducting magnetic flux quantum +" Unitfu.me +\nA quantity representing the rest mass of an electron, equal to 9.109,383,7015 +× 10^-31 kg (the CODATA 2018 recommended value). +\nDimension: [`Unitfu.ᴹ`](@ref). +\nSee also: [`Unitfu.kg`](@ref)." const me = 9.109_383_7015e-31*kg # (28) electron rest mass +" Unitfu.mn +\nA quantity representing the rest mass of a neutron, equal to 1.674,927,498,04 +× 10^-27 kg (the CODATA 2018 recommended value). +\nDimension: [`Unitfu.ᴹ`](@ref). +\nSee also: [`Unitfu.kg`](@ref)." const mn = 1.674_927_498_04e-27*kg # (95) neutron rest mass +" Unitfu.mp +\nA quantity representing the rest mass of a proton, equal to 1.672,621,923,69 +× 10^-27 kg (the CODATA 2018 recommended value). +\nDimension: [`Unitfu.ᴹ`](@ref). +\nSee also: [`Unitfu.kg`](@ref)." const mp = 1.672_621_923_69e-27*kg # (51) proton rest mass +" Unitfu.μB +\nA quantity representing the Bohr magneton, equal to q × ħ / (2 × me). +\nDimension: ᴵ ᴸ^2. +\nSee also: [`Unitfu.q`](@ref), [`Unitfu.ħ`](@ref), [`Unitfu.me`](@ref)." const μB = q*ħ/(2*me) # Bohr magneton -const µB = μB +" Unitfu.Na +\nA quantity representing Avogadro's constant, defined as exactly +6.022,140,76 × 10^23 / mol. +\nDimension: ᴺ^-1. +\nSee also: [`Unitfu.mol`](@ref)." const Na = 6.022_140_76e23/mol # exact, Avogadro constant +" Unitfu.k +\nA quantity representing the Boltzmann constant, defined as exactly +1.380,649 × 10^-23 J / K. +\nDimension: ᴸ^2 ᴹ ᶿ^-1 ᵀ^-2. +\nSee also: [`Unitfu.J`](@ref), [`Unitfu.K`](@ref)." const k = 1.380_649e-23*(J/K) # exact, Boltzmann constant +" Unitfu.R +\nA quantity representing the molar gas constant, defined as +Na × k. +\nDimension: ᴸ^2 ᴹ ᴺ^-1 ᶿ^-1 ᵀ^-2. +\nSee also: [`Unitfu.Na`](@ref), [`Unitfu.k`](@ref)." const R = Na*k # molar gas constant +" Unitfu.σ +\nA quantity representing the Stefan-Boltzmann constant, defined as +π^2 × k^4 / (60 × ħ^3 × c^2). +\nDimension: ᴹ ᶿ^-4 ᵀ^-3. +\nSee also: [`Unitfu.k`](@ref), [`Unitfu.ħ`](@ref), [`Unitfu.c`](@ref)." const σ = π^2*k^4/(60*ħ^3*c^2) # Stefan-Boltzmann constant +" Unitfu.R∞ +\nA quantity representing the Rydberg constant, equal to 1.097,373,156,8160 × 10^-7 / m +(the CODATA 2018 recommended value). +\nDimension: ᴸ^-1. +\nSee also: [`Unitfu.m`](@ref)." const R∞ = 10_973_731.568_160/m # (21) Rydberg constant +" Unitfu.u +\nThe unified atomic mass unit, or dalton, a unit of mass defined as 1/12 the +mass of an unbound neutral atom of carbon-12, equal to 1.660,539,066,60 × 10^-27 kg +(the CODATA 2018 recommended value). +\nDimension: [`Unitfu.ᴹ`](@ref). +\nSee Also: [`Unitfu.kg`](@ref)." @unit u "u" UnifiedAtomicMassUnit 1.660_539_066_60e-27*kg false # (50) # Acceleration +" Unitfu.ge +\nThe nominal acceleration due to gravity in a vacuum near the surface of the +earth, a unit of acceleration, defined by standard to be exactly 9.806,65 m / s^2. +\n[`Unitfu.gn`](@ref) is a quantity (with units `m/s^2`) whereas `Unitfu.ge` is a unit equal to `gn`. +\nDimension: ᴸ ᵀ^-2. +\nSee also: [`Unitfu.m`](@ref), [`Unitfu.s`](@ref)." @unit ge "ge" EarthGravity gn false # CGS units -@unit Gal "Gal" Gal 1cm/s^2 true -@unit dyn "dyn" Dyne 1g*cm/s^2 true -@unit erg "erg" Erg 1g*cm^2/s^2 true -@unit Ba "Ba" Barye 1g/cm/s^2 true -@unit P "P" Poise 1g/cm/s true -@unit St "St" Stokes 1cm^2/s true -@unit Gauss "Gauss" Gauss (1//10_000)*T true -@unit Oe "Oe" Oersted (1_000/4π)*A/m true -@unit Mx "Mx" Maxwell (1//100_000_000)*Wb true +" Unitfu.Gal +\nThe gal, a CGS unit of acceleration, defined as 1 cm / s^2. +\nDimension: ᴸ ᵀ^-2. +\nSee also: [`Unitfu.cm`](@ref), [`Unitfu.s`](@ref)." +@unit Gal "Gal" Gal 1cm/s^2 true true +" Unitfu.dyn +\nThe dyne, a CGS unit of force, defined as 1 g × cm / s^2. +\nDimension: ᴸ ᴹ ᵀ^-2. +\nSee also: [`Unitfu.cm`](@ref), [`Unitfu.s`](@ref), [`Unitfu.g`](@ref)." +@unit dyn "dyn" Dyne 1g*cm/s^2 true true +" Unitfu.erg +\nThe erg, a CGS unit of energy, defined as 1 dyn × cm. +\nDimension: ᴸ^2 ᴹ ᵀ^-2. +\nSee also: [`Unitfu.cm`](@ref), [`Unitfu.dyn`](@ref)" +@unit erg "erg" Erg 1g*cm^2/s^2 true true +" Unitfu.Ba +\nThe barye, a CGS unit of pressure, defined as 1 dyn / cm^2. +\nDimension: ᴹ ᴸ^-1 ᵀ^-2. +\nSee also: [`Unitfu.cm`](@ref), [`Unitfu.dyn`](@ref)" +@unit Ba "Ba" Barye 1g/cm/s^2 true true +" Unitfu.P +\nThe poise, a CGS unit of dynamic viscosity, defined as 1 dyn × s / cm^2. +\nDimension: ᴹ ᴸ^-1 ᵀ^-1. +\nSee also: [`Unitfu.cm`](@ref), [`Unitfu.dyn`](@ref), [`Unitfu.s`](@ref)" +@unit P "P" Poise 1g/cm/s true true +" Unitfu.St +\nThe stokes, a CGS unit of kinematic viscosity, defined as 1 cm^2 / s. +\nDimension: ᴹ^2 ᵀ^-1. +\nSee also: [`Unitfu.cm`](@ref), [`Unitfu.s`](@ref)" +@unit St "St" Stokes 1cm^2/s true true +" Unitfu.Gauss +\nThe gauss, a CGS unit of magnetic B-field strength, defined as 1 Mx / cm^2. +\nDimension: ᴹ ᴵ^-1 ᵀ^-2. +\nSee also: [`Unitfu.cm`](@ref), [`Unitfu.Mx`](@ref)" +@unit Gauss "Gauss" Gauss (1//10_000)*T true true +" Unitfu.Oe +\nThe oersted, a CGS unit of magnetic H-field strength, defined as 1000 A / (4π × m). +\nDimension: ᴵ ᴸ^-1. +\nSee also: [`Unitfu.A`](@ref), [`Unitfu.m`](@ref)" +@unit Oe "Oe" Oersted (1_000/4π)*A/m true true +" Unitfu.Mx +\nThe maxwell, a CGS unit of magnetic flux, defined as 1 Gauss × cm^2. +\nDimension: ᴸ^2 ᴹ ᴵ^-1 ᵀ^-2. +\nSee also: [`Unitfu.cm`](@ref), [`Unitfu.Gauss`](@ref)" +@unit Mx "Mx" Maxwell (1//100_000_000)*Wb true true ######### @@ -185,38 +557,111 @@ const R∞ = 10_973_731.568_160/m # (21) Rydberg constant # Length #key: Symbol Display Name Equivalent to 10^n prefixes? +" Unitfu.inch +\nThe inch, a US customary unit of length defined as 2.54 cm. +\nDimension: [`Unitfu.ᴸ`](@ref). +\nSee Also: [`Unitfu.cm`](@ref)." @unit inch "inch" Inch (254//10000)*m false +" Unitfu.mil +\nThe mil, a US customary unit of length defined as 1/1000 inch. +\nDimension: [`Unitfu.ᴸ`](@ref). +\nSee Also: [`Unitfu.inch`](@ref)." @unit mil "mil" Mil (1//1000)*inch false +" Unitfu.ft +\nThe foot, a US customary unit of length defined as 12 inch. +\nDimension: [`Unitfu.ᴸ`](@ref). +\nSee Also: [`Unitfu.inch`](@ref)." @unit ft "ft" Foot 12inch false +" Unitfu.yd +\nThe yard, a US customary unit of length defined as 3 ft. +\nDimension: [`Unitfu.ᴸ`](@ref). +\nSee Also: [`Unitfu.ft`](@ref)." @unit yd "yd" Yard 3ft false +" Unitfu.mi +\nThe mile, a US customary unit of length defined as 1760 yd. +\nDimension: [`Unitfu.ᴸ`](@ref). +\nSee Also: [`Unitfu.yd`](@ref)." @unit mi "mi" Mile 1760yd false +" Unitfu.angstrom + Unitfu.Å +\nThe angstrom, a metric unit of length defined as 1/10 nm. +\nDimension: [`Unitfu.ᴸ`](@ref). +\nSee Also: [`Unitfu.nm`](@ref)." @unit angstrom "Å" Angstrom (1//10)*nm false # U+00c5 (opt-shift-A on macOS) and U+212b ('\Angstrom' in REPL) look identical: -const Å = Å = angstrom +@doc @doc(angstrom) const Å = Å = angstrom # Area +" Unitfu.ac +\nThe acre, a US customary unit of area defined as 4840 yd^2. +\nDimension: ᴸ^2. +\nSee Also: [`Unitfu.yd`](@ref)." @unit ac "ac" Acre (316160658//78125)*m^2 false # Temperatures +" Unitfu.Ra +\nThe rankine, a US customary unit of temperature defined as 5/9 K. +\nDimension: [`Unitfu.ᶿ`](@ref). +\nSee Also: [`Unitfu.K`](@ref)." @unit Ra "Ra" Rankine (5//9)*K false +" Unitfu.°F +\nThe degree Fahrenheit, a US customary unit of temperature, defined such that 0 °F = 459.67 Ra. +\nDimension: [`Unitfu.ᶿ`](@ref). +\nSee also: [`Unitfu.Ra`](@ref)." @affineunit °F "°F" (45967//100)Ra # Masses +" Unitfu.lb +\nThe pound-mass, a US customary unit of mass defined as exactly 0.453,592,37 kg. +\nDimension: [`Unitfu.ᴹ`](@ref). +\nSee Also: [`Unitfu.kg`](@ref)." @unit lb "lb" Pound 0.45359237kg false # is exact +" Unitfu.oz +\nThe ounce, a US customary unit of mass defined as 1/16 lb. +\nDimension: [`Unitfu.ᴹ`](@ref). +\nSee Also: [`Unitfu.lb`](@ref)." @unit oz "oz" Ounce lb//16 false +" Unitfu.slug +\nThe slug, a US customary unit of mass defined as 1 lbf × s^2 / ft. +\nDimension: [`Unitfu.ᴹ`](@ref). +\nSee Also: [`Unitfu.lbf`](@ref), [`Unitfu.s`](@ref), [`Unitfu.ft`](@ref)." @unit slug "slug" Slug 1lb*ge*s^2/ft false +" Unitfu.dr +\nThe dram, a US customary unit of mass defined as 1/16 oz. +\nDimension: [`Unitfu.ᴹ`](@ref). +\nSee Also: [`Unitfu.oz`](@ref)." @unit dr "dr" Dram oz//16 false +" Unitfu.gr +\nThe grain, a US customary unit of mass defined as 1/7000 lb. +\nDimension: [`Unitfu.ᴹ`](@ref). +\nSee Also: [`Unitfu.lb`](@ref)." @unit gr "gr" Grain (32//875)*dr false # Force +" Unitfu.lbf +\nThe pound-force, a US customary unit of force defined as 1 lb × ge. +\nDimension: ᴸ ᴹ ᵀ^-2. +\nSee Also: [`Unitfu.lb`](@ref), [`Unitfu.ge`](@ref)." @unit lbf "lbf" PoundsForce 1lb*ge false # Energy # Use ISO 31-4 for BTU definition -@unit cal "cal" Calorie 4.184J true +" Unitfu.cal +\nThe calorie, a unit of energy defined as exactly 4.184 J. +\nDimension: ᴸ^2 ᴹ ᵀ^-2. +\nSee Also: [`Unitfu.J`](@ref)." +@unit cal "cal" Calorie 4.184J true true +" Unitfu.btu +\nThe British thermal unit, a US customary unit of heat defined by ISO 31-4 as exactly 1055.06 J. +\nDimension: ᴸ^2 ᴹ ᵀ^-2. +\nSee Also: [`Unitfu.J`](@ref)." @unit btu "btu" BritishThermalUnit 1055.06J false # Pressure +" Unitfu.psi +\nPounds per square inch, a US customary unit of pressure defined as 1 lbf / inch^2. +\nDimension: ᴹ ᴸ^-1 ᵀ^-2. +\nSee Also: [`Unitfu.lbf`](@ref), [`Unitfu.inch`](@ref)." @unit psi "psi" PoundsPerSquareInch 1lbf/inch^2 false ######### @@ -245,7 +690,7 @@ isrootpower_dim(::typeof(dimension(Pa))) = true isrootpower_dim(::typeof(dimension(W/m^2/Hz))) = false # spectral flux dens. isrootpower_dim(::typeof(dimension(W/m^2))) = false # intensity isrootpower_dim(::typeof(dimension(W/m^2/m))) = false -isrootpower_dim(::typeof(dimension(m^3))) = false # reflectivity +isrootpower_dim(::typeof(dimension(m^3))) = false # reflectivity isrootpower_dim(::typeof(dimension(Ω))) = true isrootpower_dim(::typeof(dimension(S))) = true isrootpower_dim(::typeof(dimension(Hz))) = false @@ -259,8 +704,7 @@ isrootpower_dim(::typeof(dimension(J))) = false # - Candela conflicts with `Base.cd` so it is not brought in (issue #102) # - Degrees: ° -# The following line has two different character encodings for μ -const si_prefixes = (:y, :z, :a, :f, :p, :n, :μ, :µ, :m, :c, :d, +const si_prefixes = (:y, :z, :a, :f, :p, :n, :μ, :m, :c, :d, Symbol(""), :da, :h, :k, :M, :G, :T, :P, :E, :Z, :Y) const si_no_prefix = (:m, :s, :A, :K, :g, :mol, :rad, :sr, :Hz, :N, :Pa, #:cd, @@ -291,6 +735,27 @@ end ######### preferunits(kg) # others done in @refunit +# Fix documentation for all kg based units +for (k,v) in prefixdict + if k != 3 + sym = Symbol(v,:g) + docstring = """ + Unitfu.$sym + + A prefixed unit, equal to 10^$(k-3) kg. Note that `kg`, not `g`, is the base unit. + + Dimension: [`Unitfu.ᴹ`](@ref). + + See also: [`Unitfu.kg`](@ref). + """ + run = quote @doc $docstring $sym end + eval(run) + end +end +@doc " Unitfu.kg +\nThe kilogram, the SI base unit of mass. +Note that `kg`, not `g`, is the base unit. +\nDimension: [`Unitfu.ᴹ`](@ref)." kg """ Unitfu.promote_to_derived() @@ -310,7 +775,7 @@ of quantities: - `T` (tesla) for B-field - `J*s` (joule-second) for action -If you want this as default behavior (it was for versions of Unitful prior to 0.1.0), +If you want this as default behavior (it was for versions of Unitfu prior to 0.1.0), consider invoking this function in your `.juliarc.jl` file which is loaded when you open Julia. This function is not exported. """ diff --git a/src/quantities.jl b/src/quantities.jl index 26fad9c4..97cf281c 100644 --- a/src/quantities.jl +++ b/src/quantities.jl @@ -135,7 +135,7 @@ end Base.mod2pi(x::DimensionlessQuantity) = mod2pi(strict_uconvert(NoUnits, x)) Base.mod2pi(x::AbstractQuantity{S, NoDims, <:Units{(Unitfu.Unit{:Degree, NoDims}(0, 1//1),), NoDims}}) where S = mod(x, 360°) -Base.modf(x::DimensionlessQuantity) = modf(uconvert(NoUnits, x)) +Base.modf(x::DimensionlessQuantity) = modf(strict_uconvert(NoUnits, x)) # Addition / subtraction for op in [:+, :-] @@ -223,8 +223,10 @@ sqrt(x::AbstractQuantity) = Quantity(sqrt(x.val), sqrt(unit(x))) cbrt(x::AbstractQuantity) = Quantity(cbrt(x.val), cbrt(unit(x))) for _y in (:sin, :cos, :tan, :asin, :acos, :atan, :sinh, :cosh, :tanh, :asinh, :acosh, :atanh, - :sinpi, :cospi, :sinc, :cosc, :cis) - @eval ($_y)(x::DimensionlessQuantity) = ($_y)(strict_uconvert(NoUnits, x)) + :sinpi, :cospi, :sinc, :cosc, :cis, :cispi, :sincospi) + if isdefined(Base, _y) + @eval Base.$(_y)(x::DimensionlessQuantity) = Base.$(_y)(strict_uconvert(NoUnits, x)) + end end atan(y::AbstractQuantity{T1,D,U1}, x::AbstractQuantity{T2,D,U2}) where {T1,T2,D,U1,U2} = @@ -287,18 +289,31 @@ for (i,j) in zip((:<, :isless), (:_lt, :_isless)) end Base.rtoldefault(::Type{<:AbstractQuantity{T,D,U}}) where {T,D,U} = Base.rtoldefault(T) -isapprox(x::AbstractQuantity{T,D,U}, y::AbstractQuantity{T,D,U}; atol=zero(Quantity{real(T),D,U}), kwargs...) where {T,D,U} = - isapprox(x.val, y.val; atol=strict_uconvert(unit(y), atol).val, kwargs...) + +function isapprox( + x::AbstractQuantity{T,D,U}, + y::AbstractQuantity{T,D,U}; + atol = zero(Quantity{real(T),D,U}), + kwargs..., +) where {T,D,U} + return isapprox(x.val, y.val; atol=strict_uconvert(unit(y), atol).val, kwargs...) +end + function isapprox(x::AbstractQuantity, y::AbstractQuantity; kwargs...) dimension(x) != dimension(y) && return false return isapprox(promote(x,y)...; kwargs...) end + isapprox(x::AbstractQuantity, y::Number; kwargs...) = isapprox(promote(x,y)...; kwargs...) isapprox(x::Number, y::AbstractQuantity; kwargs...) = isapprox(y, x; kwargs...) -function isapprox(x::AbstractArray{<:AbstractQuantity{T1,D,U1}}, - y::AbstractArray{<:AbstractQuantity{T2,D,U2}}; rtol::Real=Base.rtoldefault(T1,T2,0), - atol=zero(Quantity{T1,D,U1}), norm::Function=norm) where {T1,D,U1,T2,U2} +function isapprox( + x::AbstractArray{<:AbstractQuantity{T1,D,U1}}, + y::AbstractArray{<:AbstractQuantity{T2,D,U2}}; + rtol::Real=Base.rtoldefault(T1,T2,0), + atol=zero(Quantity{real(T1),D,U1}), + norm::Function=norm, +) where {T1,D,U1,T2,U2} d = norm(x - y) if isfinite(d) @@ -308,6 +323,7 @@ function isapprox(x::AbstractArray{<:AbstractQuantity{T1,D,U1}}, return all(ab -> isapprox(ab[1], ab[2]; rtol=rtol, atol=atol), zip(x, y)) end end + isapprox(x::AbstractArray{S}, y::AbstractArray{T}; kwargs...) where {S <: AbstractQuantity,T <: AbstractQuantity} = false function isapprox(x::AbstractArray{S}, y::AbstractArray{N}; @@ -318,6 +334,7 @@ function isapprox(x::AbstractArray{S}, y::AbstractArray{N}; false end end + isapprox(y::AbstractArray{N}, x::AbstractArray{S}; kwargs...) where {S <: AbstractQuantity,N <: Number} = isapprox(x,y; kwargs...) @@ -356,11 +373,11 @@ round(x::AbstractQuantity, r::RoundingMode=RoundNearest; kwargs...) = _rounderr() round(x::DimensionlessQuantity; kwargs...) = round(strict_uconvert(NoUnits, x); kwargs...) round(x::DimensionlessQuantity, r::RoundingMode; kwargs...) = - round(strict_uconvert(NoUnits, x), r; kwargs...) + round(uconvert(NoUnits, x), r; kwargs...) round(::Type{T}, x::AbstractQuantity, r=RoundingMode=RoundNearest; kwargs...) where {T<:Number} = _dimerr(:round) round(::Type{T}, x::DimensionlessQuantity, r::RoundingMode=RoundNearest; - kwargs...) where {T<:Number} = round(T, strict_uconvert(NoUnits, x), r; kwargs...) + kwargs...) where {T<:Number} = round(T, uconvert(NoUnits, x), r; kwargs...) function round(::Type{T}, x::AbstractQuantity; kwargs...) where {S, T <: Quantity{S}} u = unit(T) @@ -389,6 +406,30 @@ zero(x::Type{<:AbstractQuantity{T,D}}) where {T,D} = zero(T) * upreferred(D) zero(x::Type{<:AbstractQuantity{T,D,U}}) where {T,D,U<:ScalarUnits} = zero(T)*U() zero(x::Type{<:AbstractQuantity{T,D,U}}) where {T,D,U<:AffineUnits} = zero(T)*absoluteunit(U()) +function zero(x::AbstractArray{T}) where T<:AbstractQuantity + if isconcretetype(T) + z = zero(T) + fill!(similar(x, typeof(z)), z) + else + dest = similar(x) + for i = eachindex(x) + if isassigned(x, i...) + dest[i] = zero(x[i]) + else + dest[i] = zero(T) + end + end + dest + end +end +@static if VERSION < v"1.8.0-DEV.107" + function zero(x::AbstractArray{Union{T,Missing}}) where T<:AbstractQuantity # only matches _concrete_ T ... + @assert isconcretetype(T) # ... but check anyway + z = zero(T) + fill!(similar(x, typeof(z)), z) + end +end + one(x::AbstractQuantity) = one(x.val) one(x::AffineQuantity) = throw(AffineError("no multiplicative identity for affine quantity $x.")) @@ -422,8 +463,7 @@ real(x::AbstractQuantity) = Quantity(real(x.val), unit(x)) imag(x::AbstractQuantity) = Quantity(imag(x.val), unit(x)) conj(x::AbstractQuantity) = Quantity(conj(x.val), unit(x)) -@inline norm(x::AbstractQuantity, p::Real=2) = - p == 0 ? (x==zero(x) ? typeof(abs(x))(0) : typeof(abs(x))(1)) : abs(x) +@inline norm(x::AbstractQuantity, p::Real=2) = Quantity(norm(x.val, p), unit(x)) """ sign(x::AbstractQuantity) diff --git a/src/units.jl b/src/units.jl index fd07579e..61f4fd1a 100644 --- a/src/units.jl +++ b/src/units.jl @@ -302,6 +302,6 @@ factory defaults, this function will return a product of powers of base SI units (as [`Unitfu.FreeUnits`](@ref)). """ @generated function upreferred(x::Dimensions{D}) where {D} - u = *(FreeUnits{((Unitfu.promotion[name(z)]^z.power for z in D)...,),()}()) + u = prod((NoUnits, (promotion[name(z)]^z.power for z in D)...)) :($u) end diff --git a/src/user.jl b/src/user.jl index 252e2938..e501fb0f 100644 --- a/src/user.jl +++ b/src/user.jl @@ -26,7 +26,7 @@ function register(unit_module::Module) end """ - @dimension(symb, abbr, name) + @dimension(symb, abbr, name, autodocs=false) Creates new dimensions. `name` will be used like an identifier in the type parameter for a [`Unitfu.Dimension`](@ref) object. `symb` will be a symbol defined in the namespace from which this macro is called that is bound to a @@ -53,19 +53,51 @@ Returns the `Dimensions` object to which `symb` is bound. Usage example from `src/pkgdefaults.jl`: `@dimension ᴸ "ᴸ" Length` """ -macro dimension(symb, abbr, name) +macro dimension(symb, abbr, name, autodocs=false) s = Symbol(symb) x = Expr(:quote, name) uname = Symbol(name,"Units") funame = Symbol(name,"FreeUnits") + name_links = __module__ == Unitfu ? "[`Unitfu.Quantity`](@ref), [`Unitfu.Level`](@ref)" : "`Unitfu.Quantity`, `Unitfu.Level`" + unit_links = __module__ == Unitfu ? "[`Unitfu.Units`](@ref)" : "`Unitfu.Units`" + funit_links = __module__ == Unitfu ? "[`Unitfu.FreeUnits`](@ref)" : "`Unitfu.FreeUnits`" + name_doc = """ + $__module__.$name{T, U} + + A supertype for quantities and levels of dimension [`$__module__.$s`](@ref) with a value + of type `T` and units `U`. + + See also: [`$__module__.$s`](@ref), $name_links. + """ + unit_doc = """ + $__module__.$uname{U} + + A supertype for units of dimension [`$__module__.$s`](@ref). Equivalent to + `Unitfu.Units{U, $__module__.$s}`. + + See also: [`$__module__.$s`](@ref), $unit_links. + """ + funit_doc = """ + $__module__.$funame{U} + + A supertype for $funit_links of dimension [`$__module__.$s`](@ref). Equivalent to + `Unitfu.FreeUnits{U, $__module__.$s}`. + + See also: [`$__module__.$s`](@ref). + """ esc(quote $Unitfu.abbr(::$Dimension{$x}) = $abbr - const global $s = $Dimensions{($Dimension{$x}(1),)}() + Base.@__doc__ const global $s = $Dimensions{($Dimension{$x}(1),)}() const global ($name){T,U} = Union{ $Quantity{T,$s,U}, $Level{L,S,$Quantity{T,$s,U}} where {L,S}} const global ($uname){U} = $Units{U,$s} const global ($funame){U} = $FreeUnits{U,$s} + if $autodocs + @doc $name_doc $name + @doc $unit_doc $uname + @doc $funit_doc $funame + end $s end) end @@ -85,22 +117,46 @@ Usage examples: - `@derived_dimension Area ᴸ^2` gives `Area` and `AreaUnit` type aliases - `@derived_dimension Speed ᴸ/ᵀ` gives `Speed` and `SpeedUnit` type aliases """ -macro derived_dimension(name, dims) +macro derived_dimension(name, dims, autodocs=false) uname = Symbol(name,"Units") funame = Symbol(name,"FreeUnits") + name_links = __module__ == Unitfu ? "[`Unitfu.Quantity`](@ref), [`Unitfu.Level`](@ref)" : "`Unitfu.Quantity`, `Unitfu.Level`" + unit_links = __module__ == Unitfu ? "[`Unitfu.Units`](@ref)" : "`Unitfu.Units`" + funit_links = __module__ == Unitfu ? "[`Unitfu.FreeUnits`](@ref)" : "`Unitfu.FreeUnits`" + name_doc = """ + $__module__.$name{T, U} + A supertype for quantities and levels of dimension `$dims` with a value of type `T` and + units `U`. + See also: $name_links. + """ + unit_doc = """ + $__module__.$uname{U} + A supertype for units of dimension `$dims`. Equivalent to `Unitfu.Units{U, $dims}`. + See also: $unit_links. + """ + funit_doc = """ + $__module__.$funame{U} + A supertype for $funit_links of dimension `$dims`. Equivalent to + `Unitfu.FreeUnits{U, $dims}`. + """ esc(quote const global ($name){T,U} = Union{ $Quantity{T,$dims,U}, $Level{L,S,$Quantity{T,$dims,U}} where {L,S}} const global ($uname){U} = $Units{U,$dims} const global ($funame){U} = $FreeUnits{U,$dims} + if $autodocs + @doc $name_doc $name + @doc $unit_doc $uname + @doc $funit_doc $funame + end nothing end) end """ - @refunit(symb, name, abbr, dimension, tf) + @refunit(symb, name, abbr, dimension, tf, autodocs=false) Define a reference unit, typically SI. Rather than define conversion factors between each and every unit of a given dimension, conversion factors are given between each unit and a reference unit, defined by this macro. @@ -109,7 +165,12 @@ This macro extends [`Unitfu.abbr`](@ref) so that the reference unit can be displayed in an abbreviated format. If `tf == true`, this macro generates symbols for every power of ten of the unit, using the standard SI prefixes. A `dimension` must be given ([`Unitfu.Dimensions`](@ref) object) that specifies the dimension -of the reference unit. +of the reference unit. If `autodocs == true`, autogenerated docstrings for +SI-prefixed units will be added. This option has no effect when 'tf == false'. + +!!! compat "Unitfu 1.10" + Documenting the resulting unit by adding a docstring before the `@refunit` call requires + Unitfu 1.10 or later. The `autodocs` argument also requires Unitfu 1.10 or later. In principle, users can use this macro, but it probably does not make much sense to do so. If you define a new (probably unphysical) dimension using @@ -128,7 +189,7 @@ Usage example: `@refunit m "m" Meter ᴸ true` This example, found in `src/pkgdefaults.jl`, generates `km`, `m`, `cm`, ... """ -macro refunit(symb, abbr, name, dimension, tf) +macro refunit(symb, abbr, name, dimension, tf, autodocs=false) expr = Expr(:block) n = Meta.quot(Symbol(name)) @@ -138,11 +199,11 @@ macro refunit(symb, abbr, name, dimension, tf) if tf push!(expr.args, quote - Unitfu.@prefixed_unit_symbols $symb $name $dimension (1.0, 1) + Base.@__doc__ $Unitfu.@prefixed_unit_symbols $symb $name $dimension (1.0, 1) $autodocs end) else push!(expr.args, quote - Unitfu.@unit_symbols $symb $name $dimension (1.0, 1) + Base.@__doc__ $Unitfu.@unit_symbols $symb $name $dimension (1.0, 1) end) end @@ -155,7 +216,7 @@ macro refunit(symb, abbr, name, dimension, tf) end """ - @unit(symb,abbr,name,equals,tf) + @unit(symb,abbr,name,equals,tf,autodocs=false) Define a unit. Rather than specifying a dimension like in [`@refunit`](@ref), `equals` should be a [`Unitfu.Quantity`](@ref) equal to one of the unit being defined. If `tf == true`, symbols will be made for each power-of-ten prefix. @@ -166,7 +227,7 @@ Usage example: `@unit mi "mi" Mile (201168//125)*m false` This example will *not* generate `kmi` (kilomiles). """ -macro unit(symb,abbr,name,equals,tf) +macro unit(symb,abbr,name,equals,tf,autodocs=false) expr = Expr(:block) n = Meta.quot(Symbol(name)) @@ -180,11 +241,11 @@ macro unit(symb,abbr,name,equals,tf) if tf push!(expr.args, quote - Unitfu.@prefixed_unit_symbols $symb $name $d $basef + Base.@__doc__ $Unitfu.@prefixed_unit_symbols $symb $name $d $basef $autodocs end) else push!(expr.args, quote - Unitfu.@unit_symbols $symb $name $d $basef + Base.@__doc__ $Unitfu.@unit_symbols $symb $name $d $basef end) end @@ -204,7 +265,7 @@ in terms of an absolute scale; the scaling is the same as the absolute scale. Ex macro affineunit(symb, abbr, offset) s = Symbol(symb) return esc(quote - const global $s = affineunit($offset) + Base.@__doc__ const global $s = affineunit($offset) Base.show(io::IO, ::genericunit($s)) = begin col = get(io, :unitsymbolcolor, :cyan) printstyled(io, color = col, $abbr) @@ -228,35 +289,50 @@ function basefactors_expr(m::Module, n, basefactor) end """ - @prefixed_unit_symbols(symb,name,dimension,basefactor) + @prefixed_unit_symbols(symb,name,dimension,basefactor,autodocs=false) Not called directly by the user. Given a unit symbol and a unit's name, -will define units for each possible SI power-of-ten prefix on that unit. +will define units for each possible SI power-of-ten prefix on that unit. If +`autodocs == true`, it will automatically generate docstrings for these units. -Example: `@prefixed_unit_symbols m Meter ᴸ (1.0,1)` results in nm, cm, m, km, ... -all getting defined in the calling namespace. +!!! compat "Unitfu 1.10" + Documenting the resulting unit by adding a docstring before the `@prefixed_unit_symbols` + call requires Unitfu 1.10 or later. The `autodocs` argument also requires Unitfu 1.10 + or later. + +Example: `@prefixed_unit_symbols m Meter ᴸ (1.0,1) true` results in `nm`, `cm`, `m`, `km`, ... +all getting defined in the calling namespace, with docstrings automatically defined for SI-prefixed units. """ -macro prefixed_unit_symbols(symb,name,user_dimension,basefactor) +macro prefixed_unit_symbols(symb,name,user_dimension,basefactor,autodocs=false) expr = Expr(:block) n = Meta.quot(Symbol(name)) for (k,v) in prefixdict s = Symbol(v,symb) u = :($Unit{$n, $user_dimension}($k,1//1)) - ea = quote - $(basefactors_expr(__module__, n, basefactor)) - const global $s = $FreeUnits{($u,), $dimension($u), $nothing}() + if k == 0 + ea = quote + $(basefactors_expr(__module__, n, basefactor)) + Base.@__doc__ const global $s = $FreeUnits{($u,), $dimension($u), $nothing}() + end + else + docstring1 = """ + $__module__.$s + + A prefixed unit, equal to 10^$k $symb. + + Dimension: """ + docstring2 = "\n\nSee also: [`$__module__.$symb`](@ref)." + ea = quote + $(basefactors_expr(__module__, n, basefactor)) + const global $s = $FreeUnits{($u,), $dimension($u), $nothing}() + if $autodocs + @doc $docstring1*string($user_dimension)*$docstring2 $s + end + end end push!(expr.args, ea) end - # These lines allow for μ to be typed with option-m on a Mac. - s = Symbol(:µ, symb) - u = :($Unit{$n, $user_dimension}(-6,1//1)) - push!(expr.args, quote - $(basefactors_expr(__module__, n, basefactor)) - const global $s = $FreeUnits{($u,), $dimension($u), nothing}() - end) - esc(expr) end @@ -273,7 +349,7 @@ macro unit_symbols(symb,name,user_dimension,basefactor) u = :($Unit{$n, $user_dimension}(0,1//1)) esc(quote $(basefactors_expr(__module__, n, basefactor)) - const global $s = $FreeUnits{($u,), $dimension($u), $nothing}() + Base.@__doc__ const global $s = $FreeUnits{($u,), $dimension($u), $nothing}() end) end @@ -314,7 +390,28 @@ function preferunits(u0::Units, u::Units...) "For instance, it should not be used with units of dimension ᴸ^2.") end y = typeof(dim).parameters[1][1] - promotion[name(y)] = typeof(unit).parameters[1][1] + promotion[name(y)] = unit + end + + # Let's run a quick check for anything where, for instance, current was defined is C/ms and time + # was defined in ns. We'll give a warning if this is the case so the user knows they may get + # unexpected (though still correct) results. There may be cases where people would want to do this, + # so we won't stop them, just let them know they're doing it. + ulist = (typeof(i[2]).parameters[1] for i in promotion) + check = Dict{Dimensions, Unit}() + for a in ulist + ulistA = (i^(1/i.power) for i in a) + for i in ulistA + k = dimension(i) + if haskey(check, k) + if i!=check[k] + @warn "Preferred units contain complex units based on units of the same dimension but different scales: "*string(i)*" and "*string(check[k])* + "\nThis may be intentional, but otherwise could lead to redundant units, such as ms/s or kg/g" + end + else + check[k] = i + end + end end nothing @@ -498,7 +595,7 @@ julia> u"m,kg,s" (m, kg, s) julia> typeof(1.0u"m/s") -Quantity{Float64,ᴸ*ᵀ^-1,Unitfu.FreeUnits{(m, s^-1),ᴸ ᵀ^-1,nothing}} +Quantity{Float64, ᴸ∙ ᵀ⁻¹, FreeUnits{(m, s⁻¹), ᴸ∙ ᵀ⁻¹, nothing}} julia> u"ħ" 1.0545718176461565e-34 J s diff --git a/src/utils.jl b/src/utils.jl index 55f24b87..c0fd3a94 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -61,17 +61,17 @@ the result to PyPlot, for example. ```jldoctest julia> a = [1u"m", 2u"m"] -2-element Array{Quantity{Int64,ᴸ,Unitfu.FreeUnits{(m,),ᴸ,nothing}},1}: - 1 m - 2 m +2-element Vector{Quantity{Int64, ᴸ, FreeUnits{(m,), ᴸ, nothing}}}: + 1 + 2 julia> b = ustrip(a) -2-element reinterpret(Int64, ::Array{Quantity{Int64,ᴸ,Unitfu.FreeUnits{(m,),ᴸ,nothing}},1}): +2-element reinterpret(Int64, ::Vector{Quantity{Int64, ᴸ, FreeUnits{(m,), ᴸ, nothing}}}): 1 2 julia> a[1] = 3u"m"; b -2-element reinterpret(Int64, ::Array{Quantity{Int64,ᴸ,Unitfu.FreeUnits{(m,),ᴸ,nothing}},1}): +2-element reinterpret(Int64, ::Vector{Quantity{Int64, ᴸ, FreeUnits{(m,), ᴸ, nothing}}}): 3 2 ``` diff --git a/test/runtests.jl b/test/runtests.jl index 991671f2..5d364f86 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -218,12 +218,17 @@ end @test 1u"rps" == 2π/s @test 1u"rpm" == 360°/minute @test 1u"rpm" == 2π/minute + + # Issue 458: + @test deg2rad(360°) ≈ 2π * rad + @test rad2deg(2π * rad) ≈ 360° end end end include("dates.jl") + @testset "Temperature and affine quantities" begin @testset "Affine transforms and quantities" begin @test 1°C isa RelativeScaleTemperature @@ -341,6 +346,16 @@ Unitfu.uconvert(U::Unitfu.Units, q::QQQ) = uconvert(U, Quantity(q.val, cm)) @testset "Promotion" begin @testset "> Unit preferences" begin + # Should warn on possible redundant units issue (ms and s) + @test_logs (:warn, r"^Preferred units contain complex units") Unitfu.preferunits(C/ms) + # Test for wacky prefered units functionality + Unitfu.preferunits(C/s) + @test @inferred(upreferred(V/m)) == kg*m*C^-1*s^-2 + @test dimension(upreferred(V/m)) == dimension(V/m) + # Reset preferred units to default, except for units of dimension 𝐋*𝐌*𝐈^-1*𝐓^-3, + # because upreferred has already been called for that dimension + Unitfu.preferunits(A) + # Only because we favor SI, we have the following: @test @inferred(upreferred(N)) === kg*m/s^2 @test @inferred(upreferred(dimension(N))) === kg*m/s^2 @@ -398,7 +413,7 @@ Unitfu.uconvert(U::Unitfu.Units, q::QQQ) = uconvert(U, Quantity(q.val, cm)) @test @inferred(promote(1g, 1.0kg)) === (0.001kg, 1.0kg) @test @inferred(promote(1.0m, 1kg)) === (1.0m, 1.0kg) @test @inferred(promote(1kg, 1.0m)) === (1.0kg, 1.0m) - @test_broken @inferred(promote(1.0m, 1)) === (1.0m, 1.0) # issue 52 + @test @inferred(promote(1.0m, 1)) == (1.0m, 1.0) # issue 52 @test @inferred(promote(π, 180°)) === (float(π), float(π)) # issue 168 @test @inferred(promote(180°, π)) === (float(π), float(π)) # issue 168 @@ -461,7 +476,22 @@ Unitfu.uconvert(U::Unitfu.Units, q::QQQ) = uconvert(U, Quantity(q.val, cm)) @test TT3 <: Quantity{Int64} @test TTT1 <: Quantity{Float64} end + @testset "> Promotion for nested, real-valued structures" begin + T1 = typeof(1.0kg) + T2 = typeof(1cm) + T3 = typeof(1kg) + T4 = typeof(1) + TT1 = promote_rule(T1, T2) + TT3 = promote_rule(T3, T4) + TTT1 = promote_rule(TT1, TT3) + @test TT1 <: Quantity{Float64} + @test TT3 <: Quantity{Int64} + @test TTT1 <: Quantity{Float64} + end end + + + @testset "Unit string parsing" begin @test uparse("m") == m @test uparse("m,s") == (m,s) @@ -619,6 +649,12 @@ end @test @inferred((3m)*m) === 3*(m*m) # Associative multiplication @test @inferred(true*1kg) === 1kg # Boolean multiplication (T) @test @inferred(false*1kg) === 0kg # Boolean multiplication (F) + @test @inferred(true*(1+im)kg) === (1+im)kg # Boolean-complex multiplication (T) + @test @inferred(false*(1+im)kg) === (0+0im)kg # Boolean-complex multiplication (F) + @test @inferred((1+im)kg*true) === (1+im)kg # Complex-boolean multiplication (T) + @test @inferred((1+im)kg*false) === (0+0im)kg # Complex-boolean multiplication (F) + @test @inferred((NaN*kg)*false) === 0.0kg # `false` acts as "strong zero" + @test @inferred(false*(-Inf*kg)) === -0.0kg # `false` acts as "strong zero" @test typeof(one(eltype([1.0s, 1kg]))) <: Float64 # issue 159, multiplicative identity end @testset "> Division" begin @@ -646,6 +682,9 @@ end @test mod(1hr+3minute+5s, 24s) == 17s @test mod2pi(360°) === 0° # 2pi is 360° @test mod2pi(0.5pi*u"m/dm") ≈ pi # just testing the dimensionless fallback + @test modf(2.5rad) === (0.5, 2.0) + @test modf(-250cm/m) === (-1//2, -2//1) + @test_throws MethodError modf(1m) @test @inferred(inv(s)) === s^-1 @test inv(ContextUnits(m,km)) === ContextUnits(m^-1,km^-1) @test inv(FixedUnits(m)) === FixedUnits(m^-1) @@ -665,7 +704,7 @@ end @test (8m)^(1//3) === 2.0*m^(1//3) @test @inferred(cis(90°)) ≈ im - # Test inferrability of integer literal powers + # Test inferrability of literal powers _pow_m3(x) = x^-3 _pow_0(x) = x^0 _pow_3(x) = x^3 @@ -716,7 +755,7 @@ end @test @inferred(sinh(0.0rad)) == 0.0 @test @inferred(sinh(1J/N/m) + cosh(1rad)) ≈ MathConstants.e - @test @inferred(tanh(1m/1µm)) == 1 + @test @inferred(tanh(1m/1μm)) == 1 @test @inferred(csch(0.0°)) == Inf @test @inferred(sech(0K/Ra)) == 1 @test @inferred(coth(1e3m*mm^-1)) == 1 @@ -731,6 +770,12 @@ end @test @inferred(cospi(1rad)) == -1 @test @inferred(sinc(1rad)) === 0 @test @inferred(cosc(1ft/3inch)) === 0.25 + if isdefined(Base, :cispi) + @test @inferred(cispi(rad/2)) === complex(0.0, 1.0) + end + if isdefined(Base, :sincospi) + @test @inferred(sincospi(rad/2)) === (1.0, 0.0) + end @test @inferred(atan(m*sqrt(3),1m)) ≈ 60° @test @inferred(atan(m*sqrt(3),1.0m)) ≈ 60° @@ -781,6 +826,11 @@ end @test isapprox(1.0u"m", 1.1u"m"; rtol=0.2) @test !isapprox(1.0u"m", 1.1u"m"; rtol=0.05) + # Issue 465: + z = fill((1+im)m, 2, 3) + @test !isapprox(z, 2z) + @test isapprox(z, z * (1 + 1e-15)) + # Test eps @test eps(1.0u"s") == eps(1.0)u"s" @test eps(typeof(1.0u"s")) == eps(Float64) @@ -834,6 +884,7 @@ end @test_throws DimensionError fma(2, 1m, 1V) @test muladd(1s, 1.0mol/s, 2.0mol) === 3.0mol # issue 138 end + @testset "> @fastmath" begin one32 = one(Float32)*m eps32 = eps(Float32)*m @@ -1163,6 +1214,7 @@ end @test (range(0, stop=2, length=5) * u"°")[2:end] == range(0.5, stop=2, length=4) * u"°" # issue 241 @test range(big(1.0)m, step=big(1.0)m, length=5) == (big(1.0):big(1.0):big(5.0))*m + @test range(big(1.0)m, step=big(1.0)m, length=5) == (big(1.0):big(1.0):big(5.0))*m end @testset ">> LinSpace" begin # Not using Compat.range for these because kw args don't infer in julia 0.6.2 @@ -1219,7 +1271,92 @@ end @test @inferred(LinRange(0f0, 1f0, 3) .* 3f0m) === LinRange(0f0m, 3f0m, 3) @test @inferred(3f0m .* LinRange(0.0, 1.0, 3)) === LinRange(0.0m, 3.0m, 3) @test @inferred(1.0s .* range(0.1, step=0.1, length=3)) === @inferred(range(0.1, step=0.1, length=3) * 1.0s) - + @test @inferred((1:2:5) .* cm .|> mm) === 10mm:20mm:50mm + @test mm.((1:2:5) .* cm) === 10mm:20mm:50mm + @test @inferred((1:2:5) .* km .|> upreferred) === 1000m:2000m:5000m + @test @inferred((1:2:5) .* cm .|> mm .|> ustrip) === 10:20:50 + @test @inferred((1f0:2f0:5f0) .* cm .|> mm .|> ustrip) === 10f0:20f0:50f0 + end + @testset ">> quantities and non-quantities" begin + @test range(1, step=1m/mm, length=5) == 1:1000:4001 + @test range(1, step=1mm/m, length=5) == (1//1):(1//1000):(251//250) + @test eltype(range(1, step=1m/mm, length=5)) == Int + @test eltype(range(1, step=1mm/m, length=5)) == Rational{Int} + @test range(1m/mm, step=1, length=5) == ((1//1):(1//1000):(251//250)) * m/mm + @test range(1mm/m, step=1, length=5) == (1:1000:4001) * mm/m + @test eltype(range(1m/mm, step=1, length=5)) == typeof((1//1)m/mm) + @test eltype(range(1mm/m, step=1, length=5)) == typeof(1mm/m) + end + @testset ">> complex" begin + @test range((1+2im)m, step=(1+2im)m, length=5) == range(1+2im, step=1+2im, length=5) * m + @test range((1+2im)m, step=(1+2im)mm, length=5) == range(1//1+(2//1)im, step=1//1000+(1//500)im, length=5) * m + @test range((1.0+2.0im)m, stop=(3.0+4.0im)m, length=5) == LinRange(1.0+2.0im, 3.0+4.0im, 5) * m + @test range((1.0+2.0im)mm, stop=(3.0+4.0im)m, length=3) == LinRange(0.001+0.002im, 3.0+4.0im, 3) * m + end + @testset ">> step defaults to 1" begin + @test range(1.0mm/m, length=5) == (1.0mm/m):(1000.0mm/m):(4001.0mm/m) + @test range((1+2im)mm/m, length=5) == range(1+2im, step=1000, length=5)*mm/m + @test_throws DimensionError range(1.0m, length=5) + @test_throws DimensionError range((1+2im)m, length=5) + @test (1mm/m):(5001mm/m) == (1:1000:5001) * mm/m + @test (1m/mm):(5m/mm) == (1//1:1//1000:5//1) * m/mm + @test (1mm/m):(1m/mm) == 1//1000:999001//1000 + @test (1m/mm):(1mm/m) == 1000//1:999//1 + @test (1.0mm/m):(5001mm/m) == (1.0:1000.0:5001.0) * mm/m + @test (1m/mm):(5.0m/mm) == (1.0:0.001:5.0) * m/mm + @test (1.0mm/m):(1m/mm) == 0.001:999.001 + @test (1m/mm):(1.0mm/m) == 1000.0:1.0:999.0 + @test_throws DimensionError (1m):(1m) + @test_throws DimensionError (1m):(1000cm) + @test_throws DimensionError (1m):(1s) + @test (1m/cm):1 == 100:99 + @test (1m/cm):1000 == 100:1000 + @test (1m/cm):1.0 == 100.0:99.0 + @test (1.0m/cm):1000 == 100.0:1000.0 + @test_throws DimensionError (1m):1 + @test 1:(1m/mm) == 1:1000 + @test 1000:(1m/mm) == 1000:1000 + @test 1.0:(1m/mm) == 1.0:1000.0 + @test 1000:(1.0m/mm) == 1000.0:1000.0 + @test_throws DimensionError 1:(1m) + end + @static if VERSION ≥ v"1.7" + @testset ">> no start argument" begin + @test range(stop=1.0m, step=2.0m, length=5) == -7.0m:2.0m:1.0m + @test range(stop=1.0mm, step=1.0m, length=5) == -3999.0mm:1000.0mm:1.0mm + @test range(stop=(1.0+2.0im)mm, step=(1.0+1.0im)m, length=5) == range(stop=1.0+2.0im, step=(1000+1000im), length=5)*mm + @test range(stop=1.0mm/m, length=5) == (-3999.0mm/m):(1000.0mm/m):(1.0mm/m) + @test range(stop=(1+2im)mm/m, length=5) == range(stop=1+2im, step=1000, length=5)*mm/m + @test range(stop=1.0mm/m, step=1, length=5) == (-3999.0mm/m):(1000.0mm/m):(1.0mm/m) + @test_throws DimensionError range(stop=1.0m, step=1V, length=5) + @test_throws DimensionError range(stop=(1+2im)m, step=1V, length=5) + @test_throws DimensionError range(stop=1.0m, length=5) + @test_throws DimensionError range(stop=(1+2im)m, length=5) + @test range(stop=1, step=1m/mm, length=5) == -3999:1000:1 + @test range(stop=1, step=1mm/m, length=5) == (249//250):(1//1000):(1//1) + @test eltype(range(stop=1, step=1m/mm, length=5)) == Int + @test eltype(range(stop=1, step=1mm/m, length=5)) == Rational{Int} + @test range(stop=1m/mm, step=1, length=5) == ((249//250):(1//1000):(1//1)) * m/mm + @test range(stop=1mm/m, step=1, length=5) == (-3999:1000:1) * mm/m + @test eltype(range(stop=1m/mm, step=1, length=5)) == typeof((1//1)m/mm) + @test eltype(range(stop=1mm/m, step=1, length=5)) == typeof(1mm/m) + @test_throws ArgumentError range(step=1m, length=5) + end + end + @testset ">> broadcasting" begin + @test @inferred((1:5) .* mm) === 1mm:1mm:5mm + @test @inferred(mm .* (1:5)) === 1mm:1mm:5mm + @test @inferred((1:2:5) .* mm) === 1mm:2mm:5mm + @test @inferred((1.0:2.0:5.01) .* mm) === 1.0mm:2.0mm:5.0mm + r = @inferred(range(0.1, step=0.1, length=3) .* 1.0s) + @test r[3] === 0.3s + @test @inferred((0:2) .* 3f0m) === StepRangeLen{typeof(0f0m)}(0.0m, 3.0m, 3) # issue #477 + @test @inferred(3f0m .* (0:2)) === StepRangeLen{typeof(0f0m)}(0.0m, 3.0m, 3) # issue #477 + @test @inferred((0f0:2f0) .* 3f0m) === 0f0m:3f0m:6f0m + @test @inferred(3f0m .* (0.0:2.0)) === 0.0m:3.0m:6.0m + @test @inferred(LinRange(0f0, 1f0, 3) .* 3f0m) === LinRange(0f0m, 3f0m, 3) + @test @inferred(3f0m .* LinRange(0.0, 1.0, 3)) === LinRange(0.0m, 3.0m, 3) + @test @inferred(1.0s .* range(0.1, step=0.1, length=3)) === @inferred(range(0.1, step=0.1, length=3) * 1.0s) @test @inferred((1:2:5) .* cm .|> mm) === 10mm:20mm:50mm @test mm.((1:2:5) .* cm) === 10mm:20mm:50mm @test @inferred((1:2:5) .* km .|> upreferred) === 1000m:2000m:5000m @@ -1339,7 +1476,6 @@ end @test @inferred([1V, 2V] .* [true, false]) == [1V, 0V] @test @inferred([1.0m, 2.0m] ./ 3) == [1m/3, 2m/3] @test @inferred([1V, 2.0V] ./ [3m, 4m]) == [1V/(3m), 0.5V/m] - @test @inferred([1, 2]kg) == [1, 2] * kg @test @inferred([1, 2]kg .* [2, 3]kg^-1) == [2, 6] @test @inferred([1, 2]/kg) == [1/kg, 2/kg] @@ -1400,12 +1536,14 @@ end @test typeof(ustrip(SymTridiagonal([1,2,3]u"m", [4,5]u"m"))) <: SymTridiagonal{Int} end + @testset ">> Linear algebra" begin @test istril([1 1; 0 1]u"m") == false @test istriu([1 1; 0 1]u"m") == true end @testset ">> Array initialization" begin + Q = typeof(1u"m") Q = typeof(1u"m") @test @inferred(zeros(Q, 2)) == [0, 0]u"m" @test @inferred(zeros(Q, (2,))) == [0, 0]u"m" @@ -1418,12 +1556,37 @@ end @test size(rand(Q, 2)) == (2,) @test size(rand(Q, 2, 3)) == (2,3) @test eltype(@inferred(rand(Q, 2))) == Q - @test @inferred(zero([1.0u"m", 2.0u"m"])) == [0.0u"m", 0.0u"m"] - @test @inferred(zero([1.0u"m", 1.0u"s"])) == [0.0, 0.0] - @test @inferred(zero([1u"m", 1u"s"])) == [0, 0] - @test zero(Quantity{Int,ᴸ}[1u"m", 1u"mm"]) == [0, 0]u"m" - r0 = [1.0km, 2km, 3m/s, 4m/s] - @test @inferred(zero(r0)) == [0.0, 0.0, 0.0, 0.0] + @test zero([1m, 2m]) == [0m, 0m] + @test zero(Quantity{Int,ᴸ}[1m, 1mm]) == [0m, 0mm] + @test zero(Quantity{Int}[1m, 1s]) == [0m, 0s] + @test zero(Quantity[1m, 1s]) == [0m, 0s] + @test zero([1mm, missing]) == [0mm, 0mm] + @test zero(Union{typeof(0.0s),Missing}[missing]) == [0.0s] + @test_broken zero(Union{Quantity{Int,ᴸ},Missing}[1mm, missing]) == [0mm, 0m] + @test_broken zero(Union{Quantity{Float64,ᴸ},Missing}[1.0mm, missing]) == [0.0mm, 0.0m] + @test_broken zero(Union{Quantity,Missing}[1m, 1mm]) == [0m, 0mm] + @test zero([1°C, 2°C]) == [0K, 0K] + @test zero(Quantity[1°C, 2°F]) == [0K, 0K] + @test zero(Union{typeof(0°C),Missing}[missing]) == [0K] + @test_broken zero(Union{Quantity{Int,ᶿ},Missing}[1°C, 2°F, missing]) == [0K, 0K, 0K] + @test zero(Vector{typeof(big(1)mm)}(undef, 1)) == [big(0)mm] + @test zero(Vector{Union{typeof(big(1)mm),Missing}}(undef, 1)) == [big(0)mm] + @test zero(Vector{Quantity{Float64,ᴸ}}(undef, 1)) == [0.0m] + @test_broken zero(Vector{Union{Quantity{Float64,ᴸ},Missing}}(undef, 1)) == [0.0m] + @test_throws MethodError zero(Union{Quantity,Missing}[1m, 1s, missing]) + @test_throws MethodError zero(Vector{Quantity}(undef, 1)) + @test_throws MethodError zero(Vector{Union{Quantity,Missing}}(undef, 1)) + end + @testset ">> Tuple addition" begin + # Dimensionless quantities + @test @inferred((1mm/m) .+ (1.0cm/m)) == (0.011) + @test typeof((1mm/m,) .+ (1.0cm/m,)) == Tuple{Float64} + @test @inferred((1mm/m,) .+ (1cm/m,)) == (11//1000,) + @test typeof((1mm/m,) .+ (1cm/m,)) == Tuple{Rational{Int}} + @test @inferred((1mm/m,) .+ (2,)) == (2001//1000,) + @test typeof((1mm/m,) .+ (2,)) == Tuple{Rational{Int}} + @test_throws DimensionError (1m,) .+ (2V,) + @test_throws DimensionError (1,) .+ (1m,) end end @testset "> Tuples and NTuples" begin @@ -1469,8 +1632,10 @@ end end end end -if VERSION >= VersionNumber("1.5.0-rc1") - @testset "Display" begin + + +@static if VERSION >= VersionNumber("1.5.0-rc1") + @testset "Display no fancy exponents" begin withenv("UNITFUL_FANCY_EXPONENTS" => false) do @test string(typeof(1.0m/s)) == "Quantity{Float64, ᴸ∙ ᵀ^-1, FreeUnits{(m, s^-1), ᴸ∙ ᵀ^-1, nothing}}" || @@ -1490,11 +1655,12 @@ if VERSION >= VersionNumber("1.5.0-rc1") end end + struct Foo <: Number end Base.show(io::IO, x::Foo) = print(io, "1") Base.show(io::IO, ::MIME"text/plain", ::Foo) = print(io, "42.0") -if VERSION >= VersionNumber("1.6.0-rc1") +@static if VERSION >= VersionNumber("1.6.0-rc1") @testset "Show quantities" begin withenv("UNITFUL_FANCY_EXPONENTS" => false) do @test repr(1.0 * u"m * s * kg^-1") == "1.0m∙s∙kg^-1" @@ -1532,7 +1698,7 @@ if VERSION >= VersionNumber("1.6.0-rc1") end end -if VERSION >= VersionNumber("1.5.0-rc1") +@static if VERSION >= VersionNumber("1.5.0-rc1") @testset "Show quantities in collection" begin shortp(x) = repr(x, context = :color=>true) longp(x) = repr(:"text/plain", x, context = :color=>true) @@ -1610,6 +1776,7 @@ if VERSION >= VersionNumber("1.5.0-rc1") end end + @testset "DimensionError message" begin function errorstr(e) b = IOBuffer() @@ -1960,6 +2127,7 @@ end end end + @testset "Output ordered by unit exponent" begin ordered = Unitfu.sortexp(typeof(u"J*mol^-1*K^-1").parameters[1]) @test typeof(ordered[1]) <: Unitfu.Unit{:Joule,<:Any} @@ -1972,6 +2140,8 @@ end @test typeof(ordered[3]) <: Unitfu.Unit{:Kelvin,<:Any} end + + # Test that the @u_str macro will find units in other modules. module ShadowUnits using Unitfu