symeval
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 symeval
Axial stress under a compressive force
Define 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,
)
$$\begin{align*} \sigma &= \frac{F}{A} \ &= \frac{,-680\ \mathrm{kN}}{,10580\ \mathrm{mm}^{2}} \ \sigma &= -6.43\times 10^{7}\ \mathrm{Pa} = -64.27\ \mathrm{MPa} \end{align*}$$
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")
$$\begin{align*} \sigma &= \frac{F}{A} \ &= \frac{,-680\ \mathrm{kN}}{,10580\ \mathrm{mm}^{2}} \ &= \frac{,-6.800\times 10^{5}\ \mathrm{N}}{,1.058\times 10^{-2}\ \mathrm{m}^{2}} \ \sigma &= -6.43\times 10^{7}\ \mathrm{Pa} = -64.27\ \mathrm{MPa} \end{align*}$$
mode="one_line" collapses the working onto a single line:
axial_stress.sym_evalf(subs=fa, output_unit="MPa", decimals=1, mode="one_line")
$$\sigma = \frac{F}{A} = \frac{,-680\ \mathrm{kN}}{,10580\ \mathrm{mm}^{2}} = -64.3\ \mathrm{MPa}$$
quantity_evalf() on a DataFrame
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,
)
$$\begin{align*} \sigma &= \frac{F}{A} \ &= \frac{,-680\ \mathrm{kN}}{,10580\ \mathrm{mm}^{2}} \ \sigma &= -6.4\times 10^{7}\ \mathrm{Pa} = -64.3\ \mathrm{MPa} \end{align*}$$
Axial resistance of a steel HSS member
A worked example from CSA S16-17. Each symbolic evaluation is chained into the
next, that is F_e into $\lambda$, $\lambda$ into $C_r$, $C_r$ into $DCR$, so the
working captures every step of a multi-step engineering check:
$$F_{e} = \frac{\pi^{2} E r_{y}^{2}}{L^{2} k^{2}} = \frac{\pi^{2} ,200\ \mathrm{GPa} ,\left(76.1\ \mathrm{mm}\right)^{2}}{,\left(6.5\ \mathrm{m}\right)^{2} ,1^{2}} = 0.271\ \mathrm{GPa}$$
$$\lambda = \left(\frac{F_{y}}{F_{e}}\right)^{n} = \left(\frac{,400\ \mathrm{MPa}}{,0.2706\ \mathrm{GPa}}\right)^{,1.34} = 1.689$$
$$C_{r} = A F_{y} \phi_{s} \left(\lambda + 1\right)^{- \frac{1}{n}} = ,10580\ \mathrm{mm}^{2} ,400\ \mathrm{MPa} ,0.85 \left(,1.6886 + 1\right)^{- \frac{1}{,1.34}} = 1.720\ \mathrm{MN}$$
$$DCR = \frac{C_{f}}{C_{r}} = \frac{,680\ \mathrm{kN}}{,1.7196\ \mathrm{MN}} = 0.395$$
See symeval_mo.py for the full reactive marimo notebook with input UIs.
Ideal Gas Law: symbolic rearrangement
Starting from $PV = nRT$ as a sympy.Eq, sym_evalf solves for the single
unknown and evaluates, so you never write the rearranged form by hand:
$$\begin{align*} P &= \frac{R T n}{V} \ &= \frac{,8.314\ \frac{\mathrm{J}}{\left(\mathrm{K} \cdot \mathrm{mol}\right)} ,273.15\ \mathrm{K} ,1\ \mathrm{mol}}{,22.4\ \mathrm{l}} \ P &= 1.01\times 10^{5}\ \mathrm{Pa} = 101.39\ \mathrm{kPa} \end{align*}$$
See symeval_mo.py for the full reactive marimo notebook with input UIs.
Author
Built and maintained by Joost Gevaert at Bedrock.
Feedback & contributing
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.
Inspiration
- handcalcs, renders Python calculation code as LaTeX in Jupyter
- CalcPad, engineering calculations DSL with symbolic/numeric workflow
- Bret Victor's Explorable Explanations
License
Apache License 2.0, see LICENSE.
Release files for symeval 0.4.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| symeval-0.4.1.tar.gz | 17.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| symeval-0.4.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 32.8 kB
Release files / symeval-0.4.1.tar.gz
| Download URL | symeval-0.4.1.tar.gz |
|---|---|
| Size | 17.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
eb66a1f6b51dde2f5ebeb23abce7ce0fa0ef68604790ae3f4a6664fde57d9ef2
|
|
BLAKE2b-256 checksum How to use checksums |
8f26edf43259ae44d91d66de7a91951f924ca54a5007e4d09d314a20180db35c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / symeval-0.4.1-py3-none-any.whl
| Download URL | symeval-0.4.1-py3-none-any.whl |
|---|---|
| Size | 15.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
698f9bbf5d70e512d6955526c878e790a60a6c0b16575b87978d900c748b2da1
|
|
BLAKE2b-256 checksum How to use checksums |
975e524f9696e8c2b07fc82fb291e6373cbb1c1ed4cb3e3d32811175a539c07d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|