@tellegen/svelte 0.4.0 → 0.4.2
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.
- package/README.md +38 -10
- package/dist/Sparkline.svelte +4 -4
- package/dist/TellegenMap.svelte +80 -66
- package/dist/TellegenProvider.svelte +29 -13
- package/dist/colors.d.ts +37 -1
- package/dist/colors.js +63 -0
- package/dist/components/AppFooter.svelte +4 -4
- package/dist/components/AppHeader.svelte +15 -19
- package/dist/components/BindingLines.svelte +10 -9
- package/dist/components/BusPicker.svelte +5 -6
- package/dist/components/ControlPanel.svelte +16 -11
- package/dist/components/ControlPanel.svelte.d.ts +6 -1
- package/dist/components/DemandSlider.svelte +26 -29
- package/dist/components/DiagramCanvas.svelte +16 -4
- package/dist/components/DisplayControls.svelte +5 -24
- package/dist/components/DropZone.svelte +7 -3
- package/dist/components/DropZone.svelte.d.ts +6 -17
- package/dist/components/FormulationSelector.svelte +2 -7
- package/dist/components/LocalCaseDetails.svelte +126 -126
- package/dist/components/LocalCaseDetails.svelte.d.ts +6 -1
- package/dist/components/MulticonductorDetails.svelte +48 -38
- package/dist/components/NetworkStats.svelte +14 -16
- package/dist/components/PanelCard.svelte +1 -1
- package/dist/components/PlacementCue.svelte +1 -1
- package/dist/components/RatingSlider.svelte +22 -23
- package/dist/components/RestoreDefaultsButton.svelte +2 -3
- package/dist/components/SensitivityReadout.svelte +9 -27
- package/dist/components/SolveCard.svelte +69 -35
- package/dist/components/TopMovers.svelte +5 -5
- package/dist/context.svelte.d.ts +6 -2
- package/dist/context.svelte.js +4 -2
- package/dist/controller.svelte.d.ts +7 -0
- package/dist/controller.svelte.js +37 -8
- package/dist/display.d.ts +3 -2
- package/dist/display.js +28 -11
- package/dist/format.d.ts +1 -9
- package/dist/format.js +1 -14
- package/dist/index.d.ts +1 -1
- package/dist/map.d.ts +1 -1
- package/dist/map.js +1 -1
- package/dist/state.svelte.d.ts +6 -1
- package/dist/state.svelte.js +1 -0
- package/dist/styles.css +3 -0
- package/dist/types.d.ts +1 -1
- package/package.json +10 -6
|
@@ -9,15 +9,10 @@
|
|
|
9
9
|
{#if ctrl.activeSolvable}
|
|
10
10
|
{@const c = ctrl.activeSolvable}
|
|
11
11
|
<div class="formulation">
|
|
12
|
-
<label
|
|
13
|
-
class="formulation-row mono"
|
|
14
|
-
for="formulation-select"
|
|
15
|
-
title={formulationHint(c.formulation)}
|
|
16
|
-
>
|
|
12
|
+
<label class="formulation-row" for="formulation-select" title={formulationHint(c.formulation)}>
|
|
17
13
|
<span>Calculation</span>
|
|
18
14
|
<select
|
|
19
15
|
id="formulation-select"
|
|
20
|
-
class="mono"
|
|
21
16
|
disabled={c.solving}
|
|
22
17
|
value={c.formulation}
|
|
23
18
|
onchange={(e) => ctrl.changeFormulation(c, e.currentTarget.value as Formulation)}
|
|
@@ -52,7 +47,7 @@
|
|
|
52
47
|
}
|
|
53
48
|
|
|
54
49
|
.formulation-row select {
|
|
55
|
-
font-family:
|
|
50
|
+
font-family: inherit;
|
|
56
51
|
font-size: 13px;
|
|
57
52
|
padding: 3px 22px 3px 8px;
|
|
58
53
|
border: 1px solid var(--line);
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
2
3
|
import { getAppState, getController } from '../context.svelte.js';
|
|
3
4
|
import { fmt } from '../format.js';
|
|
4
5
|
import type { LocalCase } from '../state.svelte.js';
|
|
5
6
|
import { formatPowerIoDiagnostic, type PowerIoDiagnostic } from '@tellegen/engine';
|
|
6
7
|
|
|
8
|
+
/** Optional host content rendered under the case stats. */
|
|
9
|
+
let { note }: { note?: Snippet<[LocalCase]> } = $props();
|
|
10
|
+
|
|
7
11
|
const app = getAppState();
|
|
8
12
|
const ctrl = getController();
|
|
9
13
|
|
|
@@ -22,6 +26,23 @@
|
|
|
22
26
|
let exportOpen = $state(false);
|
|
23
27
|
let exportDiagnostics = $state<PowerIoDiagnostic[]>([]);
|
|
24
28
|
|
|
29
|
+
/** Where the map coordinates came from. */
|
|
30
|
+
function coordinateSource(lc: LocalCase): string {
|
|
31
|
+
if (!lc.view) return 'None';
|
|
32
|
+
switch (lc.coordsKind) {
|
|
33
|
+
case 'file':
|
|
34
|
+
return 'From file';
|
|
35
|
+
case 'geofile':
|
|
36
|
+
return lc.geoSource ? `From ${lc.geoSource}` : 'From geographic file';
|
|
37
|
+
case 'manual':
|
|
38
|
+
return 'Manual layout';
|
|
39
|
+
case 'synthetic':
|
|
40
|
+
return lc.summary?.coords_method === 'generated' ? 'Generated' : 'Synthetic layout';
|
|
41
|
+
default:
|
|
42
|
+
return 'None';
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
25
46
|
async function saveCase(lc: LocalCase) {
|
|
26
47
|
busy = true;
|
|
27
48
|
exportDiagnostics = [];
|
|
@@ -64,137 +85,128 @@
|
|
|
64
85
|
|
|
65
86
|
{#if app.activeLocal}
|
|
66
87
|
{@const lc = app.activeLocal}
|
|
67
|
-
<h2>{lc.label}
|
|
88
|
+
<h2>{lc.label}</h2>
|
|
68
89
|
{#if lc.substations}
|
|
69
|
-
<dl
|
|
90
|
+
<dl>
|
|
91
|
+
<div>
|
|
92
|
+
<dt>Substations</dt>
|
|
93
|
+
<dd class="mono">{lc.substations.points.length}</dd>
|
|
94
|
+
</div>
|
|
95
|
+
<div>
|
|
96
|
+
<dt>Positions</dt>
|
|
97
|
+
<dd>Inferred from diagram</dd>
|
|
98
|
+
</div>
|
|
70
99
|
<div>
|
|
71
|
-
<dt>
|
|
72
|
-
<dd>{lc.
|
|
100
|
+
<dt>File</dt>
|
|
101
|
+
<dd class="mono">{lc.fileName}</dd>
|
|
73
102
|
</div>
|
|
74
103
|
</dl>
|
|
75
|
-
<p class="footnote mono">
|
|
76
|
-
display only: positions inferred from the PowerWorld diagram, not surveyed latitude and
|
|
77
|
-
longitude
|
|
78
|
-
</p>
|
|
79
|
-
<p class="footnote mono">decoded in your browser by powerio (wasm); never uploaded</p>
|
|
80
104
|
{:else if lc.summary}
|
|
81
|
-
<dl
|
|
105
|
+
<dl>
|
|
82
106
|
<div>
|
|
83
|
-
<dt>
|
|
84
|
-
<dd>{lc.summary.n_bus}</dd>
|
|
107
|
+
<dt>Buses</dt>
|
|
108
|
+
<dd class="mono">{lc.summary.n_bus}</dd>
|
|
85
109
|
</div>
|
|
86
110
|
<div>
|
|
87
|
-
<dt>
|
|
88
|
-
<dd>{lc.summary.n_branch}</dd>
|
|
111
|
+
<dt>Branches</dt>
|
|
112
|
+
<dd class="mono">{lc.summary.n_branch}</dd>
|
|
89
113
|
</div>
|
|
90
114
|
<div>
|
|
91
|
-
<dt>
|
|
92
|
-
<dd>{lc.summary.n_gen}</dd>
|
|
115
|
+
<dt>Generators</dt>
|
|
116
|
+
<dd class="mono">{lc.summary.n_gen}</dd>
|
|
93
117
|
</div>
|
|
94
118
|
<div>
|
|
95
|
-
<dt>
|
|
96
|
-
<dd>{fmt.format(lc.summary.load_mw)} MW</dd>
|
|
119
|
+
<dt>Load</dt>
|
|
120
|
+
<dd class="mono">{fmt.format(lc.summary.load_mw)} MW</dd>
|
|
97
121
|
</div>
|
|
98
122
|
<div>
|
|
99
|
-
<dt>
|
|
100
|
-
<dd>{fmt.format(lc.summary.gen_mw)} MW</dd>
|
|
123
|
+
<dt>Generation capacity</dt>
|
|
124
|
+
<dd class="mono">{fmt.format(lc.summary.gen_mw)} MW</dd>
|
|
101
125
|
</div>
|
|
102
126
|
<div>
|
|
103
|
-
<dt>
|
|
104
|
-
<dd>{fmt.format(lc.summary.base_mva)}</dd>
|
|
127
|
+
<dt>Base MVA</dt>
|
|
128
|
+
<dd class="mono">{fmt.format(lc.summary.base_mva)}</dd>
|
|
105
129
|
</div>
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
<details>
|
|
111
|
-
<summary>Drawing details</summary>
|
|
112
|
-
<ul class="warnings mono">
|
|
113
|
-
{#each lc.diagram.warnings as warning, i (i)}<li>{warning}</li>{/each}
|
|
114
|
-
</ul>
|
|
115
|
-
</details>
|
|
116
|
-
{/if}
|
|
117
|
-
{:else if !lc.view}
|
|
118
|
-
<p class="footnote mono">
|
|
119
|
-
no coordinates in this file: click the map or drop a geographic file
|
|
120
|
-
</p>
|
|
121
|
-
{:else if lc.coordsKind === 'synthetic' && lc.summary.coords_method === 'generated'}
|
|
122
|
-
<p class="footnote mono">coordinates: synthetic grid sited on real geography</p>
|
|
123
|
-
{:else if lc.coordsKind === 'synthetic' || lc.coordsKind === 'manual'}
|
|
124
|
-
<p class="footnote mono">
|
|
125
|
-
coordinates: {lc.coordsKind} topology layout centered where it was placed
|
|
126
|
-
</p>
|
|
127
|
-
{:else if lc.coordsKind === 'geofile'}
|
|
128
|
-
<p class="footnote mono">
|
|
129
|
-
coordinates: geographic file data from {lc.geoSource}
|
|
130
|
-
</p>
|
|
131
|
-
{/if}
|
|
132
|
-
<p class="footnote mono">parsed in your browser by powerio (wasm); never uploaded</p>
|
|
133
|
-
{#if lc.studyInputJson}
|
|
134
|
-
<div class="case-actions">
|
|
135
|
-
<button class="reset mono" disabled={busy} onclick={() => saveCase(lc)}>
|
|
136
|
-
save PowerIO module (.json)
|
|
137
|
-
</button>
|
|
138
|
-
{#if lc.formulation === 'dcopf' && lc.solution}
|
|
139
|
-
<button class="reset mono" disabled={busy} onclick={() => saveSolution(lc)}>
|
|
140
|
-
save exact solution (.json)
|
|
141
|
-
</button>
|
|
142
|
-
{/if}
|
|
143
|
-
<div class="export">
|
|
144
|
-
<button
|
|
145
|
-
class="reset mono"
|
|
146
|
-
disabled={busy}
|
|
147
|
-
aria-expanded={exportOpen}
|
|
148
|
-
onclick={() => (exportOpen = !exportOpen)}
|
|
149
|
-
>
|
|
150
|
-
export committed state…
|
|
151
|
-
</button>
|
|
152
|
-
{#if exportOpen}
|
|
153
|
-
<ul class="export-menu mono">
|
|
154
|
-
{#each EXPORT_FORMATS as f (f.token)}
|
|
155
|
-
<li>
|
|
156
|
-
<button
|
|
157
|
-
class="reset mono"
|
|
158
|
-
disabled={busy}
|
|
159
|
-
onclick={() => exportCase(lc, f.token)}
|
|
160
|
-
>
|
|
161
|
-
{f.label}
|
|
162
|
-
</button>
|
|
163
|
-
</li>
|
|
164
|
-
{/each}
|
|
165
|
-
</ul>
|
|
166
|
-
{/if}
|
|
130
|
+
{#if lc.displayMode === 'diagram' && lc.diagram}
|
|
131
|
+
<div>
|
|
132
|
+
<dt>Drawing</dt>
|
|
133
|
+
<dd>{lc.diagram.name}</dd>
|
|
167
134
|
</div>
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
135
|
+
{:else}
|
|
136
|
+
<div>
|
|
137
|
+
<dt>Coordinates</dt>
|
|
138
|
+
<dd>{coordinateSource(lc)}</dd>
|
|
139
|
+
</div>
|
|
140
|
+
{/if}
|
|
141
|
+
<div>
|
|
142
|
+
<dt>File</dt>
|
|
143
|
+
<dd class="mono">{lc.fileName}</dd>
|
|
175
144
|
</div>
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
{#
|
|
182
|
-
<li>+{exportDiagnostics.length - 4} more</li>
|
|
183
|
-
{/if}
|
|
145
|
+
</dl>
|
|
146
|
+
{#if lc.displayMode === 'diagram' && lc.diagram?.warnings.length}
|
|
147
|
+
<details>
|
|
148
|
+
<summary>Drawing details</summary>
|
|
149
|
+
<ul class="warnings">
|
|
150
|
+
{#each lc.diagram.warnings as warning, i (i)}<li>{warning}</li>{/each}
|
|
184
151
|
</ul>
|
|
152
|
+
</details>
|
|
153
|
+
{/if}
|
|
154
|
+
{/if}
|
|
155
|
+
{@render note?.(lc)}
|
|
156
|
+
{#if lc.summary && lc.studyInputJson}
|
|
157
|
+
<div class="case-actions">
|
|
158
|
+
<button class="reset" disabled={busy} onclick={() => saveCase(lc)}>Save case</button>
|
|
159
|
+
{#if lc.formulation === 'dcopf' && lc.solution}
|
|
160
|
+
<button class="reset" disabled={busy} onclick={() => saveSolution(lc)}>Save solution</button
|
|
161
|
+
>
|
|
162
|
+
{/if}
|
|
163
|
+
<button
|
|
164
|
+
class="reset"
|
|
165
|
+
disabled={busy}
|
|
166
|
+
aria-expanded={exportOpen}
|
|
167
|
+
aria-controls="case-export-formats"
|
|
168
|
+
onclick={() => (exportOpen = !exportOpen)}
|
|
169
|
+
>
|
|
170
|
+
Export as
|
|
171
|
+
</button>
|
|
172
|
+
{#if lc.view || lc.diagram}
|
|
173
|
+
<button class="reset" disabled={busy} onclick={() => downloadLayout(lc)}>
|
|
174
|
+
{lc.displayMode === 'diagram' ? 'Save drawing' : 'Save geography'}
|
|
175
|
+
</button>
|
|
185
176
|
{/if}
|
|
177
|
+
</div>
|
|
178
|
+
{#if exportOpen}
|
|
179
|
+
<ul class="case-actions" id="case-export-formats" aria-label="Export formats">
|
|
180
|
+
{#each EXPORT_FORMATS as f (f.token)}
|
|
181
|
+
<li>
|
|
182
|
+
<button class="reset" disabled={busy} onclick={() => exportCase(lc, f.token)}>
|
|
183
|
+
{f.label}
|
|
184
|
+
</button>
|
|
185
|
+
</li>
|
|
186
|
+
{/each}
|
|
187
|
+
</ul>
|
|
188
|
+
{/if}
|
|
189
|
+
{#if exportDiagnostics.length > 0}
|
|
190
|
+
<ul class="warnings">
|
|
191
|
+
{#each exportDiagnostics.slice(0, 4) as diagnostic, i (i)}
|
|
192
|
+
<li>{formatPowerIoDiagnostic(diagnostic)}</li>
|
|
193
|
+
{/each}
|
|
194
|
+
{#if exportDiagnostics.length > 4}
|
|
195
|
+
<li>+{exportDiagnostics.length - 4} more</li>
|
|
196
|
+
{/if}
|
|
197
|
+
</ul>
|
|
186
198
|
{/if}
|
|
187
199
|
{/if}
|
|
188
200
|
{#if lc.displayMode !== 'diagram' && lc.topology && lc.coordsKind !== 'file'}
|
|
189
|
-
<button class="reset
|
|
201
|
+
<button class="reset" onclick={() => ctrl.moveLocalCase(lc)}>
|
|
190
202
|
{lc.coordsKind === 'synthetic_pending'
|
|
191
|
-
? '
|
|
203
|
+
? 'Place on map'
|
|
192
204
|
: lc.coordsKind === 'geofile'
|
|
193
|
-
? '
|
|
194
|
-
: '
|
|
205
|
+
? 'Place manually'
|
|
206
|
+
: 'Move layout'}
|
|
195
207
|
</button>
|
|
196
208
|
{/if}
|
|
197
|
-
<button class="reset
|
|
209
|
+
<button class="reset" onclick={() => ctrl.removeLocalCase(lc)}>Remove</button>
|
|
198
210
|
{/if}
|
|
199
211
|
|
|
200
212
|
<style>
|
|
@@ -212,22 +224,25 @@
|
|
|
212
224
|
dl div {
|
|
213
225
|
display: flex;
|
|
214
226
|
justify-content: space-between;
|
|
227
|
+
gap: 12px;
|
|
215
228
|
padding: 3px 0;
|
|
216
229
|
}
|
|
217
230
|
|
|
218
231
|
dt {
|
|
232
|
+
flex: none;
|
|
219
233
|
color: var(--text-secondary);
|
|
220
234
|
}
|
|
221
235
|
|
|
222
236
|
dd {
|
|
237
|
+
min-width: 0;
|
|
223
238
|
margin: 0;
|
|
239
|
+
text-align: right;
|
|
240
|
+
overflow-wrap: anywhere;
|
|
224
241
|
}
|
|
225
242
|
|
|
226
|
-
|
|
227
|
-
margin: 8px
|
|
243
|
+
details {
|
|
244
|
+
margin-top: 8px;
|
|
228
245
|
font-size: 12px;
|
|
229
|
-
color: var(--text-tertiary);
|
|
230
|
-
letter-spacing: 0;
|
|
231
246
|
}
|
|
232
247
|
|
|
233
248
|
.warnings {
|
|
@@ -243,27 +258,12 @@
|
|
|
243
258
|
display: flex;
|
|
244
259
|
flex-wrap: wrap;
|
|
245
260
|
gap: 8px;
|
|
246
|
-
margin
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
.export {
|
|
250
|
-
position: relative;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
.export-menu {
|
|
254
|
-
position: absolute;
|
|
255
|
-
z-index: 1;
|
|
256
|
-
margin: 4px 0 0;
|
|
257
|
-
padding: 4px;
|
|
261
|
+
margin: 10px 0 0;
|
|
262
|
+
padding: 0;
|
|
258
263
|
list-style: none;
|
|
259
|
-
min-width: 100%;
|
|
260
|
-
background: var(--surface, var(--surface-menu));
|
|
261
|
-
border: 1px solid var(--border, rgba(0, 0, 0, 0.15));
|
|
262
|
-
border-radius: 6px;
|
|
263
|
-
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);
|
|
264
264
|
}
|
|
265
265
|
|
|
266
|
-
.
|
|
267
|
-
|
|
266
|
+
.case-actions .reset {
|
|
267
|
+
margin-top: 0;
|
|
268
268
|
}
|
|
269
269
|
</style>
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { LocalCase } from '../state.svelte.js';
|
|
3
|
+
type $$ComponentProps = {
|
|
4
|
+
note?: Snippet<[LocalCase]>;
|
|
5
|
+
};
|
|
6
|
+
declare const LocalCaseDetails: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
2
7
|
type LocalCaseDetails = ReturnType<typeof LocalCaseDetails>;
|
|
3
8
|
export default LocalCaseDetails;
|
|
@@ -46,51 +46,60 @@
|
|
|
46
46
|
{#if app.activeMulti}
|
|
47
47
|
{@const mc = app.activeMulti}
|
|
48
48
|
{@const s = mc.summary}
|
|
49
|
-
<h2>{mc.label}
|
|
49
|
+
<h2>{mc.label}</h2>
|
|
50
50
|
{#if s}
|
|
51
|
-
<p class="tag
|
|
51
|
+
<p class="tag">Multiconductor</p>
|
|
52
52
|
<details class="case-details">
|
|
53
53
|
<summary>{s.n_bus} buses, {s.n_edge} {s.n_edge === 1 ? 'branch' : 'branches'}</summary>
|
|
54
|
-
<dl
|
|
54
|
+
<dl>
|
|
55
55
|
<div>
|
|
56
|
-
<dt>
|
|
57
|
-
<dd>{s.n_bus}</dd>
|
|
56
|
+
<dt>Buses</dt>
|
|
57
|
+
<dd class="mono">{s.n_bus}</dd>
|
|
58
58
|
</div>
|
|
59
59
|
<div>
|
|
60
60
|
<dt>Lines / switches / transformers</dt>
|
|
61
|
-
<dd>{s.n_line} / {s.n_switch} / {s.n_transformer}</dd>
|
|
61
|
+
<dd class="mono">{s.n_line} / {s.n_switch} / {s.n_transformer}</dd>
|
|
62
62
|
</div>
|
|
63
63
|
<div>
|
|
64
|
-
<dt>
|
|
65
|
-
<dd>{fmt.format(s.load_kw)} kW</dd>
|
|
64
|
+
<dt>Load</dt>
|
|
65
|
+
<dd class="mono">{fmt.format(s.load_kw)} kW</dd>
|
|
66
66
|
</div>
|
|
67
67
|
<div>
|
|
68
68
|
<dt>Generation capacity</dt>
|
|
69
|
-
<dd>{fmt.format(s.gen_kw)} kW</dd>
|
|
69
|
+
<dd class="mono">{fmt.format(s.gen_kw)} kW</dd>
|
|
70
70
|
</div>
|
|
71
71
|
<div>
|
|
72
|
-
<dt>
|
|
73
|
-
<dd>{s.n_source} / {s.n_load} / {s.n_generator}</dd>
|
|
72
|
+
<dt>Sources / loads / generators</dt>
|
|
73
|
+
<dd class="mono">{s.n_source} / {s.n_load} / {s.n_generator}</dd>
|
|
74
74
|
</div>
|
|
75
75
|
{#if s.n_ibr > 0 || s.n_shunt > 0 || (s.n_capacitor ?? 0) > 0}
|
|
76
76
|
<div>
|
|
77
|
-
<dt>IBRs / shunts /
|
|
78
|
-
<dd>{s.n_ibr} / {s.n_shunt} / {s.n_capacitor ?? 0}</dd>
|
|
77
|
+
<dt>IBRs / shunts / capacitors</dt>
|
|
78
|
+
<dd class="mono">{s.n_ibr} / {s.n_shunt} / {s.n_capacitor ?? 0}</dd>
|
|
79
79
|
</div>
|
|
80
80
|
{/if}
|
|
81
|
+
<div>
|
|
82
|
+
<dt>File</dt>
|
|
83
|
+
<dd class="mono">{mc.fileName}</dd>
|
|
84
|
+
</div>
|
|
81
85
|
</dl>
|
|
82
86
|
</details>
|
|
83
87
|
|
|
84
|
-
<
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
<dl>
|
|
89
|
+
<div>
|
|
90
|
+
<dt>Coordinates</dt>
|
|
91
|
+
<dd>
|
|
92
|
+
{mc.coordsKind === 'geographic'
|
|
93
|
+
? 'Geographic'
|
|
94
|
+
: mc.coordsKind === 'planar'
|
|
95
|
+
? 'Drawing'
|
|
96
|
+
: 'Generated diagram'}
|
|
97
|
+
</dd>
|
|
98
|
+
</div>
|
|
99
|
+
</dl>
|
|
91
100
|
<div class="calculation-actions">
|
|
92
101
|
{#if mc.solving}
|
|
93
|
-
<span role="status">Calculating
|
|
102
|
+
<span role="status">Calculating…</span>{#if mc.solveAbort}<button
|
|
94
103
|
onclick={() => mc.solveAbort?.abort()}>Cancel</button
|
|
95
104
|
>{/if}
|
|
96
105
|
{:else}
|
|
@@ -110,27 +119,27 @@
|
|
|
110
119
|
notices.push({
|
|
111
120
|
kind: 'warning',
|
|
112
121
|
title: 'AC power flow unavailable',
|
|
113
|
-
details: mc.mcPfReason ?? '
|
|
122
|
+
details: mc.mcPfReason ?? 'AC power flow does not support this case'
|
|
114
123
|
})}>Why unavailable</button
|
|
115
124
|
>
|
|
116
125
|
{/if}
|
|
117
126
|
{/if}
|
|
118
127
|
</div>
|
|
119
128
|
{#if mc.result}<p class="footnote">Converged, {mc.result.iterations} iterations</p>
|
|
120
|
-
<button class="reset
|
|
129
|
+
<button class="reset" onclick={openResults}>View results in Studies</button>{/if}
|
|
121
130
|
{/if}
|
|
122
131
|
|
|
123
132
|
{#if mc.selectedBus}
|
|
124
133
|
{@const b = mc.selectedBus}
|
|
125
134
|
<hr />
|
|
126
|
-
<h3 class="mono">
|
|
135
|
+
<h3>Bus <span class="mono">{b.id}</span></h3>
|
|
127
136
|
<div class="terminals">
|
|
128
137
|
{#each b.terminals as t (t)}
|
|
129
138
|
<span
|
|
130
139
|
class="term"
|
|
131
140
|
class:grounded={b.grounded.includes(t)}
|
|
132
141
|
style={`--tc:${terminalColor(t)}`}
|
|
133
|
-
title={b.grounded.includes(t) ? `
|
|
142
|
+
title={b.grounded.includes(t) ? `Terminal ${t}, grounded` : `Terminal ${t}`}
|
|
134
143
|
>
|
|
135
144
|
{t}{#if b.grounded.includes(t)}<span class="gnd" aria-hidden="true">⏚</span>{/if}
|
|
136
145
|
</span>
|
|
@@ -145,7 +154,7 @@
|
|
|
145
154
|
{/each}
|
|
146
155
|
</div>
|
|
147
156
|
{:else}
|
|
148
|
-
<p class="footnote
|
|
157
|
+
<p class="footnote">No attachments</p>
|
|
149
158
|
{/if}
|
|
150
159
|
{@const loadBranches = mc.mcLoadBranches.filter((load) => load.bus === b.id)}
|
|
151
160
|
{#if loadBranches.length > 0}
|
|
@@ -154,7 +163,7 @@
|
|
|
154
163
|
{#each loadBranches as load (`${load.load}:${load.branch}`)}
|
|
155
164
|
<div class="load-row">
|
|
156
165
|
<span class="mono"
|
|
157
|
-
>{load.load}{loadBranches.length > 1 ? `
|
|
166
|
+
>{load.load}{loadBranches.length > 1 ? ` (${load.branch + 1})` : ''}</span
|
|
158
167
|
>
|
|
159
168
|
<label
|
|
160
169
|
>P kW<input
|
|
@@ -200,7 +209,7 @@
|
|
|
200
209
|
>
|
|
201
210
|
</div>
|
|
202
211
|
{/each}
|
|
203
|
-
<p class="footnote">Changes
|
|
212
|
+
<p class="footnote">Changes solve automatically.</p>
|
|
204
213
|
</div>
|
|
205
214
|
{/if}
|
|
206
215
|
{:else if mc.selectedEdge}
|
|
@@ -211,15 +220,19 @@
|
|
|
211
220
|
{e.kind}
|
|
212
221
|
{e.from} – {e.to}
|
|
213
222
|
</h3>
|
|
214
|
-
<dl
|
|
223
|
+
<dl>
|
|
224
|
+
<div>
|
|
225
|
+
<dt>Phases</dt>
|
|
226
|
+
<dd class="mono">{e.n_phases}</dd>
|
|
227
|
+
</div>
|
|
215
228
|
<div>
|
|
216
|
-
<dt>
|
|
217
|
-
<dd>{e.
|
|
229
|
+
<dt>Conductors</dt>
|
|
230
|
+
<dd class="mono">{e.conductors.length}</dd>
|
|
218
231
|
</div>
|
|
219
232
|
{#if e.kind === 'switch'}
|
|
220
233
|
<div>
|
|
221
|
-
<dt>
|
|
222
|
-
<dd>{e.closed ? '
|
|
234
|
+
<dt>State</dt>
|
|
235
|
+
<dd>{e.closed ? 'Closed' : 'Open'}</dd>
|
|
223
236
|
</div>
|
|
224
237
|
{/if}
|
|
225
238
|
</dl>
|
|
@@ -232,8 +245,6 @@
|
|
|
232
245
|
</span>
|
|
233
246
|
{/each}
|
|
234
247
|
</div>
|
|
235
|
-
{:else if mc.placed}
|
|
236
|
-
<p class="footnote mono">select a bus or a line to expand its detail</p>
|
|
237
248
|
{/if}
|
|
238
249
|
|
|
239
250
|
<div class="mc-legend mono">
|
|
@@ -251,8 +262,8 @@
|
|
|
251
262
|
</span>
|
|
252
263
|
</div>
|
|
253
264
|
|
|
254
|
-
<button class="reset
|
|
255
|
-
<button class="reset
|
|
265
|
+
<button class="reset" onclick={() => app.requestFrame(mc.id)}>Fit case</button>
|
|
266
|
+
<button class="reset" onclick={() => ctrl.removeMultiCase(mc)}>Remove</button>
|
|
256
267
|
{/if}
|
|
257
268
|
|
|
258
269
|
<style>
|
|
@@ -309,7 +320,6 @@
|
|
|
309
320
|
font-size: 12px;
|
|
310
321
|
color: var(--text-secondary);
|
|
311
322
|
letter-spacing: 0;
|
|
312
|
-
text-transform: uppercase;
|
|
313
323
|
}
|
|
314
324
|
|
|
315
325
|
dl {
|
|
@@ -11,34 +11,32 @@
|
|
|
11
11
|
{@const [cname, cregion] = splitName(app.active?.name ?? '')}
|
|
12
12
|
{@const deltaObjective = stats.deltaObjective}
|
|
13
13
|
<h2>{cname} <span class="region mono">{cregion}</span></h2>
|
|
14
|
-
<dl
|
|
14
|
+
<dl>
|
|
15
15
|
<div>
|
|
16
|
-
<dt>
|
|
17
|
-
<dd>{stats.buses}</dd>
|
|
16
|
+
<dt>Buses</dt>
|
|
17
|
+
<dd class="mono">{stats.buses}</dd>
|
|
18
18
|
</div>
|
|
19
19
|
<div>
|
|
20
|
-
<dt>
|
|
21
|
-
<dd>{stats.branches}</dd>
|
|
20
|
+
<dt>Lines</dt>
|
|
21
|
+
<dd class="mono">{stats.branches}</dd>
|
|
22
22
|
</div>
|
|
23
23
|
{#if ctrl.activeFormulation !== 'acpf'}
|
|
24
24
|
<div>
|
|
25
|
-
<dt>
|
|
26
|
-
<dd>{stats.binding ?? '
|
|
25
|
+
<dt>Binding lines</dt>
|
|
26
|
+
<dd class="mono">{stats.binding ?? '…'}</dd>
|
|
27
27
|
</div>
|
|
28
28
|
<div>
|
|
29
29
|
<dt>OPF objective</dt>
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
{
|
|
34
|
-
|
|
35
|
-
{/if}
|
|
36
|
-
</dd>
|
|
30
|
+
{#if stats.objective === null}
|
|
31
|
+
<dd class="blink">Solving…</dd>
|
|
32
|
+
{:else}
|
|
33
|
+
<dd class="mono">{fmt.format(stats.objective)}</dd>
|
|
34
|
+
{/if}
|
|
37
35
|
</div>
|
|
38
36
|
{#if ctrl.isPerturbed(ctrl.activeSolvable) && deltaObjective !== null}
|
|
39
37
|
<div class="delta">
|
|
40
|
-
<dt>
|
|
41
|
-
<dd>{signed(deltaObjective)}</dd>
|
|
38
|
+
<dt>Change from base</dt>
|
|
39
|
+
<dd class="mono">{signed(deltaObjective)}</dd>
|
|
42
40
|
</div>
|
|
43
41
|
{/if}
|
|
44
42
|
{/if}
|
|
@@ -158,7 +158,7 @@
|
|
|
158
158
|
<button
|
|
159
159
|
class="title"
|
|
160
160
|
aria-label={`Move ${panel.title} panel`}
|
|
161
|
-
title="
|
|
161
|
+
title="Drag or use arrow keys to move"
|
|
162
162
|
onpointerdown={(event) => start(event, 'move')}
|
|
163
163
|
onkeydown={key}
|
|
164
164
|
onclick={() => layout.activate(panel)}>{panel.title}</button
|