Appearance
The Calc sheet
The Calc tab (top bar, next to Footings) is a MathCAD-style working document, freehand: click anywhere on the page and type. It is not a form. There is no "add block" button, no name field beside an expression field, no dialog. A line you finish typing is recognised and typeset live; the line your caret is on stays plain text so you are never fighting a re-render while you type.
It exists for the calculations CivilKit doesn't do for you - fire protection, vibration, temporary works, a hand check against the solver's own number - written next to live references into the model, so a value in your working updates when the model does.
Gated behind a setting
The Calc tab is pre-release. Enable it in File ▸ Settings… ▸ Experimental ▸ Calculation document.
Quick start
Open the Calc tab and type:
Bearing check, pad F1.
phi: 0.9
fy: 280
Zex: 1230
phi.Ms: phi * fy * Zex / 1000Press Enter after each line. As soon as you move off a finished line it typeset itself:
φ := 0.9 = 0.9
fy := 280 = 280
Zex := 1230 = 1230
φ.Ms := φ · fy · Zex / 1000 = 309.96 = 0.9 * 280 * 1230 / 1000That's the whole idiom. name: value defines something; a name used later reads it back; a finished line renders as maths with the substituted working shown alongside, exactly the way you'd derive it by hand.
The line grammar at a glance
Every line is one of a small number of shapes. The parser decides which by looking at how the line starts - so nothing needs a mode switch or a toolbar button. This table is the whole language:
| You type | Becomes | Section |
|---|---|---|
name: expr or name := expr | a calculation (formula, substitution, result) | Defining a variable |
a bare number, V.R: 45 | a given (boxed as an input) | Defining a variable |
# text, ## text, ### text | a heading (large / sub / small) | Headings and structure |
| any other line | prose - free text, typed as-is | Headings and structure |
... | note (trailing single bar) | adds a clause-column note to that line | The clause column |
|| cell | cell | cell (leading double bar) | a table row | Tables |
$$ expr $$ | a display equation (boxed, centred) | Display equations |
!section name=… d=… bf=… … | an auto-drawn section sketch | Section sketches |
!result name | caption | a boxed final answer | Result boxes |
!bmd 3, !contour mx | a diagram or contour from the model | Diagrams and contours |
t := 10mm..40mm | a range - every dependent line computes once per point | Ranges and parameter studies |
!plot Ms for t, !table Ms, util for t | a plot or parameter table of that range | Ranges and parameter studies |
@ | opens the reference picker (your vars + model values) | Referencing the model |
Inside prose and table cells you can also use a little inline formatting - see Inline formatting.
Defining a variable
Any line shaped name: value or name: expression is a calculation:
kt: 0.45
phi.Nt.fire: phi.Nt * kt- MathCAD's own
:=also works -kt := 0.45is identical tokt: 0.45. - Dotted names are fine -
phi.Nt.fire,M.zcat,q.applied- and print with the trailing part as a subscript:φ.Nt.firebecomes φ_Nt.fire. - Greek letters spell themselves: type
phi,alpha,beta,gamma,delta,lambda,mu,rho,sigma,tau,theta,psi,omega,eta,epsilon,nuorpiand it typesets as the symbol. Everything else renders upright as a name. - A literal value is a given and boxes itself.
V.R: 45is highlighted because 45 is a number you typed in, not something derived.A: b * dis not boxed - it's computed from other values. You don't mark this yourself; the sheet can tell the difference. - Only lines above can be referenced. A calc block cannot use a name defined further down the sheet - that's what keeps a sheet acyclic: no circular references, ever. When the same name is defined more than once, a reference (and a result box) reads the nearest definition above it, the way MathCAD re-defines a variable down the page - so one sheet can carry several worked examples that each reuse
phiorfywithout colliding.
Functions
The core language is + - * / ^ and parentheses, plus a fixed, whitelisted set of functions - not a general programming language, and not eval: every function is a known, deterministic operation, an unknown name is always an error, and there is no way to define a new one from inside a sheet.
B.req: sqrt(N / q.allow)
util: min(N / q.allow / (B * B), 1.5)
ratio.deg: asin(0.5) * 180 / pitypesets as
B.req := √(N/q.allow) = 2.28
util := min(N/q.allow/(B·B), 1.5) = 0.903
ratio.deg := sin⁻¹(0.5) · 180/π = 30A function wrapping a live @ reference typesets correctly too - sqrt({member:1.phiNt} / 1000) renders as a proper nested radical over the reference, not broken text.
| function | does | notes |
|---|---|---|
sqrt(x) | square root | |
abs(x) | absolute value | |
floor(x), ceil(x) | round down / up | |
round(x), round(x, n) | round to the nearest integer, or to n decimal places | |
min(a, b, ...), max(a, b, ...) | smallest / largest of two or more values | takes 2 or more arguments |
sin(x), cos(x), tan(x) | trig functions | radians - multiply degrees by pi / 180 first |
asin(x), acos(x), atan(x) | inverse trig | result is in radians |
ln(x) | natural log | |
log(x) | log base 10 | MathCAD's own split: ln is natural, log is base 10 |
exp(x) | e^x | |
pi | 3.14159… | a constant, not a function - used bare, no parentheses |
Units by function, following the same logic as +/-/* above:
abs,floor,ceil,roundkeep the unit - the value's physical dimension hasn't changed, only its sign or precision.min/maxkeep the unit when every argument shares one, the same rule+/-already follow; otherwise it drops.sqrt, the trig functions,ln/log/expalways drop the unit - each of these changes the physical dimension of its input (or requires a dimensionless one), and a plausible-looking wrong unit is worse than none.
Reserved names. sqrt, min, max, pi and every function above are reserved - you can define a variable with one of those names, but a bare use of the name in an expression always means the built-in, never your variable. Simplest just to avoid naming a variable sqrt or pi.
What's still not there: ^ still needs a literal exponent - x ^ 2 works, x ^ n where n is a variable does not; sqrt(x) (or x ^ 0.5) is how you write a square root either way.
Comparisons and branching
<, <=, >, >=, ==, != compare two values, and if(condition, a, b) picks between two results - the piece a real calc sheet needs for a loss-of-contact check, a code-limit check, or anything else that reads "if this, use that":
e: M / N
q.max: if(e <= B/6, N/A * (1 + 6*e/B), 2*N/(3*(B/2-e)))typesets as
e := M/N = 0.42
q.max := if(e ≤ B/6, N/A · (1 + 6·e/B), 2·N/(3·(B/2-e))) = 187.3if() evaluates both branches before picking one - there's no lazy/short-circuit evaluation, so neither branch can divide by a value that would be zero specifically in the case that branch is meant to avoid.
and(a, b, ...), or(a, b, ...) and not(x) combine conditions - and/or take two or more arguments:
ok: if(and(e <= B/6, N > 0), 1, 0)A bare comparison is a pass/fail check. A line like flange.compact: lambda.ef <= lambda.ep resolves to a true/false result, and prints as a verdict - the substituted comparison, an arrow, and a labelled chip (green when it holds) - rather than a bare 1. Give it a clause note and that note becomes the chip's label:
flange.compact: lambda.ef <= lambda.ep | Table 5.2renders the check with a green TABLE 5.2 ✓ chip on the right, which is what a reviewer scans down the margin for.
Comparisons never chain. 0 < x < 10 is a parse error, not silently read left-to-right - 0 < x < 10 reads as a range to a person, but (0 < x) < 10 to most languages that allow it, and those are two different answers from the same six characters. Write it as and(x > 0, x < 10) instead.
Units. A comparison's result is a fact (true/false), never a physical quantity, so it never carries a unit regardless of its operands' units. if() keeps the unit only when both branches agree - same rule as min/max - and the condition's own unit is irrelevant to the result. and/or/not always drop the unit, same as a comparison.
if, and, or, not are reserved names, same as every function in the table above.
Headings and structure
Markdown's own convention, because it's what people already type:
# Wind Design Actions
## 1) Derivation of Design Wind Speeds
### Step 1 - reference speed# gives you a large underlined heading, ## a sub-heading, ### a smaller one. A plain line with neither a heading mark nor a name: shape is prose - free text, exactly as typed.
Inline formatting
Inside prose lines and table cells, a deliberately small subset of Markdown works - just enough to emphasise a section name or drop a symbol into a sentence, never a second document format to learn:
| You type | Renders |
|---|---|
**460UB82.1** | 460UB82.1 (bold) |
*governing* | governing (italic) |
$\phi M_s$ | inline typeset maths (a single $…$ span) |
{member:1.phiNt} | a live model reference (inserted by the @ picker) |
Note the difference between the two dollar forms: a single $…$ is inline maths inside a line of prose or a table cell; a double $$…$$ wrapping a whole line is a display equation block. Everything else in prose is taken literally.
Tables
A line that starts with a doubled bar || is a table row; its cells are separated by single |:
|| Geometric data (OneSteel HRSSP, 4th Edition) | Value | Unit
|| d | 460 | mm
|| bf | 191 | mm
|| Sx | 1840e3 | mm^3Consecutive || rows join into one table automatically - the first row reads as the header. Cells may hold plain text, inline formatting (**bold**, $math$), or a {token} model reference, so a "Value" column can carry live numbers rather than typed-in ones.
The doubled bar is deliberate: a single | is already the clause-column separator, so an ordinary Markdown pipe table would be ambiguous. || is the table's own marker; a lone || with no cell after it stays prose rather than becoming an empty one-cell row.
Display equations
Wrap a whole line in $$ … $$ to typeset it as a boxed, centred display equation - the formula on its own, before you substitute numbers into it, the way a worked example states the governing equation first and evaluates it below:
$$ lambda.ef := ((bf - tw)/(2*tf)) * sqrt(fy/250) $$ | Clause 5.2.2The $$ wrapper must be the whole body of the line (after any clause note is split off) - a $$ appearing inside a sentence stays prose, so a line that reads as words never silently becomes maths. A trailing | note still attaches, so the boxed equation can carry its own clause reference.
This is distinct from a name: expr calc line, which shows formula → substitution → result on one line. A $$…$$ block shows only the symbolic form - use it to present an equation, then follow it with the name: expr lines that evaluate it.
Section sketches
!section draws a structural section to scale from typed dimensions - the small drawing that anchors a "geometric data" table on a steel calc:
!section name=460UB82.1 d=460 bf=191 tf=16 tw=9.9dandbfare required (overall depth and flange width); a line missing either falls back to prose rather than drawing a broken shape.tfandtw(flange and web thickness) refine the I/H profile; supply them for a true-to-scale section.name=labels the sketch (e.g. the section designation). Anynamevalue with spaces should be written without them, as one token.
The sketch draws at a scale derived from the dimensions themselves, so a deep beam looks deep and a stocky UC looks square - it is generated from the numbers, not a stock image. Place it just before the matching geometric-data table and the two sit side by side as one "given" panel.
Result boxes
!result promotes a value out of the line flow into the accent box a reviewer looks at first - the governing answer, boxed and captioned:
!result phi.Msx | Design section moment capacity- The first token is the variable name whose value fills the box - it must be defined somewhere above (the box reads the nearest definition above it, so several examples on one sheet each get their own answer).
- The text after
|is the caption shown beside the value. - An optional
unit=sets display text for the unit:!result phi.Msx unit=kNm | ….
If the named value hasn't resolved, the box says so rather than showing a stale or zero number - the same honest-refusal rule the whole feature follows.
The clause column
A trailing | ... on any line becomes the right-hand margin note - the code clause or a short remark, exactly where a reviewer looks for it on a printed calc sheet:
M.zcat: 0.91 | Tbl 4.1
Not in lee zone | Cl 4.4.3| can't appear inside an expression, so there's no escaping to remember - just add it at the end of the line. (The clause note is split off before the rest of the line is parsed, so it works on any line kind - a calc, a display equation, a !section, a verdict check - not just definitions.)
Referencing the model: @
Type @ anywhere and a search opens over two things: your own variables defined above the caret, and live values from the model - footing results, member actions and capacities, node reactions and displacements.
q.applied: @Typing narrows it, ↑/↓ moves the selection, Enter or Tab accepts. Picking a model value is two steps: first the entity (a footing mark, M12, N5), then that entity's field. The result is inserted as a live reference - not a copied number - so if the model changes and you re-solve, the value in your sheet changes with it.
Available today:
| domain | fields |
|---|---|
| footing | B, L, D, cover, q_max, q_allow, util_bearing, util_punching, A_st_x, A_st_y |
| member (actions) | N, Vy, Vz, T, My, Mz - the six internal actions, PEAK along the member |
| member (capacities) | phiMs, phiMb, phiMsy (minor-axis), phiNt, phiVv, Ze, fy, util (governing utilisation) |
| node | Rx, Ry, Rz (reactions), Mx, My (moment reactions), ux, uy, uz (displacements) |
Units are handled for you - a member capacity reads in kN/kN·m, not the raw SI newtons the solver works in, and the unit shown is always the unit of the number next to it. member and node fields need @env (the picker adds this automatically) - that means governing value across every solved load case, sign kept, so a reaction of -28 kN stays negative because the direction matters.
Having both halves on one domain means the check every steel design is can be written in one line, with both sides coming from the engine:
Mz_star := {member:12.Mz@env} | governing major-axis moment
phi_Ms := {member:12.phiMs} | AS 4100 Cl 5.2.1
util := abs(Mz_star) / phi_Ms | demand / capacityAn action is the PEAK ALONG the member, not the value at an end
A portal rafter's worst sagging moment is at neither of its ends, so {member:12.Mz@env} reports the largest magnitude at any station along the member - which is the number a designer quotes. On a model solved without stations it falls back to the end forces and says nothing untrue by doing so.
Play the tutorial (Calc → Play tutorial) to watch this typed out: chapter 3 is a live check against whichever model you have loaded.
A refused check is never a silent zero
If a value hasn't been computed - punching shear that the engine declined to check, a footing that hasn't been designed yet - the reference shows a dashed marker with the reason, never 0.00. That is the one rule this whole feature exists to protect: a stamped calculation must never show a passing number for something that was never actually checked.
Diagrams and contours: !
Type ! the same way you type @ - it opens a list of diagram kinds, and picking one immediately opens the second question (which member, or which plate field):
!bmd 3draws the bending moment along member 3, taken from its solved stations on the governing load case - filled to the axis, drawn on the tension side (the usual convention), the peak value called out where it occurs.
Member diagrams (!kind memberNumber):
| kind | draws | unit |
|---|---|---|
bmd | bending moment along the member | kN·m |
sfd | shear force along the member | kN |
axial | axial force along the member | kN |
defl | deflected shape along the member | mm |
Plate contours (!contour field):
!contour mxdraws the chosen field over the whole plate mesh in plan - one polygon per element region, smoothly shaded rather than a flat block per element. The shading is nodal-averaged and bilinearly interpolated: values are averaged at nodes shared between elements (what any FEA package means by a "nodal" contour), then interpolated across each element using the element's own shape function - so the colour at any point is the value the element would actually report there, not a blur filter. The mesh outline is drawn on top, so a coarse mesh still visibly looks coarse behind the smooth field.
| field | draws | unit |
|---|---|---|
mx | bending moment about the local x-axis | kN·m/m |
my | bending moment about the local y-axis | kN·m/m |
mxy | twisting moment | kN·m/m |
vm | von Mises stress | MPa |
Both diagram kinds pick the governing load case by peak magnitude, the same case a @ reference next to them would resolve against - so a diagram and a number on the same sheet can never quietly come from different solves.
!section and !result also begin with ! but are their own blocks (above) - the diagram list is every ! kind except those two.
Ranges and parameter studies
A calculation sheet answers "is this section adequate". The question an engineer actually asks next is "how thick does it need to be", and answering it by retyping the same sheet at four thicknesses is how a study ends up disagreeing with the check it came from.
So a value can be a range instead of a number:
t := 10mm..40mmThat is 41 points from 10 to 40 mm. A unit written on one end applies to both, so t := 10..40mm means the same thing; two different units (10mm..40kN) have no defensible reading and the sheet says so on the line rather than picking one. n= sets the count (between 2 and 400):
x := 0..10 n=21Every line below the range that depends on it is then computed once per point. Nothing else about the sheet changes: the same expressions, the same units, the same clause notes. The study is the check, evaluated repeatedly - not a second document that can drift from it.
Ask for the result as a plot or as a table:
t := 10mm..40mm
Ze := b * t^2 / 4
Ms := fy * Ze / 1e6
util := M.star / Ms
!plot Ms for t
!table Ms, util for t!plot draws one series per name you list, against the range. The axes fit the data, not a forced zero baseline - a capacity that varies between 180 and 240 kNm should fill the frame, not sit as a flat smear along the top of it. A series that genuinely does not vary is given a nominal band so it stays visible as a horizontal line rather than collapsing onto the frame edge.
!table prints the same series as a parameter table. Long ranges are thinned to 12 rows, spaced evenly and always keeping the first and last point, with a caption saying how many of how many points are shown - so a table can never imply it is the whole series when it is a sample of it.
Reading the answer off it: at
The point of a study is rarely the curve. It is the thickness at which the curve reaches something - so say what that something is:
!plot util for t at 1
!table Ms, util for t at 1The plot gains a dashed line at that level, a marker where the series reaches it, and the sentence itself: util = 1 at t = 33.5 mm. The table marks the first tabulated row past the crossing as the governing row and repeats the same sentence in its caption.
The crossing is not read off the plotted points. A chord across the gap between two of 41 points on a curve that goes as t^2 is wrong in the third figure, which is the figure the sentence quotes. It is found by bisecting the sheet's own arithmetic - re-evaluating the document at each trial value of t, exactly as a plotted point is evaluated - so the number in the callout is the sheet's answer rather than a measurement of a picture of it. Plot and table are handed the same one; they cannot disagree.
Three things it will not do:
- Invent an answer. A level the series never reaches gets its dashed line and no callout, so you can see that the level is nowhere near rather than reading a nearest point as a crossing.
- Hide the second answer. A curve that reaches the level twice has two answers. The first is reported and the caption says how many more there are.
- Extrapolate. The crossing is only ever inside the range you asked for. If it is not in there, widen the range.
The level is read in the unit the series is displayed in: util is dimensionless so at 1 means 1.0, and a series shown in kN·m takes its level in kN·m.
Two rules keep a study honest:
- The range must be defined above the plot that uses it. A sheet reads top to bottom; a plot that reached forward for its parameter would depend on lines the reader has not seen yet.
- A refusal at any point refuses the whole series. If the expression cannot be evaluated at t = 34 mm - a unit mismatch, a root of a negative number, a reference that does not resolve - the plot does not appear with a gap in it. It reports the refusal and names the value of
tthat caused it. A curve with a silent hole in it is a curve an engineer will read straight past.
Arithmetic inside || table cells is still deliberately not supported: a table is a presentation of values computed on the lines above it. !table is how a table gets computed values.
Importing a spreadsheet
Import… on the Calc toolbar reads an .xlsx onto the sheet. The interesting part is not that it reads it - it is what it tells you about it.
It checks your spreadsheet against itself
A workbook caches the value of every formula. That cached value is what prints, what gets pasted into a report, and what a checker signs. It also goes stale the moment somebody edits an input with calculation set to manual, or copies a row and does not notice the reference did not follow.
So every formula that comes across is evaluated here and compared against the value your workbook cached. A disagreement is reported with both numbers:
Moment (B3): the workbook says 45, this sheet computes 56.25 kN·m (20.00%)
That is either a stale workbook or a defect in this importer, and you are the only one who can tell which - so it is reported rather than refused. Where the cause is on our side, the line says so: a disagreement downstream of a unit the importer could not read is labelled as that, so you do not go and re-check a spreadsheet that was right.
The shape it can read
Label in column A, value or formula in column B, unit in column C - the convention nearly every engineering spreadsheet already follows, and the same shape as a calc sheet: one variable per row, its name beside it.
A cell reference becomes the label of the row it points at, so =B3*B2^2/8 arrives as
Moment := UDL*Span^2/8in the names you typed. No cell addresses survive into the sheet - not B3, and not a {xlsx:Sheet1!B7} reference either. A cell address in a document you have signed ties it to a workbook's layout, which is exactly the unreadable reference this sheet exists to avoid.
Rows outside that shape are left out, and the summary says how many.
What it refuses, and why that is the point
A formula this sheet cannot evaluate arrives as a refusal naming its cell, never as the number your workbook cached:
NOT IMPORTED - Lookup (B10) was not imported: it calls VLOOKUP(), which this sheet cannot evaluate=VLOOKUP(...) imported as 437.2 would be a number with no derivation, and on the page it is indistinguishable from one that was derived. The same goes for a formula that joins text, uses a range, reads another sheet, or reaches around a circular reference - each says which of those it was.
Units
A unit column is read when this sheet knows the unit. When it does not - kNm and cm3 are common spreadsheet shorthand that it does not - the line arrives dimensionless and says so, with the workbook's own text in the clause column:
Ze := 1840 | unit "cm3" not understood - add oneIt is deliberately not guessed at. Quietly rewriting cm3 to cm^3 would be the importer assuming the thing it is meant to be flagging, and an import that silently produced unitless lines would defeat the sheet's dimensional algebra while looking like it had worked.
Adding units line by line after an import is tedious. That cost is real, and it is the price of the algebra being worth anything.
What it does not do
- Export. The sheet prints to PDF and into the report; a live-formula Excel export is a separate decision and is not built.
.xls- the pre-2007 binary format is a different file format, not a variant of this one.- More than one sheet at a time. The first worksheet is read and the summary names the others.
- Charts, pivot tables, and defined names used as operands.
Images
Paste an image anywhere on the sheet (screenshot, a phone photo of a trial pit, a scanned manufacturer chart) and it lands as its own line, downscaled automatically so the model file doesn't balloon. It travels with the model exactly like everything else on the sheet.
Saving, printing and the report
- The sheet saves with the model - it's part of the
.sgfile, and it also survives a browser refresh through session autosave. Nothing extra to do. - PDF (top-right of the Calc tab) prints just the sheet on its own - references become plain inline text (
F1.q_max (governing)rather than a coloured chip), diagrams and contours print as vector images, and an unresolved reference keeps its warning in the printed page too. This is a one-off export; it doesn't touch your report's configuration. - The full report can include the sheet as a section - open File ▸ Report, turn on Calculation sheet under Report content, and it appears wherever you've placed it in the report order.
Stale-results banner
The Calc sheet shows a warning when the model has changed since the last solve. Any @ reference pulling a value from the FEM results (reactions, member capacities, footing utilisations) may be out of date. Re-solve the model to clear the banner before trusting the numbers.
Worked example: a section moment capacity to AS 4100
Putting the blocks together - a heading, a section sketch, a given-data table, calc lines with clause notes, a verdict check and a result box - reproduces a textbook worked example:
## Example 1 - Design section moment capacity (460UB82.1, full lateral restraint)
!section name=460UB82.1 d=460 bf=191 tf=16 tw=9.9
|| Geometric data | Value | Unit
|| d | 460 | mm
|| bf | 191 | mm
|| tf | 16 | mm
|| tw | 9.9 | mm
bf: 191
tw: 9.9
tf: 16
fy: 300
lambda.ep: 9 | Table 5.2
lambda.ef: ((bf - tw)/(2*tf)) * sqrt(fy/250) | Clause 5.2.2
flange.compact: lambda.ef <= lambda.ep | Table 5.2
Sx: 1840e3 | mm^3
Zx: 1610e3 | mm^3
Zex: min(Sx, 1.5*Zx) | mm^3; Clause 5.2.4
phi: 0.9 | Table 3.4
phi.Msx: phi * fy * Zex/10^6 | kNm; Clauses 5.1, 5.2.1
!result phi.Msx | Design section moment capacityphi.Msx evaluates to 496.8 - the section sketch, the compactness verdict chip and the boxed answer all come out of the blocks above, no configuration.
Worked example: a fire check the solver doesn't do
FIRE PROTECTION - GOVERNING COLUMN
AS 4100 Section 12. CivilKit has no fire module, so this is the engineer's own working,
wired to the model.
kt: 0.45
phi.Nt: {member:1.phiNt}
phi.Nt.fire: phi.Nt * kt
util.ambient: {member:1.util@env}
N.fire: util.ambient * phi.Nt * 0.7
util.fire: N.fire / phi.Nt.fire
Adequate in fire when the utilisation above is below 1.0.(The {member:1.phiNt} form is what a @-picked reference actually stores - you never type this by hand, the picker inserts it for you.)
Worked example: wind actions with the clause column
# Wind Design Actions
## 1) Derivation of Design Wind Speeds
Location: Tauranga, design working life 50 years | AS/NZS 1170.0
V.R: 45 | Tbl 3.1
M.d: 1.0 | Tbl 3.2
h: 5 | reference height
b: 20 | width in wind direction
d: 15 | length in wind direction
A: b * d | plan area
TC: 2 | Cl 4.2.1
M.zcat: 0.91 | Tbl 4.1
V.site: V.R * M.d * M.zcat | Cl 2.2V.R, M.d, h, b, d, TC and M.zcat are all boxed automatically (they're literals - the givens); A and V.site are not (they're derived from the lines above).
What's not there yet
- No
root()/ solve-for-an-unknown. A range plus!plotwill show you where a curve crosses 1.0; the sheet will not yet find that crossing for you and print it as a number. - No named load combinations for
member/nodereferences - only@env(governing) resolves today. - No spreadsheet (xlsx) EXPORT. Import is built (see Importing a spreadsheet); a live-formula Excel export is a separate decision and is not.
- No way to save a sheet as a reusable template across models (see below).
Roadmap: saved sheets / templates
Saving a sheet as a personal template - so a fire check, a wind derivation or a standard cover note can be dropped into a new model without retyping it - is planned but not built. Today the way to reuse a sheet is to copy the text out of one model's Calc tab and paste it into another's; the @ references will need re-picking against the new model's entities (a reference is stored against a stable id, and that id doesn't exist in a different model).