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.
Files changed (93) hide show
  1. opendss_designer-0.1.0/.github/workflows/ci.yml +64 -0
  2. opendss_designer-0.1.0/.github/workflows/docs.yml +42 -0
  3. opendss_designer-0.1.0/.github/workflows/publish.yml +51 -0
  4. opendss_designer-0.1.0/.gitignore +20 -0
  5. opendss_designer-0.1.0/FUTURE_IMPROVEMENTS.md +109 -0
  6. opendss_designer-0.1.0/LICENSE +21 -0
  7. opendss_designer-0.1.0/PKG-INFO +126 -0
  8. opendss_designer-0.1.0/README.md +98 -0
  9. opendss_designer-0.1.0/config/linecodes.csv +26 -0
  10. opendss_designer-0.1.0/docs/adding-an-element.md +54 -0
  11. opendss_designer-0.1.0/docs/development.md +68 -0
  12. opendss_designer-0.1.0/docs/features.md +75 -0
  13. opendss_designer-0.1.0/docs/getting-started.md +75 -0
  14. opendss_designer-0.1.0/docs/importing-dss.md +38 -0
  15. opendss_designer-0.1.0/docs/index.md +53 -0
  16. opendss_designer-0.1.0/docs/screenshot.png +0 -0
  17. opendss_designer-0.1.0/examples/demo-substation.oneline.json +28 -0
  18. opendss_designer-0.1.0/frontend/e2e/smoke.spec.ts +141 -0
  19. opendss_designer-0.1.0/frontend/index.html +12 -0
  20. opendss_designer-0.1.0/frontend/package-lock.json +2594 -0
  21. opendss_designer-0.1.0/frontend/package.json +31 -0
  22. opendss_designer-0.1.0/frontend/playwright.config.ts +29 -0
  23. opendss_designer-0.1.0/frontend/playwright.static.config.ts +10 -0
  24. opendss_designer-0.1.0/frontend/src/App.tsx +124 -0
  25. opendss_designer-0.1.0/frontend/src/components/BottomPanel.tsx +420 -0
  26. opendss_designer-0.1.0/frontend/src/components/ContextMenu.tsx +93 -0
  27. opendss_designer-0.1.0/frontend/src/components/EditorCanvas.tsx +309 -0
  28. opendss_designer-0.1.0/frontend/src/components/GraphPanel.tsx +390 -0
  29. opendss_designer-0.1.0/frontend/src/components/LoadingPie.tsx +25 -0
  30. opendss_designer-0.1.0/frontend/src/components/Palette.tsx +146 -0
  31. opendss_designer-0.1.0/frontend/src/components/PropertiesPanel.tsx +147 -0
  32. opendss_designer-0.1.0/frontend/src/components/ResultTooltip.tsx +127 -0
  33. opendss_designer-0.1.0/frontend/src/components/Toolbar.tsx +227 -0
  34. opendss_designer-0.1.0/frontend/src/components/edges/LineEdge.tsx +62 -0
  35. opendss_designer-0.1.0/frontend/src/components/edges/WireEdge.tsx +19 -0
  36. opendss_designer-0.1.0/frontend/src/components/edges/waypoints.tsx +80 -0
  37. opendss_designer-0.1.0/frontend/src/components/nodes/BreakerNode.tsx +45 -0
  38. opendss_designer-0.1.0/frontend/src/components/nodes/BusbarNode.tsx +67 -0
  39. opendss_designer-0.1.0/frontend/src/components/nodes/CapacitorNode.tsx +41 -0
  40. opendss_designer-0.1.0/frontend/src/components/nodes/GeneratorNode.tsx +37 -0
  41. opendss_designer-0.1.0/frontend/src/components/nodes/LoadNode.tsx +35 -0
  42. opendss_designer-0.1.0/frontend/src/components/nodes/TransformerNode.tsx +38 -0
  43. opendss_designer-0.1.0/frontend/src/components/nodes/VsourceNode.tsx +33 -0
  44. opendss_designer-0.1.0/frontend/src/components/nodes/common.tsx +135 -0
  45. opendss_designer-0.1.0/frontend/src/index.css +507 -0
  46. opendss_designer-0.1.0/frontend/src/lib/api.ts +43 -0
  47. opendss_designer-0.1.0/frontend/src/lib/colorScale.ts +22 -0
  48. opendss_designer-0.1.0/frontend/src/lib/defaults.ts +49 -0
  49. opendss_designer-0.1.0/frontend/src/lib/fields.test.ts +42 -0
  50. opendss_designer-0.1.0/frontend/src/lib/fields.tsx +158 -0
  51. opendss_designer-0.1.0/frontend/src/lib/graph.test.ts +104 -0
  52. opendss_designer-0.1.0/frontend/src/lib/graph.ts +155 -0
  53. opendss_designer-0.1.0/frontend/src/lib/layout.test.ts +114 -0
  54. opendss_designer-0.1.0/frontend/src/lib/layout.ts +173 -0
  55. opendss_designer-0.1.0/frontend/src/lib/lineCodes.test.ts +33 -0
  56. opendss_designer-0.1.0/frontend/src/lib/lineCodes.ts +66 -0
  57. opendss_designer-0.1.0/frontend/src/lib/solve.ts +38 -0
  58. opendss_designer-0.1.0/frontend/src/main.tsx +28 -0
  59. opendss_designer-0.1.0/frontend/src/store/circuitStore.test.ts +313 -0
  60. opendss_designer-0.1.0/frontend/src/store/circuitStore.ts +519 -0
  61. opendss_designer-0.1.0/frontend/src/store/resultsStore.ts +72 -0
  62. opendss_designer-0.1.0/frontend/src/types/circuit.ts +108 -0
  63. opendss_designer-0.1.0/frontend/tsconfig.json +21 -0
  64. opendss_designer-0.1.0/frontend/vite.config.ts +24 -0
  65. opendss_designer-0.1.0/mkdocs.yml +49 -0
  66. opendss_designer-0.1.0/pyproject.toml +56 -0
  67. opendss_designer-0.1.0/scripts/build_frontend.py +29 -0
  68. opendss_designer-0.1.0/src/opendss_designer/__init__.py +3 -0
  69. opendss_designer-0.1.0/src/opendss_designer/api/__init__.py +0 -0
  70. opendss_designer-0.1.0/src/opendss_designer/api/routes.py +60 -0
  71. opendss_designer-0.1.0/src/opendss_designer/cli.py +55 -0
  72. opendss_designer-0.1.0/src/opendss_designer/core/__init__.py +0 -0
  73. opendss_designer-0.1.0/src/opendss_designer/core/compiler.py +263 -0
  74. opendss_designer-0.1.0/src/opendss_designer/core/connectivity.py +191 -0
  75. opendss_designer-0.1.0/src/opendss_designer/core/engine.py +272 -0
  76. opendss_designer-0.1.0/src/opendss_designer/core/importer.py +345 -0
  77. opendss_designer-0.1.0/src/opendss_designer/core/linecodes.py +80 -0
  78. opendss_designer-0.1.0/src/opendss_designer/core/model.py +73 -0
  79. opendss_designer-0.1.0/src/opendss_designer/core/validate.py +110 -0
  80. opendss_designer-0.1.0/src/opendss_designer/server.py +32 -0
  81. opendss_designer-0.1.0/src/opendss_designer/static/assets/index-D0kQVP4J.js +73 -0
  82. opendss_designer-0.1.0/src/opendss_designer/static/assets/index-D37i8SmI.css +1 -0
  83. opendss_designer-0.1.0/src/opendss_designer/static/index.html +13 -0
  84. opendss_designer-0.1.0/tests/conftest.py +46 -0
  85. opendss_designer-0.1.0/tests/fixtures/full-circuit.oneline.json +172 -0
  86. opendss_designer-0.1.0/tests/test_analysis.py +61 -0
  87. opendss_designer-0.1.0/tests/test_compiler.py +53 -0
  88. opendss_designer-0.1.0/tests/test_connectivity.py +120 -0
  89. opendss_designer-0.1.0/tests/test_import_roundtrip.py +107 -0
  90. opendss_designer-0.1.0/tests/test_linecodes.py +62 -0
  91. opendss_designer-0.1.0/tests/test_schema_fixture.py +74 -0
  92. opendss_designer-0.1.0/tests/test_solve_smoke.py +43 -0
  93. 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
+ ![screenshot](docs/screenshot.png)
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
+ ![screenshot](docs/screenshot.png)
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`.