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

This commit is contained in:
smillmorel 2026-09-08 02:41:03 -04:00
commit 3d809ca301
11 changed files with 233 additions and 263 deletions

View file

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

159
README.md
View file

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

View file

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

View file

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

View file

@ -37,7 +37,7 @@
<!-- Wordmark --> <!-- Wordmark -->
<g font-family="Segoe UI, Inter, Helvetica, Arial, sans-serif"> <g font-family="Segoe UI, Inter, Helvetica, Arial, sans-serif">
<text x="200" y="92" font-size="56" font-weight="700" letter-spacing="-1"> <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>
<text x="202" y="124" font-size="16" font-weight="500" letter-spacing="3" fill="#8FA2BF"> <text x="202" y="124" font-size="16" font-weight="500" letter-spacing="3" fill="#8FA2BF">
A SAP2000-STYLE GUI FOR OPENSEESPY 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 # Roadmap
OTKO is built in eight phases. Phases 0–7 ship the core GUI Eight phases. 0–7 are the core GUI plus post-processing. Phase 8 is the
plus all the post-processing tooling we need for verification work. earthquake-engineering primitives — the part that makes it a research
Phase 8 layers in the earthquake-engineering primitives that turn the tool instead of a model viewer.
GUI from "OpenSees frontend" into a usable research tool.
Status legend: ✅ done · 🟡 partial · ⬜ planned · ✂️ deferred / out-of-scope. Status legend: ✅ done · 🟡 partial · ⬜ planned · ✂️ deferred / out-of-scope.

View file

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

View file

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

View file

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

View file

@ -55,7 +55,7 @@ class OpenSeesAnalysisRunner(OpenSeesEmitter):
if isinstance(case, ResponseSpectrumCase): if isinstance(case, ResponseSpectrumCase):
return self._run_response_spectrum(case) return self._run_response_spectrum(case)
if isinstance(case, TransientCase): 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) return self._run_transient(case, target)
raise TypeError(f"Unsupported analysis case type: {type(case).__name__}") 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: Returns:
The script source. The header pins ``openseespy==3.5.1.12``, 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: Raises:
ValueError: If ``case_id`` matches no analysis case. ValueError: If ``case_id`` matches no analysis case.