Write a sympy equation, fill in pint quantities, and get the full working, that is (1) the symbolic form, (2) the substituted form with units, and (3) the result with its unit, rendered as LaTeX in your marimo or Jupyter notebook.
- ✨ Crystal-clear: shows the full working, that is symbolic form, substituted form, 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 symevalDefine the formula as a sympy.Eq, fill in pint quantities, and sym_evalf
renders the working. The output symbol is taken from the equation, so you do not
pass it separately:
from pint import Quantity
from sympy import Eq, Symbol
from symeval import sym_evalf
axial_stress = Eq(Symbol(r"\sigma"), Symbol("F") / Symbol("A"))
sym_evalf(
axial_stress,
subs={Symbol("F"): Quantity(-680, "kN"), Symbol("A"): Quantity(10_580, "mm^2")},
output_unit="MPa",
decimals=2,
)You can also call .sym_evalf() as a method on the equation. Pass mode= to
choose the rendering style; mode="verbose" adds an extra line to the working,
showing all values converted to SI base units:
axial_stress = Eq(Symbol(r"\sigma"), Symbol("F") / Symbol("A"))
fa = {Symbol("F"): Quantity(-680, "kN"), Symbol("A"): Quantity(10_580, "mm^2")}
axial_stress.sym_evalf(subs=fa, output_unit="MPa", decimals=2, mode="verbose")mode="one_line" collapses the working onto a single line:
axial_stress.sym_evalf(subs=fa, output_unit="MPa", decimals=1, mode="one_line")quantity_evalf is the numeric-only sibling of sym_evalf, that is the same
unit-aware evaluation without the working. It takes an expression rather than an
equation, so pass the equation's right-hand side (axial_stress.rhs). This makes
it useful for applying a formula across every row of a DataFrame:
import polars as pl
from pint import Quantity
from sympy import Eq, Symbol
from symeval import quantity_evalf
axial_stress = Eq(Symbol(r"\sigma"), Symbol("F") / Symbol("A"))
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(
axial_stress.rhs,
subs={Symbol("F"): Quantity(row["F_kN"], "kN"), Symbol("A"): 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 working for any row you want to inspect:
axial_stress.sym_evalf(
subs={Symbol("F"): Quantity(-680, "kN"), Symbol("A"): Quantity(10_580, "mm^2")},
output_unit="MPa",
decimals=1,
)A worked example from CSA S16-17. Each symbolic evaluation is chained into the
next, that is F_e into
See symeval_mo.py for the full reactive marimo notebook with input UIs.
Starting from sympy.Eq, sym_evalf solves for the single
unknown and evaluates, so you never write the rearranged form by hand:
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.