@tellegen/svelte 0.4.7 → 0.5.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/dist/components/DiagramCanvas.svelte +5 -1
- package/dist/components/McModelQualification.svelte +67 -0
- package/dist/components/McModelQualification.svelte.d.ts +8 -0
- package/dist/components/McNetworkScope.svelte +90 -0
- package/dist/components/McNetworkScope.svelte.d.ts +9 -0
- package/dist/components/McPfSettings.svelte +158 -0
- package/dist/components/McPfSettings.svelte.d.ts +9 -0
- package/dist/components/McResults.svelte +16 -1
- package/dist/components/McResults.svelte.d.ts +1 -0
- package/dist/components/McWorkbench.svelte +2 -0
- package/dist/components/MulticonductorDetails.svelte +25 -6
- package/dist/components.d.ts +3 -0
- package/dist/components.js +3 -0
- package/dist/controller.svelte.d.ts +9 -5
- package/dist/controller.svelte.js +83 -18
- package/dist/diagram/schematic-equipment.d.ts +4 -3
- package/dist/diagram/schematic-equipment.js +13 -1
- package/dist/diagram/snapshot.d.ts +6 -3
- package/dist/diagram/snapshot.js +56 -35
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/multiconductor.js +38 -4
- package/dist/state.svelte.d.ts +28 -8
- package/dist/state.svelte.js +29 -5
- package/package.json +2 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { mcNetworkScopeLabel, mcLoadModelLabel } from '@tellegen/engine';
|
|
2
3
|
import { untrack } from 'svelte';
|
|
3
4
|
import { on } from 'svelte/events';
|
|
4
5
|
import type { Network, NetworkBus } from '../api.js';
|
|
@@ -395,7 +396,10 @@
|
|
|
395
396
|
>
|
|
396
397
|
</div>
|
|
397
398
|
</div>
|
|
398
|
-
<p class="coordinate-label">
|
|
399
|
+
<p class="coordinate-label">
|
|
400
|
+
Diagram coordinates{#if multiconductor?.result}
|
|
401
|
+
· {mcLoadModelLabel(multiconductor.result.load_model)} · {mcNetworkScopeLabel(multiconductor.result.network_scope)}{/if}
|
|
402
|
+
</p>
|
|
399
403
|
</div>
|
|
400
404
|
|
|
401
405
|
<style>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { mcLoadModelLabel, type McLoadModelAssessment } from '@tellegen/engine';
|
|
3
|
+
let { model, compact = false }: { model?: McLoadModelAssessment | null; compact?: boolean } =
|
|
4
|
+
$props();
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<aside class:warning={model?.status !== 'original'} aria-label="Load-model qualification">
|
|
8
|
+
<strong>{mcLoadModelLabel(model)}</strong>
|
|
9
|
+
{#if model?.status === 'modified'}
|
|
10
|
+
<p>
|
|
11
|
+
The calculation converged using modified load equations on {model.modified_branch_count}
|
|
12
|
+
load {model.modified_branch_count === 1 ? 'branch' : 'branches'}. These values are not a
|
|
13
|
+
verified solution of the original load model. The qualification is retained when saving or
|
|
14
|
+
exporting.
|
|
15
|
+
</p>
|
|
16
|
+
{:else if model?.status !== 'original'}
|
|
17
|
+
<p>
|
|
18
|
+
These results have no verified solution of the original load equations. Rerun to assess the
|
|
19
|
+
load model.
|
|
20
|
+
</p>
|
|
21
|
+
{/if}
|
|
22
|
+
{#if model && !compact}
|
|
23
|
+
<details>
|
|
24
|
+
<summary>Model assessment and settings used</summary>
|
|
25
|
+
<p>
|
|
26
|
+
Original-model KCL residual: {model.original_kcl_residual === null
|
|
27
|
+
? 'undefined at this voltage'
|
|
28
|
+
: `${model.original_kcl_residual.toExponential(3)} A`}. Scaled residual: {model.original_scaled_kcl_residual ===
|
|
29
|
+
null
|
|
30
|
+
? 'unavailable'
|
|
31
|
+
: model.original_scaled_kcl_residual.toPrecision(4)} (at most 1 passes).
|
|
32
|
+
</p>
|
|
33
|
+
<p>
|
|
34
|
+
Load-model envelope {model.options.voltage_envelope ? 'enabled' : 'disabled'}; Vlow {model
|
|
35
|
+
.options.v_low_pu}, Vmin {model.options.v_min_pu}, Vmax {model.options.v_max_pu} pu. These thresholds
|
|
36
|
+
change load equations; they are not operating voltage limits.
|
|
37
|
+
</p>
|
|
38
|
+
<p>
|
|
39
|
+
Maximum iterations {model.options.max_iterations}; damping {model.options.damping};
|
|
40
|
+
voltage-change tolerance {model.options.tolerance} V; zero-voltage tolerance {model.options
|
|
41
|
+
.zero_voltage_tolerance} V; KCL tolerances {model.options.absolute_kcl_tolerance} A absolute
|
|
42
|
+
and {model.options.relative_kcl_tolerance} relative.
|
|
43
|
+
</p>
|
|
44
|
+
</details>
|
|
45
|
+
{/if}
|
|
46
|
+
</aside>
|
|
47
|
+
|
|
48
|
+
<style>
|
|
49
|
+
aside {
|
|
50
|
+
margin: 0.75rem 0;
|
|
51
|
+
padding: 0.65rem;
|
|
52
|
+
border: 1px solid var(--line);
|
|
53
|
+
border-radius: 4px;
|
|
54
|
+
font-size: 12px;
|
|
55
|
+
}
|
|
56
|
+
.warning {
|
|
57
|
+
border-left: 4px solid #b7791f;
|
|
58
|
+
}
|
|
59
|
+
p {
|
|
60
|
+
margin: 0.4rem 0;
|
|
61
|
+
line-height: 1.5;
|
|
62
|
+
}
|
|
63
|
+
summary {
|
|
64
|
+
cursor: pointer;
|
|
65
|
+
margin-top: 0.4rem;
|
|
66
|
+
}
|
|
67
|
+
</style>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type McLoadModelAssessment } from '@tellegen/engine';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
model?: McLoadModelAssessment | null;
|
|
4
|
+
compact?: boolean;
|
|
5
|
+
};
|
|
6
|
+
declare const McModelQualification: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
7
|
+
type McModelQualification = ReturnType<typeof McModelQualification>;
|
|
8
|
+
export default McModelQualification;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
mcNetworkScopeLabel,
|
|
4
|
+
type McNetworkScope as NetworkScope,
|
|
5
|
+
type McExcludedIsland
|
|
6
|
+
} from '@tellegen/engine';
|
|
7
|
+
let {
|
|
8
|
+
scope,
|
|
9
|
+
islands = [],
|
|
10
|
+
preview = false
|
|
11
|
+
}: {
|
|
12
|
+
scope?: NetworkScope | null;
|
|
13
|
+
islands?: McExcludedIsland[];
|
|
14
|
+
preview?: boolean;
|
|
15
|
+
} = $props();
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
{#if scope}
|
|
19
|
+
<aside aria-label="Network calculation scope">
|
|
20
|
+
<strong>{preview ? 'Unpowered islands detected' : mcNetworkScopeLabel(scope)}</strong>
|
|
21
|
+
{#if preview}<p>
|
|
22
|
+
{scope.excluded_island_count}
|
|
23
|
+
{scope.excluded_island_count === 1 ? 'island has' : 'islands have'} no voltage source.
|
|
24
|
+
</p>{/if}
|
|
25
|
+
<p>
|
|
26
|
+
{scope.excluded_bus_count}
|
|
27
|
+
{scope.excluded_bus_count === 1 ? 'bus' : 'buses'} and {scope.excluded_equipment_count}
|
|
28
|
+
equipment
|
|
29
|
+
{scope.excluded_equipment_count === 1 ? 'record' : 'records'}
|
|
30
|
+
{preview
|
|
31
|
+
? 'will be excluded when you solve the energized network'
|
|
32
|
+
: 'are excluded from the calculation'}
|
|
33
|
+
because their {scope.excluded_island_count === 1 ? 'island has' : 'islands have'} no voltage source.
|
|
34
|
+
The full network remains available for inspection. Excluded equipment has no calculated result;
|
|
35
|
+
its voltage and power are not reported as zero.
|
|
36
|
+
</p>
|
|
37
|
+
{#if scope.excluded_load_count}<p>
|
|
38
|
+
{scope.excluded_load_count}
|
|
39
|
+
{scope.excluded_load_count === 1 ? 'load has' : 'loads have'} no voltage source and {preview
|
|
40
|
+
? 'will be omitted'
|
|
41
|
+
: scope.excluded_load_count === 1
|
|
42
|
+
? 'is absent'
|
|
43
|
+
: 'are absent'} from solved totals.
|
|
44
|
+
</p>{/if}
|
|
45
|
+
{#if islands.length}
|
|
46
|
+
<details>
|
|
47
|
+
<summary
|
|
48
|
+
>{preview
|
|
49
|
+
? 'Islands and equipment to exclude'
|
|
50
|
+
: 'Excluded islands and equipment'}</summary
|
|
51
|
+
>
|
|
52
|
+
{#each islands as island, i}
|
|
53
|
+
<h4>Island {i + 1}: no voltage source</h4>
|
|
54
|
+
<p>Buses: {island.buses.join(', ')}</p>
|
|
55
|
+
<ul>
|
|
56
|
+
{#each island.equipment as item}<li>
|
|
57
|
+
{item.kind}: {item.name}
|
|
58
|
+
</li>{/each}
|
|
59
|
+
</ul>
|
|
60
|
+
{/each}
|
|
61
|
+
</details>
|
|
62
|
+
{/if}
|
|
63
|
+
</aside>
|
|
64
|
+
{/if}
|
|
65
|
+
|
|
66
|
+
<style>
|
|
67
|
+
aside {
|
|
68
|
+
margin: 0.75rem 0;
|
|
69
|
+
padding: 0.65rem;
|
|
70
|
+
border: 1px solid var(--line);
|
|
71
|
+
border-left: 4px solid #b7791f;
|
|
72
|
+
border-radius: 4px;
|
|
73
|
+
font-size: 12px;
|
|
74
|
+
overflow-wrap: anywhere;
|
|
75
|
+
}
|
|
76
|
+
p {
|
|
77
|
+
margin: 0.4rem 0;
|
|
78
|
+
line-height: 1.5;
|
|
79
|
+
}
|
|
80
|
+
summary {
|
|
81
|
+
cursor: pointer;
|
|
82
|
+
}
|
|
83
|
+
details {
|
|
84
|
+
max-height: 20rem;
|
|
85
|
+
overflow: auto;
|
|
86
|
+
}
|
|
87
|
+
h4 {
|
|
88
|
+
margin: 0.75rem 0 0.25rem;
|
|
89
|
+
}
|
|
90
|
+
</style>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type McNetworkScope as NetworkScope, type McExcludedIsland } from '@tellegen/engine';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
scope?: NetworkScope | null;
|
|
4
|
+
islands?: McExcludedIsland[];
|
|
5
|
+
preview?: boolean;
|
|
6
|
+
};
|
|
7
|
+
declare const McNetworkScope: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
8
|
+
type McNetworkScope = ReturnType<typeof McNetworkScope>;
|
|
9
|
+
export default McNetworkScope;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { DEFAULT_MC_PF_OPTIONS, validateMcPfOptions, type McPfOptions } from '@tellegen/engine';
|
|
3
|
+
let {
|
|
4
|
+
options = $bindable<McPfOptions>({ ...DEFAULT_MC_PF_OPTIONS }),
|
|
5
|
+
disabled = false,
|
|
6
|
+
onapply
|
|
7
|
+
}: {
|
|
8
|
+
options: McPfOptions;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
onapply?: (options: McPfOptions) => Promise<unknown>;
|
|
11
|
+
} = $props();
|
|
12
|
+
let failure = $state('');
|
|
13
|
+
const validation = $derived(validateMcPfOptions(options));
|
|
14
|
+
// Keep one shared set of next-run settings for every solve action. Fill
|
|
15
|
+
// omitted options when opening an older snapshot with partial settings.
|
|
16
|
+
$effect(() => {
|
|
17
|
+
if (Object.keys(DEFAULT_MC_PF_OPTIONS).some((key) => !(key in options))) {
|
|
18
|
+
options = { ...DEFAULT_MC_PF_OPTIONS, ...options };
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
async function apply() {
|
|
22
|
+
failure = '';
|
|
23
|
+
if (validation) return;
|
|
24
|
+
try {
|
|
25
|
+
await onapply?.({ ...options });
|
|
26
|
+
} catch (error) {
|
|
27
|
+
failure = error instanceof Error ? error.message : String(error);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<details class="settings">
|
|
33
|
+
<summary>Power flow settings</summary>
|
|
34
|
+
<p>
|
|
35
|
+
These settings apply to both Run power flow and Solve energized network. Editing them does not
|
|
36
|
+
start a calculation. A failed run retains the previous result and the settings used for that
|
|
37
|
+
result.
|
|
38
|
+
</p>
|
|
39
|
+
<fieldset {disabled}>
|
|
40
|
+
<legend>Load-model envelope</legend>
|
|
41
|
+
<label class="toggle"
|
|
42
|
+
><input type="checkbox" bind:checked={options.voltage_envelope} />Use load-model envelope</label
|
|
43
|
+
>
|
|
44
|
+
<p>
|
|
45
|
+
Outside the normal band, the envelope changes the load equations to help convergence. Modified
|
|
46
|
+
results are labelled explicitly. These thresholds are not operating voltage limits.
|
|
47
|
+
</p>
|
|
48
|
+
<label>Vlow (pu)<input type="number" step="any" bind:value={options.v_low_pu} /></label>
|
|
49
|
+
<label>Vmin (pu)<input type="number" step="any" bind:value={options.v_min_pu} /></label>
|
|
50
|
+
<label>Vmax (pu)<input type="number" step="any" bind:value={options.v_max_pu} /></label>
|
|
51
|
+
<p>
|
|
52
|
+
With the envelope disabled, the original load equations are used at every voltage; convergence
|
|
53
|
+
is not guaranteed.
|
|
54
|
+
</p>
|
|
55
|
+
</fieldset>
|
|
56
|
+
<fieldset {disabled}>
|
|
57
|
+
<legend>Numerical solver</legend>
|
|
58
|
+
<label
|
|
59
|
+
>Maximum iterations<input
|
|
60
|
+
type="number"
|
|
61
|
+
min="1"
|
|
62
|
+
step="1"
|
|
63
|
+
bind:value={options.max_iterations}
|
|
64
|
+
/></label
|
|
65
|
+
>
|
|
66
|
+
<label
|
|
67
|
+
>Damping<input type="number" min="0" max="1" step="any" bind:value={options.damping} /></label
|
|
68
|
+
>
|
|
69
|
+
<details>
|
|
70
|
+
<summary>Advanced tolerances</summary>
|
|
71
|
+
<label
|
|
72
|
+
>Voltage-change tolerance (V)<input
|
|
73
|
+
type="number"
|
|
74
|
+
step="any"
|
|
75
|
+
bind:value={options.tolerance}
|
|
76
|
+
/></label
|
|
77
|
+
>
|
|
78
|
+
<label
|
|
79
|
+
>Zero-voltage tolerance (V)<input
|
|
80
|
+
type="number"
|
|
81
|
+
step="any"
|
|
82
|
+
bind:value={options.zero_voltage_tolerance}
|
|
83
|
+
/></label
|
|
84
|
+
>
|
|
85
|
+
<label
|
|
86
|
+
>Absolute KCL tolerance (A)<input
|
|
87
|
+
type="number"
|
|
88
|
+
step="any"
|
|
89
|
+
bind:value={options.absolute_kcl_tolerance}
|
|
90
|
+
/></label
|
|
91
|
+
>
|
|
92
|
+
<label
|
|
93
|
+
>Relative KCL tolerance<input
|
|
94
|
+
type="number"
|
|
95
|
+
step="any"
|
|
96
|
+
bind:value={options.relative_kcl_tolerance}
|
|
97
|
+
/></label
|
|
98
|
+
>
|
|
99
|
+
</details>
|
|
100
|
+
</fieldset>
|
|
101
|
+
{#if validation}<p role="alert">{validation}</p>{/if}
|
|
102
|
+
{#if failure}<p role="alert">{failure}</p>{/if}
|
|
103
|
+
<div class="actions">
|
|
104
|
+
{#if onapply}<button disabled={disabled || !!validation} onclick={() => void apply()}
|
|
105
|
+
>Apply settings and run</button
|
|
106
|
+
>{/if}
|
|
107
|
+
<button
|
|
108
|
+
{disabled}
|
|
109
|
+
onclick={() => {
|
|
110
|
+
options = { ...DEFAULT_MC_PF_OPTIONS };
|
|
111
|
+
failure = '';
|
|
112
|
+
}}>Restore defaults</button
|
|
113
|
+
>
|
|
114
|
+
</div>
|
|
115
|
+
</details>
|
|
116
|
+
|
|
117
|
+
<style>
|
|
118
|
+
.settings {
|
|
119
|
+
margin: 0.75rem 0;
|
|
120
|
+
font-size: 12px;
|
|
121
|
+
}
|
|
122
|
+
summary {
|
|
123
|
+
cursor: pointer;
|
|
124
|
+
font-weight: 600;
|
|
125
|
+
}
|
|
126
|
+
fieldset {
|
|
127
|
+
margin: 0.75rem 0;
|
|
128
|
+
border: 1px solid var(--line);
|
|
129
|
+
border-radius: 4px;
|
|
130
|
+
}
|
|
131
|
+
label {
|
|
132
|
+
display: flex;
|
|
133
|
+
align-items: center;
|
|
134
|
+
justify-content: space-between;
|
|
135
|
+
gap: 0.5rem;
|
|
136
|
+
margin: 0.5rem 0;
|
|
137
|
+
}
|
|
138
|
+
input[type='number'] {
|
|
139
|
+
width: 7rem;
|
|
140
|
+
min-width: 0;
|
|
141
|
+
}
|
|
142
|
+
.toggle {
|
|
143
|
+
justify-content: flex-start;
|
|
144
|
+
}
|
|
145
|
+
p {
|
|
146
|
+
line-height: 1.5;
|
|
147
|
+
}
|
|
148
|
+
.actions {
|
|
149
|
+
display: flex;
|
|
150
|
+
flex-wrap: wrap;
|
|
151
|
+
gap: 0.5rem;
|
|
152
|
+
}
|
|
153
|
+
[role='alert'] {
|
|
154
|
+
color: var(--text-primary);
|
|
155
|
+
border-left: 3px solid #b7791f;
|
|
156
|
+
padding-left: 0.5rem;
|
|
157
|
+
}
|
|
158
|
+
</style>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type McPfOptions } from '@tellegen/engine';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
options: McPfOptions;
|
|
4
|
+
disabled?: boolean;
|
|
5
|
+
onapply?: (options: McPfOptions) => Promise<unknown>;
|
|
6
|
+
};
|
|
7
|
+
declare const McPfSettings: import("svelte").Component<$$ComponentProps, {}, "options">;
|
|
8
|
+
type McPfSettings = ReturnType<typeof McPfSettings>;
|
|
9
|
+
export default McPfSettings;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import McNetworkScope from './McNetworkScope.svelte';
|
|
3
|
+
import McModelQualification from './McModelQualification.svelte';
|
|
2
4
|
import {
|
|
3
5
|
mcPfResultDetail,
|
|
4
6
|
summarizeMcPfResult,
|
|
@@ -13,6 +15,7 @@
|
|
|
13
15
|
import type { McEditTiming } from '../state.svelte.js';
|
|
14
16
|
let {
|
|
15
17
|
result,
|
|
18
|
+
excludedIslands = [],
|
|
16
19
|
detail,
|
|
17
20
|
timing = null,
|
|
18
21
|
elapsedMs,
|
|
@@ -22,6 +25,7 @@
|
|
|
22
25
|
}: {
|
|
23
26
|
/** A live session's summary, or a stored full result. */
|
|
24
27
|
result: McPfSummary | McPfResult;
|
|
28
|
+
excludedIslands?: import('@tellegen/engine').McExcludedIsland[];
|
|
25
29
|
/** Fetches one bus's terminals and a page of equipment ports. A full
|
|
26
30
|
* `result` serves its own pages when this is omitted. */
|
|
27
31
|
detail?: (query: McPfDetailQuery) => Promise<McPfDetail | null>;
|
|
@@ -120,6 +124,11 @@
|
|
|
120
124
|
</script>
|
|
121
125
|
|
|
122
126
|
<section aria-label="AC power flow results" class="mc-results">
|
|
127
|
+
<McNetworkScope
|
|
128
|
+
scope={summary.network_scope}
|
|
129
|
+
islands={'excluded_islands' in result ? result.excluded_islands : excludedIslands}
|
|
130
|
+
/>
|
|
131
|
+
<McModelQualification model={summary.load_model} />
|
|
123
132
|
<div class="result-heading">
|
|
124
133
|
<strong>AC power flow</strong><span
|
|
125
134
|
>{summary.iterations} iterations{elapsedMs === null
|
|
@@ -133,7 +142,7 @@
|
|
|
133
142
|
<dd>{summary.converged ? 'Converged' : 'Not converged'}</dd>
|
|
134
143
|
</div>
|
|
135
144
|
<div>
|
|
136
|
-
<dt>
|
|
145
|
+
<dt>Load-model voltage band</dt>
|
|
137
146
|
<dd>{summary.voltage_valid ? 'Valid' : `${summary.voltage_violation_count} violations`}</dd>
|
|
138
147
|
</div>
|
|
139
148
|
<div>
|
|
@@ -165,6 +174,12 @@
|
|
|
165
174
|
>{/each}</select
|
|
166
175
|
></label
|
|
167
176
|
>
|
|
177
|
+
{#if excludedIslands.some((island) => island.buses.includes(busId ?? ''))}
|
|
178
|
+
<p role="status">
|
|
179
|
+
This bus is excluded from the calculation because its island has no voltage source. No result
|
|
180
|
+
is available.
|
|
181
|
+
</p>
|
|
182
|
+
{/if}
|
|
168
183
|
<label class="search"
|
|
169
184
|
>Terminals<input
|
|
170
185
|
aria-label="Find terminal"
|
|
@@ -3,6 +3,7 @@ import type { McEditTiming } from '../state.svelte.js';
|
|
|
3
3
|
type $$ComponentProps = {
|
|
4
4
|
/** A live session's summary, or a stored full result. */
|
|
5
5
|
result: McPfSummary | McPfResult;
|
|
6
|
+
excludedIslands?: import('@tellegen/engine').McExcludedIsland[];
|
|
6
7
|
/** Fetches one bus's terminals and a page of equipment ports. A full
|
|
7
8
|
* `result` serves its own pages when this is omitted. */
|
|
8
9
|
detail?: (query: McPfDetailQuery) => Promise<McPfDetail | null>;
|
|
@@ -206,6 +206,7 @@
|
|
|
206
206
|
`Tellegen export ${new Date().toISOString()}`,
|
|
207
207
|
`Case: ${label}`,
|
|
208
208
|
`${snapshot.label}; ${snapshot.iterations ?? '?'} iterations`,
|
|
209
|
+
...(snapshot.provenance ?? []),
|
|
209
210
|
'P/Q are positive into the equipment at each end; voltages are terminal-pair magnitudes'
|
|
210
211
|
]);
|
|
211
212
|
const url = URL.createObjectURL(new Blob([csv], { type: 'text/csv;charset=utf-8' }));
|
|
@@ -227,6 +228,7 @@
|
|
|
227
228
|
onclick={() => onschematic(!schematic)}>{schematic ? 'Show map' : 'Show diagram'}</button
|
|
228
229
|
>{/if}
|
|
229
230
|
</div>
|
|
231
|
+
{#if snapshot}<p class="result-provenance">{snapshot.label}</p>{/if}
|
|
230
232
|
<OmniSearch
|
|
231
233
|
index={search}
|
|
232
234
|
hint={searchHint}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import McNetworkScope from './McNetworkScope.svelte';
|
|
3
|
+
import McModelQualification from './McModelQualification.svelte';
|
|
2
4
|
import { getAppState, getController } from '../context.svelte.js';
|
|
3
5
|
import { fmt, rgbaCss } from '../format.js';
|
|
4
6
|
import {
|
|
@@ -10,7 +12,7 @@
|
|
|
10
12
|
} from '../multiconductor.js';
|
|
11
13
|
import { getPanelLayout } from '../panels.svelte.js';
|
|
12
14
|
import { getNoticeCenter } from '../notices.svelte.js';
|
|
13
|
-
import { type DistAttachmentKind } from '@tellegen/engine';
|
|
15
|
+
import { validateMcPfOptions, type DistAttachmentKind } from '@tellegen/engine';
|
|
14
16
|
|
|
15
17
|
const app = getAppState();
|
|
16
18
|
const ctrl = getController();
|
|
@@ -50,7 +52,10 @@
|
|
|
50
52
|
{#if s}
|
|
51
53
|
<p class="tag">Multiconductor</p>
|
|
52
54
|
<details class="case-details">
|
|
53
|
-
<summary
|
|
55
|
+
<summary
|
|
56
|
+
>{s.n_bus} buses, {s.n_edge}
|
|
57
|
+
{s.n_edge === 1 ? 'branch' : 'branches'}</summary
|
|
58
|
+
>
|
|
54
59
|
<dl>
|
|
55
60
|
<div>
|
|
56
61
|
<dt>Buses</dt>
|
|
@@ -97,6 +102,15 @@
|
|
|
97
102
|
</dd>
|
|
98
103
|
</div>
|
|
99
104
|
</dl>
|
|
105
|
+
<McNetworkScope
|
|
106
|
+
scope={mc.mcPreparedScope}
|
|
107
|
+
islands={mc.mcExcludedIslands}
|
|
108
|
+
preview={!mc.result}
|
|
109
|
+
/>
|
|
110
|
+
{#if !mc.mcPfSupported}<p class="footnote">{mc.mcPfReason}</p>{/if}
|
|
111
|
+
{#if validateMcPfOptions(mc.mcOptions)}<p class="footnote">
|
|
112
|
+
{validateMcPfOptions(mc.mcOptions)} Update Power flow settings in Studies before solving.
|
|
113
|
+
</p>{/if}
|
|
100
114
|
<div class="calculation-actions">
|
|
101
115
|
{#if mc.solving}
|
|
102
116
|
<span role="status">Calculating…</span>{#if mc.solveAbort}<button
|
|
@@ -104,13 +118,13 @@
|
|
|
104
118
|
>{/if}
|
|
105
119
|
{:else}
|
|
106
120
|
<button
|
|
107
|
-
disabled={!mc.mcPfSupported}
|
|
121
|
+
disabled={!mc.mcPfSupported || !!validateMcPfOptions(mc.mcOptions)}
|
|
108
122
|
onclick={() => {
|
|
109
123
|
void ctrl
|
|
110
|
-
.solveMultiCase(mc)
|
|
124
|
+
.solveMultiCase(mc, mc.mcOptions, undefined, mc.mcRequiresEnergizedSolve)
|
|
111
125
|
.then(openResults)
|
|
112
126
|
.catch(() => {});
|
|
113
|
-
}}>
|
|
127
|
+
}}>{mc.mcSolveLabel}</button
|
|
114
128
|
>
|
|
115
129
|
{#if !mc.mcPfSupported}
|
|
116
130
|
<button
|
|
@@ -125,12 +139,17 @@
|
|
|
125
139
|
{/if}
|
|
126
140
|
{/if}
|
|
127
141
|
</div>
|
|
128
|
-
{#if mc.result}<
|
|
142
|
+
{#if mc.result}<McModelQualification model={mc.result.load_model} compact />
|
|
143
|
+
<p class="footnote">Converged, {mc.result.iterations} iterations</p>
|
|
129
144
|
<button class="reset" onclick={openResults}>View results in Studies</button>{/if}
|
|
130
145
|
{/if}
|
|
131
146
|
|
|
132
147
|
{#if mc.selectedBus}
|
|
133
148
|
{@const b = mc.selectedBus}
|
|
149
|
+
{#if mc.mcExcludedIslands.some((island) => island.buses.includes(b.id))}<p role="status">
|
|
150
|
+
This bus belongs to an island with no voltage source and will be excluded from
|
|
151
|
+
energized-network calculations. No power-flow result is available for it.
|
|
152
|
+
</p>{/if}
|
|
134
153
|
<hr />
|
|
135
154
|
<h3>Bus <span class="mono">{b.id}</span></h3>
|
|
136
155
|
<div class="terminals">
|
package/dist/components.d.ts
CHANGED
|
@@ -25,3 +25,6 @@ export { default as TopMovers } from './components/TopMovers.svelte';
|
|
|
25
25
|
export { default as Sparkline } from './Sparkline.svelte';
|
|
26
26
|
export { default as McResults } from './components/McResults.svelte';
|
|
27
27
|
export { default as McWorkbench } from './components/McWorkbench.svelte';
|
|
28
|
+
export { default as McPfSettings } from './components/McPfSettings.svelte';
|
|
29
|
+
export { default as McModelQualification } from './components/McModelQualification.svelte';
|
|
30
|
+
export { default as McNetworkScope } from './components/McNetworkScope.svelte';
|
package/dist/components.js
CHANGED
|
@@ -25,3 +25,6 @@ export { default as TopMovers } from './components/TopMovers.svelte';
|
|
|
25
25
|
export { default as Sparkline } from './Sparkline.svelte';
|
|
26
26
|
export { default as McResults } from './components/McResults.svelte';
|
|
27
27
|
export { default as McWorkbench } from './components/McWorkbench.svelte';
|
|
28
|
+
export { default as McPfSettings } from './components/McPfSettings.svelte';
|
|
29
|
+
export { default as McModelQualification } from './components/McModelQualification.svelte';
|
|
30
|
+
export { default as McNetworkScope } from './components/McNetworkScope.svelte';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type TellegenApiClient } from './api.js';
|
|
2
2
|
import type { BranchRatingDeltas, Network, NetworkBus, SensitivityColumn, Solution } from './api.js';
|
|
3
3
|
import { CaseState, LocalCase, MulticonductorCase, type AppState, type DemandRangeMode, type SolvableCase } from './state.svelte.js';
|
|
4
|
-
import { type EngineTransport, type McPfDetail, type McPfDetailQuery, type McPfOptions, type McPfResult, type McPfSummary, type McStudySnapshot, type BrowserStudy, type Formulation, type IngestedCase, type PowerIoDiagnostic, type SensTarget } from '@tellegen/engine';
|
|
4
|
+
import { type EngineTransport, type McPfDetail, type McPfDetailQuery, type McPfOptions, type McPfResult, type McPfResultTables, type McPfSummary, type McStudySnapshot, type BrowserStudy, type Formulation, type IngestedCase, type PowerIoDiagnostic, type SensTarget } from '@tellegen/engine';
|
|
5
5
|
/** A parsed geographic sidecar awaiting application: the canonical `.geo.json`
|
|
6
6
|
* layer document from the engine's tolerant reader, its source file name, and
|
|
7
7
|
* the reader's notes on records it could not use. */
|
|
@@ -38,7 +38,7 @@ export interface ControllerOptions {
|
|
|
38
38
|
* `false`, `load()` only marks the shell loaded and frames the map. */
|
|
39
39
|
loadDefaultCases?: boolean;
|
|
40
40
|
acOpfWasmUrl?: string;
|
|
41
|
-
mcTransport?: Pick<EngineTransport, 'solveMcModule' | 'applyMcGeo'> & Partial<Pick<EngineTransport, 'solveMcStudy' | 'applyMcStudyGeo' | 'createMcPfSession'>>;
|
|
41
|
+
mcTransport?: Pick<EngineTransport, 'solveMcModule' | 'applyMcGeo'> & Partial<Pick<EngineTransport, 'solveMcStudy' | 'applyMcStudyGeo' | 'createMcPfSession' | 'prepareMcEnergizedNetwork'>>;
|
|
42
42
|
}
|
|
43
43
|
/** Input to `Controller.openBalancedModule` and `replaceLocalCase`: a
|
|
44
44
|
* balanced (transmission) powerio module, either as its JSON text or as the
|
|
@@ -66,7 +66,7 @@ export declare class Controller {
|
|
|
66
66
|
* server path then reports "compute off" instead of fetching. */
|
|
67
67
|
api: TellegenApiClient | null;
|
|
68
68
|
readonly loadDefaultCases: boolean;
|
|
69
|
-
mcTransport: Pick<EngineTransport, 'solveMcModule' | 'applyMcGeo'> & Partial<Pick<EngineTransport, 'solveMcStudy' | 'applyMcStudyGeo' | 'createMcPfSession'>>;
|
|
69
|
+
mcTransport: Pick<EngineTransport, 'solveMcModule' | 'applyMcGeo'> & Partial<Pick<EngineTransport, 'solveMcStudy' | 'applyMcStudyGeo' | 'createMcPfSession' | 'prepareMcEnergizedNetwork'>>;
|
|
70
70
|
abort: AbortController | null;
|
|
71
71
|
sensitivity429Until: number;
|
|
72
72
|
serverComputeOff: boolean;
|
|
@@ -359,7 +359,7 @@ export declare class Controller {
|
|
|
359
359
|
/** Retain parsed electrical inputs and draw each declared coordinate space directly. */
|
|
360
360
|
private addMultiCase;
|
|
361
361
|
/** Compute terminal quantities only for the retained input and current case revision. */
|
|
362
|
-
solveMultiCase: (c: MulticonductorCase, options?: McPfOptions, signal?: AbortSignal) => Promise<McPfSummary>;
|
|
362
|
+
solveMultiCase: (c: MulticonductorCase, options?: McPfOptions, signal?: AbortSignal, energizedOnly?: boolean) => Promise<McPfSummary>;
|
|
363
363
|
/** Materialize the current retained operating point only when a portable
|
|
364
364
|
* snapshot is actually needed (save/export/geography). */
|
|
365
365
|
snapshotMultiCase: (c: MulticonductorCase) => Promise<McStudySnapshot>;
|
|
@@ -368,7 +368,7 @@ export declare class Controller {
|
|
|
368
368
|
* when the operating point changed before the page arrived. */
|
|
369
369
|
multiResultDetail: (c: MulticonductorCase, query: McPfDetailQuery) => Promise<McPfDetail | null>;
|
|
370
370
|
/** Above this many buses, reading and indexing the whole operating point takes a noticeable
|
|
371
|
-
* fraction of a second (evidence/diagram), so the overview is computed on request instead of
|
|
371
|
+
* fraction of a second (docs/evidence/diagram), so the overview is computed on request instead of
|
|
372
372
|
* after every solve. */
|
|
373
373
|
static readonly AUTO_OVERVIEW_MAX_BUSES = 25000;
|
|
374
374
|
private overviewSeq;
|
|
@@ -382,6 +382,10 @@ export declare class Controller {
|
|
|
382
382
|
* for a case that computes the overview on request. Call from an effect that tracks the case's
|
|
383
383
|
* `result` and `mcFullResult`; the returned function cancels a pending read. */
|
|
384
384
|
followOverview: (c: MulticonductorCase | null) => (() => void) | undefined;
|
|
385
|
+
/** The complete operating point in the form whole-network views read: the stored result, or
|
|
386
|
+
* the live session's as columns, which hold no object per terminal or port. Null when there is
|
|
387
|
+
* no result or it changed before the read finished. */
|
|
388
|
+
multiOverviewResult: (c: MulticonductorCase) => Promise<McPfResult | McPfResultTables | null>;
|
|
385
389
|
/** The complete operating point for whole-network views: the stored result, or one read from
|
|
386
390
|
* the live session. Null when there is no result or it changed before the read finished. */
|
|
387
391
|
multiFullResult: (c: MulticonductorCase) => Promise<McPfResult | null>;
|