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
121
examples/cantilever.py
Normal file
121
examples/cantilever.py
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
"""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.SI_M_N),
|
||||
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()
|
||||
Loading…
Reference in a new issue