Compare commits

..
11 changed files with 263 additions and 233 deletions

View file

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

159
README.md
View file

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

View file

@ -24,11 +24,11 @@ the reverse.
### Why this matters ### Why this matters
- `core` tests without a display server, without OpenSees, without Qt. - The `core` package is testable without a display server, without OpenSees,
CI runs `pytest tests/unit/` in milliseconds. and without Qt. CI runs `pytest tests/unit/` in milliseconds.
- Swapping solvers (e.g. `xara`, a future fork) touches - Replacing OpenSeesPy with another solver (e.g. `xara`, a future fork) only
`services/opensees_runner.py` and nothing else. touches `services/opensees_runner.py`.
- A future CLI or notebook front-end reuses `core` and `services` as-is. - A future CLI or Jupyter frontend reuses `core` and `services` unchanged.
## 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 OTKO?** | ✅ fully supported · 🟡 partial · ❌ missing | | **In Studio?** | ✅ 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 OTKO? | In gidopensees? | Priority | | Category | Object (gidopensees) | OTKO name | In Studio? | 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 OTKO? | In gidopensees? | Priority | | Category | Object (gidopensees) | OTKO name | In Studio? | 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 OTKO? | In gidopensees? | Priority | | Category | Object (gidopensees) | OTKO name | In Studio? | 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 OTKO? | In gidopensees? | Priority | | Category | Object (gidopensees) | OTKO name | In Studio? | 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 OTKO? | In gidopensees? | Priority | | Category | Object (gidopensees) | OTKO name | In Studio? | 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 OTKO? | In gidopensees? | Priority | | Category | Object (gidopensees) | OTKO name | In Studio? | 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 OTKO? | In gidopensees? | Priority | | Category | Object (gidopensees) | OTKO name | In Studio? | 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 OTKO? | In gidopensees? | Priority | | Category | Object (gidopensees) | OTKO name | In Studio? | 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 OTKO? | In gidopensees? | Priority | | Category | Object (gidopensees) | OTKO name | In Studio? | 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 OTKO? | In gidopensees? | Priority | | Category | Object (gidopensees) | OTKO name | In Studio? | 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 OTKO? | In gidopensees? | Priority | | Category | Object (gidopensees) | OTKO name | In Studio? | 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 OTKO? | In gidopensees? | Priority | | Category | Object (gidopensees) | OTKO name | In Studio? | 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 OTKO? | In gidopensees? | Priority | | Category | Object (gidopensees) | OTKO name | In Studio? | 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 OTKO? | In gidopensees? | Priority | | Category | Object (gidopensees) | OTKO name | In Studio? | 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 OTKO? | In gidopensees? | Priority | | Category | Object (gidopensees) | OTKO name | In Studio? | 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 OTKO? | In gidopensees? | Priority | | Category | Object (gidopensees) | OTKO name | In Studio? | 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 OTKO? | In gidopensees? | Priority | | Category | Object (gidopensees) | OTKO name | In Studio? | 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 OTKO yet): **Top P1 targets** (highest EQ-engineering impact, not in Studio 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">OTKO</tspan> <tspan fill="#E8EDF5">Open</tspan><tspan fill="#7BB1F0">Sees</tspan><tspan fill="#E8EDF5"> Studio</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.1 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before After
Before After

View file

