from __future__ import annotations import json import math import sys from pathlib import Path try: import yaml except ImportError: raise SystemExit("Install PyYAML: python -m pip install pyyaml") try: from pint import DimensionalityError, UndefinedUnitError, UnitRegistry except ImportError: raise SystemExit("Install Pint: python -m pip install pint") HERE = Path(__file__).resolve().parent ureg = UnitRegistry() ureg.define("kip = 1000 * force_pound") ureg.define("ksi = kip / inch ** 2") ureg.define("klf = kip / foot") if "psf" not in ureg: ureg.define("psf = force_pound / foot ** 2") if "pcf" not in ureg: ureg.define("pcf = force_pound / foot ** 3") def to_magnitude(value, unit: str, name: str) -> float: try: quantity = ureg.Quantity(value) except (UndefinedUnitError, ValueError, TypeError) as exc: raise ValueError(f"{name}: cannot parse {value!r}") from exc try: magnitude = float(quantity.to(unit).magnitude) except DimensionalityError as exc: raise ValueError(f"{name}: expected {unit}, got {value!r}") from exc if magnitude <= 0: raise ValueError(f"{name} must be positive") return magnitude def compute(inp: dict) -> dict: span_ft = to_magnitude(inp["span"], "ft", "span") tributary_ft = to_magnitude(inp["tributary"], "ft", "tributary") D_psf = to_magnitude(inp["D"], "psf", "D") L_psf = to_magnitude(inp["L"], "psf", "L") bw_in = to_magnitude(inp["bw"], "in", "bw") h_in = to_magnitude(inp["h"], "in", "h") d_in = to_magnitude(inp["d"], "in", "d") fc_ksi = to_magnitude(inp["fc"], "ksi", "fc") fy_ksi = to_magnitude(inp["fy"], "ksi", "fy") As_in2 = to_magnitude(inp["As"], "in**2", "As") concrete_pcf = to_magnitude(inp["concrete_density"], "pcf", "concrete_density") self_weight_klf = (bw_in * h_in / 144.0) * concrete_pcf / 1000.0 wD_klf = D_psf * tributary_ft / 1000.0 + self_weight_klf wL_klf = L_psf * tributary_ft / 1000.0 wu_klf = 1.2 * wD_klf + 1.6 * wL_klf Mu_kipft = wu_klf * span_ft**2 / 8.0 Vu_kip = wu_klf * span_ft / 2.0 fc_psi = fc_ksi * 1000.0 fy_psi = fy_ksi * 1000.0 a_in = As_in2 * fy_ksi / (0.85 * fc_ksi * bw_in) beta1 = max(0.65, min(0.85, 0.85 - 0.05 * max(0.0, (fc_psi - 4000.0) / 1000.0))) c_in = a_in / beta1 et = 0.003 * (d_in - c_in) / c_in if c_in > 0 else 0.0 if et >= 0.005: phi = 0.90 else: phi = max(0.65, min(0.90, 0.65 + (et - 0.002) * 250.0 / 3.0)) Mn_kipft = As_in2 * fy_ksi * (d_in - a_in / 2.0) / 12.0 phiMn_kipft = phi * Mn_kipft rho_min = max(3.0 * math.sqrt(fc_psi) / fy_psi, 200.0 / fy_psi) As_min_in2 = rho_min * bw_in * d_in Vc_kip = 2.0 * math.sqrt(fc_psi) * bw_in * d_in / 1000.0 phiVc_kip = 0.75 * Vc_kip def q(value: float) -> float: return round(value, 6) return { "tool": "concrete_beam2", "version": "0.1", "project": inp.get("project", ""), "prepared_by": inp.get("prepared_by", ""), "values": { "span_ft": q(span_ft), "tributary_ft": q(tributary_ft), "D_psf": q(D_psf), "L_psf": q(L_psf), "self_weight_klf": q(self_weight_klf), "wD_klf": q(wD_klf), "wL_klf": q(wL_klf), "wu_klf": q(wu_klf), "Mu_kipft": q(Mu_kipft), "Vu_kip": q(Vu_kip), "bw_in": q(bw_in), "h_in": q(h_in), "d_in": q(d_in), "fc_ksi": q(fc_ksi), "fy_ksi": q(fy_ksi), "As_in2": q(As_in2), "a_in": q(a_in), "et": q(et), "phi": q(phi), "Mn_kipft": q(Mn_kipft), "phiMn_kipft": q(phiMn_kipft), "As_min_in2": q(As_min_in2), "Vc_kip": q(Vc_kip), "phiVc_kip": q(phiVc_kip), }, "checks": { "flexure": { "demand": q(Mu_kipft), "capacity": q(phiMn_kipft), "ok": Mu_kipft <= phiMn_kipft, }, "minimum_steel": { "demand": q(As_min_in2), "capacity": q(As_in2), "ok": As_in2 >= As_min_in2, }, "shear": { "demand": q(Vu_kip), "capacity": q(phiVc_kip), "ok": Vu_kip <= phiVc_kip, }, }, } def write_results(result: dict, path: Path) -> None: path.write_text(json.dumps(result, indent=2) + "\n", encoding="utf-8") def main() -> int: input_path = Path(sys.argv[1]) if len(sys.argv) > 1 else HERE / "input.yaml" output_path = Path(sys.argv[2]) if len(sys.argv) > 2 else input_path.with_name("results.json") with input_path.open(encoding="utf-8") as handle: inp = yaml.safe_load(handle) result = compute(inp) write_results(result, output_path) print(output_path) return 0 if __name__ == "__main__": raise SystemExit(main())