Markdown renderer#

MarkdownRenderer produces Markdown text with LaTeX math in $$ delimiters. It is the default for %%render in Jupyter, and works anywhere Markdown is shown with MathJax or KaTeX.

from handcalcs.renderers import MarkdownRenderer
## Bolt group
# Shear is shared equally between the bolts.
n = 4  # bolts
V_f = 120  # kN
V_b = V_f / n
if V_b <= 40:
    status = "OK"
Output (MarkdownRenderer)

Bolt group

Shear is shared equally between the bolts.

\[\begin{aligned} n &= 4 \quad\text{(bolts)} \\ V_{f} &= 120 \quad\text{(kN)} \\ V_{b} &= \frac{V_{f}}{n} = \frac{120}{4} = 30 \end{aligned}\]

Since \(\bigl(V_{b} \le 40\bigr) \rightarrow \bigl(30 \le 40\bigr)\) is True:

\[\begin{aligned} \mathrm{status} &= \text{"OK"} \end{aligned}\]
# Bolt group

Shear is shared equally between the bolts.

$$\begin{aligned} n &= 4 \quad\text{(bolts)} \\ V_{f} &= 120 \quad\text{(kN)} \\ V_{b} &= \frac{V_{f}}{n} = \frac{120}{4} = 30 \end{aligned}$$

Since $\bigl(V_{b} \le 40\bigr) \rightarrow \bigl(30 \le 40\bigr)$ is True:

> $$\begin{aligned} \mathrm{status} &= \text{"OK"} \end{aligned}$$

The raw output:

Output (MarkdownRenderer)
# Bolt group

Shear is shared equally between the bolts.

$$\begin{aligned} n &= 4 \quad\text{(bolts)} \\ V_{f} &= 120 \quad\text{(kN)} \\ V_{b} &= \frac{V_{f}}{n} = \frac{120}{4} = 30 \end{aligned}$$

Since $\bigl(V_{b} \le 40\bigr) \rightarrow \bigl(30 \le 40\bigr)$ is True:

> $$\begin{aligned} \mathrm{status} &= \text{"OK"} \end{aligned}$$

A fuller example#

Headings and prose, a parameter grid, a long equation broken with -m, a governing branch, and a pass/fail check, all in one calculation:

## Steel beam
# Lateral-torsional buckling resistance of a simply supported span.
# hc: -h -c 3
E = 200000     # MPa
G = 77000      # MPa
L = 6000       # mm
I_y = 160e6    # mm⁴
J = 133e3      # mm⁴
C_w = 200e9    # mm⁶
### Elastic critical moment
M_u = pi / L * sqrt(E * I_y * G * J + (pi * E / L)**2 * I_y * C_w)  # hc: -m
M_p = 285e6    # N mm
if M_u > 0.67 * M_p:
    M_r = 1.15 * 0.9 * M_p * (1 - 0.28 * M_p / M_u)  # hc: -m
else:
    M_r = 0.9 * M_u
M_f = 210e6    # N mm
M_f <= M_r     # hc: --check
Output (MarkdownRenderer)

Steel beam

Lateral-torsional buckling resistance of a simply supported span.

\[\begin{aligned} E &= 200000 \; \text{(MPa)} \qquad &G &= 77000 \; \text{(MPa)} \qquad &L &= 6000 \; \text{(mm)} \\ I_{y} &= 1.6 \times 10^{8} \; \text{(mm⁴)} \qquad &J &= 1.33 \times 10^{5} \; \text{(mm⁴)} \qquad &C_{w} &= 2 \times 10^{11} \; \text{(mm⁶)} \end{aligned}\]

Elastic critical moment

\[\begin{aligned} M_{u} &= \frac{\pi}{L} \cdot \sqrt{E \cdot I_{y} \cdot G \cdot J + \left(\frac{\pi \cdot E}{L}\right)^{2} \cdot I_{y} \cdot C_{w}} = \frac{3.1416}{6000} \cdot \sqrt{200000 \cdot 1.6 \times 10^{8} \cdot 77000 \cdot 1.33 \times 10^{5} + \left(\frac{3.1416 \cdot 200000}{6000}\right)^{2} \cdot 1.6 \times 10^{8} \cdot 2 \times 10^{11}} = 4.3134 \times 10^{8} \\ M_{p} &= 2.85 \times 10^{8} \quad\text{(N mm)} \end{aligned}\]

Since \(\bigl(M_{u} \gt 0.67 \cdot M_{p}\bigr) \rightarrow \bigl(4.3134 \times 10^{8} \gt 0.67 \cdot 2.85 \times 10^{8}\bigr)\) is True:

