Write a sympy expression, fill in pint quantities, and get the full derivation, that is (1) formula, (2) substituted values with units, and (3) the result with unit — rendered as LaTeX in your marimo or Jupyter notebook.
- ✨ Crystal-clear — shows the full derivation: formula, values with units, and result
- 🐍 Pure Python — drop into your interactive notebooks and other Python code, no special syntax, no cell magic, no Domain-Specific Language (DSL)
- 📏 Unit-aware —
pintquantities carry units through every step and convert to your chosen output unit - 🧮 Sympy-native — rearrange or simplify your formula symbolically first, then evaluate
- 📊 DataFrame-ready — use
quantity_evalf()to compute a new unit-aware column on aDataFrame
pip install symevalfrom pint import Quantity
from sympy import Symbol
from symeval import sym_evalf
sigma = sym_evalf(
expr=Symbol("F") / Symbol("A"),
subs={Symbol("F"): Quantity(-680, "kN"), Symbol("A"): Quantity(10_580, "mm^2")},
output_symbol=r"\sigma",
output_unit="MPa",
decimals=2,
)You can also build the sympy expression first and call .sym_evalf() as a method — useful when you want to do symbolic math before filling in numbers. Pass mode= to choose the rendering style; mode="verbose" adds an extra line showing all values converted to SI base units:
f_sym, a_sym = Symbol("F"), Symbol("A")
sigma_expr = f_sym / a_sym
sigma_expr.sym_evalf(
subs={f_sym: Quantity(-680, "kN"), a_sym: Quantity(10_580, "mm^2")},
output_symbol=r"\sigma",
output_unit="MPa",
decimals=2,
mode="verbose",
)mode="one_line" collapses the derivation onto a single line:
sigma_expr.sym_evalf(
subs={f_sym: Quantity(-680, "kN"), a_sym: Quantity(10_580, "mm^2")},
output_symbol=r"\sigma",
output_unit="MPa",
decimals=1,
mode="one_line",
)quantity_evalf is the numeric-only sibling of sym_evalf — same unit-aware evaluation, no LaTeX overhead. It's useful for applying a formula across every row of a DataFrame:
import polars as pl
from pint import Quantity
from sympy import Symbol
from symeval import quantity_evalf
f_sym, a_sym = Symbol("F"), Symbol("A")
sigma_expr = f_sym / a_sym
members = pl.DataFrame({
"member_type": ["column", "column", "brace", "strut", "tie"],
"section": ["W14x90", "HSS8x8x5/8", "HSS6x6x3/8", "L4x4", "C8x11.5"],
"F_kN": [-720.0, -680.0, 340.0, -110.0, 250.0],
"A_mm2": [17_100.0, 10_580.0, 4_890.0, 1_870.0, 2_168.0],
})
def stress_MPa(row):
return quantity_evalf(
sigma_expr,
subs={f_sym: Quantity(row["F_kN"], "kN"), a_sym: Quantity(row["A_mm2"], "mm^2")},
output_unit="MPa",
).magnitude
members_with_stress = members.with_columns(
pl.struct(["F_kN", "A_mm2"])
.map_elements(stress_MPa, return_dtype=pl.Float64)
.alias("sigma_MPa")
)| member_type | section | F_kN | A_mm2 | sigma_MPa |
|---|---|---|---|---|
| column | W14x90 | -720.00 | 17100.00 | -42.11 |
| column | HSS8x8x5/8 | -680.00 | 10580.00 | -64.27 |
| brace | HSS6x6x3/8 | 340.00 | 4890.00 | 69.53 |
| strut | L4x4 | -110.00 | 1870.00 | -58.82 |
| tie | C8x11.5 | 250.00 | 2168.00 | 115.31 |
Then use sym_evalf to show the full derivation for any row you want to inspect:
sigma_expr.sym_evalf(
subs={f_sym: Quantity(-680, "kN"), a_sym: Quantity(10_580, "mm^2")},
output_symbol=r"\sigma",
output_unit="MPa",
decimals=1,
)A worked example from CSA S16-17. Each sym_evalf result feeds into the next — F_e into
See symeval_mo.py for the full reactive marimo notebook with input UIs.
Starting from sympy.solve rearranges the equation symbolically for any variable, then the resulting expression feeds straight into sym_evalf:
See symeval_mo.py for the full reactive marimo notebook with input UIs.
Built and maintained by Joost Gevaert at Bedrock.
Found a bug or have a feature request? Open an issue — pull requests are welcome too. The package is a single marimo notebook (symeval_mo.py) with ## EXPORT-marked cells extracted into src/symeval/ via mobuild; see CLAUDE.md for the project layout and RELEASING.md for the release workflow.
- handcalcs — renders Python calculation code as LaTeX in Jupyter
- CalcPad — engineering calculations DSL with symbolic/numeric workflow
- Bret Victor's Explorable Explanations
Apache License 2.0 — see LICENSE.