otko/examples/rc_frame_pushover.py
smillmorel 612936a00b
Some checks failed
CI / lint (push) Has been cancelled
CI / test (macos-latest, 3.10) (push) Has been cancelled
CI / test (macos-latest, 3.11) (push) Has been cancelled
CI / test (macos-latest, 3.12) (push) Has been cancelled
CI / test (ubuntu-latest, 3.10) (push) Has been cancelled
CI / test (ubuntu-latest, 3.11) (push) Has been cancelled
CI / test (ubuntu-latest, 3.12) (push) Has been cancelled
CI / test (windows-latest, 3.10) (push) Has been cancelled
CI / test (windows-latest, 3.11) (push) Has been cancelled
CI / test (windows-latest, 3.12) (push) Has been cancelled
feat: initial otko import
2026-09-08 02:12:15 -04:00

139 lines
5.2 KiB
Python
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.

"""RC Frame Pushover Analysis — OpenSees Examples Manual, Example 3.2.
Sources the Example 3 gravity model and extends it with a lateral
reference load pattern + DisplacementControl pushover on the top-left
joint (node 3, DOF 1 = Ux). Target displacement 15 in with dU = 0.1
in per step. Matches the Tcl walkthrough at:
https://opensees.berkeley.edu/wiki/index.php?title=RC_Portal_Frame_Pushover_Analysis
Model (kip-in-ksi):
- Geometry + section + elements = Example 3 (rc_frame_gravity).
- Gravity preload = a ``StaticCase`` with 10 × LoadControl(0.1)
steps under a Linear TS — same recipe the Tcl uses
(``analyze 10; loadConst -time 0.0``). Referenced by the
PushoverCase via ``preload_case_ids``.
- Lateral pattern: H = 10 kip at nodes 3 and 4 in +X, Linear TS,
scaled by DisplacementControl.
GUI walkthrough: File → Open → rc_frame_pushover.osmodel → Analyze →
Run → Pushover → Display → Show Pushover Curve.
"""
from __future__ import annotations
from pathlib import Path
from otko.core import (
LinearTimeSeries,
NodalLoad,
PlainLoadPattern,
PushoverCase,
StaticCase,
)
from otko.services import load_project, save_project
# Reuse the Example 3 gravity project as the foundation — identical
# geometry, materials, sections, elements. Only the load patterns and
# analysis case change for the pushover. Works both when importing as
# ``examples.rc_frame_pushover`` (pytest) and when running this file
# directly (``python examples/rc_frame_pushover.py``).
try:
from examples.rc_frame_gravity import build_rc_frame_gravity, P_LOAD
except ImportError:
import sys
sys.path.insert(0, str(Path(__file__).parent))
from rc_frame_gravity import build_rc_frame_gravity, P_LOAD # type: ignore
# Pushover parameters from the Tcl reference.
H_LATERAL = 10.0 # kip — reference lateral load
D_STEP = 0.1 # in — DisplacementControl increment
D_TARGET = 15.0 # in — total pushover displacement
def build_rc_frame_pushover(): # type: ignore[no-untyped-def]
"""Start from the Ex3 gravity model and re-plumb for pushover.
Three edits to the gravity project:
1. Add a ``LinearTimeSeries`` + ``PlainLoadPattern`` for the
lateral reference load (H = 10 kip at nodes 3 & 4, +X).
2. Keep the gravity pattern on its own Linear TS — the preload
runs as a 10-step ``LoadControl(0.1)`` ramp, exactly as the
Tcl walkthrough does.
3. Replace the StaticCase with two cases: a preload ``StaticCase``
(id 100) for gravity, and a ``PushoverCase`` whose
``preload_case_ids=[100]`` references it. The pushover's own
``pattern_ids`` holds only the lateral reference.
"""
proj = build_rc_frame_gravity()
proj.meta.name = "RC Frame Pushover (OpenSees Ex 3.2)"
proj.meta.description = (
"Ex 3 gravity preload (StaticCase) + lateral reference load + "
"DisplacementControl pushover on node 3 (DOF 1) to 15 in, "
"chained via preload_case_ids."
)
proj.time_series = [
LinearTimeSeries(id=1, name="Gravity"),
LinearTimeSeries(id=2, name="Lateral"),
]
proj.load_patterns = [
PlainLoadPattern(
id=1, name="Gravity",
time_series_id=1,
nodal_loads=[
NodalLoad(node_id=3, forces=(0, -P_LOAD, 0, 0, 0, 0)),
NodalLoad(node_id=4, forces=(0, -P_LOAD, 0, 0, 0, 0)),
],
),
# Lateral reference — scaled by the DisplacementControl factor.
PlainLoadPattern(
id=2, name="Lateral",
time_series_id=2,
nodal_loads=[
NodalLoad(node_id=3, forces=(H_LATERAL, 0, 0, 0, 0, 0)),
NodalLoad(node_id=4, forces=(H_LATERAL, 0, 0, 0, 0, 0)),
],
),
]
proj.analyses = [
StaticCase(
id=100, name="Gravity-Preload",
pattern_ids=[1],
n_steps=10, load_factor_increment=0.1,
system="BandGeneral", constraints="Transformation",
integrator="LoadControl", algorithm="Newton",
test="NormDispIncr", tolerance=1e-12, max_iter=10,
),
PushoverCase(
id=1, name="Pushover",
preload_case_ids=[100],
pattern_ids=[2], # lateral only
control_node=3, control_dof=1, # Ux at top-left joint
target_disp=D_TARGET,
step_size=D_STEP,
base_nodes=[1, 2],
system="BandGeneral", constraints="Transformation",
algorithm="Newton",
test="NormDispIncr", tolerance=1e-12, max_iter=10,
),
]
return proj
def main() -> None:
project = build_rc_frame_pushover()
project.validate_references()
print(f"Built '{project.meta.name}'")
print(f" H = {H_LATERAL} kip reference, dU = {D_STEP} in, target = {D_TARGET} in")
print(f" Steps = {int(D_TARGET / D_STEP)}")
out_path = Path(__file__).with_suffix(".osmodel")
save_project(project, out_path)
print(f"Saved -> {out_path}")
restored = load_project(out_path)
restored.validate_references()
assert restored.model_dump(by_alias=True) == project.model_dump(by_alias=True)
print("Round-trip OK.")
if __name__ == "__main__":
main()