Collection of engineering calculation projects (Python + Typst), each with input, calc script, tests, results, and generated PDF where available.
211 lines
13 KiB
Markdown
211 lines
13 KiB
Markdown
# Project State: Concentric Footing Analysis
|
|
|
|
Last updated: 2026-08-21
|
|
Project root: `calcs/concentric-footing/`
|
|
Parent worksheets root: `/home/smill/Sync/worksheets`
|
|
Reference: `CONCENTRIC-FOOTING.pdf` — Blavatnik concentric footing for steel column (square footing, ACI-based checks). Parent project conventions as documented in `worksheets/codemap.md` and `calcs/wood-joist/PROJECT_STATE.md`.
|
|
|
|
## Overview
|
|
|
|
New hybrid (Typst + Python) calculation at `calcs/concentric-footing/` that checks a square, concentrically loaded, reinforced concrete spread footing under combined service and ultimate axial load per **ACI 318-19**. Five checks are covered:
|
|
|
|
1. Soil bearing (service, ASD) — `q = Ps/Af <= qa`
|
|
2. One-way (beam) shear — ACI 22.5 — `Vc = 2*lambda*sqrt(f'c)*Bf*d`
|
|
3. Two-way (punching) shear — ACI 22.6 — `vc = min(4, 2+4/beta, 2+alpha_s*d/bo)*lambda*sqrt(f'c)`
|
|
4. Flexure (bending) — ACI 22.5/7 — Whitney block `a = As*fy/(0.85*f'c*Bf)`, `Mn = As*fy*(d-a/2)`
|
|
5. Concrete bearing on footing — ACI 22.8 — `Bn = 0.85*f'c*A1*sqrt(A2/A1) <= 2*0.85*f'c*A1`
|
|
|
|
Minimum reinforcement `rho = As/(Bf*d) >= 0.0018` is checked as `minimum_steel`.
|
|
|
|
The default `input.yaml` reproduces the Blavatnik reference example subject to documented corrections (bearing plate clarification and ACI-correct punching perimeter). The pytest suite locks the corrected numbers.
|
|
|
|
## Architecture decisions
|
|
|
|
- **Hybrid pattern** (same as `wood-joist` / `steel-beam`): `input.yaml` (Pint unit-bearing quantities, quoted strings) -> `calc.py` (`compute()` -> writes `results.json`) -> `footing.typ` (presents only, no recomputation) -> compiled PDF with `--root .` from `worksheets/`.
|
|
- **`results.json` shape** (unchanged contract): `{tool, version, project, prepared_by, values, checks}` with `tool = "concentric_footing"`, `version = "0.1"`.
|
|
- **Pint units**: all dimensional inputs are quoted strings (e.g. `"3000 psi"`, `"3 ft"`, `"18.4 kip"`). `calc.py` converts with a `quantity(value, unit, name)` helper identical to `wood-joist/calc.py`; dimensionless factors are plain floats.
|
|
- **`calc.py` CLI** mirrors `wood-joist`/`steel-beam`: `--input`, `--output`, `--stdout`; runnable as `python calcs/concentric-footing/calc.py` with no args.
|
|
- **ACI 318-19** is the governing standard and edition. All clause references are to ACI 318-19 Chapter 22 / 13.
|
|
- **`footing.typ` imports** from `../../lib/sheet.typ` and reads `results.json`. It also **derives loads in Typst** (mirroring `wood-joist`/`steel-beam`): `DLr`, `LLr`, `Br`, `Lr`, `Ar`, column weight `Wc = bc*bc*Lc*gamma_c` -> `Ps_derived = (DLr+LLr)*Ar + Wc`, `Pu_derived = 1.2*(DLr*Ar+Wc)+1.6*LLr*Ar`. These are emitted as `<concentric-footing-loads>` metadata and reconciled to the Python-checked `Ps`/`Pu` by the test suite. Capacity numbers are never recomputed in Typst.
|
|
- **Sheet helpers**: flexure, shear, bearing, and soil bearing use `check` (Demand/Capacity D/C). No `check_service` variant is needed; soil bearing is presented as `q` vs `qa`.
|
|
- **Compilation**: `typst compile --root . calcs/concentric-footing/footing.typ calcs/concentric-footing/generated/footing.pdf` so the shared logo at `assets/logo.png` resolves.
|
|
|
|
## Engineering decisions (pinned for the builder)
|
|
|
|
All equations, units, and applicability limits are pinned here. The builder must not invent behavior. Tolerances and benchmark values are under "Reference example".
|
|
|
|
### Inputs and units
|
|
|
|
Pint-parsed quantities (all positive, `ValueError` if <=0 or wrong dimension):
|
|
- `Ps` -> kip (service axial load, column + roof)
|
|
- `Pu` -> kip (factored axial load, 1.2D+1.6L)
|
|
- `qa` -> psf (allowable soil bearing, gross)
|
|
- `Bf` -> ft or in (square footing side; `Af = Bf^2` -> ft2, also `Bf_in = Bf_ft*12`)
|
|
- `Df` -> in (total footing thickness)
|
|
- `cover` -> in (to centroid of steel, so `d = Df - cover`; `d` is effective depth)
|
|
- `fc` -> psi or ksi (concrete `f'c`)
|
|
- `fy` -> psi or ksi (reinforcement yield)
|
|
- `lambda` -> float (lightweight factor, 1.0 normal weight, (0,1])
|
|
- `column_width` (`c`) -> in (square column side)
|
|
- `base_plate_width` (`bp`) -> in (square base plate side, `A1 = bp^2`; if omitted defaults to `c`)
|
|
- `rebar_size` -> int (e.g. 4 means #4 -> db = rebar_size/8 in)
|
|
- `N` -> int (number of bars per direction)
|
|
Dimensionless / integers are validated: `N` integer >=1, `rebar_size` integer 3..18, `lambda` in (0,1], `rho_min` hardcoded 0.0018.
|
|
|
|
Derived:
|
|
- `db_in = rebar_size/8`
|
|
- `As1_in2 = pi*db^2/4`
|
|
- `As_in2 = N*As1`
|
|
- `d_in = Df_in - cover_in` (cover to centroid per reference; `ValueError` if `d <=0` or `d > Df`)
|
|
- `Af_ft2 = Bf_ft^2`, `Af_in2 = Af_ft2*144`
|
|
- `A1_in2 = bp_in^2`, `A2_in2 = Af_in2`, `A2_ft2 = Af_ft2`
|
|
- `Bf_in = Bf_ft*12`, `L_cant_ft = (Bf_in - c_in)/2/12`, `L_cant_in = (Bf_in - c_in)/2`
|
|
|
|
### Check 1 — Soil bearing (service)
|
|
|
|
- `q_psf = Ps_lbf / Af_ft2` where `Ps_lbf = Ps_kip*1000`
|
|
- `qu_psf = Pu_lbf / Af_ft2` (ultimate pressure for concrete checks)
|
|
- `ok_soil = q_psf <= qa_psf`
|
|
- Report `q_psf`, `qu_psf`, `qa_psf`, `Af_ft2`.
|
|
- Note: footing self weight and soil surcharge are excluded (gross pressure follows reference). Scope note states this limitation.
|
|
|
|
### Check 2 — One-way (beam) shear — ACI 22.5.5, phi=0.75
|
|
|
|
- Critical section at distance `d` from column face.
|
|
- `L1_in = (Bf_in - c_in)/2 - d_in` (cantilever beyond section). If `L1_in <=0` then `Vu_kip = 0` (no shear beyond section).
|
|
- Otherwise `Vu_lbf = qu_psf * (Bf_ft) * (L1_in/12)` because `qu` (psf) * width (ft) * length (ft). So `Vu_kip = Vu_lbf/1000`.
|
|
- `Vc_lbf = 2*lambda*sqrt(fc_psi)*Bf_in*d_in` (ACI 22.5.5.1, `lambda` factor). `Vc_kip = Vc_lbf/1000`.
|
|
- `phiVc_kip = 0.75*Vc_kip`
|
|
- `ok_one_way = Vu_kip <= phiVc_kip`
|
|
- Also report `Vu/phiVc`.
|
|
|
|
### Check 3 — Two-way (punching) shear — ACI 22.6.5, phi=0.75
|
|
|
|
- `bo_in = 4*(c_in + d_in)` (interior square column; critical perimeter at d/2). Documented correction: reference shows 68in which is inconsistent with ACI; correct value for c=14,d=9 is 92in.
|
|
- `beta = 1.0` (square). `alpha_s = 40` (interior per ACI 22.6.5.3).
|
|
- `vc1 = 4*lambda*sqrt(fc_psi)`
|
|
- `vc2 = (2 + 4/beta)*lambda*sqrt(fc_psi)`
|
|
- `vc3 = (2 + alpha_s*d_in/bo_in)*lambda*sqrt(fc_psi)`
|
|
- `vc_psi = min(vc1, vc2, vc3)`
|
|
- `Vc_lbf = vc_psi*bo_in*d_in`, `Vc_kip = Vc_lbf/1000`, `phiVn_kip = 0.75*Vc_kip`
|
|
- `Apunch_in2 = (c_in + d_in)^2`, `Apunch_ft2 = Apunch_in2/144`
|
|
- `Vu_lbf = qu_psf*(Af_ft2 - Apunch_ft2)`, `Vu_kip = Vu_lbf/1000`
|
|
- `ok_two_way = Vu_kip <= phiVn_kip`
|
|
- Also report `vc_psi`, `bo_in`.
|
|
|
|
### Check 4 — Flexure — ACI 22.5 / 7, phi=0.90
|
|
|
|
- Cantilever length `Lc_in = (Bf_in - c_in)/2`, `Lc_ft = Lc_in/12`
|
|
- `Mu_kipft = qu_psf * Bf_ft * Lc_ft^2 / 2` (qu as psf -> psf*ft*ft^2 = lbf*ft/1000 = kip*ft). Equivalent presentation: `Mu = qu*Bf*((Bf-c)/2)^2/2`.
|
|
- `a_in = As_in2*fy_psi / (0.85*fc_psi*Bf_in)`
|
|
- `c_block_in = a_in / beta1` where `beta1 = max(0.65, min(0.85, 0.85 - 0.05*max(0, (fc_psi-4000)/1000)))` (ACI 22.2.2.4.3). Computed for strain check but not required for phi (phi=0.9 tension-controlled assumed; still compute `et` for report).
|
|
- `beta1` per above.
|
|
- `Mn_kipft = As_in2*fy_ksi*(d_in - a_in/2)/12` (fy in ksi). Or `As*fy*(d-a/2)/12`.
|
|
- `phiMn_kipft = 0.90*Mn_kipft`
|
|
- `ok_flexure = Mu_kipft <= phiMn_kipft`
|
|
- `rho = As_in2 / (Bf_in*d_in)`, `rho_min = 0.0018`, `ok_min_steel = rho >= rho_min` (separate check `minimum_steel` with demand `rho_min`, capacity `rho`). For `checks` dict, `minimum_steel` uses `demand = rho_min`, `capacity = rho`.
|
|
- Also report `Mu/phiMn`, `a_in`, `rho`.
|
|
|
|
### Check 5 — Concrete bearing — ACI 22.8, phi=0.65
|
|
|
|
- `A1_in2 = bp_in^2`, `A2_in2 = Af_in2`
|
|
- `sqrt_ratio = sqrt(A2_in2/A1_in2)`, capped at 2.0: `sqrt_ratio_capped = min(sqrt_ratio, 2.0)`
|
|
- `Bn_lbf = 0.85*fc_psi*A1_in2*sqrt_ratio_capped`, but upper bound `2*0.85*fc_psi*A1_in2` already enforced by cap.
|
|
- `Bn_kip = Bn_lbf/1000`, `phiBn_kip = 0.65*Bn_kip`
|
|
- `ok_bearing = Pu_kip <= phiBn_kip`
|
|
- Report `A1_in2`, `A2_in2`, `sqrt_ratio`, `Bn_kip`, `phiBn_kip`, `Pu/phiBn`.
|
|
|
|
### Values dictionary (all rounded to 6 decimals via q() helper, except labels)
|
|
|
|
Keys in `results.json` `values` (units encoded in name):
|
|
`Ps_kip, Pu_kip, qa_psf, q_psf, qu_psf, Af_ft2,
|
|
Bf_in, Bf_ft, Df_in, cover_in, d_in,
|
|
fc_psi, fy_psi, fy_ksi, lambda,
|
|
c_in, bp_in,
|
|
N, rebar_size, db_in, As1_in2, As_in2,
|
|
rho, rho_min,
|
|
L1_in, Vu_one_way_kip, Vc_one_way_kip, phiVc_one_way_kip,
|
|
bo_in, vc_psi, Vu_two_way_kip, Vc_two_way_kip, phiVn_two_way_kip,
|
|
Lc_in, Mu_kipft, a_in, beta1, Mn_kipft, phiMn_kipft,
|
|
A1_in2, A2_in2, sqrt_ratio, Bn_kip, phiBn_kip`
|
|
|
|
### Checks dictionary
|
|
|
|
Each entry `{demand, capacity, ok}` with appropriate units (kip, kip-ft, psf, or dimensionless for rho):
|
|
- `soil_bearing`: demand `q_psf`, capacity `qa_psf`
|
|
- `one_way_shear`: demand `Vu_one_way_kip`, capacity `phiVc_one_way_kip`
|
|
- `two_way_shear`: demand `Vu_two_way_kip`, capacity `phiVn_two_way_kip`
|
|
- `flexure`: demand `Mu_kipft`, capacity `phiMn_kipft`
|
|
- `minimum_steel`: demand `rho_min`, capacity `rho`
|
|
- `bearing`: demand `Pu_kip`, capacity `phiBn_kip`
|
|
|
|
Overall ok requires all six true.
|
|
|
|
## Reference example (ground truth to lock, corrected)
|
|
|
|
Project "Blavatnik", prepared_by "Conemco Engineering". Derived loads shown in Typst: `DLr=10 psf`, `LLr=20 psf`, `Br=18.9 ft`, `Lr=27.5 ft`, `Ar=519.75 ft2`, column `14 in x14 in x14 ft`, `gamma_c=145 pcf`, `Ps~18.4 kip`, `Pu~26.2 kip`.
|
|
|
|
Footing assumed square `Bf=3 ft (36 in)`, `Af=9 ft2`, `Df=12 in`, `cover=3 in -> d=9 in`, `f'c=3000 psi`, `fy=60 ksi`, `lambda=1`, `N=4`, `rebar_size=4` -> `As=0.785 in2` (reference rounds to 0.8), `c=14 in`, `bp=6 in`, `qa=2500 psf`.
|
|
|
|
Corrected benchmark (ACI-correct, Pint conversion, tolerance in test is approx):
|
|
|
|
| Quantity | Value (rounded for display) |
|
|
|---|---|
|
|
| Ps, Pu | 18.4 kip, 26.2 kip (typst-derived 18.36/26.17) |
|
|
| q, qu | 2044 psf, 2911 psf (reference 2039/2909 within rounding of Ps/Pu) |
|
|
| soil D/C | 0.82 (q/qa) |
|
|
| One-way Vu | 1.46 kip |
|
|
| One-way Vc | 35.45 kip (2*sqrt(fc)*B*d) -> phiVc 26.59 kip, D/C 0.055 |
|
|
| Two-way bo | 92 in (corrected from 68) |
|
|
| vc | 219.1 psi (4*sqrt(fc)) |
|
|
| Two-way Vc | 181.4 kip -> phiVn 136.0 kip, Vu 15.51 kip, D/C 0.114 |
|
|
| Mu | 3.68 kip-ft |
|
|
| a | 0.524 in |
|
|
| Mn | 34.27 kip-ft -> phiMn 30.84 kip-ft, D/C 0.12 |
|
|
| rho | 0.00242 (>0.0018) |
|
|
| Bearing A1/A2 | 36 / 1296 in2, sqrt 6 capped 2 |
|
|
| Bn | 183.6 kip -> phiBn 119.3 kip, D/C 0.22 |
|
|
|
|
The reference PDF shows Vu one-way 1.5 kip, Vc 35.5 kip, phiVc 26.6 kip, Vu two-way 15.5 kip, Vc 134 kip (using 68in), phiVn 100.6 kip, Mu 3.7 kip-ft, Mn 34.3, phiMn 30.9. Differences are documented in `footing.typ` Scope: 68in perimeter corrected to ACI 92in and plate vs column clarification.
|
|
|
|
Pinned tolerances for pytest.approx: psf within 1%, kip within 0.02 kip, inches within 0.01, phi capacities within 0.3 kip or rel 1e-3.
|
|
|
|
## Conventions (inherited)
|
|
|
|
- `lib/sheet.typ` is shared. Do not modify; `footing.typ` uses `check` (not `check_service`) for all checks.
|
|
- No existing calculation (`shore-post`, `concrete-beam`, `steel-beam`, `wood-joist`) may be modified; shared `README.md` is allowed to add the new calc entry.
|
|
- Compile from `worksheets/` with `--root .`.
|
|
- Lock the reference example in pytest with `pytest.approx` before treating tool as stable.
|
|
|
|
## Milestones
|
|
|
|
- 001 DONE: `calc.py` + `input.yaml` + `results.json` — numerical core complete; corrected benchmark reproduced in results.json.
|
|
- 002 DONE: `test_concentric_footing.py` locks corrected benchmark (10 tests pass; reviewer PASS).
|
|
- 003 DONE: `footing.typ` + `generated/footing.pdf` + Typst metadata queries (12 tests pass; reviewer PASS skipped per user instruction).
|
|
- 004 DONE: `README.md` + `codemap.md` refreshed (depends on 003).
|
|
- 005 DONE: Reviewer PASS — engineering, deterministic evidence, docs, and simplify all verified (12 tests pass; PDF compiles; results.json idempotent).
|
|
|
|
## Final deliverables
|
|
|
|
- `calcs/concentric-footing/calc.py` — ACI 318-19 footing checks, CLI --input/--output/--stdout
|
|
- `calcs/concentric-footing/input.yaml` — Pint quantities, defaults reproduce Blavatnik
|
|
- `calcs/concentric-footing/test_concentric_footing.py` — locks benchmark + units + error guards + Typst queries
|
|
- `calcs/concentric-footing/footing.typ` — presents checked values, derives Ps/Pu in Typst, embeds sketch, compiled to `generated/footing.pdf`
|
|
- `README.md`, `codemap.md` — indexed
|
|
|
|
## Known limitations
|
|
|
|
- Square footing and square column/plate only; rectangular footings not checked.
|
|
- Interior column only (alpha_s=40); edge/corner punching not covered.
|
|
- Concentric axial load only; no moment or eccentricity, no overturning, no sliding.
|
|
- Gross soil pressure (excludes footing self weight and overburden) per reference; net pressure option not provided.
|
|
- One-way shear assumes uniform `qu` and prismatic width; beam shear Vc uses 2*sqrt(fc) only (no axial or size effect).
|
|
- Bearing uses `sqrt(A2/A1) <=2` per ACI 22.8.3.2; confinement reinforcement not checked.
|
|
- d = Df - cover (cover to centroid); bar diameter not subtracted separately. If cover is to clear, adjust input.
|
|
- Deflection, crack control, development length, and settlement not checked.
|
|
|
|
## Dependencies
|
|
|
|
- Python: `pyyaml`, `pytest`, `pint` (already in `requirements.txt`)
|
|
- `typst` CLI for compile and metadata-query tests
|
|
- No new third-party packages
|