calcs/wood-joist/PROJECT_STATE.md
smillmorel d5ac3fca7e Add structural calculation worksheets
Collection of engineering calculation projects (Python + Typst), each with
input, calc script, tests, results, and generated PDF where available.
2026-09-21 12:19:20 -04:00

207 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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:
1. Flexure (bending) — NDS 3.3 (beam stability factor `C_L`)
2. Shear — NDS 3.4
3. Bearing (compression perpendicular to grain) — NDS 3.10
4. 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, writes `results.json`) → `beam.typ`
(presents only, no recomputation) → compiled PDF.
- **`results.json` shape** (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.py` converts with a `quantity()` helper identical in spirit
to `steel-beam/calc.py`. Dimensionless adjustment factors are plain floats.
- **`calc.py` CLI** mirrors `steel-beam`: `--input`, `--output`, `--stdout`
flags; runnable as `python calcs/wood-joist/calc.py` with no args.
- **NDS 2018** is the governing standard and edition. All clause references are
to NDS 2018.
- **`beam.typ` imports** from `../../lib/sheet.typ` and reads `results.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.typ` determines loads inline.** It binds `DL = 25 psf`, `LL = 50 psf`,
span `L = 9 ft`, spacing `B = 13 ft` as Typst `#let` values, then derives
`w = (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 fields
`w_plf`, `wL_plf`, `M_ftlbf`, `V_lbf`, `R_lbf`. `beam.typ` presents both the
Typst load-derived values and the Python-checked values (like steel-beam's
`M_u,load` vs `M_u,design`), and the test suite reconciles them via the
metadata query.
- **New `input.yaml` contract.** `DL`, `LL`, and `spacing` are 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"`. `span` is 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.py` reads demands.** It no longer performs load-path arithmetic.
`w_plf`, `wL_plf`, `M_ftlbf`, `V_lbf`, `R_lbf` come from `quantity()` 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"`). `values` no longer includes `DL_psf`,
`LL_psf`, or `spacing_ft`.
- **`check_service` added to `lib/sheet.typ`** as a sibling of `check`.
Signature `check_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 in `calcs/wood-joist/beam.typ`; flexure, shear, and bearing
keep `check`.
- **Convention change.** The inherited "`lib/sheet.typ` must not be modified"
convention is superseded by this explicit user request. The change is
**additive only**: `check` is 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·d`
- `I_x = b·d³ / 12`
- `S_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). Require `R_B < 50`.
If `R_B >= 50`, `calc.py` raises `ValueError` (member needs lateral bracing;
the `C_L` equation is out of scope).
- `F*_b = F_b · C_D · C_M · C_t · C_F · C_i · C_r` (all factors except `C_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.95` for sawn lumber). Formula yields `C_L ≤ 1.0`.
- `F'_b = F*_b · C_L · C_fu`
- `f_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_vr`
- `f_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) and `L/360` (live). **Hardcoded constants** in
`calc.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.typ` is shared. It must not be modified except where an explicit
user request authorizes an **additive-only** change (see the 2026-08-20
change: `check_service` added; `check` unchanged).
- 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.approx` before treating the
tool as stable.
## Milestones
- 2026-08-19 — Task 001 DONE: `calc.py` + `input.yaml` + `results.json` created.
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). See `TASKS.md`.
- 2026-08-19 — Task 002 DONE: `test_wood_joist.py` created with 10 numerical
lock tests; all pass (`10 passed`).
- 2026-08-19 — Task 003 DONE: `beam.typ` created, compiled to
`generated/wood-joist.pdf` (206 KB), and the Typst metadata test appended;
all 11 tests pass (`11 passed`).
- 2026-08-19 — Task 004 DONE: `README.md` and `codemap.md` refreshed 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_distance` input 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_service` added to `lib/sheet.typ` (Acting/Allowed/
Utilization) and used for the two deflection checks, `input.yaml` switched 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
to `calcs/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_r` factor.
- 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 in `requirements.txt`).
- `typst` CLI for compile and the metadata-query test.
- No new third-party Python packages beyond `requirements.txt`.