MATSIM
QLinkLanesImpl.java
Go to the documentation of this file.
1 /* *********************************************************************** *
2  * project: org.matsim.*
3  * QueueLink.java
4  * *
5  * *********************************************************************** *
6  * *
7  * copyright : (C) 2007 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.*;
24 
25 import org.apache.logging.log4j.LogManager;
26 import org.apache.logging.log4j.Logger;
27 import org.matsim.api.core.v01.Id;
40 import org.matsim.lanes.Lane;
41 import org.matsim.lanes.ModelLane;
42 import org.matsim.lanes.VisLane;
45 import org.matsim.vehicles.Vehicle;
48 
111 public final class QLinkLanesImpl extends AbstractQLink {
112  private static final Logger log = LogManager.getLogger(QLinkLanesImpl.class);
113 
114  /* public? */ static final class Builder {
115  private final NetsimEngineContext context;
116  private final NetsimInternalInterface netsimEngine;
117  private LinkSpeedCalculator linkSpeedCalculator;
118  private VehicleHandler vehicleHandler = new DefaultVehicleHandler();
120 
121  public Builder(NetsimEngineContext context, NetsimInternalInterface netsimEngine ) {
122  this.context = context;
123  this.netsimEngine = netsimEngine;
124  }
125 
126  public final void setLinkSpeedCalculator( LinkSpeedCalculator linkSpeedCalculator ) {
127  this.linkSpeedCalculator = linkSpeedCalculator ;
128  }
129 
130  public void setFlowEfficiencyCalculator(FlowEfficiencyCalculator flowEfficiencyCalculator) {
131  this.flowEfficiencyCalculator = flowEfficiencyCalculator;
132  }
133 
134  AbstractQLink build(Link link, QNodeI toNodeQ, List<ModelLane> lanes ) {
135  Objects.requireNonNull( linkSpeedCalculator );
136  return new QLinkLanesImpl(link, toNodeQ, lanes, context, netsimEngine, linkSpeedCalculator, flowEfficiencyCalculator, vehicleHandler ) ;
137  }
138 
139  }
140 
144  private final QNodeI toQueueNode;
149 
153  private final LinkedHashMap<Id<Lane>, QLaneI> laneQueues;
154 
159  private List<QLaneI> toNodeLaneQueues = null;
160 
161  private VisData visdata = null;
162 
163  private final List<ModelLane> lanes;
164 
166 
167  private final Map<Id<Lane>, Map<Id<Link>, List<QLaneI>>> nextQueueToLinkCache;
168 
170 
178  private QLinkLanesImpl(final Link link, final QNodeI toNodeQ, List<ModelLane> lanes, NetsimEngineContext context,
179  NetsimInternalInterface netsimEngine, LinkSpeedCalculator linkSpeedCalculator, FlowEfficiencyCalculator flowEfficiencyCalculator, VehicleHandler vehicleHandler) {
180  super(link, toNodeQ, context, netsimEngine, linkSpeedCalculator, vehicleHandler);
181  this.context = context ;
182  this.toQueueNode = toNodeQ;
183  this.laneQueues = new LinkedHashMap<>();
184  this.toNodeLaneQueues = new ArrayList<>();
185  this.lanes = lanes;
186  this.flowEfficiencyCalculator = flowEfficiencyCalculator;
187  this.nextQueueToLinkCache = new LinkedHashMap<>(); // maps a lane id to a map containing the
188  // downstream queues indexed by a
189  // downstream link
190  this.initLaneQueues();
191  this.visdata = this.new VisDataImpl();
192  this.setTransitQLink(new TransitQLink(this.firstLaneQueue));
193  }
194 
195  private void initLaneQueues() {
196  Stack<QLaneI> stack = new Stack<>();
197  Map<Id<Lane>, QLaneI> queueByIdMap = new HashMap<>();
198  Map<Id<Lane>, Set<Id<Link>>> laneIdToLinksMap = new HashMap<>();
199  for (ModelLane lane : lanes) { // lanes is sorted downstream to upstream
200  Id<Lane> laneId = lane.getLaneData().getId();
201  // --
202  // QLaneI queue = new QueueWithBuffer(this, new FIFOVehicleQ(), laneId,
203  // lane.getLength(), noEffectiveLanes,
204  // (lane.getLaneData().getCapacityVehiclesPerHour()/3600.0));
205 
206  QueueWithBuffer.Builder builder = new QueueWithBuffer.Builder( context ) ;
207  builder.setVehicleQueue(new FIFOVehicleQ());
208  builder.setLaneId(laneId);
209  builder.setLength(lane.getLength());
210  builder.setEffectiveNumberOfLanes(lane.getLaneData().getNumberOfRepresentedLanes());
211  builder.setFlowCapacity_s(lane.getLaneData().getCapacityVehiclesPerHour() / 3600.);
212  builder.setFlowEfficiencyCalculator(flowEfficiencyCalculator);
213  QLaneI queue = builder.createLane(this);
214  // --
215  queueByIdMap.put(laneId, queue);
216  firstLaneQueue = queue;
217  stack.push(queue);
218  Set<Id<Link>> toLinkIds = new HashSet<>();
219 
220  if (lane.getToLanes() == null || lane.getToLanes().isEmpty()) { // lane is at the end of
221  // link
222  this.toNodeLaneQueues.add(queue);
223  toLinkIds.addAll(lane.getLaneData().getToLinkIds());
224  laneIdToLinksMap.put(laneId, toLinkIds);
225  } else { // lane is within the link and has no connection to a node
226  Map<Id<Link>, List<QLaneI>> toLinkIdDownstreamQueues = new LinkedHashMap<>();
227  nextQueueToLinkCache.put(Id.create(queue.getId(), Lane.class),
228  toLinkIdDownstreamQueues);
229  for (ModelLane toLane : lane.getToLanes()) {
230  Set<Id<Link>> toLinks = laneIdToLinksMap.get(toLane.getLaneData().getId());
231  if (toLinks == null){
232  // nothing to do here
233  break;
234  }
235  if (!laneIdToLinksMap.containsKey(laneId)) {
236  laneIdToLinksMap.put(laneId, new HashSet<Id<Link>>());
237  }
238  laneIdToLinksMap.get(laneId).addAll(toLinks);
239  for (Id<Link> toLinkId : toLinks) {
240  List<QLaneI> downstreamQueues = toLinkIdDownstreamQueues.get(toLinkId);
241  if (downstreamQueues == null) {
242  downstreamQueues = new ArrayList<>();
243  toLinkIdDownstreamQueues.put(toLinkId, downstreamQueues);
244  }
245  downstreamQueues.add(queueByIdMap.get(toLane.getLaneData().getId()));
246  }
247  }
248  }
249 // queue.changeSpeedMetersPerSecond( this.getLink().getFreespeed() );
250 
251  }
252  // reverse the order in the linked map, i.e. upstream to downstream
253  while (!stack.isEmpty()) {
254  QLaneI queue = stack.pop();
255  this.laneQueues.put(queue.getId(), queue);
256  }
257  }
258 
259  @Override
260  public List<QLaneI> getOfferingQLanes() {
261  return this.toNodeLaneQueues;
262  }
263 
264  @Override
265  public void clearVehicles() {
266  super.clearVehicles();
267  for (QLaneI lane : this.laneQueues.values()) {
268  lane.clearVehicles();
269  }
270  }
271 
272  @Override
273  public boolean doSimStep() {
274  double now = context.getSimTimer().getTimeOfDay() ;
275 
276  boolean lanesActive = false;
277  boolean movedWaitToRoad = false;
278  if ( context.qsimConfig.isInsertingWaitingVehiclesBeforeDrivingVehicles() ) {
279  //TODO
280  //Because moveBufferToNextLane() (called from moveLanes()) is kind of "moveInternalNodes()",
281  //it should be executed before moveWaitToRoad() to keep the sequence fully consistent.
282  //The sequence is broken only if isInsertingWaitingVehiclesBeforeDrivingVehicles==true.
283  //Currently, the buffer of the accepting lane gets emptied after moveWaitToRoad(),
284  //which gives preference to already driving vehicles
285  //michalm, jan'17
286  this.moveWaitToRoad(now);
287  this.getTransitQLink().handleTransitVehiclesInStopQueue(now);
288  lanesActive = this.moveLanes();
289  } else {
290  this.getTransitQLink().handleTransitVehiclesInStopQueue(now);
291  lanesActive = this.moveLanes();
292  movedWaitToRoad = this.moveWaitToRoad(now);
293  }
294  this.setActive(lanesActive || movedWaitToRoad || (!this.getWaitingList().isEmpty())
295  || !this.getTransitQLink().getTransitVehicleStopQueue().isEmpty());
296  return this.isActive();
297  }
298 
299  private boolean moveLanes() {
300  boolean activeLane = false;
301  for (QLaneI lane : this.laneQueues.values()) {
302  // (go through all lanes)
303 
304  /* part A */
305  if (!this.toNodeLaneQueues.contains(lane)) {
306  // (so it HAS a link-internal next lane)
307 
308  // move vehicles from the lane buffer to the next lane
309  this.moveBufferToNextLane(lane);
310  } else {
311  // move vehicles from the lane buffer to the link buffer, i.e. prepare moving them
312  // to the next link
313 // ((QueueWithBuffer) queue).moveQueueToBuffer();
314  // yy just commented out the above line. Can't say why it might needed; doSimStep also calls moveQueueToBuffer.
315  // Tests run ok, but it may have capacity ramifications outside tests. kai, mar'16
316  }
317  /* end of part A */
318  }
319 
320  for (QLaneI lane : this.laneQueues.values()) {
321  lane.initBeforeSimStep();
322  }
323 
324  for (QLaneI lane : this.laneQueues.values()) {
325  // (go through all lanes)
326 
327  /* part B */
328  // move vehicles to the lane buffer if they have reached their earliest lane exit time
329  lane.doSimStep();
330  /* end of part B */
331 
332  activeLane = activeLane || lane.isActive();
333 
334  /*
335  * Remark: The order of part A and B influences the travel time on lanes.
336  *
337  * Before Jul'15 order B, A was used, such that travel time on lanes was practically
338  * rounded down. This has the unintended effect that introducing lanes on a link may
339  * decrease its travel time: Dividing the link into sufficient many lanes (each one
340  * shorter than the number of meters that an agent may travel in 1 second) reduces the
341  * link travel time to 1 second.
342  *
343  * Order A, B produces the same behavior for lanes as for links. I.e. up to one
344  * additional second may occur for every lane, because travel times are now practically
345  * rounded up.
346  *
347  * Theresa, Jul'15
348  */
349  }
350  return activeLane;
351  }
352 
353  private void moveBufferToNextLane(QLaneI qlane) {
354  QVehicle veh;
355  while (!qlane.isNotOfferingVehicle()) {
356  veh = qlane.getFirstVehicle();
357  Id<Link> toLinkId = veh.getDriver().chooseNextLinkId();
358  QLaneI nextQueue = this.chooseNextLane(qlane, toLinkId);
359  if (nextQueue != null) {
360  if (nextQueue.isAcceptingFromUpstream()) {
361  qlane.popFirstVehicle();
362  nextQueue.addFromUpstream(veh);
363  } else {
364  break;
365  }
366  } else {
367  StringBuilder b = new StringBuilder();
368  b.append("Person Id: ").append(veh.getDriver().getId());
369  b.append(" is on Lane Id ").append(qlane.getId());
370  b.append(" on Link Id ").append(this.getLink().getId());
371  b.append(" and wants to drive to Link Id ").append(toLinkId);
372  b.append(" but there is no Lane leading to that Link!");
373  log.error(b.toString());
374  throw new IllegalStateException(b.toString());
375  }
376  }
377  }
378 
379  private QLaneI chooseNextLane(QLaneI queue, Id<Link> toLinkId) {
380  List<QLaneI> toQueues = this.nextQueueToLinkCache.get(queue.getId()).get(toLinkId);
381  QLaneI retLane = toQueues.get(0);
382  if (toQueues.size() == 1) {
383  return retLane;
384  }
385  // else chose lane by storage cap
386  for (int i = 1; i < toQueues.size(); i++) {
387  QLaneI toQueue = toQueues.get(i);
388  if (toQueue.getLoadIndicator() < retLane.getLoadIndicator()) {
389  retLane = toQueue;
390  }
391  }
392  return retLane;
393  }
394 
402  private boolean moveWaitToRoad(final double now) {
403  boolean movedWaitToRoad = false;
404  while (!getWaitingList().isEmpty()) {
405  if (!firstLaneQueue.isAcceptingFromWait(this.getWaitingList().peek())) {
406  return movedWaitToRoad;
407  }
408  QVehicle veh = this.getWaitingList().poll();
409 
410  movedWaitToRoad = true;
411  context .getEventsManager() .processEvent(
412  new VehicleEntersTrafficEvent(now, veh.getDriver().getId(),
413  this.getLink().getId(), veh.getId(), veh.getDriver().getMode(), 1.0));
414 
415  if (this.getTransitQLink().addTransitToStopQueue(now, veh, this.getLink().getId())) {
416  continue;
417  }
418 
419  // if (veh.getDriver().chooseNextLinkId() == null) {
421  // If the driver wants to stop on this link, give them a special treatment.
422  // addFromWait doesn't work here, because after that, they cannot stop anymore.
423  this.firstLaneQueue.addTransitSlightlyUpstreamOfStop(veh);
424  continue;
425  }
426 
427  this.firstLaneQueue.addFromWait(veh);
428  }
429  return movedWaitToRoad;
430  }
431 
432  @Override
433  public boolean isNotOfferingVehicle() {
434  // otherwise we have to do a bit more work
435  for (QLaneI lane : this.toNodeLaneQueues) {
436  if (!lane.isNotOfferingVehicle()) {
437  return false;
438  }
439  }
440  return true;
441  }
442 
443  @Override
445 // double now = context.getSimTimer().getTimeOfDay() ;
446 
447  for (QLaneI lane : this.laneQueues.values()) {
448 // lane.changeEffectiveNumberOfLanes( getLink().getNumberOfLanes( now ) ) ;
449 // lane.changeSpeedMetersPerSecond( getLink().getFreespeed(now) ) ;
450 // lane.changeUnscaledFlowCapacityPerSecond( ((Link)getLink()).getFlowCapacityPerSec(now) );
451  lane.recalcTimeVariantAttributes();
452  }
453  }
454 
455  @Override
456  public QVehicle getVehicle(Id<Vehicle> vehicleId) {
457  QVehicle ret = super.getVehicle(vehicleId);
458  if (ret != null) {
459  return ret;
460  }
461  for (QVehicle veh : this.getWaitingList()) {
462  if (veh.getId().equals(vehicleId))
463  return veh;
464  }
465  for (QLaneI lane : this.laneQueues.values()) {
466  ret = lane.getVehicle(vehicleId);
467  if (ret != null) {
468  return ret;
469  }
470  }
471  return ret;
472  }
473 
474  @Override
475  public final Collection<MobsimVehicle> getAllNonParkedVehicles() {
476  Collection<MobsimVehicle> ret = new ArrayList<MobsimVehicle>(this.getWaitingList());
477  for (QLaneI lane : this.laneQueues.values()) {
478  ret.addAll(lane.getAllVehicles());
479  }
480  return ret;
481  }
482 
487  double getSpaceCap() {
488  // (only for tests)
489  double total = 0.0;
490  for (QLaneI lane : this.laneQueues.values()) {
491  final double storageCapacity = lane.getStorageCapacity();
492  log.warn("storageCapacity=" + storageCapacity) ;
493  total += storageCapacity;
494  }
495  return total;
496  }
497 
498  @Override
499  public QNodeI getToNode() {
500  return this.toQueueNode;
501  }
502 
511  double getSimulatedFlowCapacity() {
512  return this.firstLaneQueue.getSimulatedFlowCapacityPerTimeStep();
513  }
514 
518  public LinkedHashMap<Id<Lane>, QLaneI> getQueueLanes() {
519  return this.laneQueues;
520  }
521 
522  @Override
523  public VisData getVisData() {
524  return this.visdata;
525  }
526 
527  QLaneI getOriginalLane() {
528  return this.firstLaneQueue;
529  }
530 
537  class VisDataImpl implements VisData {
538  private VisLaneModelBuilder visModelBuilder = null;
539  private VisLinkWLanes visLink = null;
540 
541  VisDataImpl() {
542  double nodeOffset = context.qsimConfig.getNodeOffset();
543  if (nodeOffset != 0.0) {
544  nodeOffset = nodeOffset + 2.0; // +2.0: eventually we need a bit space for the
545  // signal
546  visModelBuilder = new VisLaneModelBuilder();
547  CoordinateTransformation transformation = new IdentityTransformation();
548  visLink = visModelBuilder.createVisLinkLanes(transformation, QLinkLanesImpl.this, nodeOffset, lanes);
549  visModelBuilder.recalculatePositions(visLink, context.linkWidthCalculator);
550  }
551  }
552 
553  @Override
554  public Collection<AgentSnapshotInfo> addAgentSnapshotInfo(
555  final Collection<AgentSnapshotInfo> positions) {
556 
557  double now = context.getSimTimer().getTimeOfDay() ;
558 
559 
560  if (visLink != null) {
561  for (QLaneI ql : QLinkLanesImpl.this.laneQueues.values()) {
562  VisLane otfLane = visLink.getLaneData().get(
563  ql.getId().toString());
564  ((QueueWithBuffer.VisDataImpl) ql.getVisData()).setVisInfo(
565  otfLane.getStartCoord(), otfLane.getEndCoord());
566  }
567  }
568 
569  for (QLaneI road : QLinkLanesImpl.this.getQueueLanes().values()) {
570  road.getVisData().addAgentSnapshotInfo(positions, now);
571  }
572 
573  int cnt2 = 10;
574 
575  // treat vehicles from transit stops
576  cnt2 = context.snapshotInfoBuilder.positionVehiclesFromTransitStop(positions, getLink(),
577  getTransitQLink().getTransitVehicleStopQueue(), cnt2);
578  // treat vehicles from waiting list:
579  context.snapshotInfoBuilder.positionVehiclesFromWaitingList(positions,
580  QLinkLanesImpl.this.getLink(), cnt2, QLinkLanesImpl.this.getWaitingList());
581  cnt2 = QLinkLanesImpl.this.getWaitingList().size();
582  context.snapshotInfoBuilder.positionAgentsInActivities(positions, QLinkLanesImpl.this.getLink(),
583  QLinkLanesImpl.this.getAdditionalAgentsOnLink(), cnt2);
584 
585  return positions;
586  }
587  }
588 
589  @Override
591  return this.firstLaneQueue ;
592  }
593 
594 }
void addTransitSlightlyUpstreamOfStop(final QVehicle veh)
static< T > Id< T > create(final long key, final Class< T > type)
Definition: Id.java:68
List< ModelLane > getToLanes()
Definition: ModelLane.java:54
VisLinkWLanes createVisLinkLanes(CoordinateTransformation transform, VisLink link, double nodeOffsetMeter, List< ModelLane > lanes)
void recalculatePositions(VisLinkWLanes linkData, SnapshotLinkWidthCalculator linkWidthCalculator)