MATSIM
TravelTimeCollector.java
Go to the documentation of this file.
1 /* *********************************************************************** *
2  * project: org.matsim.*
3  * TravelTimeCollector.java
4  * *
5  * *********************************************************************** *
6  * *
7  * copyright : (C) 2011 by the members listed in the COPYING, *
8  * LICENSE and WARRANTY file. *
9  * email : info at matsim dot org *
10  * *
11  * *********************************************************************** *
12  * *
13  * This program is free software; you can redistribute it and/or modify *
14  * it under the terms of the GNU General Public License as published by *
15  * the Free Software Foundation; either version 2 of the License, or *
16  * (at your option) any later version. *
17  * See also COPYING, LICENSE and WARRANTY file *
18  * *
19  * *********************************************************************** */
20 
21 package org.matsim.withinday.trafficmonitoring;
22 
23 import org.apache.log4j.Logger;
24 import org.matsim.api.core.v01.Id;
25 import org.matsim.api.core.v01.Scenario;
26 import org.matsim.api.core.v01.events.*;
31 import org.matsim.core.gbl.Gbl;
32 import org.matsim.core.mobsim.framework.events.MobsimAfterSimStepEvent;
33 import org.matsim.core.mobsim.framework.events.MobsimBeforeCleanupEvent;
34 import org.matsim.core.mobsim.framework.events.MobsimBeforeSimStepEvent;
35 import org.matsim.core.mobsim.framework.events.MobsimInitializedEvent;
40 import org.matsim.core.mobsim.qsim.QSim;
46 import org.matsim.core.utils.misc.Time;
47 import org.matsim.vehicles.Vehicle;
48 
49 import javax.inject.Inject;
50 import javax.inject.Singleton;
51 import java.util.*;
52 import java.util.concurrent.BrokenBarrierException;
53 import java.util.concurrent.ConcurrentHashMap;
54 import java.util.concurrent.CyclicBarrier;
55 
65 @Singleton
66 public class TravelTimeCollector implements TravelTime,
71 
72  private static final Logger log = Logger.getLogger(TravelTimeCollector.class);
73 
74  private Network network;
75 
76  // Trips with no Activity on the current Link
77  private Map<Id<Vehicle>, TripBin> regularActiveTrips; // VehicleId
78  private Map<Id<Link>, TravelTimeInfo> travelTimeInfos; // LinkId
79 
81 
82  // Links that are changed by network change events
83  private TreeMap<Double, Map<Link,Double>> changedLinks;
84  // yy better a priority queue. kai, dec'17
85 
86  /*
87  * For parallel Execution
88  */
89  private CyclicBarrier startBarrier;
90  private CyclicBarrier endBarrier;
92  private final int numOfThreads;
93 
94  private final int infoTimeStep = 3600;
95  private int nextInfoTime = 0;
96 
97  private Set<Id<Vehicle>> vehiclesToFilter;
98  private final Set<String> analyzedModes;
99  private final boolean filterModes;
100 
101  private boolean problem = true ;
102  private int resetCnt = 0;
103 
104  private double now = Double.NEGATIVE_INFINITY ;
105 
106  @Inject
107  TravelTimeCollector(Scenario scenario) {
108  this(scenario, null);
109  }
110 
111  public TravelTimeCollector(Scenario scenario, Set<String> analyzedModes) {
112 // log.setLevel(Level.DEBUG);
113 
114  /*
115  * The parallelization should scale almost linear, therefore we do use
116  * the number of available threads according to the config file.
117  */
118  this.network = scenario.getNetwork();
119  this.numOfThreads = scenario.getConfig().global().getNumberOfThreads();
120 
121  if (analyzedModes == null || analyzedModes.size() == 0) {
122  this.filterModes = false;
123  this.analyzedModes = null;
124  } else {
125  this.analyzedModes = new HashSet<>(analyzedModes);
126  filterModes = true;
127  }
128 
129  init();
130  }
131 
132  private void init() {
133  this.regularActiveTrips = new HashMap<>();
134  this.travelTimeInfos = new ConcurrentHashMap<>();
135  this.changedLinks = new TreeMap<>();
136  this.vehiclesToFilter = new HashSet<>();
137 
138  // one TravelTimeInfo per link:
139  for (Link link : this.network.getLinks().values()) {
140  TravelTimeInfo travelTimeInfo = new TravelTimeInfo();
141  this.travelTimeInfos.put(link.getId(), travelTimeInfo);
142  }
143 
144  /*
145  * If no RoutingNetwork is used, ArrayBasedTravelTimeInfoProvider uses
146  * a MapBasedTravelTimeInfoProvider as fall back solution. This increases
147  * routing times of a AStarLandmarks router by ~5%.
148  */
149 // this.travelTimeInfoProvider = new MapBasedTravelTimeInfoProvider(this.travelTimeInfos);
150  this.travelTimeInfoProvider = new ArrayBasedTravelTimeInfoProvider(this.travelTimeInfos, this.network);
151 
152  /*
153  * If the network is time variant, we have to update the link parameters
154  * according to the network change events.
155  */
156  Queue<NetworkChangeEvent> networkChangeEvents = NetworkUtils.getNetworkChangeEvents(this.network);
157 
158  // I just changed the return type of the network change events from Collection to Queue.
159  // This should, in a first step, allow to remove the separate changedLinks data structure.
160  // In a second step, this should allow to insert link change events after everything
161  // has started. kai, dec'17
162  // Well, but maybe I don't even want this, and I want this class here recognize changes
163  // only when someone observes them??? (bushfire evacuation use case). kai, dec'17
164 
165  // If one looks at transport telematics, then we need to also be able to set expected
166  // speeds for the future! Which would indeed justify its own data model. kai, dec'17
167  // (in contrast, watch out: the NetworkChangeEventsEngine also keeps its own
168  // copy for the mobsim)
169 
170  if (networkChangeEvents != null) {
171  for (NetworkChangeEvent networkChangeEvent : networkChangeEvents) {
172  ChangeValue freespeedChange = networkChangeEvent.getFreespeedChange();
173  if (freespeedChange != null) {
174  double startTime = networkChangeEvent.getStartTime();
175  Map<Link,Double> links = changedLinks.computeIfAbsent(startTime, k -> new HashMap<>());
176  for ( Link link : networkChangeEvent.getLinks() ) {
177  // yy seems that the following should be available centrally. kai, dec'17
178  double newSpeed ;
179  switch ( freespeedChange.getType() ) {
180  case ABSOLUTE_IN_SI_UNITS:
181  newSpeed = freespeedChange.getValue() ;
182  break;
183  case FACTOR:
184  newSpeed = link.getFreespeed() * freespeedChange.getValue() ;
185  break;
186  case OFFSET_IN_SI_UNITS:
187  newSpeed = link.getFreespeed() + freespeedChange.getValue() ;
188  break;
189  default:
190  throw new RuntimeException("change event type not implemented") ;
191  }
192  if ( startTime > 0. ) {
193  log.debug( "registering a change event for time=" + startTime
194  + "; linkId=" + link.getId() ) ;
195  }
196  links.put( link, newSpeed ) ;
197  }
198  }
199  }
200  }
201  }
202 
203  public void changeSpeedMetersPerSecond( Link link, double speed ) {
204  // yy note that this method should allow to set expectations for the future (transport telematics).
205  // But the implementation actually does not. Since this class is the one that enforces
206  // the behavior that agents cannot look ahead. Which also is not true: they can speculate
207  // about the future. kai, dec'17
208 
209  Map<Link,Double> links = changedLinks.computeIfAbsent(this.now, k -> new HashMap<>());
210  links.put( link, speed ) ;
211  }
212 
213  @Override
214  public double getLinkTravelTime(Link link, double time, Person person, Vehicle vehicle) {
215  final double travelTime = this.travelTimeInfoProvider.getTravelTimeInfo(link).travelTime;
216  if ( Id.createLinkId(51825).equals(link.getId())) {
217  log.debug( "link=" + link.getId() + ";\ttime=" + time + ";\tttime=" + travelTime ) ;
218  }
219  return travelTime;
220  }
221 
222  @Override
223  public void reset(int iteration) {
224  init();
225  resetCnt++ ;
226  if ( resetCnt >1 ) {
227  if ( problem ) {
228  throw new RuntimeException("using TravelTimeCollector, but mobsim notifications not called between two resets. "
229  + "Did you really add this as a mobsim listener?") ;
230  }
231  }
232  }
233 
234  @Override
235  public void handleEvent(LinkEnterEvent event) {
236  /*
237  * If only some modes are analyzed, we check whether the vehicle
238  * performs a trip with one of those modes. if not, we skip the event.
239  */
240  if (filterModes && vehiclesToFilter.contains(event.getVehicleId())) return;
241 
242  Id<Vehicle> vehicleId = event.getVehicleId();
243  double time = event.getTime();
244 
245  TripBin tripBin = new TripBin();
246  tripBin.enterTime = time;
247 
248  this.regularActiveTrips.put(vehicleId, tripBin);
249  }
250 
251  @Override
252  public void handleEvent(LinkLeaveEvent event) {
253  Id<Link> linkId = event.getLinkId();
254  Id<Vehicle> vehicleId = event.getVehicleId();
255  double time = event.getTime();
256 
257  TripBin tripBin = this.regularActiveTrips.remove(vehicleId);
258  if (tripBin != null) {
259  tripBin.leaveTime = time;
260 
261  double tripTime = tripBin.leaveTime - tripBin.enterTime;
262 
263  TravelTimeInfo travelTimeInfo = this.travelTimeInfoProvider.getTravelTimeInfo(linkId);
264  travelTimeInfo.tripBins.add(tripBin);
265  travelTimeInfo.addedTravelTimes += tripTime;
266  travelTimeInfo.addedTrips++;
267 
268  travelTimeInfo.checkActiveState();
269  travelTimeInfo.checkBinSize(tripTime);
270  }
271  }
272 
273  /*
274  * We don't have to count Stuck Events. The MobSim creates LeaveLink Events
275  * before throwing Stuck Events.
276  */
277  @Override
278  public void handleEvent(PersonStuckEvent event) {
279 
280  }
281 
282  /*
283  * If a vehicle leaves the traffic we have to remove its current
284  * trip. Otherwise we would have a trip with the duration of the trip itself
285  * and the activity.
286  */
287  @Override
289  Id<Vehicle> vehicleId = event.getVehicleId();
290 
291  this.regularActiveTrips.remove(vehicleId);
292 
293  // try to remove vehicle from set with filtered vehicles
294  if (filterModes) this.vehiclesToFilter.remove(event.getVehicleId());
295  }
296 
297  @Override
299  /*
300  * If filtering transport modes is enabled and the vehicle
301  * starts a leg on a non analyzed transport mode, add the vehicle
302  * to the filtered vehicles set.
303  */
304  if (filterModes && !analyzedModes.contains(event.getNetworkMode())) this.vehiclesToFilter.add(event.getVehicleId());
305  }
306 
307  /*
308  * Initially set free speed travel time.
309  */
310  @Override
311  public void notifyMobsimInitialized(MobsimInitializedEvent e) {
312  problem = false ;
313 
314  if (e.getQueueSimulation() instanceof QSim) {
315  double simStartTime = ((QSim) e.getQueueSimulation()).getSimTimer().getSimStartTime();
316 
317  /*
318  * infoTime may be < simStartTime, this ensures to print
319  * out the info at the very first timestep already
320  */
321  this.nextInfoTime = (int)(Math.floor(simStartTime / this.infoTimeStep) * this.infoTimeStep);
322  }
323 
324 
325  for (Link link : this.network.getLinks().values()) {
326  double freeSpeedTravelTime = link.getLength() / link.getFreespeed(Time.UNDEFINED_TIME);
327 
328  TravelTimeInfo travelTimeInfo = this.travelTimeInfoProvider.getTravelTimeInfo(link);
329  travelTimeInfo.travelTime = freeSpeedTravelTime;
330  travelTimeInfo.init(freeSpeedTravelTime);
331  }
332 
333  // Now initialize the Parallel Update Threads
335  }
336 
337  // Update Link TravelTimeInfos if link attributes have changed
338  @Override
339  public void notifyMobsimAfterSimStep(MobsimAfterSimStepEvent e) {
340  problem = false ;
341 
342  // yyyy In terms of "bushfire evacuation" design (and maybe even in terms of more general transport telematics)
343  // one would need some settable TimeDependentNetworkUpdater class. kai, dec'17
344 
345 // Collection<Link> links = changedLinks.remove(e.getSimulationTime());
346  // yyyyyy I find it quite optimistic to do this with doubles. What
347  // if someone adds a link change event in between two integer
348  // time steps? kai, dec'17
349 
350  while( !changedLinks.isEmpty() && changedLinks.firstKey() <= e.getSimulationTime() ) {
351  Map<Link, Double> map = changedLinks.pollFirstEntry().getValue();
352  for ( Map.Entry<Link,Double> link2speed : map.entrySet() ) {
353  Link link = link2speed.getKey() ;
354  double freeSpeedTravelTime = link.getLength() / link2speed.getValue() ;
355  if ( e.getSimulationTime() > ((QSim)e.getQueueSimulation()).getSimTimer().getSimStartTime() ) {
356  // (otherwise, in some simulations one gets a lot of change events at time 0. kai, dec'17)
357  log.debug("time=" + e.getSimulationTime() +
358  "; network change event for link=" + link.getId() +
359  "; new ttime="+ freeSpeedTravelTime );
360  }
361  TravelTimeInfo travelTimeInfo = this.travelTimeInfoProvider.getTravelTimeInfo(link);
362  travelTimeInfo.init(freeSpeedTravelTime);
363  travelTimeInfo.checkActiveState(); // ensure that the estimated link travel time is updated
364  }
365  }
366 
367 // if (links != null) {
368 // for (Link link : links) {
369 // double freeSpeedTravelTime = link.getLength() / link.getFreespeed(e.getSimulationTime());
370 // if ( e.getSimulationTime() > 0 ) {
371 // log.debug("time=" + e.getSimulationTime() +
372 // ";\tnetwork change event for link=" + link.getId() +
373 // ";\tnew ttime="+ freeSpeedTravelTime );
374 // }
375 // TravelTimeInfo travelTimeInfo = this.travelTimeInfoProvider.getTravelTimeInfo(link);
376 // travelTimeInfo.init(freeSpeedTravelTime);
377 // travelTimeInfo.checkActiveState(); // ensure that the estimated link travel time is updated
378 // }
379 // }
380  }
381 
382  // Update Link TravelTimes
383  @Override
384  public void notifyMobsimBeforeSimStep(MobsimBeforeSimStepEvent e) {
385  problem = false ;
386 
387  now = e.getSimulationTime() ;
388 
389  // parallel Execution
390  this.run(e.getSimulationTime());
391 
392  printInfo(e.getSimulationTime());
393  }
394 
395 
396  @Override
397  public void notifyMobsimBeforeCleanup(MobsimBeforeCleanupEvent e) {
398  problem = false ;
399 
400  /*
401  * Calling the afterSim Method of the Threads will set their
402  * simulationRunning flag to false.
403  */
404  for (UpdateMeanTravelTimesRunnable runnable : this.updateMeanTravelTimesRunnables) {
405  runnable.afterSim();
406  }
407 
408  /*
409  * Triggering the startBarrier of the UpdateMeanTravelTimesRunnables.
410  * They will check whether the Simulation is still running.
411  * It is not, so the Threads will terminate.
412  */
413  try {
414  this.startBarrier.await();
415  } catch (InterruptedException | BrokenBarrierException ex) {
416  throw new RuntimeException(ex);
417  }
418  }
419 
420  private void printInfo(double time) {
421  if (time >= this.nextInfoTime) {
422  int activeLinks = 0;
423  for (UpdateMeanTravelTimesRunnable runnable : this.updateMeanTravelTimesRunnables) {
424  activeLinks += runnable.getActiveLinksCount();
425  }
426 
427  log.info("TravelTimeCollector at " + Time.writeTime(time) + " #links=" + activeLinks);
428 
429  this.nextInfoTime += this.infoTimeStep;
430  }
431  }
432 
433  private static class TripBin {
434  double enterTime;
435  double leaveTime;
436  }
437 
438  /*package*/ static class TravelTimeInfo {
439 
441  List<TripBin> tripBins = new ArrayList<>();
442 
443  boolean isActive = false;
444  // int numActiveTrips = 0;
445  int addedTrips = 0;
446  double addedTravelTimes = 0.0;
447  double sumTravelTimes = 0.0; // We cache the sum of the TravelTimes
448 
449  double freeSpeedTravelTime = Double.MAX_VALUE; // We cache the FreeSpeedTravelTimes
450  double travelTime = Double.MAX_VALUE;
451 
452  double dynamicBinSize = 0.0; // size of the time window that is taken into account
453 
454  static Counter enlarge = new Counter("TravelTimeCollector: enlarged time bin size: ");
455  static Counter shrink = new Counter("TravelTimeCollector: shrunk time bin size: ");
456 
457  /*package*/ void init(double freeSpeedTravelTime) {
458  this.freeSpeedTravelTime = freeSpeedTravelTime;
459  this.dynamicBinSize = freeSpeedTravelTime * 2.5;
460  }
461 
462  /*package*/ void checkActiveState() {
463  if (!isActive) {
464  this.isActive = true;
465  runnable.addTravelTimeInfo(this);
466  }
467  }
468 
469  /*package*/ void checkBinSize(double tripTime) {
470  if (tripTime > dynamicBinSize) {
471  dynamicBinSize = tripTime * 2;
472  enlarge.incCounter();
473  } else if (tripTime * 3 < dynamicBinSize) {
474  dynamicBinSize = tripTime * 3;
475  shrink.incCounter();
476  }
477  }
478  }
479 
480  /*
481  * ----------------------------------------------------------------
482  * Methods for parallel Execution
483  * ----------------------------------------------------------------
484  */
485 
486  /*
487  * The Threads are waiting at the TimeStepStartBarrier. We trigger them by
488  * reaching this Barrier. Now the Threads will start moving the Nodes. We
489  * wait until all of them reach the TimeStepEndBarrier to move on. We should
490  * not have any Problems with Race Conditions because even if the Threads
491  * would be faster than this Thread, means they reach the TimeStepEndBarrier
492  * before this Method does, it should work anyway.
493  */
494  private void run(double time) {
495 
496  try {
497  // set current Time
498  for (UpdateMeanTravelTimesRunnable updateMeanTravelTimesRunnable : updateMeanTravelTimesRunnables) {
499  updateMeanTravelTimesRunnable.setTime(time);
500  }
501 
502  this.startBarrier.await();
503 
504  this.endBarrier.await();
505  } catch (InterruptedException | BrokenBarrierException e) {
506  throw new RuntimeException(e);
507  }
508  }
509 
510  private void initParallelThreads() {
511 
512  this.startBarrier = new CyclicBarrier(numOfThreads + 1);
513  this.endBarrier = new CyclicBarrier(numOfThreads + 1);
514 
515  Thread[] threads = new Thread[numOfThreads];
516  this.updateMeanTravelTimesRunnables = new UpdateMeanTravelTimesRunnable[numOfThreads];
517 
518  // setup threads
519  for (int i = 0; i < numOfThreads; i++) {
520  UpdateMeanTravelTimesRunnable updateMeanTravelTimesRunnable = new UpdateMeanTravelTimesRunnable();
521  updateMeanTravelTimesRunnable.setStartBarrier(this.startBarrier);
522  updateMeanTravelTimesRunnable.setEndBarrier(this.endBarrier);
523  updateMeanTravelTimesRunnables[i] = updateMeanTravelTimesRunnable;
524 
525  Thread thread = new Thread(updateMeanTravelTimesRunnable);
526  thread.setName("UpdateMeanTravelTimes" + i);
527  thread.setDaemon(true); // make the Thread demons so they will terminate automatically
528  threads[i] = thread;
529 
530  thread.start();
531  }
532 
533  /*
534  * Assign the TravelTimeInfos to the Threads
535  */
536  int roundRobin = 0;
537  for (TravelTimeInfo travelTimeInfo : this.travelTimeInfos.values()) {
538  travelTimeInfo.runnable = updateMeanTravelTimesRunnables[roundRobin % numOfThreads];
539  roundRobin++;
540  }
541 
542  /*
543  * After initialization the Threads are waiting at the endBarrier. We
544  * trigger this Barrier once so they wait at the startBarrier what has
545  * to be their state when the run() method is called.
546  */
547  try {
548  this.endBarrier.await();
549  } catch (InterruptedException | BrokenBarrierException e) {
550  throw new RuntimeException(e);
551  }
552  }
553 
554  /*
555  * The thread class that updates the mean travel times.
556  */
557  private static class UpdateMeanTravelTimesRunnable implements Runnable {
558 
559  private volatile boolean simulationRunning = true;
560 
561  private CyclicBarrier startBarrier = null;
562  private CyclicBarrier endBarrier = null;
563 
564  private double time = Time.UNDEFINED_TIME;
565  private Collection<TravelTimeInfo> activeTravelTimeInfos;
566 
568  activeTravelTimeInfos = new ArrayList<>();
569  }
570 
571  public void setStartBarrier(CyclicBarrier cyclicBarrier) {
572  this.startBarrier = cyclicBarrier;
573  }
574 
575  public void setEndBarrier(CyclicBarrier cyclicBarrier) {
576  this.endBarrier = cyclicBarrier;
577  }
578 
579  public void setTime(final double t) {
580  time = t;
581  }
582 
583  public void addTravelTimeInfo(TravelTimeInfo travelTimeInfo) {
584  this.activeTravelTimeInfos.add(travelTimeInfo);
585  }
586 
587  public int getActiveLinksCount() {
588  return this.activeTravelTimeInfos.size();
589  }
590 
591  public void afterSim() {
592  this.simulationRunning = false;
593  }
594 
595  @Override
596  public void run() {
597 
598  while (true) {
599  try {
600  /*
601  * The End of the Moving is synchronized with the
602  * endBarrier. If all Threads reach this Barrier the main
603  * run() Thread can go on.
604  *
605  * The Threads wait now at the startBarrier until they are
606  * triggered again in the next TimeStep by the main run()
607  * method.
608  */
609  endBarrier.await();
610 
611  startBarrier.await();
612 
613  /*
614  * Check if Simulation is still running.
615  * Otherwise print CPU usage and end Thread.
616  */
617  if (!simulationRunning) {
619  return;
620  }
621 
622  Iterator<TravelTimeInfo> iter = activeTravelTimeInfos.iterator();
623  while (iter.hasNext()) {
624  TravelTimeInfo travelTimeInfo = iter.next();
625  calcBinTravelTime(this.time, travelTimeInfo);
626 
627  /*
628  * If no further trips are stored in the TravelTimeInfo,
629  * we deactivate the link and ensure that its expected
630  * travel time is its free speed travel time.
631  */
632  if (travelTimeInfo.tripBins.size() == 0) {
633  travelTimeInfo.isActive = false;
634  travelTimeInfo.travelTime = travelTimeInfo.freeSpeedTravelTime;
635  iter.remove();
636  }
637  }
638 
639  } catch (InterruptedException | BrokenBarrierException e) {
640  throw new RuntimeException(e);
641  }
642  }
643  } // run()
644 
645  private void calcBinTravelTime(double time, TravelTimeInfo travelTimeInfo) {
646  double removedTravelTimes = 0.0;
647 
648  List<TripBin> tripBins = travelTimeInfo.tripBins;
649 
650  // first remove old TravelTimes
651  Iterator<TripBin> iter = tripBins.iterator();
652  while (iter.hasNext()) {
653  TripBin tripBin = iter.next();
654  if (tripBin.leaveTime + travelTimeInfo.dynamicBinSize < time) {
655  double travelTime = tripBin.leaveTime - tripBin.enterTime;
656  removedTravelTimes += travelTime;
657  iter.remove();
658  } else break;
659  }
660 
661  /*
662  * We don't need an update if no Trips have been added or removed
663  * within the current SimStep. The initial FreeSpeedTravelTime has
664  * to be set correctly via setTravelTime!
665  */
666  if (removedTravelTimes == 0.0 && travelTimeInfo.addedTravelTimes == 0.0) return;
667 
668  travelTimeInfo.sumTravelTimes = travelTimeInfo.sumTravelTimes - removedTravelTimes + travelTimeInfo.addedTravelTimes;
669 
670  travelTimeInfo.addedTravelTimes = 0.0;
671 
672  /*
673  * Ensure, that we don't allow TravelTimes shorter than the
674  * FreeSpeedTravelTime.
675  */
676  double meanTravelTime = travelTimeInfo.freeSpeedTravelTime;
677  if (!tripBins.isEmpty()) meanTravelTime = travelTimeInfo.sumTravelTimes / tripBins.size();
678 
679  if (meanTravelTime < travelTimeInfo.freeSpeedTravelTime) {
680  log.warn("Mean TravelTime to short?");
681  travelTimeInfo.travelTime = travelTimeInfo.freeSpeedTravelTime;
682  } else travelTimeInfo.travelTime = meanTravelTime;
683  }
684 
685  } // ReplannerRunnable
686 
687 }
static final double UNDEFINED_TIME
Definition: Time.java:45
TravelTimeInfo getTravelTimeInfo(final Id< Link > linkId)
TravelTimeCollector(Scenario scenario, Set< String > analyzedModes)
double getLinkTravelTime(Link link, double time, Person person, Vehicle vehicle)
static Queue< NetworkChangeEvent > getNetworkChangeEvents(Network network)
static final String writeTime(final double seconds, final String timeformat)
Definition: Time.java:86
Map< Id< Link >,?extends Link > getLinks()
GlobalConfigGroup global
Definition: Config.java:90
static final void printCurrentThreadCpuTime()
Definition: Gbl.java:192