docs: rewrite READMEs dry and blunt, rename Studio to OTKO #1

Merged
smill merged 1 commit from rewrite-readmes into main 2026-09-08 02:43:47 -04:00
11 changed files with 233 additions and 263 deletions
Showing only changes of commit 3d809ca301 - Show all commits

docs: rewrite READMEs dry and blunt, rename Studio to OTKO
Some checks failed
CI / lint (pull_request) Has been cancelled
CI / test (macos-latest, 3.10) (pull_request) Has been cancelled
CI / test (macos-latest, 3.11) (pull_request) Has been cancelled
CI / test (macos-latest, 3.12) (pull_request) Has been cancelled
CI / test (ubuntu-latest, 3.10) (pull_request) Has been cancelled
CI / test (ubuntu-latest, 3.11) (pull_request) Has been cancelled
CI / test (ubuntu-latest, 3.12) (pull_request) Has been cancelled
CI / test (windows-latest, 3.10) (pull_request) Has been cancelled
CI / test (windows-latest, 3.11) (pull_request) Has been cancelled
CI / test (windows-latest, 3.12) (pull_request) Has been cancelled

smillmorel 2026-09-08 02:41:03 -04:00

View file

@ -1,8 +1,8 @@
# Contributing
Thanks for your interest. This project is in an early phase; the bar for
incoming changes is on architecture cleanliness rather than feature
breadth.
Early-stage project. The bar is architecture cleanliness, not feature
count. If your change breaks a layering rule below, it won't merge —
no matter how useful the feature.
## Dev setup

159
README.md
View file

