@tellegen/svelte 0.4.1 → 0.4.3
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 +41 -46
- package/dist/TellegenProvider.svelte +29 -13
- 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 +12 -9
- package/dist/components/ControlPanel.svelte.d.ts +6 -1
- package/dist/components/DemandSlider.svelte +24 -29
- 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 +33 -6
- package/dist/display.js +8 -9
- package/dist/format.d.ts +1 -9
- package/dist/format.js +0 -13
- package/dist/styles.css +3 -0
- package/package.json +10 -6
|
@@ -83,6 +83,7 @@ export declare class Controller {
|
|
|
83
83
|
localSeq: number;
|
|
84
84
|
loading: Promise<void> | null;
|
|
85
85
|
private readonly dropBatchGate;
|
|
86
|
+
disposed: boolean;
|
|
86
87
|
nearbyRangeAnchor: DemandRangeAnchor | null;
|
|
87
88
|
hiddenDefaults: Set<string>;
|
|
88
89
|
casesLoaded: boolean;
|
|
@@ -129,6 +130,12 @@ export declare class Controller {
|
|
|
129
130
|
maybeStartLocalSolve(id: string): void;
|
|
130
131
|
getStudy(c: SolvableCase, studyInputJson: string): Promise<BrowserStudy | null>;
|
|
131
132
|
browserSensitivity(c: SolvableCase, studyInputJson: string, target: SensTarget): Promise<SensitivityColumn | null>;
|
|
133
|
+
/** Release every engine handle this controller holds: each case's browser
|
|
134
|
+
* Study, including one still being built, and each multiconductor
|
|
135
|
+
* session, with their pending edits and in-flight requests.
|
|
136
|
+
* `TellegenProvider` calls it when it unmounts; the controller builds no
|
|
137
|
+
* Study and keeps no new session afterwards. */
|
|
138
|
+
dispose(): void;
|
|
132
139
|
disposeStudy(c: SolvableCase): void;
|
|
133
140
|
acceptSensitivity(c: SolvableCase, col: SensitivityColumn | null, target: SensTarget | null, sensitivitySeq?: number): void;
|
|
134
141
|
finishSolve(c: SolvableCase, seq: number, target: SensTarget | null): void;
|
|
@@ -19,10 +19,9 @@ function localHasBus(c, busId) {
|
|
|
19
19
|
}
|
|
20
20
|
/** Terminal copy when nothing can solve. Two facts decide the wording: whether
|
|
21
21
|
* this browser's engine failure is known permanent, and that the server
|
|
22
|
-
* declines compute.
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
const COMPUTE_OFF_NOTICE = 'solving in this browser did not complete, and server side compute is disabled in this demo. retry to run the browser engine again, or email Samuel Talkington at talks@umich.edu to express interest in server compute';
|
|
22
|
+
* declines compute. */
|
|
23
|
+
const WASM_REQUIRED_NOTICE = 'This browser cannot run the WebAssembly engine, and server compute is disabled';
|
|
24
|
+
const COMPUTE_OFF_NOTICE = 'The browser solve did not complete, and server compute is disabled. Retry to run it again';
|
|
26
25
|
/** Upper bound on waiting for a camera fly-to before the sensitivity solve
|
|
27
26
|
* starts anyway: a missing map or a cue that never appears must not stall the
|
|
28
27
|
* selection. */
|
|
@@ -107,6 +106,8 @@ export class Controller {
|
|
|
107
106
|
// File reads and worker messages retain whole buffers. Admit one validated
|
|
108
107
|
// batch at a time so repeated drops cannot multiply the aggregate byte cap.
|
|
109
108
|
dropBatchGate = new DropBatchGate();
|
|
109
|
+
// Set by dispose(). Not $state; nothing renders it.
|
|
110
|
+
disposed = false;
|
|
110
111
|
nearbyRangeAnchor = $state(null);
|
|
111
112
|
// Default cases the user has closed; drives the restore affordance. Seeded in load().
|
|
112
113
|
hiddenDefaults = $state(new Set());
|
|
@@ -268,6 +269,9 @@ export class Controller {
|
|
|
268
269
|
// under that formulation. Returns null when the sens module can't load; the caller then
|
|
269
270
|
// falls back to the server, surfacing solveFallbackReason.
|
|
270
271
|
async getStudy(c, studyInputJson) {
|
|
272
|
+
// A disposed controller builds nothing that would outlive it.
|
|
273
|
+
if (this.disposed)
|
|
274
|
+
return null;
|
|
271
275
|
const latched = this.studyUnavailable.get(c);
|
|
272
276
|
if (latched) {
|
|
273
277
|
c.solveFallbackReason ??= latched;
|
|
@@ -347,6 +351,28 @@ export class Controller {
|
|
|
347
351
|
const study = await this.getStudy(c, studyInputJson);
|
|
348
352
|
return study ? study.sensitivity(c.id, this.caseDeltas(c), this.caseRatings(c), target) : null;
|
|
349
353
|
}
|
|
354
|
+
/** Release every engine handle this controller holds: each case's browser
|
|
355
|
+
* Study, including one still being built, and each multiconductor
|
|
356
|
+
* session, with their pending edits and in-flight requests.
|
|
357
|
+
* `TellegenProvider` calls it when it unmounts; the controller builds no
|
|
358
|
+
* Study and keeps no new session afterwards. */
|
|
359
|
+
dispose() {
|
|
360
|
+
if (this.disposed)
|
|
361
|
+
return;
|
|
362
|
+
this.disposed = true;
|
|
363
|
+
this.abort?.abort();
|
|
364
|
+
this.abort = null;
|
|
365
|
+
for (const c of [...this.app.cases, ...this.app.localCases])
|
|
366
|
+
this.disposeStudy(c);
|
|
367
|
+
for (const c of this.app.multiCases) {
|
|
368
|
+
c.solveAbort?.abort();
|
|
369
|
+
if (c.mcEditTimer !== null)
|
|
370
|
+
clearTimeout(c.mcEditTimer);
|
|
371
|
+
c.mcEditTimer = null;
|
|
372
|
+
c.mcSession?.free();
|
|
373
|
+
c.mcSession = null;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
350
376
|
// Release a case's Study (if any) when the case is removed.
|
|
351
377
|
disposeStudy(c) {
|
|
352
378
|
const cached = this.caseStudies.get(c);
|
|
@@ -2144,7 +2170,7 @@ export class Controller {
|
|
|
2144
2170
|
}
|
|
2145
2171
|
/** Compute terminal quantities only for the retained input and current case revision. */
|
|
2146
2172
|
solveMultiCase = async (c, options = {}, signal) => {
|
|
2147
|
-
if (signal?.aborted)
|
|
2173
|
+
if (signal?.aborted || this.disposed)
|
|
2148
2174
|
throw new DOMException('Calculation cancelled', 'AbortError');
|
|
2149
2175
|
if (c.solving)
|
|
2150
2176
|
throw new Error('A calculation is already running for this case');
|
|
@@ -2187,7 +2213,8 @@ export class Controller {
|
|
|
2187
2213
|
snapshot?.result ?? (await this.mcTransport.solveMcModule(input, options, abort.signal));
|
|
2188
2214
|
result = summarizeMcPfResult(fullResult);
|
|
2189
2215
|
}
|
|
2190
|
-
|
|
2216
|
+
// dispose() aborts too; a session that arrives after it is freed below.
|
|
2217
|
+
if (abort.signal.aborted || this.disposed)
|
|
2191
2218
|
throw new DOMException('Calculation cancelled', 'AbortError');
|
|
2192
2219
|
if (seq !== c.solveSeq ||
|
|
2193
2220
|
(retainedAtStart ? retainedAtStart !== c.mcSession : input !== c.moduleJson) ||
|
package/dist/display.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { demandGradient, priceGradient } from './colors.js';
|
|
2
|
-
import { priceCopy } from './format.js';
|
|
3
2
|
import { CaseState } from './state.svelte.js';
|
|
4
3
|
/** Variables present in the current result. Power flow has no LMP values. */
|
|
5
4
|
export function displayMetaFor(c) {
|
|
@@ -11,7 +10,9 @@ export function displayMetaFor(c) {
|
|
|
11
10
|
mode: 'price',
|
|
12
11
|
label: 'LMP',
|
|
13
12
|
unit: 'objective units/MW',
|
|
14
|
-
copy:
|
|
13
|
+
copy: c.formulation === 'socwr'
|
|
14
|
+
? 'Locational marginal price, SOCWR relaxation'
|
|
15
|
+
: 'Locational marginal price',
|
|
15
16
|
gradient: priceGradient
|
|
16
17
|
});
|
|
17
18
|
}
|
|
@@ -20,18 +21,16 @@ export function displayMetaFor(c) {
|
|
|
20
21
|
mode: 'voltage',
|
|
21
22
|
label: '|V|',
|
|
22
23
|
unit: 'pu',
|
|
23
|
-
copy: c.formulation === 'socwr'
|
|
24
|
-
? 'SOCWR voltage magnitude from the current relaxed solution.'
|
|
25
|
-
: 'Bus voltage magnitude from the current power-flow result.',
|
|
24
|
+
copy: c.formulation === 'socwr' ? 'Voltage magnitude, SOCWR relaxation' : 'Voltage magnitude',
|
|
26
25
|
gradient: priceGradient
|
|
27
26
|
});
|
|
28
27
|
}
|
|
29
28
|
if (c.solution.va.length > 0) {
|
|
30
29
|
options.push({
|
|
31
30
|
mode: 'angle',
|
|
32
|
-
label: '
|
|
31
|
+
label: 'Angle',
|
|
33
32
|
unit: 'rad',
|
|
34
|
-
copy: '
|
|
33
|
+
copy: 'Voltage angle',
|
|
35
34
|
gradient: priceGradient
|
|
36
35
|
});
|
|
37
36
|
}
|
|
@@ -39,9 +38,9 @@ export function displayMetaFor(c) {
|
|
|
39
38
|
if (c.network?.buses.length) {
|
|
40
39
|
options.push({
|
|
41
40
|
mode: 'demand',
|
|
42
|
-
label: '
|
|
41
|
+
label: 'Demand',
|
|
43
42
|
unit: 'MW',
|
|
44
|
-
copy: '
|
|
43
|
+
copy: 'Demand, square-root scale',
|
|
45
44
|
gradient: demandGradient
|
|
46
45
|
});
|
|
47
46
|
}
|
package/dist/format.d.ts
CHANGED
|
@@ -1,19 +1,11 @@
|
|
|
1
|
-
import type { SolveIteration } from './api.js';
|
|
2
1
|
import type { RGBA } from './colors.js';
|
|
3
|
-
import type { DisplayMode
|
|
2
|
+
import type { DisplayMode } from './state.svelte.js';
|
|
4
3
|
import { type Formulation } from '@tellegen/engine';
|
|
5
4
|
export { errorText } from '@tellegen/engine';
|
|
6
5
|
/** The short menu label for a formulation tag (e.g. `acopf` -> `AC OPF`). */
|
|
7
6
|
export declare function formulationLabel(id: Formulation): string;
|
|
8
7
|
export declare function formulationHint(id: Formulation): string;
|
|
9
|
-
export declare function priceCopy(id: Formulation): string;
|
|
10
8
|
export declare function splitName(name: string): [string, string];
|
|
11
|
-
/** Structural shape `solveMetaLabel` needs, satisfied by `CaseState` and `LocalCase`. */
|
|
12
|
-
type SolveMeta = {
|
|
13
|
-
iterations?: SolveIteration[];
|
|
14
|
-
solveBackend: SolveBackend | null;
|
|
15
|
-
};
|
|
16
|
-
export declare function solveMetaLabel(c: SolveMeta): string;
|
|
17
9
|
export declare const INFEASIBLE_DISPATCH = "No feasible dispatch: demand cannot be served within line and generator limits";
|
|
18
10
|
/** The message for a local case whose browser solve failed, and the
|
|
19
11
|
* technical detail to disclose behind it. An infeasible DC OPF means the
|
package/dist/format.js
CHANGED
|
@@ -9,23 +9,10 @@ export function formulationLabel(id) {
|
|
|
9
9
|
export function formulationHint(id) {
|
|
10
10
|
return FORMULATIONS.find((f) => f.id === id)?.hint ?? id;
|
|
11
11
|
}
|
|
12
|
-
export function priceCopy(id) {
|
|
13
|
-
const definition = 'Locational marginal price (LMP): the change in the OPF objective for an additional MW at a bus, in objective units/MW.';
|
|
14
|
-
return id === 'socwr'
|
|
15
|
-
? `${definition} Prices come from the SOCWR relaxation. Select a bus to see their sensitivity.`
|
|
16
|
-
: `${definition} Select a bus to see its demand sensitivity.`;
|
|
17
|
-
}
|
|
18
12
|
export function splitName(name) {
|
|
19
13
|
const m = name.match(/^(.*?)\s*\((.*)\)$/);
|
|
20
14
|
return m ? [m[1], m[2]] : [name, ''];
|
|
21
15
|
}
|
|
22
|
-
export function solveMetaLabel(c) {
|
|
23
|
-
if ((c.iterations ?? []).length > 1)
|
|
24
|
-
return `${c.iterations?.length} iterations`;
|
|
25
|
-
if (c.solveBackend === 'clarabel-wasm-server-sensitivity')
|
|
26
|
-
return 'server price sensitivity';
|
|
27
|
-
return c.solveBackend === 'rust-server' ? 'server solve' : 'browser solve';
|
|
28
|
-
}
|
|
29
16
|
export const INFEASIBLE_DISPATCH = 'No feasible dispatch: demand cannot be served within line and generator limits';
|
|
30
17
|
/** The message for a local case whose browser solve failed, and the
|
|
31
18
|
* technical detail to disclose behind it. An infeasible DC OPF means the
|
package/dist/styles.css
CHANGED
|
@@ -298,6 +298,7 @@ a {
|
|
|
298
298
|
before the component split. Scoping this would raise its specificity past that rule. */
|
|
299
299
|
.mode > button {
|
|
300
300
|
margin-left: auto;
|
|
301
|
+
font-family: inherit;
|
|
301
302
|
font-size: 12.5px;
|
|
302
303
|
padding: 2px 7px;
|
|
303
304
|
background: none;
|
|
@@ -313,6 +314,7 @@ a {
|
|
|
313
314
|
}
|
|
314
315
|
|
|
315
316
|
.display-mode {
|
|
317
|
+
flex-wrap: wrap;
|
|
316
318
|
gap: 8px;
|
|
317
319
|
}
|
|
318
320
|
|
|
@@ -387,6 +389,7 @@ a {
|
|
|
387
389
|
|
|
388
390
|
.reset {
|
|
389
391
|
margin-top: 10px;
|
|
392
|
+
font-family: inherit;
|
|
390
393
|
font-size: 12.5px;
|
|
391
394
|
padding: 3px 10px;
|
|
392
395
|
background: none;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tellegen/svelte",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"description": "Svelte components for tellegen power system maps, local case files, and browser solves.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"svelte",
|
|
@@ -67,18 +67,22 @@
|
|
|
67
67
|
"pack:dry-run": "npm pack --dry-run --cache ../../node_modules/.cache/npm-pack"
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
+
"@tellegen/engine": "^0.4.1"
|
|
71
|
+
},
|
|
72
|
+
"peerDependencies": {
|
|
70
73
|
"@deck.gl/core": "^9.4.0",
|
|
71
74
|
"@deck.gl/layers": "^9.4.0",
|
|
72
75
|
"@deck.gl/maplibre": "^9.4.0",
|
|
73
|
-
"
|
|
74
|
-
"maplibre-gl": "^6.15.0"
|
|
75
|
-
},
|
|
76
|
-
"peerDependencies": {
|
|
76
|
+
"maplibre-gl": "^6.15.0",
|
|
77
77
|
"svelte": ">=5.30 <6"
|
|
78
78
|
},
|
|
79
79
|
"devDependencies": {
|
|
80
|
-
"@
|
|
80
|
+
"@deck.gl/core": "^9.4.0",
|
|
81
|
+
"@deck.gl/layers": "^9.4.0",
|
|
82
|
+
"@deck.gl/maplibre": "^9.4.0",
|
|
83
|
+
"@sveltejs/package": "^3.0.1",
|
|
81
84
|
"@sveltejs/vite-plugin-svelte": "^7.3.1",
|
|
85
|
+
"maplibre-gl": "^6.15.0",
|
|
82
86
|
"svelte": "^5.57.2",
|
|
83
87
|
"svelte-check": "^4.7.7",
|
|
84
88
|
"typescript": "^6.0.3",
|