linny-r 2.1.5 → 2.1.7
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/package.json +1 -1
- package/static/index.html +18 -2
- package/static/linny-r.css +44 -0
- package/static/scripts/linny-r-config.js +2 -2
- package/static/scripts/linny-r-gui-chart-manager.js +3 -2
- package/static/scripts/linny-r-gui-controller.js +32 -25
- package/static/scripts/linny-r-gui-dataset-manager.js +8 -1
- package/static/scripts/linny-r-gui-experiment-manager.js +28 -16
- package/static/scripts/linny-r-gui-file-manager.js +2 -1
- package/static/scripts/linny-r-gui-finder.js +260 -22
- package/static/scripts/linny-r-gui-power-grid-manager.js +6 -0
- package/static/scripts/linny-r-milp.js +7 -1
- package/static/scripts/linny-r-model.js +94 -133
- package/static/scripts/linny-r-utils.js +210 -30
- package/static/scripts/linny-r-vm.js +21 -7
package/package.json
CHANGED
package/static/index.html
CHANGED
@@ -3178,6 +3178,12 @@ where X can be one or several of these letters: ABCDELPQ">
|
|
3178
3178
|
<img id="finder-chart-btn" class="btn enab"
|
3179
3179
|
src="images/chart.png"
|
3180
3180
|
title="Add attribute to chart">
|
3181
|
+
<img id="finder-experiment-btn" class="btn enab"
|
3182
|
+
src="images/experiment.png"
|
3183
|
+
title="Only consider experiment outcomes">
|
3184
|
+
<img id="finder-table-btn" class="btn enab"
|
3185
|
+
src="images/table.png"
|
3186
|
+
title="View entity attribute values">
|
3181
3187
|
<img id="finder-copy-btn" class="btn enab"
|
3182
3188
|
src="images/table-to-clpbrd.png"
|
3183
3189
|
title="Copy entity attributes to clipboard">
|
@@ -3193,6 +3199,14 @@ where X can be one or several of these letters: ABCDELPQ">
|
|
3193
3199
|
<table id="finder-expression-table">
|
3194
3200
|
</table>
|
3195
3201
|
</div>
|
3202
|
+
<div id="finder-data-pane">
|
3203
|
+
<table id="finder-data-header">
|
3204
|
+
</table>
|
3205
|
+
<div id="finder-data-scroll-area">
|
3206
|
+
<table id="finder-data-table">
|
3207
|
+
</table>
|
3208
|
+
</div>
|
3209
|
+
</div>
|
3196
3210
|
<div id="finder-resize" class="resizer"></div>
|
3197
3211
|
</div>
|
3198
3212
|
|
@@ -3206,8 +3220,10 @@ where X can be one or several of these letters: ABCDELPQ">
|
|
3206
3220
|
<img class="ok-btn" src="images/ok.png">
|
3207
3221
|
</div>
|
3208
3222
|
<div style="margin: 2px; padding-right: 2px; white-space: nowrap">
|
3209
|
-
<
|
3210
|
-
|
3223
|
+
<div id="confirm-add-chart-variables-attr-of">
|
3224
|
+
<select id="confirm-add-chart-variables-attribute"></select> of
|
3225
|
+
</div>
|
3226
|
+
<span id="confirm-add-chart-variables-count"></span>
|
3211
3227
|
</div>
|
3212
3228
|
<div>
|
3213
3229
|
<div id="confirm-add-chart-variables-absolute" class="box clear"></div>
|
package/static/linny-r.css
CHANGED
@@ -1167,6 +1167,10 @@ table.power-flow th:not(:first-child) {
|
|
1167
1167
|
filter: brightness(160%);
|
1168
1168
|
}
|
1169
1169
|
|
1170
|
+
#settings-power-btn.ignore {
|
1171
|
+
filter: hue-rotate(230deg);
|
1172
|
+
}
|
1173
|
+
|
1170
1174
|
#password-dlg {
|
1171
1175
|
width: min-content;
|
1172
1176
|
height: min-content;
|
@@ -2660,6 +2664,7 @@ div.io-box {
|
|
2660
2664
|
#finder-table,
|
2661
2665
|
#finder-item-table,
|
2662
2666
|
#finder-expression-table,
|
2667
|
+
#finder-data-table,
|
2663
2668
|
#boundline-data-series-table,
|
2664
2669
|
#boundline-data-sel-table,
|
2665
2670
|
#restore-table {
|
@@ -5160,6 +5165,8 @@ img.finder {
|
|
5160
5165
|
|
5161
5166
|
#finder-edit-btn,
|
5162
5167
|
#finder-chart-btn,
|
5168
|
+
#finder-experiment-btn,
|
5169
|
+
#finder-table-btn,
|
5163
5170
|
#finder-copy-btn {
|
5164
5171
|
height: 15px;
|
5165
5172
|
width: 15px;
|
@@ -5211,9 +5218,46 @@ img.finder {
|
|
5211
5218
|
border-top: 1px solid Silver;
|
5212
5219
|
}
|
5213
5220
|
|
5221
|
+
#finder-data-pane {
|
5222
|
+
position: absolute;
|
5223
|
+
background-color: inherit;
|
5224
|
+
top: 23px;
|
5225
|
+
left: calc(50% + 4px);
|
5226
|
+
width: calc(50% - 4px);
|
5227
|
+
height: calc(100% - 30px);
|
5228
|
+
}
|
5229
|
+
|
5230
|
+
#finder-data-header {
|
5231
|
+
height: 20px;
|
5232
|
+
font-weight: bold;
|
5233
|
+
text-align: right;
|
5234
|
+
width: 100%
|
5235
|
+
}
|
5236
|
+
|
5237
|
+
#finder-data-scroll-area {
|
5238
|
+
width: calc(100% - 2px);
|
5239
|
+
height: calc(100% - 32px);
|
5240
|
+
overflow-y: auto;
|
5241
|
+
border-top: 1px solid Silver;
|
5242
|
+
}
|
5243
|
+
|
5244
|
+
#finder-data-table {
|
5245
|
+
text-align: right;
|
5246
|
+
}
|
5247
|
+
|
5248
|
+
#finder-data-header > tbody > tr > td:last-child,
|
5249
|
+
#finder-data-table > tbody > tr > td:last-child {
|
5250
|
+
padding-right: 3%;
|
5251
|
+
}
|
5252
|
+
|
5214
5253
|
#confirm-add-chart-variables-dlg {
|
5215
5254
|
width: min-content;
|
5216
5255
|
height: min-content;
|
5256
|
+
min-width: 135px;
|
5257
|
+
}
|
5258
|
+
|
5259
|
+
#confirm-add-chart-variables-attr-of {
|
5260
|
+
display: inline-block;
|
5217
5261
|
}
|
5218
5262
|
|
5219
5263
|
#confirm-add-chart-variables-attribute {
|
@@ -11,7 +11,7 @@ warning or information messages are displayed.
|
|
11
11
|
*/
|
12
12
|
|
13
13
|
/*
|
14
|
-
Copyright (c) 2017-
|
14
|
+
Copyright (c) 2017-2025 Delft University of Technology
|
15
15
|
|
16
16
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
17
17
|
of this software and associated documentation files (the "Software"), to deal
|
@@ -41,7 +41,7 @@ const CONFIGURATION = {
|
|
41
41
|
// To keep model files compact, floating point values in datasets and run
|
42
42
|
// results are stored with a limited number of significant digits
|
43
43
|
dataset_precision: 8,
|
44
|
-
results_precision:
|
44
|
+
results_precision: 8,
|
45
45
|
// Default properties for new models
|
46
46
|
default_currency_unit: 'EUR',
|
47
47
|
default_time_unit: 'hour',
|
@@ -209,7 +209,7 @@ class GUIChartManager extends ChartManager {
|
|
209
209
|
this.variable_index = -1;
|
210
210
|
this.stretch_factor = 1;
|
211
211
|
this.drawing_graph = false;
|
212
|
-
// Clear the model-related DOM elements
|
212
|
+
// Clear the model-related DOM elements.
|
213
213
|
this.chart_selector.innerHTML = '';
|
214
214
|
this.variables_table.innerHTML = '';
|
215
215
|
this.options_shown = true;
|
@@ -641,7 +641,8 @@ class GUIChartManager extends ChartManager {
|
|
641
641
|
for(const cv of c.variables) {
|
642
642
|
const nv = new ChartVariable(nc);
|
643
643
|
nv.setProperties(cv.object, cv.attribute, cv.stacked,
|
644
|
-
cv.color, cv.scale_factor, cv.absolute, cv.line_width,
|
644
|
+
cv.color, cv.scale_factor, cv.absolute, cv.line_width,
|
645
|
+
cv.visible, cv.sorted);
|
645
646
|
nc.variables.push(nv);
|
646
647
|
}
|
647
648
|
this.chart_index = MODEL.indexOfChart(nc.title);
|
@@ -3078,6 +3078,7 @@ class GUIController extends Controller {
|
|
3078
3078
|
MODEL.t = Math.max(1, MODEL.t - dt);
|
3079
3079
|
UI.updateTimeStep();
|
3080
3080
|
UI.drawDiagram(MODEL);
|
3081
|
+
if(FINDER.visible && FINDER.tabular_view) FINDER.updateTabularView();
|
3081
3082
|
}
|
3082
3083
|
}
|
3083
3084
|
|
@@ -3088,6 +3089,7 @@ class GUIController extends Controller {
|
|
3088
3089
|
MODEL.t = Math.min(MODEL.end_period - MODEL.start_period + 1, MODEL.t + dt);
|
3089
3090
|
UI.updateTimeStep();
|
3090
3091
|
UI.drawDiagram(MODEL);
|
3092
|
+
if(FINDER.visible && FINDER.tabular_view) FINDER.updateTabularView();
|
3091
3093
|
}
|
3092
3094
|
}
|
3093
3095
|
|
@@ -3096,39 +3098,39 @@ class GUIController extends Controller {
|
|
3096
3098
|
//
|
3097
3099
|
|
3098
3100
|
copyStringToClipboard(string) {
|
3099
|
-
//
|
3100
|
-
let msg = pluralS(string.split('\n').length, 'line') +
|
3101
|
-
' copied to clipboard',
|
3102
|
-
type = 'notification';
|
3101
|
+
// Copy string to clipboard and notifies user of #lines copied.
|
3103
3102
|
if(navigator.clipboard) {
|
3104
|
-
|
3105
|
-
|
3103
|
+
const msg = pluralS(string.split('\n').length, 'line') +
|
3104
|
+
' copied to clipboard';
|
3105
|
+
navigator.clipboard.writeText(string)
|
3106
|
+
.then(() => UI.setMessage(msg, 'notification'))
|
3107
|
+
.catch(() => UI.setMessage('Failed to copy to clipboard', 'warning'));
|
3106
3108
|
} else {
|
3107
|
-
|
3108
|
-
|
3109
|
-
document.body.appendChild(ta);
|
3110
|
-
ta.value = string;
|
3111
|
-
ta.select();
|
3112
|
-
document.execCommand('copy');
|
3113
|
-
document.body.removeChild(ta);
|
3109
|
+
UI.setMessage('Your browser does not support copying to clipboard',
|
3110
|
+
'warning');
|
3114
3111
|
}
|
3115
|
-
UI.setMessage(msg, type);
|
3116
3112
|
}
|
3117
3113
|
|
3118
|
-
copyHtmlToClipboard(html) {
|
3119
|
-
// Copy HTML to clipboard
|
3120
|
-
|
3121
|
-
|
3122
|
-
|
3114
|
+
copyHtmlToClipboard(html, plain=false) {
|
3115
|
+
// Copy HTML (as such or as plain text) to clipboard and notify user.
|
3116
|
+
if(navigator.clipboard) {
|
3117
|
+
const
|
3118
|
+
item = (plain ? {'text/plain': html} : {'text/html': html}),
|
3119
|
+
data = [new ClipboardItem(item)],
|
3120
|
+
msg = 'HTML copied to clipboard' + (plain ? ' as plain text' : '');
|
3121
|
+
navigator.clipboard.write(data)
|
3122
|
+
.then(() => UI.setMessage(msg, 'notification'))
|
3123
|
+
.catch((error) => UI.setMessage('Failed to copy HTML to clipboard',
|
3124
|
+
'warning', error));
|
3125
|
+
} else {
|
3126
|
+
UI.setMessage('Your browser does not support copying HTML to clipboard',
|
3127
|
+
'warning');
|
3123
3128
|
}
|
3124
|
-
document.addEventListener('copy', listener);
|
3125
|
-
document.execCommand('copy');
|
3126
|
-
document.removeEventListener('copy', listener);
|
3127
3129
|
}
|
3128
3130
|
|
3129
3131
|
logHeapSize(msg='') {
|
3130
|
-
//
|
3131
|
-
// NOTE:
|
3132
|
+
// Log MB's of used heap memory to console (to detect memory leaks).
|
3133
|
+
// NOTE: This feature is supported only by Chrome.
|
3132
3134
|
if(msg) msg += ' -- ';
|
3133
3135
|
if(performance.memory !== undefined) {
|
3134
3136
|
console.log(msg + 'Allocated memory: ' + Math.round(
|
@@ -4035,11 +4037,16 @@ console.log('HERE name conflicts', name_conflicts, mapping);
|
|
4035
4037
|
this.setBox('settings-encrypt', model.encrypt);
|
4036
4038
|
const pg_btn = md.element('power-btn');
|
4037
4039
|
pg_btn.style.display = (model.with_power_flow ? 'inline-block' : 'none');
|
4040
|
+
if(model.ignore_grid_capacity || model.ignore_KVL || model.ignore_power_losses) {
|
4041
|
+
pg_btn.classList.add('ignore');
|
4042
|
+
} else {
|
4043
|
+
pg_btn.classList.remove('ignore');
|
4044
|
+
}
|
4038
4045
|
md.show('name');
|
4039
4046
|
}
|
4040
4047
|
|
4041
4048
|
updateSettings(model) {
|
4042
|
-
// Valdidate inputs
|
4049
|
+
// Valdidate inputs.
|
4043
4050
|
const px = this.validNumericInput('settings-grid-pixels', 'grid resolution');
|
4044
4051
|
if(px === false) return false;
|
4045
4052
|
const ts = this.validNumericInput('settings-time-scale', 'time step');
|
@@ -288,7 +288,7 @@ class GUIDatasetManager extends DatasetManager {
|
|
288
288
|
for(const r of this.dataset_table.rows) if(r.dataset.prefix === lcp) return r;
|
289
289
|
return null;
|
290
290
|
}
|
291
|
-
|
291
|
+
|
292
292
|
selectPrefixRow(e) {
|
293
293
|
// Select expand/collapse prefix row.
|
294
294
|
this.focal_table = this.dataset_table;
|
@@ -579,6 +579,13 @@ class GUIDatasetManager extends DatasetManager {
|
|
579
579
|
// NOTE: Updating entire dialog may be very time-consuming
|
580
580
|
// when model contains numerous prefixed datasets.
|
581
581
|
this.updatePanes();
|
582
|
+
// NOTE: The selected row now has to be highlighted, as the table
|
583
|
+
// HTML is not changed.
|
584
|
+
let r = event.target;
|
585
|
+
while(r && r.tagName !== 'TR') r = r.parentNode;
|
586
|
+
const sel = this.dataset_table.getElementsByClassName('sel-set');
|
587
|
+
if(sel.length > 0) sel[0].classList.remove('sel-set');
|
588
|
+
r.classList.add('sel-set');
|
582
589
|
}
|
583
590
|
|
584
591
|
selectModifier(event, id, x=true) {
|
@@ -343,6 +343,8 @@ class GUIExperimentManager extends ExperimentManager {
|
|
343
343
|
// NOTE: When UpdateDialog is called after an entity has been renamed,
|
344
344
|
// its variable list should be updated.
|
345
345
|
this.updateViewerVariable();
|
346
|
+
// NOTE: Finder may need updating as well.
|
347
|
+
if(FINDER.experiment_view) FINDER.updateDialog();
|
346
348
|
}
|
347
349
|
|
348
350
|
updateParameters() {
|
@@ -727,6 +729,11 @@ class GUIExperimentManager extends ExperimentManager {
|
|
727
729
|
// Toggle `n` consecutive rows, starting at row `r` (0 = top), to be
|
728
730
|
// (no longer) part of the chart combination set.
|
729
731
|
// @@TO DO: shift-key indicates "add row(s) to selection"
|
732
|
+
if(MODEL.running_experiment) {
|
733
|
+
// NOTE: do NOT change run selection while VM is solving!
|
734
|
+
UI.notify('Run selection cannot be changed when an experiment is running');
|
735
|
+
return;
|
736
|
+
}
|
730
737
|
const
|
731
738
|
x = this.selected_experiment,
|
732
739
|
// Let `first` be the number of the first run on row `r`.
|
@@ -751,7 +758,10 @@ class GUIExperimentManager extends ExperimentManager {
|
|
751
758
|
}
|
752
759
|
}
|
753
760
|
this.updateData();
|
761
|
+
CHART_MANAGER.resetChartVectors();
|
754
762
|
CHART_MANAGER.updateDialog();
|
763
|
+
// NOTE: Finder may need updating as well.
|
764
|
+
if(FINDER.experiment_view) FINDER.updateDialog();
|
755
765
|
}
|
756
766
|
}
|
757
767
|
|
@@ -761,34 +771,36 @@ class GUIExperimentManager extends ExperimentManager {
|
|
761
771
|
|
762
772
|
toggleChartCombi(n, shift, alt) {
|
763
773
|
// Set `n` to be the chart combination, or toggle if Shift-key is pressed,
|
764
|
-
// or execute single run if Alt-key is pressed
|
774
|
+
// or execute single run if Alt-key is pressed.
|
775
|
+
if(MODEL.running_experiment) {
|
776
|
+
// NOTE: do NOT do this while VM is solving, as this would interfere!
|
777
|
+
UI.notify('Run selection cannot be changed when an experiment is running');
|
778
|
+
return;
|
779
|
+
}
|
765
780
|
const x = this.selected_experiment;
|
766
781
|
if(x && alt && n >= 0) {
|
767
782
|
this.startExperiment(n);
|
768
783
|
return;
|
769
784
|
}
|
770
785
|
if(x && n < x.combinations.length) {
|
771
|
-
//
|
772
|
-
if(!shift) x.chart_combinations.length = 0;
|
773
|
-
// Toggle => add if not in selection, otherwise remove
|
786
|
+
// Toggle => add if not in selection, otherwise remove.
|
774
787
|
const ci = x.chart_combinations.indexOf(n);
|
775
788
|
if(ci < 0) {
|
789
|
+
// Clear current selection unless Shift-key is pressed.
|
790
|
+
if(!shift) x.chart_combinations.length = 0;
|
776
791
|
x.chart_combinations.push(n);
|
777
792
|
} else {
|
778
793
|
x.chart_combinations.splice(ci, 1);
|
779
794
|
}
|
780
795
|
}
|
781
796
|
this.updateData();
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
CHART_MANAGER.resetChartVectors();
|
790
|
-
CHART_MANAGER.updateDialog();
|
791
|
-
}
|
797
|
+
// Show the messages for this run in the monitor.
|
798
|
+
VM.setRunMessages(n);
|
799
|
+
// Update the chart.
|
800
|
+
CHART_MANAGER.resetChartVectors();
|
801
|
+
CHART_MANAGER.updateDialog();
|
802
|
+
// NOTE: Finder may need updating as well.
|
803
|
+
if(FINDER.experiment_view) FINDER.updateDialog();
|
792
804
|
}
|
793
805
|
|
794
806
|
runInfo(n) {
|
@@ -839,7 +851,7 @@ class GUIExperimentManager extends ExperimentManager {
|
|
839
851
|
info.html = html.join('');
|
840
852
|
return info;
|
841
853
|
}
|
842
|
-
// Fall-through (should not occur)
|
854
|
+
// Fall-through (should not occur).
|
843
855
|
return null;
|
844
856
|
}
|
845
857
|
|
@@ -848,7 +860,7 @@ class GUIExperimentManager extends ExperimentManager {
|
|
848
860
|
// NOTE: Skip when viewer is showing!
|
849
861
|
if(!UI.hidden('experiment-viewer')) return;
|
850
862
|
if(n < MODEL.experiments.length) {
|
851
|
-
// NOTE:
|
863
|
+
// NOTE: Mouse move over title in viewer passes n = -1.
|
852
864
|
const x = (n < 0 ? this.selected_experiment : MODEL.experiments[n]);
|
853
865
|
DOCUMENTATION_MANAGER.update(x, shift);
|
854
866
|
}
|
@@ -90,7 +90,8 @@ class GUIFileManager {
|
|
90
90
|
if(!UI.hidden('series-modal')) {
|
91
91
|
DATASET_MANAGER.series_data.value = data.split(';').join('\n');
|
92
92
|
} else {
|
93
|
-
|
93
|
+
// NOTE: FALSE indicates that data is *not* B62-encoded.
|
94
|
+
dataset.unpackDataString(data, false);
|
94
95
|
}
|
95
96
|
}
|
96
97
|
// NOTE: remove dataset from the "loading" list
|