linny-r 2.0.9 → 2.0.11

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linny-r",
3
- "version": "2.0.9",
3
+ "version": "2.0.11",
4
4
  "description": "Executable graphical language with WYSIWYG editor for MILP models",
5
5
  "main": "server.js",
6
6
  "scripts": {
@@ -21,7 +21,11 @@
21
21
  "optimization",
22
22
  "simulation",
23
23
  "unit commitment",
24
- "generation expansion"
24
+ "industrial ecology",
25
+ "linearized power flow",
26
+ "loss approximation",
27
+ "generation expansion",
28
+ "grid expansion"
25
29
  ],
26
30
  "author": "Pieter W.G. Bots",
27
31
  "license": "MIT",
package/static/index.html CHANGED
@@ -1730,7 +1730,86 @@ Each line should represent the points of a different bound line.">
1730
1730
  </div>
1731
1731
  </div>
1732
1732
  </div>
1733
+
1734
+ <!-- the DATASET GROUP modal allows editing properties of multiple datasets -->
1735
+ <div id="datasetgroup-modal" class="modal">
1736
+ <div id="datasetgroup-dlg" class="inp-dlg">
1737
+ <div class="dlg-title">
1738
+ Dataset group properties
1739
+ <span id="datasetgroup-group"></span>
1740
+ <img class="cancel-btn" src="images/cancel.png">
1741
+ <img class="ok-btn" src="images/ok.png">
1742
+ </div>
1743
+ <div id="datasetgroup-prefix-lbl">Prefix:</div>
1744
+ <input id="datasetgroup-prefix" type="text" autocomplete="off">
1745
+ <div id="datasetgroup-default-lbl">Default value:</div>
1746
+ <input id="datasetgroup-default" type="text" autocomplete="off">
1747
+ <div id="datasetgroup-unit-lbl">Unit:</div>
1748
+ <input id="datasetgroup-unit" type="text" list="units-data" autocomplete="off">
1749
+ <div id="datasetgroup-periodic" class="box clear"></div>
1750
+ <div id="datasetgroup-periodic-lbl">Periodic</div>
1751
+ <div id="datasetgroup-array" class="box clear"></div>
1752
+ <div id="datasetgroup-array-lbl">Array</div>
1753
+ <div id="datasetgroup-no-time-msg">
1754
+ Arrays ignore time dimension
1755
+ </div>
1756
+ <div id="datasetgroup-time-step-lbl">Time step:</div>
1757
+ <input id="datasetgroup-time-scale" type="text" autocomplete="off">
1758
+ <select id="datasetgroup-time-unit">
1759
+ <option value="year">yr</option>
1760
+ <option value="week">wk</option>
1761
+ <option value="day">d</option>
1762
+ <option value="hour">h</option>
1763
+ <option value="minute">m</option>
1764
+ <option value="second">s</option>
1765
+ </select>
1766
+ <div id="datasetgroup-method-lbl">Method:</div>
1767
+ <select id="datasetgroup-method">
1768
+ <option value="nearest">at nearest t</option>
1769
+ <option value="w-mean">weighted mean</option>
1770
+ <option value="w-sum">weighted sum</option>
1771
+ <option value="max">maximum</option>
1772
+ </select>
1773
+ <div id="datasetgroup-separator"></div>
1774
+ <div id="datasetgroup-modif-header">Modifiers</div>
1775
+ <div id="datasetgroup-modif-titles">
1776
+ <span>Selector</span>
1777
+ <span style="margin-left: 35px">Expression</span>
1778
+ </div>
1779
+ <div id="datasetgroup-modif-scroll-area">
1780
+ <table id="datasetgroup-modif-table">
1781
+ </table>
1782
+ </div>
1783
+ <div id="datasetgroup-modif-buttons">
1784
+ <img id="dsg-add-modif-btn" class="btn enab"
1785
+ src="images/add-selector.png" title="Add new selector">
1786
+ <img id="dsg-rename-modif-btn" class="btn disab" src="images/rename.png"
1787
+ title="Rename selected modifier">
1788
+ <img id="dsg-edit-modif-btn" class="btn disab"
1789
+ src="images/edit.png" title="Edit expression of selected modifier">
1790
+ <img id="dsg-delete-modif-btn" class="btn disab"
1791
+ src="images/delete.png" title="Delete selected modifier"
1792
+ style="margin-left: 20px">
1793
+ </div>
1794
+ </div>
1795
+ </div>
1733
1796
 
