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
121 lines
3.9 KiB
Python
121 lines
3.9 KiB
Python
"""Cantilever beam example — ideal for showing force diagrams.
|
|
|
|
A 5 m horizontal cantilever fixed at the left end, with a point load
|
|
at the tip and a distributed-equivalent set of nodal loads along the
|
|
span. Pure bending response makes for instantly recognisable N/V/M
|
|
diagrams.
|
|
|
|
Run from the repository root:
|
|
|
|
python examples/cantilever.py
|
|
|
|
Produces ``examples/cantilever.osmodel``.
|
|
Open in the GUI, run "Tip-Load" Static case, then:
|
|
|
|
Display → Show Force Diagram → V2 (shear) → constant within each segment
|
|
Display → Show Force Diagram → M3 (moment) → linear, max at the fixed end
|
|
Display → Show Deformed Shape → classic cantilever curve
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
from otko.core import (
|
|
ElasticBeamColumn,
|
|
ElasticSection,
|
|
LinearTimeSeries,
|
|
ModalCase,
|
|
NodalLoad,
|
|
Node,
|
|
PlainLoadPattern,
|
|
Project,
|
|
ProjectMeta,
|
|
StaticCase,
|
|
UniformElementLoad,
|
|
UnitSystem,
|
|
)
|
|
from otko.services import load_project, save_project
|
|
|
|
|
|
def build_cantilever() -> Project:
|
|
"""5 m cantilever discretised into 5 elements (1 m each)."""
|
|
n_segments = 5
|
|
seg_len = 1.0
|
|
nodes = []
|
|
for i in range(n_segments + 1):
|
|
x = i * seg_len
|
|
restraint = (True,) * 6 if i == 0 else (False,) * 6
|
|
# Lump some mass at every free node so modal works too.
|
|
mass = (1000.0, 1000.0, 1000.0, 0.0, 0.0, 0.0) if i > 0 else (0.0,) * 6
|
|
nodes.append(Node(id=i + 1, name=f"N{i+1}",
|
|
coords=(x, 0.0, 0.0), restraint=restraint, mass=mass))
|
|
|
|
elements = [
|
|
ElasticBeamColumn(id=i + 1, name=f"E{i+1}",
|
|
nodes=(i + 1, i + 2), section_id=1)
|
|
for i in range(n_segments)
|
|
]
|
|
|
|
return Project(
|
|
meta=ProjectMeta(name="Cantilever", author="Ozan", units=UnitSystem.METRIC),
|
|
ndm=3, ndf=6,
|
|
nodes=nodes,
|
|
sections=[
|
|
ElasticSection(
|
|
id=1, name="W12x40",
|
|
E=200e9, A=0.0076,
|
|
Iz=2.0e-4, Iy=4.5e-5,
|
|
G=80e9, J=8.5e-7,
|
|
),
|
|
],
|
|
elements=elements,
|
|
time_series=[LinearTimeSeries(id=1, name="Ramp")],
|
|
load_patterns=[
|
|
# Tip horizontal load (y) of 10 kN — this produces shear V2
|
|
# and moment M3 about the strong axis, the conventional 2D
|
|
# "in-plane bending" components.
|
|
PlainLoadPattern(
|
|
id=1, name="TipLoad",
|
|
time_series_id=1,
|
|
nodal_loads=[
|
|
NodalLoad(node_id=n_segments + 1,
|
|
forces=(0.0, -10_000.0, 0.0, 0, 0, 0)),
|
|
],
|
|
),
|
|
# Uniform distributed load wy = -2 kN/m along every element.
|
|
# Produces parabolic moment diagram, max at fixed end
|
|
# (M_max = q·L²/2 = 2·5²/2 = 25 kN·m).
|
|
PlainLoadPattern(
|
|
id=2, name="UniformLoad",
|
|
time_series_id=1,
|
|
element_loads=[
|
|
UniformElementLoad(element_id=i + 1, wy=-2_000.0)
|
|
for i in range(n_segments)
|
|
],
|
|
),
|
|
],
|
|
analyses=[
|
|
StaticCase(id=1, name="Tip-Load", pattern_ids=[1]),
|
|
StaticCase(id=2, name="Uniform-Load", pattern_ids=[2]),
|
|
ModalCase(id=3, name="Modal-3", n_modes=3),
|
|
],
|
|
)
|
|
|
|
|
|
def main() -> None:
|
|
project = build_cantilever()
|
|
project.validate_references()
|
|
print(f"Built '{project.meta.name}' — {len(project.nodes)} nodes, "
|
|
f"{len(project.elements)} elements, {len(project.analyses)} cases.")
|
|
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()
|