@tellegen/svelte 0.2.0 → 0.4.0
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 +269 -5
- package/dist/Sparkline.svelte +2 -2
- package/dist/TellegenMap.svelte +518 -202
- package/dist/TellegenMap.svelte.d.ts +45 -0
- package/dist/TellegenProvider.svelte +40 -4
- package/dist/TellegenShell.svelte +24 -46
- package/dist/TellegenViewer.svelte +12 -2
- package/dist/api.d.ts +18 -3
- package/dist/api.js +8 -2
- package/dist/basemap.d.ts +192 -0
- package/dist/basemap.js +259 -0
- package/dist/colors.d.ts +25 -7
- package/dist/colors.js +39 -13
- package/dist/components/AppFooter.svelte +3 -2
- package/dist/components/AppHeader.svelte +44 -19
- package/dist/components/BindingLines.svelte +4 -7
- package/dist/components/BusPicker.svelte +11 -5
- package/dist/components/ControlPanel.svelte +106 -343
- package/dist/components/DemandSlider.svelte +36 -29
- package/dist/components/DiagramCanvas.svelte +486 -0
- package/dist/components/DiagramCanvas.svelte.d.ts +13 -0
- package/dist/components/DisplayControls.svelte +8 -2
- package/dist/components/DropZone.svelte +1 -1
- package/dist/components/FormulationSelector.svelte +9 -4
- package/dist/components/LocalCaseDetails.svelte +61 -48
- package/dist/components/McResults.svelte +428 -0
- package/dist/components/McResults.svelte.d.ts +17 -0
- package/dist/components/ModelDetails.svelte +230 -0
- package/dist/components/ModelDetails.svelte.d.ts +7 -0
- package/dist/components/MulticonductorDetails.svelte +232 -83
- package/dist/components/NetworkStats.svelte +20 -18
- package/dist/components/NotificationCenter.svelte +245 -0
- package/dist/components/NotificationCenter.svelte.d.ts +3 -0
- package/dist/components/PanelCard.svelte +384 -0
- package/dist/components/PanelCard.svelte.d.ts +9 -0
- package/dist/components/PanelFrame.svelte +46 -0
- package/dist/components/PanelFrame.svelte.d.ts +16 -0
- package/dist/components/PanelHost.svelte +237 -0
- package/dist/components/PanelHost.svelte.d.ts +6 -0
- package/dist/components/PlacementCue.svelte +1 -1
- package/dist/components/PowerFlowDetails.svelte +99 -0
- package/dist/components/PowerFlowDetails.svelte.d.ts +3 -0
- package/dist/components/RatingSlider.svelte +19 -14
- package/dist/components/RestoreDefaultsButton.svelte +1 -1
- package/dist/components/SavedNetworkDetails.svelte +214 -0
- package/dist/components/SavedNetworkDetails.svelte.d.ts +3 -0
- package/dist/components/SensitivityReadout.svelte +26 -18
- package/dist/components/ShellEnvironment.svelte +51 -0
- package/dist/components/ShellEnvironment.svelte.d.ts +18 -0
- package/dist/components/SizeLegend.svelte +2 -2
- package/dist/components/SolveCard.svelte +48 -51
- package/dist/components/TopMovers.svelte +9 -4
- package/dist/components/TopMovers.svelte.d.ts +2 -17
- package/dist/components.d.ts +5 -0
- package/dist/components.js +5 -0
- package/dist/context.svelte.d.ts +8 -1
- package/dist/context.svelte.js +8 -3
- package/dist/controller.svelte.d.ts +117 -36
- package/dist/controller.svelte.js +1133 -253
- package/dist/diagram-labels.d.ts +9 -0
- package/dist/diagram-labels.js +40 -0
- package/dist/display.d.ts +3 -7
- package/dist/display.js +31 -27
- package/dist/format.d.ts +9 -0
- package/dist/format.js +17 -4
- package/dist/index.d.ts +15 -5
- package/dist/index.js +9 -2
- package/dist/map.d.ts +4 -2
- package/dist/map.js +2 -1
- package/dist/maplibre-worker.d.ts +5 -0
- package/dist/maplibre-worker.dev.d.ts +11 -0
- package/dist/maplibre-worker.dev.js +15 -0
- package/dist/maplibre-worker.js +8 -0
- package/dist/multiconductor.d.ts +11 -2
- package/dist/multiconductor.js +107 -14
- package/dist/notices.svelte.d.ts +29 -0
- package/dist/notices.svelte.js +86 -0
- package/dist/panels.svelte.d.ts +71 -0
- package/dist/panels.svelte.js +245 -0
- package/dist/slider.d.ts +12 -0
- package/dist/slider.js +31 -0
- package/dist/state.svelte.d.ts +107 -23
- package/dist/state.svelte.js +106 -20
- package/dist/styles.css +59 -6
- package/dist/synthetic-layout.js +1 -0
- package/package.json +29 -11
- package/dist/study-package.d.ts +0 -8
- package/dist/study-package.js +0 -17
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type PositionedLabel = {
|
|
2
|
+
id: number;
|
|
3
|
+
lon: number;
|
|
4
|
+
lat: number;
|
|
5
|
+
name?: string | null;
|
|
6
|
+
};
|
|
7
|
+
/** Place labels in screen pixels so zooming reveals equipment names as space opens. */
|
|
8
|
+
export declare function diagramLabels(buses: PositionedLabel[], scale: number, x: number, y: number, width: number, height: number, selected: number | null): Set<number>;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** Place labels in screen pixels so zooming reveals equipment names as space opens. */
|
|
2
|
+
export function diagramLabels(buses, scale, x, y, width, height, selected) {
|
|
3
|
+
const visible = new Set();
|
|
4
|
+
const cells = new Map();
|
|
5
|
+
const order = selected === null
|
|
6
|
+
? buses
|
|
7
|
+
: [
|
|
8
|
+
...buses.filter((bus) => bus.id === selected),
|
|
9
|
+
...buses.filter((bus) => bus.id !== selected)
|
|
10
|
+
];
|
|
11
|
+
for (const bus of order) {
|
|
12
|
+
const label = bus.name || String(bus.id);
|
|
13
|
+
const left = bus.lon * scale + x + 8;
|
|
14
|
+
const bottom = bus.lat * scale + y - 7;
|
|
15
|
+
const box = { left, bottom, right: left + label.length * 7 + 6, top: bottom - 16 };
|
|
16
|
+
if (box.right < 0 || box.left > width || box.bottom < 0 || box.top > height)
|
|
17
|
+
continue;
|
|
18
|
+
const keys = [];
|
|
19
|
+
let overlaps = false;
|
|
20
|
+
for (let col = Math.floor(Math.max(0, box.left) / 64); col <= Math.floor(Math.min(width, box.right) / 64); col++) {
|
|
21
|
+
for (let row = Math.floor(Math.max(0, box.top) / 64); row <= Math.floor(Math.min(height, box.bottom) / 64); row++) {
|
|
22
|
+
const key = `${col},${row}`;
|
|
23
|
+
keys.push(key);
|
|
24
|
+
if (cells
|
|
25
|
+
.get(key)
|
|
26
|
+
?.some((other) => box.left < other.right &&
|
|
27
|
+
box.right > other.left &&
|
|
28
|
+
box.top < other.bottom &&
|
|
29
|
+
box.bottom > other.top))
|
|
30
|
+
overlaps = true;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (overlaps)
|
|
34
|
+
continue;
|
|
35
|
+
visible.add(bus.id);
|
|
36
|
+
for (const key of keys)
|
|
37
|
+
cells.set(key, [...(cells.get(key) ?? []), box]);
|
|
38
|
+
}
|
|
39
|
+
return visible;
|
|
40
|
+
}
|
package/dist/display.d.ts
CHANGED
|
@@ -10,14 +10,10 @@ export type DisplayOption = {
|
|
|
10
10
|
copy: string;
|
|
11
11
|
gradient: string;
|
|
12
12
|
};
|
|
13
|
-
/**
|
|
14
|
-
* LMP always, the DC phase angle under DC OPF, the relaxed |V| under SOCWR. Metadata
|
|
15
|
-
* only; call `displaySeriesFor` for one mode's per-bus values. Empty when unsolved. */
|
|
13
|
+
/** Variables present in the current result. Power flow has no LMP values. */
|
|
16
14
|
export declare function displayMetaFor(c: SolvableCase | null): DisplayOption[];
|
|
17
|
-
/**
|
|
18
|
-
*
|
|
19
|
-
* Empty when the case is unsolved. Single source for both the panel legend stats and
|
|
20
|
-
* the map node coloring, so the |V| transform stays in one place. */
|
|
15
|
+
/** Bus LMPs, voltage angles in radians, or voltage magnitudes in per unit.
|
|
16
|
+
* SOCWR supplies squared voltages, which are converted to magnitudes here. */
|
|
21
17
|
export declare function displaySeriesFor(c: SolvableCase | null, mode: DisplayMode): {
|
|
22
18
|
bus: number;
|
|
23
19
|
value: number;
|
package/dist/display.js
CHANGED
|
@@ -1,45 +1,44 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { priceGradient } from './colors.js';
|
|
2
2
|
import { priceCopy } from './format.js';
|
|
3
3
|
import { CaseState } from './state.svelte.js';
|
|
4
|
-
/**
|
|
5
|
-
* LMP always, the DC phase angle under DC OPF, the relaxed |V| under SOCWR. Metadata
|
|
6
|
-
* only; call `displaySeriesFor` for one mode's per-bus values. Empty when unsolved. */
|
|
4
|
+
/** Variables present in the current result. Power flow has no LMP values. */
|
|
7
5
|
export function displayMetaFor(c) {
|
|
8
6
|
if (!c?.solution)
|
|
9
7
|
return [];
|
|
10
|
-
const options = [
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
const options = [];
|
|
9
|
+
if (c.formulation !== 'acpf' && c.solution.prices.length > 0) {
|
|
10
|
+
options.push({
|
|
11
|
+
mode: 'price',
|
|
13
12
|
label: 'LMP',
|
|
14
|
-
unit: '
|
|
13
|
+
unit: 'objective units/MW',
|
|
15
14
|
copy: priceCopy(c.formulation),
|
|
16
|
-
gradient:
|
|
17
|
-
}
|
|
18
|
-
];
|
|
19
|
-
if (c.formulation === 'dcopf' && c.solution.va.length > 0) {
|
|
20
|
-
options.push({
|
|
21
|
-
mode: 'angle',
|
|
22
|
-
label: 'angle',
|
|
23
|
-
unit: 'rad',
|
|
24
|
-
copy: 'DC bus voltage phase angle from the current OPF solution.',
|
|
25
|
-
gradient: lmpGradient
|
|
15
|
+
gradient: priceGradient
|
|
26
16
|
});
|
|
27
17
|
}
|
|
28
|
-
if (c.
|
|
18
|
+
if (c.solution.vm?.length || c.solution.w.length > 0) {
|
|
29
19
|
options.push({
|
|
30
20
|
mode: 'voltage',
|
|
31
21
|
label: '|V|',
|
|
32
22
|
unit: 'pu',
|
|
33
|
-
copy:
|
|
34
|
-
|
|
23
|
+
copy: c.formulation === 'socwr'
|
|
24
|
+
? 'SOCWR voltage magnitude from the current relaxed solution.'
|
|
25
|
+
: 'Bus voltage magnitude from the current power-flow result.',
|
|
26
|
+
gradient: priceGradient
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
if (c.solution.va.length > 0) {
|
|
30
|
+
options.push({
|
|
31
|
+
mode: 'angle',
|
|
32
|
+
label: 'angle',
|
|
33
|
+
unit: 'rad',
|
|
34
|
+
copy: 'Bus voltage angle from the current result.',
|
|
35
|
+
gradient: priceGradient
|
|
35
36
|
});
|
|
36
37
|
}
|
|
37
38
|
return options;
|
|
38
39
|
}
|
|
39
|
-
/**
|
|
40
|
-
*
|
|
41
|
-
* Empty when the case is unsolved. Single source for both the panel legend stats and
|
|
42
|
-
* the map node coloring, so the |V| transform stays in one place. */
|
|
40
|
+
/** Bus LMPs, voltage angles in radians, or voltage magnitudes in per unit.
|
|
41
|
+
* SOCWR supplies squared voltages, which are converted to magnitudes here. */
|
|
43
42
|
export function displaySeriesFor(c, mode) {
|
|
44
43
|
const sol = c?.solution;
|
|
45
44
|
if (!sol)
|
|
@@ -47,9 +46,14 @@ export function displaySeriesFor(c, mode) {
|
|
|
47
46
|
if (mode === 'angle')
|
|
48
47
|
return sol.va;
|
|
49
48
|
if (mode === 'voltage') {
|
|
50
|
-
|
|
49
|
+
if (sol.vm?.length)
|
|
50
|
+
return sol.vm;
|
|
51
|
+
return sol.w.map((s) => ({
|
|
52
|
+
bus: s.bus,
|
|
53
|
+
value: Math.sqrt(Math.max(0, s.value))
|
|
54
|
+
}));
|
|
51
55
|
}
|
|
52
|
-
return sol.
|
|
56
|
+
return sol.prices.map((s) => ({ bus: s.bus, value: s.value }));
|
|
53
57
|
}
|
|
54
58
|
/** The committed demand deltas for a case (MW from base, keyed by bus). A backend
|
|
55
59
|
* case always carries a deltas map; a local case defaults to an empty one. */
|
package/dist/format.d.ts
CHANGED
|
@@ -14,6 +14,15 @@ type SolveMeta = {
|
|
|
14
14
|
solveBackend: SolveBackend | null;
|
|
15
15
|
};
|
|
16
16
|
export declare function solveMetaLabel(c: SolveMeta): string;
|
|
17
|
+
export declare const INFEASIBLE_DISPATCH = "No feasible dispatch: demand cannot be served within line and generator limits";
|
|
18
|
+
/** The message for a local case whose browser solve failed, and the
|
|
19
|
+
* technical detail to disclose behind it. An infeasible DC OPF means the
|
|
20
|
+
* network cannot carry the demand, which deserves plain words. The engine
|
|
21
|
+
* spells it `DC OPF solve infeasible`, and an unbounded one as `unbounded`. */
|
|
22
|
+
export declare function browserSolveFailure(label: string, reason: string | null): {
|
|
23
|
+
message: string;
|
|
24
|
+
detail: string | null;
|
|
25
|
+
};
|
|
17
26
|
export declare function rgbaCss([r, g, b, a]: RGBA): string;
|
|
18
27
|
/** Min and max of a numeric array in one pass. Used instead of
|
|
19
28
|
* `Math.min(...values)` / `Math.max(...values)`, which throw a RangeError once the
|
package/dist/format.js
CHANGED
|
@@ -10,9 +10,10 @@ export function formulationHint(id) {
|
|
|
10
10
|
return FORMULATIONS.find((f) => f.id === id)?.hint ?? id;
|
|
11
11
|
}
|
|
12
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.';
|
|
13
14
|
return id === 'socwr'
|
|
14
|
-
?
|
|
15
|
-
:
|
|
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.`;
|
|
16
17
|
}
|
|
17
18
|
export function splitName(name) {
|
|
18
19
|
const m = name.match(/^(.*?)\s*\((.*)\)$/);
|
|
@@ -22,9 +23,21 @@ export function solveMetaLabel(c) {
|
|
|
22
23
|
if ((c.iterations ?? []).length > 1)
|
|
23
24
|
return `${c.iterations?.length} iterations`;
|
|
24
25
|
if (c.solveBackend === 'clarabel-wasm-server-sensitivity')
|
|
25
|
-
return 'server
|
|
26
|
+
return 'server price sensitivity';
|
|
26
27
|
return c.solveBackend === 'rust-server' ? 'server solve' : 'browser solve';
|
|
27
28
|
}
|
|
29
|
+
export const INFEASIBLE_DISPATCH = 'No feasible dispatch: demand cannot be served within line and generator limits';
|
|
30
|
+
/** The message for a local case whose browser solve failed, and the
|
|
31
|
+
* technical detail to disclose behind it. An infeasible DC OPF means the
|
|
32
|
+
* network cannot carry the demand, which deserves plain words. The engine
|
|
33
|
+
* spells it `DC OPF solve infeasible`, and an unbounded one as `unbounded`. */
|
|
34
|
+
export function browserSolveFailure(label, reason) {
|
|
35
|
+
const technical = `${label}: browser solve unavailable${reason ? `: ${reason}` : ''}`;
|
|
36
|
+
if (reason && /\binfeasible\b/i.test(reason)) {
|
|
37
|
+
return { message: INFEASIBLE_DISPATCH, detail: technical };
|
|
38
|
+
}
|
|
39
|
+
return { message: technical, detail: null };
|
|
40
|
+
}
|
|
28
41
|
export function rgbaCss([r, g, b, a]) {
|
|
29
42
|
return `rgba(${r}, ${g}, ${b}, ${(a / 255).toFixed(3)})`;
|
|
30
43
|
}
|
|
@@ -46,4 +59,4 @@ export function extent(values) {
|
|
|
46
59
|
export const fmt = new Intl.NumberFormat('en-US', { maximumFractionDigits: 1 });
|
|
47
60
|
export const signed = (v) => `${v < 0 ? '−' : '+'}${fmt.format(Math.abs(v))}`;
|
|
48
61
|
export const signedExp = (v) => `${v < 0 ? '−' : '+'}${Math.abs(v).toExponential(2)}`;
|
|
49
|
-
export const displayFmt = (mode, value) => mode === '
|
|
62
|
+
export const displayFmt = (mode, value) => mode === 'price' ? fmt.format(value) : value.toFixed(3);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,19 +1,29 @@
|
|
|
1
|
+
export { getNoticeCenter, errorNoticeTitle } from './notices.svelte.js';
|
|
2
|
+
export { default as ModelDetails } from './components/ModelDetails.svelte';
|
|
3
|
+
export { default as PanelFrame } from './components/PanelFrame.svelte';
|
|
4
|
+
export { getPanelLayout } from './panels.svelte.js';
|
|
5
|
+
export type { PanelSide, PanelPosition, PanelBounds } from './panels.svelte.js';
|
|
1
6
|
export { default as TellegenProvider } from './TellegenProvider.svelte';
|
|
2
7
|
export { default as TellegenShell } from './TellegenShell.svelte';
|
|
3
8
|
export { default as TellegenViewer } from './TellegenViewer.svelte';
|
|
4
9
|
export { default as TellegenMap } from './TellegenMap.svelte';
|
|
5
10
|
export { DEFAULT_TELLEGEN_UI_CONFIG, getAppState, getController, getUiConfig, resolveTellegenUiConfig, setAppState, setController, setUiConfig } from './context.svelte.js';
|
|
6
11
|
export { ApiError, createApiClient } from './api.js';
|
|
12
|
+
export { CARTO_STYLES, OPENFREEMAP_STYLES, attachBasemapFallback, cartoStyleUrl, defaultBasemap, isOpenFreeMapStyle, openFreeMapEdits, resolveBasemap } from './basemap.js';
|
|
13
|
+
export { MAP_PALETTE } from './colors.js';
|
|
7
14
|
export { Controller, createController } from './controller.svelte.js';
|
|
8
15
|
export { AppState, CaseState, LocalCase, MulticonductorCase, createAppState } from './state.svelte.js';
|
|
9
|
-
export { attachmentColor, attachmentGlyph, edgeColor, edgeWidth, phaseColor, placeMultiView, buildGeographicView } from './multiconductor.js';
|
|
16
|
+
export { attachmentColor, attachmentGlyph, edgeColor, edgeWidth, phaseColor, neutralTerminal, placeMultiView, buildGeographicView } from './multiconductor.js';
|
|
10
17
|
export { distExtensionFormat } from './drop-classify.js';
|
|
11
18
|
export { caseDeltas, caseRatings, displayMetaFor, displaySeriesFor } from './display.js';
|
|
12
|
-
export { DEFAULT_FORMULATION, FORMULATIONS, BrowserStudy, Study, capabilities, createStudy, errorText, formatOf, classifyJson, ingestJsonDrop, ingestCase, isDisplayFile, isPermanentEngineFailure, parseDisplay,
|
|
19
|
+
export { DEFAULT_FORMULATION, FORMULATIONS, BrowserStudy, Study, capabilities, createStudy, errorText, formatOf, classifyJson, ingestJsonDrop, ingestCase, isDisplayFile, isPermanentEngineFailure, parseDisplay, solveModule } from '@tellegen/engine';
|
|
13
20
|
export type { BranchRatingDeltas, CaseSummary, DemandDeltas, Network, NetworkBranch, NetworkBus, SensitivityColumn, Solution, SolveIteration, SolveStreamHandlers, TellegenApiClient, TellegenApiClientOptions } from './api.js';
|
|
14
21
|
export type { TellegenUiConfig, TellegenUiOptions } from './context.svelte.js';
|
|
15
|
-
export type { ControllerOptions } from './controller.svelte.js';
|
|
22
|
+
export type { ControllerOptions, OpenBalancedModuleInput } from './controller.svelte.js';
|
|
23
|
+
export type { BasemapFallbackReason, BasemapMapLike, BasemapProvider, BasemapSpec, BasemapStyle, BasemapSwitch, BasemapTheme, DefaultBasemap, MapProjection, MapTheme, ResolvedBasemap, StyleEdit } from './basemap.js';
|
|
24
|
+
export type { MapPalette } from './colors.js';
|
|
16
25
|
export type { DisplayOption } from './display.js';
|
|
17
|
-
export type { DemandRangeMode, DisplayMode, FallbackTarget, LocalCaseInit, LocalSubstations, MultiCaseSummary, MultiCoordsKind, SolvableCase, SolveBackend } from './state.svelte.js';
|
|
26
|
+
export type { CameraSnapshot, DiagramCameraSnapshot, DemandRangeMode, DisplayMode, FallbackTarget, LocalCaseInit, LocalSubstations, MultiCaseSummary, MultiCoordsKind, SolvableCase, StudyDisplaySnapshot, SolveBackend } from './state.svelte.js';
|
|
18
27
|
export type { MultiView, PlacedMultiBus, PlacedMultiEdge } from './multiconductor.js';
|
|
19
|
-
export type { CaseFileSummary, DistAttachmentKind, DistEdgeKind, DistGraph, DistGraphAttachment, DistGraphBus, DistGraphEdge, DisplayPreview, Formulation, IngestedCase, IngestedDistCase, IngestedJsonDrop, JsonDropClassification, JsonDropKind, SensTarget, Topology, TopologyBranch, TopologyBus } from '@tellegen/engine';
|
|
28
|
+
export type { CaseFileSummary, DistAttachmentKind, DistEdgeKind, DistGraph, DistGraphAttachment, DistGraphBus, DistGraphEdge, DisplayPreview, Formulation, IngestedCase, IngestedDistCase, IngestedJsonDrop, JsonDropClassification, JsonDropKind, CapacityPlanBusWeightJson, CapacityPlanIterationJson, CapacityPlanOutcomeJson, CapacityPlanRatingChangeJson, CapacityPlanResultSummaryJson, CapacityPlanSpecJson, ImplicitObjectiveJson, SensTarget, Topology, TopologyBranch, TopologyBus } from '@tellegen/engine';
|
|
29
|
+
export { default as McResults } from './components/McResults.svelte';
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
|
+
export { getNoticeCenter, errorNoticeTitle } from './notices.svelte.js';
|
|
2
|
+
export { default as ModelDetails } from './components/ModelDetails.svelte';
|
|
3
|
+
export { default as PanelFrame } from './components/PanelFrame.svelte';
|
|
4
|
+
export { getPanelLayout } from './panels.svelte.js';
|
|
1
5
|
export { default as TellegenProvider } from './TellegenProvider.svelte';
|
|
2
6
|
export { default as TellegenShell } from './TellegenShell.svelte';
|
|
3
7
|
export { default as TellegenViewer } from './TellegenViewer.svelte';
|
|
4
8
|
export { default as TellegenMap } from './TellegenMap.svelte';
|
|
5
9
|
export { DEFAULT_TELLEGEN_UI_CONFIG, getAppState, getController, getUiConfig, resolveTellegenUiConfig, setAppState, setController, setUiConfig } from './context.svelte.js';
|
|
6
10
|
export { ApiError, createApiClient } from './api.js';
|
|
11
|
+
export { CARTO_STYLES, OPENFREEMAP_STYLES, attachBasemapFallback, cartoStyleUrl, defaultBasemap, isOpenFreeMapStyle, openFreeMapEdits, resolveBasemap } from './basemap.js';
|
|
12
|
+
export { MAP_PALETTE } from './colors.js';
|
|
7
13
|
export { Controller, createController } from './controller.svelte.js';
|
|
8
14
|
export { AppState, CaseState, LocalCase, MulticonductorCase, createAppState } from './state.svelte.js';
|
|
9
|
-
export { attachmentColor, attachmentGlyph, edgeColor, edgeWidth, phaseColor, placeMultiView, buildGeographicView } from './multiconductor.js';
|
|
15
|
+
export { attachmentColor, attachmentGlyph, edgeColor, edgeWidth, phaseColor, neutralTerminal, placeMultiView, buildGeographicView } from './multiconductor.js';
|
|
10
16
|
export { distExtensionFormat } from './drop-classify.js';
|
|
11
17
|
export { caseDeltas, caseRatings, displayMetaFor, displaySeriesFor } from './display.js';
|
|
12
|
-
export { DEFAULT_FORMULATION, FORMULATIONS, BrowserStudy, Study, capabilities, createStudy, errorText, formatOf, classifyJson, ingestJsonDrop, ingestCase, isDisplayFile, isPermanentEngineFailure, parseDisplay,
|
|
18
|
+
export { DEFAULT_FORMULATION, FORMULATIONS, BrowserStudy, Study, capabilities, createStudy, errorText, formatOf, classifyJson, ingestJsonDrop, ingestCase, isDisplayFile, isPermanentEngineFailure, parseDisplay, solveModule } from '@tellegen/engine';
|
|
19
|
+
export { default as McResults } from './components/McResults.svelte';
|
package/dist/map.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { ComponentProps } from 'svelte';
|
|
2
2
|
import TellegenMap from './TellegenMap.svelte';
|
|
3
3
|
export { default as TellegenMap } from './TellegenMap.svelte';
|
|
4
|
-
export {
|
|
4
|
+
export { CARTO_STYLES, OPENFREEMAP_STYLES, attachBasemapFallback, cartoStyleUrl, defaultBasemap, isOpenFreeMapStyle, openFreeMapEdits, resolveBasemap } from './basemap.js';
|
|
5
|
+
export type { BasemapFallbackReason, BasemapMapLike, BasemapProvider, BasemapSpec, BasemapStyle, BasemapSwitch, BasemapTheme, DefaultBasemap, MapProjection, MapTheme, ResolvedBasemap, StyleEdit } from './basemap.js';
|
|
6
|
+
export { MAP_PALETTE, branchColor, branchWidth, busNeutral, busRadius, priceColor, priceDomain, priceGradient, scalarDomain, sensColor, sensFlatColor, sensGradient, sensNeutral, sensitivityDomain } from './colors.js';
|
|
5
7
|
export { caseDeltas, displayMetaFor, displaySeriesFor } from './display.js';
|
|
6
|
-
export type { RGBA, SensitivityDomain } from './colors.js';
|
|
8
|
+
export type { MapPalette, RGBA, SensitivityDomain } from './colors.js';
|
|
7
9
|
export type { DisplayOption } from './display.js';
|
|
8
10
|
export type TellegenMapProps = ComponentProps<typeof TellegenMap>;
|
package/dist/map.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import TellegenMap from './TellegenMap.svelte';
|
|
2
2
|
export { default as TellegenMap } from './TellegenMap.svelte';
|
|
3
|
-
export {
|
|
3
|
+
export { CARTO_STYLES, OPENFREEMAP_STYLES, attachBasemapFallback, cartoStyleUrl, defaultBasemap, isOpenFreeMapStyle, openFreeMapEdits, resolveBasemap } from './basemap.js';
|
|
4
|
+
export { MAP_PALETTE, branchColor, branchWidth, busNeutral, busRadius, priceColor, priceDomain, priceGradient, scalarDomain, sensColor, sensFlatColor, sensGradient, sensNeutral, sensitivityDomain } from './colors.js';
|
|
4
5
|
export { caseDeltas, displayMetaFor, displaySeriesFor } from './display.js';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** The URL of MapLibre's worker, which the host's Vite build bundles with its
|
|
2
|
+
* shared chunk. `TellegenMap` hands it to `setWorkerUrl` before it creates a
|
|
3
|
+
* map. Reached through the `@tellegen/svelte/maplibre-worker` export; see
|
|
4
|
+
* `maplibre-worker.dev.ts` for the dev server. */
|
|
5
|
+
export declare function maplibreWorkerUrl(): Promise<string>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** The dev server's `@tellegen/svelte/maplibre-worker`, chosen by the
|
|
2
|
+
* `development` export condition.
|
|
3
|
+
*
|
|
4
|
+
* Vite's dev server pre-bundles this package with Rolldown, which cannot load
|
|
5
|
+
* the `?worker&url` import in `maplibre-worker.ts`, so a static import of that
|
|
6
|
+
* module would stop the dev server at startup. Here it is imported at run time
|
|
7
|
+
* by URL instead, outside the pre-bundle, and the dev server resolves the
|
|
8
|
+
* query itself. Vite rewrites the `new URL` in a pre-bundled file to point at
|
|
9
|
+
* the original. A production build takes the default condition and imports
|
|
10
|
+
* `maplibre-worker.ts` directly. */
|
|
11
|
+
export declare function maplibreWorkerUrl(): Promise<string>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** The dev server's `@tellegen/svelte/maplibre-worker`, chosen by the
|
|
2
|
+
* `development` export condition.
|
|
3
|
+
*
|
|
4
|
+
* Vite's dev server pre-bundles this package with Rolldown, which cannot load
|
|
5
|
+
* the `?worker&url` import in `maplibre-worker.ts`, so a static import of that
|
|
6
|
+
* module would stop the dev server at startup. Here it is imported at run time
|
|
7
|
+
* by URL instead, outside the pre-bundle, and the dev server resolves the
|
|
8
|
+
* query itself. Vite rewrites the `new URL` in a pre-bundled file to point at
|
|
9
|
+
* the original. A production build takes the default condition and imports
|
|
10
|
+
* `maplibre-worker.ts` directly. */
|
|
11
|
+
export async function maplibreWorkerUrl() {
|
|
12
|
+
const url = new URL('./maplibre-worker.js', import.meta.url).href;
|
|
13
|
+
const module = await import(/* @vite-ignore */ url);
|
|
14
|
+
return module.maplibreWorkerUrl();
|
|
15
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import workerUrl from 'maplibre-gl/dist/maplibre-gl-worker.mjs?worker&url';
|
|
2
|
+
/** The URL of MapLibre's worker, which the host's Vite build bundles with its
|
|
3
|
+
* shared chunk. `TellegenMap` hands it to `setWorkerUrl` before it creates a
|
|
4
|
+
* map. Reached through the `@tellegen/svelte/maplibre-worker` export; see
|
|
5
|
+
* `maplibre-worker.dev.ts` for the dev server. */
|
|
6
|
+
export async function maplibreWorkerUrl() {
|
|
7
|
+
return workerUrl;
|
|
8
|
+
}
|
package/dist/multiconductor.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export interface PlacedMultiBus {
|
|
|
12
12
|
lat: number;
|
|
13
13
|
terminals: string[];
|
|
14
14
|
grounded: string[];
|
|
15
|
+
neutral_terminal?: string | null;
|
|
15
16
|
load_kw: number;
|
|
16
17
|
gen_kw: number;
|
|
17
18
|
has_source: boolean;
|
|
@@ -28,7 +29,7 @@ export interface PlacedMultiEdge {
|
|
|
28
29
|
conductors: [string, string][];
|
|
29
30
|
closed: boolean;
|
|
30
31
|
n_phases: number;
|
|
31
|
-
path: [
|
|
32
|
+
path: [number, number][];
|
|
32
33
|
}
|
|
33
34
|
/** The placed graph the map renders. */
|
|
34
35
|
export interface MultiView {
|
|
@@ -37,7 +38,11 @@ export interface MultiView {
|
|
|
37
38
|
}
|
|
38
39
|
/** Place a geographic graph: each bus's `xy` is `[longitude, latitude]` and
|
|
39
40
|
* drops straight onto the map. */
|
|
40
|
-
export declare function buildGeographicView(graph: DistGraph): MultiView;
|
|
41
|
+
export declare function buildGeographicView(graph: DistGraph, layer?: string): MultiView;
|
|
42
|
+
/** Retain native drawing positions; unpositioned buses receive a separate generated layout. */
|
|
43
|
+
export declare function buildDiagramView(graph: DistGraph, layer?: string): MultiView;
|
|
44
|
+
/** Numeric indices address drawing objects only; source identities remain on each object. */
|
|
45
|
+
export declare function multiDiagramNetwork(id: string, name: string, view: MultiView): import('./api.js').Network;
|
|
41
46
|
/** Place a non-geographic graph at a map `center`: fit the provided planar
|
|
42
47
|
* coordinates when the case carries them, otherwise fall back to the shared
|
|
43
48
|
* synthetic force layout. */
|
|
@@ -48,6 +53,10 @@ export declare function placeMultiView(graph: DistGraph, center: PlacementCenter
|
|
|
48
53
|
export declare function phaseColor(terminal: string): RGBA;
|
|
49
54
|
/** True when a terminal is a phase conductor (not a neutral/ground return). */
|
|
50
55
|
export declare function isPhaseTerminal(terminal: string): boolean;
|
|
56
|
+
/** Return the explicit neutral conductor in a terminal map. Other non-phase
|
|
57
|
+
* labels are left alone: an arbitrary terminal name is not evidence that the
|
|
58
|
+
* network has a usable neutral reference. */
|
|
59
|
+
export declare function neutralTerminal(terminals: string[], declared?: string | null): string | null;
|
|
51
60
|
/** Base color for an edge by kind and state. Lines are the warm gray of the
|
|
52
61
|
* single-phase branches; transformers are steel; a switch is amber when closed
|
|
53
62
|
* and a faded red when open. */
|
package/dist/multiconductor.js
CHANGED
|
@@ -4,13 +4,7 @@
|
|
|
4
4
|
* `MulticonductorCase` state and the map own those. */
|
|
5
5
|
import { placeSyntheticTopology } from './synthetic-layout.js';
|
|
6
6
|
import { extent } from './format.js';
|
|
7
|
-
const ATTACHMENT_ORDER = [
|
|
8
|
-
'source',
|
|
9
|
-
'generator',
|
|
10
|
-
'ibr',
|
|
11
|
-
'load',
|
|
12
|
-
'shunt'
|
|
13
|
-
];
|
|
7
|
+
const ATTACHMENT_ORDER = ['source', 'generator', 'ibr', 'load', 'shunt'];
|
|
14
8
|
/** Flatten a bus's per-terminal attachments to the distinct kinds present, in a
|
|
15
9
|
* stable render order. */
|
|
16
10
|
function attachmentKindsOf(bus) {
|
|
@@ -28,6 +22,7 @@ function placedBus(bus, lon, lat) {
|
|
|
28
22
|
lat,
|
|
29
23
|
terminals: bus.terminals,
|
|
30
24
|
grounded: bus.grounded,
|
|
25
|
+
neutral_terminal: bus.neutral_terminal,
|
|
31
26
|
load_kw: bus.load_kw,
|
|
32
27
|
gen_kw: bus.gen_kw,
|
|
33
28
|
has_source: bus.has_source,
|
|
@@ -75,13 +70,105 @@ function viewFrom(graph, coords) {
|
|
|
75
70
|
}
|
|
76
71
|
/** Place a geographic graph: each bus's `xy` is `[longitude, latitude]` and
|
|
77
72
|
* drops straight onto the map. */
|
|
78
|
-
export function buildGeographicView(graph) {
|
|
73
|
+
export function buildGeographicView(graph, layer) {
|
|
79
74
|
const coords = new Map();
|
|
80
75
|
for (const bus of graph.buses) {
|
|
81
76
|
if (bus.xy)
|
|
82
77
|
coords.set(bus.id, bus.xy);
|
|
83
78
|
}
|
|
84
|
-
return viewFrom(graph, coords);
|
|
79
|
+
return withRoutes(viewFrom(graph, coords), layer);
|
|
80
|
+
}
|
|
81
|
+
/** Retain native drawing positions; unpositioned buses receive a separate generated layout. */
|
|
82
|
+
export function buildDiagramView(graph, layer) {
|
|
83
|
+
const generated = layoutSynthetic(graph, { lon: 0, lat: 0 });
|
|
84
|
+
const coords = new Map();
|
|
85
|
+
const positioned = graph.buses.filter((bus) => bus.xy?.every(Number.isFinite));
|
|
86
|
+
const offset = positioned.length ? Math.max(...positioned.map((bus) => bus.xy[0])) + 100 : 0;
|
|
87
|
+
for (const bus of graph.buses) {
|
|
88
|
+
if (bus.xy?.every(Number.isFinite))
|
|
89
|
+
coords.set(bus.id, bus.xy);
|
|
90
|
+
else {
|
|
91
|
+
const xy = generated.get(bus.id);
|
|
92
|
+
if (xy)
|
|
93
|
+
coords.set(bus.id, [offset + xy[0] * 100, xy[1] * 100]);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return withRoutes(viewFrom(graph, coords), layer);
|
|
97
|
+
}
|
|
98
|
+
/** Match canonical GeoLayer paths by equipment identity or an unambiguous endpoint pair. */
|
|
99
|
+
function withRoutes(view, layer) {
|
|
100
|
+
if (!layer)
|
|
101
|
+
return view;
|
|
102
|
+
let features;
|
|
103
|
+
try {
|
|
104
|
+
features = JSON.parse(layer).features;
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return view;
|
|
108
|
+
}
|
|
109
|
+
if (!Array.isArray(features))
|
|
110
|
+
return view;
|
|
111
|
+
const routes = new Map();
|
|
112
|
+
const same = (a, b) => typeof a === 'string' && a.toLowerCase() === b.toLowerCase();
|
|
113
|
+
for (const feature of features) {
|
|
114
|
+
const props = feature?.properties;
|
|
115
|
+
const geometry = feature?.geometry;
|
|
116
|
+
if (!props || geometry?.type !== 'LineString' || !Array.isArray(geometry.coordinates))
|
|
117
|
+
continue;
|
|
118
|
+
const points = geometry.coordinates
|
|
119
|
+
.filter((point) => Array.isArray(point) &&
|
|
120
|
+
point.length >= 2 &&
|
|
121
|
+
Number.isFinite(point[0]) &&
|
|
122
|
+
Number.isFinite(point[1]))
|
|
123
|
+
.map((point) => [point[0], point[1]]);
|
|
124
|
+
if (points.length < 2)
|
|
125
|
+
continue;
|
|
126
|
+
const from = props.from ?? props.bus_from;
|
|
127
|
+
const to = props.to ?? props.bus_to;
|
|
128
|
+
const id = props.id ?? props.uid;
|
|
129
|
+
const matches = view.edges.filter((edge) => id !== undefined
|
|
130
|
+
? same(id, edge.id)
|
|
131
|
+
: (same(from, edge.from) && same(to, edge.to)) ||
|
|
132
|
+
(same(from, edge.to) && same(to, edge.from)));
|
|
133
|
+
if (matches.length !== 1)
|
|
134
|
+
continue;
|
|
135
|
+
const edge = matches[0];
|
|
136
|
+
const reverse = same(from, edge.to) && same(to, edge.from);
|
|
137
|
+
routes.set(edge.id, reverse ? points.reverse() : points);
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
...view,
|
|
141
|
+
edges: view.edges.map((edge) => ({ ...edge, path: routes.get(edge.id) ?? edge.path }))
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/** Numeric indices address drawing objects only; source identities remain on each object. */
|
|
145
|
+
export function multiDiagramNetwork(id, name, view) {
|
|
146
|
+
const indexes = new Map(view.buses.map((bus, index) => [bus.id.toLowerCase(), index]));
|
|
147
|
+
return {
|
|
148
|
+
id,
|
|
149
|
+
name,
|
|
150
|
+
base_mva: 1,
|
|
151
|
+
coordinate_space: 'diagram',
|
|
152
|
+
synthetic_coords: false,
|
|
153
|
+
buses: view.buses.map((bus, index) => ({
|
|
154
|
+
id: index,
|
|
155
|
+
uid: bus.id,
|
|
156
|
+
name: bus.id,
|
|
157
|
+
lon: bus.lon,
|
|
158
|
+
lat: bus.lat,
|
|
159
|
+
demand_mw: bus.load_kw / 1000,
|
|
160
|
+
gen_mw: bus.gen_kw / 1000
|
|
161
|
+
})),
|
|
162
|
+
branches: view.edges.map((edge, index) => ({
|
|
163
|
+
id: index,
|
|
164
|
+
uid: edge.id,
|
|
165
|
+
from: indexes.get(edge.from.toLowerCase()),
|
|
166
|
+
to: indexes.get(edge.to.toLowerCase()),
|
|
167
|
+
path: edge.path,
|
|
168
|
+
rate_mw: 0,
|
|
169
|
+
status: edge.closed ? 1 : 0
|
|
170
|
+
}))
|
|
171
|
+
};
|
|
85
172
|
}
|
|
86
173
|
/** Whether at least two buses carry a planar `xy`, enough to fit a layout to. */
|
|
87
174
|
function hasPlanarCoords(graph) {
|
|
@@ -116,10 +203,7 @@ function projectPlanar(graph, center) {
|
|
|
116
203
|
// reads as further north.
|
|
117
204
|
const nx = 0.5 + (bus.xy[0] - cx) * s;
|
|
118
205
|
const ny = 0.5 + (bus.xy[1] - cy) * s;
|
|
119
|
-
coords.set(bus.id, [
|
|
120
|
-
center.lon + (nx - 0.5) * lonSpan,
|
|
121
|
-
center.lat + (ny - 0.5) * latSpan
|
|
122
|
-
]);
|
|
206
|
+
coords.set(bus.id, [center.lon + (nx - 0.5) * lonSpan, center.lat + (ny - 0.5) * latSpan]);
|
|
123
207
|
}
|
|
124
208
|
return coords;
|
|
125
209
|
}
|
|
@@ -188,7 +272,7 @@ export function placeMultiView(graph, center) {
|
|
|
188
272
|
// ---------------------------------------------------------------------------
|
|
189
273
|
/** Rust, green, and steel-blue for the three phases; a warm gray for the
|
|
190
274
|
* neutral/ground return. Distinct in hue and lightness so they survive color
|
|
191
|
-
* vision deficiency and never read as the
|
|
275
|
+
* vision deficiency and never read as the nodal value ramp. */
|
|
192
276
|
const PHASE_A = [194, 86, 75, 255];
|
|
193
277
|
const PHASE_B = [74, 143, 95, 255];
|
|
194
278
|
const PHASE_C = [63, 111, 187, 255];
|
|
@@ -215,6 +299,15 @@ export function phaseColor(terminal) {
|
|
|
215
299
|
export function isPhaseTerminal(terminal) {
|
|
216
300
|
return ['1', '2', '3', 'a', 'b', 'c'].includes(terminal.trim().toLowerCase());
|
|
217
301
|
}
|
|
302
|
+
/** Return the explicit neutral conductor in a terminal map. Other non-phase
|
|
303
|
+
* labels are left alone: an arbitrary terminal name is not evidence that the
|
|
304
|
+
* network has a usable neutral reference. */
|
|
305
|
+
export function neutralTerminal(terminals, declared) {
|
|
306
|
+
if (declared !== undefined)
|
|
307
|
+
return declared && terminals.includes(declared) ? declared : null;
|
|
308
|
+
return (terminals.find((terminal) => ['n', '4', 'neutral'].includes(terminal.trim().toLowerCase())) ??
|
|
309
|
+
null);
|
|
310
|
+
}
|
|
218
311
|
/** Base color for an edge by kind and state. Lines are the warm gray of the
|
|
219
312
|
* single-phase branches; transformers are steel; a switch is amber when closed
|
|
220
313
|
* and a faded red when open. */
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface NoticeInput {
|
|
2
|
+
title: string;
|
|
3
|
+
details: string;
|
|
4
|
+
kind?: 'error' | 'warning';
|
|
5
|
+
retry?: () => void;
|
|
6
|
+
canRetry?: () => boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface Notice extends NoticeInput {
|
|
9
|
+
id: number;
|
|
10
|
+
count: number;
|
|
11
|
+
updatedAt: number;
|
|
12
|
+
}
|
|
13
|
+
/** Recent messages stay in memory; dismissal hides the temporary notice only. */
|
|
14
|
+
export declare class NoticeCenter {
|
|
15
|
+
#private;
|
|
16
|
+
entries: Notice[];
|
|
17
|
+
activeId: number | null;
|
|
18
|
+
expanded: boolean;
|
|
19
|
+
announcement: string;
|
|
20
|
+
get active(): Notice | null;
|
|
21
|
+
push(input: NoticeInput): void;
|
|
22
|
+
pause(value: boolean): void;
|
|
23
|
+
dismiss(): void;
|
|
24
|
+
clear(): void;
|
|
25
|
+
dispose(): void;
|
|
26
|
+
}
|
|
27
|
+
export declare const setNoticeCenter: (value: NoticeCenter) => NoticeCenter;
|
|
28
|
+
export declare function getNoticeCenter(): NoticeCenter;
|
|
29
|
+
export declare function errorNoticeTitle(details: string): string;
|