opendss-designer 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- opendss_designer-0.1.0/.github/workflows/ci.yml +64 -0
- opendss_designer-0.1.0/.github/workflows/docs.yml +42 -0
- opendss_designer-0.1.0/.github/workflows/publish.yml +51 -0
- opendss_designer-0.1.0/.gitignore +20 -0
- opendss_designer-0.1.0/FUTURE_IMPROVEMENTS.md +109 -0
- opendss_designer-0.1.0/LICENSE +21 -0
- opendss_designer-0.1.0/PKG-INFO +126 -0
- opendss_designer-0.1.0/README.md +98 -0
- opendss_designer-0.1.0/config/linecodes.csv +26 -0
- opendss_designer-0.1.0/docs/adding-an-element.md +54 -0
- opendss_designer-0.1.0/docs/development.md +68 -0
- opendss_designer-0.1.0/docs/features.md +75 -0
- opendss_designer-0.1.0/docs/getting-started.md +75 -0
- opendss_designer-0.1.0/docs/importing-dss.md +38 -0
- opendss_designer-0.1.0/docs/index.md +53 -0
- opendss_designer-0.1.0/docs/screenshot.png +0 -0
- opendss_designer-0.1.0/examples/demo-substation.oneline.json +28 -0
- opendss_designer-0.1.0/frontend/e2e/smoke.spec.ts +141 -0
- opendss_designer-0.1.0/frontend/index.html +12 -0
- opendss_designer-0.1.0/frontend/package-lock.json +2594 -0
- opendss_designer-0.1.0/frontend/package.json +31 -0
- opendss_designer-0.1.0/frontend/playwright.config.ts +29 -0
- opendss_designer-0.1.0/frontend/playwright.static.config.ts +10 -0
- opendss_designer-0.1.0/frontend/src/App.tsx +124 -0
- opendss_designer-0.1.0/frontend/src/components/BottomPanel.tsx +420 -0
- opendss_designer-0.1.0/frontend/src/components/ContextMenu.tsx +93 -0
- opendss_designer-0.1.0/frontend/src/components/EditorCanvas.tsx +309 -0
- opendss_designer-0.1.0/frontend/src/components/GraphPanel.tsx +390 -0
- opendss_designer-0.1.0/frontend/src/components/LoadingPie.tsx +25 -0
- opendss_designer-0.1.0/frontend/src/components/Palette.tsx +146 -0
- opendss_designer-0.1.0/frontend/src/components/PropertiesPanel.tsx +147 -0
- opendss_designer-0.1.0/frontend/src/components/ResultTooltip.tsx +127 -0
- opendss_designer-0.1.0/frontend/src/components/Toolbar.tsx +227 -0
- opendss_designer-0.1.0/frontend/src/components/edges/LineEdge.tsx +62 -0
- opendss_designer-0.1.0/frontend/src/components/edges/WireEdge.tsx +19 -0
- opendss_designer-0.1.0/frontend/src/components/edges/waypoints.tsx +80 -0
- opendss_designer-0.1.0/frontend/src/components/nodes/BreakerNode.tsx +45 -0
- opendss_designer-0.1.0/frontend/src/components/nodes/BusbarNode.tsx +67 -0
- opendss_designer-0.1.0/frontend/src/components/nodes/CapacitorNode.tsx +41 -0
- opendss_designer-0.1.0/frontend/src/components/nodes/GeneratorNode.tsx +37 -0
- opendss_designer-0.1.0/frontend/src/components/nodes/LoadNode.tsx +35 -0
- opendss_designer-0.1.0/frontend/src/components/nodes/TransformerNode.tsx +38 -0
- opendss_designer-0.1.0/frontend/src/components/nodes/VsourceNode.tsx +33 -0
- opendss_designer-0.1.0/frontend/src/components/nodes/common.tsx +135 -0
- opendss_designer-0.1.0/frontend/src/index.css +507 -0
- opendss_designer-0.1.0/frontend/src/lib/api.ts +43 -0
- opendss_designer-0.1.0/frontend/src/lib/colorScale.ts +22 -0
- opendss_designer-0.1.0/frontend/src/lib/defaults.ts +49 -0
- opendss_designer-0.1.0/frontend/src/lib/fields.test.ts +42 -0
- opendss_designer-0.1.0/frontend/src/lib/fields.tsx +158 -0
- opendss_designer-0.1.0/frontend/src/lib/graph.test.ts +104 -0
- opendss_designer-0.1.0/frontend/src/lib/graph.ts +155 -0
- opendss_designer-0.1.0/frontend/src/lib/layout.test.ts +114 -0
- opendss_designer-0.1.0/frontend/src/lib/layout.ts +173 -0
- opendss_designer-0.1.0/frontend/src/lib/lineCodes.test.ts +33 -0
- opendss_designer-0.1.0/frontend/src/lib/lineCodes.ts +66 -0
- opendss_designer-0.1.0/frontend/src/lib/solve.ts +38 -0
- opendss_designer-0.1.0/frontend/src/main.tsx +28 -0
- opendss_designer-0.1.0/frontend/src/store/circuitStore.test.ts +313 -0
- opendss_designer-0.1.0/frontend/src/store/circuitStore.ts +519 -0
- opendss_designer-0.1.0/frontend/src/store/resultsStore.ts +72 -0
- opendss_designer-0.1.0/frontend/src/types/circuit.ts +108 -0
- opendss_designer-0.1.0/frontend/tsconfig.json +21 -0
- opendss_designer-0.1.0/frontend/vite.config.ts +24 -0
- opendss_designer-0.1.0/mkdocs.yml +49 -0
- opendss_designer-0.1.0/pyproject.toml +56 -0
- opendss_designer-0.1.0/scripts/build_frontend.py +29 -0
- opendss_designer-0.1.0/src/opendss_designer/__init__.py +3 -0
- opendss_designer-0.1.0/src/opendss_designer/api/__init__.py +0 -0
- opendss_designer-0.1.0/src/opendss_designer/api/routes.py +60 -0
- opendss_designer-0.1.0/src/opendss_designer/cli.py +55 -0
- opendss_designer-0.1.0/src/opendss_designer/core/__init__.py +0 -0
- opendss_designer-0.1.0/src/opendss_designer/core/compiler.py +263 -0
- opendss_designer-0.1.0/src/opendss_designer/core/connectivity.py +191 -0
- opendss_designer-0.1.0/src/opendss_designer/core/engine.py +272 -0
- opendss_designer-0.1.0/src/opendss_designer/core/importer.py +345 -0
- opendss_designer-0.1.0/src/opendss_designer/core/linecodes.py +80 -0
- opendss_designer-0.1.0/src/opendss_designer/core/model.py +73 -0
- opendss_designer-0.1.0/src/opendss_designer/core/validate.py +110 -0
- opendss_designer-0.1.0/src/opendss_designer/server.py +32 -0
- opendss_designer-0.1.0/src/opendss_designer/static/assets/index-D0kQVP4J.js +73 -0
- opendss_designer-0.1.0/src/opendss_designer/static/assets/index-D37i8SmI.css +1 -0
- opendss_designer-0.1.0/src/opendss_designer/static/index.html +13 -0
- opendss_designer-0.1.0/tests/conftest.py +46 -0
- opendss_designer-0.1.0/tests/fixtures/full-circuit.oneline.json +172 -0
- opendss_designer-0.1.0/tests/test_analysis.py +61 -0
- opendss_designer-0.1.0/tests/test_compiler.py +53 -0
- opendss_designer-0.1.0/tests/test_connectivity.py +120 -0
- opendss_designer-0.1.0/tests/test_import_roundtrip.py +107 -0
- opendss_designer-0.1.0/tests/test_linecodes.py +62 -0
- opendss_designer-0.1.0/tests/test_schema_fixture.py +74 -0
- opendss_designer-0.1.0/tests/test_solve_smoke.py +43 -0
- opendss_designer-0.1.0/voltage graph plot.png +0 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
backend:
|
|
10
|
+
name: Backend (pytest)
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: actions/setup-python@v5
|
|
15
|
+
with:
|
|
16
|
+
python-version: '3.12'
|
|
17
|
+
cache: pip
|
|
18
|
+
- run: pip install -e ".[dev]"
|
|
19
|
+
- run: pytest -q
|
|
20
|
+
|
|
21
|
+
frontend:
|
|
22
|
+
name: Frontend (tsc + vitest)
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
defaults:
|
|
25
|
+
run:
|
|
26
|
+
working-directory: frontend
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v4
|
|
29
|
+
- uses: actions/setup-node@v4
|
|
30
|
+
with:
|
|
31
|
+
node-version: '24'
|
|
32
|
+
cache: npm
|
|
33
|
+
cache-dependency-path: frontend/package-lock.json
|
|
34
|
+
- run: npm ci
|
|
35
|
+
- run: npm run build
|
|
36
|
+
- run: npm test
|
|
37
|
+
|
|
38
|
+
e2e:
|
|
39
|
+
name: End-to-end (Playwright)
|
|
40
|
+
runs-on: ubuntu-latest
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/checkout@v4
|
|
43
|
+
- uses: actions/setup-python@v5
|
|
44
|
+
with:
|
|
45
|
+
python-version: '3.12'
|
|
46
|
+
cache: pip
|
|
47
|
+
- uses: actions/setup-node@v4
|
|
48
|
+
with:
|
|
49
|
+
node-version: '24'
|
|
50
|
+
cache: npm
|
|
51
|
+
cache-dependency-path: frontend/package-lock.json
|
|
52
|
+
- run: pip install -e ".[dev]"
|
|
53
|
+
- run: npm ci
|
|
54
|
+
working-directory: frontend
|
|
55
|
+
- run: npx playwright install --with-deps chromium
|
|
56
|
+
working-directory: frontend
|
|
57
|
+
- run: npm run e2e
|
|
58
|
+
working-directory: frontend
|
|
59
|
+
- uses: actions/upload-artifact@v4
|
|
60
|
+
if: failure()
|
|
61
|
+
with:
|
|
62
|
+
name: playwright-traces
|
|
63
|
+
path: frontend/test-results/
|
|
64
|
+
retention-days: 7
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: Docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths:
|
|
7
|
+
- docs/**
|
|
8
|
+
- mkdocs.yml
|
|
9
|
+
- .github/workflows/docs.yml
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
pages: write
|
|
15
|
+
id-token: write
|
|
16
|
+
|
|
17
|
+
concurrency:
|
|
18
|
+
group: pages
|
|
19
|
+
cancel-in-progress: true
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
deploy:
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
environment:
|
|
25
|
+
name: github-pages
|
|
26
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v4
|
|
29
|
+
- uses: actions/setup-python@v5
|
|
30
|
+
with:
|
|
31
|
+
python-version: '3.12'
|
|
32
|
+
cache: pip
|
|
33
|
+
- run: pip install mkdocs-material
|
|
34
|
+
- run: mkdocs build --strict
|
|
35
|
+
- uses: actions/configure-pages@v5
|
|
36
|
+
with:
|
|
37
|
+
enablement: true
|
|
38
|
+
- uses: actions/upload-pages-artifact@v3
|
|
39
|
+
with:
|
|
40
|
+
path: site
|
|
41
|
+
- id: deployment
|
|
42
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: actions/setup-node@v4
|
|
14
|
+
with:
|
|
15
|
+
node-version: '24'
|
|
16
|
+
cache: npm
|
|
17
|
+
cache-dependency-path: frontend/package-lock.json
|
|
18
|
+
- uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: '3.12'
|
|
21
|
+
cache: pip
|
|
22
|
+
- run: python scripts/build_frontend.py
|
|
23
|
+
- run: pip install build
|
|
24
|
+
- run: python -m build
|
|
25
|
+
# The frontend must ship inside the wheel; fail loudly if it didn't.
|
|
26
|
+
- name: Verify wheel contains the frontend
|
|
27
|
+
run: |
|
|
28
|
+
python - <<'EOF'
|
|
29
|
+
import glob, sys, zipfile
|
|
30
|
+
wheel = glob.glob("dist/*.whl")[0]
|
|
31
|
+
names = zipfile.ZipFile(wheel).namelist()
|
|
32
|
+
assert "opendss_designer/static/index.html" in names, names
|
|
33
|
+
print(f"OK: {wheel} contains the built frontend")
|
|
34
|
+
EOF
|
|
35
|
+
- uses: actions/upload-artifact@v4
|
|
36
|
+
with:
|
|
37
|
+
name: dist
|
|
38
|
+
path: dist/
|
|
39
|
+
|
|
40
|
+
publish:
|
|
41
|
+
needs: build
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
environment: pypi
|
|
44
|
+
permissions:
|
|
45
|
+
id-token: write # PyPI trusted publishing (OIDC) — no API token needed
|
|
46
|
+
steps:
|
|
47
|
+
- uses: actions/download-artifact@v4
|
|
48
|
+
with:
|
|
49
|
+
name: dist
|
|
50
|
+
path: dist/
|
|
51
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.pyc
|
|
3
|
+
.pytest_cache/
|
|
4
|
+
dist/
|
|
5
|
+
build/
|
|
6
|
+
*.egg-info/
|
|
7
|
+
|
|
8
|
+
frontend/node_modules/
|
|
9
|
+
frontend/dist/
|
|
10
|
+
|
|
11
|
+
# built frontend copied into the package (regenerate with scripts/build_frontend.py)
|
|
12
|
+
src/opendss_designer/static/
|
|
13
|
+
frontend/*.tsbuildinfo
|
|
14
|
+
|
|
15
|
+
# MkDocs build output
|
|
16
|
+
site/
|
|
17
|
+
|
|
18
|
+
# Playwright output
|
|
19
|
+
frontend/test-results/
|
|
20
|
+
frontend/playwright-report/
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Roadmap
|
|
2
|
+
|
|
3
|
+
Features deferred from v1, organized into milestones. Each milestone leaves the app
|
|
4
|
+
in a coherent, working state. Ordering rationale: foundation first (tests/CI protect
|
|
5
|
+
everything after), then balanced passes across editor UX, new components, analysis,
|
|
6
|
+
and platform. Sizing is rough (working sessions).
|
|
7
|
+
|
|
8
|
+
## M1 — Foundation & hardening — ✅ DONE (2026-08-30)
|
|
9
|
+
|
|
10
|
+
No visible features; protects everything after.
|
|
11
|
+
|
|
12
|
+
- **Frontend test harness** (vitest) for the pure logic: `circuitStore.ts`
|
|
13
|
+
(`validateConnection`, `toCircuitJSON`/`fromCircuitJSON`, busbar handle remapping),
|
|
14
|
+
`lib/layout.ts` geometry, `lib/fields.tsx` winding get/patch
|
|
15
|
+
- **Playwright smoke e2e**: place source → line → load, solve, assert voltage overlay;
|
|
16
|
+
export/import round-trip
|
|
17
|
+
- **CI** (GitHub Actions): pytest + `tsc -b` + vitest + Playwright on push
|
|
18
|
+
- **Schema-drift guard**: round-trip test with every node/edge type, catching
|
|
19
|
+
`types/circuit.ts` ↔ `core/model.py` divergence (revisit codegen if it keeps biting)
|
|
20
|
+
- **Error UX cleanup**: replace `alert()` call sites with the existing `flash` toast;
|
|
21
|
+
surface errors from `lib/solve.ts`; narrow the bare `except Exception` in `routes.py`
|
|
22
|
+
so server bugs return 500, not 400
|
|
23
|
+
- Housekeeping: remove stray root screenshot, drop unused `react-hook-form`
|
|
24
|
+
|
|
25
|
+
## M2 — Editor UX quick wins — ✅ DONE (2026-08-30)
|
|
26
|
+
|
|
27
|
+
All frontend-only; the M1 vitest harness covers the store changes.
|
|
28
|
+
|
|
29
|
+
- ~~**Copy/paste** and duplicate (Ctrl+C/V/D)~~ — collision-safe renaming, cascading offsets
|
|
30
|
+
- ~~**Keyboard palette shortcuts**~~ — S/B/T/K/L place, W/E switch wire/line mode
|
|
31
|
+
- ~~**Rotate symbols** (R key)~~ — params.rotation, handles follow; busbars excluded
|
|
32
|
+
- ~~**Right-click context menus**~~ — open/close breaker, rotate, duplicate, delete,
|
|
33
|
+
straighten edge
|
|
34
|
+
- ~~**Result tooltips** on hover~~ — per-phase V/angle (backend now returns `vangDeg`),
|
|
35
|
+
currents, power, loading
|
|
36
|
+
- ~~**Finer undo granularity**~~ — per-gesture grouping via begin/endGesture; selection
|
|
37
|
+
changes excluded from history
|
|
38
|
+
- Still open from this bucket: **multi-select property editing** (deferred to a later
|
|
39
|
+
milestone; single-element editing plus spreadsheet fill-down covers most of it)
|
|
40
|
+
|
|
41
|
+
## M3 — Component pack 1: real-feeder essentials — ✅ DONE (2026-08-30)
|
|
42
|
+
|
|
43
|
+
- ~~**Capacitor banks**~~ — shunt kvar, delta/wye, numsteps; imports/exports/solves
|
|
44
|
+
- ~~**Generators**~~ — kW/pf with model 1 (const PQ) or 3 (PV, holds vpu); circle-G symbol
|
|
45
|
+
- ~~**Line codes**~~ — built-in conductor preset library (`lib/lineCodes.ts`, 8 typical
|
|
46
|
+
OH/UG constructions) that stamps editable Ω/km values; imported linecode names kept
|
|
47
|
+
as reference tags. True LineCode entity round-trip stays in M7.
|
|
48
|
+
- ~~Importer support~~ — capacitors and generators read back; `docs/adding-an-element.md`
|
|
49
|
+
checklist written so remaining component types are mechanical
|
|
50
|
+
|
|
51
|
+
## M4 — Analysis pack 1 — ✅ DONE (2026-08-30)
|
|
52
|
+
|
|
53
|
+
- ~~**Fault study**~~ — `/api/faultstudy` (mode=faultstudy → per-bus Zsc1/Zsc0);
|
|
54
|
+
"Fault" overlay shows 3φ kA badges on busbars, tooltip adds 1φ/SC-MVA/Z1;
|
|
55
|
+
runs lazily on overlay select, invalidated by any circuit edit
|
|
56
|
+
- ~~**Losses breakdown**~~ — per-series-element kW/kvar losses (shunt elements
|
|
57
|
+
deliberately report none) in a sortable Losses tab with % of total
|
|
58
|
+
- ~~**Voltage profile plot**~~ — grew into a general **Graph tab**: pick Y
|
|
59
|
+
(bus V min/max, or per-element P/Q flow, current, loading, losses) vs X
|
|
60
|
+
(km from source via solver `busDistances`, or bus voltage); classic
|
|
61
|
+
OpenDSS plot styling — per-phase traces (black/red/blue), bold red
|
|
62
|
+
0.95/1.05 limit lines, framed white plot — with zoom buttons, drag-pan,
|
|
63
|
+
Shift+drag zoom box, wheel zoom, and phase toggles. Bottom panel is
|
|
64
|
+
drag-resizable (persisted height).
|
|
65
|
+
|
|
66
|
+
## M5 — DER pack + time series (~3–4 sessions)
|
|
67
|
+
|
|
68
|
+
The biggest single milestone; the M3 checklist makes the components mechanical.
|
|
69
|
+
|
|
70
|
+
- **PV systems** (`PVSystem`) — irradiance/temperature curves, inverter kVA
|
|
71
|
+
- **Storage** (`Storage`) — kWh rating, charge/discharge dispatch
|
|
72
|
+
- **LoadShape editor** (CSV paste + curve editor), assignable to loads/PV/storage
|
|
73
|
+
- **Daily/yearly time series** — energy meters and monitors, progress streaming
|
|
74
|
+
(websocket/SSE), result plots over time (add a charting dependency here)
|
|
75
|
+
|
|
76
|
+
## M6 — Regulation, protection & phases (~2–3 sessions)
|
|
77
|
+
|
|
78
|
+
- **Voltage regulators** (`RegControl` on an autotransformer) — band, PT ratio, LDC
|
|
79
|
+
- **3-winding transformers** — third handle; the per-winding editor already generalizes
|
|
80
|
+
- **Fuses, reclosers, relays** (`Fuse`, `Recloser`, `Relay`) — pairs with M4's fault study
|
|
81
|
+
- **Phase pinning** — connect 1-phase elements to a chosen phase (`.2`, `.3` suffixes;
|
|
82
|
+
`compiler.py` already accepts explicit suffixes, so this is mostly UI)
|
|
83
|
+
- **Per-phase display** — phase labels on wires, per-phase voltage readouts
|
|
84
|
+
|
|
85
|
+
## M7 — Platform & polish (~2–3 sessions, pick-and-choose)
|
|
86
|
+
|
|
87
|
+
- **Smarter .dss import layout**: elkjs layered layout; keep 2-terminal pass-through
|
|
88
|
+
buses as plain wires instead of busbars
|
|
89
|
+
- **Automatic wire routing** (elkjs edge routing — shares the elkjs dependency)
|
|
90
|
+
- **File System Access API** in-place saves (localStorage autosave already shipped)
|
|
91
|
+
- **Dark mode**, printable/exportable diagram (SVG/PNG export)
|
|
92
|
+
- **Round-trip preservation** of comments and unsupported elements on export
|
|
93
|
+
- **Split line**: drop a bus in the middle of an existing Line edge
|
|
94
|
+
|
|
95
|
+
## Parking lot (deferred until actually needed)
|
|
96
|
+
|
|
97
|
+
- **Incremental solve** — reuse the compiled circuit when only parameter values changed;
|
|
98
|
+
matters once circuits reach thousands of elements (v1 rebuilds every solve)
|
|
99
|
+
- **Multi-circuit tabs** / compare two scenarios side by side — big architectural change;
|
|
100
|
+
wait until the single-circuit workflow is mature
|
|
101
|
+
- **Explicit grounding elements** (`Reactor` to ground, grounding transformer symbols)
|
|
102
|
+
|
|
103
|
+
## Done since v1
|
|
104
|
+
|
|
105
|
+
- Project autosave to browser storage (debounced localStorage save + restore in `App.tsx`)
|
|
106
|
+
- M1 (2026-08-30): vitest unit tests (`frontend/src/**/*.test.ts`), Playwright e2e
|
|
107
|
+
(`frontend/e2e/`), GitHub Actions CI, schema-drift guard
|
|
108
|
+
(`tests/fixtures/full-circuit.oneline.json` round-tripped by both pytest and vitest),
|
|
109
|
+
flash-toast error surfacing (no more `alert()`), import bugs now 500 not 400
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ryan Sparks
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: opendss-designer
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A graphical user interface (GUI) for OpenDSS: draw one-line diagrams in your browser, run power flows, import and export .dss files
|
|
5
|
+
Project-URL: Homepage, https://opendssdesigner.ryanmsparks.com
|
|
6
|
+
Project-URL: Documentation, https://opendssdesigner.ryanmsparks.com
|
|
7
|
+
Project-URL: Repository, https://github.com/rsparks3/opendss-designer
|
|
8
|
+
Project-URL: Issues, https://github.com/rsparks3/opendss-designer/issues
|
|
9
|
+
Author-email: Ryan Sparks <ryan@ryanmsparks.com>
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: distribution-system,electrical-engineering,epri,graphical-interface,gui,one-line-diagram,opendss,opendssdirect,power-flow,power-systems,single-line-diagram
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Requires-Dist: fastapi>=0.115
|
|
21
|
+
Requires-Dist: opendssdirect-py>=0.9
|
|
22
|
+
Requires-Dist: pydantic>=2.9
|
|
23
|
+
Requires-Dist: uvicorn[standard]>=0.30
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: httpx; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# OpenDSS Designer
|
|
30
|
+
|
|
31
|
+
A free, open-source **graphical user interface (GUI) for
|
|
32
|
+
[OpenDSS](https://www.epri.com/pages/sa/opendss)**, EPRI's distribution system
|
|
33
|
+
simulator — built on [OpenDSSDirect.py](https://github.com/dss-extensions/OpenDSSDirect.py).
|
|
34
|
+
Draw a substation-style one-line (single-line) diagram in your browser — the
|
|
35
|
+
drawing **is** the circuit model — then run a power flow and see voltages,
|
|
36
|
+
loading, and violations right on the diagram. Import existing `.dss` files,
|
|
37
|
+
edit visually or in a spreadsheet view, and export runnable OpenDSS scripts.
|
|
38
|
+
|
|
39
|
+
📖 **Documentation: [opendssdesigner.ryanmsparks.com](https://opendssdesigner.ryanmsparks.com)**
|
|
40
|
+
|
|
41
|
+

|
|
42
|
+
|
|
43
|
+
## Features (v1)
|
|
44
|
+
|
|
45
|
+
- **Click-and-place palette**: Source (Vsource), Busbar, 2-winding Transformer, Breaker/Switch, Load —
|
|
46
|
+
placement is sticky, so keep clicking to drop several; Esc to stop
|
|
47
|
+
- **Drag-to-wire**: drag between terminals; choose **Wire** (ideal connection, merges buses) or
|
|
48
|
+
**Line** (a real OpenDSS Line with impedance and length). Illegal connections (busbar-to-busbar
|
|
49
|
+
wires, self-connections, duplicates) are refused with an explanation
|
|
50
|
+
- **Stretchable busbars** with connection points along both edges (top and bottom rows), plus
|
|
51
|
+
implicit junction buses when you wire elements directly together
|
|
52
|
+
- **Double-click a breaker** to open/close it; **double-click a wire or line** to add a draggable
|
|
53
|
+
routing point and shape the run yourself (double-click a point to remove it)
|
|
54
|
+
- **Properties panel** with the OpenDSS parameters for each element (kV, kVA, impedances,
|
|
55
|
+
phases 1/2/3, wye/delta, load model…)
|
|
56
|
+
- **Solve** button → snapshot power flow → overlays on the diagram:
|
|
57
|
+
bus voltages (pu), element loading as pie charts + %, power flows, with color-coded
|
|
58
|
+
violations (undervoltage blue, overvoltage/overload red) and total losses in the
|
|
59
|
+
status bar — or toggle **Auto** to re-solve automatically on every circuit change
|
|
60
|
+
- **Live validation**: unconnected terminals, missing source, islands, duplicate names,
|
|
61
|
+
kV mismatches — errors disable Solve and halo the offending element
|
|
62
|
+
- **Spreadsheet view**: an Elements tab in the bottom panel lists every source, busbar,
|
|
63
|
+
transformer, line, breaker, and load in an editable table (plus a read-only bus-results
|
|
64
|
+
table after a solve) — edit values in bulk with an Excel-style fill-down handle,
|
|
65
|
+
click ⌖ to locate an element on the diagram
|
|
66
|
+
- **Undo/redo** (Ctrl+Z / Ctrl+Y), delete key, grid snapping, pan/zoom, minimap
|
|
67
|
+
- **Save/Open** projects as JSON; **Export** a runnable `.dss` file;
|
|
68
|
+
**Import** existing `.dss` files (select the main file plus anything it redirects to)
|
|
69
|
+
with a hierarchical auto-layout — source at top, loads beneath their buses
|
|
70
|
+
|
|
71
|
+
## Install & run
|
|
72
|
+
|
|
73
|
+
Requires Python 3.10+.
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pip install opendss-designer
|
|
77
|
+
opendss-designer # starts a local server and opens your browser
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
(Or from a clone of this repo: `pip install -e .` — see Development below for
|
|
81
|
+
building the frontend.)
|
|
82
|
+
|
|
83
|
+
Options: `--port 8721`, `--no-browser`.
|
|
84
|
+
|
|
85
|
+
Try it: **Open** `examples/demo-substation.oneline.json`, press **Solve**.
|
|
86
|
+
|
|
87
|
+
## Development
|
|
88
|
+
|
|
89
|
+
Backend (FastAPI + OpenDSSDirect.py):
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
pip install -e .[dev]
|
|
93
|
+
pytest # backend test suite
|
|
94
|
+
PYTHONPATH=src python -m opendss_designer.cli --no-browser # serve API + built frontend
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Frontend (React + Vite + React Flow), in a second terminal:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
cd frontend
|
|
101
|
+
npm install
|
|
102
|
+
npm run dev # http://localhost:5173, proxies /api to :8721
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
To bundle the frontend into the Python package (before building a wheel):
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
python scripts/build_frontend.py
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Architecture
|
|
112
|
+
|
|
113
|
+
- `frontend/` — React + Vite app. React Flow canvas with custom ANSI-symbol nodes;
|
|
114
|
+
zustand store is the canonical circuit model (zundo for undo history).
|
|
115
|
+
`window.opendssDesigner` exposes the stores for console scripting.
|
|
116
|
+
- `src/opendss_designer/core/connectivity.py` — union-find translation of the drawn graph
|
|
117
|
+
into OpenDSS bus names (wires merge terminals; Line edges are series elements).
|
|
118
|
+
- `src/opendss_designer/core/compiler.py` — circuit JSON → ordered OpenDSS Text commands;
|
|
119
|
+
the same commands back both `/api/solve` and `.dss` export.
|
|
120
|
+
- `src/opendss_designer/core/engine.py` — solves and extracts per-bus voltages and
|
|
121
|
+
per-element loading through the OpenDSSDirect API. Full rebuild per solve; no engine
|
|
122
|
+
state persists between requests.
|
|
123
|
+
- `src/opendss_designer/core/importer.py` — imports `.dss` by compiling it with OpenDSS's
|
|
124
|
+
own parser and reading the model back out.
|
|
125
|
+
|
|
126
|
+
See `FUTURE_IMPROVEMENTS.md` for the roadmap.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# OpenDSS Designer
|
|
2
|
+
|
|
3
|
+
A free, open-source **graphical user interface (GUI) for
|
|
4
|
+
[OpenDSS](https://www.epri.com/pages/sa/opendss)**, EPRI's distribution system
|
|
5
|
+
simulator — built on [OpenDSSDirect.py](https://github.com/dss-extensions/OpenDSSDirect.py).
|
|
6
|
+
Draw a substation-style one-line (single-line) diagram in your browser — the
|
|
7
|
+
drawing **is** the circuit model — then run a power flow and see voltages,
|
|
8
|
+
loading, and violations right on the diagram. Import existing `.dss` files,
|
|
9
|
+
edit visually or in a spreadsheet view, and export runnable OpenDSS scripts.
|
|
10
|
+
|
|
11
|
+
📖 **Documentation: [opendssdesigner.ryanmsparks.com](https://opendssdesigner.ryanmsparks.com)**
|
|
12
|
+
|
|
13
|
+

|
|
14
|
+
|
|
15
|
+
## Features (v1)
|
|
16
|
+
|
|
17
|
+
- **Click-and-place palette**: Source (Vsource), Busbar, 2-winding Transformer, Breaker/Switch, Load —
|
|
18
|
+
placement is sticky, so keep clicking to drop several; Esc to stop
|
|
19
|
+
- **Drag-to-wire**: drag between terminals; choose **Wire** (ideal connection, merges buses) or
|
|
20
|
+
**Line** (a real OpenDSS Line with impedance and length). Illegal connections (busbar-to-busbar
|
|
21
|
+
wires, self-connections, duplicates) are refused with an explanation
|
|
22
|
+
- **Stretchable busbars** with connection points along both edges (top and bottom rows), plus
|
|
23
|
+
implicit junction buses when you wire elements directly together
|
|
24
|
+
- **Double-click a breaker** to open/close it; **double-click a wire or line** to add a draggable
|
|
25
|
+
routing point and shape the run yourself (double-click a point to remove it)
|
|
26
|
+
- **Properties panel** with the OpenDSS parameters for each element (kV, kVA, impedances,
|
|
27
|
+
phases 1/2/3, wye/delta, load model…)
|
|
28
|
+
- **Solve** button → snapshot power flow → overlays on the diagram:
|
|
29
|
+
bus voltages (pu), element loading as pie charts + %, power flows, with color-coded
|
|
30
|
+
violations (undervoltage blue, overvoltage/overload red) and total losses in the
|
|
31
|
+
status bar — or toggle **Auto** to re-solve automatically on every circuit change
|
|
32
|
+
- **Live validation**: unconnected terminals, missing source, islands, duplicate names,
|
|
33
|
+
kV mismatches — errors disable Solve and halo the offending element
|
|
34
|
+
- **Spreadsheet view**: an Elements tab in the bottom panel lists every source, busbar,
|
|
35
|
+
transformer, line, breaker, and load in an editable table (plus a read-only bus-results
|
|
36
|
+
table after a solve) — edit values in bulk with an Excel-style fill-down handle,
|
|
37
|
+
click ⌖ to locate an element on the diagram
|
|
38
|
+
- **Undo/redo** (Ctrl+Z / Ctrl+Y), delete key, grid snapping, pan/zoom, minimap
|
|
39
|
+
- **Save/Open** projects as JSON; **Export** a runnable `.dss` file;
|
|
40
|
+
**Import** existing `.dss` files (select the main file plus anything it redirects to)
|
|
41
|
+
with a hierarchical auto-layout — source at top, loads beneath their buses
|
|
42
|
+
|
|
43
|
+
## Install & run
|
|
44
|
+
|
|
45
|
+
Requires Python 3.10+.
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install opendss-designer
|
|
49
|
+
opendss-designer # starts a local server and opens your browser
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
(Or from a clone of this repo: `pip install -e .` — see Development below for
|
|
53
|
+
building the frontend.)
|
|
54
|
+
|
|
55
|
+
Options: `--port 8721`, `--no-browser`.
|
|
56
|
+
|
|
57
|
+
Try it: **Open** `examples/demo-substation.oneline.json`, press **Solve**.
|
|
58
|
+
|
|
59
|
+
## Development
|
|
60
|
+
|
|
61
|
+
Backend (FastAPI + OpenDSSDirect.py):
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pip install -e .[dev]
|
|
65
|
+
pytest # backend test suite
|
|
66
|
+
PYTHONPATH=src python -m opendss_designer.cli --no-browser # serve API + built frontend
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Frontend (React + Vite + React Flow), in a second terminal:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
cd frontend
|
|
73
|
+
npm install
|
|
74
|
+
npm run dev # http://localhost:5173, proxies /api to :8721
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
To bundle the frontend into the Python package (before building a wheel):
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
python scripts/build_frontend.py
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Architecture
|
|
84
|
+
|
|
85
|
+
- `frontend/` — React + Vite app. React Flow canvas with custom ANSI-symbol nodes;
|
|
86
|
+
zustand store is the canonical circuit model (zundo for undo history).
|
|
87
|
+
`window.opendssDesigner` exposes the stores for console scripting.
|
|
88
|
+
- `src/opendss_designer/core/connectivity.py` — union-find translation of the drawn graph
|
|
89
|
+
into OpenDSS bus names (wires merge terminals; Line edges are series elements).
|
|
90
|
+
- `src/opendss_designer/core/compiler.py` — circuit JSON → ordered OpenDSS Text commands;
|
|
91
|
+
the same commands back both `/api/solve` and `.dss` export.
|
|
92
|
+
- `src/opendss_designer/core/engine.py` — solves and extracts per-bus voltages and
|
|
93
|
+
per-element loading through the OpenDSSDirect API. Full rebuild per solve; no engine
|
|
94
|
+
state persists between requests.
|
|
95
|
+
- `src/opendss_designer/core/importer.py` — imports `.dss` by compiling it with OpenDSS's
|
|
96
|
+
own parser and reading the model back out.
|
|
97
|
+
|
|
98
|
+
See `FUTURE_IMPROVEMENTS.md` for the roadmap.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# OpenDSS Designer - line conductor preset library
|
|
2
|
+
#
|
|
3
|
+
# Each row becomes a "Conductor preset" option on Line elements. Edit freely
|
|
4
|
+
# (add, remove, or tune rows); the server re-reads this file on every request,
|
|
5
|
+
# so changes appear after a browser refresh - no rebuild or restart needed.
|
|
6
|
+
#
|
|
7
|
+
# Columns:
|
|
8
|
+
# code - short unique id (stored on the line as a reference tag)
|
|
9
|
+
# label - text shown in the preset dropdown
|
|
10
|
+
# units - length unit for the impedances (km, m, mi, kft, ft)
|
|
11
|
+
# r1,x1 - positive-sequence resistance/reactance, ohms per unit length
|
|
12
|
+
# r0,x0 - zero-sequence resistance/reactance, ohms per unit length
|
|
13
|
+
# normamps - normal ampacity rating, amps
|
|
14
|
+
#
|
|
15
|
+
# Values shipped here are REPRESENTATIVE of typical distribution construction
|
|
16
|
+
# (overhead: flat crossarm spacing; UG: 3-1/c XLPE, trefoil). Replace with
|
|
17
|
+
# your utility's data for real studies.
|
|
18
|
+
code,label,units,r1,x1,r0,x0,normamps
|
|
19
|
+
acsr-1/0,OH ACSR 1/0 (Raven),km,0.696,0.494,0.874,1.482,230
|
|
20
|
+
acsr-4/0,OH ACSR 4/0 (Penguin),km,0.35,0.468,0.528,1.456,340
|
|
21
|
+
acsr-336,OH ACSR 336.4 (Linnet),km,0.19,0.451,0.368,1.439,530
|
|
22
|
+
acsr-556,OH ACSR 556.5 (Dove),km,0.118,0.427,0.296,1.415,730
|
|
23
|
+
acsr-795,OH ACSR 795 (Drake),km,0.084,0.412,0.262,1.4,900
|
|
24
|
+
ug-1/0-al,UG XLPE 1/0 Al,km,0.552,0.156,1.06,0.52,175
|
|
25
|
+
ug-4/0-al,UG XLPE 4/0 Al,km,0.277,0.14,0.6,0.44,260
|
|
26
|
+
ug-750-al,UG XLPE 750 Al,km,0.081,0.122,0.28,0.35,475
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Adding a new element type
|
|
2
|
+
|
|
3
|
+
Checklist for adding a component to the palette (as done for `capacitor` and
|
|
4
|
+
`generator` in M3 — diff those commits for a worked example). Items marked ⚡
|
|
5
|
+
break loudly if forgotten (schema-drift tests); the rest fail quietly, so walk
|
|
6
|
+
the whole list.
|
|
7
|
+
|
|
8
|
+
## Backend (`src/opendss_designer/`)
|
|
9
|
+
|
|
10
|
+
1. ⚡ `core/model.py` — add the type to the `NodeType` literal and its handles
|
|
11
|
+
to `NODE_TERMINALS` (skip `NODE_TERMINALS` only for dynamic-handle nodes
|
|
12
|
+
like busbar).
|
|
13
|
+
2. `core/compiler.py` — emission block in `compile_circuit`: filter nodes by
|
|
14
|
+
type, build the `new <class>.<name> ...` command via `element_name()` (which
|
|
15
|
+
registers the element_map entry for results/issue mapping), use
|
|
16
|
+
`conn.node_buses[n.id]` + `_bus_suffix` for bus connections, and
|
|
17
|
+
`kv_bases.add()` any rated kV.
|
|
18
|
+
3. `core/importer.py` — add the OpenDSS class prefix to `SUPPORTED_PREFIXES`
|
|
19
|
+
and a read-back block in `_read_model_back` (iterate `dss.<Class>.First()/
|
|
20
|
+
Next()`, preserve `busNodes` suffixes, `wire()` terminals to `busbar_for()`).
|
|
21
|
+
4. `core/validate.py` — extend the kV-consistency check if the element declares
|
|
22
|
+
a voltage; add any element-specific structural checks.
|
|
23
|
+
|
|
24
|
+
## Frontend (`frontend/src/`)
|
|
25
|
+
|
|
26
|
+
5. ⚡ `types/circuit.ts` — add to the `NodeType` union (mirror of model.py).
|
|
27
|
+
6. `lib/defaults.ts` — `defaultParams()` case (name prefix + sensible params)
|
|
28
|
+
and `NODE_SIZE` entry.
|
|
29
|
+
7. `lib/fields.tsx` — `FIELDS` entry; drives both the properties panel and the
|
|
30
|
+
spreadsheet tab.
|
|
31
|
+
8. `components/nodes/<X>Node.tsx` — symbol component: use `useSymbolRotation`,
|
|
32
|
+
`rotatedBox`, `SymbolSvg`, `rotatePosition` from `nodes/common.tsx` so
|
|
33
|
+
rotation works; add `VoltageBadge` (bus-connected) or `ElementBadge`
|
|
34
|
+
(series/shunt with element results).
|
|
35
|
+
9. `components/EditorCanvas.tsx` — register in `nodeTypes` and add a letter to
|
|
36
|
+
`PLACE_KEYS`.
|
|
37
|
+
10. `components/Palette.tsx` — palette item with icon + the same `kbd` letter.
|
|
38
|
+
11. `lib/layout.ts` — if it's a 1-terminal shunt device, add it to
|
|
39
|
+
`SHUNT_TYPES` so imports hang it under its busbar; 2-terminal series
|
|
40
|
+
devices need `orientedEdges` / `alignDevicesBetweenBuses` handling.
|
|
41
|
+
12. `store/circuitStore.ts` — add the name prefix to `NAME_PREFIX`
|
|
42
|
+
(copy/paste renaming).
|
|
43
|
+
|
|
44
|
+
## Tests
|
|
45
|
+
|
|
46
|
+
13. ⚡ `tests/fixtures/full-circuit.oneline.json` — add a wired, solvable
|
|
47
|
+
instance of the element. `test_schema_fixture.py::
|
|
48
|
+
test_fixture_covers_every_type` fails until you do; the same fixture
|
|
49
|
+
drives the frontend round-trip test and the e2e solve test.
|
|
50
|
+
14. `tests/test_schema_fixture.py` — add the expected `new <class>.<name>`
|
|
51
|
+
fragment to `test_fixture_compiles_cleanly`.
|
|
52
|
+
15. `tests/test_import_roundtrip.py` — extend the round-trip coverage.
|
|
53
|
+
|
|
54
|
+
Then: `pytest`, `npm test`, `npm run e2e`, and update `FUTURE_IMPROVEMENTS.md`.
|