@ -3,14 +3,13 @@
</p>
<p align="center">
A modern, SAP2000-style desktop GUI for
<a href="https://openseespydoc.readthedocs.io/">OpenSeesPy</a> —
built for structural and earthquake engineers who want a visual
modeling environment without leaving the OpenSees ecosystem.
A SAP2000-style desktop GUI for
<a href="https://openseespydoc.readthedocs.io/">OpenSeesPy</a>.
Draw the model, click run, look at the diagrams.
</p>
<p align="center">
<em>Status: Pre-alpha. Active development. APIs and file formats will change.</em>
<em>Pre-alpha. Under active development. APIs and file formats will change.</em>
</p>
---
@ -19,21 +18,20 @@
## Why
OpenSees is the gold-standard nonlinear FEM solver for earthquake
engineering, but its native interface is Tcl/Python scripts.
OTKO adds a visual front-end so you can:
OpenSees does nonlinear FEM well. Its user interface is a script.
OTKO puts a visual front-end on it:
- Click to draw nodes, frames, supports, and loads on a snapped grid.
- Draw nodes, frames, supports, and loads on a snapped grid.
- Assign materials, sections, and load patterns through dialogs.
- Run static, modal, pushover, and time-history analyses with progress
and cancel support.
- Inspect results visually — deformed shape, mode shapes, force
and cancel.
- Look at the results — deformed shape, mode shapes, force
diagrams, pushover curves, time-history plots, hysteresis loops.
- Save the model as a single `.osmodel` JSON file that round-trips
cleanly (diff-able in Git, scriptable from Python).
- Save the model as one `.osmodel` JSON file. Diffs cleanly in Git,
builds cleanly from Python.
Behind the GUI, the same `core` Pydantic model is fully usable from a
script or Jupyter notebook — the GUI is one frontend, not the only one.
Underneath, the `core` Pydantic model works fine from a script or
notebook. The GUI is a front-end, not the whole product.
## What works today
@ -53,11 +51,11 @@ script or Jupyter notebook — the GUI is one frontend, not the only one.
mode shapes, axial / shear / moment diagrams, pushover curves
(in display units), time-history plots, hysteresis loops,
response-spectrum SRSS / CQC, snapshot + video export.
- **Persistence** — projects save as a single JSON `.osmodel` file
(Pydantic-validated, round-trip-clean).
- **Examples** — 20+ verified examples bundled, including the OpenSees
Wiki Examples-1 through Example-4 family and a fiber-section RC frame
pushover. See [`examples/README.md`](examples/README.md).
- **Persistence** — one JSON `.osmodel` per project, Pydantic-validated,
round-trips clean.
- **Examples** — 20+ verified examples, including OpenSees
Wiki Examples 1–4 and a fiber-section RC frame pushover.
See [`examples/README.md`](examples/README.md).
## Tech stack
@ -74,22 +72,22 @@ script or Jupyter notebook — the GUI is one frontend, not the only one.
## Architecture
Strict MVVM + service layer. The `core` package is pure Python — no Qt,
no OpenSeesPy imports — and is fully unit-testable in isolation.
Strict MVVM + service layer. `core` is pure Python — no Qt,
no OpenSeesPy imports — and unit-tests in isolation.
```
views (Qt) → viewmodels → services (OpenSeesRunner, Persistence) → core (model)
```
See [`docs/architecture.md`](docs/architecture.md) for the long form,
including the canonical OpenSeesPy command sequence the runner emits.
Long version in [`docs/architecture.md`](docs/architecture.md),
including the OpenSeesPy command order the runner emits.
## Install (development)
**Desktop GUI** (includes Qt, PyVista, pyqtgraph, imageio):
**Desktop GUI** (Qt, PyVista, pyqtgraph, imageio):
```bash
git clone https://github.com/ogunc/otko.git
git clone ssh://git@smill-home.ddns.net/smill/otko.git
cd otko
python -m venv .venv
@ -99,22 +97,21 @@ source .venv/bin/activate # Linux / macOS
pip install -e ".[gui,dev]"
```
**Headless / web reuse** (core + services only, no Qt pulled in):
**Headless** (core + services only, no Qt):
```bash
pip install -e .
```
This installs only the headless base set (pydantic, numpy, h5py, openseespy).
It is the correct install for web backends, scripts, and Jupyter notebooks that
reuse `otko.core` or `otko.services` without the GUI.
That pulls pydantic, numpy, h5py, openseespy and nothing else.
Use it for scripts, notebooks, and web backends that reuse
`otko.core` or `otko.services` without the GUI.
Python 3.10+ is required. On Windows use **3.12+** — the `openseespywin==3.8.0.0`
wheel has no 3.11 build (`Requires-Python >=3.12`). Pin both
`openseespy==3.8.0.0` and `openseespywin==3.8.0.0` (already pinned
in `pyproject.toml`).
Python 3.10+. On Windows use **3.12+** — `openseespywin==3.8.0.0`
has no 3.11 wheel (`Requires-Python >=3.12`). Both pins already
live in `pyproject.toml`.
## Quick start — the 60-second tour
## Quick start
```bash
python -m otko
@ -122,22 +119,20 @@ python -m otko
Then:
1. **File → Open** → pick `examples/cantilever.osmodel`.
1. **File → Open** → `examples/cantilever.osmodel`.
2. **Analyze → Cases** → run `Tip-Load`.
3. **Display → Show Force Diagram** → component **M3** → linear moment
peaking at 50 kN·m at the fixed end. Component **V2** → constant
-10 kN.
4. **Display → Show Deformed Shape** → the classic cantilever curve.
3. **Display → Show Force Diagram** → **M3**: linear moment,
50 kN·m at the fixed end. **V2**: constant -10 kN.
4. **Display → Show Deformed Shape** → cantilever curve, as advertised.
For a nonlinear walkthrough, open `examples/portal_pushover.osmodel`,
run the `Push-X` case, then **Display → Show Pushover Curve** — you'll
see the elastic ramp followed by a yield plateau as the fiber-section
hinges form at the column bases.
Nonlinear version: open `examples/portal_pushover.osmodel`,
run `Push-X`, **Display → Show Pushover Curve**. Elastic ramp,
then a yield plateau as the base hinges form.
## Run the test suite
```bash
pytest tests/unit # pure-logic tests, milliseconds
pytest tests/unit # pure logic, milliseconds
pytest tests/gui # Qt event-loop tests (pytest-qt)
pytest tests/integration # real OpenSeesPy runs on bundled examples
```
@ -147,57 +142,47 @@ CI runs lint + the non-`slow` subset on Linux / macOS / Windows
## Roadmap
See [`docs/roadmap.md`](docs/roadmap.md) for the phase-by-phase plan.
Phases 0–7 (modeling, analysis, post-processing) are largely done.
Phase 8 (earthquake-engineering primitives — isolators, ground-motion
library, IDA, fiber-section editor polish) is the active edge.
[`docs/roadmap.md`](docs/roadmap.md) has the phase-by-phase plan.
Phases 0–7 (modeling, analysis, post-processing) are mostly done.
Phase 8 (isolators, ground-motion library, IDA, fiber-section
editor polish) is where the open work is.
## We're looking for collaborators
## Collaborators wanted
This project is most useful to researchers and engineers who already
work with OpenSees and want a faster path from "idea" to "model" —
**and who would rather build that path together than alone.**
Most useful to people who already work with OpenSees and want a
shorter path from idea to model — and would rather build it together
than alone. Open an issue or say hi if you are:
If any of the following sounds like you, please open an issue or
say hi:
- A **structural / earthquake engineer** who knows OpenSees Tcl
or OpenSeesPy and can tell us when a feature is almost right
but not quite.
- A **researcher** running pushover, IDA, or response-spectrum studies
who can check the GUI against hand-built scripts.
- A **Python / Qt developer** into scientific desktop apps,
VTK rendering, or Pydantic schema design.
- A **student** learning FEM and GUI architecture at the same time —
the examples and tests are meant to read as documentation.
- A **UX / icon designer** willing to argue about dialogs, toolbar
icons, and visual language.
- 🌉 **Structural / earthquake engineers** comfortable with OpenSees Tcl
or OpenSeesPy who can spot when a feature is "almost right but not
quite" — that calibration feedback is gold.
- 🧪 **Researchers** running pushover, IDA, or response-spectrum studies
who want to validate the GUI against their hand-built scripts.
- 🐍 **Python / Qt developers** interested in scientific desktop apps,
PyVista / VTK rendering, or Pydantic-driven schema design.
- 📚 **Students** who want to learn structural FEM and modern GUI
architecture at the same time — example walkthroughs and tests are
designed to read as documentation.
- 🎨 **UX / icon designers** willing to help shape the dialog set,
toolbar icons, and overall visual language.
Open issues, bug reports, and reproducible test cases are just as
valuable as code. See [`CONTRIBUTING.md`](CONTRIBUTING.md) for the dev
setup and the architectural rules enforced in review.
Bug reports and reproducible test cases count as contributions.
See [`CONTRIBUTING.md`](CONTRIBUTING.md) for setup and the rules
enforced in review.
## License
OTKO is released under the **GNU Affero General Public
License v3.0** ([`LICENSE`](LICENSE)).
OTKO is **GNU Affero General Public License v3.0**
([`LICENSE`](LICENSE)). Read the license itself, not just this:
Plain-language summary (not legal advice — read the license itself):
- ✅ Use it for **research, education, and personal projects** with no
obligation other than keeping the copyright notice intact.
- ✅ Modify and fork it freely.
- ⚠️ If you **distribute** it, modified or not, you must release your
full source under AGPL-3.0.
- ⚠️ If you **run it as a network service** (e.g. host a modified
version as a SaaS), you must release your modifications under
- Research, education, personal projects: fine, keep the copyright
notice.
- Fork and modify: fine.
- Distribute it (modified or not): release your full source under
AGPL-3.0.
- Run a modified version as a network service: release your
modifications under AGPL-3.0.
In other words: anyone is free to learn from and build on this code,
but commercial forks and proprietary derivatives must contribute their
changes back to the community. If your use case needs a different
arrangement (e.g. a closed-source commercial license), please open an
issue to discuss.
Commercial forks stay open. If you need a different arrangement
(e.g. closed-source commercial license), open an issue.
Copyright © 2026 Ozan and contributors.

View file

@ -24,11 +24,11 @@ the reverse.
### Why this matters
- The `core` package is testable without a display server, without OpenSees,
and without Qt. CI runs `pytest tests/unit/` in milliseconds.
- Replacing OpenSeesPy with another solver (e.g. `xara`, a future fork) only
touches `services/opensees_runner.py`.
- A future CLI or Jupyter frontend reuses `core` and `services` unchanged.
- `core` tests without a display server, without OpenSees, without Qt.
CI runs `pytest tests/unit/` in milliseconds.
- Swapping solvers (e.g. `xara`, a future fork) touches
`services/opensees_runner.py` and nothing else.
- A future CLI or notebook front-end reuses `core` and `services` as-is.
## Package map

View file

@ -11,7 +11,7 @@
| **Category** | Schema group (material family, element type, etc.) |
| **Object** | Name as it appears in gidopensees BOOK/CONDITION |
| **OTKO name** | Corresponding class in `core/` (if any) |
| **In Studio?** | ✅ fully supported · 🟡 partial · ❌ missing |
| **In OTKO?** | ✅ fully supported · 🟡 partial · ❌ missing |
| **In gidopensees?** | ✅ · ❌ |
| **Priority** | P0 = already done · P1 = Phase 8 target · P2 = later |
@ -28,7 +28,7 @@ Priority rationale:
## 1. Uniaxial Materials
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
| Category | Object (gidopensees) | OTKO name | In OTKO? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Uniaxial / linear | Elastic | `ElasticUniaxial` | ✅ | ✅ | P0 |
| Uniaxial / elastic-plastic | Elastic_Perfectly_Plastic | `ElasticPP` | ✅ | ✅ | P0 |
@ -43,7 +43,7 @@ Priority rationale:
## 2. Steel Uniaxial Materials
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
| Category | Object (gidopensees) | OTKO name | In OTKO? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Steel | Steel01 | `Steel01` | ✅ | ✅ | P0 |
| Steel | Steel02 | `Steel02` | ✅ | ✅ | P0 |
@ -53,7 +53,7 @@ Priority rationale:
## 3. Concrete Uniaxial Materials
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
| Category | Object (gidopensees) | OTKO name | In OTKO? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Concrete | Concrete01_(Zero_tensile_strength) | `Concrete01` | ✅ | ✅ | P0 |
| Concrete | Concrete02_(Linear_tension_softening) | `Concrete02` | ✅ | ✅ | P0 |
@ -63,7 +63,7 @@ Priority rationale:
## 4. Combined Materials
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
| Category | Object (gidopensees) | OTKO name | In OTKO? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Combination | Series | — | ❌ | ✅ | P1 |
| Combination | Parallel | — | ❌ | ✅ | P1 |
@ -71,7 +71,7 @@ Priority rationale:
## 5. nD (Multi-dimensional) Materials
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
| Category | Object (gidopensees) | OTKO name | In OTKO? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| nD | Elastic_Isotropic | `ElasticIsotropic` | ✅ | ✅ | P0 |
| nD | Elastic_Orthotropic | — | ❌ | ✅ | P2 |
@ -84,7 +84,7 @@ Priority rationale:
## 6. Sections
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
| Category | Object (gidopensees) | OTKO name | In OTKO? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Section | Elastic_Section | `ElasticSection` | ✅ | ✅ | P0 |
| Section | Fiber | `FiberSection` | ✅ | ✅ | P0 |
@ -97,7 +97,7 @@ Priority rationale:
## 7. Beam-Column Elements
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
| Category | Object (gidopensees) | OTKO name | In OTKO? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Frame | Elastic_Beam-Column | `ElasticBeamColumn` | ✅ | ✅ | P0 |
| Frame | Elastic_Timoshenko_Beam-Column | — | ❌ | ✅ | P1 |
@ -108,14 +108,14 @@ Priority rationale:
## 8. Truss Elements
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
| Category | Object (gidopensees) | OTKO name | In OTKO? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Truss | Truss | `TrussElement` | ✅ | ✅ | P0 |
| Truss | Corotational_Truss | `CorotTrussElement` | ✅ | ✅ | P0 |
## 9. Surface / Plate Elements
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
| Category | Object (gidopensees) | OTKO name | In OTKO? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Surface | Quad | `QuadElement` | ✅ | ✅ | P0 |
| Surface | Shell (ShellMITC4 / MITC4) | — | ❌ | ✅ | P1 |
@ -125,13 +125,13 @@ Priority rationale:
## 10. Solid Elements
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
| Category | Object (gidopensees) | OTKO name | In OTKO? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Solid | Standard_Brick_Element | — | ❌ | ✅ | P2 |
## 11. Zero-Length / Special Elements
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
| Category | Object (gidopensees) | OTKO name | In OTKO? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Special | Auto_Zero_Length (per-DOF uniaxial) | `ZeroLengthElement` | ✅ | ✅ | P0 |
| Special | Auto_equal_constraint (auto equalDOF) | `EqualDOFConstraint` | ✅ | ✅ | P0 |
@ -140,7 +140,7 @@ Priority rationale:
## 12. Restraints (Boundary Conditions)
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
| Category | Object (gidopensees) | OTKO name | In OTKO? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Restraint | Point_Restraints | Node.restraint (6-tuple) | ✅ | ✅ | P0 |
| Restraint | Line_Restraints (auto-apply to nodes on line) | — | ❌ | ✅ | P2 |
@ -148,7 +148,7 @@ Priority rationale:
## 13. Nodal Loads & Displacements
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
| Category | Object (gidopensees) | OTKO name | In OTKO? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Load | Point_Forces | `NodalLoad` | ✅ | ✅ | P0 |
| Load | Line_Forces (nodal, along a line) | — | ❌ | ✅ | P2 |
@ -160,7 +160,7 @@ Priority rationale:
## 14. Ground Motions
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
| Category | Object (gidopensees) | OTKO name | In OTKO? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Ground motion | Point_Ground_Motion_from_Record | `PathTimeSeries` + `UniformExcitationPattern` | ✅ | ✅ | P0 |
| Ground motion | Point_Sine_Ground_Motion | — (no `TrigTimeSeries`) | ❌ | ✅ | P1 |
@ -168,7 +168,7 @@ Priority rationale:
## 15. Constraints
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
| Category | Object (gidopensees) | OTKO name | In OTKO? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Constraint | Point_Equal_constraint (master + slave) | `EqualDOFConstraint` | ✅ | ✅ | P0 |
| Constraint | Line_Equal_constraint (slave nodes on line) | — | ❌ | ✅ | P1 |
@ -179,7 +179,7 @@ Priority rationale:
## 16. Mass
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
| Category | Object (gidopensees) | OTKO name | In OTKO? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Mass | Point_Mass | node mass (Properties dock + SetMassCommand) | ✅ | ✅ | P0 |
| Mass | Line_Mass (auto-lump to nodes) | — | ❌ | ✅ | P1 |
@ -188,7 +188,7 @@ Priority rationale:
## 17. Rayleigh Damping
| Category | Object (gidopensees) | OTKO name | In Studio? | In gidopensees? | Priority |
| Category | Object (gidopensees) | OTKO name | In OTKO? | In gidopensees? | Priority |
|---|---|---|---|---|---|
| Damping | Global αM + βK (TransientCase fields) | `TransientCase.rayleigh_alpha_m/beta_k` | ✅ | 🟡 | P0 |
| Damping | Mode-1 stiffness-proportional βK auto-compute | `TransientCase.rayleigh_mode1_damping` | ✅ | ❌ | P0 |
@ -205,7 +205,7 @@ Priority rationale:
| ❌ P1 targets (Phase 8 additions) | 23 |
| ❌ P2 deferred | 21 |
**Top P1 targets** (highest EQ-engineering impact, not in Studio yet):
**Top P1 targets** (highest EQ-engineering impact, not in OTKO yet):
1. `ElasticPP_with_Gap` — bearing pad / isolation gap nonlinearity
2. `Viscous` / `Viscous_Damper` — supplemental damping devices

View file

@ -37,7 +37,7 @@
<!-- Wordmark -->
<g font-family="Segoe UI, Inter, Helvetica, Arial, sans-serif">
<text x="200" y="92" font-size="56" font-weight="700" letter-spacing="-1">
<tspan fill="#E8EDF5">Open</tspan><tspan fill="#7BB1F0">Sees</tspan><tspan fill="#E8EDF5"> Studio</tspan>
<tspan fill="#E8EDF5">OTKO</tspan>
</text>
<text x="202" y="124" font-size="16" font-weight="500" letter-spacing="3" fill="#8FA2BF">
A SAP2000-STYLE GUI FOR OPENSEESPY

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before After
Before After

View file

@ -1,9 +1,8 @@
# Roadmap
OTKO is built in eight phases. Phases 0–7 ship the core GUI
plus all the post-processing tooling we need for verification work.
Phase 8 layers in the earthquake-engineering primitives that turn the
GUI from "OpenSees frontend" into a usable research tool.
Eight phases. 0–7 are the core GUI plus post-processing. Phase 8 is the
earthquake-engineering primitives — the part that makes it a research
tool instead of a model viewer.
Status legend: ✅ done · 🟡 partial · ⬜ planned · ✂️ deferred / out-of-scope.

View file

@ -1,34 +1,34 @@
# Example models
Pre-built `.osmodel` files plus the Python scripts that produce them.
Each model is set up with whichever case types the post-processing
features need, so you can exercise the full GUI without manually
defining materials, sections, loads, and analysis cases.
Pre-built `.osmodel` files plus the Python scripts that generate them.
Each one carries the case types the post-processing views need, so you
can exercise the GUI without defining materials, sections, loads, and
cases by hand.
## Files
| Model | Nodes | Elements | Cases | Best for demonstrating |
| Model | Nodes | Elements | Cases | Shows |
|---|---|---|---|---|
| `cantilever.osmodel` | 6 | 5 | Static × 2, Modal | Point & distributed loads, force diagrams, deformed shape, mode shapes |
| `portal_frame.osmodel` | 4 | 3 | Static, Modal, Transient | All Display features, simplest 3D |
| `space_frame_3d.osmodel` | 12 | 16 | Static, Modal, Transient (5% damping) | Realistic 3D rendering, multiple modes, damped EQ time-history |
| `cantilever.osmodel` | 6 | 5 | Static × 2, Modal | Point + distributed loads, force diagrams, deformed shape, mode shapes |
| `portal_frame.osmodel` | 4 | 3 | Static, Modal, Transient | All Display features, smallest 3D |
| `space_frame_3d.osmodel` | 12 | 16 | Static, Modal, Transient (5% damping) | 3D rendering, multiple modes, damped EQ time-history |
| `sdof_pushover.osmodel` | 2 | 1 | Pushover, Modal | Monotonic pushover curve, HystereticMaterial |
| `portal_pushover.osmodel` | 4 | 3 | Pushover, Modal | Fiber sections, BeamWithHinges, nonlinear pushover with yielding |
| `ex1a_canti2d.osmodel` | 2 | 1 | Static preload, Pushover, Transient EQ | Original OpenSees Ex 1a with shared gravity, push, and earthquake cases |
| `ex1b_portal2d.osmodel` | 4 | 3 | Static preload, Pushover, Transient EQ | Original OpenSees Ex 1b elastic portal frame with distributed gravity |
| `ex2a_canti2d_elastic_element.osmodel` | 2 | 1 | Static preload, Pushover, Transient EQ | Variable-driven cantilever example with derived parameters |
| `ex2b_canti2d_inelastic_section.osmodel` | 2 | 1 | Static preload, Pushover, Transient EQ | First nonlinear cantilever with aggregated uniaxial section |
| `ex2c_canti2d_inelastic_fiber_section.osmodel` | 2 | 1 | Static preload, Pushover, Transient EQ | Fiber-section cantilever with coupled axial-flexural nonlinearity |
| `ex3_canti2d_elastic_element.osmodel` | 2 | 1 | Static preload, Pushover, Transient EQ | Example 3 elastic build with unit-scaled parameters |
| `ex3_canti2d_inelastic_section.osmodel` | 2 | 1 | Static preload, Pushover, Transient EQ | Example 3 aggregated-section nonlinear build |
| `ex3_canti2d_inelastic_fiber_section.osmodel` | 2 | 1 | Static preload, Pushover, Transient EQ | Example 3 fiber-section nonlinear build |
| `ex4_portal2d_elastic_element.osmodel` | 4 | 3 | Static preload, Pushover, Transient sine | Example 4 elastic portal frame with separated build/analysis workflow |
| `ex4_portal2d_inelastic_section.osmodel` | 4 | 3 | Static preload, Pushover, Transient sine | Example 4 aggregated-section portal frame variant |
| `ex4_portal2d_inelastic_fiber_section.osmodel` | 4 | 3 | Static preload, Pushover, Transient sine | Example 4 fiber-section portal frame variant |
| `ex1a_canti2d_eq.osmodel` | 2 | 1 | Static preload, Transient EQ | OpenSees Ex 1a style gravity + base excitation workflow |
| `eigen_two_storey_shear_frame.osmodel` | 6 | 6 | Modal | equalDOF floor constraints, mode shapes, eigenvalue workflow |
| `eigen_two_storey_one_bay_frame.osmodel` | 6 | 6 | Modal | classic elastic frame modal example, sway mode shapes |
| `concrete04_cantilever.osmodel` | 2 | 1 | Static (gravity), Pushover | Popovics Concrete04 fiber section; proof-of-concept for the Concrete04 end-to-end stack |
| `portal_pushover.osmodel` | 4 | 3 | Pushover, Modal | Fiber sections, BeamWithHinges, yielding pushover |
| `ex1a_canti2d.osmodel` | 2 | 1 | Static preload, Pushover, Transient EQ | OpenSees Ex 1a, shared gravity + push + quake |
| `ex1b_portal2d.osmodel` | 4 | 3 | Static preload, Pushover, Transient EQ | OpenSees Ex 1b elastic portal, distributed gravity |
| `ex2a_canti2d_elastic_element.osmodel` | 2 | 1 | Static preload, Pushover, Transient EQ | Ex 2a cantilever, dimensions as named parameters |
| `ex2b_canti2d_inelastic_section.osmodel` | 2 | 1 | Static preload, Pushover, Transient EQ | Ex 2b, aggregated axial+flexure section |
| `ex2c_canti2d_inelastic_fiber_section.osmodel` | 2 | 1 | Static preload, Pushover, Transient EQ | Ex 2c, fiber section, coupled axial-flexure |
| `ex3_canti2d_elastic_element.osmodel` | 2 | 1 | Static preload, Pushover, Transient EQ | Ex 3 elastic build, unit-scaled parameters |
| `ex3_canti2d_inelastic_section.osmodel` | 2 | 1 | Static preload, Pushover, Transient EQ | Ex 3 aggregated-section build |
| `ex3_canti2d_inelastic_fiber_section.osmodel` | 2 | 1 | Static preload, Pushover, Transient EQ | Ex 3 fiber-section build |
| `ex4_portal2d_elastic_element.osmodel` | 4 | 3 | Static preload, Pushover, Transient sine | Ex 4 elastic portal, build/analysis split |
| `ex4_portal2d_inelastic_section.osmodel` | 4 | 3 | Static preload, Pushover, Transient sine | Ex 4 aggregated-section portal |
| `ex4_portal2d_inelastic_fiber_section.osmodel` | 4 | 3 | Static preload, Pushover, Transient sine | Ex 4 fiber-section portal |
| `ex1a_canti2d_eq.osmodel` | 2 | 1 | Static preload, Transient EQ | Ex 1a gravity + base excitation only |
| `eigen_two_storey_shear_frame.osmodel` | 6 | 6 | Modal | equalDOF floor constraints, shear-frame modes |
| `eigen_two_storey_one_bay_frame.osmodel` | 6 | 6 | Modal | Chopra 10.5 frame, sway modes, no constraints |
| `concrete04_cantilever.osmodel` | 2 | 1 | Static (gravity), Pushover | Concrete04 fiber section end-to-end |
## Quick tour
@ -38,37 +38,36 @@ File → Open → cantilever.osmodel
Analyze → Cases → run "Tip-Load"
Display → Show Force Diagram → component "M3" → linear moment, max at fixed end (50 kN·m)
→ component "V2" → constant -10 kN along the whole span
→ component "N" → ~zero (no axial load applied)
→ component "N" → ~zero (no axial load)
→ component "T" → ~zero (no torsion → console hint, no diagram)
Display → Show Deformed Shape → classic cantilever curve
Display → Show Deformed Shape → cantilever curve
```
The load is applied along the global Y axis (perpendicular to the beam,
in the horizontal plane). With the default 3D vertical-reference
convention this gives V2 / M3 — i.e. the "in-plane bending" pair.
Load runs along global Y (perpendicular to the beam, horizontal plane).
With the default 3D vertical-reference convention that lands on the
V2 / M3 pair — the in-plane bending pair.
**Distributed load (UDL) variant** — run the second case to see a
parabolic moment diagram:
UDL variant, parabolic moment:
```
Analyze → Cases → run "Uniform-Load"
Display → Show Force Diagram → M3 → parabolic, max 25 kN·m at fixed end
→ V2 → linear, max 10 kN at fixed end
```
### 2. Mode shapes — `portal_frame.osmodel` or `space_frame_3d.osmodel`
### 2. Mode shapes — `space_frame_3d.osmodel`
```
File → Open → space_frame_3d.osmodel
Analyze → Cases → run "Modal-6"
Display → Animate Mode Shape → mode 1 = X-sway, mode 2 = Y-sway
→ ▶ Play, scrub timeline, change scale
→ Play, scrub timeline, change scale
```
### 3. Time-history & hysteresis — `portal_frame.osmodel` or `space_frame_3d.osmodel`
### 3. Time-history and hysteresis — `space_frame_3d.osmodel`
```
File → Open → space_frame_3d.osmodel
Analyze → Cases → run "EQ-4s" (~5-10 sec on a modern laptop)
Display → Time-History Plot
- Node 12 (roof corner) + DOF 1 (X displacement) → "Add trace"
- Node 9 + DOF 1 → another trace, compare phase
- Node 9 + DOF 1 → second trace, compare phase
Display → Hysteresis Plot
- X = Node 12 / DOF 1, Y = Node 12 / DOF 3 → orbit
```
@ -78,137 +77,129 @@ Display → Hysteresis Plot
File → Open → sdof_pushover.osmodel
Analyze → Cases → run "Push-X"
Display → Show Pushover Curve
→ linear segment from origin, then softens through yield
→ linear from origin, then softens through yield
```
Note: this demo keeps the column elastic (proper nonlinear hinges require
BeamWithHingesElement with fibre sections — infrastructure is in place,
fibre-section editor is future work).
Column stays elastic here. Real nonlinear hinges need
BeamWithHinges + fiber sections; the machinery exists, the
fiber-section editor is still rough.
### 5. Nonlinear pushover with fiber hinges — `portal_pushover.osmodel`
```
File → Open → portal_pushover.osmodel
Analyze → Cases → run "Push-X"
Display → Show Pushover Curve
→ initial linear stiffness, then yield plateau as base hinges form
→ peak base shear corresponds to concrete crushing + rebar yield
→ linear stiffness, then yield plateau as base hinges form
→ peak base shear = concrete crushing + rebar yield
```
The columns use BeamWithHingesElements with FiberSections (concrete core
+ rebar layers) wrapped in a SectionAggregator (torsion spring).
Columns are BeamWithHinges + FiberSections (concrete core, rebar
layers) wrapped in a SectionAggregator for torsion.
### 6. Gravity + time-history chain — `ex1a_canti2d_eq.osmodel`
```bash
```
File → Open → ex1a_canti2d_eq.osmodel
Analyze → Cases → run "Earthquake"
Display → Time-History Plot
- Node 2 + DOF 1 (Ux) → horizontal response of the cantilever tip
- Node 2 + DOF 2 (Uy) → verify gravity stays essentially locked
- Node 2 + DOF 1 (Ux) → tip horizontal response
- Node 2 + DOF 2 (Uy) → gravity should stay locked
```
This model is intentionally tiny but important for workflow coverage:
it demonstrates the general transient recipe of
`Static preload → loadConst reset → UniformExcitation transient`
using a real ground-motion record imported into a `PathTimeSeries`.
Tiny model, exists for one reason: the standard transient recipe
`static preload → loadConst reset → UniformExcitation transient`
against a real ground-motion record in a `PathTimeSeries`.
### 7. Original OpenSees Ex 1a bundle — `ex1a_canti2d.osmodel`
```bash
### 7. OpenSees Ex 1a bundle — `ex1a_canti2d.osmodel`
```
File → Open → ex1a_canti2d.osmodel
Analyze → Cases → run "Push" or "Earthquake"
Display → Show Pushover Curve / Time-History Plot
```
This is the original cantilever-column Example 1a packaged as one model
with a shared gravity preload plus both lateral load variants. It is a
good small benchmark for checking that pushover and transient workflows
behave consistently on the same geometry.
Cantilever column with shared gravity preload and both lateral
variants. Small benchmark for checking pushover and transient agree
on the same geometry.
### 8. Original OpenSees Ex 1b bundle — `ex1b_portal2d.osmodel`
```bash
### 8. OpenSees Ex 1b bundle — `ex1b_portal2d.osmodel`
```
File → Open → ex1b_portal2d.osmodel
Analyze → Cases → run "Push" or "Earthquake"
Display → Show Pushover Curve / Time-History Plot
```
This is the original elastic portal-frame Example 1b bundled as one
project. It is especially useful because the gravity preload is carried
by a distributed beam load instead of nodal loads only.
Elastic portal frame. Gravity comes from a distributed beam load
instead of nodal loads, which is the whole point of keeping it
around.
### 9. Variable-driven cantilever example — `ex2a_canti2d_elastic_element.osmodel`
```bash
### 9. Ex 2a, parameter-driven — `ex2a_canti2d_elastic_element.osmodel`
```
File → Open → ex2a_canti2d_elastic_element.osmodel
Analyze → Cases → run "Push" or "Earthquake"
Display → Show Pushover Curve / Time-History Plot
```
This is the Ex2a cantilever tutorial recast as a project model. It is
useful when we want the same basic physics as Ex1a but with all major
dimensions and derived quantities exposed as named parameters.
Same physics as Ex 1a, but dimensions and derived quantities are
named parameters instead of literals.
### 10. Nonlinear aggregated-section cantilever — `ex2b_canti2d_inelastic_section.osmodel`
```bash
### 10. Ex 2b, aggregated section — `ex2b_canti2d_inelastic_section.osmodel`
```
File → Open → ex2b_canti2d_inelastic_section.osmodel
Analyze → Cases → run "Push" or "Earthquake"
Display → Show Pushover Curve / Time-History Plot
```
This is the first nonlinear cantilever benchmark in the tutorial series.
It demonstrates how separate axial and flexural uniaxial responses can
be aggregated into one section and used by a force-based beam-column element.
First nonlinear cantilever in the series. Separate axial and flexural
uniaxial responses aggregated into one section on a force-based
beam-column.
### 11. Fiber-section cantilever example — `ex2c_canti2d_inelastic_fiber_section.osmodel`
```bash
### 11. Ex 2c, fiber section — `ex2c_canti2d_inelastic_fiber_section.osmodel`
```
File → Open → ex2c_canti2d_inelastic_fiber_section.osmodel
Analyze → Cases → run "Push" or "Earthquake"
Display → Show Pushover Curve / Time-History Plot
```
This is the Ex2c fiber-section counterpart to Ex2b. It is useful for
checking coupled axial-flexural section behavior with inelastic concrete
and steel materials assigned directly to fibers and rebar layers.
Ex 2b's fiber counterpart. Coupled axial-flexure with concrete and
steel assigned to fibers and rebar layers directly.
### 12. Example 3 build variants — `ex3_canti2d_*.osmodel`
```bash
### 12. Ex 3 family — `ex3_canti2d_*.osmodel`
```
File → Open → ex3_canti2d_elastic_element.osmodel
Analyze → Cases → run "Push" or "Earthquake"
```
The Example 3 family is useful when we want the same cantilever analyses
to run on three different build styles: elastic element, aggregated
uniaxial section, and fiber section, all with unit-scaled parameters.
Same cantilever analyses on three build styles: elastic element,
aggregated uniaxial section, fiber section. All unit-scaled.
### 13. Modal shear-building example — `eigen_two_storey_shear_frame.osmodel`
```bash
### 13. Modal shear building — `eigen_two_storey_shear_frame.osmodel`
```
File → Open → eigen_two_storey_shear_frame.osmodel
Analyze → Cases → run "Modal-2"
Display → Animate Mode Shape
- mode 1 → in-phase storey sway
- mode 2 → out-of-phase storey sway
- mode 1 → stories sway in phase
- mode 2 → stories sway out of phase
```
This example is useful for validating modal workflows on a tiny model
that still needs multi-point constraints (`equalDOF`) to behave like an
idealized shear frame.
Validates modal workflows on a model small enough to check by hand,
with `equalDOF` doing the shear-frame duty.
### 9. Modal elastic frame example — `eigen_two_storey_one_bay_frame.osmodel`
```bash
File → Open → eigen_two_storey_one_bay_frame.osmodel
Analyze → Cases → run "Modal-2"
Display → Animate Mode Shape
- mode 1 → in-phase sway of the two storeys
- mode 2 → upper storey reverses relative to the first storey
### 14. Modal frame, Chopra 10.5 — `eigen_two_storey_one_bay_frame.osmodel`
```
This is the Chopra Example 10.5 frame counterpart to the shear-building
example above. It gives us a small modal benchmark with ordinary
beam-column frame behavior and no multi-point constraints.
File → Open → eigen_two_storey_one_bay_frame.osmodel
Analyze → Cases → run "Modal-2"
Display → Animate Mode Shape
- mode 1 → in-phase sway of both stories
- mode 2 → top story reverses against the first
```
Companion to the shear building above. Ordinary beam-column behavior,
no multi-point constraints.
### 13. Example 4 portal-frame variants
```bash
File -> Open -> ex4_portal2d_elastic_element.osmodel
Analyze -> Cases -> run "Push" or "Sine-Uniform"
Display -> Show Pushover Curve / Time-History Plot
### 15. Ex 4 portal family — `ex4_portal2d_*.osmodel`
```
The Example 4 family keeps the OpenSees split between model-building
and analysis files, but moves it into project variants. These are
useful benchmarks for pinned-base frame sway, distributed gravity on the
beam, and support-motion dynamics without depending on an external
earthquake file. The fiber-section transient is intentionally retained
as a strong nonlinear stress test and may stop early while still
producing useful partial histories.
File → Open → ex4_portal2d_elastic_element.osmodel
Analyze → Cases → run "Push" or "Sine-Uniform"
Display → Show Pushover Curve / Time-History Plot
```
Keeps the OpenSees split between model-building and analysis files,
recast as project variants. Covers pinned-base sway, distributed
girder gravity, and support-motion dynamics without an external quake
file. The fiber transient is kept as a nonlinear stress test — it may
stop early and still produce usable partial histories.
## Regenerating the .osmodel files
If you change the Python scripts, run them to regenerate the saved models:
Scripts are the source of truth, `.osmodel` files are build artifacts
checked in for convenience. Change a script, rerun it:
```bash
python examples/cantilever.py
@ -232,6 +223,5 @@ python examples/eigen_two_storey_shear_frame.py
python examples/eigen_two_storey_one_bay_frame.py
```
Each script builds the project, saves it, reloads it, and asserts a clean
round-trip. The Python source is the source of truth; the `.osmodel` files
are generated artifacts checked in for convenience.
Each script builds the project, saves it, reloads it, and asserts a
clean round-trip.

View file

@ -3,8 +3,8 @@
OpenSees Wiki:
https://opensees.berkeley.edu/wiki/index.php?title=OpenSees_Example_1b._Elastic_Portal_Frame
This packages the original Example 1b portal frame into one OpenSees
Studio project with shared gravity preload and both lateral-load cases:
This packages the original Example 1b portal frame into one OTKO
project with shared gravity preload and both lateral-load cases:
- static pushover
- base-excitation earthquake analysis with ``BM68elc.acc``

View file

@ -1,25 +1,24 @@
# core/catalog — GiD schema catalog
This package contains **auto-generated Pydantic v2 schema descriptions** for
all OpenSees materials and conditions defined in the
Auto-generated Pydantic v2 schema descriptions for every OpenSees
material and condition in the
[gidopensees](https://github.com/rclab-auth/gidopensees) GiD preprocessor.
## Public import surface
## Use
```python
from otko.core.catalog import CATALOG
# Look up a Spec class by its gidopensees name
Steel02Spec = CATALOG["Steel02"]
spec = Steel02Spec() # instantiate with defaults
spec = Steel02Spec() # defaults
spec.model_dump_json() # serialize
```
`CATALOG` is a `dict[str, type[BaseModel]]` mapping every material's
gidopensees name (e.g. `"Steel02"`) to its generated `Spec` class.
It contains exactly 58 entries (one per material in `OpenSees.mat`).
`CATALOG` maps each material's gidopensees name (e.g. `"Steel02"`) to its
generated `Spec` class. 58 entries, one per material in `OpenSees.mat`.
Per-book discriminated Union types are available in `generated/__init__.py`:
Per-book discriminated unions live in `generated/__init__.py`:
```python
from otko.core.catalog.generated import UniaxialSteelMaterials
@ -29,7 +28,7 @@ Condition specs live under `generated/conditions/`.
## Regenerating
Run the codegen tool any time the upstream `schemas.json` changes:
When upstream `schemas.json` changes, rerun codegen:
```bash
python -m tools.gidopensees_import.codegen \
@ -39,17 +38,15 @@ python -m tools.gidopensees_import.codegen \
## Do not hand-edit `generated/`
Files under `generated/` are overwritten on each codegen run.
Hand-curated overrides, corrections, or extensions belong in
`curated/` (currently empty — reserved for future use).
Codegen overwrites it. Put overrides, corrections, and extensions in
`curated/` (empty for now, reserved).
## Scope note
Catalog Spec classes are **schema descriptions only**. They capture the
field names, types, defaults, and UI metadata from the gidopensees
definition files. They are **not yet wired into the OpenSees runtime**.
The mapping from a `Spec` to an actual `uniaxialMaterial` call is a
future deliverable tracked in the ADR.
Spec classes are schema descriptions: field names, types, defaults, UI
metadata from the gidopensees definition files. They are not wired into
the runtime. Mapping a `Spec` to an actual `uniaxialMaterial` call is
still open — see the ADR.
## Attribution
@ -57,7 +54,6 @@ Schema data from [gidopensees](https://github.com/rclab-auth/gidopensees),
Copyright (C) Reinforced Concrete Laboratory, Aristotle University of
Thessaloniki (AUTh).
`CATALOG` exposes the 58 material specs only; condition specs are intentionally
kept in a separate namespace (`generated/conditions/`, 39 specs) so that
material and boundary-condition objects remain independently importable and
do not pollute each other's namespace.
`CATALOG` holds the 58 material specs. Condition specs stay in their own
namespace (`generated/conditions/`, 39 specs) so the two don't pollute
each other.

View file

@ -55,7 +55,7 @@ class OpenSeesAnalysisRunner(OpenSeesEmitter):
if isinstance(case, ResponseSpectrumCase):
return self._run_response_spectrum(case)
if isinstance(case, TransientCase):
target = results_dir or Path(tempfile.mkdtemp(prefix="osstudio_"))
target = results_dir or Path(tempfile.mkdtemp(prefix="otko_"))
return self._run_transient(case, target)
raise TypeError(f"Unsupported analysis case type: {type(case).__name__}")

View file

@ -363,7 +363,7 @@ def export_opspy(project: Project, case_id: int | None = None) -> str:
Returns:
The script source. The header pins ``openseespy==3.5.1.12``,
the Studio version and the display units.
the OTKO version and the display units.
Raises:
ValueError: If ``case_id`` matches no analysis case.