Skip to main content

Elegant engineering math substrate for executable MBSE.

Project description

UnitFlow
UnitFlow

Python 3.13+ Ruff mypy License: Apache 2.0 Tests: 171 passed Coverage: 85%

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 a SerializationError.

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

unitflow-0.1.0.tar.gz (1.9 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

unitflow-0.1.0-py3-none-any.whl (35.9 kB view details)

Uploaded Python 3

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

Hashes for unitflow-0.1.0.tar.gz
Algorithm Hash digest
SHA256 3de9332b9e675899f3fc58ea85782b3ad01219a74feca1965061ddde2dc60ce1
MD5 37a9b883f2816177731ba59fc54f7aa4
BLAKE2b-256 5c8f38bff47fe7e1ff413f0a36d9ebc9ae485cae8504c822893f0d435e68a712

See more details on using hashes here.

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

Hashes for unitflow-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 45c0eb14f8cbc54de4cba14b694aedd722c73bf851665b7a2fdad5442e0d65f0
MD5 f97b0ba6cf71f5e3b8a4d4248cc3dabd
BLAKE2b-256 0c4cc6f67a0f1271ba13cd0f51e70b636124b1d1d5246d99a655501ab65508a0

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page