In structural and mechanical engineering, the calculation report is the document that matters most and is read most carefully. Before a beam is ordered or a bracket is machined, someone else, often a senior engineer or an independent checker, goes through the calculation line by line. They need to see the formula, the numbers that went into it and the result, with units, in an order they can follow. If a number cannot be traced, the calculation is sent back.
This post is about producing that document from code: why the format matters, what engineers use for it today, and a complete, runnable example that turns a Python calculation into a typeset PDF without a TeX installation.
What a checker needs to see
Engineers call it a "hand calc" because it reads the way it was written on paper for a century: for every quantity, the general formula, then the same formula with the numbers substituted, then the result and its unit. Under limit state design, and codes such as the Eurocodes that are built on it, each check also ends in a comparison: the action effect against the resistance, or the deflection against a limit.
Three things make such a report easy to check:
- The substitution step is shown. A result written as M = 108 kNm only says what the author got; M = wL²/8 = 24 · 6²/8 = 108 kNm lets the reader repeat it in their head.
- Every input is stated once, with its unit and source. Section properties come from a table, loads from the load take-down, material strength from the standard.
- The mathematics is typeset. Fractions, subscripts and Greek letters written as plain text (M_Rd = W_el*f_y/gamma_M0) are easy to misread, and misreading is exactly what checking is meant to prevent.
How it is done today
Most offices use one of four approaches, and each solves part of the problem:
- Spreadsheets. Fast and familiar, but the formulas live inside cells. The printout shows results, so the checker has to click through the file or trust it.
- Engineering math worksheets. Mathcad made the "live worksheet" the standard for typeset calculations; SMath Studio is a lighter alternative, and Calcpad writes its results as HTML reports that can be printed or saved as Word documents. They read well, but the calculation lives in a proprietary file that is hard to review in version control.
- Calculation platforms. Web services with libraries of checked design templates, such as ClearCalcs (US$79 to $149 a month per plan at the time of writing), produce branded PDF reports. They are worth their price for the standard members they cover; anything outside the library still needs another tool.
- Code. More engineers now write their calculations in Python, often in Jupyter notebooks: the calculation is plain text, it can be reviewed in Git, rerun with new inputs and tested. What code usually lacks is the readable report.
handcalcs: Python that writes the hand calc
handcalcs closes that gap. It is an open-source Python library (Apache 2.0, about 5,800 stars on GitHub) that reads ordinary Python assignments and renders them as LaTeX, in the hand-calc order: symbolic formula, substituted numbers, result. In a notebook, a cell that starts with %%render is displayed that way. Outside a notebook, the @handcalc decorator returns the LaTeX as a string, together with the computed values, which is what a script needs. It also works with SymPy expressions and with unit libraries such as forallpeople and pint.
The catch is the last step. LaTeX is source code for a document; turning it into a PDF needs a TeX distribution, and the usual one, TeX Live, installs everything by default, which the TeX Users Group puts at over 7 GB. That is fine on one workstation and awkward everywhere else: on a colleague's laptop, on a build server that should produce the report on every change, or for an AI assistant running in the cloud that cannot install anything at all.
A complete example
Here is a small but real check: a simply supported steel roof beam, an IPE 300 in grade S355 spanning 6 m under a uniform load, checked for bending at midspan and for deflection under the service load. The physics is textbook Euler-Bernoulli beam theory: the maximum bending moment is wL²/8, the elastic resistance is the section modulus times the yield strength, and the midspan deflection is 5wL⁴/384EI, with I the second moment of area.
The script below does the calculation with handcalcs, places the rendered steps in a short LaTeX report and sends it to Pressa, which compiles it and returns a link to the PDF. It needs Python with pip install handcalcs requests and nothing else.
# A simply supported steel beam under a uniform load, checked for bending
# and deflection, written once in Python and typeset as a PDF by Pressa.
import os
import requests
from handcalcs.decorator import handcalc
@handcalc(override="long", precision=2)
def bending(w, L, W_el, f_y, gamma_M0):
M_Ed = w * L**2 / 8 # kNm, design moment at midspan
M_Rd = W_el * f_y / gamma_M0 / 1e6 # kNm, elastic resistance
eta = M_Ed / M_Rd # utilisation
return locals()
@handcalc(override="long", precision=2)
def deflection(w_k, L, E, I_y):
delta = 5 * w_k * (L * 1000)**4 / (384 * E * I_y) # mm
delta_lim = L * 1000 / 250 # mm
return locals()
m_latex, m = bending(w=24.0, L=6.0, W_el=557e3, f_y=355, gamma_M0=1.0)
d_latex, d = deflection(w_k=16.0, L=6.0, E=210000, I_y=8356e4)
verdict = "OK" if m["eta"] <= 1 and d["delta"] <= d["delta_lim"] else "NOT OK"
document = rf"""
\documentclass[11pt]{{article}}
\usepackage[a4paper,margin=22mm]{{geometry}}
\usepackage{{amsmath,booktabs}}
\usepackage[T1]{{fontenc}}
\usepackage{{lmodern}}
\setlength{{\parindent}}{{0pt}}
\begin{{document}}
{{\Large\bfseries Calculation B-01: roof beam, grid line C}}\\[2pt]
Simply supported IPE 300, S355, span 6.0 m, uniform load.
\section*{{Input}}
\begin{{tabular}}{{lll}}
\toprule
Design load (ULS) & $w$ & 24.0 kN/m \\
Service load (SLS) & $w_k$ & 16.0 kN/m \\
Span & $L$ & 6.0 m \\
Elastic section modulus & $W_{{el}}$ & 557 cm$^3$ \\
Second moment of area & $I_y$ & 8356 cm$^4$ \\
Yield strength & $f_y$ & 355 MPa \\
\bottomrule
\end{{tabular}}
\section*{{Bending}}
{m_latex}
\section*{{Deflection}}
{d_latex}
\section*{{Result}}
Utilisation in bending {m["eta"]:.2f}; deflection {d["delta"]:.1f} mm against a limit of
{d["delta_lim"]:.1f} mm (L/250). \textbf{{{verdict}}}.
\end{{document}}
"""
response = requests.post(
os.environ.get("PRESSA_URL", "https://api.pressa.dev") + "/api/v1/compile",
json={"latex": document},
timeout=60,
)
response.raise_for_status()
pdf_url = response.json()["pdf_url"]
with open("beam-calculation.pdf", "wb") as f:
f.write(requests.get(pdf_url, timeout=60).content)
print("Saved beam-calculation.pdf")
Running it writes beam-calculation.pdf. This is the page it produced, unedited:
Every line can be checked by hand: 24 kN/m over 6 m gives 108 kNm; 557 cm³ at 355 MPa resists 197.7 kNm, a utilisation of 0.55; the service load deflects the beam 15.4 mm against a limit of 24 mm. The handcalcs output for the first of those steps is plain LaTeX, which is also what makes the report easy to adjust:
M_{Ed} &= w \cdot \frac{ \left( L \right) ^{ 2 } }{ 8 } \\
&= 24.00 \cdot \frac{ \left( 6.00 \right) ^{ 2 } }{ 8 } \\
&= 108.00 \; \;\textrm{(kNm, design moment at midspan)}Why the source should be code
Once the calculation is a script, the report stops being a separate document that can drift from the numbers:
- Change an input, get a new report. A heavier roof build-up or a longer span is one edited number and one run, and every step that depends on it is recalculated and re-typeset.
- Review it like code. The calculation and the report template sit in Git. A pull request shows exactly which formula or input changed between revision B and revision C.
- Build it automatically. The same script can run on every change in a CI pipeline and attach the PDF to the build, because it needs only an HTTP call, not a TeX installation on the runner.
- Batch it. Twenty beams on a floor plan are a loop over a table of spans and loads, and twenty consistent pages.
With an AI assistant
The same pattern works when an assistant writes the code. Ask Claude, ChatGPT or Cursor to "write this check in Python with handcalcs and compile the report", and an assistant connected to Pressa through its MCP server, or one that can run the script, returns the PDF instead of a code listing. If it can only open web addresses, the link compile takes short documents in the address itself. When the LaTeX does not compile, Pressa answers with the error and a suggested fix rather than a raw TeX log, so the assistant can correct the source and try again on its own.
The format matters even more here. An assistant can make an arithmetic or modelling mistake that looks plausible; a report that shows every substitution is how an engineer catches it. The tool writes and typesets the calculation, and the engineer who signs it remains responsible for it.
What this does not do
- Neither handcalcs nor Pressa knows the design codes. They show the mathematics you wrote; whether it is the right check, with the right partial factors and the right deflection limit for the project and its national annex, is engineering judgement. L/250 in the example is a common limit, not a universal one.
- Pressa compiles documents; it does not store your calculations. The compiled PDF is kept only so you can download it, for an hour without an account and a day with one. See the limits for pages and sizes per plan.
- Without an account you can compile 3 documents a day, and a free account gives 50 a month, which is plenty to try this on a real calculation.
The script above runs as it is. Copy it, change the section and the loads, and read the API documentation for everything else the compile call accepts.
