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.
- package/console.js +150 -89
- package/package.json +1 -1
- package/server.js +1 -1
- package/static/scripts/linny-r-ctrl.js +16 -13
- package/static/scripts/linny-r-gui-paper.js +18 -18
- package/static/scripts/linny-r-gui-receiver.js +5 -5
- package/static/scripts/linny-r-model.js +17 -17
- package/static/scripts/linny-r-utils.js +20 -3
- package/static/scripts/linny-r-vm.js +145 -70
@@ -2390,8 +2390,13 @@ class VirtualMachine {
|
|
2390
2390
|
this.issue_list.length = 0;
|
2391
2391
|
this.issue_index = -1;
|
2392
2392
|
UI.updateIssuePanel();
|
2393
|
-
//
|
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:
|
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 +
|
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:
|
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:
|
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:
|
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
|
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,
|
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!
|
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:
|
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:
|
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(
|
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:
|
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:
|
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
|
-
//
|
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
|
-
//
|
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
|
-
//
|
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
|
-
//
|
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
|
-
//
|
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:
|
7808
|
-
// coefficients
|
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:
|
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:
|
7839
|
-
// generate cash IN or cash OUT
|
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:
|
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
|
-
|
7896
|
-
|
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
|
-
//
|
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(
|
8679
|
+
if(DEBUGGING) {
|
8605
8680
|
console.log(`-- ${vmi}(${vector.x.name}, ${vector.y.name})`);
|
8606
8681
|
}
|
8607
8682
|
// NOTE: Vectors should have equal length.
|