Skip to main content

Symbolic evaluation for engineering calculations — renders expression → numbers with units → result as LaTeX

Project description

symeval

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-awarepint quantities 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 a DataFrame
pip install symeval

Axial stress under a compressive force

from 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,
)

$$\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 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",
)

$$\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 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",
)

$$\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 — 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,
)

$$\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 sym_evalf result feeds into the next — F_e into $\lambda$, $\lambda$ into $C_r$, $C_r$ into $DCR$ — so the LaTeX rendering captures the full audit trail 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$, sympy.solve rearranges the equation symbolically for any variable, then the resulting expression feeds straight into sym_evalf:

$$\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

License

Apache License 2.0 — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

symeval-0.3.2.tar.gz (15.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

symeval-0.3.2-py3-none-any.whl (13.3 kB view details)

Uploaded Python 3

File details

Details for the file symeval-0.3.2.tar.gz.

File metadata

  • Download URL: symeval-0.3.2.tar.gz
  • Upload date:
  • Size: 15.9 kB
  • Tags: Source
  • Uploaded using 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}

File hashes

Hashes for symeval-0.3.2.tar.gz
Algorithm Hash digest
SHA256 582c4c21584bb17ecdc747df976f0c6bdffdeeaf0685ad7b06a0a708c6e0a2c6
MD5 52654b1c5e88fc4b31256243c83917e8
BLAKE2b-256 412edfc6038384fe017c3cbdb6004ccea1a47eb0d39b8f22f013f850d4c14327

See more details on using hashes here.

File details

Details for the file symeval-0.3.2-py3-none-any.whl.

File metadata

  • Download URL: symeval-0.3.2-py3-none-any.whl
  • Upload date:
  • Size: 13.3 kB
  • Tags: Python 3
  • Uploaded using 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}

File hashes

Hashes for symeval-0.3.2-py3-none-any.whl
Algorithm Hash digest
SHA256 c5b5eed2649bdf7159c5e5bb7c38dc225de614169579c63f0d076ccbc123c116
MD5 0e4e4945e45715973e2dfe79d2bcabc5
BLAKE2b-256 cb65be7ff66cee9cd37dbae82dabcc7b815110608e0a06dd11d5a507a5fc5cc9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page