\[\begin{aligned} M_{r} &= 1.15 \cdot 0.9 \cdot M_{p} \cdot \left(1 - \frac{0.28 \cdot M_{p}}{M_{u}}\right) = 1.15 \cdot 0.9 \cdot 2.85 \times 10^{8} \cdot \left(1 - \frac{0.28 \cdot 2.85 \times 10^{8}}{4.3134 \times 10^{8}}\right) = 2.404 \times 10^{8} \end{aligned}\]
\[\begin{aligned} M_{f} &= 2.1 \times 10^{8} \quad\text{(N mm)} \\ M_{f} \le M_{r} &= 2.1 \times 10^{8} \le 2.404 \times 10^{8}\quad{\color{green}\checkmark} \end{aligned}\]
# Steel beam

Lateral-torsional buckling resistance of a simply supported span.

$$\begin{aligned} E &= 200000 \; \text{(MPa)} \qquad &G &= 77000 \; \text{(MPa)} \qquad &L &= 6000 \; \text{(mm)} \\ I_{y} &= 1.6 \times 10^{8} \; \text{(mm⁴)} \qquad &J &= 1.33 \times 10^{5} \; \text{(mm⁴)} \qquad &C_{w} &= 2 \times 10^{11} \; \text{(mm⁶)} \end{aligned}$$

## Elastic critical moment

$$\begin{aligned} M_{u} &= \frac{\pi}{L} \cdot \sqrt{E \cdot I_{y} \cdot G \cdot J + \left(\frac{\pi \cdot E}{L}\right)^{2} \cdot I_{y} \cdot C_{w}} = \frac{3.1416}{6000} \cdot \sqrt{200000 \cdot 1.6 \times 10^{8} \cdot 77000 \cdot 1.33 \times 10^{5} + \left(\frac{3.1416 \cdot 200000}{6000}\right)^{2} \cdot 1.6 \times 10^{8} \cdot 2 \times 10^{11}} = 4.3134 \times 10^{8} \\ M_{p} &= 2.85 \times 10^{8} \quad\text{(N mm)} \end{aligned}$$

Since $\bigl(M_{u} \gt 0.67 \cdot M_{p}\bigr) \rightarrow \bigl(4.3134 \times 10^{8} \gt 0.67 \cdot 2.85 \times 10^{8}\bigr)$ is True:

> $$\begin{aligned} M_{r} &= 1.15 \cdot 0.9 \cdot M_{p} \cdot \left(1 - \frac{0.28 \cdot M_{p}}{M_{u}}\right) = 1.15 \cdot 0.9 \cdot 2.85 \times 10^{8} \cdot \left(1 - \frac{0.28 \cdot 2.85 \times 10^{8}}{4.3134 \times 10^{8}}\right) = 2.404 \times 10^{8} \end{aligned}$$

$$\begin{aligned} M_{f} &= 2.1 \times 10^{8} \quad\text{(N mm)} \\ M_{f} \le M_{r} &= 2.1 \times 10^{8} \le 2.404 \times 10^{8}\quad{\color{green}\checkmark} \end{aligned}$$

Where it works#

  • Jupyter (Lab, Notebook, VS Code): %%render displays it with IPython.display.Markdown.

  • MyST / Sphinx with the dollarmath and amsmath extensions, and Quarto.

  • GitHub and other sites that render $$ math with MathJax or KaTeX.

Output structure#

Source

Markdown

## Heading

# Heading (one # fewer than the source)

Comment lines

A paragraph

import ...

A code span

A run of calculation lines

One $$\begin{aligned} ... \end{aligned}$$ on a single line

A parameter grid

Its own aligned group

# hc: -b

An &nbsp; spacer paragraph

if / for body

A blockquote, one > per nesting level

The math is the same as the LaTeX renderer’s (Greek letters, subscripts, fractions, \sqrt), with two changes for MathJax and KaTeX: < and > are written as \lt and \gt (a raw < can be mistaken for an HTML tag), and text inside math (comments, strings) is wrapped in \text{}.

Each math group is kept on one physical line because Jupyter extracts math from the raw text before parsing the Markdown; a group spread over several >-quoted lines would carry the > characters into the LaTeX.

Customizing#

MarkdownRenderer subclasses LatexRenderer and is controlled by the same class attributes (MATH_OPEN, ROW_SEP, BLOCK_PREFIX, LINE_BREAK, OPERATOR_SYMBOLS, …). See LaTeX and Markdown tokens.

Source#

src/handcalcs/renderers/markdown.py