linny-r 2.1.4 → 2.1.6

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.
@@ -395,6 +395,9 @@ class Paper {
395
395
  id = 'i_n_a_c_t_i_v_e__t_r_i_a_n_g_l_e__t_i_p__ID';
396
396
  this.inactive_triangle = `url(#${id})`;
397
397
  this.addMarker(defs, id, tri, 8, 'silver');
398
+ id = 'i_g_n_o_r_e__t_r_i_a_n_g_l_e__t_i_p__ID';
399
+ this.ignore_triangle = `url(#${id})`;
400
+ this.addMarker(defs, id, tri, 8, this.palette.ignore);
398
401
  id = 'o_p_e_n__t_r_i_a_n_g_l_e__t_i_p__ID*';
399
402
  this.open_triangle = `url(#${id})`;
400
403
  this.addMarker(defs, id, tri, 7.5, 'white');
@@ -1034,9 +1037,9 @@ class Paper {
1034
1037
 
1035
1038
  // Arrows having both "from" and "to" are displayed as "real" arrows
1036
1039
  // The hidden nodes list must contain the nodes that have no position
1037
- // in the cluster being drawn
1038
- // NOTE: products are "hidden" typically when this arrow represents multiple
1039
- // links, but also if it is a single link from a cluster to a process
1040
+ // in the cluster being drawn.
1041
+ // NOTE: Products are "hidden" typically when this arrow represents multiple
1042
+ // links, but also if it is a single link from a cluster to a process.
1040
1043
  const
1041
1044
  from_c = from_nb instanceof Cluster,
1042
1045
  to_c = to_nb instanceof Cluster,
@@ -1049,12 +1052,12 @@ class Paper {
1049
1052
  fn = lnk.from_node,
1050
1053
  tn = lnk.to_node;
1051
1054
  if(fn instanceof Product && fn != from_nb && fn != to_nb) {
1052
- // Add node only if they not already shown at EITHER end of the arrow
1055
+ // Add node only if they not already shown at EITHER end of the arrow.
1053
1056
  addDistinct(fn, arrw.hidden_nodes);
1054
- // Count number of data flows represented by arrow
1057
+ // Count number of data flows represented by arrow.
1055
1058
  if(tn.is_data) data_flows++;
1056
1059
  }
1057
- // NOTE: no ELSE IF, because BOTH link nodes can be products
1060
+ // NOTE: No ELSE IF, because BOTH link nodes can be products.
1058
1061
  if(tn instanceof Product && tn != from_nb && tn != to_nb) {
1059
1062
  addDistinct(tn, arrw.hidden_nodes);
1060
1063
  // Count number of data flows represented by arrow
@@ -1063,7 +1066,7 @@ class Paper {
1063
1066
  }
1064
1067
  }
1065
1068
 
1066
- // NEXT: some more local variables
1069
+ // NEXT: Some more local variables.
1067
1070
  fnx = from_nb.x + dx;
1068
1071
  fny = from_nb.y + dy;
1069
1072
  fnw = from_nb.width;
@@ -1088,19 +1091,19 @@ class Paper {
1088
1091
  tnh = 24;
1089
1092
  }
1090
1093
 
1091
- // Do not draw arrow if so short that it is hidden by its FROM and TO nodes
1094
+ // Do not draw arrow if so short that it is hidden by its FROM and TO nodes.
1092
1095
  if((Math.abs(fnx - tnx) < (fnw + tnw)/2) &&
1093
1096
  (Math.abs(fny - tny) <= (fnh + tnh)/2)) {
1094
1097
  return false;
1095
1098
  }
1096
1099
 
1097
- // Adjust node heights if nodes are thick-rimmed
1100
+ // Adjust node heights if nodes are thick-rimmed.
1098
1101
  if((from_nb instanceof Product) && from_nb.is_buffer) fnh += 2;
1099
1102
  if((to_nb instanceof Product) && to_nb.is_buffer) tnh += 2;
1100
- // Get horizontal distance dx and vertical distance dy of the node centers
1103
+ // Get horizontal distance dx and vertical distance dy of the node centers.
1101
1104
  dx = tnx - fnx;
1102
1105
  dy = tny - fny;
1103
- // If dx is less than half a pixel, draw a vertical line
1106
+ // If dx is less than half a pixel, draw a vertical line.
1104
1107
  if(Math.abs(dx) < 0.5) {
1105
1108
  arrw.from_x = fnx;
1106
1109
  arrw.to_x = fnx;
@@ -1112,11 +1115,11 @@ class Paper {
1112
1115
  arrw.to_y = tny + tnh/2;
1113
1116
  }
1114
1117
  } else {
1115
- // Now dx > 0, so no division by zero can occur when calculating dy/dx
1116
- // First compute X and Y of tail (FROM node)
1118
+ // Now dx > 0, so no division by zero can occur when calculating dy/dx.
1119
+ // First compute X and Y of tail (FROM node).
1117
1120
  w = (from_nb instanceof Product ? from_nb.frame_width : fnw);
1118
1121
  if(Math.abs(dy / dx) >= Math.abs(fnh / w)) {
1119
- // Arrow connects to horizontal edge
1122
+ // Arrow connects to horizontal edge.
1120
1123
  arrw.from_y = (dy > 0 ? fny + fnh/2 : fny - fnh/2);
1121
1124
  arrw.from_x = fnx + fnh/2 * dx / Math.abs(dy);
1122
1125
  } else if(from_nb instanceof Product) {
@@ -1127,7 +1130,7 @@ class Paper {
1127
1130
  dd = fnw/2;
1128
1131
  nn = (-dd - Math.sqrt(rr - aa * dd * dd + aa * rr)) / (1 + aa);
1129
1132
  if(dx > 0) {
1130
- // link points towards the right
1133
+ // link points towards the right.
1131
1134
  arrw.from_x = fnx - nn;
1132
1135
  arrw.from_y = fny - nn * dy / dx;
1133
1136
  } else {
@@ -1135,28 +1138,28 @@ class Paper {
1135
1138
  arrw.from_y = fny + nn * dy / dx;
1136
1139
  }
1137
1140
  } else {
1138
- // Rectangular box
1141
+ // Rectangular box.
1139
1142
  arrw.from_x = (dx > 0 ? fnx + w/2 : fnx - w/2);
1140
1143
  arrw.from_y = fny + w/2 * dy / Math.abs(dx);
1141
1144
  }
1142
- // Then compute X and Y of head (TO node)
1145
+ // Then compute X and Y of head (TO node).
1143
1146
  w = (to_nb instanceof Product ? to_nb.frame_width : tnw);
1144
1147
  dx = arrw.from_x - tnx;
1145
1148
  dy = arrw.from_y - tny;
1146
1149
  if(Math.abs(dx) > 0) {
1147
1150
  if(Math.abs(dy / dx) >= Math.abs(tnh / w)) {
1148
- // Connects to horizontal edge
1151
+ // Connects to horizontal edge.
1149
1152
  arrw.to_y = (dy > 0 ? tny + tnh/2 : tny - tnh/2);
1150
1153
  arrw.to_x = tnx + tnh/2 * dx / Math.abs(dy);
1151
1154
  } else if(to_nb instanceof Product) {
1152
- // Node with semicircular sides}
1155
+ // Node with semicircular sides.
1153
1156
  tnw = to_nb.frame_width;
1154
1157
  rr = (tnh/2) * (tnh/2); // R square
1155
1158
  aa = (dy / dx) * (dy / dx); // A square
1156
1159
  dd = tnw/2;
1157
1160
  nn = (-dd - Math.sqrt(rr - aa*(dd*dd - rr))) / (1 + aa);
1158
1161
  if(dx > 0) {
1159
- // Link points towards the right
1162
+ // Link points towards the right.
1160
1163
  arrw.to_x = tnx - nn;
1161
1164
  arrw.to_y = tny - nn * dy / dx;
1162
1165
  } else {
@@ -1164,30 +1167,30 @@ class Paper {
1164
1167
  arrw.to_y = tny + nn * dy / dx;
1165
1168
  }
1166
1169
  } else {
1167
- // Rectangular node
1170
+ // Rectangular node.
1168
1171
  arrw.to_x = (dx > 0 ? tnx + w/2 : tnx - w/2);
1169
1172
  arrw.to_y = tny + w/2 * dy / Math.abs(dx);
1170
1173
  }
1171
1174
  }
1172
1175
  }
1173
1176
 
1174
- // Assume default arrow properties
1177
+ // Assume default arrow properties.
1175
1178
  sda = 'none';
1176
1179
  stroke_color = (ignored ? this.palette.ignore : this.palette.node_rim);
1177
1180
  stroke_width = 1.5;
1178
1181
  arrow_start = 'none';
1179
- arrow_end = this.triangle;
1180
- // Default multi-flow values are: NO multiflow, NOT congested or reversed
1182
+ arrow_end = (ignored ? this.ignore_triangle : this.triangle);
1183
+ // Default multi-flow values are: NO multiflow, NOT congested or reversed.
1181
1184
  let mf = [0, 0, 0, false, false],
1182
1185
  reversed = false;
1183
1186
  // These may need to be modified due to actual flow, etc.
1184
1187
  if(arrw.links.length === 1) {
1185
- // Display link properties of a specific link if arrow is plain
1188
+ // Display link properties of a specific link if arrow is plain.
1186
1189
  luc = arrw.links[0];
1187
- ignored = MODEL.ignored_entities[luc.identifier];
1190
+ ignored = ignored || MODEL.ignored_entities[luc.identifier];
1188
1191
  if(MODEL.solved && !ignored) {
1189
1192
  // Draw arrow in dark blue if a flow occurs, or in a lighter gray
1190
- // if NO flow occurs
1193
+ // if NO flow occurs.
1191
1194
  af = luc.actualFlow(MODEL.t);
1192
1195
  if(Math.abs(af) > VM.SIG_DIF_FROM_ZERO) {
1193
1196
  // NOTE: negative flow should affect arrow heads only when link has
@@ -1203,12 +1206,13 @@ class Paper {
1203
1206
  arrow_end = this.active_triangle;
1204
1207
  }
1205
1208
  } else {
1206
- stroke_color = (MODEL.ignored_entities[luc.identifier] ?
1207
- this.palette.ignore : 'silver');
1209
+ stroke_color = 'silver';
1208
1210
  arrow_end = this.inactive_triangle;
1209
1211
  }
1210
- } else {
1212
+ } else if(ignored) {
1211
1213
  af = VM.UNDEFINED;
1214
+ stroke_color = this.palette.ignore;
1215
+ arrow_end = this.ignore_triangle;
1212
1216
  }
1213
1217
  if(luc.from_node instanceof Process) {
1214
1218
  proc = luc.from_node;
@@ -1225,12 +1229,12 @@ class Paper {
1225
1229
  arrow_end = this.feedback_triangle;
1226
1230
  }
1227
1231
  }
1228
- // Data link => dotted line
1232
+ // Data link => dotted line.
1229
1233
  if(luc.dataOnly) {
1230
1234
  sda = UI.sda.dot;
1231
1235
  }
1232
1236
  if(luc.selected) {
1233
- // Draw arrow line thick and in red
1237
+ // Draw arrow line thick and in red.
1234
1238
  stroke_color = this.palette.select;
1235
1239
  stroke_width = 2;
1236
1240
  if(arrow_end == this.open_wedge) {
@@ -1239,7 +1243,6 @@ class Paper {
1239
1243
  arrow_end = this.selected_triangle;
1240
1244
  }
1241
1245
  }
1242
- if(ignored) stroke_color = this.palette.ignore;
1243
1246
  } else {
1244
1247
  // A composite arrow is visualized differently, depending on the number
1245
1248
  // of related products and the direction of the underlying links:
@@ -1264,23 +1267,23 @@ class Paper {
1264
1267
  if(arrw.bidirectional) arrow_start = arrow_end;
1265
1268
  }
1266
1269
  // Correct the start and end points of the shaft for the stroke width
1267
- // and size and number of the arrow heads
1268
- // NOTE: re-use of dx and dy for different purpose!
1270
+ // and size and number of the arrow heads.
1271
+ // NOTE: Re-use of dx and dy for different purpose!
1269
1272
  dx = arrw.to_x - arrw.from_x;
1270
1273
  dy = arrw.to_y - arrw.from_y;
1271
1274
  l = Math.sqrt(dx * dx + dy * dy);
1272
1275
  let cdx = 0, cdy = 0;
1273
1276
  if(l > 0) {
1274
- // Amount to shorten the line to accommodate arrow head
1275
- // NOTE: for thicker arrows, subtract a bit more
1277
+ // Amount to shorten the line to accommodate arrow head.
1278
+ // NOTE: For thicker arrows, subtract a bit more.
1276
1279
  cdx = (4 + 1.7 * (stroke_width - 1.5)) * dx / l;
1277
1280
  cdy = (4 + 1.7 * (stroke_width - 1.5)) * dy / l;
1278
1281
  }
1279
1282
  if(reversed) {
1280
- // Adjust end points by 1/2 px for rounded stroke end
1283
+ // Adjust end points by 1/2 px for rounded stroke end.
1281
1284
  bpx = arrw.to_x - 0.5*dx / l;
1282
1285
  bpy = arrw.to_y - 0.5*dy / l;
1283
- // Adjust start points for arrow head(s)
1286
+ // Adjust start points for arrow head(s).
1284
1287
  epx = arrw.from_x + cdx;
1285
1288
  epy = arrw.from_y + cdy;
1286
1289
  if(arrw.bidirectional) {
@@ -1288,10 +1291,10 @@ class Paper {
1288
1291
  bpy -= cdy;
1289
1292
  }
1290
1293
  } else {
1291
- // Adjust start points by 1/2 px for rounded stroke end
1294
+ // Adjust start points by 1/2 px for rounded stroke end.
1292
1295
  bpx = arrw.from_x + 0.5*dx / l;
1293
1296
  bpy = arrw.from_y + 0.5*dy / l;
1294
- // Adjust end points for arrow head(s)
1297
+ // Adjust end points for arrow head(s).
1295
1298
  epx = arrw.to_x - cdx;
1296
1299
  epy = arrw.to_y - cdy;
1297
1300
  if(arrw.bidirectional) {
@@ -1299,8 +1302,8 @@ class Paper {
1299
1302
  bpy += cdy;
1300
1303
  }
1301
1304
  }
1302
- // Calculate actual (multi)flow, as this co-determines the color of the arrow
1303
- if(MODEL.solved) {
1305
+ // Calculate actual (multi)flow, as this co-determines the color of the arrow.
1306
+ if(MODEL.solved && !ignored) {
1304
1307
  if(!luc) {
1305
1308
  mf = arrw.multiFlows;
1306
1309
  af = mf[1] + mf[2];
@@ -1315,22 +1318,20 @@ class Paper {
1315
1318
  } else {
1316
1319
  arrow_end = this.active_triangle;
1317
1320
  }
1318
- if(arrw.bidirectional) {
1319
- arrow_start = arrow_end;
1320
- }
1321
1321
  } else {
1322
1322
  if(stroke_color != this.palette.select) stroke_color = 'silver';
1323
1323
  if(arrow_end === this.double_triangle) {
1324
1324
  arrow_end = this.inactive_double_triangle;
1325
- if(arrw.bidirectional) {
1326
- arrow_start = this.inactive_double_triangle;
1327
- }
1328
1325
  }
1329
1326
  }
1330
1327
  } else {
1331
1328
  af = VM.UNDEFINED;
1329
+ if(ignored && stroke_color != this.palette.select) {
1330
+ stroke_color = this.palette.ignore;
1331
+ arrow_end = this.ignore_triangle;
1332
+ }
1332
1333
  }
1333
-
1334
+ if(arrw.bidirectional) arrow_start = arrow_end;
1334
1335
  // Draw arrow shaft
1335
1336
  if(stroke_width === 3 && data_flows) {
1336
1337
  // Hollow shaft arrow: dotted when *all* represented links are
@@ -1509,7 +1510,7 @@ class Paper {
1509
1510
 
1510
1511
  // Draw the actual flow
1511
1512
  const absf = Math.abs(af);
1512
- if(l > 0 && af < VM.UNDEFINED && absf > VM.SIG_DIF_FROM_ZERO) {
1513
+ if(!ignored && l > 0 && af < VM.UNDEFINED && absf > VM.SIG_DIF_FROM_ZERO) {
1513
1514
  const ffill = {fill:'white', opacity:0.8};
1514
1515
  if(luc || mf[0] == 1) {
1515
1516
  // Draw flow data halfway the arrow only if calculated and non-zero.
@@ -173,6 +173,12 @@ class PowerGridManager {
173
173
  MODEL.ignore_KVL = UI.boxChecked('power-grids-KVL');
174
174
  MODEL.ignore_power_losses = UI.boxChecked('power-grids-losses');
175
175
  this.dialog.hide();
176
+ const pg_btn = document.getElementById('settings-power-btn');
177
+ if(MODEL.ignore_grid_capacity || MODEL.ignore_KVL || MODEL.ignore_power_losses) {
178
+ pg_btn.classList.add('ignore');
179
+ } else {
180
+ pg_btn.classList.remove('ignore');
181
+ }
176
182
  }
177
183
 
178
184
  selectPowerGrid(event, id, focus) {
@@ -124,7 +124,7 @@ class GUISensitivityAnalysis extends SensitivityAnalysis {
124
124
  this.color_scales.rb.addEventListener('click', csf);
125
125
  this.color_scales.no.addEventListener('click', csf);
126
126
  document.getElementById('sa-copy-btn').addEventListener(
127
- 'click', () => SENSITIVITY_ANALYSIS.copyTableToClipboard());
127
+ 'click', (event) => SENSITIVITY_ANALYSIS.copyTableToClipboard(event.shiftKey));
128
128
  document.getElementById('sa-copy-data-btn').addEventListener(
129
129
  'click', () => SENSITIVITY_ANALYSIS.copyDataToClipboard());
130
130
  this.outcome_name = document.getElementById('sa-outcome-name');
@@ -255,7 +255,7 @@ class GUISensitivityAnalysis extends SensitivityAnalysis {
255
255
  // Otherwise, display list of all dataset selectors in docu-viewer.
256
256
  if(DOCUMENTATION_MANAGER.visible) {
257
257
  const
258
- ds_dict = MODEL.listOfAllSelectors,
258
+ ds_dict = MODEL.dictOfAllSelectors,
259
259
  html = [],
260
260
  sl = Object.keys(ds_dict).sort((a, b) => UI.compareFullNames(a, b, true));
261
261
  for(const s of sl) {
@@ -346,7 +346,7 @@ class GUISensitivityAnalysis extends SensitivityAnalysis {
346
346
  sl = this.base_selectors.value.replace(/[\;\,]/g, ' ').trim().replace(
347
347
  /[^a-zA-Z0-9\+\-\%\_\s]/g, '').split(/\s+/),
348
348
  bs = sl.join(' '),
349
- sd = MODEL.listOfAllSelectors,
349
+ sd = MODEL.dictOfAllSelectors,
350
350
  us = [];
351
351
  for(const s of sl) if(s.length > 0 && !(s in sd)) us.push(s);
352
352
  if(us.length > 0) {
@@ -760,9 +760,8 @@ class GUISensitivityAnalysis extends SensitivityAnalysis {
760
760
  this.updateData();
761
761
  }
762
762
 
763
- copyTableToClipboard() {
764
- UI.copyHtmlToClipboard(this.scroll_area.innerHTML);
765
- UI.notify('Table copied to clipboard (as HTML)');
763
+ copyTableToClipboard(plain) {
764
+ UI.copyHtmlToClipboard(this.scroll_area.innerHTML, plain);
766
765
  }
767
766
 
768
767
  copyDataToClipboard() {
@@ -488,6 +488,8 @@ module.exports = class MILPSolver {
488
488
  } else {
489
489
  inttol = Math.max(1e-9, Math.min(0.1, inttol));
490
490
  }
491
+ // Use integer tolerance setting as "near zero" threshold.
492
+ this.near_zero = inttol;
491
493
  // Default relative MIP gap is 1e-4.
492
494
  if(isNaN(mipgap)) {
493
495
  mipgap = 1e-4;
@@ -578,7 +580,14 @@ module.exports = class MILPSolver {
578
580
  x_values.push(0);
579
581
  col++;
580
582
  }
581
- x_values.push(x_dict[v]);
583
+ // Return near-zero values as 0.
584
+ let xv = x_dict[v];
585
+ const xfv = parseFloat(xv);
586
+ if(xfv && Math.abs(xfv) < this.near_zero) {
587
+ console.log('NOTE: Truncated ', xfv, ' to zero for variable', v);
588
+ xv = '0';
589
+ }
590
+ x_values.push(xv);
582
591
  col++;
583
592
  }
584
593
  // Add zeros to vector for remaining columns.