Symbols and notation#

Recipes for making variable names, functions and operators look the way they do in your code of practice or textbook. The background is in Naming variables and Symbols and functions.

Eurocode-style subscripts#

Asked in #101.

Each further underscore adds a comma-separated subscript, and a subscript of more than one letter is set upright:

N_pl_Rd = 1250   # kN
M_c_Rd = 410     # kN m
sigma_x_Ed = 185 # MPa
Output (MarkdownRenderer)
\[\begin{aligned} N_{\mathrm{pl,Rd}} &= 1250 \quad\text{(kN)} \\ M_{c,\mathrm{Rd}} &= 410 \quad\text{(kN m)} \\ \sigma_{x,\mathrm{Ed}} &= 185 \quad\text{(MPa)} \end{aligned}\]
$$\begin{aligned} N_{\mathrm{pl,Rd}} &= 1250 \quad\text{(kN)} \\ M_{c,\mathrm{Rd}} &= 410 \quad\text{(kN m)} \\ \sigma_{x,\mathrm{Ed}} &= 185 \quad\text{(MPa)} \end{aligned}$$

For v1-style nested subscripts (xab), use # hc: subscripts = "nested".

Greek letters joined to text#

Asked in #117.

A Greek name followed by an uppercase letter or a digit is split off, so DeltaH and gammaM0 read as written on paper. Unicode letters work too:

DeltaH = 4.2     # kJ
gamma_M0 = 1.0
gammaM1 = 1.1
ΔT = 35          # K
Output (MarkdownRenderer)
\[\begin{aligned} \Delta H &= 4.2 \quad\text{(kJ)} \\ \gamma_{\mathrm{M0}} &= 1 \\ \gamma \mathrm{M1} &= 1.1 \\ \Delta T &= 35 \quad\text{(K)} \end{aligned}\]
$$\begin{aligned} \Delta H &= 4.2 \quad\text{(kJ)} \\ \gamma_{\mathrm{M0}} &= 1 \\ \gamma \mathrm{M1} &= 1.1 \\ \Delta T &= 35 \quad\text{(K)} \end{aligned}$$

lamb or lambda_ gives λ (lambda is a Python keyword). To turn the prefix split off, use # hc: greek_prefixes = False.

Accents, primes and stars#

Asked in #132.

