https://github.com/bgreni/kelvin
A powerful, type-safe dimensional analysis library in Mojo
https://github.com/bgreni/kelvin
Last synced: 5 months ago
JSON representation
A powerful, type-safe dimensional analysis library in Mojo
- Host: GitHub
- URL: https://github.com/bgreni/kelvin
- Owner: bgreni
- License: apache-2.0
- Created: 2025-04-02T03:59:53.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2025-07-04T03:00:34.000Z (about 1 year ago)
- Last Synced: 2025-07-04T04:18:26.558Z (about 1 year ago)
- Language: Mojo
- Homepage:
- Size: 291 KB
- Stars: 8
- Watchers: 2
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
- Codeowners: CODEOWNERS
Awesome Lists containing this project
README
# Kelvin
[](https://opensource.org/licenses/Apache-2.0)


A powerful dimensional analysis library written in Mojo for all your scientific computing needs.
Heavily inspired by [uom](https://docs.rs/uom/latest/uom/index.html).
## Defining a Quantity
To define a specific unit, you simply need to create an comptime to `Quantity` with
the particular dimensions defined. Each dimension also requires a `Ratio` and a
suffix string. The ratio defines the relationship between it and the unitary amount.
All dimensions that do not exist for a given quantity are given `Dimension.Invalid`.
```mojo
# Base unit for time, `Second`
comptime Meter = Quantity[
Dimensions[
Dimension[1, Ratio.Unitary, "m"](), # Length
Dimension.Invalid, # Mass
Dimension.Invalid, # Time
Dimension.Invalid, # Electric current
Dimension.Invalid, # Temperature
Dimension.Invalid, # Substance Amount
Dimension.Invalid, # Luminosity
Angle.Invalid, # Angle
](),
...,
]
# Use Ratio.Kilo to create a `Kilometer`
comptime Kilometer = Quantity[
Dimensions[
Dimension[1, Ratio.Kilo, "km"](),
Dimension.Invalid,
Dimension.Invalid,
Dimension.Invalid,
Dimension.Invalid,
Dimension.Invalid,
Dimension.Invalid,
Angle.Invalid,
](),
...,
]
# Quantity with a weird conversion ratio
comptime Mile = Quantity[
Dimensions[
Dimension[1, Ratio[1609344, 1000]().simplify(), "mile"](),
Dimension.Invalid,
Dimension.Invalid,
Dimension.Invalid,
Dimension.Invalid,
Dimension.Invalid,
Dimension.Invalid,
Angle.Invalid,
](),
...,
]
```
With these simple units defined, we can use arithmetic syntax on the `Dimensions`
struct to create derivative units very easily. Here we can define Velocity in
a single line of code.
```mojo
comptime MetersPerSecond = Quantity[Meter.D / Second.D, ...,]
comptime MetersSquared = Quantity[Meter.D**2, ...,]
comptime kg = Kilogram.D
comptime m = Meter.D
comptime s = Second.D
comptime Newton = Quantity[kg * m * (s ** -2)]
```
`Quantity` uses the mojo builtin `SIMD` type, so users can also supply a vector width
parameter after the DType.
```mojo
comptime SIMDSeconds = Second[_, 4]
comptime SIMDMeter = Meter[_, 4]
print(SIMDMeter(40) / SIMDSeconds(10)) # [4.0, 4.0, 4.0, 4.0] m^1 s^-1
```
## Numerical Type
Quantity accepts a `DType` parameter to define the numerical type and bitwidth
of the underlying value. The default is `DType.float64`.
```mojo
var s = Second(10.0) # Will be a float64
comptime IntSecond = Second[DType.int64]
var is = IntSecond(10) # will be int64
```
## Quantity Arithmetic
Since the dimensions of a quantity are a dynamic part of the type system,
we can also do type-safe quantity multiplication and division. Though they
must have matching scale on all valid dimensions
```mojo
var velocity: MetersPerSecond = Meter(10) / Second(2)
var area: MetersSquared = Meter(10) * Meter(20)
var a = Meter(10) * Mile(20) # Nope
# Power must be an IntLiteral so it's compile time known
area = Meter(10) ** 2
```
Addition and subtraction are only defined on matching quantities.
```mojo
var a = Meter(10) + Meter(10)
var b = Meter(10) + Mile(20) # Nope
var c = Meter(10) + Second(10) # Nope
```
You can also use scalar values for multiplication and division.
```mojo
var m = Meter(10)
m = m * 5
m /= 5
```
## Quantity Conversions
Quantities with matching dimensions, but different scale can be casted to one
another.
```mojo
var m = Minute(cast_from=Second(600))
var s = Second(cast_from=m)
```
## Sharp Edges
### Dimensions operations are unchecked
Dimensional type safety is only implemented at the `Quantity` level, due to
the usage of `@always_inline('builtin')` throughout `Dimensions, Dimension, Ratio and Angle`
to keep compile times fast, and there is currently no way of adding additional contraints
to methods that use that decorator. The result is expressions like these are erroneously allowed.
```mojo
comptime BadUnit = Meter.D * Mile.D # Bad
var a = Meter(10) * Mile(10) # Reject properly due to using Quantity
```
You are still protected from surprising behavior when doing actual calculations, but
care must be taken when defining new units.
### Integer Rounding
When using integer value representations, operations are subject to integer rounding
rules. If precision is important, please use a float point representation
```mojo
comptime IntSeconds = Second[DType.int64]
var a = IntSeconds(11) / IntSeconds(3) # returns IntSeconds(3)
```