View Javadoc
1   package org.opentrafficsim.road.gtu.generator;
2   
3   import java.util.ArrayList;
4   import java.util.LinkedHashMap;
5   import java.util.List;
6   import java.util.Map;
7   
8   import nl.tudelft.simulation.dsol.SimRuntimeException;
9   
10  import org.djunits.unit.LengthUnit;
11  import org.djunits.unit.SpeedUnit;
12  import org.djunits.unit.TimeUnit;
13  import org.opentrafficsim.core.OTS_DIST;
14  import org.opentrafficsim.core.OTS_SCALAR;
15  import org.opentrafficsim.core.dsol.OTSDEVSSimulatorInterface;
16  import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
17  import org.opentrafficsim.core.gtu.GTUException;
18  import org.opentrafficsim.core.gtu.GTUType;
19  import org.opentrafficsim.core.gtu.RelativePosition;
20  import org.opentrafficsim.core.gtu.animation.GTUColorer;
21  import org.opentrafficsim.core.network.NetworkException;
22  import org.opentrafficsim.core.network.route.RouteGenerator;
23  import org.opentrafficsim.road.car.LaneBasedIndividualCar;
24  import org.opentrafficsim.road.car.LaneBasedIndividualCar.LaneBasedIndividualCarBuilder;
25  import org.opentrafficsim.road.gtu.animation.DefaultCarAnimation;
26  import org.opentrafficsim.road.gtu.following.GTUFollowingModel;
27  import org.opentrafficsim.road.gtu.following.HeadwayGTU;
28  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
29  import org.opentrafficsim.road.gtu.lane.changing.LaneChangeModel;
30  import org.opentrafficsim.road.network.lane.Lane;
31  import org.opentrafficsim.road.network.route.LaneBasedRouteGenerator;
32  import org.opentrafficsim.road.network.route.LaneBasedRouteNavigator;
33  
34  /**
35   * Common code for LaneBasedGTU generators that may have to postpone putting a GTU on the road due to congestion growing into
36   * the generator. <br>
37   * Generally, these generators will discover that there is not enough room AFTER having decided what kind (particular length) of
38   * GTU will be constructed next. When this happens, the generator must remember the properties of the GTU, but postpone actual
39   * generation until there is enough room.
40   * <p>
41   * Copyright (c) 2013-2015 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
42   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
43   * <p>
44   * @version $Revision: 1401 $, $LastChangedDate: 2015-09-14 01:33:02 +0200 (Mon, 14 Sep 2015) $, by $Author: averbraeck $,
45   *          initial version Feb 2, 2015 <br>
46   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
47   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
48   */
49  public abstract class AbstractGTUGenerator implements OTS_SCALAR, OTS_DIST
50  {
51      /** The generator name. Will be used for generated GTUs as Name:# where # is the id of the GTU when ID is a String. */
52      private final String name;
53  
54      /** The type of GTU to generate. */
55      private final GTUType gtuType;
56  
57      /** The GTU class to instantiate. */
58      private final Class<?> gtuClass;
59  
60      /** The GTU following model to use. */
61      private final GTUFollowingModel gtuFollowingModel;
62  
63      /** The lane change model to use. */
64      private final LaneChangeModel laneChangeModel;
65  
66      /** Distribution of the initial speed of the GTU. */
67      private final ContinuousDistScalar.Abs<Speed.Abs, SpeedUnit> initialSpeedDist;
68  
69      /** Distribution of the interarrival time. */
70      private final ContinuousDistScalar.Rel<Time.Rel, TimeUnit> interarrivelTimeDist;
71  
72      /** Generated number of GTUs. */
73      private long generatedGTUs = 0;
74  
75      /** Maximum number of GTUs to generate. */
76      private final long maxGTUs;
77  
78      /** Start time of generation (delayed start). */
79      private final Time.Abs startTime;
80  
81      /** End time of generation. */
82      private final Time.Abs endTime;
83  
84      /** Lane to generate the GTU on -- at the end for now. */
85      private final Lane lane;
86  
87      /** position on the lane, relative to the design line of the link. */
88      private final Length.Rel position;
89  
90      /** Route generator used to create a route for each generated GTU. */
91      private LaneBasedRouteGenerator routeGenerator;
92  
93      /** GTUColorer to use. */
94      private final GTUColorer gtuColorer;
95  
96      /** Car builder list. */
97      private List<LaneBasedIndividualCarBuilder> carBuilderList = new ArrayList<>();
98  
99      /** Number of generated GTUs. */
100     @SuppressWarnings("checkstyle:visibilitymodifier")
101     protected long numberGTUs = 0;
102 
103     /**
104      * @param name the name of the generator
105      * @param simulator the simulator to schedule the start of the generation
106      * @param gtuType the type of GTU to generate
107      * @param gtuClass the GTU class to instantiate
108      * @param gtuFollowingModel the GTU following model to use
109      * @param laneChangeModel the lane change model to use
110      * @param initialSpeedDist distribution of the initial speed of the GTU
111      * @param interarrivelTimeDist distribution of the interarrival time
112      * @param maxGTUs maximum number of GTUs to generate
113      * @param startTime start time of generation (delayed start)
114      * @param endTime end time of generation
115      * @param lane the lane to generate the GTU on
116      * @param position position on the lane, relative to the design line of the link
117      * @param routeGenerator RouteGenerator; the route generator that will create a route for each generated GTU
118      * @param gtuColorer the GTUColorer to use
119      * @throws SimRuntimeException when simulation scheduling fails
120      */
121     @SuppressWarnings("checkstyle:parameternumber")
122     public AbstractGTUGenerator(final String name, final OTSDEVSSimulatorInterface simulator, final GTUType gtuType,
123         final Class<?> gtuClass, final GTUFollowingModel gtuFollowingModel, final LaneChangeModel laneChangeModel,
124         final ContinuousDistScalar.Abs<Speed.Abs, SpeedUnit> initialSpeedDist,
125         final ContinuousDistScalar.Rel<Time.Rel, TimeUnit> interarrivelTimeDist, final long maxGTUs,
126         final Time.Abs startTime, final Time.Abs endTime, final Lane lane, final Length.Rel position,
127         final LaneBasedRouteGenerator routeGenerator, final GTUColorer gtuColorer) throws SimRuntimeException
128     {
129         super();
130         this.name = name;
131         this.gtuType = gtuType;
132         this.gtuClass = gtuClass;
133         this.gtuFollowingModel = gtuFollowingModel;
134         this.laneChangeModel = laneChangeModel;
135         this.initialSpeedDist = initialSpeedDist;
136         this.interarrivelTimeDist = interarrivelTimeDist;
137         this.maxGTUs = maxGTUs;
138         this.startTime = startTime;
139         this.endTime = endTime;
140         this.lane = lane;
141         this.position = position;
142         this.routeGenerator = routeGenerator;
143         this.gtuColorer = gtuColorer;
144 
145         simulator.scheduleEventAbs(startTime, this, this, "generate", null);
146     }
147 
148     /**
149      * Generate a GTU.
150      * @throws Exception when something in the generation fails.
151      */
152     protected final void generate() throws Exception
153     {
154         // check if we are after the end time
155         if (getSimulator().getSimulatorTime().getTime().gt(this.endTime))
156         {
157             return;
158         }
159 
160         // check if we have generated sufficient GTUs
161         if (this.generatedGTUs >= this.maxGTUs)
162         {
163             return;
164         }
165 
166         // create a unique id
167         this.numberGTUs++;
168         String id = this.name + ":" + this.numberGTUs;
169 
170         // create the GTU
171         if (LaneBasedIndividualCar.class.isAssignableFrom(getGtuClass()))
172         {
173             LaneBasedIndividualCarBuilder carBuilder = new LaneBasedIndividualCarBuilder();
174             carBuilder.setId(id);
175             carBuilder.setGtuType(getGtuType());
176             carBuilder.setGTUFollowingModel(this.gtuFollowingModel);
177             carBuilder.setLaneChangeModel(this.laneChangeModel);
178             Length.Rel carLength = getLengthDist().draw();
179             carBuilder.setLength(carLength);
180             carBuilder.setWidth(getWidthDist().draw());
181             carBuilder.setMaximumVelocity(getMaximumSpeedDist().draw());
182             carBuilder.setInitialSpeed(getInitialSpeedDist().draw());
183             carBuilder.setSimulator(getSimulator());
184             Map<Lane, Length.Rel> initialLongitudinalPositions = new LinkedHashMap<>(1);
185             initialLongitudinalPositions.put(this.lane, this.position);
186             carBuilder.setInitialLongitudinalPositions(initialLongitudinalPositions);
187             carBuilder.setRouteGenerator(this.routeGenerator);
188             carBuilder.setAnimationClass(DefaultCarAnimation.class);
189             carBuilder.setGtuColorer(this.gtuColorer);
190             this.generatedGTUs++;
191 
192             if (enoughSpace(carBuilder))
193             {
194                 carBuilder.build();
195             }
196             else
197             {
198                 // put the car in the queue and take it from there -- if the headway is enough, build the car.
199                 this.carBuilderList.add(carBuilder);
200                 // System.out.println("GTUGenerator - backlog = " + this.carBuilderList.size());
201                 if (this.carBuilderList.size() == 1)
202                 {
203                     // first entry in list - start the watch thread
204                     getSimulator().scheduleEventRel(new Time.Rel(0.1, SECOND), this, this, "checkCarBuilderList", null);
205                 }
206             }
207         }
208         else
209         {
210             throw new GTUException("GTU class " + getGtuClass().getName() + ": cannot instantiate, no builder.");
211         }
212 
213         // reschedule next arrival
214         OTSSimTimeDouble nextTime = getSimulator().getSimulatorTime().plus(this.interarrivelTimeDist.draw());
215         if (nextTime.get().le(this.endTime))
216         {
217             getSimulator().scheduleEventAbs(nextTime, this, this, "generate", null);
218         }
219     }
220 
221     /**
222      * Check if the car to be built is not overlapping with another GTU on the same lane, and if it has enough headway to be
223      * generated safely.
224      * @param carBuilder the car to be generated
225      * @return true if car can be safely built, false otherwise.
226      * @throws NetworkException if GTU does not have a position on the lane where it is registered
227      */
228     protected final boolean enoughSpace(final LaneBasedIndividualCarBuilder carBuilder) throws NetworkException
229     {
230         Lane generatorLane = carBuilder.getInitialLongitudinalPositions().keySet().iterator().next();
231         double genPosSI = carBuilder.getInitialLongitudinalPositions().get(generatorLane).getSI();
232         double lengthSI = generatorLane.getLength().getSI();
233         double frontNew = (genPosSI + carBuilder.getLength().getSI()) / lengthSI;
234         double rearNew = genPosSI / lengthSI;
235 
236         // test for overlap with other GTUs
237         for (LaneBasedGTU gtu : generatorLane.getGtuList())
238         {
239             double frontGTU = gtu.fractionalPosition(generatorLane, gtu.getFront());
240             double rearGTU = gtu.fractionalPosition(generatorLane, gtu.getRear());
241             if ((frontNew >= rearGTU && frontNew <= frontGTU) || (rearNew >= rearGTU && rearNew <= frontGTU)
242                 || (frontGTU >= rearNew && frontGTU <= frontNew) || (rearGTU >= rearNew && rearGTU <= frontNew))
243             {
244                 // System.out.println(getSimulator().getSimulatorTime() + ", generator overlap with GTU " + gtu);
245                 return false;
246             }
247         }
248 
249         // test for sufficient headway
250         GTUFollowingModel followingModel = carBuilder.getGtuFollowingModel();
251 
252         HeadwayGTU headwayGTU =
253             headway(new Length.Rel(250.0, METER), carBuilder.getRouteGenerator().generateRouteNavigator(),
254                 generatorLane);
255         Length.Rel minimumHeadway = new Length.Rel(0.0, METER);
256         if (headwayGTU.getOtherGTU() != null)
257         {
258             minimumHeadway =
259                 followingModel.minimumHeadway(carBuilder.getInitialSpeed(), headwayGTU.getOtherGTU()
260                     .getLongitudinalVelocity(), new Length.Rel(1.0, CENTIMETER), generatorLane.getSpeedLimit(carBuilder
261                     .getGtuType()), carBuilder.getMaximumVelocity());
262             double acc =
263                 followingModel
264                     .computeAcceleration(carBuilder.getInitialSpeed(), carBuilder.getMaximumVelocity(),
265                         headwayGTU.getOtherGTU().getLongitudinalVelocity(), minimumHeadway,
266                         carBuilder.getMaximumVelocity()).getSI();
267             if (acc < 0)
268             {
269                 System.err.println(getSimulator().getSimulatorTime() + ", generator headway for GTU "
270                     + headwayGTU.getOtherGTU() + ", distance " + headwayGTU.getDistanceSI() + " m, max "
271                     + minimumHeadway + ", has to brake with a=" + acc + " m/s^2");
272                 return false;
273             }
274         }
275 
276         // System.out.println(getSimulator().getSimulatorTime() + ", generator headway for GTU " + headwayGTU.getOtherGTU()
277         // + ", distance " + headwayGTU.getDistanceSI() + " m, max " + minimumHeadway);
278         return headwayGTU.getDistance().ge(minimumHeadway);
279     }
280 
281     /**
282      * Calculate the minimum headway, possibly on subsequent lanes, in forward direction.
283      * @param theLane the lane where we are looking right now
284      * @param lanePositionSI from which position on this lane do we start measuring? This is the current position of the GTU
285      *            when we measure in the lane where the original GTU is positioned, and 0.0 for each subsequent lane
286      * @param cumDistanceSI the distance we have already covered searching on previous lanes
287      * @param maxDistanceSI the maximum distance to look for in SI units; stays the same in subsequent calls
288      * @param when the current or future time for which to calculate the headway
289      * @param routeNavigator Route; the route that the GTU intends to follow
290      * @return the headway in SI units when we have found the GTU, or a null GTU with a distance of Double.MAX_VALUE meters when
291      *         no other GTU could not be found within maxDistanceSI meters
292      * @throws NetworkException when there is a problem with the geometry of the network
293      */
294     private HeadwayGTU headwayRecursiveForwardSI(final Lane theLane, final double lanePositionSI,
295         final double cumDistanceSI, final double maxDistanceSI, final Time.Abs when,
296         final LaneBasedRouteNavigator routeNavigator) throws NetworkException
297     {
298         LaneBasedGTU otherGTU = theLane.getGtuAfter(new Length.Rel(lanePositionSI, METER), RelativePosition.REAR, when);
299         if (otherGTU != null)
300         {
301             double distanceM =
302                 cumDistanceSI + otherGTU.position(theLane, otherGTU.getRear(), when).getSI() - lanePositionSI;
303             if (distanceM > 0 && distanceM <= maxDistanceSI)
304             {
305                 return new HeadwayGTU(otherGTU, distanceM);
306             }
307             return new HeadwayGTU(null, Double.MAX_VALUE);
308         }
309 
310         // Continue search on successor lanes.
311         if (cumDistanceSI + theLane.getLength().getSI() - lanePositionSI < maxDistanceSI)
312         {
313             // is there a successor link?
314             if (theLane.nextLanes(this.gtuType).size() > 0)
315             {
316                 HeadwayGTU foundMaxGTUDistanceSI = new HeadwayGTU(null, Double.MAX_VALUE);
317                 for (Lane nextLane : theLane.nextLanes(this.gtuType))
318                 {
319                     // TODO Only follow links on the Route if there is a "real" Route
320                     // if (routeNavigator.getRoute() == null || routeNavigator.getRoute().size() == 0 /* XXXXX STUB dummy route
321                     // */
322                     // || routeNavigator.getRoute().containsLink((Link) theLane.getParentLink()))
323                     {
324                         double traveledDistanceSI = cumDistanceSI + theLane.getLength().getSI() - lanePositionSI;
325                         HeadwayGTU closest =
326                             headwayRecursiveForwardSI(nextLane, 0.0, traveledDistanceSI, maxDistanceSI, when,
327                                 routeNavigator);
328                         if (closest.getDistanceSI() < maxDistanceSI
329                             && closest.getDistanceSI() < foundMaxGTUDistanceSI.getDistanceSI())
330                         {
331                             foundMaxGTUDistanceSI = closest;
332                         }
333                     }
334                 }
335                 return foundMaxGTUDistanceSI;
336             }
337         }
338 
339         // No other GTU was not on one of the current lanes or their successors.
340         return new HeadwayGTU(null, Double.MAX_VALUE);
341     }
342 
343     /**
344      * Find the first GTU starting on the specified lane following the specified route.
345      * @param maxDistanceSI the maximum distance to look for in SI units
346      * @param routeNavigator Route; the route that the GTU intends to follow
347      * @param generatorLane Lane; the lane on which the the search for a leader starts
348      * @return the nearest GTU and the net headway to this GTU in SI units when we have found the GTU, or a null GTU with a
349      *         distance of Double.MAX_VALUE meters when no other GTU could not be found within maxDistanceSI meters
350      * @throws NetworkException when there is a problem with the geometry of the network
351      */
352     private HeadwayGTU headwayGTUSIForward(final double maxDistanceSI, final LaneBasedRouteNavigator routeNavigator,
353         final Lane generatorLane) throws NetworkException
354     {
355         Time.Abs when = getSimulator().getSimulatorTime().getTime();
356         HeadwayGTU foundMaxGTUDistanceSI = new HeadwayGTU(null, Double.MAX_VALUE);
357         // search for the closest GTU on all current lanes we are registered on.
358         // look forward.
359         HeadwayGTU closest =
360             headwayRecursiveForwardSI(this.lane, generatorLane.getLength().getSI(), 0.0, maxDistanceSI, when,
361                 routeNavigator);
362         if (closest.getDistanceSI() < maxDistanceSI && closest.getDistanceSI() < foundMaxGTUDistanceSI.getDistanceSI())
363         {
364             foundMaxGTUDistanceSI = closest;
365         }
366         return foundMaxGTUDistanceSI;
367     }
368 
369     /**
370      * Check the available headway for GTU that is about to be constructed.
371      * @param maxDistance DoubleScalar.Rel&lt;LengthUnit&gt;; the maximum distance to look for a leader
372      * @param routeNavigator RouteNavigator; the route that this GTU intends to take
373      * @param generatorLane Lane; the lane on which the GTU is generated
374      * @return HeadwayGTU; the available headway and the GTU at that headway
375      * @throws NetworkException on network inconsistency
376      */
377     public final HeadwayGTU headway(final Length.Rel maxDistance, final LaneBasedRouteNavigator routeNavigator,
378         final Lane generatorLane) throws NetworkException
379     {
380         return headwayGTUSIForward(maxDistance.getSI(), routeNavigator, generatorLane);
381     }
382 
383     /**
384      * Check if car can be generated.
385      * @throws Exception on any problem
386      */
387     protected final void checkCarBuilderList() throws Exception
388     {
389         if (!this.carBuilderList.isEmpty())
390         {
391             LaneBasedIndividualCarBuilder carBuilder = this.carBuilderList.get(0);
392             if (enoughSpace(carBuilder))
393             {
394                 this.carBuilderList.remove(0);
395                 carBuilder.build();
396             }
397         }
398 
399         // only reschedule if list not empty
400         if (!this.carBuilderList.isEmpty())
401         {
402             getSimulator().scheduleEventRel(new Time.Rel(0.1, SECOND), this, this, "checkCarBuilderList", null);
403         }
404     }
405 
406     /** @return simulator. */
407     public abstract OTSDEVSSimulatorInterface getSimulator();
408 
409     /** @return lengthDist. */
410     public abstract ContinuousDistScalar.Rel<Length.Rel, LengthUnit> getLengthDist();
411 
412     /** @return widthDist. */
413     public abstract ContinuousDistScalar.Rel<Length.Rel, LengthUnit> getWidthDist();
414 
415     /** @return maximumSpeedDist. */
416     public abstract ContinuousDistScalar.Abs<Speed.Abs, SpeedUnit> getMaximumSpeedDist();
417 
418     /**
419      * @return name.
420      */
421     public final String getName()
422     {
423         return this.name;
424     }
425 
426     /**
427      * @return gtuType.
428      */
429     public final GTUType getGtuType()
430     {
431         return this.gtuType;
432     }
433 
434     /**
435      * @return gtuClass.
436      */
437     public final Class<?> getGtuClass()
438     {
439         return this.gtuClass;
440     }
441 
442     /**
443      * @return gtuFollowingModel.
444      */
445     public final GTUFollowingModel getGtuFollowingModel()
446     {
447         return this.gtuFollowingModel;
448     }
449 
450     /**
451      * @return initialSpeedDist.
452      */
453     public final ContinuousDistScalar.Abs<Speed.Abs, SpeedUnit> getInitialSpeedDist()
454     {
455         return this.initialSpeedDist;
456     }
457 
458     /**
459      * @return interarrivelTimeDist.
460      */
461     public final ContinuousDistScalar.Rel<Time.Rel, TimeUnit> getInterarrivelTimeDist()
462     {
463         return this.interarrivelTimeDist;
464     }
465 
466     /**
467      * @return maxGTUs.
468      */
469     public final long getMaxGTUs()
470     {
471         return this.maxGTUs;
472     }
473 
474     /**
475      * @return startTime.
476      */
477     public final Time.Abs getStartTime()
478     {
479         return this.startTime;
480     }
481 
482     /**
483      * @return endTime.
484      */
485     public final Time.Abs getEndTime()
486     {
487         return this.endTime;
488     }
489 
490     /**
491      * @return routeGenerator.
492      */
493     public final RouteGenerator getRouteGenerator()
494     {
495         return this.routeGenerator;
496     }
497 
498     /**
499      * @param routeGenerator set routeGenerator.
500      */
501     public final void setRouteGenerator(final LaneBasedRouteGenerator routeGenerator)
502     {
503         this.routeGenerator = routeGenerator;
504     }
505 
506     /**
507      * @return laneChangeModel.
508      */
509     public final LaneChangeModel getLaneChangeModel()
510     {
511         return this.laneChangeModel;
512     }
513 
514     /**
515      * @return gtuColorer.
516      */
517     public final GTUColorer getGtuColorer()
518     {
519         return this.gtuColorer;
520     }
521 
522 }