1797
+ <!-- the GROUP SELECTOR dialog prompts for a new selector -->
1798
+ <div id="group-selector-modal" class="modal">
1799
+ <div id="group-selector-dlg" class="inp-dlg">
1800
+ <div class="dlg-title">
1801
+ <span id="group-selector-action">Add</span> selector
1802
+ <img class="cancel-btn" src="images/cancel.png">
1803
+ <img class="ok-btn" src="images/ok.png">
1804
+ </div>
1805
+ <input id="group-selector-name" type="text"
1806
+ title="Can contain only alphanumeric characters, +, -, and %
1807
+ NOTE: * and ? will be interpreted as wildcards"
1808
+ maxlength="10" autocomplete="off">
1809
+ <div id="group-selector-label">(1 to 10 characters)</div>
1810
+ </div>
1811
+ </div>
1812
+
1734
1813
  <!-- The CLONE dialog prompts for a prefix -->
1735
1814
  <div id="clone-modal" class="modal">
1736
1815
  <div id="clone-dlg" class="inp-dlg">
@@ -1863,17 +1942,7 @@ Each line should represent the points of a different bound line.">
1863
1942
  title="Delete selected dataset"
1864
1943
  style="position: absolute; right: 2px">
1865
1944
  </div>
1866
- <div id="dataset-header">
1867
- Datasets
1868
- <img id="ds-filter-btn" class="btn enab" src="images/filter.png"
1869
- title="Filter on name">
1870
- </div>
1871
- <div id="ds-filter-bar">
1872
- <input id="ds-filter-text" type="text"
1873
- placeholder="(name filtering pattern)"
1874
- title="Pattern may contain logical & (AND), | (OR) and ^ (NOT)
1875
- Start with = to find exact match, with ~ to match first characters">
1876
- </div>
1945
+ <div id="dataset-header">Datasets</div>
1877
1946
  <div id="dataset-scroll-area">
1878
1947
  <table id="dataset-table">
1879
1948
  </table>
@@ -1905,6 +1974,7 @@ Start with = to find exact match, with ~ to match first characters">
1905
1974
  <div id="dataset-export">&uarr;</div>
1906
1975
  </div>
1907
1976
  </div>
1977
+ <div id="dataset-prefixed-count" class="blink"></div>
1908
1978
  <div id="dataset-separator"></div>
1909
1979
  <div id="dataset-modif-header">(no dataset selected)</div>
1910
1980
  <div id="dataset-modif-ds-name"></div>
@@ -284,6 +284,7 @@ img.off {
284
284
  }
285
285
  }
286
286
 
287
+ div.blink,
287
288
  img.blink {
288
289
  animation: blink 1s step-start 0s infinite;
289
290
  }
@@ -396,6 +397,28 @@ input[type="text"]:disabled {
396
397
  background-color: #f4f0f2;
397
398
  }
398
399
 
