Skip to content

PyCUC v3.0.0

Latest

Choose a tag to compare

@sinagilassi sinagilassi released this 07 Sep 17:26
· 62 commits to main since this release

New: dimensional unit conversion

PyCUC 3.0.0 adds pycuc.from_to(), a general-purpose converter for
dimensionally compatible unit expressions. It is intended for equation
outputs and engineering expressions which were not practical to enumerate in
the legacy fixed-reference tables.

import pycuc

pycuc.from_to(1, "kg.m/s^2", "N")               # 1.0
pycuc.from_to(1, "kg/(m.s^2)", "Pa")            # 1.0
pycuc.from_to(1, "kJ/(mol.K)", "J/(mol.K)")     # 1000.0
pycuc.from_to(1, "kg.m^2/s^3", "W")             # 1.0

The engine parses every atom, recursively reduces it to the seven SI base
dimensions, checks dimensional compatibility, then applies the factor ratio.
It does not treat a complete engineering expression as an opaque lookup key.

Independent RefsX catalog

The new engine uses pycuc.docs.refsx.RefsX, an independent catalog of SI,
derived, process-engineering, and legacy-compatible unit definitions. It does
not depend on the legacy Refs category tables, so from_to() is suitable for
blind dimensional conversion of supported units.

Examples of supported engineering conversions:

# Heat-transfer coefficient; both denominator styles are accepted.
pycuc.from_to(1, "W/(m^2.K)", "BTU/(hr.ft^2.F)")
# 0.176110183682...

pycuc.from_to(1, "BTU/hr/ft^2/F", "W/m^2/K")
# 5.67826334111...

# Pressure, energy, viscosity, flow, and power.
pycuc.from_to(1, "torr", "Pa")                  # 133.322368421...
pycuc.from_to(1, "kg/cm2", "bar")               # 0.980665
pycuc.from_to(1, "erg", "J")                    # 1e-7
pycuc.from_to(1, "cP", "Pa.s")                  # 0.001
pycuc.from_to(10, "gal(US)/min", "L/min")       # 37.85411784
pycuc.from_to(1, "hp", "W")                     # 745.699871582...

Expression syntax

# Multiplication: `.` and `*` are equivalent.
pycuc.from_to(1, "kg.m/s^2", "N")
pycuc.from_to(1, "kg*m/s^2", "N")

# Integer powers: caret and legacy compact forms.
pycuc.from_to(1, "m^2", "cm2")
pycuc.from_to(1, "kg.m2/s2", "J")

# Parentheses and repeated division.
pycuc.from_to(1, "(m/s)^2", "m^2/s^2")
pycuc.from_to(1, "kg/m/s^2", "Pa")

At one expression level, factors following the first / remain in the
denominator. Thus kg/m/s^2 means kg/(m.s^2). Parentheses may be used to
make grouping explicit.

Unicode superscripts are deliberately rejected. Write m^2, not a Unicode
superscript form such as square-metre notation.

Concentration names

The catalog uses clear, case-sensitive concentration symbols:

pycuc.from_to(1, "M", "mol/L")         # molar concentration
pycuc.from_to(1, "ML", "mol/kg")       # molal concentration
pycuc.from_to(1, "molality", "mol/kg")

m remains metre and is never overloaded as molality.

Rules for blind conversion

from_to() is a blind dimensional converter. It can convert without a
quantity category only when every rule below is satisfied:

  1. Both expressions use units defined by RefsX (including documented SI
    prefixes and expression aliases).
  2. Both expressions parse under the strict grammar: units, . or *, /,
    balanced parentheses, and integer exponents.
  3. Both expressions reduce to exactly the same seven-element SI dimension
    vector.
  4. The conversion is multiplicative; no unprovided material property,
    reference condition, density, gravity convention, or semantic assumption is
    required.
  5. The caller supplies the intended target unit. Matching dimensions do not
    prove physical meaning--for example, J and N.m share dimensions but can
    represent energy and torque respectively.

When a rule is not met, the converter raises a precise error such as
UnknownUnitError, UnitSyntaxError, or DimensionMismatchError rather than
guessing.

Explicit non-blind cases

Absolute temperature is affine and must continue to use the existing API:

pycuc.convert_from_to(25, "C", "K")  # 298.15

For temperature intervals, use explicit delta-style symbols:

pycuc.from_to(1, "dC", "dK")  # 1.0
pycuc.from_to(1, "dF", "dC")  # 0.555555...

Likewise, ppm is a dimensionless ratio. Converting ppm directly to
mg/L is not blind because it requires a density assumption; provide that
assumption in the calling calculation instead.