otko/tests/integration/test_basic_truss.py
smillmorel 61b850bf46
Some checks failed
CI / lint (pull_request) Has been cancelled
CI / type (pull_request) Has been cancelled
CI / test-headless (macos-latest, 3.10) (pull_request) Has been cancelled
CI / test-headless (macos-latest, 3.11) (pull_request) Has been cancelled
CI / test-headless (macos-latest, 3.12) (pull_request) Has been cancelled
CI / test-headless (ubuntu-latest, 3.10) (pull_request) Has been cancelled
CI / test-headless (ubuntu-latest, 3.11) (pull_request) Has been cancelled
CI / test-headless (ubuntu-latest, 3.12) (pull_request) Has been cancelled
CI / test-headless (windows-latest, 3.10) (pull_request) Has been cancelled
CI / test-headless (windows-latest, 3.11) (pull_request) Has been cancelled
CI / test-headless (windows-latest, 3.12) (pull_request) Has been cancelled
CI / test-gui (pull_request) Has been cancelled
CI / test-integration (macos-latest) (pull_request) Has been cancelled
CI / test-integration (ubuntu-latest) (pull_request) Has been cancelled
CI / test-integration (windows-latest) (pull_request) Has been cancelled
style: resolve the remaining ruff findings
Auto-fixes (UP037, UP038, F401, I001, SIM105, RUF100) plus hand-fixes:
drop the now-unused `Union` imports, annotate mutable class attributes
with ClassVar, raise ValidationError instead of blind Exception in the
zero-length test, delete two dead locals, and bind the per-iteration
grid-builder closures to their loop variables (B023).
2026-09-16 13:20:36 -04:00

55 lines
1.9 KiB
Python

"""OpenSees integration test for the Basic Truss example.
Verifies the Example 1 (3-bar truss) model builds, runs, and produces
a physically reasonable tip deflection. This is the first of the
OpenSees Examples Manual regression tests.
"""
from __future__ import annotations
import pytest
pytest.importorskip("openseespy")
from otko.services.opensees_runner import OpenSeesRunner
pytestmark = pytest.mark.slow
def test_basic_truss_matches_opensees_tcl_reference() -> None:
"""Crown displacement must match the OpenSees Tcl Example-1 reference.
The Tcl model (kip-in) reports node 4 disp = (+0.5301, -0.1779) in.
Our SI model converts to that: 0.5301" = 0.01346 m,
0.1779" = 0.004518 m. Tolerance is tight because the SI model is
a linear rescale of the Tcl model — any deviation would flag a
real solver/translation issue.
"""
from examples.basic_truss import IN_TO_M, build_basic_truss
proj = build_basic_truss()
runner = OpenSeesRunner(proj)
result = runner.run(proj.analyses[0])
assert 4 in result.node_disp
ux, uy = result.node_disp[4][-1]
ux_in = ux / IN_TO_M
uy_in = uy / IN_TO_M
assert ux_in == pytest.approx(+0.5301, abs=1e-3), f"Ux = {ux_in} in"
assert uy_in == pytest.approx(-0.1779, abs=1e-3), f"Uy = {uy_in} in"
# All three truss bars must have recorded forces.
assert set(result.element_forces.keys()) == {1, 2, 3}
def test_basic_truss_round_trips(tmp_path) -> None: # type: ignore[no-untyped-def]
"""The example project must survive save/load without any information loss."""
from examples.basic_truss import build_basic_truss
from otko.services import load_project, save_project
p = build_basic_truss()
path = tmp_path / "basic_truss.osmodel"
save_project(p, path)
r = load_project(path)
assert r.model_dump(by_alias=True) == p.model_dump(by_alias=True)