400
+ td.same-not-changed {
401
+ color: #5060b0;
402
+ background-color: #f4f8ff;
403
+ }
404
+
405
+ td.not-same-not-changed {
406
+ color: #b06050;
407
+ background-color: #fff6f4;
408
+ }
409
+
410
+ td.same-changed {
411
+ color: #0000e0;
412
+ background-color: #f4f8ff;
413
+ text-shadow: 0px 0px 10px #00b0ff;
414
+ }
415
+
416
+ td.not-same-changed {
417
+ color: #e00000;
418
+ background-color: #fff6f4;
419
+ text-shadow: 0px 0px 10px #ff6000;
420
+ }
421
+
399
422
  input[type="text"].same-not-changed {
400
423
  color: #5060b0;
401
424
  background-color: #f4f8ff;
@@ -426,10 +449,12 @@ select.not-same-not-changed {
426
449
 
427
450
  select.same-changed {
428
451
  color: #0000e0;
452
+ text-shadow: 0px 0px 10px #00b0ff;
429
453
  }
430
454
 
431
455
  select.not-same-changed {
432
456
  color: #e00000;
457
+ text-shadow: 0px 0px 10px #ff6000;
433
458
  }
434
459
 
435
460
  textarea {
@@ -685,6 +710,7 @@ img.inline-cancel-btn:hover {
685
710
  #actor-group,
686
711
  #constraint-group,
687
712
  #cluster-group,
713
+ #datasetgroup-group,
688
714
  #link-group,
689
715
  #process-group,
690
716
  #product-group {
@@ -1910,6 +1936,7 @@ td.export {
1910
1936
 
1911
1937
  #actor-export,
1912
1938
  #dataset-export,
1939
+ #datasetgroup-export,
1913
1940
  #product-export {
1914
1941
  position: absolute;
1915
1942
  bottom: 10px;
@@ -1921,6 +1948,7 @@ td.export {
1921
1948
 
1922
1949
  #actor-import,
1923
1950
  #dataset-import,
1951
+ #datasetgroup-import,
1924
1952
  #product-import {
1925
1953
  position: absolute;
1926
1954
  bottom: 9px;
@@ -2607,6 +2635,7 @@ div.io-box {
2607
2635
  #power-grids-table,
2608
2636
  #dataset-table,
2609
2637
  #dataset-modif-table,
2638
+ #datasetgroup-modif-table,
2610
2639
  #equation-table,
2611
2640
  #experiment-table,
2612
2641
  #experiment-dim-table,
@@ -2637,6 +2666,15 @@ div.io-box {
2637
2666
  height: 59px;
2638
2667
  }
2639
2668
 
2669
+ #dataset-prefixed-count {
2670
+ position: absolute;
2671
+ bottom: 2px;
2672
+ left: 2px;
2673
+ max-width: 40%;
2674
+ color: #700090;
2675
+ font-weight: bold;
2676
+ }
2677
+
2640
2678
  #dataset-outcome {
2641
2679
  position: absolute;
2642
2680
  bottom: 0px;
@@ -2647,6 +2685,7 @@ div.io-box {
2647
2685
  }
2648
2686
 
2649
2687
  #dataset-outcome.not-selected,
2688
+ #datasetgroup-outcome.not-selected,
2650
2689
  #equation-outcome.not-selected {
2651
2690
  filter: saturate(0) contrast(50%) brightness(170%);
2652
2691
  }
@@ -2703,7 +2742,8 @@ div.io-box {
2703
2742
  left: calc(40% + 3px);
2704
2743
  }
2705
2744
 
2706
- #dataset-modif-buttons > img.btn {
2745
+ #dataset-modif-buttons > img.btn,
2746
+ #datasetgroup-modif-buttons > img.btn {
2707
2747
  height: 20px;
2708
2748
  width: 20px;
2709
2749
  }
@@ -2729,6 +2769,11 @@ tr.sel-set {
2729
2769
  background-color: #eff0ff;
2730
2770
  }
2731
2771
 
2772
+ tr.sel-set > td,
2773
+ tr.dataset-modif:hover > td {
2774
+ background-color: inherit !important;
2775
+ }
2776
+
2732
2777
  tr.def-sel {
2733
2778
  color: #400090;
2734
2779
  font-weight: bold;
@@ -2931,11 +2976,13 @@ td.equation-expression-multi {
2931
2976
  color: #e0e0e0;
2932
2977
  }
2933
2978
 
2934
- #dataset-blackbox.off {
2979
+ #dataset-blackbox.off,
2980
+ #datasetgroup-blackbox.off {
2935
2981
  color: #e0e0e0;
2936
2982
  }
2937
2983
 
2938
- #dataset-blackbox.on {
2984
+ #dataset-blackbox.on,
2985
+ #datasetgroup-blackbox.on {
2939
2986
  color: #403848;
2940
2987
  }
2941
2988
 
@@ -2999,18 +3046,21 @@ td.equation-expression-multi {
2999
3046
 
3000
3047
  /* NOTE: New and Rename modals must be above boundline data modal */
3001
3048
  #new-selector-modal,
3002
- #rename-selector-modal {
3049
+ #rename-selector-modal,
3050
+ #group-selector-modal {
3003
3051
  z-index: 102;
3004
3052
  }
3005
3053
 
3006
3054
  #new-selector-dlg,
3007
- #rename-selector-dlg {
3055
+ #rename-selector-dlg,
3056
+ #group-selector-dlg {
3008
3057
  width: 215px;
3009
3058
  height: 45px;
3010
3059
  }
3011
3060
 
3012
3061
  #new-selector-name,
3013
- #rename-selector-name {
3062
+ #rename-selector-name,
3063
+ #group-selector-name {
3014
3064
  position: absolute;
3015
3065
  bottom: 2px;
3016
3066
  left: 2px;
@@ -3018,7 +3068,8 @@ td.equation-expression-multi {
3018
3068
  }
3019
3069
 
3020
3070
  #new-selector-label,
3021
- #rename-selector-label {
3071
+ #rename-selector-label,
3072
+ #group-selector-label {
3022
3073
  position: absolute;
3023
3074
  left: 110px;
3024
3075
  bottom: 5px;
@@ -5104,6 +5155,198 @@ img.finder {
5104
5155
  font-size: 12px;
5105
5156
  }
5106
5157
 
5158
+ /* the DATASET GROUP modal permits editing properties of multiple datasets */
5159
+ #datasetgroup-dlg {
5160
+ width: 440px;
5161
+ height: 190px;
5162
+ }
5163
+
5164
+ #datasetgroup-prefix-lbl {
5165
+ position: absolute;
5166
+ top: 25px;
5167
+ left: 2px;
5168
+ }
5169
+
5170
+ #datasetgroup-prefix {
5171
+ position: absolute;
5172
+ top: 24px;
5173
+ left: 38px;
5174
+ width: 121px;
5175
+ font-size: 12px;
5176
+ }
5177
+
5178
+ #datasetgroup-default-lbl {
5179
+ position: absolute;
5180
+ top: 48px;
5181
+ left: 2px;
5182
+ }
5183
+
5184
+ #datasetgroup-default {
5185
+ position: absolute;
5186
+ top: 47px;
5187
+ left: 79px;
5188
+ width: 80px;
5189
+ font-size: 12px;
5190
+ }
5191
+
5192
+ #datasetgroup-unit-lbl {
5193
+ position: absolute;
5194
+ top: 71px;
5195
+ left: 2px;
5196
+ }
5197
+
5198
+ #datasetgroup-unit {
5199
+ position: absolute;
5200
+ top: 70px;
5201
+ left: 32px;
5202
+ width: 70px;
5203
+ font-size: 12px;
5204
+ }
5205
+
5206
+ #datasetgroup-periodic {
5207
+ position: absolute;
5208
+ top: 90px;
5209
+ left: 0px;
5210
+ }
5211
+
5212
+ #datasetgroup-periodic-lbl {
5213
+ position: absolute;
5214
+ top: 92px;
5215
+ left: 22px;
5216
+ }
5217
+
5218
+ #datasetgroup-array {
5219
+ position: absolute;
5220
+ top: 90px;
5221
+ left: 77px;
5222
+ }
5223
+
5224
+ #datasetgroup-array-lbl {
5225
+ position: absolute;
5226
+ top: 92px;
5227
+ left: 99px;
5228
+ }
5229
+
5230
+ #datasetgroup-no-time-msg {
5231
+ position: absolute;
5232
+ top: 111px;
5233
+ left: 1px;
5234
+ width: 164px;
5235
+ height: 31px;
5236
+ z-index: 1;
5237
+ background-color: inherit;
5238
+ font-style: italic;
5239
+ padding-top: 15px;
5240
+ text-align: center;
5241
+ display: none;
5242
+ }
5243
+
5244
+ #datasetgroup-time-step-lbl {
5245
+ position: absolute;
5246
+ top: 113px;
5247
+ left: 2px;
5248
+ }
5249
+
5250
+ #datasetgroup-time-scale {
5251
+ position: absolute;
5252
+ top: 112px;
5253
+ left: 60px;
5254
+ width: 50px;
5255
+ font-size: 12px;
5256
+ }
5257
+
5258
+ #datasetgroup-time-unit {
5259
+ position: absolute;
5260
+ top: 113px;
5261
+ left: 116px;
5262
+ height: 19px;
5263
+ width: 45px;
5264
+ font-size: 12px;
5265
+ }
5266
+
5267
+ #datasetgroup-method-lbl {
5268
+ position: absolute;
5269
+ top: 137px;
5270
+ left: 2px;
5271
+ }
5272
+
5273
+ #datasetgroup-method {
5274
+ position: absolute;
5275
+ top: 135px;
5276
+ left: 50px;
5277
+ height: 21px;
5278
+ width: 111px;
5279
+ font-size: 12px;
5280
+ }
5281
+
5282
+ #datasetgroup-blackbox {
5283
+ position: absolute;
5284
+ bottom: 7px;
5285
+ left: 124px;
5286
+ width: 16px;
5287
+ height: 16px;
5288
+ font-size: 19px;
5289
+ cursor: pointer;
5290
+ color: #e0e0e0;
5291
+ }
5292
+
5293
+ #datasetgroup-outcome {
5294
+ position: absolute;
5295
+ bottom: 1px;
5296
+ left: 144px;
5297
+ width: 16px;
5298
+ height: 16px;
5299
+ cursor: pointer;
5300
+ }
5301
+
5302
+ #datasetgroup-io {
5303
+ position: absolute;
5304
+ bottom: -2px;
5305
+ left: 144px;
5306
+ font-size: 18px;
5307
+ color: Silver;
5308
+ cursor: pointer;
5309
+ }
5310
+
5311
+ #datasetgroup-separator {
5312
+ position: absolute;
5313
+ top: 23px;
5314
+ left: 165px;
5315
+ height: 164px;
5316
+ width: 3px;
5317
+ border-left: 1.5px Silver ridge;
5318
+ }
5319
+
5320
+ #datasetgroup-modif-header {
5321
+ position: absolute;
5322
+ top: 22px;
5323
+ left: 170px;
5324
+ font-style: italic;
5325
+ }
5326
+
5327
+ #datasetgroup-modif-titles {
5328
+ position: absolute;
5329
+ top: 38px;
5330
+ left: 170px;
5331
+ font-weight: 600;
5332
+ }
5333
+
5334
+ #datasetgroup-modif-scroll-area {
5335
+ position: absolute;
5336
+ top: 54px;
5337
+ left: 170px;
5338
+ width: 267px;
5339
+ height: 110px;
5340
+ overflow-y: auto;
5341
+ border-top: 1px solid Silver;
5342
+ }
5343
+
5344
+ #datasetgroup-modif-buttons {
5345
+ position: absolute;
5346
+ bottom: 1px;
5347
+ left: 170px;
5348
+ }
5349
+
5107
5350
  /* the MONITOR DIALOG displays solver progress and messages */
