Pint performs arithmetic with physical units and converts between them. pip install pint. Setup: from pint import UnitRegistry; ureg = UnitRegistry(); Q_ = ureg.Quantity. Create: distance = 5 * ureg.meter. velocity = Q_(60, "km/hour"). Q_(100, "degF"). Convert: distance.to("feet"). velocity.to("m/s"). Q_(100, "degF").to("degC") → 37.78 °C. Q_(1, "kg").to("lbs"). Arithmetic: (5 * ureg.km) / (2 * ureg.hour) → “2.5 km/hour”. (10 * ureg.m) + (50 * ureg.cm) → “10.5 m”. Dimensionality: q.dimensionality → [length] ** 1. q.check("[length]"). ureg.kilometer.dimensionality. Magnitude: q.magnitude → float. q.units → “meter”. str(q) → “5 meter”. Compact: q.to_compact() — choose best prefix automatically. numpy: import numpy as np; arr = np.array([1,2,3]) * ureg.meter. arr.to("feet"). Custom: ureg.define("pixel = 0.26458 mm = px"). ureg.define("dollar = [currency]"). Context: with ureg.context("spectroscopy"): Q_(500,"nm").to("eV"). wraps: @ureg.wraps("m/s", ["m","s"]) — enforce units on function parameters. @ureg.check("[length]"). pandas: df["distance_m"] = df["distance_ft"].apply(lambda x: Q_(x,"ft").to("m").magnitude). Claude Code generates pint unit converters, engineering calculators, and pandas unit pipelines.
CLAUDE.md for Pint
## Pint Stack
- Version: pint >= 0.23 | pip install pint
- Registry: from pint import UnitRegistry; ureg = UnitRegistry(); Q_ = ureg.Quantity
- Create: 5 * ureg.meter | Q_(60, "km/hour") | Q_(100, "degF")
- Convert: q.to("feet") | q.ito("m") in-place | q.to_compact() for best prefix
- Arithmetic: (10*ureg.m) + (50*ureg.cm) → auto-normalizes | dimensional analysis
- Check: q.dimensionality | q.check("[length]") | ureg.check("[mass]") decorator
- numpy: np.array([1,2,3]) * ureg.meter → vectorized; .to("feet") works on arrays
Pint Physical Units Pipeline
# app/units.py — pint unit-aware arithmetic, conversion, and pandas integration
from __future__ import annotations
from typing import Any
import numpy as np
from pint import UnitRegistry, errors as pint_errors
# ─────────────────────────────────────────────────────────────────────────────
# Shared registry — use one UnitRegistry per application
# ─────────────────────────────────────────────────────────────────────────────
ureg = UnitRegistry()
Q_ = ureg.Quantity # shorthand constructor
# ─────────────────────────────────────────────────────────────────────────────
# 1. Conversion helpers
# ─────────────────────────────────────────────────────────────────────────────
def convert(value: float, from_unit: str, to_unit: str) -> float:
"""
Convert a scalar value between units.
Raises pint.DimensionalityError if the units are incompatible.
"""
return Q_(value, from_unit).to(to_unit).magnitude
def convert_qty(value: float, from_unit: str, to_unit: str) -> Any:
"""Return the converted Quantity object (includes units in repr)."""
return Q_(value, from_unit).to(to_unit)
def is_compatible(unit_a: str, unit_b: str) -> bool:
"""Return True if two units measure the same physical dimension."""
try:
a = ureg.Unit(unit_a)
b = ureg.Unit(unit_b)
return a.dimensionality == b.dimensionality
except Exception:
return False
def best_prefix(value: float, unit: str) -> str:
"""
Return the quantity in its most readable prefix form.
to_compact() picks the prefix that keeps the magnitude in [1, 1000).
1_000_000 mm → "1.0 km"
"""
return str(Q_(value, unit).to_compact())
# ─────────────────────────────────────────────────────────────────────────────
# 2. Temperature conversions
# ─────────────────────────────────────────────────────────────────────────────
def celsius_to_fahrenheit(c: float) -> float:
return Q_(c, "degC").to("degF").magnitude
def fahrenheit_to_celsius(f: float) -> float:
return Q_(f, "degF").to("degC").magnitude
def kelvin_to_celsius(k: float) -> float:
return Q_(k, "kelvin").to("degC").magnitude
def temp_convert(value: float, from_scale: str, to_scale: str) -> float:
"""Generic temperature converter: degC, degF, kelvin."""
return Q_(value, from_scale).to(to_scale).magnitude
# ─────────────────────────────────────────────────────────────────────────────
# 3. Distance / speed / area / volume
# ─────────────────────────────────────────────────────────────────────────────
def km_to_miles(km: float) -> float:
return convert(km, "kilometer", "mile")
def mph_to_kph(mph: float) -> float:
return convert(mph, "miles_per_hour", "kilometer / hour")
def meters_to_feet(m: float) -> float:
return convert(m, "meter", "foot")
def liters_to_gallons(l: float, us: bool = True) -> float:
target = "US_liquid_gallon" if us else "gallon"
return convert(l, "liter", target)
def kg_to_lbs(kg: float) -> float:
return convert(kg, "kilogram", "pound")
# ─────────────────────────────────────────────────────────────────────────────
# 4. Engineering calculations with dimensional analysis
# ─────────────────────────────────────────────────────────────────────────────
def speed(distance_m: float, time_s: float) -> dict[str, float]:
"""
Calculate speed from distance (meters) and time (seconds).
Pint tracks dimensions — dividing [length] / [time] → [velocity].
Returns speed in m/s, km/h, and mph.
"""
d = Q_(distance_m, "meter")
t = Q_(time_s, "second")
v = d / t # → pint Quantity in "meter/second"
return {
"m_s": v.magnitude,
"km_h": v.to("km/h").magnitude,
"mph": v.to("mph").magnitude,
}
def fuel_efficiency(distance_km: float, fuel_liters: float) -> dict[str, float]:
"""Compute fuel efficiency in L/100km and MPG."""
d = Q_(distance_km, "kilometer")
f = Q_(fuel_liters, "liter")
l100 = (f / d).to("L/100km")
mpg = (d / f).to("miles / US_liquid_gallon")
return {
"l_per_100km": l100.magnitude,
"mpg": mpg.magnitude,
}
def pressure_drop(
flow_rate_m3s: float,
pipe_diameter_m: float,
length_m: float,
friction_factor: float,
density_kgm3: float = 1000.0,
) -> float:
"""
Darcy-Weisbach pressure drop (Pa).
Demonstrates unit-aware formula: Δp = f * (L/D) * (ρv²/2)
"""
Q = Q_(flow_rate_m3s, "m³/s")
D = Q_(pipe_diameter_m, "m")
L = Q_(length_m, "m")
rho = Q_(density_kgm3, "kg/m³")
A = (3.14159 / 4) * D**2
v = Q / A # m/s
dp = friction_factor * (L / D) * (rho * v**2 / 2)
return dp.to("pascal").magnitude
# ─────────────────────────────────────────────────────────────────────────────
# 5. NumPy array conversion
# ─────────────────────────────────────────────────────────────────────────────
def convert_array(
values: list[float] | np.ndarray,
from_unit: str,
to_unit: str,
) -> np.ndarray:
"""
Convert an array of values between units using numpy Quantity.
Returns a plain numpy array of converted magnitudes.
"""
arr = np.asarray(values)
qty = Q_(arr, from_unit)
return qty.to(to_unit).magnitude
def celsius_array_to_fahrenheit(temps_c: list[float]) -> np.ndarray:
return convert_array(temps_c, "degC", "degF")
# ─────────────────────────────────────────────────────────────────────────────
# 6. Pandas integration
# ─────────────────────────────────────────────────────────────────────────────
def add_converted_column(
df,
source_col: str,
from_unit: str,
to_unit: str,
new_col: str | None = None,
) -> Any:
"""
Add a unit-converted column to a pandas DataFrame.
new_col defaults to "{source_col}_{to_unit}".
"""
import pandas as pd
out_col = new_col or f"{source_col}_{to_unit.replace('/','_per_')}"
values = convert_array(df[source_col].values, from_unit, to_unit)
df[out_col] = values
return df
# ─────────────────────────────────────────────────────────────────────────────
# 7. Custom unit definition
# ─────────────────────────────────────────────────────────────────────────────
def define_custom_units() -> None:
"""
Register domain-specific units.
ureg.define("name = factor * base_unit = alias").
Custom units persist in the registry for the process lifetime.
"""
# CSS pixels: 96 dpi standard (1 inch = 96 px)
try:
ureg.define("pixel = 1/96 * inch = px")
except Exception:
pass
# Story points (dimensionless effort unit)
try:
ureg.define("story_point = dimensionless = sp")
except Exception:
pass
# ─────────────────────────────────────────────────────────────────────────────
# 8. Unit-aware function decorators
# ─────────────────────────────────────────────────────────────────────────────
@ureg.wraps("m/s", (ureg.meter, ureg.second))
def compute_speed(distance, time):
"""Will raise if called with incompatible units."""
return distance / time
# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
print("=== Basic conversions ===")
cases = [
(100, "degF", "degC"),
(1, "mile", "kilometer"),
(1, "gallon", "liter"),
(1, "pound", "kilogram"),
(1, "horsepower", "watt"),
(1, "atmosphere", "pascal"),
(1, "calorie", "joule"),
(299_792_458, "m/s", "km/h"),
]
for val, from_u, to_u in cases:
result = convert(val, from_u, to_u)
print(f" {val} {from_u:15} → {result:.4f} {to_u}")
print("\n=== Temperature ===")
for c in [0, 20, 37, 100, -40]:
f = celsius_to_fahrenheit(c)
print(f" {c:6}°C = {f:.1f}°F")
print("\n=== Speed calculation ===")
s = speed(100, 9.58) # Usain Bolt 100m sprint
print(f" 100m in 9.58s: {s['m_s']:.2f} m/s = {s['km_h']:.2f} km/h = {s['mph']:.2f} mph")
print("\n=== Fuel efficiency ===")
fe = fuel_efficiency(500, 40)
print(f" 500km, 40L: {fe['l_per_100km']:.1f} L/100km = {fe['mpg']:.1f} mpg")
print("\n=== Array conversion ===")
temps_c = [0, 20, 37, 100]
temps_f = celsius_array_to_fahrenheit(temps_c)
for c, f in zip(temps_c, temps_f):
print(f" {c}°C → {f:.1f}°F")
print("\n=== Compact display ===")
for val, unit in [(0.001, "meter"), (1e9, "meter"), (1500, "watt"), (0.000001, "gram")]:
print(f" {val} {unit} → {best_prefix(val, unit)}")
print("\n=== Dimensionality ===")
samples = [
Q_(5, "meter"),
Q_(60, "km/h"),
Q_(100, "degF"),
Q_(1, "kilogram * meter / second**2"),
]
for q in samples:
print(f" {q!s:30} dim={q.dimensionality}")
For the astropy.units alternative — astropy.units is the gold standard for astrophysics (parsecs, light-years, solar masses, angular units), but Pint is the right choice for general engineering and scientific Python because it has a simpler API, doesn’t require the full astropy dependency, and covers all SI, imperial, and derived units; Pint’s ureg.define() makes it easy to add domain-specific units (CSS pixels, story points, custom chemical concentrations). For the quantities package alternative — quantities wraps numpy arrays with attached units but is less actively maintained and has a more complex API; Pint works naturally with plain numpy arrays (np.array([1,2,3]) * ureg.meter) and pandas Series, making it easier to integrate into existing data pipelines without restructuring code. The Claude Skills 360 bundle includes Pint skill sets covering UnitRegistry and Q_ shorthand, Q_(value, unit).to(target) conversion, magnitude and units attributes, to_compact() for best prefix selection, is_compatible() dimensionality check, temp_convert() for degC/degF/kelvin, speed() and fuel_efficiency() engineering calculations, convert_array() for numpy vectorized conversion, celsius_array_to_fahrenheit() array helper, add_converted_column() pandas integration, ureg.define() custom unit registration, and @ureg.wraps() unit-enforcing decorator. Start with the free tier to try unit-aware arithmetic code generation.