linny-r 1.7.0 → 1.7.1

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.
@@ -2390,8 +2390,13 @@ class VirtualMachine {
2390
2390
  this.issue_list.length = 0;
2391
2391
  this.issue_index = -1;
2392
2392
  UI.updateIssuePanel();
2393
- // NOTE: Special tracking of potential solver license errors.
2393
+ // Special tracking of potential solver license errors.
2394
2394
  this.license_expired = 0;
2395
+ // Variables that will be decided by the solver again in the next
2396
+ // block must be "fixated" when, due to a negative link delay, they
2397
+ // would have consequences for the previous block (and these will be
2398
+ // ignored).
2399
+ this.variables_to_fixate = {};
2395
2400
  // Reset solver result arrays.
2396
2401
  this.round_times.length = 0;
2397
2402
  this.solver_times.length = 0;
@@ -2951,7 +2956,6 @@ class VirtualMachine {
2951
2956
  return obj.la_peak_inc[c];
2952
2957
  }
2953
2958
  const prior_level = obj.actualLevel(t);
2954
- //console.log('HERE obj prilev t', obj.displayName, prior_level, t, obj.level);
2955
2959
  if(type === 'OO') return prior_level > 0 ? 1 : 0;
2956
2960
  if(type === 'IZ') return prior_level === 0 ? 1 : 0;
2957
2961
  // Start-up at time t entails that t is in the list of start-up
@@ -3271,18 +3275,18 @@ class VirtualMachine {
3271
3275
  if(!MODEL.ignored_entities[k]) {
3272
3276
  p = MODEL.processes[k];
3273
3277
  lbx = p.lower_bound;
3274
- // NOTE: if UB = LB, set UB to LB only if LB is defined,
3278
+ // NOTE: If UB = LB, set UB to LB only if LB is defined,
3275
3279
  // because LB expressions default to -INF while UB expressions
3276
- // default to + INF
3280
+ // default to +INF.
3277
3281
  ubx = (p.equal_bounds && lbx.defined ? lbx : p.upper_bound);
3278
3282
  if(lbx.isStatic) lbx = lbx.result(0);
3279
3283
  if(ubx.isStatic) ubx = ubx.result(0);
3280
- // NOTE: pass TRUE as fourth parameter to indicate that +INF
3284
+ // NOTE: Pass TRUE as fourth parameter to indicate that +INF
3281
3285
  // and -INF can be coded as the infinity values used by the
3282
3286
  // solver, rather than the Linny-R values used to detect
3283
- // unbounded problems
3287
+ // unbounded problems.
3284
3288
  this.code.push([VMI_set_bounds, [p.level_var_index, lbx, ubx, true]]);
3285
- // Add level variable index to "fixed" list for specified rounds
3289
+ // Add level variable index to "fixed" list for specified rounds.
3286
3290
  const rf = p.actor.round_flags;
3287
3291
  if(rf != 0) {
3288
3292
  // Note: 32-bit integer `b` is used for bit-wise AND
@@ -4365,26 +4369,26 @@ class VirtualMachine {
4365
4369
  ncv_msg + ' is not a multiple of # columns', this.cols);
4366
4370
  // Assume no warnings or errors.
4367
4371
  this.error_count = 0;
4368
- // Set cash flows for all actors
4369
- // NOTE: all cash IN and cash OUT values should normally be non-negative,
4372
+ // Set cash flows for all actors.
4373
+ // NOTE: All cash IN and cash OUT values should normally be non-negative,
4370
4374
  // but since Linny-R permits negative lower bounds on processes, and also
4371
4375
  // negative link rates, cash flows may become negative. If that occurs,
4372
4376
  // the modeler should be warned.
4373
4377
  for(let o in MODEL.actors) if(MODEL.actors.hasOwnProperty(o)) {
4374
4378
  const a = MODEL.actors[o];
4375
- // NOTE: `b` is the index to be used for the vectors
4379
+ // NOTE: `b` is the index to be used for the vectors.
4376
4380
  let b = bb;
4377
- // Iterate over all time steps in this block
4378
- // NOTE: -1 because indices start at 1, but list is zero-based
4381
+ // Iterate over all time steps in this block.
4382
+ // NOTE: -1 because indices start at 1, but list is zero-based.
4379
4383
  let j = -1;
4380
4384
  for(let i = 0; i < abl; i++) {
4381
- // NOTE: cash coefficients computed by the solver must be scaled back
4385
+ // NOTE: Cash coefficients computed by the solver must be scaled back.
4382
4386
  a.cash_in[b] = this.checkForInfinity(
4383
4387
  x[a.cash_in_var_index + j] * this.cash_scalar);
4384
4388
  a.cash_out[b] = this.checkForInfinity(
4385
4389
  x[a.cash_out_var_index + j] * this.cash_scalar);
4386
4390
  a.cash_flow[b] = a.cash_in[b] - a.cash_out[b];
4387
- // Count occurrences of a negative cash flow (threshold -0.5 cent)
4391
+ // Count occurrences of a negative cash flow (threshold -0.5 cent).
4388
4392
  if(a.cash_in[b] < -0.005) {
4389
4393
  this.logMessage(block, `${this.WARNING}(t=${b}${round}) ` +
4390
4394
  a.displayName + ' cash IN = ' + a.cash_in[b].toPrecision(2));
@@ -4393,13 +4397,13 @@ class VirtualMachine {
4393
4397
  this.logMessage(block, `${this.WARNING}(t=${b}${round}) ` +
4394
4398
  a.displayName + ' cash IN = ' + a.cash_out[b].toPrecision(2));
4395
4399
  }
4396
- // Advance column offset in tableau by the # cols per time step
4400
+ // Advance column offset in tableau by the # cols per time step.
4397
4401
  j += this.cols;
4398
- // Advance to the next time step in this block
4402
+ // Advance to the next time step in this block.
4399
4403
  b++;
4400
4404
  }
4401
4405
  }
4402
- // Set production levels and start-up moments for all processes
4406
+ // Set production levels and start-up moments for all processes.
4403
4407
  for(let o in MODEL.processes) if(MODEL.processes.hasOwnProperty(o) &&
4404
4408
  !MODEL.ignored_entities[o]) {
4405
4409
  const
@@ -4560,6 +4564,8 @@ class VirtualMachine {
4560
4564
  bb = (block - 1) * MODEL.block_length + 1,
4561
4565
  cbl = this.actualBlockLength(block);
4562
4566
 
4567
+ // Start with an empty list of variables to "fixate" in the next block.
4568
+ this.variables_to_fixate = {};
4563
4569
  // FIRST: Calculate the actual flows on links.
4564
4570
  let b, bt, p, pl, ld, ci;
4565
4571
  for(let l in MODEL.links) if(MODEL.links.hasOwnProperty(l) &&
@@ -4574,6 +4580,22 @@ class VirtualMachine {
4574
4580
  // NOTE: Flows may have a delay!
4575
4581
  ld = l.actualDelay(b);
4576
4582
  bt = b - ld;
4583
+ // If delay < 0 AND this results in a block time beyond the
4584
+ // block length, this means that the level of the FROM node
4585
+ // must be "fixated" in the next block.
4586
+ const nbt = bt - bb - MODEL.block_length + 1;
4587
+ // NOTE: `nbt` (next block time) cannot be beyond the look-ahead
4588
+ // period, as for those time steps the levels are still undefined,
4589
+ // NOR can it be later than the duration of the (negative) delay
4590
+ // on the link.
4591
+ if(ld < 0 && nbt > 0 && nbt <= MODEL.look_ahead && nbt <= -ld) {
4592
+ this.addNodeToFixate(l.from_node, nbt,
4593
+ // NOTE: Use the level at time `bt` (i.e., in the future)
4594
+ // because that is the optimal level computed for this chunk
4595
+ // (in its look-ahead period) that should be maintained in
4596
+ // the next block.
4597
+ l.from_node.nonZeroLevel(bt));
4598
+ }
4577
4599
  // NOTE: Block index may fall beyond actual chunk length.
4578
4600
  ci = i - ld;
4579
4601
  // NOTE: Use non-zero level here to ignore non-zero values that
@@ -5028,7 +5050,7 @@ class VirtualMachine {
5028
5050
  let rem = (MODEL.runLength - MODEL.look_ahead) % MODEL.block_length;
5029
5051
  // If this remainder equals 0, the last block is a full chunk.
5030
5052
  if(rem === 0) return this.chunk_length;
5031
- // Otherwise, the last block if remainder + look-ahead time steps.
5053
+ // Otherwise, the last block has remainder + look-ahead time steps.
5032
5054
  return rem + MODEL.look_ahead;
5033
5055
  }
5034
5056
 
@@ -5058,6 +5080,33 @@ class VirtualMachine {
5058
5080
  return this.chunk_length * this.cols + this.chunk_variables.length;
5059
5081
  }
5060
5082
 
5083
+ addNodeToFixate(n, bt, level) {
5084
+ // Record that level of node `n` must be fixated for block time `bt`
5085
+ // in the next block by setting it to the specified level.
5086
+ const
5087
+ vi = n.level_var_index,
5088
+ oo = n.on_off_var_index,
5089
+ iz = n.is_zero_var_index;
5090
+ if(!this.variables_to_fixate.hasOwnProperty(vi)) {
5091
+ this.variables_to_fixate[vi] = {};
5092
+ }
5093
+ this.variables_to_fixate[vi][bt] = level;
5094
+ if(oo >= 0) {
5095
+ if(!this.variables_to_fixate.hasOwnProperty(oo)) {
5096
+ this.variables_to_fixate[oo] = {};
5097
+ }
5098
+ this.variables_to_fixate[oo][bt] = (
5099
+ Math.abs(level) < VM.NEAR_ZERO ? 0 : 1);
5100
+ }
5101
+ if(iz >= 0) {
5102
+ if(!this.variables_to_fixate.hasOwnProperty(iz)) {
5103
+ this.variables_to_fixate[iz] = {};
5104
+ }
5105
+ this.variables_to_fixate[iz][bt] = (
5106
+ Math.abs(level) < VM.NEAR_ZERO ? 1 : 0);
5107
+ }
5108
+ }
5109
+
5061
5110
  writeLpFormat(cplex=false) {
5062
5111
  // NOTE: Up to version 1.5.6, actual block length of last block used
5063
5112
  // to be shorter than the chunk length so as not to go beyond the
@@ -5701,6 +5750,37 @@ Solver status = ${json.status}`);
5701
5750
  this.logMessage(this.block_count, 'Zero columns -- nothing to solve');
5702
5751
  return;
5703
5752
  }
5753
+ // If negative delays require "fixating" variables for some number
5754
+ // of time steps, this must be logged in the monitor.
5755
+ const keys = Object.keys(this.variables_to_fixate);
5756
+ if(keys.length) {
5757
+ const msg = ['NOTE: Due to negative link delays, levels for ' +
5758
+ pluralS(keys.length, 'variable') + ' are pre-set:'];
5759
+ for(let i = 0; i < keys.length; i++) {
5760
+ const
5761
+ vi = parseInt(keys[i]),
5762
+ // NOTE: Subtract 1 because variable list is zero-based.
5763
+ vbl = this.variables[vi - 1],
5764
+ fv = this.variables_to_fixate[vi],
5765
+ fvk = Object.keys(fv),
5766
+ fvl = [];
5767
+ // Add constraints that fixate the levels directly to the tableau.
5768
+ for(let i = 0; i < fvk.length; i++) {
5769
+ const
5770
+ bt = fvk[i],
5771
+ pl = fv[bt],
5772
+ k = (bt - 1) * VM.cols + vi,
5773
+ row = {};
5774
+ row[k] = 1;
5775
+ VM.matrix.push(row);
5776
+ VM.right_hand_side.push(pl);
5777
+ VM.constraint_types.push(VM.EQ);
5778
+ fvl.push(pl + ' for bt=' + bt);
5779
+ }
5780
+ msg.push(`- ${vbl[1].displayName} [${vbl[0]}]: ${fvl.join(', ')}`);
5781
+ }
5782
+ this.logMessage(this.block_count, msg.join('\n'));
5783
+ }
5704
5784
  this.logMessage(this.block_count,
5705
5785
  'Creating model for block #' + this.blockWithRound);
5706
5786
  this.cbl = CONFIGURATION.progress_needle_interval * 200;
@@ -7467,19 +7547,19 @@ function VMI_set_bounds(args) {
7467
7547
  k = VM.offset + vi,
7468
7548
  r = VM.round_letters.indexOf(VM.round_sequence[VM.current_round]),
7469
7549
  // Optional fourth parameter indicates whether the solver's
7470
- // infinity values should be used
7550
+ // infinity values should be used.
7471
7551
  solver_inf = args.length > 3 && args[3],
7472
7552
  inf_val = (solver_inf ? VM.SOLVER_PLUS_INFINITY : VM.PLUS_INFINITY);
7473
7553
  let l,
7474
7554
  u,
7475
7555
  fixed = (vi in VM.fixed_var_indices[r - 1]);
7476
7556
  if(fixed) {
7477
- // Set both bounds equal to the level set in the previous round, or to 0
7478
- // if this is the first round
7557
+ // Set both bounds equal to the level set in the previous round,
7558
+ // or to 0 if this is the first round.
7479
7559
  if(VM.current_round) {
7480
7560
  l = vbl.actualLevel(VM.t);
7481
- // QUICK PATCH! should resolve that small non-zero process levels
7482
- // computed in prior round make problem infeasible
7561
+ // QUICK PATCH! Should resolve that small non-zero process levels
7562
+ // computed in prior round make problem infeasible.
7483
7563
  if(l < 0.0005) l = 0;
7484
7564
  } else {
7485
7565
  l = 0;
@@ -7487,7 +7567,7 @@ function VMI_set_bounds(args) {
7487
7567
  u = l;
7488
7568
  fixed = ' (FIXED ' + vbl.displayName + ')';
7489
7569
  } else {
7490
- // Set bounds as specified by the two arguments
7570
+ // Set bounds as specified by the two arguments.
7491
7571
  l = args[1];
7492
7572
  if(l instanceof Expression) l = l.result(VM.t);
7493
7573
  if(l === VM.UNDEFINED) l = 0;
@@ -7500,22 +7580,22 @@ function VMI_set_bounds(args) {
7500
7580
  }
7501
7581
  fixed = '';
7502
7582
  }
7503
- // NOTE: to see in the console whether fixing across rounds works, insert
7504
- // "fixed !== '' || " before DEBUGGING below
7583
+ // NOTE: To see in the console whether fixing across rounds works, insert
7584
+ // "fixed !== '' || " before DEBUGGING below.
7505
7585
  if(DEBUGGING) {
7506
7586
  console.log(['set_bounds [', k, '] ', vbl.displayName, ' t = ', VM.t,
7507
7587
  ' LB = ', VM.sig4Dig(l), ', UB = ', VM.sig4Dig(u), fixed].join(''));
7508
7588
  }
7509
- // NOTE: since the VM vectors for lower bounds and upper bounds are
7589
+ // NOTE: Since the VM vectors for lower bounds and upper bounds are
7510
7590
  // initialized with default values (0 for LB, +INF for UB), there is
7511
- // no need to set them
7591
+ // no need to set them.
7512
7592
  if(l !== 0 || u < inf_val) {
7513
7593
  VM.lower_bounds[k] = l;
7514
7594
  VM.upper_bounds[k] = u;
7515
7595
  // If associated node is FROM-node of a "peak increase" link, then
7516
7596
  // the "peak increase" variables of this node must have the highest
7517
7597
  // UB of the node (for all t in this block, hence MAX) MINUS their
7518
- // peak level in previous block
7598
+ // peak level in previous block.
7519
7599
  if(vbl.peak_inc_var_index >= 0) {
7520
7600
  u = Math.max(0, u - vbl.b_peak[VM.block_count - 1]);
7521
7601
  const
@@ -7560,8 +7640,8 @@ function VMI_add_const_to_coefficient(args) {
7560
7640
  console.log(`add_const_to_coefficient [${k}]: ${VM.sig4Dig(n)}`);
7561
7641
  }
7562
7642
  // A negative delay may result in a variable index beyond the tableau
7563
- // column range. Such "future variables" should be ignored.
7564
- if(d < 0 && k > VM.chunk_offset) return;
7643
+ // column range. Such "future variables" should always be ignored.
7644
+ if(k > VM.chunk_offset) return;
7565
7645
  if(k <= 0) {
7566
7646
  // NOTE: If `k` falls PRIOR to the start of the block being solved,
7567
7647
  // this means that the value of the decision variable X for which the
@@ -7577,7 +7657,7 @@ function VMI_add_const_to_coefficient(args) {
7577
7657
  if(DEBUGGING) {
7578
7658
  console.log(`--lookup[${k}]: ${vbl[0]} ${vbl[1].displayName} @ ${t} = ${pv}`);
7579
7659
  }
7580
- // NOTE: special cases for binary variables!
7660
+ // NOTE: Special cases for binary variables!
7581
7661
  VM.rhs -= pv * n;
7582
7662
  } else if(k in VM.coefficients) {
7583
7663
  VM.coefficients[k] += n;
@@ -7587,7 +7667,7 @@ function VMI_add_const_to_coefficient(args) {
7587
7667
  }
7588
7668
 
7589
7669
  function VMI_add_const_to_sum_coefficients(args) {
7590
- // NOTE: used to implement data links with SUM multiplier
7670
+ // NOTE: Used to implement data links with SUM multiplier.
7591
7671
  // `args`: [var_index, number, delay (, 1)]
7592
7672
  const vi = args[0];
7593
7673
  let d = args[2].object.actualDelay(VM.t),
@@ -7606,11 +7686,10 @@ function VMI_add_const_to_sum_coefficients(args) {
7606
7686
  d = -d;
7607
7687
  }
7608
7688
  for(let i = 0; i <= d; i++) {
7609
- // A negative delay may result in a variable index beyond the tableau
7610
- // column range. Such "future variables" should be ignored.
7689
+ // Variables beyond the chunk length should be ignored.
7611
7690
  if(k > VM.chunk_offset) return;
7612
7691
  if(k <= 0) {
7613
- // See NOTE in VMI_add_const_to_coefficient instruction
7692
+ // See NOTE in VMI_add_const_to_coefficient instruction.
7614
7693
  const vbl = VM.variables[vi - 1];
7615
7694
  if(DEBUGGING) {
7616
7695
  console.log('--lookup[' + k + ']: ' + vbl[0] + ' ' + vbl[1].displayName);
@@ -7632,24 +7711,23 @@ function VMI_add_var_to_coefficient(args) {
7632
7711
  let d = 0;
7633
7712
  if(args.length > 2 && args[2] instanceof Expression) {
7634
7713
  d = args[2].object.actualDelay(VM.t);
7635
- // 4th argument = 1 indicates "delay + 1"
7714
+ // 4th argument = 1 indicates "delay + 1".
7636
7715
  if(args.length > 3 && args[3]) d++;
7637
7716
  }
7638
7717
  const
7639
7718
  k = VM.offset + vi - d*VM.cols,
7640
7719
  t = VM.t - d;
7641
7720
  let r = args[1].result(t);
7642
- // Optional 5th parameter is a constant multiplier
7721
+ // Optional 5th parameter is a constant multiplier.
7643
7722
  if(args.length > 4) r *= args[4];
7644
7723
  if(DEBUGGING) {
7645
7724
  console.log('add_var_to_coefficient [' + k + ']: ' +
7646
7725
  args[1].variableName + ' (t = ' + t + ')');
7647
7726
  }
7648
- // A negative delay may result in a variable index beyond the tableau
7649
- // column range. Such "future variables" should be ignored.
7727
+ // Ignore "future variables".
7650
7728
  if(k > VM.chunk_offset) return;
7651
7729
  if(k <= 0) {
7652
- // See NOTE in VMI_add_const_to_coefficient instruction
7730
+ // See NOTE in VMI_add_const_to_coefficient instruction.
7653
7731
  const vbl = VM.variables[vi - 1];
7654
7732
  if(DEBUGGING) {
7655
7733
  console.log('--lookup[' + k + ']: ' + vbl[0] + ' ' + vbl[1].displayName);
@@ -7682,8 +7760,7 @@ function VMI_add_var_to_weighted_sum_coefficients(args) {
7682
7760
  d = -d;
7683
7761
  }
7684
7762
  for(let i = 0; i <= d; i++) {
7685
- // A negative delay may result in a variable index beyond the tableau
7686
- // column range. Such "future variables" should be ignored.
7763
+ // Ignore "future variables".
7687
7764
  if(k > VM.chunk_offset) return;
7688
7765
  let r = v.result(t);
7689
7766
  if(args.length > 3) r /= (d + 1);
@@ -7721,8 +7798,7 @@ function VMI_subtract_const_from_coefficient(args) {
7721
7798
  if(DEBUGGING) {
7722
7799
  console.log('subtract_const_from_coefficient [' + k + ']: ' + VM.sig4Dig(n));
7723
7800
  }
7724
- // A negative delay may result in a variable index beyond the tableau
7725
- // column range. Such "future variables" should be ignored.
7801
+ // Ignore "future variables".
7726
7802
  if(k > VM.chunk_offset) return;
7727
7803
  if(k <= 0) {
7728
7804
  // See NOTE in VMI_add_const_to_coefficient instruction
@@ -7764,11 +7840,10 @@ function VMI_subtract_var_from_coefficient(args) {
7764
7840
  console.log('subtract_var_from_coefficient [' + k + ']: ' +
7765
7841
  args[1].variableName + ' (t = ' + t + ')');
7766
7842
  }
7767
- // A negative delay may result in a variable index beyond the tableau
7768
- // column range. Such "future variables" should be ignored.
7843
+ // Ignore "future variables".
7769
7844
  if(k > VM.chunk_offset) return;
7770
7845
  if(k <= 0) {
7771
- // See NOTE in VMI_add_const_to_coefficient instruction
7846
+ // See NOTE in VMI_add_const_to_coefficient instruction.
7772
7847
  const vbl = VM.variables[vi - 1];
7773
7848
  if(DEBUGGING) {
7774
7849
  console.log('--lookup[' + k + ']: ' + vbl[0] + ' ' + vbl[1].displayName);
@@ -7804,30 +7879,30 @@ function VMI_update_cash_coefficient(args) {
7804
7879
  // NOTE: delay > 0 affects only which variable is to be used,
7805
7880
  // not the expressions for rates or prices!
7806
7881
  const t = VM.t - d;
7807
- // NOTE: this instruction is used only for objective function
7808
- // coefficients; previously computed decision variables and variables
7882
+ // NOTE: This instruction is used only for objective function
7883
+ // coefficients. Previously computed decision variables and variables
7809
7884
  // beyond the tableau column range (when delay < 0) can be ignored.
7810
7885
  if(k <= 0 || k > VM.chunk_offset) return;
7811
- // NOTE: peak increase can generate cash only at the first time
7886
+ // NOTE: Peak increase can generate cash only at the first time
7812
7887
  // step of a block (when VM.offset = 0) and at the first time step
7813
- // of the look-ahead period (when VM.offset = block length)
7888
+ // of the look-ahead period (when VM.offset = block length).
7814
7889
  if(type === VM.PEAK_INC &&
7815
7890
  VM.offset > 0 && VM.offset !== MODEL.block_length) return;
7816
- // First compute the result to be processed
7891
+ // First compute the result to be processed.
7817
7892
  let r = 0;
7818
7893
  if(type === VM.ONE_C) {
7819
7894
  r = args[4];
7820
7895
  } else if(type === VM.TWO_X || type === VM.PEAK_INC) {
7821
- // NOTE: "peak increase" always passes two expressions
7896
+ // NOTE: "peak increase" always passes two expressions.
7822
7897
  r = args[4].result(VM.t) * args[5].result(VM.t);
7823
7898
  } else if(type === VM.THREE_X) {
7824
7899
  r = args[4].result(VM.t) * args[5].result(VM.t) * args[6].result(VM.t);
7825
7900
  } else if(type === VM.SPIN_RES) {
7826
- // "spinning reserve" equals UB - level if level > 0, or 0
7901
+ // "spinning reserve" equals UB - level if level > 0, or 0.
7827
7902
  // The cash flow then equals ON/OFF*UB*price*rate - level*price*rate.
7828
7903
  // The ON/OFF variable index is passed as third argument, hence `plvi`
7829
7904
  // (process level variable index) as first extra parameter, plus three
7830
- // expressions (UB, price, rate)
7905
+ // expressions (UB, price, rate).
7831
7906
  const
7832
7907
  plvi = args[4],
7833
7908
  // NOTE: column of second variable will be relative to same offset
@@ -7835,9 +7910,9 @@ function VMI_update_cash_coefficient(args) {
7835
7910
  ub = args[5].result(VM.t),
7836
7911
  price_rate = args[6].result(VM.t) * args[7].result(VM.t);
7837
7912
  r = ub * price_rate;
7838
- // NOTE: the sign of r determines whether this spinning reserve will
7839
- // generate cash IN or cash OUT; the *subtracted* part hence be ADDED
7840
- // if r > 0, and SUBTRACTED if r < 0 (unlike the "primary" part r itself)
7913
+ // NOTE: The sign of r determines whether this spinning reserve will
7914
+ // generate cash IN or cash OUT. The *subtracted* part hence be ADDED
7915
+ // if r > 0, and SUBTRACTED if r < 0 (unlike the "primary" part r itself).
7841
7916
  if(r > 0) {
7842
7917
  if(plk in VM.cash_in_coefficients) {
7843
7918
  VM.cash_in_coefficients[plk] += price_rate;
@@ -7852,8 +7927,8 @@ function VMI_update_cash_coefficient(args) {
7852
7927
  }
7853
7928
  }
7854
7929
  }
7855
- // NOTE: for spinning reserve and highest increment, flow will always
7856
- // be PRODUCE
7930
+ // NOTE: For spinning reserve and highest increment, flow will always
7931
+ // be PRODUCE.
7857
7932
  if(flow === VM.CONSUME) r = -r;
7858
7933
  if(DEBUGGING) {
7859
7934
  const vbl = (vi <= this.cols ? VM.variables[vi - 1] :
@@ -7862,9 +7937,9 @@ function VMI_update_cash_coefficient(args) {
7862
7937
  vbl[1].displayName, ' (t = ', t, ') ', VM.CF_CONSTANTS[type], ' ',
7863
7938
  VM.CF_CONSTANTS[flow], ' r = ', VM.sig4Dig(r)].join(''));
7864
7939
  }
7865
- // Use look-ahead peak increase when offset > 0
7940
+ // Use look-ahead peak increase when offset > 0.
7866
7941
  if(type === VM.PEAK_INC && VM.offset) k++;
7867
- // Then update the cash flow: cash IN if r > 0, otherwise cash OUT
7942
+ // Then update the cash flow: cash IN if r > 0, otherwise cash OUT .
7868
7943
  if(r > 0) {
7869
7944
  if(k in VM.cash_in_coefficients) {
7870
7945
  VM.cash_in_coefficients[k] -= r;
@@ -7872,7 +7947,7 @@ function VMI_update_cash_coefficient(args) {
7872
7947
  VM.cash_in_coefficients[k] = -r;
7873
7948
  }
7874
7949
  } else if(r < 0) {
7875
- // NOTE: Test for r < 0 because no action is needed if r = 0
7950
+ // NOTE: Test for r < 0 because no action is needed if r = 0.
7876
7951
  if(k in VM.cash_out_coefficients) {
7877
7952
  VM.cash_out_coefficients[k] += r;
7878
7953
  } else {
@@ -7882,7 +7957,7 @@ function VMI_update_cash_coefficient(args) {
7882
7957
  }
7883
7958
 
7884
7959
  function VMI_add_throughput_to_coefficient(args) {
7885
- // Special instruction to deal with throughput calculation
7960
+ // Special instruction to deal with throughput calculation.
7886
7961
  // Function: to add the contribution of variable X to the level of
7887
7962
  // variable Z when Z depends (a.o.) on the throughput of variable Y, i.e.,
7888
7963
  // X --(r2,d2)--> Y --(r1,d1)--> Z
@@ -7892,8 +7967,9 @@ function VMI_add_throughput_to_coefficient(args) {
7892
7967
  vi = args[0],
7893
7968
  d1 = args[2].object.actualDelay(VM.t),
7894
7969
  d2 = (args[4] ? args[4].object.actualDelay(VM.t) : 0),
7895
- k = VM.offset + vi - (d1 + d2)*VM.cols,
7896
- t = VM.t - d1 - d2,
7970
+ dsum = d1 + d2,
7971
+ k = VM.offset + vi - dsum * VM.cols,
7972
+ t = VM.t - dsum,
7897
7973
  // Compute the value to be added to the coefficient
7898
7974
  v = args[1].result(VM.t) * args[3].result(VM.t - d1);
7899
7975
  if(DEBUGGING) {
@@ -7901,8 +7977,7 @@ function VMI_add_throughput_to_coefficient(args) {
7901
7977
  args[1].variableName + ' * ' + args[3].variableName +
7902
7978
  ' (t = ' + VM.t + ')');
7903
7979
  }
7904
- // A negative delay may result in a variable index beyond the tableau
7905
- // column range. Such "future variables" should be ignored.
7980
+ // Ignore "future variables".
7906
7981
  if(k > VM.chunk_offset) return;
7907
7982
  if(k <= 0) {
7908
7983
  const vbl = VM.variables[vi - 1];
@@ -8601,7 +8676,7 @@ function correlation_or_slope(x, c_or_s) {
8601
8676
  x.retop(x.cache[cache_key]);
8602
8677
  return;
8603
8678
  }
8604
- if(true||DEBUGGING) {
8679
+ if(DEBUGGING) {
8605
8680
  console.log(`-- ${vmi}(${vector.x.name}, ${vector.y.name})`);
8606
8681
  }
8607
8682
  // NOTE: Vectors should have equal length.