@tellegen/svelte 0.4.2 → 0.4.4
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 +20 -0
- package/dist/TellegenMap.svelte +63 -22
- package/dist/TellegenProvider.svelte +7 -4
- package/dist/TellegenShell.svelte +10 -1
- package/dist/TellegenViewer.svelte +2 -0
- package/dist/basemap.d.ts +14 -0
- package/dist/basemap.js +27 -0
- package/dist/colors.d.ts +6 -1
- package/dist/colors.js +11 -4
- package/dist/components/DemandSlider.svelte +0 -2
- package/dist/components/FormulationSelector.svelte +1 -1
- package/dist/components/LocalCaseDetails.svelte +18 -3
- package/dist/components/McResults.svelte +18 -3
- package/dist/components/McWorkbench.svelte +688 -0
- package/dist/components/McWorkbench.svelte.d.ts +38 -0
- package/dist/components/MultiWire.svelte +136 -0
- package/dist/components/MultiWire.svelte.d.ts +7 -0
- package/dist/components/MulticonductorDetails.svelte +7 -0
- package/dist/components/OmniSearch.svelte +163 -0
- package/dist/components/OmniSearch.svelte.d.ts +14 -0
- package/dist/components/ResultsTable.svelte +259 -0
- package/dist/components/ResultsTable.svelte.d.ts +13 -0
- package/dist/components/SchematicCanvas.svelte +1896 -0
- package/dist/components/SchematicCanvas.svelte.d.ts +13 -0
- package/dist/components/SolveCard.svelte +6 -0
- package/dist/components/schematic/PickCard.svelte +126 -0
- package/dist/components/schematic/PickCard.svelte.d.ts +56 -0
- package/dist/components/schematic/export.d.ts +21 -0
- package/dist/components/schematic/export.js +110 -0
- package/dist/components/schematic/layers.d.ts +161 -0
- package/dist/components/schematic/layers.js +756 -0
- package/dist/components/schematic/paint.d.ts +28 -0
- package/dist/components/schematic/paint.js +69 -0
- package/dist/components/schematic/palette.d.ts +34 -0
- package/dist/components/schematic/palette.js +54 -0
- package/dist/components/schematic/text.d.ts +83 -0
- package/dist/components/schematic/text.js +190 -0
- package/dist/components/schematic/view-prefs.svelte.d.ts +30 -0
- package/dist/components/schematic/view-prefs.svelte.js +77 -0
- package/dist/components.d.ts +1 -0
- package/dist/components.js +1 -0
- package/dist/context.svelte.d.ts +2 -0
- package/dist/context.svelte.js +2 -0
- package/dist/controller.svelte.d.ts +33 -5
- package/dist/controller.svelte.js +173 -20
- package/dist/diagram/basemap.d.ts +28 -0
- package/dist/diagram/basemap.js +39 -0
- package/dist/diagram/bus-stacks.d.ts +19 -0
- package/dist/diagram/bus-stacks.js +99 -0
- package/dist/diagram/colour-modes.d.ts +18 -0
- package/dist/diagram/colour-modes.js +33 -0
- package/dist/diagram/connections.d.ts +30 -0
- package/dist/diagram/connections.js +129 -0
- package/dist/diagram/display-tree.d.ts +21 -0
- package/dist/diagram/display-tree.js +65 -0
- package/dist/diagram/energisation.d.ts +10 -0
- package/dist/diagram/energisation.js +31 -0
- package/dist/diagram/feeders.d.ts +30 -0
- package/dist/diagram/feeders.js +57 -0
- package/dist/diagram/galvanic-zones.d.ts +60 -0
- package/dist/diagram/galvanic-zones.js +210 -0
- package/dist/diagram/graph-inputs.d.ts +18 -0
- package/dist/diagram/graph-inputs.js +103 -0
- package/dist/diagram/index.d.ts +28 -0
- package/dist/diagram/index.js +28 -0
- package/dist/diagram/landmarks.d.ts +50 -0
- package/dist/diagram/landmarks.js +153 -0
- package/dist/diagram/layout-morph.d.ts +15 -0
- package/dist/diagram/layout-morph.js +32 -0
- package/dist/diagram/multi-wire.d.ts +44 -0
- package/dist/diagram/multi-wire.js +65 -0
- package/dist/diagram/phase-bars.d.ts +35 -0
- package/dist/diagram/phase-bars.js +44 -0
- package/dist/diagram/results-table.d.ts +47 -0
- package/dist/diagram/results-table.js +173 -0
- package/dist/diagram/schematic-equipment.d.ts +133 -0
- package/dist/diagram/schematic-equipment.js +645 -0
- package/dist/diagram/schematic-layout.d.ts +51 -0
- package/dist/diagram/schematic-layout.js +240 -0
- package/dist/diagram/search.d.ts +36 -0
- package/dist/diagram/search.js +48 -0
- package/dist/diagram/shared-routes.d.ts +21 -0
- package/dist/diagram/shared-routes.js +40 -0
- package/dist/diagram/short-names.d.ts +11 -0
- package/dist/diagram/short-names.js +54 -0
- package/dist/diagram/snapshot.d.ts +115 -0
- package/dist/diagram/snapshot.js +242 -0
- package/dist/diagram/summary.d.ts +65 -0
- package/dist/diagram/summary.js +144 -0
- package/dist/diagram/svg-export.d.ts +80 -0
- package/dist/diagram/svg-export.js +108 -0
- package/dist/diagram/tree-walk.d.ts +13 -0
- package/dist/diagram/tree-walk.js +83 -0
- package/dist/diagram/value-labels.d.ts +24 -0
- package/dist/diagram/value-labels.js +69 -0
- package/dist/diagram/view-state.d.ts +12 -0
- package/dist/diagram/view-state.js +22 -0
- package/dist/diagram/voltage-levels.d.ts +14 -0
- package/dist/diagram/voltage-levels.js +20 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/state.svelte.d.ts +37 -1
- package/dist/state.svelte.js +35 -0
- package/dist/transformer-icon.d.ts +22 -13
- package/dist/transformer-icon.js +23 -13
- package/package.json +2 -2
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { priceColor, sensColor } from '../colors.js';
|
|
2
|
+
export const COLOUR_MODES = {
|
|
3
|
+
none: { label: 'Equipment / topology', continuous: false },
|
|
4
|
+
voltage: { label: 'Voltage limits', continuous: false },
|
|
5
|
+
'voltage-band': { label: 'Voltage within band', continuous: true },
|
|
6
|
+
loading: { label: 'Loading thresholds', continuous: false },
|
|
7
|
+
'loading-heat': { label: 'Loading (continuous)', continuous: true }
|
|
8
|
+
};
|
|
9
|
+
/** Threshold and continuous variants of one quantity share issue focus, warnings and presets. */
|
|
10
|
+
export function colourFamily(mode) {
|
|
11
|
+
return mode.startsWith('voltage') ? 'voltage' : mode.startsWith('loading') ? 'loading' : 'none';
|
|
12
|
+
}
|
|
13
|
+
/** Position in the declared band: −1 at min, 0 mid-band, +1 at max; beyond ±1 is a violation. */
|
|
14
|
+
export function bandPosition(p) {
|
|
15
|
+
if (p.min === null || p.max === null || !(p.max > p.min))
|
|
16
|
+
return null;
|
|
17
|
+
return (2 * p.volts - p.min - p.max) / (p.max - p.min);
|
|
18
|
+
}
|
|
19
|
+
/** The pair nearest a limit decides a bus colour, so a violation can never be averaged away. */
|
|
20
|
+
export function worstBand(pairs) {
|
|
21
|
+
let worst = null;
|
|
22
|
+
for (const p of pairs) {
|
|
23
|
+
const t = bandPosition(p);
|
|
24
|
+
if (t !== null && (worst === null || Math.abs(t) > Math.abs(worst)))
|
|
25
|
+
worst = t;
|
|
26
|
+
}
|
|
27
|
+
return worst;
|
|
28
|
+
}
|
|
29
|
+
/** The application's sequential ramp; light loads start part-way up so they stay visible on paper. */
|
|
30
|
+
export const loadingColour = (loading) => priceColor(0.15 + 0.85 * Math.min(1, Math.max(0, loading / 100)));
|
|
31
|
+
/** The application's diverging ramp over the declared band, clamped at the limits. */
|
|
32
|
+
export const bandColour = (t) => sensColor(Math.max(-1, Math.min(1, t)));
|
|
33
|
+
export const MISSING_COLOUR = [128, 128, 128, 170];
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { DistConnection, DistGraph, DistWindingDetail } from '@tellegen/engine';
|
|
2
|
+
import type { Glyph } from './schematic-equipment.js';
|
|
3
|
+
/** How a transformer is built, as the drawing tells it: single-phase, split-phase (a centre-tapped
|
|
4
|
+
* secondary: PowerIO models its halves as two windings on one bus), or polyphase. */
|
|
5
|
+
export type TransformerKind = 'single' | 'split' | 'poly';
|
|
6
|
+
export interface TransformerInfo {
|
|
7
|
+
kind: TransformerKind;
|
|
8
|
+
/** The windings at the edge's from and to buses (the first, for a split secondary). */
|
|
9
|
+
from: DistWindingDetail | null;
|
|
10
|
+
to: DistWindingDetail | null;
|
|
11
|
+
/** "Dyn · 500 kVA", "1φ split · 25 kVA": vector group without its clock number, and rating. */
|
|
12
|
+
label: string;
|
|
13
|
+
}
|
|
14
|
+
/** What kind of transformer edge `k` belongs to, and its windings at either end. Null when the
|
|
15
|
+
* edge carries no winding detail (a graph from before connections were exported). */
|
|
16
|
+
export declare function transformerInfo(graph: DistGraph, k: number): TransformerInfo | null;
|
|
17
|
+
/** A winding connection mark centred on the origin, `r` across: Δ, Y, or Y with a neutral tail. */
|
|
18
|
+
export declare function connectionMark(w: DistWindingDetail, phases: number, r: number): Glyph;
|
|
19
|
+
/** The transformer symbol: two overlapping winding circles on the line. A polyphase unit carries
|
|
20
|
+
* each winding's connection inside its circle; a split-phase unit a tap brought out below its
|
|
21
|
+
* secondary; a single-phase unit plain circles. The from winding is drawn at −x. */
|
|
22
|
+
export declare function transformerGlyph(info: TransformerInfo | null, phases: number, leads?: boolean): Glyph;
|
|
23
|
+
/** Terminals as a compact label: "b–n" across two terminals, else the names run together when all
|
|
24
|
+
* are single characters ("123n") and dotted otherwise; a wye or delta connection leads ("Y 123n"). */
|
|
25
|
+
export declare function terminalLabel(terminals: string[], connection?: DistConnection): string;
|
|
26
|
+
/** Phase conductors and whether the neutral is among `terminals`, at a bus with `neutral`. */
|
|
27
|
+
export declare function terminalConductors(terminals: string[], neutral: string | null | undefined): {
|
|
28
|
+
phases: number;
|
|
29
|
+
neutral: boolean;
|
|
30
|
+
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
const ring = (cx, cy, r, steps = 20) => Array.from({ length: steps + 1 }, (_, i) => {
|
|
2
|
+
const a = (2 * Math.PI * i) / steps;
|
|
3
|
+
return [cx + r * Math.cos(a), cy + r * Math.sin(a)];
|
|
4
|
+
});
|
|
5
|
+
const sameBus = (a, b) => a.toLowerCase() === b.toLowerCase();
|
|
6
|
+
function formatVa(va) {
|
|
7
|
+
if (!(va > 0))
|
|
8
|
+
return '';
|
|
9
|
+
return va >= 1e6
|
|
10
|
+
? `${Number((va / 1e6).toPrecision(3))} MVA`
|
|
11
|
+
: `${Number((va / 1e3).toPrecision(3))} kVA`;
|
|
12
|
+
}
|
|
13
|
+
/** A winding's IEC letter: D or Y, N when its neutral is brought out; lower case for the lower
|
|
14
|
+
* voltage side. */
|
|
15
|
+
function windingLetters(w, phases, high) {
|
|
16
|
+
const letter = w.connection === 'delta' ? 'D' : w.connection === 'wye' ? 'Y' : '?';
|
|
17
|
+
const neutral = w.connection === 'wye' && w.terminals.length > phases ? 'N' : '';
|
|
18
|
+
const out = letter + neutral;
|
|
19
|
+
return high ? out : out.toLowerCase();
|
|
20
|
+
}
|
|
21
|
+
/** What kind of transformer edge `k` belongs to, and its windings at either end. Null when the
|
|
22
|
+
* edge carries no winding detail (a graph from before connections were exported). */
|
|
23
|
+
export function transformerInfo(graph, k) {
|
|
24
|
+
const edge = graph.edges[k];
|
|
25
|
+
const windings = edge.windings;
|
|
26
|
+
if (edge.kind !== 'transformer' || !windings?.length)
|
|
27
|
+
return null;
|
|
28
|
+
const phases = edge.n_phases;
|
|
29
|
+
const shared = windings.some((w, i) => windings.some((v, j) => j > i && sameBus(w.bus, v.bus)));
|
|
30
|
+
const kind = phases >= 2 ? 'poly' : shared ? 'split' : 'single';
|
|
31
|
+
const from = windings.find((w) => sameBus(w.bus, edge.from)) ?? null;
|
|
32
|
+
const to = windings.find((w) => sameBus(w.bus, edge.to) && w !== from) ??
|
|
33
|
+
windings.find((w) => sameBus(w.bus, edge.to)) ??
|
|
34
|
+
null;
|
|
35
|
+
const rating = formatVa(Math.max(...windings.map((w) => w.s_rating)));
|
|
36
|
+
let group;
|
|
37
|
+
if (kind === 'poly') {
|
|
38
|
+
const sorted = [...new Set([from, to].filter((w) => !!w))].sort((a, b) => b.v_ref - a.v_ref);
|
|
39
|
+
group = sorted.map((w, i) => windingLetters(w, phases, i === 0)).join('');
|
|
40
|
+
}
|
|
41
|
+
else
|
|
42
|
+
group = kind === 'split' ? '1φ split' : '1φ';
|
|
43
|
+
return { kind, from, to, label: rating ? `${group} · ${rating}` : group };
|
|
44
|
+
}
|
|
45
|
+
/** A winding connection mark centred on the origin, `r` across: Δ, Y, or Y with a neutral tail. */
|
|
46
|
+
export function connectionMark(w, phases, r) {
|
|
47
|
+
const h = 0.866 * r;
|
|
48
|
+
if (w.connection === 'delta')
|
|
49
|
+
return [
|
|
50
|
+
[
|
|
51
|
+
[0, -r],
|
|
52
|
+
[h, r / 2],
|
|
53
|
+
[-h, r / 2],
|
|
54
|
+
[0, -r]
|
|
55
|
+
]
|
|
56
|
+
];
|
|
57
|
+
if (w.connection !== 'wye')
|
|
58
|
+
return [];
|
|
59
|
+
const y = [
|
|
60
|
+
[
|
|
61
|
+
[0, 0],
|
|
62
|
+
[0, -r]
|
|
63
|
+
],
|
|
64
|
+
[
|
|
65
|
+
[0, 0],
|
|
66
|
+
[h, r / 2]
|
|
67
|
+
],
|
|
68
|
+
[
|
|
69
|
+
[0, 0],
|
|
70
|
+
[-h, r / 2]
|
|
71
|
+
]
|
|
72
|
+
];
|
|
73
|
+
if (w.terminals.length > phases)
|
|
74
|
+
y.push([
|
|
75
|
+
[0, 0],
|
|
76
|
+
[0, r * 1.15]
|
|
77
|
+
]);
|
|
78
|
+
return y;
|
|
79
|
+
}
|
|
80
|
+
/** The transformer symbol: two overlapping winding circles on the line. A polyphase unit carries
|
|
81
|
+
* each winding's connection inside its circle; a split-phase unit a tap brought out below its
|
|
82
|
+
* secondary; a single-phase unit plain circles. The from winding is drawn at −x. */
|
|
83
|
+
export function transformerGlyph(info, phases, leads = true) {
|
|
84
|
+
const circles = [ring(-0.45, 0, 0.7), ring(0.45, 0, 0.7)];
|
|
85
|
+
const out = leads
|
|
86
|
+
? [
|
|
87
|
+
[
|
|
88
|
+
[-1.4, 0],
|
|
89
|
+
[-1.15, 0]
|
|
90
|
+
],
|
|
91
|
+
...circles,
|
|
92
|
+
[
|
|
93
|
+
[1.15, 0],
|
|
94
|
+
[1.4, 0]
|
|
95
|
+
]
|
|
96
|
+
]
|
|
97
|
+
: circles;
|
|
98
|
+
if (!info)
|
|
99
|
+
return out;
|
|
100
|
+
const place = (glyph, dx) => glyph.map((line) => line.map(([x, y]) => [x + dx, y]));
|
|
101
|
+
if (info.kind === 'poly') {
|
|
102
|
+
if (info.from)
|
|
103
|
+
out.push(...place(connectionMark(info.from, phases, 0.3), -0.68));
|
|
104
|
+
if (info.to)
|
|
105
|
+
out.push(...place(connectionMark(info.to, phases, 0.3), 0.68));
|
|
106
|
+
}
|
|
107
|
+
else if (info.kind === 'split')
|
|
108
|
+
out.push([
|
|
109
|
+
[0.45, 0.7],
|
|
110
|
+
[0.45, 1.05]
|
|
111
|
+
], ring(0.45, 1.13, 0.08, 8));
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
/** Terminals as a compact label: "b–n" across two terminals, else the names run together when all
|
|
115
|
+
* are single characters ("123n") and dotted otherwise; a wye or delta connection leads ("Y 123n"). */
|
|
116
|
+
export function terminalLabel(terminals, connection) {
|
|
117
|
+
if (!terminals.length)
|
|
118
|
+
return '';
|
|
119
|
+
if (terminals.length === 2 && connection !== 'wye' && connection !== 'delta')
|
|
120
|
+
return `${terminals[0]}–${terminals[1]}`;
|
|
121
|
+
const names = terminals.every((t) => t.length === 1) ? terminals.join('') : terminals.join('·');
|
|
122
|
+
const lead = connection === 'wye' ? 'Y ' : connection === 'delta' ? 'Δ ' : '';
|
|
123
|
+
return lead + names;
|
|
124
|
+
}
|
|
125
|
+
/** Phase conductors and whether the neutral is among `terminals`, at a bus with `neutral`. */
|
|
126
|
+
export function terminalConductors(terminals, neutral) {
|
|
127
|
+
const hasNeutral = !!neutral && terminals.includes(neutral);
|
|
128
|
+
return { phases: terminals.length - (hasNeutral ? 1 : 0), neutral: hasNeutral };
|
|
129
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { DistGraph } from '@tellegen/engine';
|
|
2
|
+
/** A breadth-first spanning forest over closed edges, rooted at source buses. It orders the drawing
|
|
3
|
+
* and the voltage profile; it is not a claim about a unique electrical path through a mesh. */
|
|
4
|
+
export interface DisplayTree {
|
|
5
|
+
/** Parent bus index; a root is its own parent. Unreached buses are their own root too. */
|
|
6
|
+
parent: Int32Array;
|
|
7
|
+
/** Edge index from the parent, or −1 for roots. */
|
|
8
|
+
parentEdge: Int32Array;
|
|
9
|
+
depth: Int32Array;
|
|
10
|
+
/** Component index, one per root. */
|
|
11
|
+
component: Int32Array;
|
|
12
|
+
roots: number[];
|
|
13
|
+
}
|
|
14
|
+
/** Roots are source buses in graph order; any bus a source cannot reach starts its own component,
|
|
15
|
+
* so every bus has a depth and nothing is silently dropped. Open switches are not traversed. */
|
|
16
|
+
export declare function displayTree(graph: DistGraph): DisplayTree;
|
|
17
|
+
/** Root-to-bus path as bus and edge indices, root first. */
|
|
18
|
+
export declare function treePath(tree: DisplayTree, bus: number): {
|
|
19
|
+
buses: number[];
|
|
20
|
+
edges: number[];
|
|
21
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/** Roots are source buses in graph order; any bus a source cannot reach starts its own component,
|
|
2
|
+
* so every bus has a depth and nothing is silently dropped. Open switches are not traversed. */
|
|
3
|
+
export function displayTree(graph) {
|
|
4
|
+
const n = graph.buses.length;
|
|
5
|
+
const index = new Map(graph.buses.map((b, i) => [b.id.toLowerCase(), i]));
|
|
6
|
+
const adjacency = Array.from({ length: n }, () => []);
|
|
7
|
+
graph.edges.forEach((e, k) => {
|
|
8
|
+
const a = index.get(e.from.toLowerCase());
|
|
9
|
+
const b = index.get(e.to.toLowerCase());
|
|
10
|
+
if (!e.closed || a === undefined || b === undefined || a === b)
|
|
11
|
+
return;
|
|
12
|
+
adjacency[a].push({ to: b, edge: k });
|
|
13
|
+
adjacency[b].push({ to: a, edge: k });
|
|
14
|
+
});
|
|
15
|
+
const parent = new Int32Array(n).fill(-1);
|
|
16
|
+
const parentEdge = new Int32Array(n).fill(-1);
|
|
17
|
+
const depth = new Int32Array(n).fill(-1);
|
|
18
|
+
const component = new Int32Array(n).fill(-1);
|
|
19
|
+
const roots = [];
|
|
20
|
+
const queue = new Int32Array(n);
|
|
21
|
+
const grow = (root) => {
|
|
22
|
+
const c = roots.length;
|
|
23
|
+
roots.push(root);
|
|
24
|
+
parent[root] = root;
|
|
25
|
+
depth[root] = 0;
|
|
26
|
+
component[root] = c;
|
|
27
|
+
let head = 0;
|
|
28
|
+
let tail = 0;
|
|
29
|
+
queue[tail++] = root;
|
|
30
|
+
while (head < tail) {
|
|
31
|
+
const u = queue[head++];
|
|
32
|
+
for (const { to, edge } of adjacency[u]) {
|
|
33
|
+
if (depth[to] >= 0)
|
|
34
|
+
continue;
|
|
35
|
+
parent[to] = u;
|
|
36
|
+
parentEdge[to] = edge;
|
|
37
|
+
depth[to] = depth[u] + 1;
|
|
38
|
+
component[to] = c;
|
|
39
|
+
queue[tail++] = to;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
graph.buses.forEach((b, i) => {
|
|
44
|
+
if (b.has_source && depth[i] < 0)
|
|
45
|
+
grow(i);
|
|
46
|
+
});
|
|
47
|
+
for (let i = 0; i < n; i++)
|
|
48
|
+
if (depth[i] < 0)
|
|
49
|
+
grow(i);
|
|
50
|
+
return { parent, parentEdge, depth, component, roots };
|
|
51
|
+
}
|
|
52
|
+
/** Root-to-bus path as bus and edge indices, root first. */
|
|
53
|
+
export function treePath(tree, bus) {
|
|
54
|
+
const buses = [bus];
|
|
55
|
+
const edges = [];
|
|
56
|
+
let u = bus;
|
|
57
|
+
while (tree.parent[u] !== u) {
|
|
58
|
+
edges.push(tree.parentEdge[u]);
|
|
59
|
+
u = tree.parent[u];
|
|
60
|
+
buses.push(u);
|
|
61
|
+
if (buses.length > tree.parent.length)
|
|
62
|
+
throw new Error('Display tree has a cycle');
|
|
63
|
+
}
|
|
64
|
+
return { buses: buses.reverse(), edges: edges.reverse() };
|
|
65
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { DistGraph } from '@tellegen/engine';
|
|
2
|
+
/** Which buses and branches are connected to a source through closed branches. Transformers carry
|
|
3
|
+
* energisation; an open switch is live when either side is. */
|
|
4
|
+
export interface Energisation {
|
|
5
|
+
buses: Uint8Array;
|
|
6
|
+
edges: Uint8Array;
|
|
7
|
+
/** Buses not reached from any source. */
|
|
8
|
+
deadBuses: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function energisation(graph: DistGraph): Energisation;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export function energisation(graph) {
|
|
2
|
+
const n = graph.buses.length;
|
|
3
|
+
const index = new Map(graph.buses.map((b, i) => [b.id.toLowerCase(), i]));
|
|
4
|
+
const ends = graph.edges.map((e) => [
|
|
5
|
+
index.get(e.from.toLowerCase()) ?? -1,
|
|
6
|
+
index.get(e.to.toLowerCase()) ?? -1
|
|
7
|
+
]);
|
|
8
|
+
const adjacent = Array.from({ length: n }, () => []);
|
|
9
|
+
ends.forEach(([a, b], k) => {
|
|
10
|
+
if (a < 0 || b < 0 || !graph.edges[k].closed)
|
|
11
|
+
return;
|
|
12
|
+
adjacent[a].push(b);
|
|
13
|
+
adjacent[b].push(a);
|
|
14
|
+
});
|
|
15
|
+
const buses = new Uint8Array(n);
|
|
16
|
+
const queue = [];
|
|
17
|
+
graph.buses.forEach((b, i) => {
|
|
18
|
+
if (b.has_source) {
|
|
19
|
+
buses[i] = 1;
|
|
20
|
+
queue.push(i);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
for (let head = 0; head < queue.length; head++)
|
|
24
|
+
for (const j of adjacent[queue[head]])
|
|
25
|
+
if (!buses[j]) {
|
|
26
|
+
buses[j] = 1;
|
|
27
|
+
queue.push(j);
|
|
28
|
+
}
|
|
29
|
+
const edges = Uint8Array.from(ends, ([a, b]) => (buses[a] || buses[b] ? 1 : 0));
|
|
30
|
+
return { buses, edges, deadBuses: n - queue.length };
|
|
31
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { DistGraph } from '@tellegen/engine';
|
|
2
|
+
import type { DisplayTree } from './display-tree.js';
|
|
3
|
+
import type { GalvanicZones } from './galvanic-zones.js';
|
|
4
|
+
/** A feeder: everything a network supplies through one branch leaving its supply bus. On MV that
|
|
5
|
+
* is a feeder leaving the substation bus; on LV a circuit leaving the transformer's LV bus. Read
|
|
6
|
+
* from the display tree, so through a closed loop it is the drawn feeder, not a unique one. */
|
|
7
|
+
export interface Feeder {
|
|
8
|
+
/** The galvanic zone (network) it belongs to. */
|
|
9
|
+
zone: number;
|
|
10
|
+
/** The branch leaving the supply bus. */
|
|
11
|
+
head: number;
|
|
12
|
+
/** The supply bus: a source bus, or the bus a transformer feeds the network at. */
|
|
13
|
+
supply: number;
|
|
14
|
+
/** Its number among the feeders leaving the same supply bus, from 1, in graph order. */
|
|
15
|
+
number: number;
|
|
16
|
+
buses: number[];
|
|
17
|
+
edges: number[];
|
|
18
|
+
}
|
|
19
|
+
export interface Feeders {
|
|
20
|
+
feeders: Feeder[];
|
|
21
|
+
/** Per bus: its feeder, or −1 for a supply bus (it belongs to its network, not one feeder). */
|
|
22
|
+
busFeeder: Int32Array;
|
|
23
|
+
/** Per edge: its feeder. A transformer is equipment on the feeder upstream of it; −1 where none. */
|
|
24
|
+
edgeFeeder: Int32Array;
|
|
25
|
+
}
|
|
26
|
+
/** Feeders of every network. A bus starts a feeder when its tree parent is a supply bus (a root, or
|
|
27
|
+
* entered through a transformer) and the branch between them is not a transformer. */
|
|
28
|
+
export declare function feeders(graph: DistGraph, tree: DisplayTree, zones: GalvanicZones): Feeders;
|
|
29
|
+
/** Edges on the drawn path from bus `i` back to its source, across transformers. */
|
|
30
|
+
export declare function pathToSource(tree: DisplayTree, i: number): number[];
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/** Feeders of every network. A bus starts a feeder when its tree parent is a supply bus (a root, or
|
|
2
|
+
* entered through a transformer) and the branch between them is not a transformer. */
|
|
3
|
+
export function feeders(graph, tree, zones) {
|
|
4
|
+
const n = graph.buses.length;
|
|
5
|
+
const index = new Map(graph.buses.map((b, i) => [b.id.toLowerCase(), i]));
|
|
6
|
+
const busFeeder = new Int32Array(n).fill(-1);
|
|
7
|
+
const edgeFeeder = new Int32Array(graph.edges.length).fill(-1);
|
|
8
|
+
const list = [];
|
|
9
|
+
const isSupply = (i) => {
|
|
10
|
+
const k = tree.parentEdge[i];
|
|
11
|
+
return k < 0 || graph.edges[k].kind === 'transformer';
|
|
12
|
+
};
|
|
13
|
+
// Parents before children: buses in order of depth.
|
|
14
|
+
const order = Array.from({ length: n }, (_, i) => i).sort((a, b) => tree.depth[a] - tree.depth[b]);
|
|
15
|
+
const perSupply = new Map();
|
|
16
|
+
for (const i of order) {
|
|
17
|
+
if (isSupply(i))
|
|
18
|
+
continue;
|
|
19
|
+
const p = tree.parent[i];
|
|
20
|
+
if (isSupply(p)) {
|
|
21
|
+
const number = (perSupply.get(p) ?? 0) + 1;
|
|
22
|
+
perSupply.set(p, number);
|
|
23
|
+
busFeeder[i] = list.length;
|
|
24
|
+
list.push({
|
|
25
|
+
zone: zones.busZone[i],
|
|
26
|
+
head: tree.parentEdge[i],
|
|
27
|
+
supply: p,
|
|
28
|
+
number,
|
|
29
|
+
buses: [],
|
|
30
|
+
edges: []
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
else
|
|
34
|
+
busFeeder[i] = busFeeder[p];
|
|
35
|
+
list[busFeeder[i]].buses.push(i);
|
|
36
|
+
}
|
|
37
|
+
graph.edges.forEach((e, k) => {
|
|
38
|
+
const a = index.get(e.from.toLowerCase()) ?? -1;
|
|
39
|
+
const b = index.get(e.to.toLowerCase()) ?? -1;
|
|
40
|
+
if (a < 0 || b < 0)
|
|
41
|
+
return;
|
|
42
|
+
const [up, down] = tree.depth[a] <= tree.depth[b] ? [a, b] : [b, a];
|
|
43
|
+
// A transformer sits on the feeder that reaches it; a branch belongs where its far end does.
|
|
44
|
+
const f = e.kind === 'transformer' ? busFeeder[up] : busFeeder[down];
|
|
45
|
+
edgeFeeder[k] = f;
|
|
46
|
+
if (f >= 0)
|
|
47
|
+
list[f].edges.push(k);
|
|
48
|
+
});
|
|
49
|
+
return { feeders: list, busFeeder, edgeFeeder };
|
|
50
|
+
}
|
|
51
|
+
/** Edges on the drawn path from bus `i` back to its source, across transformers. */
|
|
52
|
+
export function pathToSource(tree, i) {
|
|
53
|
+
const out = [];
|
|
54
|
+
for (let at = i; at >= 0 && tree.parentEdge[at] >= 0; at = tree.parent[at])
|
|
55
|
+
out.push(tree.parentEdge[at]);
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { DistGraph } from '@tellegen/engine';
|
|
2
|
+
import type { DisplayTree } from './display-tree.js';
|
|
3
|
+
import type { ResultSnapshot } from './snapshot.js';
|
|
4
|
+
/** A galvanic zone: buses joined by lines and closed switches, separated from the rest only by
|
|
5
|
+
* transformers. On a distribution feeder the zones without a source are the LV networks. */
|
|
6
|
+
export interface GalvanicZone {
|
|
7
|
+
buses: number[];
|
|
8
|
+
/** Branches inside the zone. */
|
|
9
|
+
edges: number[];
|
|
10
|
+
/** Transformers with a winding in the zone; the feeding one first when the display tree has it. */
|
|
11
|
+
transformers: number[];
|
|
12
|
+
hasSource: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface GalvanicZones {
|
|
15
|
+
zones: GalvanicZone[];
|
|
16
|
+
/** Zone index per bus. */
|
|
17
|
+
busZone: Int32Array;
|
|
18
|
+
}
|
|
19
|
+
/** Connected components over closed non-transformer branches. */
|
|
20
|
+
export declare function galvanicZones(graph: DistGraph, tree?: DisplayTree): GalvanicZones;
|
|
21
|
+
export type ZoneState = 'normal' | 'warning' | 'violation' | 'missing';
|
|
22
|
+
/** A zone's worst state: a voltage outside its band or a branch over its rating is a violation,
|
|
23
|
+
* loading at or above the warning threshold a warning. Its transformers count. */
|
|
24
|
+
export declare function zoneState(zone: GalvanicZone, snapshot: ResultSnapshot | null): ZoneState;
|
|
25
|
+
/** The outline of a zone in a schematic layout: one band per drawing column (depth), from the
|
|
26
|
+
* zone's highest bus to the lowest attachment symbol below it, joined into a staircase. Tidy and
|
|
27
|
+
* leaf-order layouts keep each subtree's rows contiguous per column, so neighbouring zones'
|
|
28
|
+
* outlines do not overlap; a zone nested inside another sits inside its outline. */
|
|
29
|
+
export declare function zoneOutline(zone: GalvanicZone, positions: ArrayLike<number>, below: ArrayLike<number>, options: {
|
|
30
|
+
dx: number;
|
|
31
|
+
pad: number;
|
|
32
|
+
}): [number, number][];
|
|
33
|
+
/** A zone's corridor, for source positions: the segments of its own branches and its buses, to be
|
|
34
|
+
* drawn as a band of fixed screen width. Unlike a hull it covers only where the network runs, so
|
|
35
|
+
* networks that interleave along streets stay apart; every bus belongs to exactly one zone. */
|
|
36
|
+
export interface ZoneCorridor {
|
|
37
|
+
z: number;
|
|
38
|
+
/** Segment indices into the drawing's sources, targets and segmentEdge. */
|
|
39
|
+
segments: number[];
|
|
40
|
+
buses: number[];
|
|
41
|
+
/** Bounding box of its buses: [x0, y0, x1, y1]. */
|
|
42
|
+
bounds: [number, number, number, number];
|
|
43
|
+
}
|
|
44
|
+
export declare function zoneCorridors(zones: GalvanicZones, geometry: {
|
|
45
|
+
positions: ArrayLike<number>;
|
|
46
|
+
segmentEdge: ArrayLike<number>;
|
|
47
|
+
}, include: (zone: GalvanicZone) => boolean): ZoneCorridor[];
|
|
48
|
+
/** A zone's voltage level in volts: the declared rating of a winding or source in it (the
|
|
49
|
+
* largest), else its declared limits (midpoint of the band; a phase–neutral or phase–ground band
|
|
50
|
+
* times √3), else its solved voltages, as the median over its buses. Null when none is known. */
|
|
51
|
+
export declare function zoneVoltage(zone: GalvanicZone, graph: DistGraph, snapshot: ResultSnapshot | null): number | null;
|
|
52
|
+
/** `mv` from 1 kV phase–phase up (drawn heavier), else `lv`. Without a known voltage the source
|
|
53
|
+
* zone counts as `mv`, others as `lv`. */
|
|
54
|
+
export declare function zoneLevel(zone: GalvanicZone, volts: number | null): 'mv' | 'lv';
|
|
55
|
+
/** One-line zone summary for its label: lowest phase–neutral (else phase–ground, else
|
|
56
|
+
* phase–phase) voltage and highest loading among its branches and transformers, when solved. */
|
|
57
|
+
export declare function zoneSummary(zone: GalvanicZone, snapshot: ResultSnapshot | null): {
|
|
58
|
+
minVolts: number | null;
|
|
59
|
+
maxLoading: number | null;
|
|
60
|
+
};
|