V_dot = 2.5      # m³/s
x_bar = 12.0
f_prime_c = 30   # MPa
M_star = 85      # kN m
Output (MarkdownRenderer)
\[\begin{aligned} \dot{V} &= 2.5 \quad\text{(m³/s)} \\ \bar{x} &= 12 \\ f'_{c} &= 30 \quad\text{(MPa)} \\ M^{*} &= 85 \quad\text{(kN m)} \end{aligned}\]
$$\begin{aligned} \dot{V} &= 2.5 \quad\text{(m³/s)} \\ \bar{x} &= 12 \\ f'_{c} &= 30 \quad\text{(MPa)} \\ M^{*} &= 85 \quad\text{(kN m)} \end{aligned}$$

Any symbol for any name#

Asked in #101 and #132.

When a Python name can’t spell the symbol, map it to a display spec with the symbols field. The spec syntax is the same in every renderer:

# hc: symbols = {"N_plRd": "N_{pl,Rd}", "V_flow": "dot(V)", "dH": "ΔH^{*}"}
N_plRd = 1250
V_flow = 2.5
dH = 4.2
Output (MarkdownRenderer)
\[\begin{aligned} N_{\mathrm{pl,Rd}} &= 1250 \\ \dot{V} &= 2.5 \\ \Delta H^{*} &= 4.2 \end{aligned}\]
$$\begin{aligned} N_{\mathrm{pl,Rd}} &= 1250 \\ \dot{V} &= 2.5 \\ \Delta H^{*} &= 4.2 \end{aligned}$$

Set it for a session with handcalcs.set_option("symbols", {...}).

Raw LaTeX for a symbol#

For markup the spec syntax can’t express (\tilde{u}, \mathcal{F}), register a name:pre rule. See Custom symbols with a rule.

Keep a name as text#

Asked in #90.

Greek names become letters. To keep some names as upright text, replace their identifier in a name:pre rule:

class UprightMarkdownRenderer(MarkdownRenderer):
    """Names that must not become Greek letters or subscripts."""

    UPRIGHT = {"psi", "ksi", "load_case_1"}
@UprightMarkdownRenderer.register("name:pre")
def keep_upright(renderer, node, base_context):
    if node.identifier in renderer.UPRIGHT:
        text = node.identifier.replace("_", "\\_")
        node.identifier = f"\\mathrm{{{text}}}"
    return node
psi = 14.7
A = 3.5
F = psi * A
beta = 0.8
Output (UprightMarkdownRenderer)
\[\begin{aligned} \mathrm{psi} &= 14.7 \\ A &= 3.5 \\ F &= \mathrm{psi} \cdot A = 14.7 \cdot 3.5 = 51.45 \\ \beta &= 0.8 \end{aligned}\]
$$\begin{aligned} \mathrm{psi} &= 14.7 \\ A &= 3.5 \\ F &= \mathrm{psi} \cdot A = 14.7 \cdot 3.5 = 51.45 \\ \beta &= 0.8 \end{aligned}$$

No subscripts at all#

Asked in #97.

___ (three underscores) is a literal underscore. A name:pre rule can apply that to every name:

@NoSubscriptMarkdownRenderer.register("name:pre")
def literal_underscores(renderer, node, base_context):
    # '___' is the naming convention for a literal underscore.
    node.identifier = node.identifier.replace("_", "___")
    return node
load_case_1 = 3
M_max = load_case_1 * 2
Output (NoSubscriptMarkdownRenderer)
\[\begin{aligned} \mathrm{load\_case\_1} &= 3 \\ \mathrm{M\_max} &= \mathrm{load\_case\_1} \cdot 2 = 3 \cdot 2 = 6 \end{aligned}\]
$$\begin{aligned} \mathrm{load\_case\_1} &= 3 \\ \mathrm{M\_max} &= \mathrm{load\_case\_1} \cdot 2 = 3 \cdot 2 = 6 \end{aligned}$$

Sigma for sum, and your own function names#

Asked in #169 and #194.

The LaTeX and Markdown renderers look function names up in MATH_FUNCS. Add entries in a subclass. A function that isn’t in the table is shown by its name:

class SigmaMarkdownRenderer(MarkdownRenderer):
    """sum(...) as a capital sigma, and a project function shown by its textbook name."""

    MATH_FUNCS = MarkdownRenderer.MATH_FUNCS | {
        "sum": "\\sum",
        "interp_table": "\\operatorname{interp}",
    }
loads = [1.5, 2.0, 3.5]
P = sum(loads)
k = interp_table(2.5)
Output (SigmaMarkdownRenderer)
\[\begin{aligned} \mathrm{loads} &= [1.5, 2, 3.5] \\ P &= \sum\left(\mathrm{loads}\right) = \sum\left([1.5, 2, 3.5]\right) = 7 \\ k &= \operatorname{interp}\left(2.5\right) = 0.8 \end{aligned}\]
$$\begin{aligned} \mathrm{loads} &= [1.5, 2, 3.5] \\ P &= \sum\left(\mathrm{loads}\right) = \sum\left([1.5, 2, 3.5]\right) = 7 \\ k &= \operatorname{interp}\left(2.5\right) = 0.8 \end{aligned}$$

The same table gives ln for log: MATH_FUNCS | {"log": "\\ln"}.

Times sign for multiplication#

Asked in #163.

class TimesMarkdownRenderer(MarkdownRenderer):
    """× instead of · for multiplication."""

    OPERATOR_SYMBOLS = MarkdownRenderer.OPERATOR_SYMBOLS | {"mult_op": "\\times"}
a = 2
b = 3
c = a * b
Output (TimesMarkdownRenderer)
\[\begin{aligned} a &= 2 \\ b &= 3 \\ c &= a \times b = 2 \times 3 = 6 \end{aligned}\]
$$\begin{aligned} a &= 2 \\ b &= 3 \\ c &= a \times b = 2 \times 3 = 6 \end{aligned}$$

Each operator has a key: add_op, sub_op, mult_op, div_op, pow_op, the comparisons and more. See Operator symbols.

Cube roots, factorials and infinity#

Asked in #225, #96 and #166.

cbrt is typeset as a cube root, factorial as n!, and an infinite value as ∞:

V = 27.0
a = cbrt(V)
n = 4
ways = factorial(n)
more = factorial(n + 1)
limit = float('inf')
Output (MarkdownRenderer)
\[\begin{aligned} V &= 27 \\ a &= \sqrt[3]{V} = \sqrt[3]{27} = 3 \\ n &= 4 \\ \mathrm{ways} &= n! = 4! = 24 \\ \mathrm{more} &= \left(n + 1\right)! = \left(4 + 1\right)! = 120 \\ \mathrm{limit} &= \infty \end{aligned}\]
$$\begin{aligned} V &= 27 \\ a &= \sqrt[3]{V} = \sqrt[3]{27} = 3 \\ n &= 4 \\ \mathrm{ways} &= n! = 4! = 24 \\ \mathrm{more} &= \left(n + 1\right)! = \left(4 + 1\right)! = 120 \\ \mathrm{limit} &= \infty \end{aligned}$$

math.cbrt needs Python 3.11 or later. np.cbrt works too.

Functions from math and numpy#

Asked in #127 and #99.

Common functions are typeset as operators and radicals. min and max show their arguments:

a = sqrt(16)
b = sin(pi / 6) + cos(0)
c = log(10)
d = max(a, b, c)
Output (MarkdownRenderer)
\[\begin{aligned} a &= \sqrt{16} = 4 \\ b &= \sin\left(\frac{\pi}{6}\right) + \cos\left(0\right) = \sin\left(\frac{3.1416}{6}\right) + \cos\left(0\right) = 1.5 \\ c &= \operatorname{log}\left(10\right) = 2.3026 \\ d &= \operatorname{max}\left(a, b, c\right) = \operatorname{max}\left(4, 1.5, 2.3026\right) = 4 \end{aligned}\]
$$\begin{aligned} a &= \sqrt{16} = 4 \\ b &= \sin\left(\frac{\pi}{6}\right) + \cos\left(0\right) = \sin\left(\frac{3.1416}{6}\right) + \cos\left(0\right) = 1.5 \\ c &= \operatorname{log}\left(10\right) = 2.3026 \\ d &= \operatorname{max}\left(a, b, c\right) = \operatorname{max}\left(4, 1.5, 2.3026\right) = 4 \end{aligned}$$

np.sqrt, np.sin and the other numpy functions are typeset the same way.

:= (or anything else) between the steps#

handcalcs.set_option("assign", ":=")   # ASCII and HTML renderers

The LaTeX and Markdown renderers also read assign, but it ends up inside math, so give it LaTeX: handcalcs.set_option("assign", "\\coloneqq") (needs the mathtools package in LaTeX documents).

A narrower indent in ASCII output#

handcalcs.set_option("indent", "  ")

This works for the ASCII and base renderers; see the caution for LaTeX and Markdown.

Next: Units.