MATSIM
QueueWithBuffer.java
Go to the documentation of this file.
1 /* *********************************************************************** *
2  * project: org.matsim.*
3  * *
4  * *********************************************************************** *
5  * *
6  * copyright : (C) 2013 by the members listed in the COPYING, *
7  * LICENSE and WARRANTY file. *
8  * email : info at matsim dot org *
9  * *
10  * *********************************************************************** *
11  * *
12  * This program is free software; you can redistribute it and/or modify *
13  * it under the terms of the GNU General Public License as published by *
14  * the Free Software Foundation; either version 2 of the License, or *
15  * (at your option) any later version. *
16  * See also COPYING, LICENSE and WARRANTY file *
17  * *
18  * *********************************************************************** */
19 
20 package org.matsim.core.mobsim.qsim.qnetsimengine;
21 
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Iterator;
25 import java.util.LinkedList;
26 import java.util.Queue;
27 
28 import org.apache.commons.lang3.tuple.ImmutablePair;
29 import org.apache.commons.lang3.tuple.Pair;
30 import org.apache.logging.log4j.LogManager;
31 import org.apache.logging.log4j.Logger;
32 import org.matsim.api.core.v01.Coord;
33 import org.matsim.api.core.v01.Id;
42 import org.matsim.core.gbl.Gbl;
56 import org.matsim.lanes.Lane;
57 import org.matsim.vehicles.Vehicle;
60 
78 final class QueueWithBuffer implements QLaneI, SignalizeableItem {
79  private static final Logger log = LogManager.getLogger( QueueWithBuffer.class ) ;
80 
81  @Override
82  public final void addFromWait(final QVehicle veh) {
83  //To protect against calling addToBuffer() without calling hasFlowCapacityLeft() first.
84  //This only could happen for addFromWait(), because it can be called from outside QueueWithBuffer
85  if (flowcap_accumulate.getValue() <= 0.0 && veh.getVehicle().getType().getPcuEquivalents() > context.qsimConfig
86  .getPcuThresholdForFlowCapacityEasing()) {
87  throw new IllegalStateException("Buffer of link " + this.id + " has no space left!");
88  }
89 
90  addToBuffer(veh);
91  }
92 
101  private static class FlowcapAccumulate {
102  private double timeStep = 0.;//Double.NEGATIVE_INFINITY ;
103  private double value = 0. ;
104  private double getTimeStep(){
105  return this.timeStep;
106  }
107  private void setTimeStep(double now) {
108  this.timeStep = now;
109  }
110  private double getValue() {
111  return value;
112  }
113  private void setValue(double value ) {
114  this.value = value;
115  }
116  private void addValue(double value1, double now) {
117  this.value += value1;
118  this.timeStep = now ;
119  }
120  }
121  private final FlowcapAccumulate flowcap_accumulate = new FlowcapAccumulate() ;
122  // might be changed back to standard double after all of this was figured out. kai, sep'14
123 
127  private boolean thisTimeStepGreen = true ;
128  private double inverseFlowCapacityPerTimeStep;
132  private double flowCapacityPerTimeStep;
133  private double remainingHolesStorageCapacity = 0.0 ;
134 
135  private final Queue<QueueWithBuffer.Hole> holes = new LinkedList<>();
136 
138  private double bufferLastMovedTime = Double.NEGATIVE_INFINITY ;
139 
144  private final VehicleQ<QVehicle> vehQueue;
145 
146  private double storageCapacity;
147  private double usedStorageCapacity;
154  private final Queue<Pair<QVehicle,Double>> buffer = new LinkedList<>() ;
158  private DefaultSignalizeableItem qSignalizedItem = null ;
164  private final AbstractQLink.QLinkInternalInterface qLinkInternalInterface;
165  private final Id<Lane> id;
166  private static int spaceCapWarningCount = 0;
167  final static double HOLE_SPEED_KM_H = 15.0;
168 
169  private final double length ;
170  private double unscaledFlowCapacity_s = Double.NaN ;
171  private double effectiveNumberOfLanes = Double.NaN ;
172 
176  private Pair<QVehicle,Double> lastBufferEntry = null;
177 
181  private Pair<QVehicle,Double> lastQueueEntry = null;
182 
183 
184  private final VisData visData = new VisDataImpl() ;
185  private final NetsimEngineContext context;
186 
187  private double maxInflowUsedInQsim = Double.POSITIVE_INFINITY ;
188  private double effectiveNumberOfLanesUsedInQsim = Double.POSITIVE_INFINITY ;
189 
190  private double accumulatedInflowCap = 1. ;
191 
192  private final FlowEfficiencyCalculator flowEfficiencyCalculator;
193 
194  private QueueWithBuffer(AbstractQLink.QLinkInternalInterface qlink, final VehicleQ<QVehicle> vehicleQueue, Id<Lane> laneId,
195  double length, double effectiveNumberOfLanes, double flowCapacity_s, final NetsimEngineContext context,
196  FlowEfficiencyCalculator flowEfficiencyCalculator) {
197  // the general idea is to give this object no longer access to "everything". Objects get back pointers (here qlink), but they
198  // do not present the back pointer to the outside. In consequence, this object can go up to qlink, but not any further. kai, mar'16
199  // Now I am even trying to get rid of the full qLink back pointer (since it allows, e.g., going back to Link). kai, feb'18
200 
201 // log.setLevel(Level.DEBUG);
202 
203  this.flowEfficiencyCalculator = flowEfficiencyCalculator;
204  this.qLinkInternalInterface = qlink;
205  this.id = laneId ;
206  this.context = context ;
207  this.vehQueue = vehicleQueue ;
208  this.length = length;
209  this.unscaledFlowCapacity_s = flowCapacity_s ;
210  this.effectiveNumberOfLanes = effectiveNumberOfLanes;
211 
212 // freespeedTravelTime = this.length / qlink.getLink().getFreespeed();
213 // if (Double.isNaN(freespeedTravelTime)) {
214 // throw new IllegalStateException("Double.NaN is not a valid freespeed travel time for a link. Please check the attributes length and freespeed!");
215 // }
216  this.calculateFlowCapacity();
217  this.calculateStorageCapacity();
218 
219  flowcap_accumulate.setValue(flowCapacityPerTimeStep);
220  }
221 
222  private void addToBuffer(final QVehicle veh) {
223  // yy might make sense to just accumulate to "zero" and go into negative when something is used up.
224  // kai/mz/amit, mar'12
225 
226  double now = context.getSimTimer().getTimeOfDay() ;
227 
228  double flowConsumption = (lastBufferEntry == null) ?
229  getFlowCapacityConsumptionInEquivalents(veh, null, null) : getFlowCapacityConsumptionInEquivalents(veh, lastBufferEntry.getKey(), now - lastBufferEntry.getValue());
230  this.flowcap_accumulate.addValue(-flowConsumption, now);
231 
232  buffer.add(new ImmutablePair<>(veh,flowConsumption));
233  lastBufferEntry = new ImmutablePair<>(veh,now);
234 
235  if (buffer.size() == 1) {
236  bufferLastMovedTime = now;
237  // (if there is one vehicle in the buffer now, there were zero vehicles in the buffer before. in consequence,
238  // need to reset the lastMovedTime. If, in contrast, there was already a vehicle in the buffer before, we can
239  // use the lastMovedTime that was (somehow) computed for that vehicle.)
240  }
241  final QNodeI toNode = qLinkInternalInterface.getToNodeQ();
242  if ( toNode instanceof AbstractQNode ) {
243  ((AbstractQNode) toNode).activateNode();
244  // yy for an "upstream" QLane, this activates the toNode too early. Yet I think I founds this
245  // also in the original QLane code. kai, sep'13
246  }
247 
248  }
249 
255  private void moveQueueToBuffer() {
256  double now = context.getSimTimer().getTimeOfDay();
257 
258  QVehicle veh;
259  while ((veh = peekFromVehQueue()) != null) {
260  //we have an original QueueLink behaviour
261  if (veh.getEarliestLinkExitTime() > now) {
262  return;
263  }
264 
265  MobsimDriverAgent driver = veh.getDriver();
266 
267  if (driver instanceof TransitDriverAgent) {
268  HandleTransitStopResult handleTransitStop = qLinkInternalInterface.handleTransitStop(
269  now, veh, (TransitDriverAgent) driver, this.qLinkInternalInterface.getId()
270  );
271  if (handleTransitStop == HandleTransitStopResult.accepted) {
272  // vehicle has been accepted into the transit vehicle queue of the link.
273  removeVehicleFromQueue(veh);
274  continue;
275  } else if (handleTransitStop == HandleTransitStopResult.rehandle) {
276  continue; // yy why "continue", and not "break" or "return"? Seems to me that this
277  // is currently only working because qLink.handleTransitStop(...) also increases the
278  // earliestLinkExitTime for the present vehicle. kai, oct'13
279  // zz From my point of view it is exactly like described above. dg, mar'14
280 // } else if (handleTransitStop == HandleTransitStopResult.continue_driving) {
281  // Do nothing, but go on..
282  }
283  }
284 
285  // Check if veh has reached destination:
286  if (driver.isWantingToArriveOnCurrentLink()) {
287  if ( qLinkInternalInterface.letVehicleArrive(veh )) {
288  // remove _after_ processing the arrival to keep link active:
289  removeVehicleFromQueue(veh);
290  continue;
291  } else { // The current vehicle is not allowed to arrive, so it will block the link
292  return;
293  }
294  }
295 
296  /* is there still any flow capacity left? */
297  if (!hasFlowCapacityLeft(veh)) {
298  return;
299  }
300 
301  removeVehicleFromQueue(veh);
302  addToBuffer(veh); //isn't prevVehicle always equal to veh here!?
303 
304  if (context.qsimConfig.isRestrictingSeepage()
305  && context.qsimConfig.getLinkDynamics() == LinkDynamics.SeepageQ
306  && context.qsimConfig.getSeepModes().contains(veh.getDriver().getMode())) {
307  noOfSeepModeBringFwd++;
308  }
309  } // end while
310  }
311 
312  @Override
313  public final boolean isAcceptingFromWait(QVehicle veh) {
314  return this.hasFlowCapacityLeft(veh) ;
315  }
316 
317  private boolean hasFlowCapacityLeft(VisVehicle veh) {
318  if(context.qsimConfig.isUsingFastCapacityUpdate() ){
319  updateFastFlowAccumulation();
320  }
321 
322  return flowcap_accumulate.getValue() > 0.0 || veh.getVehicle().getType()
323  .getPcuEquivalents() <= context.qsimConfig.getPcuThresholdForFlowCapacityEasing();
324  }
325 
326  private void updateFastFlowAccumulation(){
327  double now = context.getSimTimer().getTimeOfDay() ;
328 
329  double remainingFlowCapThisTimeStep = subtractConsumptionOfVehiclesThatAreAlreadyInTheBuffer();
330 
331  if( this.flowcap_accumulate.getTimeStep() < now
332  && this.flowcap_accumulate.getValue() < remainingFlowCapThisTimeStep){
333 
334  double timeSteps = (now - flowcap_accumulate.getTimeStep()) / context.qsimConfig.getTimeStepSize();
335  double accumulateFlowCap = timeSteps * flowCapacityPerTimeStep;
336  double newFlowCap = Math.min(flowcap_accumulate.getValue() + accumulateFlowCap,
337  remainingFlowCapThisTimeStep);
338 
339  flowcap_accumulate.setValue(newFlowCap);
340  flowcap_accumulate.setTimeStep( now );
341  }
342  }
343 
344  private void updateSlowFlowAccumulation(){
345  double remainingFlowCapThisTimeStep = subtractConsumptionOfVehiclesThatAreAlreadyInTheBuffer();
346 
347  if (this.thisTimeStepGreen
348  && this.flowcap_accumulate.getValue() < remainingFlowCapThisTimeStep){
349  double newFlowCap = Math.min(flowcap_accumulate.getValue() + flowCapacityPerTimeStep,
350  remainingFlowCapThisTimeStep);
351  flowcap_accumulate.setValue(newFlowCap);
352  }
353  }
354 
355  private double subtractConsumptionOfVehiclesThatAreAlreadyInTheBuffer() {
356  double remainingFlowCapThisTimeStep = flowCapacityPerTimeStep;
357  for (Pair<QVehicle,Double> vehEfficiencyPair : buffer) {
358  // Subtract size of vehicles that are already in the buffer (from previous time steps)
359  remainingFlowCapThisTimeStep -= vehEfficiencyPair.getValue();
360  }
361  return remainingFlowCapThisTimeStep;
362  }
363 
364  @Override
365  public final void initBeforeSimStep() {
366  if(!context.qsimConfig.isUsingFastCapacityUpdate() ){
367  updateSlowFlowAccumulation();
368  }
369  }
370  private static int wrnCnt=0 ;
371  private void calculateFlowCapacity() {
372  // the following is not looking at time because it simply assumes that the lookups are "now". kai, feb'18
373  // I am currently not sure if this statement is correct. kai, feb'18
374 
375  // we need the flow capacity per sim-tick and multiplied with flowCapFactor
376  flowCapacityPerTimeStep = unscaledFlowCapacity_s * context.qsimConfig.getTimeStepSize() * context.qsimConfig.getFlowCapFactor() ;
377  inverseFlowCapacityPerTimeStep = 1.0 / flowCapacityPerTimeStep;
378 
379  // start with the base assumption, might be adjusted below depending on the traffic dynamics
380  this.effectiveNumberOfLanesUsedInQsim = this.effectiveNumberOfLanes;
381  this.maxInflowUsedInQsim = this.flowCapacityPerTimeStep;
382 
383  switch (context.qsimConfig.getTrafficDynamics()) {
384  case queue:
385  case withHoles:
386  break;
387  case kinematicWaves:
388  // uncongested branch: q = vmax * rho
389  // congested branch: q = vhole * (rhojam - rho)
390  // equal: rho * (vmax + vhole) = vhole * rhojam
391  // rho(qmax) = vhole * rhojam / (vmax + vhole)
392  // qmax = vmax * rho(qmax) = rhojam / (1/vhole + 1/vmax) ;
393 
394  // yyyyyy this should possibly be getFreespeed(now). But if that's the case, then maxFlowFromFdiag would
395  // also have to be re-computed with each freespeed change. kai, feb'18
396 
397  /* We think the maxFlowFromFdiag must be scaled with the flow capacity factor because this scales how much flow can be send/received per time.
398  * It's unit is (veh/m) / (1/ m/s) = (veh/m) / (s/m) = veh/s
399  * tilmann + theresa, feb'25 */
400  final double maxFlowFromFdiag = (context.qsimConfig.getFlowCapFactor() * this.effectiveNumberOfLanes/context.effectiveCellSize)
401  / ( 1./(HOLE_SPEED_KM_H/3.6) + 1/this.qLinkInternalInterface.getFreespeed() ) ;
402  final double minimumNumberOfLanesFromFdiag = this.flowCapacityPerTimeStep * context.effectiveCellSize * ( 1./(HOLE_SPEED_KM_H/3.6) + 1/this.qLinkInternalInterface.getFreespeed() );
403 
404  QSimConfigGroup.InflowCapacitySetting inflowCapacitySetting = context.qsimConfig.getInflowCapacitySetting();
405 
406  if(inflowCapacitySetting == QSimConfigGroup.InflowCapacitySetting.MAX_CAP_FOR_ONE_LANE){
407  if (wrnCnt<10) {
408  wrnCnt++ ;
409  log.warn("you are using the maximum capacity for one lane as the inflow capacity. This is the old standard behavior of the qsim and probably leads to wrong results " +
410  " as it does not respect the actual number of lanes nor the user-defined flow capacity. Please consider using" +
411  "InflowCapacitySetting.INCREASE_NUMBER_OF_LANES or InflowCapacitySetting.REDUCE_INFLOW_CAPACITY instead.");
412 
413  if ( wrnCnt==10 ) { //this verbose warning is only given 10 times
414  log.warn( Gbl.FUTURE_SUPPRESSED ) ;
415  }
416  }
417 
418  this.maxInflowUsedInQsim = (1/context.effectiveCellSize) / ( 1./(HOLE_SPEED_KM_H/3.6) + 1/this.qLinkInternalInterface.getFreespeed() ) ;
419  // write out the modified qsim behavior as link attribute
420  qLinkInternalInterface.getLink().getAttributes().putAttribute("maxInflowUsedInQsim", 3600* maxInflowUsedInQsim /context.qsimConfig.getTimeStepSize() );
421 
422  } else {
423  if (wrnCnt < 10) { // warnings
424  wrnCnt++;
425  log.warn("max flow from fdiag < flow cap in network file; linkId=" + qLinkInternalInterface.getId() +
426  "; network file flow cap/h=" + 3600. * flowCapacityPerTimeStep / context.qsimConfig.getTimeStepSize() +
427  "; max flow from fdiag/h=" + 3600 * maxFlowFromFdiag / context.qsimConfig.getTimeStepSize());
428 
429  log.warn("number of lanes from fdiag > number of lanes in network file; linkId=" + qLinkInternalInterface.getId() +
430  "; number of lanes in network file=" + this.effectiveNumberOfLanes +
431  "; number of lanes from fdiag=" + minimumNumberOfLanesFromFdiag);
432 
433  if (inflowCapacitySetting == QSimConfigGroup.InflowCapacitySetting.INFLOW_FROM_FDIAG) {
434  log.warn("The flow capacity will be reduced. See link attribute 'maxInflowUsedInQsim' written into the output network.");
435  } else if (inflowCapacitySetting == QSimConfigGroup.InflowCapacitySetting.NR_OF_LANES_FROM_FDIAG) {
436  log.warn("The number of lanes will be increased. See link attribute 'effectiveNumberOfLanesUsedInQsim' written into the output network.");
437  }
438 
439  if (wrnCnt == 10) {
440  log.warn(Gbl.FUTURE_SUPPRESSED);
441  }
442  }
443 
444  // now either correct the flow capacity or the number of lanes!
445  if (inflowCapacitySetting == QSimConfigGroup.InflowCapacitySetting.INFLOW_FROM_FDIAG) {
446  this.maxInflowUsedInQsim = maxFlowFromFdiag;
447  // write out the modified qsim behavior as link attribute
448  qLinkInternalInterface.getLink().getAttributes().putAttribute("maxInflowUsedInQsim", 3600* maxInflowUsedInQsim /context.qsimConfig.getTimeStepSize() );
449  } else if (inflowCapacitySetting == QSimConfigGroup.InflowCapacitySetting.NR_OF_LANES_FROM_FDIAG) {
450  this.effectiveNumberOfLanesUsedInQsim = minimumNumberOfLanesFromFdiag;
451  // write out the modified qsim behavior as link attribute
452  qLinkInternalInterface.getLink().getAttributes().putAttribute("effectiveNumberOfLanesUsedInQsim", effectiveNumberOfLanesUsedInQsim );
453  } else {
454  throw new RuntimeException("The approach "+ inflowCapacitySetting.toString()+" is not implemented yet.");
455  }
456  }
457  break;
458 
459  default: throw new RuntimeException("The traffic dynamics "+context.qsimConfig.getTrafficDynamics()+" is not implemented yet.");
460  }
461 // log.debug( "linkId=" + this.qLink.getLink().getId() + "; flowCapPerTimeStep=" + flowCapacityPerTimeStep +
462 // "; invFlowCapPerTimeStep=" + inverseFlowCapacityPerTimeStep + "; maxFlowFromFdiag=" + maxFlowFromFdiag ) ;
463 
464  }
465 
466  private void calculateStorageCapacity() {
467  // The following is not adjusted for time-dependence!! kai, apr'16
468  // No, I think that it simply assumes that the lookups are "now". kai, feb'18
469 // double now = context.getSimTimer().getTimeOfDay() ;
470 
471  // first guess at storageCapacity:
472  storageCapacity = this.length * this.effectiveNumberOfLanesUsedInQsim / context.effectiveCellSize * context.qsimConfig.getStorageCapFactor() ;
473 // storageCapacity = this.length * this.qLink.getLink().getNumberOfLanes(now) / context.effectiveCellSize * context.qsimConfig.getStorageCapFactor() ;
474 
475  // storage capacity needs to be at least enough to handle the cap_per_time_step:
476  storageCapacity = Math.max(storageCapacity, getBufferStorageCapacity());
477 
478  /*
479  * If speed on link is relatively slow, then we need MORE cells than the above spaceCap to handle the flowCap.
480  * Example: Assume freeSpeedTravelTime (aka freeTravelDuration) is 2 seconds. Than we need the spaceCap = TWO
481  * times the flowCap per second to handle the flowCap.
482  *
483  * Will base these computations (for the time being) on the standard free speed; i.e. reductions in free speed
484  * will also reduce the maximum flow.
485  */
486  double freespeedTravelTime = this.length / qLinkInternalInterface.getFreespeed();
487  // yyyyyy this should possibly be getFreespeed(now). But if that's the case, then storageCap would
488  // also have to be re-computed with each freespeed change. kai, feb'18
489  if (Double.isNaN(freespeedTravelTime)) {
490  throw new IllegalStateException("Double.NaN is not a valid freespeed travel time for a link. Please check the attributes length and freespeed!");
491  }
492 
493  //this assumes that vehicles have the flowEfficiencyFactor of 1.0; the actual flow can be different
494  double tempStorageCapacity = freespeedTravelTime * unscaledFlowCapacity_s * context.qsimConfig.getFlowCapFactor();
495  // yy note: freespeedTravelTime may be Inf. In this case, storageCapacity will also be set to Inf. This can still be
496  // interpreted, but it means that the link will act as an infinite sink. kai, nov'10
497 
498  if (storageCapacity < tempStorageCapacity) {
499  if (QueueWithBuffer.spaceCapWarningCount <= 10) {
500  log.warn("Link " + this.id + " too small: enlarge storage capacity from: " + storageCapacity
501  + " Vehicles to: " + tempStorageCapacity + " Vehicles. This is not fatal, but modifies the traffic flow dynamics.");
502  if (QueueWithBuffer.spaceCapWarningCount == 10) {
503  log.warn("Additional warnings of this type are suppressed.");
504  }
505  QueueWithBuffer.spaceCapWarningCount++;
506  }
507  storageCapacity = tempStorageCapacity;
508 
509  // write out the modified qsim behavior as link attribute
510  qLinkInternalInterface.getLink().getAttributes().putAttribute("storageCapacityUsedInQsim", storageCapacity );
511  }
512 
513  /* About minStorCapForHoles:
514  * () uncongested branch is q(rho) = rho * v_max
515  * () congested branch is q(rho) = (rho - rho_jam) * v_holes
516  * () rho_maxflow is where these two meet, resulting in rho_maxflow = v_holes * rho_jam / ( v_holes + v_max )
517  * () max flow is q(rho_maxflow), resulting in v_max * v_holes * rho_jam / ( v_holes + v_max )
518  * () Since everything else is given, rho_jam needs to be large enough so that q(rho_maxflow) can reach capacity, resulting in
519  * rho_jam >= capacity * (v_holes + v_max) / (v_max * v_holes) ;
520  * () In consequence, storage capacity needs to be larger than curved_length * rho_jam .
521  *
522  */
523 
524  switch (context.qsimConfig.getTrafficDynamics()) {
525  case queue:
526  break;
527  case withHoles:
528  case kinematicWaves:
529  // final double minStorCapForHoles = 2. * flowCapacityPerTimeStep * context.getSimTimer().getSimTimestepSize();
530  final double freeSpeed = qLinkInternalInterface.getFreespeed() ; // yyyyyy not clear why this is not time-dep. kai, feb'18
531  final double holeSpeed = HOLE_SPEED_KM_H/3.6;
532  final double minStorCapForHoles = length * flowCapacityPerTimeStep * (freeSpeed + holeSpeed) / freeSpeed / holeSpeed ;
533  // final double minStorCapForHoles = 2.* length * flowCapacityPerTimeStep * (freeSpeed + holeSpeed) / freeSpeed / holeSpeed ;
534  // I have no idea why the factor 2 needs to be there?!?! kai, apr'16
535  // I just removed the factor of 2 ... seems to work now without. kai, may'16
536  // yyyyyy (not thought through for TS != 1sec! (should use flow cap per second) kai, apr'16)
537  if ( storageCapacity < minStorCapForHoles ) {
538  if ( spaceCapWarningCount <= 10 ) {
539  log.warn("storage capacity not sufficient for holes; increasing from " + storageCapacity + " to " + minStorCapForHoles ) ;
540  QueueWithBuffer.spaceCapWarningCount++;
541  }
542  storageCapacity = minStorCapForHoles ;
543  // write out the modified qsim behavior as link attribute
544  qLinkInternalInterface.getLink().getAttributes().putAttribute("storageCapacityUsedInQsim", storageCapacity );
545  }
546 
547  remainingHolesStorageCapacity = this.storageCapacity;
548  // yyyy how is this.storageCapacity supposed to have a value here? (It might just be zero, and
549  // maybe this is the correct value, but the code is not very expressive.) kai, mar'17
550  // i think, at this location, this explains everything. amit mar'17
551  break;
552  default: throw new RuntimeException("The traffic dynmics "+context.qsimConfig.getTrafficDynamics()+" is not implemented yet.");
553  }
554  }
555 
556  private double getBufferStorageCapacity() {
557  return flowCapacityPerTimeStep;//this assumes that vehicles have the flowEfficiencyFactor of 1.0
558  }
559 
560  @Override
561  public final boolean doSimStep( ) {
562  switch (context.qsimConfig.getTrafficDynamics()) {
563  case queue:
564  break;
565  case withHoles:
566  this.processArrivalOfHoles( ) ;
567  break;
568  case kinematicWaves:
569  this.accumulatedInflowCap = Math.min(accumulatedInflowCap + maxInflowUsedInQsim, maxInflowUsedInQsim);
570  this.processArrivalOfHoles( ) ;
571  break;
572  default: throw new RuntimeException("The traffic dynmics "+context.qsimConfig.getTrafficDynamics()+" is not implemented yet.");
573  }
574  this.moveQueueToBuffer();
575  return true ;
576  }
577 
578  private void processArrivalOfHoles() {
579  double now = context.getSimTimer().getTimeOfDay() ;
580  while ( this.holes.size()>0 && this.holes.peek().getEarliestLinkExitTime() < now ) {
581  Hole hole = this.holes.poll(); // ???
582  this.remainingHolesStorageCapacity += hole.getSizeInEquivalents();
583  }
584  }
585 
586  @Override
587  public final void addFromUpstream(final QVehicle veh) {
588  double now = context.getSimTimer().getTimeOfDay();
589 
590  if (this.context.qsimConfig.isUseLanes()) {
591  if (hasMoreThanOneLane()) {
592  this.context.getEventsManager().processEvent(new LaneEnterEvent(now, veh.getId(), this.qLinkInternalInterface.getId(), this.getId()) );
593  }
594  }
595 
596  // activate link since there is now action on it:
597  qLinkInternalInterface.activateLink();
598 
599  if (context.qsimConfig.isSeepModeStorageFree() && context.qsimConfig.getSeepModes().contains(veh.getVehicle().getType().getId().toString())) {
600  // do nothing
601  } else {
602  usedStorageCapacity += veh.getSizeInEquivalents();
603  }
604 
605  // compute and set earliest link exit time:
606 // double linkTravelTime = this.length / this.linkSpeedCalculator.getMaximumVelocity(veh, qLink.getLink(), now);
607  double linkTravelTime = this.length / this.qLinkInternalInterface.getMaximumVelocityFromLinkSpeedCalculator(veh, now );
608  linkTravelTime = context.qsimConfig.getTimeStepSize() * Math.floor(linkTravelTime / context.qsimConfig.getTimeStepSize());
609 
610  veh.setEarliestLinkExitTime(now + linkTravelTime);
611 
612  // In theory, one could do something like
613  // final double discretizedEarliestLinkExitTime = timeStepSize * Math.ceil(veh.getEarliestLinkExitTime()/timeStepSize);
614  // double effectiveEntryTime = now - ( discretizedEarliestLinkExitTime - veh.getEarliestLinkExitTime() ) ;
615  // double earliestExitTime = effectiveEntryTime + linkTravelTime;
616  // We decided against this since this would effectively move the simulation to operating on true floating point time steps. For example,
617  // events could then have arbitrary floating point values (assuming one would use the "effectiveEntryTime" also for the event).
618  // Also, it could happen that vehicles with an earlier link exit time could be
619  // inserted and thus end up after vehicles with a later link exit time. theresa & kai, jun'14
620 
621 // veh.setCurrentLink(qLink.getLink());
622  this.qLinkInternalInterface.setCurrentLinkToVehicle(veh );
623  vehQueue.add(veh);
624 
625  switch (context.qsimConfig.getTrafficDynamics()) {
626  case queue:
627  break;
628  case withHoles:
629  this.remainingHolesStorageCapacity -= veh.getSizeInEquivalents();
630  break;
631  case kinematicWaves:
632  this.remainingHolesStorageCapacity -= veh.getSizeInEquivalents();
633  double flowConsumption = (lastQueueEntry == null) ?
634  getFlowCapacityConsumptionInEquivalents(veh, null, null) : getFlowCapacityConsumptionInEquivalents(veh, lastQueueEntry.getKey(), now - lastQueueEntry.getValue());
635  this.accumulatedInflowCap -= flowConsumption;
636  break;
637  default:
638  throw new RuntimeException("The traffic dynamics " + context.qsimConfig.getTrafficDynamics() + " is not implemented yet.");
639  }
640 
641  lastQueueEntry = new ImmutablePair<>(veh, now);
642  }
643 
644  private void removeVehicleFromQueue(final QVehicle veh2Remove) {
645  double now = context.getSimTimer().getTimeOfDay() ;
646 
647 
648  // QVehicle veh = vehQueue.poll();
649  // usedStorageCapacity -= veh.getSizeInEquivalents();
650 
651  QVehicle veh = pollFromVehQueue(veh2Remove);
652 
653  if(context.qsimConfig.getLinkDynamics()==LinkDynamics.SeepageQ
654  && context.qsimConfig.isSeepModeStorageFree()
655  && context.qsimConfig.getSeepModes().contains(veh.getVehicle().getType().getId().toString()) ){
656  // do nothing
657  } else {
658  usedStorageCapacity -= veh.getSizeInEquivalents();
659  }
660 
661  switch (context.qsimConfig.getTrafficDynamics()) {
662  case queue:
663  break;
664  case withHoles:
665  case kinematicWaves:
666  QueueWithBuffer.Hole hole = new QueueWithBuffer.Hole() ;
667 
668  // double offset = this.storageCapacity/this.flowCapacityPerTimeStep ;
669  /* NOTE: Start with completely full link, i.e. N_storageCap cells filled. Now make light at end of link green, discharge with
670  * flowCapPerTS. After N_storageCap/flowCapPerTS, the link is empty. Which also means that the holes must have reached
671  * the upstream end of the link. I.e. speed_holes = length / (N_storageCap/flowCap) and
672  * ttime_holes = lenth/speed = N_storCap/flowCap.
673  * Say length=75m, storCap=10, flowCap=1/2sec. offset = 20sec. 75m/20sec = 225m/1min = 13.5km/h so this is normal.
674  * Say length=75m, storCap=20, flowCap=1/2sec. offset = 40sec. ... = 6.75km/h ... to low. Reason: unphysical parameters.
675  * (Parameters assume 2-lane road, which should have discharge of 1/sec. Or we have lots of tuk tuks, which have only half a vehicle
676  * length. Thus we incur the reaction time twice as often --> half speed of holes.
677  */
678 
679  // double nLanes = 2. * flowCapacityPerTimeStep ; // pseudo-lanes
680  // double ttimeOfHoles = 0.1 * this.storageCapacity/this.flowCapacityPerTimeStep/nLanes ;
681 
682  // The calculation of the earliest exit time looked like the formula below. It looks like someone tried to include some randomness,
683  // but the random part was multiplied with zero, therefore I removed it. Janek oct' 24
684  // now + 1.0*ttimeOfHoles + 0.0*MatsimRandom.getRandom().nextDouble()*ttimeOfHoles
685  var holeTravelTime = length * 3.6 / HOLE_SPEED_KM_H;
686  var earliestExitTime = now + holeTravelTime;
687  hole.setEarliestLinkExitTime(earliestExitTime) ;
688  hole.setSizeInEquivalents(veh2Remove.getSizeInEquivalents());
689  holes.add( hole ) ;
690  break;
691  default: throw new RuntimeException("The traffic dynmics "+context.qsimConfig.getTrafficDynamics()+" is not implemented yet.");
692  }
693  }
694 
695  @Override
696  public final boolean isActive() {
697  if( context.qsimConfig.isUsingFastCapacityUpdate() ){
698  return (!this.vehQueue.isEmpty())
699  || (!this.isNotOfferingVehicle() && context.qsimConfig.isUseLanes()) // if lanes, the buffer needs to be active in order to move vehicles over an internal node
700  || ( !this.holes.isEmpty() ) ;
701  } else {
702  return (this.flowcap_accumulate.getValue() < flowCapacityPerTimeStep) // still accumulating, thus active
703  || (!this.vehQueue.isEmpty()) // vehicles are on link, thus active
704  || (!this.isNotOfferingVehicle() && context.qsimConfig.isUseLanes()) // if lanes, the buffer needs to be active in order to move vehicles over an internal node
705  || ( !this.holes.isEmpty() ); // need to process arrival of holes
706  }
707  }
708 
709  @Override
710  public final void setSignalStateAllTurningMoves( final SignalGroupState state) {
711  qSignalizedItem.setSignalStateAllTurningMoves(state);
712 
713  thisTimeStepGreen = qSignalizedItem.hasGreenForAllToLinks();
714  // (this is only for capacity accumulation)
715  }
716 
717  @Override
718  public final double getSimulatedFlowCapacityPerTimeStep() {
719  return this.flowCapacityPerTimeStep;
720  }
721 
722  @Override
723  public final boolean isAcceptingFromUpstream() {
724  boolean storageOk = usedStorageCapacity < storageCapacity ;
725 
726  if ( context.qsimConfig.getTrafficDynamics()==TrafficDynamics.queue ) {
727  return storageOk ;
728  }
729  // (continue only if HOLES and/or inflow constraint)
730 
731  if ( context.qsimConfig.getTrafficDynamics() != TrafficDynamics.queue
732  && remainingHolesStorageCapacity <= 0. ) {
733  // check the holes storage capacity if using holes only (amit, Aug 2016)
734  return false ;
735  }
736  // remainingHolesStorageCapacity is:
737  // * initialized at linkStorageCapacity
738  // * reduced by entering vehicles
739  // * increased by holes arriving at upstream end of link
740 
741  if ( context.qsimConfig.getTrafficDynamics() != TrafficDynamics.kinematicWaves) {
742  return true ;
743  }
744 
745  return this.accumulatedInflowCap > 0;
746 
747  }
748 
749  @Override
750  public void recalcTimeVariantAttributes() {
751  // not speed, since that is looked up anyways.
752  // yy might also make flow and storage self-detecting changes (not really that
753  // much more expensive). kai, feb'18
754 
755 // log.debug("just entered recalcTimeVariantAttributes; now=" + this.context.getSimTimer().getTimeOfDay() ) ;
756 
757  calculateFlowCapacity();
758  calculateStorageCapacity();
759  flowcap_accumulate.setValue(flowCapacityPerTimeStep);
760  }
761 
762 // @Override
763 // public final void changeSpeedMetersPerSecond( final double val ) {
764 // this.freespeedTravelTime = this.length / val ;
765 // if (Double.isNaN(freespeedTravelTime)) {
766 // throw new IllegalStateException("Double.NaN is not a valid freespeed travel time for a link. Please check the attributes length and freespeed!");
767 // }
768 // }
769 
770  @Override
771  public final QVehicle getVehicle(final Id<Vehicle> vehicleId) {
772  for (QVehicle veh : this.vehQueue) {
773  if (veh.getId().equals(vehicleId))
774  return veh;
775  }
776  for (Pair<QVehicle,Double> vehEfficiencyPair : this.buffer) {
777  if (vehEfficiencyPair.getKey().getId().equals(vehicleId))
778  return vehEfficiencyPair.getKey();
779  }
780  return null;
781  }
782 
783  @Override
784  public final Collection<MobsimVehicle> getAllVehicles() {
785  /* since it is an instance of arrayList, insertion order is maintained. Thus, correcting the order or insertion.
786  * It will be more complicated for passingQueue. amit feb'16
787  */
788  Collection<MobsimVehicle> vehicles = new ArrayList<>();
789  for (Pair<QVehicle, Double> pair : buffer) {
790  vehicles.add(pair.getKey());
791  }
792  vehicles.addAll(vehQueue);
793  return vehicles ;
794  }
795 
796  @Override
797  public final QVehicle popFirstVehicle() {
798  double now = context.getSimTimer().getTimeOfDay() ;
799  QVehicle veh = removeFirstVehicle();
800  if (this.context.qsimConfig.isUseLanes() ) {
801  if ( hasMoreThanOneLane() ) {
802  this.context.getEventsManager().processEvent(new LaneLeaveEvent( now, veh.getId(), this.qLinkInternalInterface.getId(), this.getId() ) );
803  }
804  }
805  return veh;
806  }
807 
808  private final QVehicle removeFirstVehicle(){
809  double now = context.getSimTimer().getTimeOfDay() ;
810  QVehicle veh = buffer.poll().getKey();
811  bufferLastMovedTime = now; // just in case there is another vehicle in the buffer that is now the new front-most
812  if( context.qsimConfig.isUsingFastCapacityUpdate() ) {
813  flowcap_accumulate.setTimeStep(now - context.qsimConfig.getTimeStepSize());
814  }
815  return veh;
816  }
817 
818  @Override
819  public final void setSignalStateForTurningMove( final SignalGroupState state, final Id<Link> toLinkId) {
820  if (!qLinkInternalInterface.getToNode().getOutLinks().containsKey(toLinkId )){
821  throw new IllegalArgumentException("ToLink " + toLinkId + " is not reachable from QLink Id " + this.id );
822  }
823  qSignalizedItem.setSignalStateForTurningMove(state, toLinkId);
824 
825  thisTimeStepGreen = qSignalizedItem.hasGreenForAllToLinks();
826  // (this is only for capacity accumulation. As soon as at least one turning relation is green, the "link" is considered
827  // green).
828  }
829 
830  @Override
831  public final boolean hasGreenForToLink(final Id<Link> toLinkId) {
832  if (qSignalizedItem != null){
833  return qSignalizedItem.hasGreenForToLink(toLinkId);
834  }
835  return true; //the lane is not signalized and thus always green
836  }
837 
838  @Override
839  public boolean hasGreenForAllToLinks() {
840  if (qSignalizedItem != null) {
841  return qSignalizedItem.hasGreenForAllToLinks();
842  }
843  return true; //the lane is not signalized and thus always green
844  }
845 
846  @Override
847  public final double getStorageCapacity() {
848  return storageCapacity;
849  }
850 
851  @Override
852  public final boolean isNotOfferingVehicle() {
853  return buffer.isEmpty();
854  }
855 
856  @Override
857  public final void clearVehicles() {
858  // yyyyyy right now it seems to me that one should rather just abort the agents and have the framework take care of the rest. kai, mar'16
859 
860  double now = context.getSimTimer().getTimeOfDay() ;
861 
862  for (QVehicle veh : vehQueue) {
863  context.getEventsManager().processEvent( new VehicleAbortsEvent(now, veh.getId(), veh.getCurrentLink().getId()));
864  context.getEventsManager().processEvent( new PersonStuckEvent(now, veh.getDriver().getId(), veh.getCurrentLink().getId(), veh.getDriver().getMode()));
865 
866  context.getAgentCounter().incLost();
867  context.getAgentCounter().decLiving();
868  }
869  vehQueue.clear();
870 
871  for (Pair<QVehicle,Double> bufferEntry : buffer) {
872  QVehicle veh = bufferEntry.getKey();
873  context.getEventsManager().processEvent( new VehicleAbortsEvent(now, veh.getId(), veh.getCurrentLink().getId()));
874  context.getEventsManager().processEvent( new PersonStuckEvent(now, veh.getDriver().getId(), veh.getCurrentLink().getId(), veh.getDriver().getMode()));
875 
876  context.getAgentCounter().incLost();
877  context.getAgentCounter().decLiving();
878  }
879  buffer.clear();
880 
881  holes.clear();
882  this.remainingHolesStorageCapacity = this.storageCapacity;
883  }
884 
885  private double getFlowCapacityConsumptionInEquivalents(QVehicle vehicle, QVehicle prevVehicle, Double timeDiff) {
886  double flowEfficiency = flowEfficiencyCalculator.calculateFlowEfficiency(vehicle, prevVehicle, timeDiff, qLinkInternalInterface.getLink(), id );
887  return vehicle.getSizeInEquivalents() / flowEfficiency;
888  }
889 
890  private boolean hasMoreThanOneLane() {
891  return this.qLinkInternalInterface.getAcceptingQLane() != this.qLinkInternalInterface.getOfferingQLanes().get(0 );
892  // this works independent from sorting since if there is only one lane, then it has to be the one to be returned by
893  // getOfferingQLanes().get(0), and it is also the same as the accepting QLane. If, however, "lanes" is used,
894  // there are at least two lanes in sequence, so the accepting lane is never the same as any of the offering lanes, and
895  // this will always return false independent from sorting. kai/theresa, dec'16
896  }
897 
898  @Override
899  public final QLaneI.VisData getVisData() {
900  return this.visData ;
901  }
902 
903  @Override
904  public final QVehicle getFirstVehicle() {
905  if (this.buffer.isEmpty()) {
906  return this.vehQueue.peek();
907  }
908  return this.buffer.peek().getKey() ;
909  }
910 
911  @Override
912  public final double getLastMovementTimeOfFirstVehicle() {
913  return this.bufferLastMovedTime ;
914  }
915 
919  @Override
920  public final void addTransitSlightlyUpstreamOfStop( final QVehicle veh) {
921  this.vehQueue.addFirst(veh) ;
922  }
923 
924  @Override
925  public final void setSignalized( final boolean isSignalized) {
926  qSignalizedItem = new DefaultSignalizeableItem( qLinkInternalInterface.getToNode().getOutLinks().keySet());
927  }
928 
929  @Override
930  public final void changeUnscaledFlowCapacityPerSecond( final double val ) {
931  this.unscaledFlowCapacity_s = val ;
932  // be defensive (might now be called twice):
933  this.recalcTimeVariantAttributes();
934  }
935 
936  @Override
937  public final void changeEffectiveNumberOfLanes( final double val ) {
938  this.effectiveNumberOfLanes = val ;
939  // be defensive (might now be called twice):
940  this.recalcTimeVariantAttributes();
941  }
942 
943  @Override public Id<Lane> getId() {
944  return this.id;
945  }
946 
947  static final class Hole implements QItem {
948  private double earliestLinkEndTime ;
949  private double pcu;
950 
951  @Override
952  public final double getEarliestLinkExitTime() {
953  return earliestLinkEndTime;
954  }
955 
956  @Override
957  public final void setEarliestLinkExitTime( double earliestLinkEndTime ) {
958  this.earliestLinkEndTime = earliestLinkEndTime;
959  }
960 
961  @Override
962  public final double getSizeInEquivalents() {
963  return this.pcu;
964  }
965 
966  final void setSizeInEquivalents(double pcuFactorOfHole) {
967  this.pcu = pcuFactorOfHole;
968  }
969 
970  @Override
971  public Vehicle getVehicle() {
972  return null ;
973  }
974 
975  @Override
976  public MobsimDriverAgent getDriver() {
977  return null ;
978  }
979 
980  @Override
981  public Id<Vehicle> getId() {
982  return null ;
983  }
984  }
985 
986  class VisDataImpl implements QLaneI.VisData {
987  private Coord upstreamCoord;
988  private Coord downstreamCoord;
989 
990  @Override
991  public final Collection<AgentSnapshotInfo> addAgentSnapshotInfo(Collection<AgentSnapshotInfo> positions, double now) {
992  if ( !buffer.isEmpty() || !vehQueue.isEmpty() || !holes.isEmpty() ) {
993  Gbl.assertNotNull(positions);
994  Gbl.assertNotNull( context.snapshotInfoBuilder );
995  if ( this.upstreamCoord==null ) {
996  this.upstreamCoord = qLinkInternalInterface.getFromNode().getCoord() ;
997  }
998  if ( this.downstreamCoord==null ) {
999  this.downstreamCoord = qLinkInternalInterface.getToNode().getCoord() ;
1000  }
1001  // vehicle positions are computed in snapshotInfoBuilder as a service:
1002  positions = context.snapshotInfoBuilder.positionVehiclesAlongLine(
1003  positions,
1004  now,
1005  getAllVehicles(),
1006  length,
1007  storageCapacity + getBufferStorageCapacity(),
1008  this.upstreamCoord,
1009  this.downstreamCoord,
1010  inverseFlowCapacityPerTimeStep,
1011  qLinkInternalInterface.getFreespeed(now ),
1012 // NetworkUtils.getNumberOfLanesAsInt(now, qLink.getLink()),
1013  qLinkInternalInterface.getNumberOfLanesAsInt(now ) ,
1014  holes,
1015  qLinkInternalInterface
1016  );
1017 
1018  }
1019  return positions ;
1020  }
1021 
1022  void setVisInfo(Coord upstreamCoord, Coord downstreamCoord) {
1023  this.upstreamCoord = upstreamCoord;
1024  this.downstreamCoord = downstreamCoord;
1025  }
1026  }
1027 
1028  private int noOfSeepModeBringFwd = 0;
1029 
1030  private QVehicle peekFromVehQueue(){
1031 
1032  QVehicle returnVeh = vehQueue.peek();
1033 
1034  if( context.qsimConfig.getLinkDynamics()==LinkDynamics.SeepageQ ) {
1035  double now = context.getSimTimer().getTimeOfDay();
1036 
1037  int maxSeepModeAllowed = 4;
1038  if( context.qsimConfig.isRestrictingSeepage() && noOfSeepModeBringFwd == maxSeepModeAllowed) {
1039  noOfSeepModeBringFwd = 0;
1040  return returnVeh;
1041  }
1042 
1043  VehicleQ<QVehicle> newVehQueue = new PassingVehicleQ();
1044  newVehQueue.addAll(vehQueue);
1045 
1046  Iterator<QVehicle> it = newVehQueue.iterator();
1047 
1048  while(it.hasNext()){
1049  QVehicle veh = newVehQueue.poll();
1050  if( veh.getEarliestLinkExitTime()<=now && context.qsimConfig.getSeepModes().contains(veh.getDriver().getMode()) ) {
1051  returnVeh = veh;
1052  break;
1053  }
1054  }
1055  }
1056  return returnVeh;
1057  }
1058 
1059  private QVehicle pollFromVehQueue(QVehicle veh2Remove){
1060  if(vehQueue.remove(veh2Remove)){
1061  return veh2Remove;
1062  } else {
1063  throw new RuntimeException("Desired vehicle is not removed from vehQueue. Aborting...");
1064  }
1065  }
1066 
1067  @Override
1068  public double getLoadIndicator() {
1069  return usedStorageCapacity;
1070  }
1071 
1072  static final class Builder implements LaneFactory {
1073  private final NetsimEngineContext context;
1074  private VehicleQ<QVehicle> vehicleQueue = new FIFOVehicleQ();
1075  private Id<Lane> id = null;
1076  private Double length = null;
1077  private Double effectiveNumberOfLanes = null;
1078  private Double flowCapacity_s = null;
1079  private FlowEfficiencyCalculator flowEfficiencyCalculator;
1080 
1081  Builder(final NetsimEngineContext context) {
1082  this.context = context;
1083  if (context.qsimConfig.getLinkDynamics() == QSimConfigGroup.LinkDynamics.PassingQ ||
1084  context.qsimConfig.getLinkDynamics() == QSimConfigGroup.LinkDynamics.SeepageQ) {
1085  this.vehicleQueue = new PassingVehicleQ();
1086  }
1087  }
1088 
1089  void setVehicleQueue(VehicleQ<QVehicle> vehicleQueue) {
1090  this.vehicleQueue = vehicleQueue;
1091  }
1092 
1093  void setLaneId(Id<Lane> id) {
1094  this.id = id;
1095  }
1096 
1097  void setLength(Double length) {
1098  this.length = length;
1099  }
1100 
1101  void setEffectiveNumberOfLanes(Double effectiveNumberOfLanes) {
1102  this.effectiveNumberOfLanes = effectiveNumberOfLanes;
1103  }
1104 
1105  void setFlowCapacity_s(Double flowCapacity_s) {
1106  this.flowCapacity_s = flowCapacity_s;
1107  }
1108 
1109  void setFlowEfficiencyCalculator(FlowEfficiencyCalculator flowEfficiencyCalculator) {
1110  this.flowEfficiencyCalculator = flowEfficiencyCalculator;
1111  }
1112 
1113  @Override
1114  public QueueWithBuffer createLane(AbstractQLink qLink) {
1115  // a number of things I cannot configure before I have the qlink:
1116  if (id == null) {
1117  id = Id.create(qLink.getLink().getId(), Lane.class);
1118  }
1119  if (length == null) {
1120  length = qLink.getLink().getLength();
1121  }
1122  if (effectiveNumberOfLanes == null) {
1123  effectiveNumberOfLanes = qLink.getLink().getNumberOfLanes();
1124  }
1125  if (flowCapacity_s == null) {
1126  flowCapacity_s = qLink.getLink().getFlowCapacityPerSec();
1127  }
1128  if (flowEfficiencyCalculator == null) {
1129  flowEfficiencyCalculator = new DefaultFlowEfficiencyCalculator();
1130  }
1131  return new QueueWithBuffer(qLink.getInternalInterface(), vehicleQueue, id, length, effectiveNumberOfLanes, flowCapacity_s, context, flowEfficiencyCalculator ) ;
1132  }
1133  }
1134 
1135 }
static final String FUTURE_SUPPRESSED
Definition: Gbl.java:44
static< T > Id< T > create(final long key, final Class< T > type)
Definition: Id.java:68
static void assertNotNull(Object obj)
Definition: Gbl.java:212
final Id< VehicleType > getId()
double calculateFlowEfficiency(QVehicle qVehicle, @Nullable QVehicle previousQVehicle, @Nullable Double timeGapToPreviousVeh, Link link, Id< Lane > laneId)