linny-r 1.6.7 → 1.6.8

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": "1.6.7",
3
+ "version": "1.6.8",
4
4
  "description": "Executable graphical language with WYSIWYG editor for MILP models",
5
5
  "main": "server.js",
6
6
  "scripts": {
@@ -443,6 +443,7 @@ class GUIController extends Controller {
443
443
  // Keep track of time since last message displayed on the infoline.
444
444
  this.time_last_message = new Date('01 Jan 2001 00:00:00 GMT');
445
445
  this.message_display_time = 3000;
446
+ this.last_message_type = '';
446
447
 
447
448
  // Initialize "main" modals, i.e., those that relate to the controller,
448
449
  // not to other dialog objects.
@@ -2572,28 +2573,32 @@ class GUIController extends Controller {
2572
2573
  // Displays message on infoline unless no type (= plain text) and some
2573
2574
  // info, warning or error message is already displayed
2574
2575
  super.setMessage(msg, type);
2576
+ const types = ['notification', 'warning', 'error'];
2575
2577
  let d = new Date(),
2576
2578
  t = d.getTime(),
2577
- dt = t - this.time_last_message;
2579
+ dt = t - this.time_last_message,
2580
+ mti = types.indexOf(type),
2581
+ lmti = types.indexOf(this.last_message_type);
2578
2582
  if(type) {
2579
- // Update global variable (and force display) only for "real" messages
2580
- this.time_last_message = t;
2581
- dt = this.message_display_time;
2582
- SOUNDS[type].play().catch(() => {
2583
- console.log('NOTICE: Sounds will only play after first user action');
2584
- });
2583
+ // Only log "real" messages.
2585
2584
  const
2586
2585
  now = [d.getHours(), d.getMinutes().toString().padStart(2, '0'),
2587
2586
  d.getSeconds().toString().padStart(2, '0')].join(':'),
2588
2587
  im = {time: now, text: msg, status: type};
2589
2588
  DOCUMENTATION_MANAGER.addMessage(im);
2590
- // When receiver is active, add message to its log
2589
+ // When receiver is active, add message to its log.
2591
2590
  if(RECEIVER.active) RECEIVER.log(`[${now}] ${msg}`);
2592
2591
  }
2593
- // Display text only if previous message has "timed out" or was plain text
2594
- if(dt >= this.message_display_time) {
2592
+ // Display text only if previous message has "timed out" or was less
2593
+ // urgent than this one.
2594
+ if(mti > lmti || dt >= this.message_display_time) {
2595
+ this.time_last_message = t;
2596
+ this.last_message_type = type;
2597
+ if(type) SOUNDS[type].play().catch(() => {
2598
+ console.log('NOTICE: Sounds will only play after first user action');
2599
+ });
2595
2600
  const il = document.getElementById('info-line');
2596
- il.classList.remove('error', 'warning', 'notification');
2601
+ il.classList.remove(...types);
2597
2602
  il.classList.add(type);
2598
2603
  il.innerHTML = msg;
2599
2604
  }
@@ -4526,6 +4526,21 @@ class VirtualMachine {
4526
4526
  }
4527
4527
  }
4528
4528
 
4529
+ severestIssue(list, result) {
4530
+ // Returns severest exception code or +/- INFINITY in `list`, or the
4531
+ // result of the computation that involves the elements of `list`.
4532
+ let issue = 0;
4533
+ for(let i = 0; i < list.length; i++) {
4534
+ if(list[i] <= VM.MINUS_INFINITY) {
4535
+ issue = Math.min(list[i], issue);
4536
+ } else if(list[i] >= VM.PLUS_INFINITY) {
4537
+ issue = Math.max(list[i], issue);
4538
+ }
4539
+ }
4540
+ if(issue) return issue;
4541
+ return result;
4542
+ }
4543
+
4529
4544
  calculateDependentVariables(block) {
4530
4545
  // Calculate the values of all model variables that depend on the
4531
4546
  // values of the decision variables output by the solver.
@@ -4539,7 +4554,7 @@ class VirtualMachine {
4539
4554
  cbl = this.actualBlockLength(block);
4540
4555
 
4541
4556
  // FIRST: Calculate the actual flows on links.
4542
- let b, bt, p, pl, ld;
4557
+ let b, bt, p, pl, ld, ci;
4543
4558
  for(let l in MODEL.links) if(MODEL.links.hasOwnProperty(l) &&
4544
4559
  !MODEL.ignored_entities[l]) {
4545
4560
  l = MODEL.links[l];
@@ -4552,6 +4567,8 @@ class VirtualMachine {
4552
4567
  // NOTE: Flows may have a delay!
4553
4568
  ld = l.actualDelay(b);
4554
4569
  bt = b - ld;
4570
+ // NOTE: Block index may fall beyond actual chunk length.
4571
+ ci = i - ld;
4555
4572
  // NOTE: Use non-zero level here to ignore non-zero values that
4556
4573
  // are very small relative to the bounds on the process
4557
4574
  // (typically values below the non-zero tolerance of the solver).
@@ -4575,20 +4592,22 @@ class VirtualMachine {
4575
4592
  // Similar to STARTUP, but now look in the shut-down list.
4576
4593
  pl = (p.shut_downs.indexOf(bt) < 0 ? 0 : 1);
4577
4594
  } else if(l.multiplier === VM.LM_INCREASE) {
4578
- pl = VM.keepException(pl, pl - p.actualLevel(bt - 1));
4595
+ const ppl = p.actualLevel(bt - 1);
4596
+ pl = this.severestIssue([pl, ppl], pl - ppl);
4579
4597
  } else if(l.multiplier === VM.LM_SUM || l.multiplier === VM.LM_MEAN) {
4580
4598
  // Level for `bt` counts as first value.
4581
4599
  let count = 1;
4582
4600
  // NOTE: Link delay may be < 0!
4583
4601
  if(ld < 0) {
4584
- // NOTE: Actual levels beyond end of period are undefined,
4602
+ // NOTE: Actual levels beyond the chunk length are undefined,
4585
4603
  // and should be ignored while summing / averaging.
4586
- if(bt >= p.level.length) pl = 0;
4604
+ if(ci >= cbl) pl = 0;
4587
4605
  // If so, take sum over t, t+1, ..., t+(d-1).
4588
4606
  for(let j = ld + 1; j <= 0; j++) {
4589
- // Again: ignore levels beyond end of period.
4590
- if(b - j < p.level.length) {
4591
- pl = VM.keepException(pl, pl + p.actualLevel(b - j));
4607
+ // Again: only consider levels up to the end of the chunk.
4608
+ if(ci - j < cbl) {
4609
+ const spl = p.actualLevel(b - j);
4610
+ pl = this.severestIssue([pl, spl], pl + spl);
4592
4611
  count++;
4593
4612
  }
4594
4613
  }
@@ -4597,22 +4616,24 @@ class VirtualMachine {
4597
4616
  for(let j = 0; j < ld; j++) {
4598
4617
  // NOTE: Actual levels before t=0 are considered equal to
4599
4618
  // the initial level, and hence should NOT be ignored.
4600
- pl = VM.keepException(pl, pl + p.actualLevel(b - j));
4619
+ const spl = p.actualLevel(b - j);
4620
+ pl = this.severestIssue([pl, spl], pl + spl);
4601
4621
  count++;
4602
4622
  }
4603
4623
  }
4604
4624
  if(l.multiplier === VM.LM_MEAN && count > 1) {
4605
4625
  // Average if more than 1 values have been summed.
4606
- pl = VM.keepException(pl, pl / count);
4626
+ pl = this.keepException(pl, pl / count);
4607
4627
  }
4608
4628
  } else if(l.multiplier === VM.LM_THROUGHPUT) {
4609
4629
  // NOTE: calculate throughput on basis of levels and rates,
4610
4630
  // as not all actual flows may have been computed yet
4611
4631
  pl = 0;
4612
4632
  for(let j = 0; j < p.inputs.length; j++) {
4613
- pl = VM.keepException(pl,
4614
- pl + (p.inputs[j].from_node.actualLevel(bt) *
4615
- p.inputs[j].relative_rate.result(bt)));
4633
+ const
4634
+ ipl = p.inputs[j].from_node.actualLevel(bt),
4635
+ rr = p.inputs[j].relative_rate.result(bt);
4636
+ pl = this.severestIssue([pl, ipl, rr], pl + ipl * rr);
4616
4637
  }
4617
4638
  } else if(l.multiplier === VM.LM_PEAK_INC) {
4618
4639
  // Actual flow over "peak increase" link is zero unless...
@@ -4627,17 +4648,10 @@ class VirtualMachine {
4627
4648
  }
4628
4649
  }
4629
4650
  // Preserve special values such as INF, UNDEFINED and VM error codes.
4630
- if(pl <= VM.MINUS_INFINITY || pl > VM.PLUS_INFINITY) {
4631
- l.actual_flow[b] = pl;
4632
- } else {
4633
- const rr = l.relative_rate.result(bt);
4634
- if(rr <= VM.MINUS_INFINITY || rr > VM.PLUS_INFINITY) {
4635
- l.actual_flow[b] = rr;
4636
- } else {
4637
- const af = rr * pl;
4638
- l.actual_flow[b] = (Math.abs(af) > VM.NEAR_ZERO ? af : 0);
4639
- }
4640
- }
4651
+ const
4652
+ rr = l.relative_rate.result(bt),
4653
+ af = this.severestIssue([pl, rr], rr * pl);
4654
+ l.actual_flow[b] = (Math.abs(af) > VM.NEAR_ZERO ? af : 0);
4641
4655
  b++;
4642
4656
  }
4643
4657
  }
@@ -5586,24 +5600,33 @@ Solver status = ${json.status}`);
5586
5600
  // levels and stock level), but do NOT overwrite "look-ahead" levels
5587
5601
  // if this block was not solved (indicated by the 4th parameter that
5588
5602
  // tests the status).
5589
- // NOTE: Appropriate status codes are solver-dependent.
5590
- this.setLevels(bnr, rl, json.data.x,
5591
- this.noSolutionStatus.indexOf(json.status) >= 0);
5592
- // NOTE: Post-process levels only AFTER the last round!
5593
- if(rl === this.lastRound) {
5594
- // Calculate data for all other dependent variables.
5595
- this.calculateDependentVariables(bnr);
5596
- // Add progress bar segment only now, knowing status AND slack use.
5597
- const issue = json.status !== 0 || this.error_count > 0;
5598
- if(issue) this.block_issues++;
5599
- // NOTE: in case of multiple rounds, use the sum of the round times.
5600
- const time = this.round_times.reduce((a, b) => a + b, 0);
5601
- this.round_times.length = 0;
5602
- this.solver_times[bnr - 1] = time;
5603
- const ssecs = this.round_secs.reduce((a, b) => a + b, 0);
5604
- this.solver_secs[bnr - 1] = (ssecs ? VM.sig4Dig(ssecs) : '0');
5605
- this.round_secs.length = 0;
5606
- MONITOR.addProgressBlock(bnr, issue, time);
5603
+ try {
5604
+ this.setLevels(bnr, rl, json.data.x,
5605
+ // NOTE: Appropriate status codes are solver-dependent.
5606
+ this.noSolutionStatus.indexOf(json.status) >= 0);
5607
+ // NOTE: Post-process levels only AFTER the last round!
5608
+ if(rl === this.lastRound) {
5609
+ // Calculate data for all other dependent variables.
5610
+ this.calculateDependentVariables(bnr);
5611
+ // Add progress bar segment only now, knowing status AND slack use.
5612
+ const issue = json.status !== 0 || this.error_count > 0;
5613
+ if(issue) this.block_issues++;
5614
+ // NOTE: in case of multiple rounds, use the sum of the round times.
5615
+ const time = this.round_times.reduce((a, b) => a + b, 0);
5616
+ this.round_times.length = 0;
5617
+ this.solver_times[bnr - 1] = time;
5618
+ const ssecs = this.round_secs.reduce((a, b) => a + b, 0);
5619
+ this.solver_secs[bnr - 1] = (ssecs ? VM.sig4Dig(ssecs) : '0');
5620
+ this.round_secs.length = 0;
5621
+ MONITOR.addProgressBlock(bnr, issue, time);
5622
+ }
5623
+ } catch(err) {
5624
+ const msg = `ERROR while processing solver data for block ${bnr}: ${err}`;
5625
+ console.log(msg);
5626
+ MONITOR.logMessage(bnr, msg);
5627
+ UI.alert(msg);
5628
+ this.stopSolving();
5629
+ this.halted = true;
5607
5630
  }
5608
5631
  // Free up memory.
5609
5632
  json = null;