Types
Quantities
FlexUnits.jl has two types of quantity values. FlexQuant, which can contain any object as a value, and Quantity which subtypes to Number (following the convention of most of Julia's unit packages). For convenience, they are often referred to together as QuantUnion. Mathematical operations on a QuantUnion with a Unit will convert to a dimensional quantity (SI by default) having an AbstractDimension instead of a AbstractUnit.
FlexUnits.Quantity — Type
struct Quantity{T<:Number, U<:AbstractUnitLike} <: Number
value :: T
unit :: U
endNumeric quantity type (that sutypes to number) with fields value and unit
FlexUnits.FlexQuant — Type
struct FlexQuant{T<:Any, U<:AbstractUnitLike}
value :: T
unit :: U
endGeneric quantity type (that can hold any value) with fields value and unit
FlexUnits.QuantUnion — Type
QuantUnion{T<:Any,U<:AbstractUnitLike}Convenience union that allows Number and Non-Number types to be considered together
Dimensions
Dimensions are the core object used to define units, and Quantity values tend to have some form of AbstractDimension in its unit field after calculations due to computational simplicity.
FlexUnits.AbstractDimensions — Type
AbstractDimensionsA class that represents a coherent units system (i.e. dimensions and a base unit for each dimension) By default, its children are Dimensions (SI units), and NoDims, but users can build their own versions (even based on imperial equivalents, or where angles are a dimension)
FlexUnits.Dimensions — Type
Dimensions{P}A dimensional structure with fields corresponding to fundamental SI units: m, kg, s, A, K, cd, mol
FlexUnits.StaticDims — Type
StaticDims{D} <: AbstractDimLikeStatic dimensions where the D` is the dimension value. This improves performance when dimensions are statically inferrable.
Units
Units contain an AbstractDimensions and an AbstractUnitTransform which represents a conversion formula to convert the Quantity to its pure dimensional form. Typically, the unit transform is called before a calculation to convert the quantity to dimensional form, and uconvert applies an invers of that transform to convert the result back to the desired unit. Because dimensions have no transforms, it is most efficient to perform calculations directly on dimensional quantities.
FlexUnits.AbstractUnits — Type
AbstractUnitsAll units in FlexDims contain two entities: (1) AbstractDimLike (anything that can be interpreted as a dimension) (2) AbstractTransform (contains the formula to convert unit value to dimensions)
FlexUnits.Units — Type
@kwdef struct Units{D<:AbstractDimLike, T<:AbstractUnitTransform} <: AbstractUnits{D, T}
dims :: D
tobase :: T
symbol :: Symbol = DEFAULT_USYMBOL
endA dynamic unit object that contains dimensions (dims) and its conversion formula to said dimensions (tobase). The conversion formula determines what kind of unit is referred to. An AffineTransform implies affine units, a NoTransform implies dimensions. Units with dynamic dimensions can generated through the @ud_str macro, while units with static dimensions can be generated throiugh the @u_str macro.
Constructors
Units{D}(units, tobase::T, symbol=DEFAULT_USYMBOL) where {D,T<:AbstractUnitTransform}
Units(dims::D, tobase::AbstractUnitTransform=NoTransform(), symbol=DEFAULT_USYMBOL) where D<:AbstractDimensions
Units(units::D, tobase::AbstractUnitTransform, symbol=DEFAULT_USYMBOL) where D<:AbstractUnitsjulia> 1*(5ud"°C") #Operations on units eagerly convert to dimensions
278.15 K
julia> 1*(5du"°C") |> ud"°C" #Converts operation results back to Celsius
5.0 °C
julia> (5ud"°C" + 2ud"°C") |> ud"°C" #Operation adds values in Kelvin, results converted back to Celsius
280.15 °C
julia> (ustrip(5ud"°C") + ustrip(2ud"°C"))*u"°C" #Strips, adds raw quantity values, converts raw number to Celsius
7 °CLogarithmic Quantities and Units
Logarithmic quantities have different algebraic rules around units. Multiplication raises units to a power, addition multiplies units, and new operators ⊕ and ⊖ verify units (much like addition and subtraction for linear quantities). The LogScale is a multi-purpose callable object that can create logarithmic units (when called on linear units), and can change the logarithmic scale of a LogQuant (when called on a LogQuant). For example dB(q::LogQuant) will convert q to decibels.
FlexUnits.LogQuant — Type
struct LogQuant{T<:Number, U<:AbstractUnitLike} <: Number
value :: T
unit :: U
endNumeric quantity type (that subtypes to number) representing the lograrithm of a Quantity. This changes arithmeic operations (+-*/) according to logarithmic algebra.
FlexUnits.LogScale — Type
struct LogScale{T<:Real}
scale :: T
base :: T
symbol :: Symbol
endA callable object used to apply a scale to a logarithmic unit. For example,
dB = LogScale(scale=0.1, base=10, symbol=:dB)After defining it, this object can also be called on a LogQuant to change the logarithmic scale
julia> dB(log(10u"kPa"))
40.0 dB(kg/(m s²))It can also be used to create logarithmic units that you can convert to
julia> 10u"kPa" |> dB(u"Pa")
39.99999999999999 dB(Pa)Unit Transforms
One unique feature to the FlexUnits design is the AbstractUnitTransform object. This is a callable object that contains a conversion formula to convert the unit into its dimensional form. The default transforms are NoTransform (a property of all dimensions) and AffineTransform which can deal with all common linear units. This design could potentially support other transforms like LogTransform for logarithmic units like dB and pH in the future.
FlexUnits.AbstractUnitTransform — Type
AbstractUnitTransformAn abstract object representing a unit conversion formula. Any object that subtypes this is made callable.
# Callable form
utrans = uconvert(u"°C", u"°F")
utrans(0.0)
31.999999999999986
# Shorthand callable form (syntactic sugar)
(u"°C" |> u"°F")(0.0)
31.999999999999986FlexUnits.NoTransform — Type
NoTransform object, the default transform returned by tobase(x::AbstractDimensionLike). Calling it results in an identity.
t = NoTransform()
t("anything")
"anything"FlexUnits.AffineTransform — Type
@kwdef struct AffineTransform{T<:Real} <: AbstractUnitTransform
scale :: T = 1.0
offset :: T = 0.0
endA type representing an affine transfomration formula that can be used to convert values from one affine unit to another. This object is callable.
Constructors
AffineTransform(scale::Real, offset::Real)
AffineTransform(; scale, offset)QuantFieldArrray
Objects are a subtype of FieldArray, which can store multiple values with different static dimensions in a type-stable manner. This is done by returning only the magnitude when indexed by number, but the quantity (both magnitude and dimensions) when indexed by field name. This allows for easy cross-conversion between the agnostic SArray and QuantFieldArray types allowing for dimension-checking in dimension-aware engineering calculations, but forgoes this in linear algebra operations allowing for unit checking only when needed at zero cost. These types are not exported by default and need to be explicitly imported.
FlexUnits.QuantFieldArray — Type
abstract type QuantFieldArray{N,T,D} <: FieldArray{N,T,D} endInheriting from this object will make it easier to define your own rank-D tensor types with known units (only supports fundamental units).
@kwdef struct Stiffness{T<:Real} <: QuantFieldArray{Tuple{2,2,2}, T, 3}
xxx::Quantity{T, D"N/m"}
yxx::Quantity{T, D"N/m"}
xyx::Quantity{T, D"N/m"}
yyx::Quantity{T, D"N/m"}
xxy::Quantity{T, D"N/m"}
yxy::Quantity{T, D"N/m"}
xyy::Quantity{T, D"N/m"}
yyy::Quantity{T, D"N/m"}
endCalling getproperty(qa::QuantFieldArray, fn) will produce a Quantity,
stiffness = Stiffness(0.01u"lbf/inch" .* ones(2,2,2))
stiffness.xxx
1.7512677165354331 kg/s²while calling getindex(qa::QuantFieldArray, ind) will produce a pure numerical scalar in the coherent base units (fundamental SI units by default).
stiffness[1]
1.7512677165354331This means that linear algebra operations will only "see" a numerical array but engineering formulas that index by field will have (coherent) units attached to them ensuring unit correctness for such formulas. This results in near zero-overhead unit verification cost in applications like ODE solving
FlexUnits.QuantFieldMatrix — Type
abstract type QuantFieldMatrix{N1, N2, T} <: QuantFieldArray{Tuple{N1, N2}, T, 2} endInheriting from this object will make it easier to define your own static matrices with known units (only supports fundamental units).
@kwdef struct Stiffness{T<:Real} <: QuantFieldMatrix{2, 2, T}
xx::Quantity{T, D"N/m"}
yx::Quantity{T, D"N/m"}
xy::Quantity{T, D"N/m"}
yy::Quantity{T, D"N/m"}
endCalling getproperty(qa::QuantFieldMatrix, fn) will produce a Quantity,
stiffness = Stiffness{Float64}(0.01u"lbf/inch" .* ones(2,2))
stiffness.xx
1.7512677165354331 kg/s²while calling getindex(qa::QuantFieldMatrix, ind) will produce a pure numerical scalar in the coherent base units (fundamental SI units by default).
stiffness[1]
1.7512677165354331This means that linear algebra operations will only "see" a numerical matrix but engineering formulas that index by field will have (coherent) units attached to them ensuring unit correctness for such formulas. This results in near zero-overhead unit verification cost in applications like ODE solving
FlexUnits.QuantFieldVector — Type
abstract type QuantFieldVector{N, T} <: QuantFieldArray{Tuple{N}, T, 1} endInheriting from this object will make it easier to define your own static vectors with known units (only supports fundamental units).
@kwdef struct ThermoState{NT<:Real} <: QuantFieldVector{3, NT}
T::Quantity{NT, D"K"}
P::Quantity{NT, D"Pa"}
V::Quantity{NT, D"m^3/mol"}
endCalling getproperty(qa::QuantFieldMatrix, fn) will produce a Quantity,
state = ThermoState{Float64}(T=5u"°C", P=101.5u"kPa", V=1.8u"m^3/mol")
state.T
278.15 Kwhile calling getindex(qa::QuantFieldMatrix, ind) will produce a pure numerical scalar in the coherent base units (fundamental SI units by default).
state[1]
278.15This means that linear algebra operations will only "see" a numerical vector but engineering formulas that index by field will have (coherent) units attached to them ensuring unit correctness for such formulas. This results in near zero-overhead unit verification cost in applications like ODE solving
FlexUnits.DimsMod — Type
struct DimsMod{SD<:StaticDims, N, T, D, A<:QuantFieldArray} <: QuantFieldArray{N, T, D}
parent :: A
endA structure that modifies the static dimensiosn of a QuantFieldArray by a constant factor "D". Useful for operations that modify a QuantFieldArray with a constant dimension (for example, like taking time derivatives). The inner parent of the structure can be captured using ustrip(dm::DimsMod).
Linear Algebra
Linear algebra functionality is achieved by observing that any matrix of quantities that supports multiplication is a special kind of quantity matrix that functions as a linear mapping. Such matrices have a special structure for units. These special unit structures are unit mappings that contain an input vector of units, and an output vector of units. Accelerated linear algebra operations are achieved by keeping numerical matrices and unit mappings separate in a LinmapQuant and performing the linear algebra and unit inference separately (not unlike how a Quantity operates).
Unit Maps
FlexUnits.UnitMap — Type
struct UnitMap{UI, UO, TI<:ScalarOrVec{UI}, TO<:ScalarOrVec{UO}} <: AbstractUnitMap{UI,UO}
u_in :: TI
u_out :: TO
endUsed to represent a unit transformation from input units 'uin' to outpout units 'uout'. Often applied to nonlinear functions.
Constructors
UnitMap(u_in::ScalarOrVec{<:AbstractUnitLike}, u_out::ScalarOrVec{<:AbstractUnitLike}) where U<:AbstractUnitLikeFlexUnits.DimsMap — Type
struct struct DimsMap{D<:AbstractDimLike, TI<:AbstractDimVector, TO<:AbstractDimVector} <: AbstractDimsMap{D}
u_fac :: D
u_in :: TI
u_out :: TO
endUsed to represent a unit transformation from input dimensions 'uin' to outpout dimensions 'uout'. This is like a unit map but focuses on dimensions and has matrix-like behaviour since dimensions support linear algebra, but generic units may not (affine units, logarithmic units etc).
WARNING: The DimsMap constructor on dimensions expects uin and uout to be scaled so that the first element is dimensionless. To prevent excessive allocations, uin and uout may be mutated in-place. If mutating arguments is undesirable, supply immutable arguments or copies; otherwise, ensure that uin and uout have dimensionless values in their first element.
Constructors
DimsMap(u_fac::U, u_in::TI, u_out::TO) where {U<:AbstractUnitLike, TI<:AbstractVector{<:AbstractUnitLike}, TO<:AbstractVector{<:AbstractUnitLike}}
DimsMap(u_fac::Nothing, u_in::TI, u_out::TO) where {TI<:AbstractVector{<:AbstractUnitLike}, TO<:AbstractVector{<:AbstractUnitLike}}
DimsMap(md::AbstractMatrix{<:AbstractDimLike})
DimsMap(mq::AbstractMatrix{<:QuantUnion})
DimsMap(d::AbstractDimsMap)Objects with Unit Maps
FlexUnits.LinmapQuant — Type
struct LinmapQuant{T, D<:AbstractDimensions, M<:AbstractMatrix{T}, U<:UnitMaps{D}} <: AbstractMatrix{Quantity{T,D}}
values :: M
units :: U
endA linear mapping of quantities. A special kind of matrix that is intended to be used for multiplying vectors of quantities; such matrices must be dimensionally consistent and can be represented by a UnitMap. These constraints lead to much faster unit inference and a smaller memory footprint (O(M+N) instead of O(MNN2) in the case of multiplication).
Constructors
LinmapQuant(m::AbstractMatrix{T}, u::UnitMap) where T
LinmapQuant(m::SMatrix{Nr,Nc,T}, u::UnitMap) where {T, Nr, Nc}
LinmapQuant(m::QuantArrayVals, d::QuantArrayDims)
LinmapQuant(m::AbstractMatrix)
LinmapQuant(m::LinmapQuant)FlexUnits.FunctionQuant — Type
struct FunctionQuant{F, U<:AbstractUnitMap}
func :: F
units :: U
endA generic mapping with units. Useful for applying units to unitless functions that assume units for inputs/outputs.
FlexUnits.FactorQuant — Type
struct FactorQuant{T, D<:AbstractDimensions, F<:Factorization{T}, U<:AbstractUnitMap{D}}
factor :: F
dims :: U
endA factored linear mapping. A subclass of Factorizations with a unit mapping attached. Calling getproperty is re-routed to the original factor, with the appropriate units calcualted from the mapping.