feat: initial otko import
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
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
This commit is contained in:
commit
612936a00b
540 changed files with 174136 additions and 0 deletions
249
examples/moment_curvature.py
Normal file
249
examples/moment_curvature.py
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
"""Moment-Curvature Example — OpenSees Examples Manual, Example 2.
|
||||
|
||||
Reinforced-concrete column cross-section, fibre discretisation, axial
|
||||
preload + monotonic moment pushover. Mirrors the OpenSees Tcl script
|
||||
at https://opensees.berkeley.edu/wiki/index.php?title=Moment_Curvature_Example
|
||||
|
||||
Model
|
||||
-----
|
||||
Two coincident nodes linked by a ``zeroLengthSection`` carrying the
|
||||
RC fibre section. Node 1 is fully restrained; node 2 is free in Ux
|
||||
(so axial can shorten) and Rz (the curvature DOF). A constant axial
|
||||
load P = -180 kip is applied first via LoadControl(0); then a
|
||||
linear reference moment pattern (Mz = 1 kip·in) is added and
|
||||
DisplacementControl on DOF 3 ramps the curvature to μ·Ky where
|
||||
μ = 15 and Ky is the elastic yield curvature estimate.
|
||||
|
||||
Units: kip, in, ksi (UnitSystem.US_IN_KIP).
|
||||
|
||||
GUI walkthrough: File → Open → moment_curvature.osmodel, Options →
|
||||
Set Display Units → US (in, kip, kip·s²/in, s, ksi), Analyze → Run →
|
||||
MK, Display → Show Pushover Curve.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from otko.core import (
|
||||
Concrete01,
|
||||
ConstantTimeSeries,
|
||||
CoordinateGridSystem,
|
||||
FiberSection,
|
||||
GridSystem,
|
||||
LinearTimeSeries,
|
||||
NodalLoad,
|
||||
Node,
|
||||
PlainLoadPattern,
|
||||
Project,
|
||||
ProjectMeta,
|
||||
PushoverCase,
|
||||
RectangularPatch,
|
||||
Steel01,
|
||||
StraightLayer,
|
||||
UnitSystem,
|
||||
ZeroLengthSectionElement,
|
||||
make_grid_lines,
|
||||
)
|
||||
from otko.services import load_project, save_project
|
||||
|
||||
|
||||
# Cross-section parameters (kip-in-ksi, from the Tcl example).
|
||||
COL_WIDTH = 15.0 # z-direction dimension
|
||||
COL_DEPTH = 24.0 # y-direction dimension
|
||||
COVER = 1.5
|
||||
AS_BAR = 0.60 # area of one #7 rebar
|
||||
|
||||
FY = 60.0 # steel yield stress, ksi
|
||||
E_STEEL = 30000.0 # steel Young's modulus, ksi
|
||||
HARDENING = 0.01 # strain-hardening ratio
|
||||
|
||||
P_AXIAL = -180.0 # kip, compression
|
||||
MU = 15 # target curvature ductility
|
||||
NUM_INCR = 100 # DisplacementControl increments
|
||||
|
||||
|
||||
def build_moment_curvature() -> Project:
|
||||
"""Build the OpenSees Example 2 Moment-Curvature project."""
|
||||
y1 = COL_DEPTH / 2.0 # 12
|
||||
z1 = COL_WIDTH / 2.0 # 7.5
|
||||
d = COL_DEPTH - COVER # 22.5
|
||||
|
||||
# Yield curvature estimate, assumed elastic + top/bottom steel only.
|
||||
eps_y = FY / E_STEEL # 0.002
|
||||
ky = eps_y / (0.7 * d) # ≈ 1.27e-4
|
||||
max_k = ky * MU # ≈ 1.905e-3
|
||||
d_k = max_k / NUM_INCR # per-step curvature increment
|
||||
|
||||
return Project(
|
||||
meta=ProjectMeta(
|
||||
name="Moment-Curvature (OpenSees Ex 2)",
|
||||
author="OpenSees Examples Manual",
|
||||
description=(
|
||||
"RC column fibre section — constant axial P + DisplacementControl "
|
||||
"curvature pushover. Kip-in-ksi units throughout."
|
||||
),
|
||||
units=UnitSystem.US_IN_KIP,
|
||||
),
|
||||
ndm=2, ndf=3,
|
||||
coord_systems=[
|
||||
# A tiny grid at the section origin so the two coincident
|
||||
# nodes have a visual anchor in the canvas.
|
||||
CoordinateGridSystem(
|
||||
name="Global",
|
||||
grid=GridSystem(
|
||||
x_grid_lines=make_grid_lines("X", [0.0]),
|
||||
y_grid_lines=make_grid_lines("Y", [0.0]),
|
||||
z_grid_lines=make_grid_lines("Z", [0.0]),
|
||||
),
|
||||
),
|
||||
],
|
||||
nodes=[
|
||||
# Node 1 — fully clamped.
|
||||
Node(id=1, name="Support",
|
||||
coords=(0.0, 0.0, 0.0),
|
||||
restraint=(True, True, False, False, False, True)),
|
||||
# Node 2 — free in Ux and Rz (axial + curvature).
|
||||
Node(id=2, name="Crown",
|
||||
coords=(0.0, 0.0, 0.0),
|
||||
restraint=(False, True, False, False, False, False)),
|
||||
],
|
||||
materials=[
|
||||
# Core concrete — confined (tag 1).
|
||||
Concrete01(id=1, name="Core-Conc",
|
||||
fpc=-6.0, epsc0=-0.004,
|
||||
fpcu=-5.0, epsU=-0.014),
|
||||
# Cover concrete — unconfined (tag 2).
|
||||
Concrete01(id=2, name="Cover-Conc",
|
||||
fpc=-5.0, epsc0=-0.002,
|
||||
fpcu=0.0, epsU=-0.006),
|
||||
# Reinforcing steel — bilinear hardening (tag 3).
|
||||
Steel01(id=3, name="Steel-60",
|
||||
Fy=FY, E0=E_STEEL, b=HARDENING),
|
||||
],
|
||||
sections=[
|
||||
FiberSection(
|
||||
id=1, name="RC-Column",
|
||||
patches=[
|
||||
# Core — confined concrete inside the rebar ring.
|
||||
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,
|
||||
),
|
||||
# Top cover (unconfined).
|
||||
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,
|
||||
),
|
||||
# Bottom cover.
|
||||
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,
|
||||
),
|
||||
# Left cover.
|
||||
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,
|
||||
),
|
||||
# Right 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=[
|
||||
# Top rebar (3 × #7).
|
||||
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,
|
||||
),
|
||||
# Middle rebar (2 × #7).
|
||||
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,
|
||||
),
|
||||
# Bottom rebar (3 × #7).
|
||||
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,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
elements=[
|
||||
ZeroLengthSectionElement(
|
||||
id=1, name="MK-Link",
|
||||
nodes=(1, 2), section_id=1,
|
||||
),
|
||||
],
|
||||
time_series=[
|
||||
ConstantTimeSeries(id=1, name="AxialP"),
|
||||
LinearTimeSeries(id=2, name="RefMoment"),
|
||||
],
|
||||
load_patterns=[
|
||||
# Constant axial preload at node 2 — Fx = P (compression).
|
||||
PlainLoadPattern(
|
||||
id=1, name="AxialP",
|
||||
time_series_id=1,
|
||||
nodal_loads=[
|
||||
NodalLoad(node_id=2,
|
||||
forces=(P_AXIAL, 0, 0, 0, 0, 0)),
|
||||
],
|
||||
),
|
||||
# Linear reference moment — Mz = 1.0, DisplacementControl
|
||||
# scales this as it ramps curvature.
|
||||
PlainLoadPattern(
|
||||
id=2, name="RefMoment",
|
||||
time_series_id=2,
|
||||
nodal_loads=[
|
||||
NodalLoad(node_id=2,
|
||||
forces=(0, 0, 0, 0, 0, 1.0)),
|
||||
],
|
||||
),
|
||||
],
|
||||
analyses=[
|
||||
PushoverCase(
|
||||
id=1, name="MK",
|
||||
pattern_ids=[1, 2],
|
||||
control_node=2, control_dof=3, # Rz = curvature
|
||||
target_disp=max_k,
|
||||
step_size=d_k,
|
||||
base_nodes=[1],
|
||||
test="NormUnbalance",
|
||||
tolerance=1e-9, max_iter=25,
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
project = build_moment_curvature()
|
||||
project.validate_references()
|
||||
y1 = COL_DEPTH / 2.0
|
||||
eps_y = FY / E_STEEL
|
||||
ky = eps_y / (0.7 * (COL_DEPTH - COVER))
|
||||
print(f"Built '{project.meta.name}'")
|
||||
print(f" ndm={project.ndm}, ndf={project.ndf}, units={project.meta.units.value}")
|
||||
print(f" Estimated yield curvature Ky = {ky:.4e} 1/in")
|
||||
print(f" Target (mu * Ky) = {ky * MU:.4e} 1/in (mu = {MU})")
|
||||
|
||||
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()
|
||||
Loading…
Reference in a new issue