Skip to main content

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. n must be an int; bool and non-integer exponents raise TypeError.
  • 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 as M L T⁻², using Unicode superscripts. Dimensionless values render as 1.
  • dims_signature(d): produce a canonical, zero-stripped signature such as M:1,L:1,T:-2, with dimensionless values represented as 1.
  • parse_dims_signature(signature): parse a signature produced by dims_signature.
  • canonicalize_dims(d): normalize dimension keys. Theta, uppercase theta, and lowercase theta all canonicalize to Theta.

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-only CheckResult exposing ok, lhs_dimensions, rhs_dimensions, reason, and steps.
  • 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 count n - 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. Set commutative=True to register both argument orders for multiplication (division rules cannot be commutative). rationale is an optional human string surfaced in CheckResult.steps.
  • KindRegistry(kinds=[...], rules=[...]): validates kind definitions and operation rules. Operation rules are checked for dimensional consistency at registration; dimensionally invalid rules raise InvalidOperationRuleError. Introspection methods: kind_dimensions(name), result_kind(left, op, right), operation_rule(left, op, right), kinds_with_dimensions(d), unique_kind_with_dimensions(d), and ambiguous_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 structured CheckResult with 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 unsupported op or is dimensionally inconsistent with its declared result.
  • MissingOperationRuleError (subclass of InvalidOperationRuleError): 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

bridgman-0.2.0.tar.gz (11.9 kB view details)

Uploaded Source

Built Distribution

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

bridgman-0.2.0-py3-none-any.whl (14.6 kB view details)

Uploaded Python 3

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

Hashes for bridgman-0.2.0.tar.gz
Algorithm Hash digest
SHA256 fc546cd6c690379e25efc59037dc7f8732a50fd1a997bfe97d8a2b7b56896963
MD5 690bba37ae3d7dba0847224ed6ed7f14
BLAKE2b-256 8ebcbcd2a4f8545a99f58d892dbcc358bf1f5650c63c6b4a14bc06db7502feae

See more details on using hashes here.

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

Hashes for bridgman-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b64bbc2d50a36861e2417e475743119272dcbb9e0b8c4cbed0e81705fa5dba55
MD5 caba70f2d4404825989a2c85155eac27
BLAKE2b-256 121022cc0c916e56fe588a09efd76da0e6e94aac66e9fda4f32d14864ff1b2e9

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