Collection of engineering calculation projects (Python + Typst), each with input, calc script, tests, results, and generated PDF where available.
10 KiB
Project State: Wood Joist Analysis
Last updated: 2026-08-20
Overview
New hybrid (Typst + Python) calculation at calcs/wood-joist/ that analyzes an
existing wood joist under uniform gravity loads per NDS 2018 Allowable Stress
Design (ASD). Four checks are covered:
- Flexure (bending) — NDS 3.3 (beam stability factor
C_L) - Shear — NDS 3.4
- Bearing (compression perpendicular to grain) — NDS 3.10
- Serviceability (deflection) — NDS 3.5, limits L/240 (total) and L/360 (live)
The default input.yaml reproduces a reference worked example ("Analysis for
existing wood joists under shoring loads", Conemco Engineering, project
"Pompano Beach") exactly. The pytest suite locks those numbers.
Architecture decisions
- Hybrid pattern (same as
steel-beam):input.yaml(Pint unit-bearing quantities) →calc.py(computes, writesresults.json) →beam.typ(presents only, no recomputation) → compiled PDF. results.jsonshape (unchanged contract):{tool, version, project, prepared_by, values, checks}.tool = "wood_joist",version = "0.1".- Pint units: quantities are quoted strings in YAML (e.g.
"25 psf","9 ft").calc.pyconverts with aquantity()helper identical in spirit tosteel-beam/calc.py. Dimensionless adjustment factors are plain floats. calc.pyCLI mirrorssteel-beam:--input,--output,--stdoutflags; runnable aspython calcs/wood-joist/calc.pywith no args.- NDS 2018 is the governing standard and edition. All clause references are to NDS 2018.
beam.typimports from../../lib/sheet.typand readsresults.json(no recomputation). Compiled with--root .from the worksheets root so the shared logo resolves.
2026-08-20 change: load determination moved to Typst (user-approved)
This change moves wood-joist gravity-load determination from Python to Typst,
mirroring the steel-beam pattern. No engineering equations changed — only
where load determination lives. The Pompano Beach benchmark values are
unchanged.
beam.typdetermines loads inline. It bindsDL = 25 psf,LL = 50 psf, spanL = 9 ft, spacingB = 13 ftas Typst#letvalues, then derivesw = (DL + LL)·B,wL = LL·B,M = w·L²/8,V = w·L/2,R = V. These are emitted as a Typst metadata label<wood-joist-loads>with fieldsw_plf,wL_plf,M_ftlbf,V_lbf,R_lbf.beam.typpresents both the Typst load-derived values and the Python-checked values (like steel-beam'sM_u,loadvsM_u,design), and the test suite reconciles them via the metadata query.- New
input.yamlcontract.DL,LL, andspacingare removed. The checked demands are supplied as Pint-quantity strings:w: "975 plf",wL: "650 plf",M: "9871.875 ft * lbf",V: "4387.5 lbf",R: "4387.5 lbf".spanis kept (Python needs it for the L/240 and L/360 deflection limits). All other keys are unchanged (width,depth,bearing_length,bearing_end_distance,unbraced_length,species_grade, NDS reference values, adjustment factors,project,prepared_by). calc.pyreads demands. It no longer performs load-path arithmetic.w_plf,wL_plf,M_ftlbf,V_lbf,R_lbfcome fromquantity()on the YAML demand keys. Section properties, all NDS 2018 ASD checks, and the{tool, version, project, prepared_by, values, checks}contract are unchanged (tool = "wood_joist").valuesno longer includesDL_psf,LL_psf, orspacing_ft.check_serviceadded tolib/sheet.typas a sibling ofcheck. Signaturecheck_service(label, acting, allowed, unit: "", ok: auto). Identical layout (label, OK/NOT OK badge, colored box, ratio) but the left column reads Acting / Allowed (instead of Demand / Capacity) and the ratio is labeled Utilization (instead of D/C). Used only by the two deflection checks incalcs/wood-joist/beam.typ; flexure, shear, and bearing keepcheck.- Convention change. The inherited "
lib/sheet.typmust not be modified" convention is superseded by this explicit user request. The change is additive only:checkis unchanged and no other sheet (steel-beam,concrete-beam,concrete-beam2,shore-post) is modified.
Pinned benchmark (unchanged): w 975 plf, wL 650 plf, M 9871.88 ft·lbf
(exact 9871.875), V 4387.5 lbf, R 4387.5 lbf; flexure util 0.938, shear
util 0.66, bearing util 0.772, Δ_total 0.339 in, Δ_live 0.226 in.
Engineering decisions (pinned for the builder)
The equations are fully pinned by the reference worked example (every
intermediate value and formula is supplied). No separate engineering
specifier pass was required; the equations reproduce NDS 2018 directly and
the clause references are recorded here and in tasks/001. The reviewer task
(tasks/005) still verifies the engineering specification.
Section properties (rectangular joist)
A = b·dI_x = b·d³ / 12S_x = b·d² / 6(=2·I_x / d)
Check 1 — Flexure (NDS 3.3)
- Slenderness:
R_B = sqrt(l_e · d / b²)(NDS 3.3.3.7). RequireR_B < 50. IfR_B >= 50,calc.pyraisesValueError(member needs lateral bracing; theC_Lequation is out of scope). F*_b = F_b · C_D · C_M · C_t · C_F · C_i · C_r(all factors exceptC_L,C_fu).F_bE = 1.20 · E'_min / R_B²C_L = (1 + F_bE/F*_b)/1.9 − sqrt(((1 + F_bE/F*_b)/1.9)² − (F_bE/F*_b)/0.95)(NDS 3.3.3,c_b = 0.95for sawn lumber). Formula yieldsC_L ≤ 1.0.F'_b = F*_b · C_L · C_fuf_b = M / S_x(M in lbf·in)- Allowable moment
M_a = S_x · F'_b(reported in ft·lbf).
Check 2 — Shear (NDS 3.4)
F'_v = F_v · C_D · C_M · C_t · C_vrf_v = 3·V / (2·A)(rectangular section, NDS 3.4.1 Eq. 3.4-1)
Check 3 — Bearing (NDS 3.10, compression perpendicular to grain)
- Bearing area factor
C_b = (l_b + 0.375) / l_b(NDS 3.10.4; bearing < 6 in and ≥ 3 in from member end). F'_c⊥ = F_c⊥ · C_M · C_t · C_i · C_b- Bearing area
A_b = l_b · b f_c⊥ = R / A_b(R = w·L/2)
Check 4 — Serviceability (NDS 3.5)
Δ_total = 5·w·L⁴ / (384·E·I_x)Δ_live = 5·w_L·L⁴ / (384·E·I_x)- Limits
L/240(total) andL/360(live). Hardcoded constants incalc.py(240,360) — not YAML inputs — matching the reference.
Reference example (ground truth to lock)
Project "Pompano Beach", prepared_by "Conemco Engineering".
| Quantity | Value |
|---|---|
| DL, LL | 25 psf, 50 psf |
| span L, spacing B | 9 ft, 13 ft |
| w, w_L | 975 plf, 650 plf |
| b × d | 3.5 in × 9.5 in |
| A, I_x, S_x | 33.25 in², 250.1 in⁴, 52.6 in³ |
| F_b, F_v, F_c⊥, E, E'_min | 2400, 300, 565, 1,700,000, 510,000 psi |
| All adjustment factors | 1.0 |
| M, V, R | 9871.88 ft·lbf, 4387.5 lbf, 4387.5 lbf |
| Flexure util / shear util / bearing util | 0.938 / 0.66 / 0.772 |
| Δ_total, Δ_live | 0.339 in, 0.226 in |
Corrected vs. the source PDF: the shear section label "acting bending stress" becomes "acting shear stress", and the blank page 3 is omitted. Corrections are noted in the sheet's Scope section.
Conventions (inherited)
lib/sheet.typis shared. It must not be modified except where an explicit user request authorizes an additive-only change (see the 2026-08-20 change:check_serviceadded;checkunchanged).- No existing calculation (
steel-beam,concrete-beam,concrete-beam2,shore-post) may be modified. - Compile from
worksheets/with--root .. - Lock the reference example in pytest with
pytest.approxbefore treating the tool as stable.
Milestones
- 2026-08-19 — Task 001 DONE:
calc.py+input.yaml+results.jsoncreated. All five checks pass for the default "Pompano Beach" input; values match the reference example (flexure 0.938, shear 0.66, bearing 0.772, Δ_total 0.339 in, Δ_live 0.226 in). SeeTASKS.md. - 2026-08-19 — Task 002 DONE:
test_wood_joist.pycreated with 10 numerical lock tests; all pass (10 passed). - 2026-08-19 — Task 003 DONE:
beam.typcreated, compiled togenerated/wood-joist.pdf(206 KB), and the Typst metadata test appended; all 11 tests pass (11 passed). - 2026-08-19 — Task 004 DONE:
README.mdandcodemap.mdrefreshed with the wood-joist entry; all 11 tests still pass. - 2026-08-19 — Task 005 DONE: Reviewer PASS after one fix cycle. Issues found
and fixed: (1) C_L ≤ 1.0 now explicitly enforced with regression test;
(2) bearing applicability limits enforced (l_b < 6 in, end distance ≥ 3 in)
with new
bearing_end_distanceinput and tests. Final state: 14 tests pass, PDF compiles, results.json idempotent. Calculation approved. - 2026-08-20 — Tasks 006–008 DONE: load determination moved to Typst (mirroring
steel-beam),
check_serviceadded tolib/sheet.typ(Acting/Allowed/ Utilization) and used for the two deflection checks,input.yamlswitched to checked demands (w,wL,M,V,R), tests updated with the<wood-joist-loads>reconciliation test, and README/codemap refreshed. Reviewer PASS (all 8 checklist items, deterministic evidence verified); simplify pass clean. Final state: 15 tests pass, PDF compiles, results.json idempotent, benchmark values unchanged (flexure 0.938, shear 0.66, bearing 0.772, Δ_total 0.339 in, Δ_live 0.226 in).
Final deliverables
calcs/wood-joist/calc.py— NDS 2018 ASD wood joist analysis (flexure with C_L, shear, bearing, deflection). CLI:--input,--output,--stdout.calcs/wood-joist/input.yaml— Pint-quantity inputs; defaults reproduce the "Pompano Beach" reference example.calcs/wood-joist/test_wood_joist.py— 14 tests locking the reference values plus validation guards and the Typst metadata query test.calcs/wood-joist/beam.typ— presentation sheet (no recomputation), compiled tocalcs/wood-joist/generated/wood-joist.pdf.README.md,codemap.md— documented and indexed.
Known limitations
- Single-span, uniformly loaded, rectangular-section joist only.
- No repetitive-member live-load reduction beyond the explicit
C_rfactor. - No lateral-torsional restraint beyond the single unbraced length
l_e. - Deflection limits hardcoded to L/240 (total) and L/360 (live).
Dependencies
- Python:
pyyaml,pytest,pint(already inrequirements.txt). typstCLI for compile and the metadata-query test.- No new third-party Python packages beyond
requirements.txt.