90 lines
2.8 KiB
Python
90 lines
2.8 KiB
Python
|
|
"""Load-combination integration: 1.2×D + 1.6×L by superposition == one combined run."""
|
|||
|
|
|
|||
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
import math
|
|||
|
|
|
|||
|
|
import pytest
|
|||
|
|
|
|||
|
|
ops = pytest.importorskip("openseespy.opensees") # skip if OpenSeesPy not installed
|
|||
|
|
|
|||
|
|
from otko.core import ( # noqa: E402
|
|||
|
|
ElasticBeamColumn,
|
|||
|
|
ElasticSection,
|
|||
|
|
LinearTimeSeries,
|
|||
|
|
LoadCombination,
|
|||
|
|
LoadCombinationItem,
|
|||
|
|
NodalLoad,
|
|||
|
|
Node,
|
|||
|
|
PlainLoadPattern,
|
|||
|
|
Project,
|
|||
|
|
StaticCase,
|
|||
|
|
)
|
|||
|
|
from otko.services import OpenSeesRunner, evaluate_combination # noqa: E402
|
|||
|
|
from otko.services.results import StaticResults # noqa: E402
|
|||
|
|
|
|||
|
|
pytestmark = pytest.mark.slow
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _two_pattern_cantilever() -> Project:
|
|||
|
|
elastic_mod = 200e9
|
|||
|
|
area = 0.01
|
|||
|
|
inertia = 8.333e-6
|
|||
|
|
return Project(
|
|||
|
|
ndm=2,
|
|||
|
|
ndf=3,
|
|||
|
|
nodes=[
|
|||
|
|
Node(
|
|||
|
|
id=1,
|
|||
|
|
coords=(0.0, 0.0, 0.0),
|
|||
|
|
restraint=(True, True, False, False, False, True),
|
|||
|
|
),
|
|||
|
|
Node(id=2, coords=(5.0, 0.0, 0.0)),
|
|||
|
|
],
|
|||
|
|
sections=[ElasticSection(id=1, E=elastic_mod, A=area, Iz=inertia)],
|
|||
|
|
elements=[ElasticBeamColumn(id=1, nodes=(1, 2), section_id=1)],
|
|||
|
|
time_series=[LinearTimeSeries(id=1), LinearTimeSeries(id=2)],
|
|||
|
|
load_patterns=[
|
|||
|
|
PlainLoadPattern(
|
|||
|
|
id=1,
|
|||
|
|
name="Dead",
|
|||
|
|
time_series_id=1,
|
|||
|
|
nodal_loads=[NodalLoad(node_id=2, forces=(0.0, -1000.0, 0.0, 0.0, 0.0, 0.0))],
|
|||
|
|
),
|
|||
|
|
PlainLoadPattern(
|
|||
|
|
id=2,
|
|||
|
|
name="Live",
|
|||
|
|
time_series_id=2,
|
|||
|
|
nodal_loads=[NodalLoad(node_id=2, forces=(0.0, -500.0, 0.0, 0.0, 0.0, 0.0))],
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
analyses=[
|
|||
|
|
StaticCase(id=1, name="Dead", pattern_ids=[1]),
|
|||
|
|
StaticCase(id=2, name="Live", pattern_ids=[2]),
|
|||
|
|
],
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_superposition_matches_combined_run() -> None:
|
|||
|
|
"""1.2*Dead + 1.6*Live via evaluate_combination == single StaticCase run."""
|
|||
|
|
proj = _two_pattern_cantilever()
|
|||
|
|
dead = OpenSeesRunner(proj).run(proj.analyses[0])
|
|||
|
|
live = OpenSeesRunner(proj).run(proj.analyses[1])
|
|||
|
|
combo = LoadCombination(
|
|||
|
|
id=1,
|
|||
|
|
name="1.2D+1.6L",
|
|||
|
|
kind="Linear",
|
|||
|
|
items=[
|
|||
|
|
LoadCombinationItem(case_id=1, factor=1.2),
|
|||
|
|
LoadCombinationItem(case_id=2, factor=1.6),
|
|||
|
|
],
|
|||
|
|
)
|
|||
|
|
combined = evaluate_combination({1: dead, 2: live}, combo)
|
|||
|
|
assert isinstance(combined, StaticResults)
|
|||
|
|
# Single run with both patterns factored identically.
|
|||
|
|
direct = OpenSeesRunner(proj).run(
|
|||
|
|
StaticCase(id=3, name="direct", pattern_ids=[1, 2], pattern_factors={1: 1.2, 2: 1.6})
|
|||
|
|
)
|
|||
|
|
assert math.isclose(
|
|||
|
|
combined.disp(node_id=2, dof=2), direct.disp(node_id=2, dof=2), rel_tol=1e-9
|
|||
|
|
)
|