Dimensional analysis arithmetic for SI quantities
Project description
bridgman
Dimensional analysis arithmetic for SI quantities. Named after Percy Bridgman.
Bridgman works with dimension dictionaries whose keys are SI base-dimension symbols and whose values are integer exponents:
force = {"M": 1, "L": 1, "T": -2}
Install
uv add bridgman
Install the symbolic API with SymPy support:
uv add "bridgman[sympy]"
Dict API
Dimensions:dict[str, int]type alias.mul_dims(d1, d2): multiply quantities by adding exponents.div_dims(d1, d2): divide quantities by subtracting exponents.pow_dims(d, n): raise dimensions to an integer power.nmust be anint;booland non-integer exponents raiseTypeError.dims_equal(d1, d2): compare after removing zero exponents.is_dimensionless(d): return true when all exponents are zero or absent.format_dims(d): produce display text such asM L T⁻², using Unicode superscripts. Dimensionless values render as1.dims_signature(d): produce a canonical, zero-stripped signature such asM:1,L:1,T:-2, with dimensionless values represented as1.parse_dims_signature(signature): parse a signature produced bydims_signature.canonicalize_dims(d): normalize dimension keys.Theta, uppercase theta, and lowercase theta all canonicalize toTheta.
Symbolic API
The symbolic API requires SymPy. Its canonical checking entry point is
verify_expr.
dims_of_expr(expr, dim_map): compute dimensions for a SymPy expression.verify_expr(eq, dim_map): verify a SymPy equality or inequality by comparing the dimensions of both sides.explain_expr(eq, dim_map): return a structured dimension-onlyCheckResultexposingok,lhs_dimensions,rhs_dimensions,reason, andsteps.DimensionalError: raised for proven dimensional inconsistency or unsupported symbolic constructs.SympyRequiredError: raised by symbolic APIs when SymPy is not installed.
Supported expression forms are symbols, numbers, multiplication, powers,
addition, Abs, Min, Max, and equality or inequality through verify_expr.
Addition, Min, and Max require every term to share dimensions. Abs
preserves the argument dimensions. Powers of dimensioned quantities require
exact integer or rational exponents; SymPy Float exponents are rejected
because they are not exact dimensional claims. Dimensionless bases may be raised
to arbitrary symbolic or floating exponents because the result remains
dimensionless.
The following functions require dimensionless arguments and return
dimensionless results: sin, cos, tan, exp, log, sinh, cosh,
and tanh. If any argument has dimensions, Bridgman raises
DimensionalError. atan2(y, x) is different: it requires y and x to have
equal dimensions, and returns a dimensionless result.
Unsupported SymPy nodes, including derivatives, integrals, piecewise
expressions, Kronecker deltas, and nested relational expressions, raise
DimensionalError rather than being silently accepted.
Buckingham Pi API
The Pi API works directly on dimension dictionaries and integer exponents. It checks and generates dimensionless monomial products without adding value-bearing quantities, unit conversion, code generation, or third-party dependencies.
from bridgman import count_pi_groups, is_dimensionless_product, pi_groups
rho = {"M": 1, "L": -3}
velocity = {"L": 1, "T": -1}
length = {"L": 1}
dynamic_viscosity = {"M": 1, "L": -1, "T": -1}
quantities = {
"rho": rho,
"v": velocity,
"L": length,
"mu": dynamic_viscosity,
}
assert count_pi_groups(quantities) == 1
assert is_dimensionless_product(
quantities,
{"rho": 1, "v": 1, "L": 1, "mu": -1},
)
assert pi_groups(quantities) == ({"rho": 1, "v": 1, "L": 1, "mu": -1},)
PiError: raised when product names or quantity labels are invalid.is_dimensionless_product(quantities, exponents): checks whether a user-authored integer power product is dimensionless.count_pi_groups(quantities): returns the Buckingham countn - rank(A).pi_groups(quantities): returns Bridgman's deterministic integer basis for dimensionless power products.
Generated bases are useful diagnostics, not semantic identity surfaces. Different valid bases can span the same dimensionless space, so downstream systems should store original quantities plus checked authored products when they need stable artifacts. Pi groups are dimension-only; they do not replace the kind layer and cannot distinguish dimensional twins such as energy and torque.
Kind API
Dimensions say whether an equation is dimensionally possible. They do not say
whether two dimensionally identical quantities mean the same thing. Energy and
torque both have dimensions {"M": 1, "L": 2, "T": -2}; pressure and energy
density also collide; angle and plain unitless values are both dimensionless.
The semantic kind layer keeps the dimension dictionaries as the arithmetic core and adds named quantity kinds above them:
import sympy as sp
from bridgman import KindRegistry, OperationRule, QuantityKind, verify_expr_kinds
E, F, d, tau = sp.symbols("E F d tau")
energy = {"M": 1, "L": 2, "T": -2}
force = {"M": 1, "L": 1, "T": -2}
length = {"L": 1}
registry = KindRegistry(
kinds=[
QuantityKind("Energy", energy),
QuantityKind("Torque", energy),
QuantityKind("Force", force),
QuantityKind("Length", length),
],
rules=[
OperationRule(
"Force",
"mul",
"Length",
"Energy",
commutative=True,
rationale="Work: W = Fd",
),
],
)
assert verify_expr_kinds(
sp.Eq(E, F * d),
registry=registry,
kind_map={"E": "Energy", "F": "Force", "d": "Length"},
)
assert not verify_expr_kinds(
sp.Eq(E, tau),
registry=registry,
kind_map={"E": "Energy", "tau": "Torque"},
)
QuantityKind(name, dimensions): declares a semantic kind. Names are arbitrary non-empty strings and do not need to be valid Python identifiers. Dimensions are canonicalized at construction.OperationRule(left_kind, op, right_kind, result_kind, commutative=False, rationale=None): declares a semantic"mul"or"div"rule. Setcommutative=Trueto register both argument orders for multiplication (division rules cannot be commutative).rationaleis an optional human string surfaced inCheckResult.steps.KindRegistry(kinds=[...], rules=[...]): validates kind definitions and operation rules. Operation rules are checked for dimensional consistency at registration; dimensionally invalid rules raiseInvalidOperationRuleError. Introspection methods:kind_dimensions(name),result_kind(left, op, right),operation_rule(left, op, right),kinds_with_dimensions(d),unique_kind_with_dimensions(d), andambiguous_kinds(d).kind_of_expr(expr, registry=..., kind_map=...): infers the semantic kind of a SymPy expression.verify_expr_kinds(eq, registry=..., kind_map=...): verifies both dimensions and semantic kind.explain_expr_kinds(eq, registry=..., kind_map=...): returns a structuredCheckResultwith kinds, dimensions, reason text, and operation steps.
Kind errors
All kind errors derive from KindError:
DuplicateKindError: same kind name registered twice.DuplicateOperationRuleError: same(left, op, right)registered twice (including the implicit reverse direction of a commutative rule).UnknownKindError: a referenced kind name is not in the registry, or no registered kind has the supplied dimensions.InvalidOperationRuleError: an operation rule uses an unsupportedopor is dimensionally inconsistent with its declared result.MissingOperationRuleError(subclass ofInvalidOperationRuleError): no rule exists for a requested(left, op, right)triple encountered during expression evaluation.AmbiguousKindError: dimensions match more than one registered kind when a unique kind is required.KindMismatchError: incompatible kinds combined in an operation that requires equal kinds (e.g. addition,Min,Max).
Kind-aware checking is stricter than dimension-only checking. Every kind-accepted equation should also be dimensionally accepted, but dimensionally accepted equations over semantic twins can still be rejected by kind-aware verification.
Example
import sympy as sp
from bridgman import verify_expr
F, m, a = sp.symbols("F m a")
dim_map = {
"F": {"M": 1, "L": 1, "T": -2},
"m": {"M": 1},
"a": {"L": 1, "T": -2},
}
assert verify_expr(sp.Eq(F, m * a), dim_map)
License
MIT
Project details
Release history Release notifications | RSS feed
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 bridgman-0.2.0.tar.gz.
File metadata
- Download URL: bridgman-0.2.0.tar.gz
- Upload date:
- Size: 11.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fc546cd6c690379e25efc59037dc7f8732a50fd1a997bfe97d8a2b7b56896963
|
|
| MD5 |
690bba37ae3d7dba0847224ed6ed7f14
|
|
| BLAKE2b-256 |
8ebcbcd2a4f8545a99f58d892dbcc358bf1f5650c63c6b4a14bc06db7502feae
|
File details
Details for the file bridgman-0.2.0-py3-none-any.whl.
File metadata
- Download URL: bridgman-0.2.0-py3-none-any.whl
- Upload date:
- Size: 14.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b64bbc2d50a36861e2417e475743119272dcbb9e0b8c4cbed0e81705fa5dba55
|
|
| MD5 |
caba70f2d4404825989a2c85155eac27
|
|
| BLAKE2b-256 |
121022cc0c916e56fe588a09efd76da0e6e94aac66e9fda4f32d14864ff1b2e9
|