5108
5351
  #monitor-dlg {
5109
5352
  display: none;
@@ -279,19 +279,19 @@ class Controller {
279
279
  // Methods to ensure proper naming of entities.
280
280
 
281
281
  cleanName(name) {
282
- // Returns `name` without the object-attribute separator |, backslashes,
282
+ // Return `name` without the object-attribute separator |, backslashes,
283
283
  // and leading and trailing whitespace, and with all internal whitespace
284
284
  // reduced to a single space.
285
285
  name = name.replace(this.OA_SEPARATOR, ' ')
286
286
  .replace(/\||\\/g, ' ').trim()
287
287
  .replace(/\s\s+/g, ' ');
288
- // NOTE: this may still result in a single space, which is not a name
288
+ // NOTE: This may still result in a single space, which is not a name.
289
289
  if(name === ' ') return '';
290
290
  return name;
291
291
  }
292
292
 
293
293
  validName(name) {
294
- // Returns TRUE if `name` is a valid Linny-R entity name. These names
294
+ // Return TRUE if `name` is a valid Linny-R entity name. These names
295
295
  // must not be empty strings, may not contain brackets, backslashes or
296
296
  // vertical bars, may not end with a colon, and must start with an
297
297
  // underscore, a letter or a digit.
@@ -361,20 +361,84 @@ class Controller {
361
361
  // Replace a leading colon in `name` by `prefix`.
362
362
  // If `name` identifies a link or a constraint, this is applied to
363
363
  // both node names.
364
+ // NOTE: To give the modeler more control over what to use as prefix,
365
+ // successive colons indicate that a shorter prefix should be used.
366
+ // For example, when the prefix is XXX: YYY: ZZZ, two colons mean
367
+ // "use only XXX: YYY:", three colons "use only XXX:", etc.
364
368
  const
365
369
  arrow = (name.indexOf(this.LINK_ARROW) >= 0 ?
366
370
  this.LINK_ARROW : this.CONSTRAINT_ARROW),
367
371
  nodes = name.split(arrow);
368
372
  for(let i = 0; i < nodes.length; i++) {
369
- nodes[i] = nodes[i].replace(/^:\s*/, prefix)
373
+ // First check for the special case of just a leading colon plus
374
+ // possibly an entity attribute.
375
+ // NOTE:
376
+ const m = nodes[i].match(/^(:+)\s*(\|[^\|]*)/);
377
+ if(m) {
378
+ // Split prefix in parts.
379
+ const p = prefix.split(UI.PREFIXER);
380
+ // Remove last (empty!) substring.
381
+ p.pop();
382
+ // Shorten prefix by the number of successive colons minus 1.
383
+ for(let i = 1; i < m[1].length; i++) p.pop();
384
+ p.push('');
385
+ // New name is just the (shortened) prefix without its last ": ".
386
+ nodes[i] = p.join(UI.PREFIXER).replace(/:\s*$/, '') + m[2];
387
+ } else {
388
+ // Prefix is typically leading, so try to replace this first.
389
+ const m = nodes[i].match(/^(:+)/);
390
+ if(m) {
391
+ // Shorten prefix by the number of successive colons minus 1.
392
+ let p = prefix.split(UI.PREFIXER);
393
+ p.pop();
394
+ for(let i = 1; i < m[1].length; i++) p.pop();
395
+ p.push('');
396
+ nodes[i] = nodes[i].replace(/^:+\s*/, p.join(UI.PREFIXER));
397
+ } else {
398
+ // If no change, try to replace an embedded double prefix.
370
399
  // NOTE: An embedded double prefix, e.g., "xxx: : yyy" indicates
371
400
  // that the second colon+space should be replaced by the prefix.
372
- // This "double prefix" may occur only once in an entity name,
373
- // hence no global regexp.
374
- .replace(/(\w+):\s+:\s+(\w+)/, `$1: ${prefix}$2`);
401
+ const m = nodes[i].match(/(\w+):\s+(:+)\s+(\w+)/);
402
+ if(m) {
403
+ // Shorten prefix by the number of successive colons minus 1.
404
+ let p = prefix.split(UI.PREFIXER);
405
+ p.pop();
406
+ for(let i = 1; i < m[2].length; i++) p.pop();
407
+ p.push('');
408
+ prefix = p.join(UI.PREFIXER);
409
+ nodes[i] = `${m[1]}: ${prefix}${m[3]}`;
410
+ }
411
+ }
412
+ }
375
413
  }
376
414
  return nodes.join(arrow);
377
415
  }
416
+
417
+ entityPrefix(name) {
418
+ // Return the prefix of `name` with its trailing colon+space.
419
+ const
420
+ arrow = (name.indexOf(this.LINK_ARROW) >= 0 ?
421
+ this.LINK_ARROW : this.CONSTRAINT_ARROW),
422
+ nodes = name.split(arrow);
423
+ if(nodes.length === 1) return this.completePrefix(name);
424
+ // For names of links and constraints, it depends:
425
+ const
426
+ fn = nodes[0],
427
+ tn = nodes[1];
428
+ if(fn.indexOf(UI.PREFIXER) >= 0) {
429
+ if(tn.indexOf(UI.PREFIXER) >= 0) {
430
+ // If BOTH nodes are prefixed, use the longest prefix that these
431
+ // nodes have in common...
432
+ return UI.sharedPrefix(fn, tn) + UI.PREFIXER;
433
+ }
434
+ // .. otherwise, return the FROM node prefix.
435
+ return UI.completePrefix(fn);
436
+ }
437
+ // No FROM node prefix => return the TO node prefix (if any)
438
+ if(tn.indexOf(UI.PREFIXER) >= 0) return UI.completePrefix(tn);
439
+ // No prefixers => empty prefix.
440
+ return '';
441
+ }
378
442
 
379
443
  tailNumber(name) {
380
444
  // Return the string of digits at the end of `name`. If not there,
@@ -429,7 +493,6 @@ class Controller {
429
493
  return pan1.length - pan2.length;
430
494
  }
431
495
 
432
-
433
496
  nameToID(name) {
434
497
  // Return a name in lower case with link arrow replaced by three
435
498
  // underscores, constraint link arrow by four underscores, and spaces
@@ -1272,7 +1335,7 @@ class ExperimentManager {
1272
1335
  MODEL.round_sequence = asel.round_sequence;
1273
1336
  }
1274
1337
  }
1275
- // Only now compute the simulation run time (number of time steps)
1338
+ // Only now compute the simulation run time (number of time steps).
1276
1339
  xr.time_steps = MODEL.end_period - MODEL.start_period + 1;
1277
1340
  VM.callback = this.callback;
1278
1341
  // NOTE: Asynchronous call. All follow-up actions must be performed
@@ -1282,18 +1345,18 @@ class ExperimentManager {
1282
1345
  }
1283
1346
 
1284
1347
  processRun() {
1285
- // This method is called by the solveBlocks method of the Virtual Machine
1348
+ // This method is called by the solveBlocks method of the Virtual Machine.
1286
1349
  const x = MODEL.running_experiment;
1287
1350
  if(!x) return;
1288
1351
  const aci = x.active_combination_index;
1289
1352
  if(MODEL.solved) {
1290
- // NOTE: addresults will call processRestOfRun when completed
1353
+ // NOTE: addresults will call processRestOfRun when completed.
1291
1354
  x.runs[aci].addResults();
1292
1355
  } else {
1293
1356
  // Do not add results...
1294
1357
  UI.warn(`Model run #${aci} incomplete -- results will be invalid`);
1295
- // ... but do perform the usual post-processing
1296
- // NOTE: when sensitivity analysis is being performed, switch back to SA
1358
+ // ... but do perform the usual post-processing.
1359
+ // NOTE: When sensitivity analysis is being performed, switch back to SA.
1297
1360
  if(SENSITIVITY_ANALYSIS.experiment) {
1298
1361
  SENSITIVITY_ANALYSIS.processRestOfRun();
1299
1362
  } else {