@ -1,8 +1,9 @@
# Roadmap # Roadmap
Eight phases. 0–7 are the core GUI plus post-processing. Phase 8 is the OTKO is built in eight phases. Phases 0–7 ship the core GUI
earthquake-engineering primitives — the part that makes it a research plus all the post-processing tooling we need for verification work.
tool instead of a model viewer. Phase 8 layers in the earthquake-engineering primitives that turn the
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 generate them. Pre-built `.osmodel` files plus the Python scripts that produce them.
Each one carries the case types the post-processing views need, so you Each model is set up with whichever case types the post-processing
can exercise the GUI without defining materials, sections, loads, and features need, so you can exercise the full GUI without manually
cases by hand. defining materials, sections, loads, and analysis cases.
## Files ## Files
| Model | Nodes | Elements | Cases | Shows | | Model | Nodes | Elements | Cases | Best for demonstrating |
|---|---|---|---|---| |---|---|---|---|---|
| `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, smallest 3D | | `portal_frame.osmodel` | 4 | 3 | Static, Modal, Transient | All Display features, simplest 3D |
| `space_frame_3d.osmodel` | 12 | 16 | Static, Modal, Transient (5% damping) | 3D rendering, multiple modes, damped EQ time-history | | `space_frame_3d.osmodel` | 12 | 16 | Static, Modal, Transient (5% damping) | Realistic 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, yielding pushover | | `portal_pushover.osmodel` | 4 | 3 | Pushover, Modal | Fiber sections, BeamWithHinges, nonlinear pushover with yielding |
| `ex1a_canti2d.osmodel` | 2 | 1 | Static preload, Pushover, Transient EQ | OpenSees Ex 1a, shared gravity + push + quake | | `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 | OpenSees Ex 1b elastic portal, distributed gravity | | `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 | Ex 2a cantilever, dimensions as named parameters | | `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 | Ex 2b, aggregated axial+flexure section | | `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 | Ex 2c, fiber section, coupled axial-flexure | | `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 | Ex 3 elastic build, unit-scaled parameters | | `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 | Ex 3 aggregated-section build | | `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 | Ex 3 fiber-section 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 | Ex 4 elastic portal, build/analysis split | | `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 | Ex 4 aggregated-section portal | | `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 | Ex 4 fiber-section portal | | `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 | Ex 1a gravity + base excitation only | | `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, shear-frame modes | | `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 | Chopra 10.5 frame, sway modes, no constraints | | `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 | Concrete04 fiber section end-to-end | | `concrete04_cantilever.osmodel` | 2 | 1 | Static (gravity), Pushover | Popovics Concrete04 fiber section; proof-of-concept for the Concrete04 end-to-end stack |
## Quick tour ## Quick tour
@ -38,36 +38,37 @@ 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) → component "N" → ~zero (no axial load applied)
→ component "T" → ~zero (no torsion → console hint, no diagram) → component "T" → ~zero (no torsion → console hint, no diagram)
Display → Show Deformed Shape → cantilever curve Display → Show Deformed Shape → classic cantilever curve
``` ```
Load runs along global Y (perpendicular to the beam, horizontal plane). The load is applied along the global Y axis (perpendicular to the beam,
With the default 3D vertical-reference convention that lands on the in the horizontal plane). With the default 3D vertical-reference
V2 / M3 pair — the in-plane bending pair. convention this gives V2 / M3 — i.e. the "in-plane bending" pair.
UDL variant, parabolic moment: **Distributed load (UDL) variant** — run the second case to see a
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 — `space_frame_3d.osmodel` ### 2. Mode shapes — `portal_frame.osmodel` or `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 and hysteresis — `space_frame_3d.osmodel` ### 3. Time-history & hysteresis — `portal_frame.osmodel` or `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 → second trace, compare phase - Node 9 + DOF 1 → another 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
``` ```
@ -77,129 +78,137 @@ 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 from origin, then softens through yield → linear segment from origin, then softens through yield
``` ```
Column stays elastic here. Real nonlinear hinges need Note: this demo keeps the column elastic (proper nonlinear hinges require
BeamWithHinges + fiber sections; the machinery exists, the BeamWithHingesElement with fibre sections — infrastructure is in place,
fiber-section editor is still rough. fibre-section editor is future work).
### 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
→ linear stiffness, then yield plateau as base hinges form → initial linear stiffness, then yield plateau as base hinges form
→ peak base shear = concrete crushing + rebar yield → peak base shear corresponds to concrete crushing + rebar yield
``` ```
Columns are BeamWithHinges + FiberSections (concrete core, rebar The columns use BeamWithHingesElements with FiberSections (concrete core
layers) wrapped in a SectionAggregator for torsion. + rebar layers) wrapped in a SectionAggregator (torsion spring).
### 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) → tip horizontal response - Node 2 + DOF 1 (Ux) → horizontal response of the cantilever tip
- Node 2 + DOF 2 (Uy) → gravity should stay locked - Node 2 + DOF 2 (Uy) → verify gravity stays essentially locked
``` ```
Tiny model, exists for one reason: the standard transient recipe This model is intentionally tiny but important for workflow coverage:
`static preload → loadConst reset → UniformExcitation transient` it demonstrates the general transient recipe of
against a real ground-motion record in a `PathTimeSeries`. `Static preload → loadConst reset → UniformExcitation transient`
using a real ground-motion record imported into a `PathTimeSeries`.
### 7. OpenSees Ex 1a bundle — `ex1a_canti2d.osmodel` ### 7. Original 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
``` ```
Cantilever column with shared gravity preload and both lateral This is the original cantilever-column Example 1a packaged as one model
variants. Small benchmark for checking pushover and transient agree with a shared gravity preload plus both lateral load variants. It is a
on the same geometry. good small benchmark for checking that pushover and transient workflows
behave consistently on the same geometry.
### 8. OpenSees Ex 1b bundle — `ex1b_portal2d.osmodel` ### 8. Original 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
``` ```
Elastic portal frame. Gravity comes from a distributed beam load This is the original elastic portal-frame Example 1b bundled as one
instead of nodal loads, which is the whole point of keeping it project. It is especially useful because the gravity preload is carried
around. by a distributed beam load instead of nodal loads only.
### 9. Ex 2a, parameter-driven — `ex2a_canti2d_elastic_element.osmodel` ### 9. Variable-driven cantilever example — `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
``` ```
Same physics as Ex 1a, but dimensions and derived quantities are This is the Ex2a cantilever tutorial recast as a project model. It is
named parameters instead of literals. useful when we want the same basic physics as Ex1a but with all major
dimensions and derived quantities exposed as named parameters.
### 10. Ex 2b, aggregated section — `ex2b_canti2d_inelastic_section.osmodel` ### 10. Nonlinear aggregated-section cantilever — `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
``` ```
First nonlinear cantilever in the series. Separate axial and flexural This is the first nonlinear cantilever benchmark in the tutorial series.
uniaxial responses aggregated into one section on a force-based It demonstrates how separate axial and flexural uniaxial responses can
beam-column. be aggregated into one section and used by a force-based beam-column element.
### 11. Ex 2c, fiber section — `ex2c_canti2d_inelastic_fiber_section.osmodel` ### 11. Fiber-section cantilever example — `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
``` ```
Ex 2b's fiber counterpart. Coupled axial-flexure with concrete and This is the Ex2c fiber-section counterpart to Ex2b. It is useful for
steel assigned to fibers and rebar layers directly. checking coupled axial-flexural section behavior with inelastic concrete
and steel materials assigned directly to fibers and rebar layers.
### 12. Ex 3 family — `ex3_canti2d_*.osmodel` ### 12. Example 3 build variants — `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"
``` ```
Same cantilever analyses on three build styles: elastic element, The Example 3 family is useful when we want the same cantilever analyses
aggregated uniaxial section, fiber section. All unit-scaled. to run on three different build styles: elastic element, aggregated
uniaxial section, and fiber section, all with unit-scaled parameters.
### 13. Modal shear building — `eigen_two_storey_shear_frame.osmodel` ### 13. Modal shear-building example — `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 → stories sway in phase - mode 1 → in-phase storey sway
- mode 2 → stories sway out of phase - mode 2 → out-of-phase storey sway
``` ```
Validates modal workflows on a model small enough to check by hand, This example is useful for validating modal workflows on a tiny model
with `equalDOF` doing the shear-frame duty. that still needs multi-point constraints (`equalDOF`) to behave like an
idealized shear frame.
### 14. Modal frame, Chopra 10.5 — `eigen_two_storey_one_bay_frame.osmodel` ### 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
``` ```
File → Open → eigen_two_storey_one_bay_frame.osmodel This is the Chopra Example 10.5 frame counterpart to the shear-building
Analyze → Cases → run "Modal-2" example above. It gives us a small modal benchmark with ordinary
Display → Animate Mode Shape beam-column frame behavior and no multi-point constraints.
- 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.
### 15. Ex 4 portal family — `ex4_portal2d_*.osmodel` ### 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
``` ```
File → Open → ex4_portal2d_elastic_element.osmodel The Example 4 family keeps the OpenSees split between model-building
Analyze → Cases → run "Push" or "Sine-Uniform" and analysis files, but moves it into project variants. These are
Display → Show Pushover Curve / Time-History Plot useful benchmarks for pinned-base frame sway, distributed gravity on the
``` beam, and support-motion dynamics without depending on an external
Keeps the OpenSees split between model-building and analysis files, earthquake file. The fiber-section transient is intentionally retained
recast as project variants. Covers pinned-base sway, distributed as a strong nonlinear stress test and may stop early while still
girder gravity, and support-motion dynamics without an external quake producing useful partial histories.
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
Scripts are the source of truth, `.osmodel` files are build artifacts If you change the Python scripts, run them to regenerate the saved models:
checked in for convenience. Change a script, rerun it:
```bash ```bash
python examples/cantilever.py python examples/cantilever.py
@ -223,5 +232,6 @@ 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 Each script builds the project, saves it, reloads it, and asserts a clean
clean round-trip. round-trip. The Python source is the source of truth; the `.osmodel` files
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 OTKO This packages the original Example 1b portal frame into one OpenSees
project with shared gravity preload and both lateral-load cases: Studio 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,24 +1,25 @@
# core/catalog — GiD schema catalog # core/catalog — GiD schema catalog
Auto-generated Pydantic v2 schema descriptions for every OpenSees This package contains **auto-generated Pydantic v2 schema descriptions** for
material and condition in the all OpenSees materials and conditions defined in the
[gidopensees](https://github.com/rclab-auth/gidopensees) GiD preprocessor. [gidopensees](https://github.com/rclab-auth/gidopensees) GiD preprocessor.
## Use ## Public import surface
```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() # defaults spec = Steel02Spec() # instantiate with defaults
spec.model_dump_json() # serialize spec.model_dump_json() # serialize
``` ```
`CATALOG` maps each material's gidopensees name (e.g. `"Steel02"`) to its `CATALOG` is a `dict[str, type[BaseModel]]` mapping every material's
generated `Spec` class. 58 entries, one per material in `OpenSees.mat`. gidopensees name (e.g. `"Steel02"`) to its generated `Spec` class.
It contains exactly 58 entries (one per material in `OpenSees.mat`).
Per-book discriminated unions live in `generated/__init__.py`: Per-book discriminated Union types are available in `generated/__init__.py`:
```python ```python
from otko.core.catalog.generated import UniaxialSteelMaterials from otko.core.catalog.generated import UniaxialSteelMaterials
@ -28,7 +29,7 @@ Condition specs live under `generated/conditions/`.
## Regenerating ## Regenerating
When upstream `schemas.json` changes, rerun codegen: Run the codegen tool any time the upstream `schemas.json` changes:
```bash ```bash
python -m tools.gidopensees_import.codegen \ python -m tools.gidopensees_import.codegen \
@ -38,15 +39,17 @@ python -m tools.gidopensees_import.codegen \
## Do not hand-edit `generated/` ## Do not hand-edit `generated/`
Codegen overwrites it. Put overrides, corrections, and extensions in Files under `generated/` are overwritten on each codegen run.
`curated/` (empty for now, reserved). Hand-curated overrides, corrections, or extensions belong in
`curated/` (currently empty — reserved for future use).
## Scope note ## Scope note
Spec classes are schema descriptions: field names, types, defaults, UI Catalog Spec classes are **schema descriptions only**. They capture the
metadata from the gidopensees definition files. They are not wired into field names, types, defaults, and UI metadata from the gidopensees
the runtime. Mapping a `Spec` to an actual `uniaxialMaterial` call is definition files. They are **not yet wired into the OpenSees runtime**.
still open — see the ADR. The mapping from a `Spec` to an actual `uniaxialMaterial` call is a
future deliverable tracked in the ADR.
## Attribution ## Attribution
@ -54,6 +57,7 @@ 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` holds the 58 material specs. Condition specs stay in their own `CATALOG` exposes the 58 material specs only; condition specs are intentionally
namespace (`generated/conditions/`, 39 specs) so the two don't pollute kept in a separate namespace (`generated/conditions/`, 39 specs) so that
each other. material and boundary-condition objects remain independently importable and
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="otko_")) target = results_dir or Path(tempfile.mkdtemp(prefix="osstudio_"))
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 OTKO version and the display units. the Studio version and the display units.
Raises: Raises:
ValueError: If ``case_id`` matches no analysis case. ValueError: If ``case_id`` matches no analysis case.