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
- core/units: two dominant systems (Metric m/kN, Imperial ft/kip) with display conversion helpers, legacy 4-system migration in ProjectMeta - dialogs/docks: unit-aware material, section, case, load, grid and results labels; diagram renderer unit labels; render controls update - docs: add consistent_units.md; regen examples/*.osmodel artifacts - tests: update persistence/phase8/project/unit-labels for new systems
148 lines
4.7 KiB
Python
148 lines
4.7 KiB
Python
"""Integration test: Concrete04 in a fiber-section cantilever.
|
|
|
|
Verifies that the full stack (model → runner → OpenSeesPy → result) works
|
|
end-to-end with Concrete04 (Popovics concrete) as the sole material.
|
|
|
|
Reference: for very small compressive strains the Popovics curve is
|
|
linear with slope Ec, so the axial shortening of a column under a
|
|
small axial load is:
|
|
|
|
delta = P * L / (Ec * A)
|
|
|
|
with negligible Popovics nonlinearity at the applied strain level.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
pytest.importorskip("openseespy")
|
|
|
|
from otko.core import ( # noqa: E402
|
|
Concrete04,
|
|
FiberSection,
|
|
ForceBeamColumn,
|
|
LinearTimeSeries,
|
|
NodalLoad,
|
|
Node,
|
|
PlainLoadPattern,
|
|
Project,
|
|
ProjectMeta,
|
|
RectangularPatch,
|
|
StaticCase,
|
|
UnitSystem,
|
|
)
|
|
from otko.services.opensees_runner import OpenSeesRunner # noqa: E402
|
|
|
|
# ── Model constants ────────────────────────────────────────────────────────────
|
|
L = 1.0 # column height [m]
|
|
B = H = 0.3 # cross-section dimensions [m]
|
|
A = B * H # section area [m²]
|
|
|
|
FC = -30e6 # peak compressive strength [Pa] (negative)
|
|
EPSC0 = -0.002 # strain at peak strength (negative)
|
|
EPSCU = -0.005 # ultimate compressive strain (negative)
|
|
EC = 30e9 # initial tangent modulus [Pa]
|
|
|
|
# Applied axial load: small enough (< 1 % of capacity) that the Popovics
|
|
# curve is indistinguishable from its linear tangent at origin.
|
|
P_AXIAL = -1200.0 # N (downward → compressive)
|
|
|
|
# Analytical axial shortening: P * L / (Ec * A)
|
|
EXPECTED_UY = P_AXIAL * L / (EC * A) # ≈ -1.333e-7 m
|
|
|
|
|
|
def _build_project() -> Project:
|
|
return Project(
|
|
meta=ProjectMeta(
|
|
name="Concrete04 fiber-section cantilever",
|
|
units=UnitSystem.METRIC,
|
|
),
|
|
ndm=2,
|
|
ndf=3,
|
|
nodes=[
|
|
Node(id=1, name="Base", coords=(0.0, 0.0, 0.0),
|
|
restraint=(True, True, True, False, False, False)),
|
|
Node(id=2, name="Top", coords=(0.0, L, 0.0)),
|
|
],
|
|
materials=[
|
|
Concrete04(
|
|
id=1, name="C30-Popovics",
|
|
fpc=FC, epsc0=EPSC0, epscu=EPSCU, Ec=EC,
|
|
),
|
|
],
|
|
sections=[
|
|
FiberSection(
|
|
id=1, name="RC-Fiber",
|
|
patches=[
|
|
RectangularPatch(
|
|
material_id=1,
|
|
n_fib_y=4, n_fib_z=4,
|
|
y_i=-H / 2, z_i=-B / 2,
|
|
y_j= H / 2, z_j= B / 2,
|
|
),
|
|
],
|
|
),
|
|
],
|
|
elements=[
|
|
ForceBeamColumn(
|
|
id=1, name="Column",
|
|
nodes=(1, 2),
|
|
section_id=1,
|
|
integration_points=3,
|
|
geom_transf="Linear",
|
|
),
|
|
],
|
|
time_series=[LinearTimeSeries(id=1, name="Ramp")],
|
|
load_patterns=[
|
|
PlainLoadPattern(
|
|
id=1, name="Gravity", time_series_id=1,
|
|
nodal_loads=[
|
|
NodalLoad(node_id=2, forces=(0.0, P_AXIAL, 0.0, 0.0, 0.0, 0.0)),
|
|
],
|
|
),
|
|
],
|
|
analyses=[
|
|
StaticCase(
|
|
id=1, name="Gravity",
|
|
pattern_ids=[1],
|
|
n_steps=1,
|
|
load_factor_increment=1.0,
|
|
system="BandGeneral",
|
|
constraints="Plain",
|
|
integrator="LoadControl",
|
|
algorithm="Newton",
|
|
test="NormDispIncr",
|
|
tolerance=1e-12,
|
|
max_iter=10,
|
|
),
|
|
],
|
|
)
|
|
|
|
|
|
def test_concrete04_gravity_axial_shortening() -> None:
|
|
"""Axial shortening under small gravity load matches the linear reference."""
|
|
proj = _build_project()
|
|
case = proj.analyses[0]
|
|
result = OpenSeesRunner(proj).run(case)
|
|
|
|
# Uy at the top node (DOF index 1 = Y in 2D 3-DOF).
|
|
uy = result.node_disp[2][0, 1]
|
|
|
|
# Tolerance: 0.1 % relative — the Popovics curve at the applied strain
|
|
# level (|ε| ≈ 1.5e-8) deviates from linear by < 1e-12 relative.
|
|
assert uy == pytest.approx(EXPECTED_UY, rel=1e-3)
|
|
|
|
|
|
def test_concrete04_with_tension_does_not_raise() -> None:
|
|
"""Smoke-test: optional tensile branch accepted by OpenSeesPy without error."""
|
|
proj = _build_project()
|
|
# Replace material with tensile-branch variant.
|
|
proj.materials[0] = Concrete04(
|
|
id=1, name="C30-WithTension",
|
|
fpc=FC, epsc0=EPSC0, epscu=EPSCU, Ec=EC,
|
|
fct=3.0e6, et=1e-4,
|
|
)
|
|
case = proj.analyses[0]
|
|
result = OpenSeesRunner(proj).run(case)
|
|
assert result.node_disp[2][0, 1] == pytest.approx(EXPECTED_UY, rel=1e-3)
|