Elegant engineering math substrate for executable MBSE.
Project description
UnitFlow
Elegant engineering math for Python.
Unit algebra. Dimensional reasoning. Symbolic constraints. Array workflows.
Built for engineers who refuse to compromise on correctness or beauty.
Quick Start · Why UnitFlow · Features · Examples · Installation · Contributing
Why UnitFlow
Most unit libraries let you convert meters to feet. UnitFlow lets you think in physics.
from unitflow import kg, m, s, N, symbol, Equation
# This just works.
force = 2 * kg * 9.81 * (m / s**2)
print(force.to(N)) # 19.62 N
# So does this.
F = symbol("F", unit=N)
mass = symbol("m", unit=kg)
a = symbol("a", unit=m / s**2)
newtons_law = F == mass * a # Returns an Equation, not a bool.
UnitFlow was designed from the ground up as an engineering math substrate -- not a conversion utility, not a wrapper around strings, and not a half-measure bolted onto NumPy after the fact.
It is the computational foundation of ThunderGraph, an AI-powered model-based systems engineering platform. Every design decision optimizes for the same thing: making engineering math feel like engineering math.
Quick Start
from unitflow import m, cm, kg, s, N, Pa, kW, MW, rpm, rad, si
# Intuitive arithmetic
speed = 10 * m / s
area = 3 * m * 50 * cm
print(area.to(m**2)) # 1.5 m^2
# Exact conversions across orders of magnitude
power = 5000 * si.W
print(power.to(kW)) # 5 kW
print(power.to(MW)) # 0.005 MW
# Angular speed with exact pi-bearing scale factors
angular = (3000 * rpm).to(rad / s)
print(angular) # 314.159... rad/s
# Semantic equality: same physics, same object
assert 1 * m == 100 * cm
assert hash(1 * m) == hash(100 * cm)
Features
Clean Unit Algebra
Units are immutable, composable, and algebraically correct. Multiply them, divide them, raise them to powers. The dimension tracking is automatic and exact.
velocity = m / s
acceleration = m / s**2
force_unit = kg * m / s**2
assert force_unit == N # Semantic equivalence
Exact Scale Representation
Unit definitions use exact rational arithmetic with explicit pi support. No floating-point drift in your conversion factors.
from unitflow import rpm, rad, s
# rpm -> rad/s uses exact (1/30) * pi, not an approximation
speed = 3000 * rpm
angular = speed.to(rad / s) # Exact pi-bearing conversion
SI Prefix System
Every standard SI prefix from pico to tera, generated programmatically from exact rational factors. No hand-coded boilerplate.
from unitflow import kW, MW, GW, kPa, MPa, GPa, kHz, GHz, nm, us
plant = 1200 * MW
print(plant.to(GW)) # 1.2 GW
print(plant.to(kW)) # 1200000 kW
wavelength = 550 * nm
cpu_clock = 3500 * MHz
response = 250 * us
Torque vs Energy Disambiguation
Same physical dimension. Different engineering meaning. UnitFlow knows the difference.
from unitflow import N, m, J, Quantity
torque = Quantity(12, (N * m).with_metadata(quantity_kind="torque"))
energy = Quantity(12, (N * m).with_metadata(quantity_kind="energy"))
print(torque) # 12 N*m
print(energy) # 12 J
Symbolic Constraints for MBSE
Define engineering equations and constraints as first-class Python objects. Build executable system models, not dead documentation.
from unitflow import symbol, N, kg, m, s, W, rpm, rad
# Define model variables
F = symbol("F", unit=N)
mass = symbol("m", unit=kg)
a = symbol("a", unit=m / s**2)
# Newton's second law as a constraint
newtons_law = F == mass * a
# Bounded variables
x = symbol("x", unit=m)
bounds = (0 * m <= x) & (x <= 10 * m)
# Negation
safe = ~(x > 100 * m)
# Full model example
shaft_speed = symbol("shaft_speed", unit=rpm)
shaft_torque = symbol("shaft_torque", unit=N * m, quantity_kind="torque")
shaft_power = symbol("shaft_power", unit=W)
power_eq = shaft_power == shaft_torque * shaft_speed.to(rad / s)
speed_bounds = (0 * rpm <= shaft_speed) & (shaft_speed <= 6000 * rpm)
NumPy Array Workflows
Array-backed quantities follow the same semantic rules as scalars. No separate "array mode." No monkey-patching.
import numpy as np
from unitflow import Quantity, kg, m, s, rad
masses = Quantity(np.array([1.0, 2.0, 5.0, 10.0]), kg)
accels = Quantity(np.array([9.81, 9.81, 9.81, 9.81]), m / s**2)
forces = masses * accels
print(np.sum(forces)) # 176.58 kg*m/s^2
print(np.mean(forces)) # 44.145 kg*m/s^2
# Trig functions enforce dimensionless inputs
angles = Quantity(np.array([0, np.pi/2, np.pi]), rad)
print(np.sin(angles)) # [0, 1, 0] -- dimensionless result
User-Defined Units and Extensibility
Define your own units and domain packs without touching library internals.
from unitflow import define_unit, UnitNamespace, Quantity, m, s, generate_prefixes
from fractions import Fraction
# Define a custom unit
ft = define_unit(name="foot", symbol="ft", expr=Quantity(Fraction(3048, 10000), m))
print((6 * ft).to(m)) # 1.8288 m
# Create a domain namespace
aero = UnitNamespace("aero")
knot = aero.define_unit(name="knot", symbol="kn", expr=Quantity(Fraction(1852, 3600), m / s))
# Generate prefixed variants automatically
generate_prefixes(aero, knot, include={"milli", "kilo"})
JSON-Safe Serialization
Quantities, expressions, and constraint trees serialize to plain dicts. No pickle. No magic. Ready for distributed workflows.
Note: Serialization currently only supports scalar magnitudes (
int,float,Fraction). Array-backed quantities from the NumPy backend are not currently serializable and will raise aSerializationError.
import json
from unitflow import serialize_quantity, deserialize_quantity, rpm
q = 3000 * rpm
data = serialize_quantity(q)
print(json.dumps(data, indent=2))
restored = deserialize_quantity(json.loads(json.dumps(data)))
assert restored == q
Explicit Error Handling
Dimension mismatches fail loudly. Incompatible conversions fail loudly. Constraints refuse to be booleans. No silent corruption.
from unitflow import m, s, DimensionMismatchError, IncompatibleUnitError
try:
_ = (3 * m) + (2 * s)
except DimensionMismatchError:
print("Can't add meters and seconds.")
try:
(3 * m).to(s)
except IncompatibleUnitError:
print("Can't convert meters to seconds.")
Installation
pip install unitflow
For NumPy support:
pip install unitflow[numpy]
Note: The core library has zero dependencies. NumPy is optional and only needed for array-backed workflows.
Architecture
UnitFlow is built in clean, composable layers:
| Layer | Purpose |
|---|---|
| Dimension Algebra | Immutable exponent vectors over SI base dimensions |
| Exact Scale | Rational coefficients with explicit pi powers |
| Unit Semantics | Immutable units with algebraic composition |
| Quantity Core | Concrete arithmetic, conversion, semantic equality |
| Display Resolution | Engineering-friendly formatting with ambiguity handling |
| Definition System | Keyword-first define_unit(), namespaces, prefix generation |
| Catalogs | Curated SI and mechanical unit packs |
| Expression Layer | Symbolic variables, expressions, and constraint trees |
| Serialization | Structural JSON-safe serialization for all objects |
| NumPy Backend | Optional array integration via __array_ufunc__ |
The semantic core imports cleanly without NumPy. The expression layer does not pollute quantity arithmetic. Display logic does not affect semantic truth. Every layer depends downward, never upward.
Designed For
- Mechanical engineers working with force, torque, pressure, and power
- Electrical engineers working with voltage, current, and frequency
- Aerospace engineers working with thrust, drag, and angular velocity
- Controls engineers working with transfer functions and system dynamics
- Simulation engineers coupling models across tools and domains
- AI/ML engineers building physics-informed models with correct units
- Anyone who has ever been burned by a silent unit mismatch
Part of ThunderGraph
UnitFlow is the foundational math layer for ThunderGraph, an AI-powered model-based systems engineering platform that replaces dead SysML notation with executable Python models.
If you are building digital twins, system simulations, or engineering optimization workflows, UnitFlow gives you the dimensional correctness and algebraic elegance that your models deserve.
Contributing
UnitFlow is open source and welcomes contributions. Please read CONTRIBUTING.md before submitting a pull request.
License
Apache 2.0
Stop converting. Start computing.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file unitflow-0.1.0.tar.gz.
File metadata
- Download URL: unitflow-0.1.0.tar.gz
- Upload date:
- Size: 1.9 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3de9332b9e675899f3fc58ea85782b3ad01219a74feca1965061ddde2dc60ce1
|
|
| MD5 |
37a9b883f2816177731ba59fc54f7aa4
|
|
| BLAKE2b-256 |
5c8f38bff47fe7e1ff413f0a36d9ebc9ae485cae8504c822893f0d435e68a712
|
File details
Details for the file unitflow-0.1.0-py3-none-any.whl.
File metadata
- Download URL: unitflow-0.1.0-py3-none-any.whl
- Upload date:
- Size: 35.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
45c0eb14f8cbc54de4cba14b694aedd722c73bf851665b7a2fdad5442e0d65f0
|
|
| MD5 |
f97b0ba6cf71f5e3b8a4d4248cc3dabd
|
|
| BLAKE2b-256 |
0c4cc6f67a0f1271ba13cd0f51e70b636124b1d1d5246d99a655501ab65508a0
|