pipe_command

pipe_command

Pipe commands for functional composition of mathematical operations.

This module provides @Pipe decorated functions that enable chain operations like: expr | pc.subs(vals) | pc.convert_to(units) | pc.N

All functions are designed to work with SymPy expressions and support functional programming patterns for mathematical computation workflows.

Functions

Name Description
order_subs Reorder substitutions using topological order for dependency resolution.
subs Substitute variables in symbolic expressions with values or other expressions.
rebuild Rebuild an expression tree so that leftover numeric pieces are evaluated.
N Numerically evaluate symbolic expressions to floating-point values.
convert_to Convert quantities to different units while preserving values.
doit Evaluate unevaluated operations in symbolic expressions.
parse_expr Parse mathematical expression strings into SymPy symbolic objects.
quantity_simplify Simplify expressions with units by combining and reducing quantities.
as_two_terms Split expression into magnitude and units components.
as_Mul Create unevaluated multiplication from tuple of expressions.

order_subs

order_subs(subs)

Reorder substitutions using topological order for dependency resolution.

Substitutions are applied one after the other, so a key must be replaced before any other key it brings into the expression or contains:

  • a key whose value contains another key comes first (y: 2*x is applied before x: 3, so the x it introduces is replaced too);
  • a key that contains another key comes first: MatrixSymbol("A", n, m) is applied before n and m, and f(x) before x. Replacing n first would turn the expression’s A into MatrixSymbol("A", 2, m), which no longer matches the key A (keecas#124).

Parameters

Name Type Description Default
subs dict[Basic, Any] Dictionary of substitutions where keys are variables and values are expressions required

Returns

Name Type Description
list[tuple[Basic, Any]] Ordered list of substitution tuples for exhaustive application

Raises

Name Type Description
ValueError If the dependencies form a cycle, e.g. x: y and y: x, or x: f(x) and f(x): 3 (the value of x contains f(x), and f(x) contains x). The message names the keys of each cycle.

subs

subs(expression, substitution, sorted=True)

Substitute variables in symbolic expressions with values or other expressions.

Applies substitutions to SymPy expressions while handling dependencies between variables. Commonly used in workflows to evaluate symbolic equations with parameters from _p dicts or intermediate expressions from _e dicts. Integrates with pipe operations for functional composition.

NOTE: Returns None if input expression is None, allowing chain operations to gracefully handle missing values.

Parameters

Name Type Description Default
expression Basic SymPy expression to apply substitutions to. Can be any symbolic expression containing variables to be replaced. required
substitution dict[Basic, Any] Dictionary mapping variables (symbols) to their replacement values (numeric values, quantities with units, or other expressions). Non-Basic keys and None values are automatically filtered out. required
sorted bool Whether to apply topological sorting for dependency resolution. When True (default), automatically orders substitutions so that dependent variables are substituted in correct order (e.g., if y depends on x, the x introduced by y is substituted too, and a MatrixSymbol with a symbolic shape is substituted before its shape symbols; see order_subs). When False, the dict is passed to SymPy’s subs unchanged, which does not resolve dependencies between entries. Defaults to True. True

Returns

Name Type Description
Basic SymPy expression with substitutions applied, or None if input is None.

Examples

from keecas import symbols, u, pc, show_eqn

# Basic parameter substitution
F, A_load = symbols(r"F, A_{load}")

_p = {
    F: 100*u.kN,
    A_load: 20*u.cm**2,
}

sigma = symbols(r"\sigma")
_e = {
    sigma: "F / A_load" | pc.parse_expr,
}

# Substitute parameters into expression
result = _e[sigma] | pc.subs(_p)
# Multi-step workflow with merged dicts
_v = {
    k: v | pc.subs(_p | _e) | pc.convert_to([u.MPa]) | pc.N
    for k, v in _e.items()
}

show_eqn([_p | _e, _v])

\[\begin{align} F & = 100{\,}\text{kN} & \\[8pt] A_{load} & = 20{\,}\text{cm}^{2} & \\[8pt] \sigma & = \dfrac{F}{A_{load}} & = 50.0{\,}\text{MPa} \end{align}\]

# Dependency resolution with sorted=True
x, y, z = symbols(r"x, y, z")

# y depends on x, z depends on y
_e = {
    z: "x + y" | pc.parse_expr,
    y: "2 * x" | pc.parse_expr,
    x: 5,
}

# Automatic ordering: x first, then y, then z
result = z | pc.subs(_e)  # sorted=True by default

Notes

  • Automatically filters out None values and non-Basic keys from substitution dict
  • Topological sorting handles complex dependency chains automatically
  • Works seamlessly with Pint quantities through SymPy integration
  • Common pattern: _p | _e merges parameters and expressions for substitution

rebuild

rebuild(expression)

Rebuild an expression tree so that leftover numeric pieces are evaluated.

parse_expr keeps the expression as typed (evaluate=False), so n - 1 is stored as n + (-1)*1. After subs() replaces n with 3, the result still reads 3 - 1*1: the (-1)*1 holds no symbol, so subs() never rebuilds it. Most steps do not mind, but Sum and Product iterate only when their limits are plain integers, so doit() leaves them unevaluated. The same happens with any unevaluated literal, such as Abs(-3) or sqrt(4).

rebuild calls every constructor in the tree again (SymPy’s sympy.strategies.rl.rebuild), giving the same tree as parsing with evaluate=True. Quantities, matrices, arrays, indexed objects and UnevaluatedExpr are kept. Use it as a step after subs() in the _v pipeline; the _e dict keeps the typed form for show_eqn().

NOTE: Returns None if input expression is None, like subs().

Parameters

Name Type Description Default
expression Any Expression to rebuild, typically the output of subs(). Non-SymPy input (numbers, mutable matrices) is sympified first, as subs() does. required

Returns

Name Type Description
Basic Rebuilt SymPy expression, or None if input is None.

Examples

from keecas import symbols, pc

n = symbols(r"n")

after_subs = "n - 1" | pc.parse_expr | pc.subs({n: 3})
print(after_subs)  # 3 - 1*1, as typed

print(after_subs | pc.rebuild)  # 2
3 - 1*1
2
# A sum whose upper limit depends on a parameter
from keecas import u, show_eqn
from sympy import Array, Idx, IndexedBase

F = symbols(r"F", cls=IndexedBase)
i = symbols(r"i", cls=Idx)
n, F_tot = symbols(r"n, F_{tot}")

_p = {
    F: Array([10, 20, 30]) * u.kN,
    n: 3,
}

_e = {
    F_tot: "Sum(F[i], (i, 0, n - 1))" | pc.parse_expr,
}

# Without pc.rebuild, pc.doit would leave the Sum unevaluated
_v = {
    k: v | pc.subs(_p | _e) | pc.rebuild | pc.doit | pc.convert_to([u.kN]) | pc.N
    for k, v in _e.items()
}

show_eqn([_p | _e, _v])

\[\begin{align} F & = \left[\begin{matrix}10{\,}\text{kN} & 20{\,}\text{kN} & 30{\,}\text{kN}\end{matrix}\right] & \\[8pt] n & = 3 & \\[8pt] F_{tot} & = \sum_{i=0}^{n - 1} {F}_{i} & = 60.0{\,}\text{kN} \end{align}\]

Notes

  • Needed only when a Sum or Product limit is a formula such as n - 1: other expressions give the same result without it, since N() evaluates the leftovers. A missing rebuild shows as an unevaluated Sum(...) in the result
  • Not applied inside subs(): its output is sometimes displayed as an intermediate step, and rebuilding can reorder the terms
  • N() before doit() is not a substitute: on a single Sum it tries a numeric estimate and can fail

N

N(expression, precision=15)

Numerically evaluate symbolic expressions to floating-point values.

Converts symbolic expressions containing irrational numbers, constants, or unevaluated operations into numeric approximations. Commonly used as final step in calculation workflows before display with show_eqn().

Parameters

Name Type Description Default
expression Basic SymPy expression to evaluate numerically. Can contain symbolic math operations, constants (pi, e, etc.), or quantities with units. required
precision int Number of decimal digits for evaluation. Controls internal calculation precision, not display formatting (use float_format in show_eqn for display). Defaults to 15. 15

Returns

Name Type Description
Basic Numerically evaluated SymPy expression as floating-point approximation.

Examples

from keecas import symbols, u, pc, show_eqn
from sympy import pi

# Basic numeric evaluation
r = symbols(r"r")
A_circle = symbols(r"A_{circle}")

_p = {
    r: 5*u.cm,
}

_e = {
    A_circle: "pi * r**2" | pc.parse_expr,
}

# Evaluate symbolic pi to numeric value
_v = {
    k: v | pc.subs(_p | _e) | pc.N
    for k, v in _e.items()
}

show_eqn([_p | _e, _v])

\[\begin{align} r & = 5{\,}\text{cm} & \\[8pt] A_{circle} & = \pi{\,}r^{2} & = 78.5398163397448{\,}\text{cm}^{2} \end{align}\]

# Control precision for high-accuracy calculations
import sympy as sp

expr = sp.sqrt(2)

# Default precision (15 digits)
result_default = expr | pc.N

# High precision (50 digits)
result_high = expr | pc.N(precision=50)
# Typical workflow: subs -> convert_to -> N
F, A_load, sigma_Sd = symbols(r"F, A_{load}, \sigma_{Sd}")

_p = {
    F: 850*u.kN,
    A_load: 120*u.cm**2,
}

_e = {
    sigma_Sd: "F / A_load" | pc.parse_expr,
}

_v = {
    k: v | pc.subs(_p | _e) | pc.convert_to([u.MPa]) | pc.N
    for k, v in _e.items()
}

show_eqn([_p | _e, _v])

\[\begin{align} F & = 850{\,}\text{kN} & \\[8pt] A_{load} & = 120{\,}\text{cm}^{2} & \\[8pt] \sigma_{Sd} & = \dfrac{F}{A_{load}} & = 70.8333333333333{\,}\text{MPa} \end{align}\]

Notes

  • Precision parameter controls internal calculation accuracy, not display
  • For display formatting, use float_format parameter in show_eqn()
  • Works with Pint quantities, preserving units in output
  • Commonly chained after subs() and convert_to() in evaluation workflows

convert_to

convert_to(expression, units=1)

Convert quantities to different units while preserving values.

Converts SymPy expressions with units to target units, handling both prefixed units (kN, daN, cm) and compound units (MPa, kN/m). Commonly used in engineering workflows to standardize units before display or comparison. Integrates seamlessly with Pint unit definitions.

Parameters

Name Type Description Default
expression Basic SymPy expression with units to convert. Must contain dimensional quantities (e.g., force, pressure, length). required
units Any Target units for conversion. Can be: - Single unit (e.g., u.MPa) - List of units (e.g., [u.kN, u.mm]) - Compound units (e.g., u.kN/u.m) Defaults to 1 (dimensionless). 1

Returns

Name Type Description
Basic Expression converted to target units with same magnitude in new units.

Examples

from keecas import symbols, u, pc, show_eqn

# Basic unit conversion
F, A_load = symbols(r"F, A_{load}")

_p = {
    F: 5000*u.N,  # Newtons
    A_load: 120*u.cm**2,
}

# Convert to kilonewtons
_v = {
    F: _p[F] | pc.convert_to(u.kN),
    A_load: _p[A_load] | pc.convert_to(u.m),
}

show_eqn([_p, _v])

\[\begin{align} F & = 5000{\,}\text{N} & = 5{\,}\text{kN} \\[8pt] A_{load} & = 120{\,}\text{cm}^{2} & = \dfrac{3}{250}{\,}\text{m}^{2} \end{align}\]

# Compound unit conversion
sigma = symbols(r"\sigma")

_p = {
    F: 850*u.kN,
    A_load: 120*u.cm**2,
}

_e = {
    sigma: "F / A_load" | pc.parse_expr,
}

# Convert pressure to MPa
_v = {
    k: v | pc.subs(_p | _e) | pc.convert_to(u.MPa) | pc.N
    for k, v in _e.items()
}

show_eqn([_p | _e, _v])

\[\begin{align} F & = 850{\,}\text{kN} & \\[8pt] A_{load} & = 120{\,}\text{cm}^{2} & \\[8pt] \sigma & = \dfrac{F}{A_{load}} & = 70.8333333333333{\,}\text{MPa} \end{align}\]

# List of target units for flexibility
L, delta = symbols(r"L, \delta")

_p = {
    L: 8*u.m,
}

_e = {
    delta: "L / 400" | pc.parse_expr,
}

# Try converting to mm, fallback to other units if needed
_v = {
    k: v | pc.subs(_p | _e) | pc.convert_to([u.mm]) | pc.N
    for k, v in _e.items()
}

show_eqn([_p | _e, _v])

\[\begin{align} L & = 8{\,}\text{m} & \\[8pt] \delta & = \dfrac{L}{400} & = 20.0{\,}\text{mm} \end{align}\]

# Sums with units and exact constants: pi stays symbolic until pc.N
b_f, A_r, g_s, w = symbols(r"b_f, A_r, \gamma_s, w")

_p = {
    b_f: 50*u.mm,
    g_s: 78.5*u.kN/u.m**3,
}

_e = {
    A_r: "2*b_f**2 - (4 - pi)*b_f**2/10" | pc.parse_expr,
    w: "A_r * g_s" | pc.parse_expr,
}

_v = {
    A_r: A_r | pc.subs(_p | _e) | pc.convert_to([u.mm]),  # (4000 + 250*pi) mm**2, exact
    w: w | pc.subs(_p | _e) | pc.convert_to([u.kN, u.m]) | pc.N,  # kN/m
}

show_eqn([_p | _e, _v])

\[\begin{align} b_{f} & = 50{\,}\text{mm} & \\[8pt] \gamma_{s} & = 78.5{\,}\dfrac{\text{kN}}{\text{m}^{3}} & \\[8pt] A_{r} & = 2{\,}b_{f}^{2} - \dfrac{\left(4 - \pi\right){\,}b_{f}^{2}}{10} & = \left(4000 + 250{\,}\pi\right){\,}\text{mm}^{2} \\[8pt] w & = A_{r}{\,}\gamma_{s} & = 0.3756537558267{\,}\dfrac{\text{kN}}{\text{m}} \end{align}\]

Notes

  • Prefixed units (kN, cm, MPa) handled automatically via SymPy prefix system
  • Non-prefixed units (kgf, lbf) use scale factors from Pint definitions
  • All Pint units convert correctly through SymPy integration
  • Commonly chained between subs() and N() in evaluation workflows
  • List of units allows flexible conversion with fallback options
  • A sum whose terms share a dimension is converted as a whole, also when it is a factor of a product ((4 - pi)*b**2, h - c - 5*u.mm), so N() is not needed before convert_to(); sympy’s own convert_to gets the unit wrong there (keecas#118)
  • Exact numbers and constants such as pi stay symbolic; chain N() after convert_to() to evaluate them

doit

doit(expression)

Evaluate unevaluated operations in symbolic expressions.

Forces evaluation of operations that SymPy keeps in symbolic form, such as derivatives, integrals, summations, or limits. Useful when symbolic operations need numeric results or simplified forms.

Parameters

Name Type Description Default
expression Basic SymPy expression containing unevaluated operations (derivatives, integrals, summations, limits, etc.) that should be evaluated. required

Returns

Name Type Description
Basic Expression with all unevaluated operations evaluated to their results.

Examples

from keecas import symbols, pc
import sympy as sp

# Evaluate derivative
x = symbols(r"x")
expr = sp.Derivative(x**2, x)

# Without doit: shows Derivative(x**2, x)
print(expr)

# With doit: evaluates to 2*x
result = expr | pc.doit
print(result)
Derivative(x**2, x)
2*x
# Evaluate integral
integral_expr = sp.Integral(sp.sin(x), x)

# Evaluates to -cos(x)
result = integral_expr | pc.doit
# Use in calculation workflow
from keecas import u, show_eqn

F, x_coord = symbols(r"F, x")

_p = {
    F: 100*u.kN,
}

# Expression with derivative
_e = {
    symbols(r"dF/dx"): sp.Derivative(F * x_coord**2, x_coord),
}

# Evaluate derivative then substitute
_v = {
    k: v | pc.doit | pc.subs(_p) | pc.N
    for k, v in _e.items()
}

show_eqn([_p | _e, _v])

\[\begin{align} F & = 100{\,}\text{kN} & \\[8pt] dF/dx & = \dfrac{\partial}{\partial x} F{\,}x^{2} & = 200.0{\,}x{\,}\text{kN} \end{align}\]

# Sum over an IndexedBase substituted with a matrix expression
from sympy import Idx, ImmutableMatrix, IndexedBase, MatrixSymbol

q = symbols(r"q", cls=IndexedBase)
i, j = symbols(r"i, j", cls=Idx)
gamma_Q, Q_tot = symbols(r"\gamma_Q, Q_{tot}")
Q_k = symbols(r"Q_k", cls=MatrixSymbol, n=2, m=3)

_p = {
    gamma_Q: 1.5,
    Q_k: ImmutableMatrix([[1, 2, 3], [4, 5, 6]]) * u.kN,
}

_e = {
    q: "gamma_Q * Q_k" | pc.parse_expr,
    Q_tot: "Sum(q[i, j], (i, 0, 1), (j, 0, 2))" | pc.parse_expr,
}

# q becomes 1.5*Q_k with Q_k a Matrix: the Sum still evaluates
_v = {
    Q_tot: Q_tot | pc.subs(_p | _e) | pc.doit | pc.convert_to(u.kN) | pc.N,
}

show_eqn([_p | _e, _v])

\[\begin{align} \gamma_{Q} & = 1.5 & \\[8pt] Q_{k} & = \left[\begin{matrix}\text{kN} & 2{\,}\text{kN} & 3{\,}\text{kN}\\[8pt]4{\,}\text{kN} & 5{\,}\text{kN} & 6{\,}\text{kN}\end{matrix}\right] & \\[8pt] q & = \gamma_{Q} Q_{k} & \\[8pt] Q_{tot} & = \sum_{\substack{0 \leq i \leq 1\\[8pt]0 \leq j \leq 2}} {q}_{i,j} & = 31.5{\,}\text{kN} \end{align}\]

Notes

  • Works with derivatives, integrals, limits, summations, and products
  • May be needed before subs() to properly substitute into evaluated forms
  • Not all operations can be evaluated symbolically (may return unchanged)
  • Combines well with other pipe commands in calculation workflows
  • An IndexedBase such as x[i, j] can be substituted with a 2-D Matrix or a matrix expression (k*B, a Hadamard product, a scalar with units times a Matrix), not only with an Array: before evaluating, such Indexed objects are rebuilt over an explicit Array so a Sum over them resolves (keecas#126). A matrix expression that still contains a MatrixSymbol is left symbolic.
  • A Sum or Product whose limit is a formula (n - 1) also needs rebuild() after subs(): pc.subs | pc.rebuild | pc.doit | pc.convert_to | pc.N
  • With a Sum, put doit() before convert_to(): on an unevaluated Sum, convert_to() converts each quantity inside it separately, and a target such as [u.kN] cannot express a kPa there
  • A literal index on both axes of a 2-D Matrix (x[0, 1]) fails earlier, in subs() (keecas#134)

parse_expr

parse_expr(expression, local_dict=None, evaluate=False, **kwargs)

Parse mathematical expression strings into SymPy symbolic objects.

Converts string representations of mathematical expressions into SymPy expressions that can be manipulated symbolically. Most commonly used with pipe operator to define expressions in _e dicts using readable string notation instead of verbose SymPy syntax.

NOTE: Automatically captures the caller’s variables when local_dict is None, both with | pc.parse_expr and with arguments (| pc.parse_expr(evaluate=True)), enabling clean string-based expression definitions.

Parameters

Name Type Description Default
expression str String representation of mathematical expression using standard notation (e.g., “F / A”, “pi * r**2”, “sqrt(x2 + y2)”). Supports operators: +, -, *, /, **, parentheses, and common functions. required
local_dict dict[str, Any] | None Dictionary of local variables for parsing context (symbol definitions, parameters, functions). If None, automatically uses the caller’s local and module variables. Defaults to None. None
evaluate bool Whether to evaluate the expression during parsing (e.g., simplify numeric operations). When False, preserves structure as written. Defaults to False. False
**kwargs Any Additional arguments passed to SymPy’s parse_expr: - transformations: List of parsing transformations (defaults to T[:11]) - global_dict: Global symbol dictionary {}

Returns

Name Type Description
Basic Parsed SymPy expression object ready for symbolic manipulation.

Examples

from keecas import symbols, u, pc, show_eqn

# Most common pattern: define expressions as strings
F, A_load, sigma = symbols(r"F, A_{load}, \sigma")

_p = {
    F: 100*u.kN,
    A_load: 20*u.cm**2,
}

# Use parse_expr to convert string to SymPy expression
_e = {
    sigma: "F / A_load" | pc.parse_expr,
}

_v = {
    k: v | pc.subs(_p | _e) | pc.convert_to([u.MPa]) | pc.N
    for k, v in _e.items()
}

show_eqn([_p | _e, _v])

\[\begin{align} F & = 100{\,}\text{kN} & \\[8pt] A_{load} & = 20{\,}\text{cm}^{2} & \\[8pt] \sigma & = \dfrac{F}{A_{load}} & = 50.0{\,}\text{MPa} \end{align}\]

# Complex expressions with functions
from sympy import pi

r, A_circle, V_sphere = symbols(r"r, A_{circle}, V_{sphere}")

_p = {
    r: 5*u.cm,
}

_e = {
    A_circle: "pi * r**2" | pc.parse_expr,
    V_sphere: "(4/3) * pi * r**3" | pc.parse_expr,
}

_v = {
    k: v | pc.subs(_p | _e) | pc.N
    for k, v in _e.items()
}

show_eqn([_p | _e, _v])

\[\begin{align} r & = 5{\,}\text{cm} & \\[8pt] A_{circle} & = \pi{\,}r^{2} & = 78.5398163397448{\,}\text{cm}^{2} \\[8pt] V_{sphere} & = \dfrac{4{\,}\pi{\,}r^{3}}{3} & = 523.598775598299{\,}\text{cm}^{3} \end{align}\]

# Custom local_dict for additional functions
from sympy import sqrt

custom_locals = {
    'special_func': lambda x: sqrt(x**2 + 1),
}

expr = "special_func(5)" | pc.parse_expr(local_dict=custom_locals)
# Multiple dependent expressions
x, y, z, result = symbols(r"x, y, z, result")

_p = {
    x: 3,
    y: 4,
}

# Expressions can reference each other
_e = {
    z: "sqrt(x**2 + y**2)" | pc.parse_expr,
    result: "z * 2" | pc.parse_expr,
}

# tip: automatic dependency resolution with pc.subs
_v = {
    k: v | pc.subs(_p | _e) | pc.N
    for k, v in _e.items()
}

show_eqn([_p | _e, _v])

\[\begin{align} x & = 3 & \\[8pt] y & = 4 & \\[8pt] z & = \sqrt{x^{2} + y^{2}} & = 5.0 \\[8pt] result & = z{\,}2 & = 10.0 \end{align}\]

Notes

  • Uses transformations T[:11] by default for standard mathematical parsing
  • Automatically captures caller’s local scope for symbol resolution; inside a generator expression only its own variables and the module globals are visible, so pass local_dict there
  • Common functions supported: sin, cos, sqrt, log, exp, abs, etc.
  • Operators: +, -, *, /, ** (power), parentheses for grouping
  • Cleaner than verbose SymPy syntax: “F/A” vs sp.Div(F, A)
  • Idiomatic pattern: define symbols, then use string expressions in _e dict

quantity_simplify

quantity_simplify(
    expression,
    across_dimensions=True,
    unit_system='SI',
    **kwargs,
)

Simplify expressions with units by combining and reducing quantities.

Applies SymPy’s quantity simplification to combine terms with compatible units and reduce complex unit expressions. Useful for cleaning up expressions with multiple unit terms or dimensional analysis.

Parameters

Name Type Description Default
expression Basic SymPy expression to simplify, typically containing multiple terms with units that can be combined or reduced. required
across_dimensions bool Whether to simplify across different dimensions (e.g., combine force and length into energy). When True, allows broader simplifications. Defaults to True. True
unit_system str Unit system for simplification, determines which base units to use. Options include “SI”, “CGS”, “imperial”. Defaults to “SI”. 'SI'
**kwargs Any Additional arguments passed to SymPy’s quantity_simplify: - across_dimensions: Override the across_dimensions parameter - unit_system: Override the unit_system parameter {}

Returns

Name Type Description
Basic Simplified SymPy expression with combined and reduced unit terms.

Examples

from keecas import symbols, u, pc, show_eqn

# Simplify expression with multiple force terms
F_1, F_2, F_total = symbols(r"F_1, F_2, F_{total}")

_p = {
    F_1: 100*u.kN,
    F_2: 50000*u.N,
}

_e = {
    F_total: "F_1 + F_2" | pc.parse_expr,
}

# Simplify combines terms with compatible units
_v = {
    k: v | pc.subs(_p | _e) | pc.quantity_simplify | pc.convert_to([u.kN]) | pc.N
    for k, v in _e.items()
}

show_eqn([_p | _e, _v])

\[\begin{align} F_{1} & = 100{\,}\text{kN} & \\[8pt] F_{2} & = 50000{\,}\text{N} & \\[8pt] F_{total} & = F_{1} + F_{2} & = 150.0{\,}\text{kN} \end{align}\]

# Dimensional analysis with unit simplification
F, d, E = symbols(r"F, d, E")

_p = {
    F: 500*u.N,
    d: 2*u.m,
}

# Work (energy) from force and distance
_e = {
    E: "F * d" | pc.parse_expr,
}

# Simplify to base energy units
_v = {
    k: v | pc.subs(_p | _e) | pc.quantity_simplify | pc.convert_to([u.J]) | pc.N
    for k, v in _e.items()
}

show_eqn([_p | _e, _v])

\[\begin{align} F & = 500{\,}\text{N} & \\[8pt] d & = 2{\,}\text{m} & \\[8pt] E & = F{\,}d & = 1000.0{\,}\text{J} \end{align}\]

# Different unit systems
from sympy import sympify
from sympy.physics.units import newton, kilonewton

# Create expression with different unit prefixes
expr = 100*kilonewton + 5000*newton

# Simplify combines compatible units
result = expr | pc.quantity_simplify(unit_system="SI")
print(result)  # Combines to single unit
105000*newton

Notes

  • Combines terms with compatible units (e.g., kN + N -> kN)
  • Simplifies compound units to base units when appropriate
  • across_dimensions=True enables dimensional analysis simplifications
  • Works with both SI and other unit systems
  • May need convert_to() afterward to get desired output units

as_two_terms

as_two_terms(expression, as_mul=False)

Split expression into magnitude and units components.

Separates multiplicative expressions or matrices into two terms, typically magnitude and units for cleaner display.

Parameters

Name Type Description Default
expression Basic SymPy expression to split required
as_mul bool If True, return as unevaluated multiplication False

Returns

Name Type Description
Basic | tuple[Basic, Basic] Two-term tuple or unevaluated multiplication, depending on as_mul.
Basic | tuple[Basic, Basic] Returns original expression if splitting is not applicable.

Notes

For matrices, splits only if all elements share the same units.

as_Mul

as_Mul(expression)

Create unevaluated multiplication from tuple of expressions.

Multiplies two expressions while keeping them visually separated, ideal for displaying magnitude and units distinctly.

Parameters

Name Type Description Default
expression tuple[Basic, Basic] Tuple containing two SymPy expressions to multiply required

Returns

Name Type Description
Basic Unevaluated multiplication expression for clean display