MATSIM
AbstractAgentSnapshotInfoBuilder.java
Go to the documentation of this file.
1 /* *********************************************************************** *
2  * project: org.matsim.*
3  * AbstractAgentSnapshotInfoBuilder
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 package org.matsim.core.mobsim.qsim.qnetsimengine;
21 
22 import org.apache.logging.log4j.LogManager;
23 import org.apache.logging.log4j.Logger;
24 import org.matsim.api.core.v01.*;
28 import org.matsim.core.gbl.Gbl;
40 
41 import java.util.Collection;
42 import java.util.Map;
43 import java.util.Queue;
44 import java.util.TreeMap;
45 
46 import static java.lang.Math.min;
47 
48 
53 abstract class AbstractAgentSnapshotInfoBuilder {
54  private static final Logger log = LogManager.getLogger(AbstractAgentSnapshotInfoBuilder.class);
55  private static int wrnCnt = 0;
56 
57  private final Scenario scenario;
58  private final SnapshotLinkWidthCalculator linkWidthCalculator;
59 
60  AbstractAgentSnapshotInfoBuilder(Scenario sc, SnapshotLinkWidthCalculator linkWidthCalculator) {
61  this.scenario = sc;
62  this.linkWidthCalculator = linkWidthCalculator;
63  }
64 
65  private PositionInfo.LinkBasedBuilder newBuilder() {
66  return new PositionInfo.LinkBasedBuilder().setLinkWidthCalculator(linkWidthCalculator);
67  }
68 
69  private static double computeHolePositionAndReturnDistance(double freespeedTraveltime, Hole hole, double now, double curvedLength) {
70  double remainingTravelTime = hole.getEarliestLinkExitTime() - now;
71  return remainingTravelTime / freespeedTraveltime * curvedLength;
72  }
73 
78  public final int positionVehiclesFromWaitingList(final Collection<AgentSnapshotInfo> positions,
79  final Link link, int cnt2, final Queue<QVehicle> waitingList) {
80  return positionStack(positions, waitingList, cnt2);
81  }
82 
83  public final int positionAgentsInActivities(final Collection<AgentSnapshotInfo> positions, Link link,
84  Collection<? extends MobsimAgent> agentsInActivities, int cnt2) {
85  var builder = newBuilder();
86  builder.setVehicleId(null); // we don't have a vehicle in this case.
87  for (MobsimAgent agent : agentsInActivities) {
88 
89  var position = builder
90  .setPersonId(agent.getId())
91  .setLinkId(link.getId())
92  .setFromCoord(link.getFromNode().getCoord())
93  .setToCoord(link.getToNode().getCoord())
94  .setDistanceOnLink(link.getLength() * 0.9)
95  .setLinkLength(link.getLength())
96  .setLane(cnt2)
97  .setAgentState(getAgentStateForActivity(agent.getId()))
98  .build();
99  positions.add(position);
100  cnt2++ ;
101  }
102  return cnt2;
103  }
104 
108  public final int positionVehiclesFromTransitStop(final Collection<AgentSnapshotInfo> positions, Link link,
109  Queue<QVehicle> transitVehicleStopQueue, int cnt2) {
110  return positionStack(positions, transitVehicleStopQueue, cnt2);
111  }
112 
113  public final void positionAgentGivenDistanceFromFNode(final Collection<AgentSnapshotInfo> positions, Coord startCoord, Coord endCoord,
114  double lengthOfCurve, QVehicle veh, double distanceFromFromNode,
115  int lane, double speedValueBetweenZeroAndOne) {
116  // I think that the main reason why this exists as public method is that AssignmentEmulatingQLane wants to use it directly.
117  // The reason for this, in return, is that positionVehiclesAlongLine(...) is a service method for queue models only. kai, apr'16
118 
119  MobsimDriverAgent driverAgent = veh.getDriver();
120  var builder = newBuilder();
121 
122  var position = builder
123  .setPersonId(driverAgent.getId())
124  .setVehicleId(veh.getId())
125  .setLinkId(veh.getCurrentLink().getId())
126  .setFromCoord(startCoord)
127  .setToCoord(endCoord)
128  .setDistanceOnLink(distanceFromFromNode)
129  .setLane(lane)
130  .setLinkLength(lengthOfCurve)
131  .setAgentState(getAgentState(driverAgent))
132  .setColorValue(speedValueBetweenZeroAndOne)
133  .build();
134 
135  this.positionPassengers(positions, veh.getPassengers(), distanceFromFromNode, startCoord,
136  endCoord, lengthOfCurve, lane + 5, speedValueBetweenZeroAndOne);
137  // (this is deliberately first memorizing "pos" but then filling in the passengers first)
138 
139  positions.add(position);
140  }
141 
142  public final Collection<AgentSnapshotInfo> positionVehiclesAlongLine( Collection<AgentSnapshotInfo> positions,
143  double now, Collection<? extends MobsimVehicle> vehs, double curvedLength, double storageCapacity,
144  Coord upstreamCoord, Coord downstreamCoord, double inverseFlowCapPerTS, double freeSpeed,
145  int numberOfLanesAsInt, Queue<Hole> holes, AbstractQLink.QLinkInternalInterface qLinkInternalInterface )
146  {
147  double spacingOfOnePCE = this.calculateVehicleSpacing( curvedLength, storageCapacity, vehs );
148  // ("vehs" is needed since the link may be more than full because of squeezing. In this case, spacingOfOnePCE is smaller than one "cell".)
149 
150  double ttimeOfHoles = curvedLength / (QueueWithBuffer.HOLE_SPEED_KM_H*1000./3600.);
151 
152  TreeMap<Double,Hole> consumableHoles = new TreeMap<>() ;
153 
154  // holes or kinematicWaves, if applicable:
155 
156  switch (scenario.getConfig().qsim().getSnapshotStyle()) {
157  case equiDist:
158  case queue:
159  break;
160  case withHoles:
161  case withHolesAndShowHoles:
162  case kinematicWaves:
163  if ( !holes.isEmpty() ) {
164  double firstHolePosition = Double.NaN ;
165  double distanceOfHoleFromFromNode = Double.NaN ;
166  double sum = 0 ;
167  for (Hole hole : holes) {
168  sum += hole.getSizeInEquivalents();
169  distanceOfHoleFromFromNode = computeHolePositionAndReturnDistance(ttimeOfHoles, hole, now, curvedLength);
170  if (Double.isNaN(firstHolePosition)) {
171  firstHolePosition = distanceOfHoleFromFromNode;
172  sum = 0; // don't include first vehicle
173  }
174 
175  if (Math.round(distanceOfHoleFromFromNode) != Math.round(curvedLength)) {
176  consumableHoles.put(distanceOfHoleFromFromNode, hole);
177  } // else {
178  // since hole is already created even if vehicle is in buffer, thus excluding such holes in vehicle position updating
179  // probably, don't create hole in visualizer also. amit May 2016
180  //}
181 
183  addHolePosition(positions, distanceOfHoleFromFromNode, hole, curvedLength, upstreamCoord, downstreamCoord);
184  }
185  }
186  final double spaceConsumptionOfHoles = sum*spacingOfOnePCE;
187  final double spaceAvailableForHoles = distanceOfHoleFromFromNode - firstHolePosition;
188  if ( wrnCnt < 10 ) {
189  wrnCnt++ ;
190  if ( spaceConsumptionOfHoles > spaceAvailableForHoles ) {
191  log.warn("we have a problem: holes consume too much space:" ) ;
192  log.warn( "summed up space consumption of holes: " + spaceConsumptionOfHoles );
193  log.warn("distance bw first and last hole: " + spaceAvailableForHoles ) ;
194  }
195  if (wrnCnt == 10) {
196  log.warn(Gbl.FUTURE_SUPPRESSED ) ;
197  }
198  }
199  }
200  break;
201  default: throw new RuntimeException("The traffic dynmics "+scenario.getConfig().qsim().getSnapshotStyle()+" is not implemented yet.");
202  }
203 
204  // yyyyyy might be faster by sorting holes into a regular array list ...
205 
206  double distanceFromFromNode = Double.NaN;
207 
208  for ( MobsimVehicle mveh : vehs) {
209  final QVehicle veh = (QVehicle) mveh;
210 
211  final double remainingTravelTime = veh.getEarliestLinkExitTime() - now;
212  // (starts off relatively small (rightmost vehicle))
213 
214  final double vehicleSpacing = mveh.getSizeInEquivalents() * spacingOfOnePCE;
215 
216  double speed = min( freeSpeed, veh.getMaximumVelocity() );
217  if ( qLinkInternalInterface!=null ){
218  speed = qLinkInternalInterface.getMaximumVelocityFromLinkSpeedCalculator( veh, now );
219  }
220 
221  distanceFromFromNode = this.calculateOdometerDistanceFromFromNode(
222  now, curvedLength,
223  speed, // min( freeSpeed, veh.getMaximumVelocity()),
224  vehicleSpacing, distanceFromFromNode, remainingTravelTime
225  );
226  // yyyy if the LinkSpeedCalculator says something that is not free speed, we are out of luck here. kai, jan'23
227 
228  int lane = VisUtils.guessLane(veh, numberOfLanesAsInt);
229  double speedValue = VisUtils.calcSpeedValueBetweenZeroAndOne(veh, inverseFlowCapPerTS, now, freeSpeed);
230  Gbl.assertNotNull(upstreamCoord);
231  Gbl.assertNotNull(downstreamCoord);
232  this.positionAgentGivenDistanceFromFNode(positions, upstreamCoord, downstreamCoord, curvedLength, veh, distanceFromFromNode, lane, speedValue);
233 
234  switch (this.scenario.getConfig().qsim().getTrafficDynamics()) {
235  case queue:
236  break;
237  case withHoles:
238  case kinematicWaves:
239  while ( !consumableHoles.isEmpty() && distanceFromFromNode < consumableHoles.lastKey() ) {
240  Map.Entry<Double, Hole> entry = consumableHoles.pollLastEntry() ;
241  distanceFromFromNode -= spacingOfOnePCE * entry.getValue().getSizeInEquivalents() ;
242  }
243  break;
244  default: throw new RuntimeException("The traffic dynmics "+this.scenario.getConfig().qsim().getTrafficDynamics()+" is not implemented yet.");
245  }
246  }
247 
248  /* Can't explain the above in easy words. Essentially, when vehicles leave the link at max rate, there still must be some space between
249  * the holes that this generates. That space is added up until a full vehicle fits into it. There must be some better way of
250  * explaining this, but I don't know it right now. kai, apr'16
251  */
252  return positions;
253  }
254 
255 
256  abstract double calculateVehicleSpacing(double linkLength, double overallStorageCapacity, Collection<? extends VisVehicle> vehs);
257 
258  abstract double calculateOdometerDistanceFromFromNode(double time, double linkLength, double freespeed,
259  double spacing, double prevVehicleDistance, double remainingTravelTime);
260 
261  private int positionStack(final Collection<AgentSnapshotInfo> positions, final Collection<QVehicle> vehicles, final int startCount) {
262  var builder = newBuilder();
263 
264  var counter = startCount;
265  for (var vehicle : vehicles) {
266  var link = vehicle.getCurrentLink();
267  for (var passenger : VisUtils.getPeopleInVehicle(vehicle)) {
268  var position = builder
269  .setPersonId(passenger.getId())
270  .setVehicleId(vehicle.getId())
271  .setLinkId(link.getId())
272  .setFromCoord(link.getFromNode().getCoord())
273  .setToCoord(link.getToNode().getCoord())
274  .setDistanceOnLink(link.getLength() * 0.9)
275  .setLane(counter)
276  .setLinkLength(link.getLength())
277  .setAgentState(getAgentState(passenger))
278  .build();
279  positions.add(position);
280  counter++;
281  }
282  }
283  return counter;
284  }
285 
286  private void positionPassengers(Collection<AgentSnapshotInfo> positions,
287  Collection<? extends PassengerAgent> passengers, double distanceOnLink, Coord startCoord, Coord endCoord,
288  double lengthOfCurve, int lane, double speedValueBetweenZeroAndOne) {
289  var builder = newBuilder();
290 
291  int cnt = passengers.size();
292  int laneInt = 2 * (cnt + 1) + lane;
293 
294  for (PassengerAgent passenger : passengers) {
295  int lanePos = laneInt - 2 * cnt;
296  var passengerPosition = builder
297  .setPersonId(passenger.getId())
298  .setVehicleId(passenger.getVehicle().getId())
299  .setLinkId(passenger.getCurrentLinkId())
300  .setFromCoord(startCoord)
301  .setToCoord(endCoord)
302  .setDistanceOnLink(distanceOnLink)
303  .setLane(lanePos)
304  .setLinkLength(lengthOfCurve)
305  .setColorValue(speedValueBetweenZeroAndOne)
306  .setAgentState(getAgentState(passenger))
307  .build();
308 
309  positions.add(passengerPosition);
310  cnt--;
311  }
312  }
313 
314  private void addHolePosition(final Collection<AgentSnapshotInfo> positions, double distanceFromFromNode, Hole veh,
315  double curvedLength, Coord upstreamCoord, Coord downstreamCoord) {
316  int lane = 20;
317  double speedValue = 1.;
318  var builder = newBuilder();
319 
320  var position = builder
321  .setPersonId(Id.createPersonId("hole"))
322  .setVehicleId(Id.createVehicleId(veh.getId()))
323  .setLinkId(null)
324  .setFromCoord(upstreamCoord)
325  .setToCoord(downstreamCoord)
326  .setDistanceOnLink(distanceFromFromNode)
327  .setLane(lane)
328  .setLinkLength(curvedLength)
329  .setColorValue(speedValue)
330  .setAgentState(AgentState.PERSON_OTHER_MODE)
331  .build();
332  positions.add(position);
333  }
334 
335  private AgentState getAgentState(Identifiable<Person> identifiable) {
336 
337  // I don't know whether I have gotten this right, but I think this is tested in every case in every method
338  var marker = getMarkerFromAttributes(identifiable.getId());
339  if (marker != null) return AgentState.MARKER;
340 
341  // these are the regular agent states
342  if (identifiable instanceof TransitDriverAgent) return AgentState.TRANSIT_DRIVER;
343  if (identifiable instanceof MobsimDriverAgent && ((MobsimDriverAgent) identifiable).getMode().equals(TransportMode.car))
345 
346  // old tests keep them here, since I'm unsure whether the other ones work
347  //if (identifiable.getId().toString().startsWith("pt")) return AgentState.TRANSIT_DRIVER;
348  //if (isFirst) return AgentState.PERSON_DRIVING_CAR;
349 
350  // we don't know. Set other mode
352  }
353 
354  private Object getMarkerFromAttributes(Id<Person> id) {
355  var person = scenario.getPopulation().getPersons().get(id);
356  return person != null ? person.getAttributes().getAttribute(AgentSnapshotInfo.marker) : null;
357  }
358 
359  private AgentState getAgentStateForActivity(Id<Person> id) {
360 
361  // I don't know whether I have gotten this right, but I think this is tested in every case in every method
362  var marker = getMarkerFromAttributes(id);
363  if (marker != null) return AgentState.MARKER;
364 
366  }
367 }
static Id< Vehicle > createVehicleId(final long key)
Definition: Id.java:220
static final String FUTURE_SUPPRESSED
Definition: Gbl.java:44
Collection<? extends PassengerAgent > getPassengers()
Map< Id< Person >,? extends Person > getPersons()
QSimConfigGroup qsim()
Definition: Config.java:447
static void assertNotNull(Object obj)
Definition: Gbl.java:212
static Id< Person > createPersonId(final long key)
Definition: Id.java:193