feat: named case-result load combinations with full GUI support
Snapshots the current development tree, headlined by proper load combinations (user request): a reusable LoadCombination entity of weighted completed static-case results (e.g. 1.2xDead + 1.6xLive). - core: LoadCombination/LoadCombinationItem entities, Project integration (lookup, unique ids, reference validation) - services: combinations.py (linear superposition + envelope), exported via services __init__ - commands: undoable Add/Delete/Update for combinations - GUI: Load Combinations manager dialog, Run-dialog evaluation, envelope display in Results panel, Combinations tab in Table dock - tests: unit coverage (validation, math, error paths) + integration superposition check vs a single factored run
This commit is contained in:
commit
f361fee969
560 changed files with 178701 additions and 0 deletions
58
AGENTS.md
Normal file
58
AGENTS.md
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# AGENTS.md — otko
|
||||
|
||||
Pre-alpha SAP2000-style desktop GUI for OpenSeesPy. Python 3.10+; **Windows requires 3.12+** (`openseespywin==3.8.0.0` has no 3.11 wheel). Solver pinned: `openseespy==3.8.0.0`.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
pip install -e ".[gui,dev]" # desktop: Qt + PyVista + dev tools
|
||||
pip install -e . # headless: core + services only, no Qt (scripts, notebooks, web backends)
|
||||
python -m otko # launch GUI (src/otko/app.py:run)
|
||||
```
|
||||
|
||||
`scipy`/`pandas` in `[gui]` extras are phantom deps (not imported as of 2026-06) — do not add imports expecting them.
|
||||
|
||||
## Architecture (enforced in review — see PR template)
|
||||
|
||||
Strict one-way MVVM + services: `views → viewmodels → services → core`.
|
||||
|
||||
- `core/` (entities: `project.py`, `geometry/`, `materials/`, `sections/`, `loads/`, `analysis/`, `catalog/`): stdlib + numpy + pydantic only. **No Qt, no openseespy. Period.**
|
||||
- `services/` (`opensees_runner.py`, `persistence.py`, `results.py`, ...): may use core + h5py + openseespy. **No Qt.**
|
||||
- `views/`: PySide6/pyvistaqt only. **No direct `import openseespy`** — go through a service.
|
||||
- `viewmodels/` bridges core↔Qt (signals, `QUndoStack`); `commands/` holds `QUndoCommand` subclasses.
|
||||
- Rules: public functions need type hints + docstring; new domain entities go through Pydantic validation; ops >50 ms run off the GUI thread (`AnalysisWorker` in QThread, cancel via `isInterruptionRequested()`, results cross threads as lightweight `ResultsHandle` to HDF5).
|
||||
|
||||
Runner emits OpenSeesPy commands in fixed order (`docs/architecture.md`): `wipe → model → node → fix → material → section → geomTransf → element → timeSeries → pattern/load → recorder → system/numberer/... → analyze`. Never reorder.
|
||||
|
||||
## Verify (in this order)
|
||||
|
||||
```bash
|
||||
ruff check src tests
|
||||
ruff format src tests # line-length 100, E501 ignored
|
||||
mypy src/otko/core src/otko/services
|
||||
pytest -m "not slow" # CI gate: lint → this, on 3.10/3.11/3.12 × ubuntu/windows/macos
|
||||
```
|
||||
|
||||
Focused runs: `pytest tests/unit` (pure logic, ms), `pytest tests/gui -k <name>` (pytest-qt, needs display), `pytest tests/integration -k <name>` (real OpenSeesPy runs). Single test: `pytest tests/unit/test_project.py::test_name -q`. Markers: `gui`, `slow`. Linux GUI tests need `xvfb-run -a pytest ...` plus system Qt libs (see `ci.yml` apt list).
|
||||
|
||||
Notes: `tests/conftest.py` auto-`ops.wipe()`s the OpenSees domain between tests (lazy import so core tests stay Qt/OpenSees-free). Coverage omits `views/`. Commit style: Conventional Commits (`feat:`, `fix:`, ...). `pre-commit install` runs ruff + mypy (mypy hook scoped to `core|services|viewmodels`).
|
||||
|
||||
## Git & Forgejo
|
||||
|
||||
Remote is a self-hosted Forgejo, SSH-only:
|
||||
|
||||
```bash
|
||||
git remote add origin ssh://git@smill-home.ddns.net/smill/otko.git
|
||||
```
|
||||
|
||||
- Web UI / HTTPS clone: `https://smill-home.ddns.net/forgejo/smill/otko.git` (note the `/forgejo` subpath — SSH URLs don't have it).
|
||||
- Forgejo SSH runs through the system sshd on **port 22** (`SSH_PORT = 22` in the server's `app.ini`). If the site ever advertises a `:2222` SSH URL again, that config regressed — push/pull with port 22 anyway and flag it.
|
||||
- Auth is by SSH key (Forgejo → Settings → SSH Keys). Test: `ssh -T git@smill-home.ddns.net` should answer `Hi there, <user>! ... Forgejo does not provide shell access.`
|
||||
- Default branch `main`. Work on short-lived branches (`fix/<topic>`, `docs/<topic>`), open a PR against `main`. Never commit directly to `main` from an agent session.
|
||||
- Repo root is `otko/` itself. (On the dev laptop there is also an empty, commit-less parent repo at `Sync/coding/` — ignore it; all git work happens inside `otko/`.)
|
||||
|
||||
## Examples & persistence
|
||||
|
||||
- `examples/*.py` are source of truth; `examples/*.osmodel` are generated artifacts (checked in). Never hand-edit `.osmodel` — change the script and regen: `python examples/cantilever.py` (each script saves, reloads, asserts clean round-trip).
|
||||
- Projects persist as single Pydantic-validated JSON `.osmodel` (diffable); analysis output goes to `*.osresults.h5` (HDF5, one group per case).
|
||||
- Quick smoke: open `examples/cantilever.osmodel` → run `Tip-Load` → M3 peaks 50 kN·m at fixed end.
|
||||
Loading…
Reference in a new issue