Collection of engineering calculation projects (Python + Typst), each with input, calc script, tests, results, and generated PDF where available.
92 lines
3.3 KiB
Python
92 lines
3.3 KiB
Python
from pathlib import Path
|
|
import importlib.util
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
|
|
import pytest
|
|
import yaml
|
|
|
|
HERE = Path(__file__).resolve().parent
|
|
spec = importlib.util.spec_from_file_location("steel_beam_calc", HERE / "calc.py")
|
|
assert spec is not None and spec.loader is not None
|
|
module = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(module)
|
|
|
|
|
|
def result():
|
|
with (HERE / "input.yaml").open(encoding="utf-8") as handle:
|
|
return module.compute(yaml.safe_load(handle))
|
|
|
|
|
|
def test_typst_load_demands_match_checked_inputs():
|
|
subprocess.run([sys.executable, str(HERE / "calc.py")], check=True, cwd=HERE.parents[1])
|
|
query = subprocess.run(
|
|
[
|
|
"typst", "eval", "query(<load-demands>)", "--root", ".",
|
|
"--in", "calcs/steel-beam/steel-beam.typ", "--format", "json",
|
|
],
|
|
check=True,
|
|
capture_output=True,
|
|
text=True,
|
|
cwd=HERE.parents[1],
|
|
)
|
|
published = json.loads(query.stdout)
|
|
assert len(published) == 1
|
|
derived = published[0]["value"]
|
|
values = result()["values"]
|
|
assert derived["Mu_kipft"] == pytest.approx(values["moment_kipft"], abs=0.001)
|
|
assert derived["Vu_kip"] == pytest.approx(values["shear_kip"], abs=0.001)
|
|
|
|
|
|
def test_ground_truth_section_properties():
|
|
values = result()["values"]
|
|
assert values["section_d"] == pytest.approx(5.875)
|
|
assert values["section_A"] == pytest.approx(2.52)
|
|
assert values["section_Sx"] == pytest.approx(5.1)
|
|
assert values["section_Zx"] == pytest.approx(5.73)
|
|
assert values["section_rts"] == pytest.approx(1.05)
|
|
|
|
|
|
def test_ground_truth_demands_and_flexure():
|
|
output = result()
|
|
values = output["values"]
|
|
assert values["point_load_lbf"] == pytest.approx(7500)
|
|
assert values["moment_kipft"] == pytest.approx(9.375)
|
|
assert values["shear_kip"] == pytest.approx(3.75)
|
|
assert values["Lp_in"] == pytest.approx(37.7, abs=0.1)
|
|
assert values["Lr_ft"] == pytest.approx(9.48, abs=0.01)
|
|
assert values["Fcr_ksi"] == pytest.approx(99.57, abs=0.25)
|
|
assert values["Mp_kipft"] == pytest.approx(23.875)
|
|
assert values["MnLTB_kipft"] == pytest.approx(21.235, abs=0.01)
|
|
assert values["phiMn_kipft"] == pytest.approx(19.1, abs=0.1)
|
|
assert output["checks"]["flexure"]["ok"] is True
|
|
|
|
|
|
def test_ground_truth_shear_and_deflection():
|
|
output = result()
|
|
values = output["values"]
|
|
assert values["Aw_in2"] == pytest.approx(1.102, abs=0.001)
|
|
assert values["Cv1"] == pytest.approx(1)
|
|
assert values["phiVn_kip"] == pytest.approx(33.047, abs=0.1)
|
|
assert values["delta_limit_in"] == pytest.approx(0.25)
|
|
assert values["delta_in"] == pytest.approx(0.078, abs=0.001)
|
|
assert all(output["checks"][name]["ok"] for name in ("shear", "deflection"))
|
|
|
|
|
|
def test_invalid_section_is_rejected():
|
|
with pytest.raises(ValueError, match="not found"):
|
|
module.compute({**yaml.safe_load((HERE / "input.yaml").read_text()), "section": "W0X0"})
|
|
|
|
|
|
def test_cli_can_write_json_to_stdout():
|
|
completed = subprocess.run(
|
|
[sys.executable, str(HERE / "calc.py"), "--input", str(HERE / "input.yaml"), "--stdout"],
|
|
check=True,
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
output = json.loads(completed.stdout)
|
|
assert output["section"] == "W6X8.5"
|
|
assert output["values"]["moment_kipft"] == pytest.approx(9.375)
|
|
assert completed.stderr == ""
|