MATSIM
AbstractQNetsimEngine.java
Go to the documentation of this file.
1 /* *********************************************************************** *
2  * project: org.matsim.*
3  * QNetsimEngine.java
4  * *
5  * *********************************************************************** *
6  * *
7  * copyright : (C) 2009 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.core.mobsim.qsim.qnetsimengine;
22 
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27 
28 import org.apache.logging.log4j.LogManager;
29 import org.apache.logging.log4j.Logger;
30 import org.matsim.api.core.v01.Id;
31 import org.matsim.api.core.v01.Scenario;
35 import org.matsim.core.config.Config;
40 import org.matsim.core.gbl.Gbl;
44 import org.matsim.core.mobsim.qsim.QSim;
47 import org.matsim.core.utils.misc.Time;
48 import org.matsim.vehicles.Vehicle;
50 
60 abstract class AbstractQNetsimEngine<A extends AbstractQNetsimEngineRunner> implements QNetsimEngineI {
61 
62  private NetsimInternalInterface ii = new NetsimInternalInterface(){
63  @Override public QNetwork getNetsimNetwork() {
64  return qNetwork;
65  }
66  @Override public void arrangeNextAgentState(MobsimAgent driver) {
67  AbstractQNetsimEngine.this.arrangeNextAgentState(driver);
68  }
69  @Override public void letVehicleArrive(QVehicle veh) {
70  AbstractQNetsimEngine.this.letVehicleArrive( veh ) ;
71  }
72  } ;
73 
74  private static final Logger log = LogManager.getLogger(AbstractQNetsimEngine.class);
75  private static final int INFO_PERIOD = 3600;
76 
77  // for detailed run time analysis - used in combination with QSim.analyzeRunTimes
78  public static int numObservedTimeSteps = 24*3600;
79  public static boolean printRunTimesPerTimeStep = false;
80 
81  private final Map<Id<Vehicle>, QVehicle> vehicles = new HashMap<>();
82  private final QSim qsim;
83 // private final NetworkModeDepartureHandler dpHandler;
84 // private final Set<QLinkI> linksToActivateInitially = new HashSet<>();
85  protected final int numOfThreads;
86  protected final QNetwork qNetwork;
87 
88  private double infoTime = 0;
89  private List<A> engines;
90  private InternalInterface internalInterface = null;
91 
92  AbstractQNetsimEngine(final QSim sim, QNetworkFactory netsimNetworkFactory, NetworkModeDepartureHandler dpHandler) {
93  if ( netsimNetworkFactory==null ) {
94  throw new RuntimeException( "this execution path is no longer allowed; network factory needs to come from elsewhere (in general via injection). kai, jun'23" );
95  }
96 
97  this.qsim = sim;
98 
99  final Config config = sim.getScenario().getConfig();
100  final QSimConfigGroup qSimConfigGroup = config.qsim();
101 
102 // // configuring the car departure hander (including the vehicle behavior)
103 // VehicleBehavior vehicleBehavior = qSimConfigGroup.getVehicleBehavior();
104 // switch(vehicleBehavior) {
105 // case exception:
106 // case teleport:
107 // case wait:
108 // break;
109 // default:
110 // throw new RuntimeException("Unknown vehicle behavior option.");
111 // }
112 
113  if(qSimConfigGroup.getLinkDynamics().equals(LinkDynamics.SeepageQ)) {
114  log.info("Seepage is allowed. Seep mode(s) is(are) " + qSimConfigGroup.getSeepModes() + ".");
115  if(qSimConfigGroup.isSeepModeStorageFree()) {
116  log.warn("Seep mode(s) " + qSimConfigGroup.getSeepModes() + " does not take storage space thus only considered for flow capacities.");
117  }
118  }
119 
120  qNetwork = new QNetwork( sim.getScenario().getNetwork(), netsimNetworkFactory ) ;
121 
122  qNetwork.initialize(this, sim.getAgentCounter(), sim.getSimTimer() );
123  // yyyy this now looks like the initialize could be integrated into the constructor?! kai, jan'25
124 
125  this.numOfThreads = sim.getScenario().getConfig().qsim().getNumberOfThreads();
126  }
127 
128  static AbstractAgentSnapshotInfoBuilder createAgentSnapshotInfoBuilder(Scenario scenario, SnapshotLinkWidthCalculator linkWidthCalculator) {
129  final SnapshotStyle snapshotStyle = scenario.getConfig().qsim().getSnapshotStyle();
130  switch(snapshotStyle) {
131  case queue:
132  return new QueueAgentSnapshotInfoBuilder(scenario, linkWidthCalculator);
133  case withHoles:
134  case withHolesAndShowHoles:
135  // the difference is not in the spacing, thus cannot be differentiated by using different classes. kai, sep'14
136  // ??? kai, nov'15
137  return new QueueAgentSnapshotInfoBuilder(scenario, linkWidthCalculator);
138  case kinematicWaves:
139  log.warn("The snapshotStyle \"" + snapshotStyle + "\" is not explicitly supported. Using \""+SnapshotStyle.withHoles+ "\" instead.");
140  return new QueueAgentSnapshotInfoBuilder(scenario, linkWidthCalculator);
141  case equiDist:
142  return new EquiDistAgentSnapshotInfoBuilder(scenario, linkWidthCalculator);
143  default:
144  log.warn("The snapshotStyle \"" + snapshotStyle + "\" is not supported. Using equiDist");
145  return new EquiDistAgentSnapshotInfoBuilder(scenario, linkWidthCalculator);
146  }
147  }
148 
149  @Override
150  public final void onPrepareSim() {
151  this.infoTime = Math.floor(internalInterface.getMobsim().getSimTimer().getSimStartTime() / INFO_PERIOD) * INFO_PERIOD;
152  // (infoTime may be < simStartTime, this ensures to print out the * info at the very first timestep already)
153 
154  this.engines = initQSimEngineRunners();
155  assignNetElementActivators();
156  initMultiThreading();
157  }
158 
162  protected abstract void initMultiThreading();
163 
164 
165  @Override
166  public final void afterSim() {
167 
168  /*
169  * Calling the afterSim Method of the QSimEngineThreads
170  * will set their simulationRunning flag to false.
171  */
172  for (AbstractQNetsimEngineRunner engine : this.getQnetsimEngineRunner()) {
173  engine.afterSim();
174  }
175 
176  finishMultiThreading();
177 
178  /* Reset vehicles on ALL links. We cannot iterate only over the active links
179  * (this.simLinksArray), because there may be links that have vehicles only
180  * in the buffer (such links are *not* active, as the buffer gets emptied
181  * when handling the nodes.
182  */
183  for (QLinkI link : qNetwork.getNetsimLinks().values()) {
184  link.clearVehicles();
185  }
186  }
187 
191  protected abstract void finishMultiThreading();
192 
199  protected abstract void run(double time);
200 
206  protected abstract List<A> initQSimEngineRunners() ;
207 
212  @Override
213  public final void doSimStep(final double time) {
214  run(time);
215 
216  this.printSimLog(time);
217  }
218 
219 
220  @Override
221  public final void setInternalInterface( InternalInterface internalInterface) {
222  this.internalInterface = internalInterface;
223  }
224 
225  private static int wrnCnt = 0;
226 
227  public final void addParkedVehicle(MobsimVehicle veh, Id<Link> startLinkId) {
228  if (this.vehicles.put(veh.getId(), (QVehicle) veh) != null) {
229  if (wrnCnt < 1) {
230  wrnCnt++ ;
231  log.warn("existing vehicle in mobsim was just overwritten by other vehicle with same ID. Not clear what this means. Continuing anyways ...") ;
232  log.warn(Gbl.ONLYONCE);
233  }
234  }
235  QLinkI qlink = qNetwork.getNetsimLinks().get(startLinkId );
236  if (qlink == null) {
237  throw new RuntimeException("requested link with id=" + startLinkId + " does not exist in network. Possible vehicles "
238  + "or activities or facilities are registered to a different network.") ;
239  }
240  qlink.addParkedVehicle(veh);
241  }
242 
243  public final int getNumberOfSimulatedLinks() {
244 
245  int numLinks = 0;
246 
247  for (AbstractQNetsimEngineRunner engine : this.engines) {
248  numLinks = numLinks + engine.getNumberOfSimulatedLinks();
249  }
250 
251  return numLinks;
252  }
253 
254  public final int getNumberOfSimulatedNodes() {
255 
256  int numNodes = 0;
257 
258  for (AbstractQNetsimEngineRunner engine : this.engines) {
259  numNodes = numNodes + engine.getNumberOfSimulatedNodes();
260  }
261 
262  return numNodes;
263  }
264 
265  public final NetsimNetwork getNetsimNetwork() {
266  // yy isn't this available from injection? kai, jan'25
267  return this.qNetwork;
268  }
269 
270 // public final NetworkModeDepartureHandler getVehicularDepartureHandler() {
271 // return dpHandler;
272 // }
273  // get from injection
274 
275  public final Map<Id<Vehicle>, QVehicle> getVehicles() {
276  return Collections.unmodifiableMap(this.vehicles);
277  }
278 
279  public final void registerAdditionalAgentOnLink(final MobsimAgent planAgent) {
280  Id<Link> linkId = planAgent.getCurrentLinkId();
281  if (linkId != null) { // may be bushwacking
282  QLinkI qLink = this.qNetwork.getNetsimLink(linkId );
283  if ( qLink==null ) {
284  throw new RuntimeException("netsim link lookup failed; agentId=" + planAgent.getId() + "; linkId=" + linkId ) ;
285  }
286  qLink.registerAdditionalAgentOnLink(planAgent);
287  }
288  }
289 
290  public final MobsimAgent unregisterAdditionalAgentOnLink(Id<Person> agentId, Id<Link> linkId) {
291  if (linkId == null) { // seems that this can happen in tests; not sure if it can happen in regular code. kai, jun'15
292  return null;
293  }
294  QLinkI qLink = this.qNetwork.getNetsimLink(linkId );
295  return qLink.unregisterAdditionalAgentOnLink(agentId);
296  }
297 
298  public final void printEngineRunTimes() {
299  if (!QSim.analyzeRunTimes) return;
300 
301  if (printRunTimesPerTimeStep) log.info("detailed QNetsimEngineRunner run times per time step:");
302  {
303  StringBuffer sb = new StringBuffer();
304  sb.append("\t");
305  sb.append("time");
306  for (int i = 0; i < this.engines.size(); i++) {
307  sb.append("\t");
308  sb.append("thread_");
309  sb.append(Integer.toString(i));
310  }
311  sb.append("\t");
312  sb.append("min");
313  sb.append("\t");
314  sb.append("max");
315  if (printRunTimesPerTimeStep) log.info(sb.toString());
316  }
317  long sum = 0;
318  long sumMin = 0;
319  long sumMax = 0;
320  for (int i = 0; i < numObservedTimeSteps; i++) {
321  StringBuffer sb = new StringBuffer();
322  sb.append("\t" + i);
323  long min = Long.MAX_VALUE;
324  long max = Long.MIN_VALUE;
325  for (AbstractQNetsimEngineRunner runner : this.engines) {
326  long runTime = runner.runTimes[i];
327  sum += runTime;
328  if (runTime < min) min = runTime;
329  if (runTime > max) max = runTime;
330  sb.append("\t");
331  sb.append(Long.toString(runTime));
332  }
333  sb.append("\t");
334  sb.append(Long.toString(min));
335  sb.append("\t");
336  sb.append(Long.toString(max));
337  if (printRunTimesPerTimeStep) log.info(sb.toString());
338  sumMin += min;
339  sumMax += max;
340  }
341  log.info("sum min run times: " + sumMin);
342  log.info("sum max run times: " + sumMax);
343  log.info("sum all run times / num threads: " + sum / this.numOfThreads);
344  }
345 
346  @Override
347  public final NetsimInternalInterface getNetsimInternalInterface() {
348  return ii;
349  }
350 
351  private final void printSimLog(double time) {
352  if (time >= this.infoTime) {
353  this.infoTime += INFO_PERIOD;
354  int nofActiveLinks = this.getNumberOfSimulatedLinks();
355  int nofActiveNodes = this.getNumberOfSimulatedNodes();
356  log.info("SIMULATION (QNetsimEngine) AT " + Time.writeTime(time)
357  + " : #links=" + nofActiveLinks
358  + " #nodes=" + nofActiveNodes);
359  }
360  }
361 
362  private void letVehicleArrive(QVehicle veh) {
363  double now = this.qsim.getSimTimer().getTimeOfDay();
364  MobsimDriverAgent driver = veh.getDriver();
365  this.qsim.getEventsManager().processEvent(new PersonLeavesVehicleEvent(now, driver.getId(), veh.getId()));
366  // reset vehicles driver
367  veh.setDriver(null);
368  driver.endLegAndComputeNextState(now);
369  this.internalInterface.arrangeNextAgentState(driver);
370  }
371 
372  /*
373  * Within the MoveThreads Links are only activated when a Vehicle is moved
374  * over a Node which is processed by that Thread. So we can assign each QLink
375  * to the Thread that handles its InNode.
376  */
377  private void assignNetElementActivators() {
378 
379  // only for statistics
380  int nodes[] = new int[this.engines.size()];
381  int links[] = new int[this.engines.size()];
382 
383  int roundRobin = 0;
384  for (QNodeI node : qNetwork.getNetsimNodes().values()) {
385  int i = roundRobin % this.engines.size();
386  if( node instanceof AbstractQNode){
387  ((AbstractQNode) node).setNetElementActivationRegistry(this.engines.get(i));
388  }
389  nodes[i]++;
390 
391  // set activator for out links
392  for (Link outLink : node.getNode().getOutLinks().values()) {
393  AbstractQLink qLink = (AbstractQLink) qNetwork.getNetsimLink(outLink.getId() );
394  // (must be of this type to work. kai, feb'12)
395 
396  // removing qsim as "person in the middle". not fully sure if this is the same in the parallel impl. kai, oct'10
397  qLink.setNetElementActivationRegistry(this.engines.get(i));
398 
399  /*
400  * If the QLink contains agents that end their activity in the first time
401  * step, the link should be activated.
402  */
403  // this set is always empty...
404 // if (linksToActivateInitially.remove(qLink)
405 // || qsim.getScenario().getConfig().qsim().getSimStarttimeInterpretation()==StarttimeInterpretation.onlyUseStarttime) {
406 // this.engines.get(i).registerLinkAsActive(qLink);
407 // }
408 
409  links[i]++;
410 
411  }
412 
413  roundRobin++;
414  }
415 
416  // print some statistics
417  for (int i = 0; i < this.engines.size(); i++) {
418  log.info("Assigned " + nodes[i] + " nodes and " + links[i] + " links to QSimEngineRunner #" + i);
419  }
420 
421 // this.linksToActivateInitially.clear();
422  }
423 
424  private final void arrangeNextAgentState(MobsimAgent pp) {
425  internalInterface.arrangeNextAgentState(pp);
426  }
427 
431  protected List<A> getQnetsimEngineRunner(){
432  return this.engines;
433  }
434 }