232 lines
8.3 KiB
Python
232 lines
8.3 KiB
Python
|
|
"""RC Frame Gravity Analysis — OpenSees Examples Manual, Example 3.
|
|||
|
|
|
|||
|
|
Single-bay, single-storey RC portal frame under gravity (two 180-kip
|
|||
|
|
nodal loads on top joints). Columns are nonlinear forceBeamColumn
|
|||
|
|
elements with the fibre section from the Moment-Curvature example;
|
|||
|
|
the beam is an elasticBeamColumn with stiffness (A, E, Iz) =
|
|||
|
|
(360, 4030, 8640) — matching the Tcl reference at:
|
|||
|
|
https://opensees.berkeley.edu/wiki/index.php?title=RC_Portal_Frame
|
|||
|
|
|
|||
|
|
Model (kip-in-ksi):
|
|||
|
|
node 3 ─────── elasticBeam 3 ─────── node 4
|
|||
|
|
│ │
|
|||
|
|
forceBC 1 forceBC 2
|
|||
|
|
│ │
|
|||
|
|
node 1 node 2
|
|||
|
|
(fixed) (fixed)
|
|||
|
|
|
|||
|
|
Width 360", height 144", columns fibre-section RC (Concrete01 +
|
|||
|
|
Steel01). Load pattern: 180 kip ↓ at each top node, Linear time series,
|
|||
|
|
LoadControl 0.1 × 10 steps = full gravity.
|
|||
|
|
|
|||
|
|
Expected terminal state (nodes 3 & 4): Uy ≈ -0.0203 in, Ux ≈ 0,
|
|||
|
|
column axial force ≈ 180 kip compression.
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
from pathlib import Path
|
|||
|
|
|
|||
|
|
from otko.core import (
|
|||
|
|
Concrete01,
|
|||
|
|
CoordinateGridSystem,
|
|||
|
|
ElasticBeamColumn,
|
|||
|
|
ElasticSection,
|
|||
|
|
FiberSection,
|
|||
|
|
ForceBeamColumn,
|
|||
|
|
GridSystem,
|
|||
|
|
LinearTimeSeries,
|
|||
|
|
NodalLoad,
|
|||
|
|
Node,
|
|||
|
|
PlainLoadPattern,
|
|||
|
|
Project,
|
|||
|
|
ProjectMeta,
|
|||
|
|
RectangularPatch,
|
|||
|
|
StaticCase,
|
|||
|
|
Steel01,
|
|||
|
|
StraightLayer,
|
|||
|
|
UnitSystem,
|
|||
|
|
make_grid_lines,
|
|||
|
|
)
|
|||
|
|
from otko.services import load_project, save_project
|
|||
|
|
|
|||
|
|
|
|||
|
|
# Frame geometry (inches).
|
|||
|
|
WIDTH = 360.0
|
|||
|
|
HEIGHT = 144.0
|
|||
|
|
|
|||
|
|
# Column section parameters (same as Moment-Curvature example).
|
|||
|
|
COL_WIDTH = 15.0
|
|||
|
|
COL_DEPTH = 24.0
|
|||
|
|
COVER = 1.5
|
|||
|
|
AS_BAR = 0.60
|
|||
|
|
|
|||
|
|
# Material properties (kip, in, ksi).
|
|||
|
|
CONC_CORE_FPC = -6.0
|
|||
|
|
CONC_COVER_FPC = -5.0
|
|||
|
|
STEEL_FY = 60.0
|
|||
|
|
# Steel Young's modulus: 30000 ksi (matches OpenSees Wiki, which sources
|
|||
|
|
# the MK example's $E = 30000). Some Tcl reprints show 3000 — that's a
|
|||
|
|
# typo; 3000 gives Uy ≈ -0.0203 instead of the reference -0.01837.
|
|||
|
|
STEEL_E = 30000.0
|
|||
|
|
STEEL_B = 0.01
|
|||
|
|
|
|||
|
|
# Beam elastic properties.
|
|||
|
|
BEAM_A = 360.0
|
|||
|
|
BEAM_E = 4030.0
|
|||
|
|
BEAM_IZ = 8640.0
|
|||
|
|
|
|||
|
|
# Gravity load.
|
|||
|
|
P_LOAD = 180.0 # kip, pointing -Y (compression on columns)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def build_rc_frame_gravity() -> Project:
|
|||
|
|
y1 = COL_DEPTH / 2.0 # 12
|
|||
|
|
z1 = COL_WIDTH / 2.0 # 7.5
|
|||
|
|
|
|||
|
|
return Project(
|
|||
|
|
meta=ProjectMeta(
|
|||
|
|
name="RC Frame Gravity (OpenSees Ex 3)",
|
|||
|
|
author="OpenSees Examples Manual",
|
|||
|
|
description=(
|
|||
|
|
"1-bay 1-storey portal frame, nonlinear fibre columns + "
|
|||
|
|
"elastic beam, 10-step LoadControl gravity pushdown."
|
|||
|
|
),
|
|||
|
|
units=UnitSystem.US_IN_KIP,
|
|||
|
|
),
|
|||
|
|
ndm=2, ndf=3,
|
|||
|
|
coord_systems=[
|
|||
|
|
CoordinateGridSystem(
|
|||
|
|
name="Global",
|
|||
|
|
grid=GridSystem(
|
|||
|
|
x_grid_lines=make_grid_lines("X", [0.0, WIDTH]),
|
|||
|
|
y_grid_lines=make_grid_lines("Y", [0.0, HEIGHT]),
|
|||
|
|
z_grid_lines=make_grid_lines("Z", [0.0]),
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
nodes=[
|
|||
|
|
Node(id=1, name="Base-L",
|
|||
|
|
coords=(0.0, 0.0, 0.0),
|
|||
|
|
restraint=(True, True, False, False, False, True)),
|
|||
|
|
Node(id=2, name="Base-R",
|
|||
|
|
coords=(WIDTH, 0.0, 0.0),
|
|||
|
|
restraint=(True, True, False, False, False, True)),
|
|||
|
|
Node(id=3, name="Top-L", coords=(0.0, HEIGHT, 0.0)),
|
|||
|
|
Node(id=4, name="Top-R", coords=(WIDTH, HEIGHT, 0.0)),
|
|||
|
|
],
|
|||
|
|
materials=[
|
|||
|
|
Concrete01(id=1, name="Core-Conc",
|
|||
|
|
fpc=CONC_CORE_FPC, epsc0=-0.004,
|
|||
|
|
fpcu=-5.0, epsU=-0.014),
|
|||
|
|
Concrete01(id=2, name="Cover-Conc",
|
|||
|
|
fpc=CONC_COVER_FPC, epsc0=-0.002,
|
|||
|
|
fpcu=0.0, epsU=-0.006),
|
|||
|
|
Steel01(id=3, name="Steel-60",
|
|||
|
|
Fy=STEEL_FY, E0=STEEL_E, b=STEEL_B),
|
|||
|
|
],
|
|||
|
|
sections=[
|
|||
|
|
# Fibre section for the columns (MK recipe).
|
|||
|
|
FiberSection(
|
|||
|
|
id=1, name="RC-Col",
|
|||
|
|
patches=[
|
|||
|
|
RectangularPatch(
|
|||
|
|
material_id=1, n_fib_y=10, n_fib_z=1,
|
|||
|
|
y_i=COVER - y1, z_i=COVER - z1,
|
|||
|
|
y_j=y1 - COVER, z_j=z1 - COVER,
|
|||
|
|
),
|
|||
|
|
RectangularPatch(
|
|||
|
|
material_id=2, n_fib_y=10, n_fib_z=1,
|
|||
|
|
y_i=-y1, z_i=z1 - COVER,
|
|||
|
|
y_j=y1, z_j=z1,
|
|||
|
|
),
|
|||
|
|
RectangularPatch(
|
|||
|
|
material_id=2, n_fib_y=10, n_fib_z=1,
|
|||
|
|
y_i=-y1, z_i=-z1,
|
|||
|
|
y_j=y1, z_j=COVER - z1,
|
|||
|
|
),
|
|||
|
|
RectangularPatch(
|
|||
|
|
material_id=2, n_fib_y=2, n_fib_z=1,
|
|||
|
|
y_i=-y1, z_i=COVER - z1,
|
|||
|
|
y_j=COVER - y1, z_j=z1 - COVER,
|
|||
|
|
),
|
|||
|
|
RectangularPatch(
|
|||
|
|
material_id=2, n_fib_y=2, n_fib_z=1,
|
|||
|
|
y_i=y1 - COVER, z_i=COVER - z1,
|
|||
|
|
y_j=y1, z_j=z1 - COVER,
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
layers=[
|
|||
|
|
StraightLayer(
|
|||
|
|
material_id=3, n_bars=3, bar_area=AS_BAR,
|
|||
|
|
y_start=y1 - COVER, z_start=z1 - COVER,
|
|||
|
|
y_end=y1 - COVER, z_end=COVER - z1,
|
|||
|
|
),
|
|||
|
|
StraightLayer(
|
|||
|
|
material_id=3, n_bars=2, bar_area=AS_BAR,
|
|||
|
|
y_start=0.0, z_start=z1 - COVER,
|
|||
|
|
y_end=0.0, z_end=COVER - z1,
|
|||
|
|
),
|
|||
|
|
StraightLayer(
|
|||
|
|
material_id=3, n_bars=3, bar_area=AS_BAR,
|
|||
|
|
y_start=COVER - y1, z_start=z1 - COVER,
|
|||
|
|
y_end=COVER - y1, z_end=COVER - z1,
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
# Elastic section for the beam.
|
|||
|
|
ElasticSection(
|
|||
|
|
id=2, name="Beam",
|
|||
|
|
E=BEAM_E, A=BEAM_A, Iz=BEAM_IZ,
|
|||
|
|
Iy=BEAM_IZ, G=1500.0, J=1.0, # placeholders for 3D round-trip
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
elements=[
|
|||
|
|
# Columns — fibre-section forceBeamColumn.
|
|||
|
|
ForceBeamColumn(id=1, name="Col-L",
|
|||
|
|
nodes=(1, 3), section_id=1,
|
|||
|
|
integration_points=5, geom_transf="Linear"),
|
|||
|
|
ForceBeamColumn(id=2, name="Col-R",
|
|||
|
|
nodes=(2, 4), section_id=1,
|
|||
|
|
integration_points=5, geom_transf="Linear"),
|
|||
|
|
# Beam — elastic.
|
|||
|
|
ElasticBeamColumn(id=3, name="Beam",
|
|||
|
|
nodes=(3, 4), section_id=2,
|
|||
|
|
geom_transf="Linear"),
|
|||
|
|
],
|
|||
|
|
time_series=[LinearTimeSeries(id=1, name="Gravity")],
|
|||
|
|
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)),
|
|||
|
|
],
|
|||
|
|
)],
|
|||
|
|
analyses=[StaticCase(
|
|||
|
|
id=1, name="Gravity",
|
|||
|
|
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,
|
|||
|
|
)],
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def main() -> None:
|
|||
|
|
project = build_rc_frame_gravity()
|
|||
|
|
project.validate_references()
|
|||
|
|
print(f"Built '{project.meta.name}'")
|
|||
|
|
print(f" ndm={project.ndm}, ndf={project.ndf}, units={project.meta.units.value}")
|
|||
|
|
print(f" Frame: {WIDTH}in x {HEIGHT}in, P = -{P_LOAD} kip at each top node")
|
|||
|
|
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()
|