View Javadoc
1   package org.opentrafficsim.road.gtu.lane.tactical.util;
2   
3   import static org.opentrafficsim.core.gtu.behavioralcharacteristics.AbstractParameterType.Check.ATLEASTONE;
4   import static org.opentrafficsim.core.gtu.behavioralcharacteristics.AbstractParameterType.Check.POSITIVE;
5   
6   import java.io.Serializable;
7   import java.util.ArrayList;
8   import java.util.HashMap;
9   import java.util.HashSet;
10  import java.util.Iterator;
11  import java.util.List;
12  import java.util.Map;
13  import java.util.Set;
14  import java.util.SortedMap;
15  import java.util.SortedSet;
16  import java.util.TreeMap;
17  import java.util.UUID;
18  
19  import org.djunits.unit.AccelerationUnit;
20  import org.djunits.unit.LengthUnit;
21  import org.djunits.unit.TimeUnit;
22  import org.djunits.value.vdouble.scalar.Acceleration;
23  import org.djunits.value.vdouble.scalar.Duration;
24  import org.djunits.value.vdouble.scalar.Length;
25  import org.djunits.value.vdouble.scalar.Speed;
26  import org.djunits.value.vdouble.scalar.Time;
27  import org.opentrafficsim.core.gtu.GTUException;
28  import org.opentrafficsim.core.gtu.TurnIndicatorIntent;
29  import org.opentrafficsim.core.gtu.behavioralcharacteristics.BehavioralCharacteristics;
30  import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterException;
31  import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypeDouble;
32  import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypeDuration;
33  import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypeLength;
34  import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypes;
35  import org.opentrafficsim.core.network.Node;
36  import org.opentrafficsim.core.network.route.Route;
37  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
38  import org.opentrafficsim.road.gtu.lane.RoadGTUTypes;
39  import org.opentrafficsim.road.gtu.lane.perception.headway.AbstractHeadwayGTU;
40  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayConflict;
41  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTU;
42  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTUSimple;
43  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayStopLine;
44  import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
45  import org.opentrafficsim.road.gtu.lane.tactical.pt.BusSchedule;
46  import org.opentrafficsim.road.network.lane.CrossSectionLink;
47  import org.opentrafficsim.road.network.lane.conflict.BusStopConflictRule;
48  import org.opentrafficsim.road.network.lane.conflict.ConflictRule;
49  import org.opentrafficsim.road.network.lane.conflict.ConflictType;
50  import org.opentrafficsim.road.network.speed.SpeedLimitInfo;
51  
52  import nl.tudelft.simulation.language.Throw;
53  
54  /**
55   * This class implements default behavior for intersection conflicts for use in tactical planners.
56   * <p>
57   * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
58   * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
59   * <p>
60   * @version $Revision$, $LastChangedDate$, by $Author$, initial version Jun 3, 2016 <br>
61   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
62   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
63   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
64   */
65  // TODO do not ignore vehicles upstream of conflict if they have green
66  // TODO conflict over multiple lanes (longitudinal in own direction)
67  // TODO a) yielding while having priority happens only when leaders is standing still on conflict (then its useless...)
68  // b) two vehicles can remain upstream of merge if vehicle stands on merge but leaves some space to move
69  // probably 1 is yielding, and 1 is courtesy yielding as the other stands still
70  // c) they might start moving together and collide further down (no response to negative headway on merge)
71  public final class ConflictUtil
72  {
73  
74      /** Minimum time gap between events. */
75      public static final ParameterTypeDuration MIN_GAP = new ParameterTypeDuration("minGap", "Minimum gap for conflicts.",
76              new Duration(0.000001, TimeUnit.SECOND), POSITIVE);
77  
78      /** Stopping distance at conflicts. */
79      public static final ParameterTypeLength S0_CONF =
80              new ParameterTypeLength("s0 conf", "Stopping distance at conflicts.", new Length(1.5, LengthUnit.METER), POSITIVE);
81  
82      /** Multiplication factor on time for conservative assessment. */
83      public static final ParameterTypeDouble TIME_FACTOR =
84              new ParameterTypeDouble("timeFactor", "Safety factor on estimated time.", 1.25, ATLEASTONE);
85  
86      /** Area before stop line where one is considered arrived at the intersection. */
87      public static final ParameterTypeLength STOP_AREA =
88              new ParameterTypeLength("stopArea", "Area before stop line where one is considered arrived at the intersection.",
89                      new Length(4, LengthUnit.METER), POSITIVE);
90  
91      /** Parameter of how much time before departure a bus indicates its departure to get priority. */
92      public static final ParameterTypeDuration TI =
93              new ParameterTypeDuration("ti", "Indicator time before departure.", Duration.createSI(3.0), POSITIVE);
94  
95      /** Time step for free acceleration anticipation. */
96      private static final Duration TIME_STEP = new Duration(0.5, TimeUnit.SI);
97  
98      /**
99       * Do not instantiate.
100      */
101     private ConflictUtil()
102     {
103         //
104     }
105 
106     /**
107      * Approach conflicts by applying appropriate acceleration (or deceleration). The model may yield for a vehicle even while
108      * having priority. Such a 'yield plan' is remembered in <tt>YieldPlans</tt>. By forwarding the same <tt>YieldPlans</tt> for
109      * a GTU consistency of such plans is provided. If any conflict is not accepted to pass, stopping before a more upstream
110      * conflict is applied if there not sufficient stopping length in between conflicts.
111      * @param behavioralCharacteristics behavioral characteristics
112      * @param conflicts set of conflicts to approach
113      * @param leaders leading vehicles
114      * @param carFollowingModel car-following model
115      * @param vehicleLength length of vehicle
116      * @param speed current speed
117      * @param acceleration current acceleration
118      * @param speedLimitInfo speed limit info
119      * @param conflictPlans set of plans for conflict
120      * @param gtu gtu
121      * @return acceleration appropriate for approaching the conflicts
122      * @throws GTUException in case of an unsupported conflict rule
123      * @throws ParameterException if a parameter is not defined or out of bounds
124      */
125     @SuppressWarnings("checkstyle:parameternumber")
126     public static Acceleration approachConflicts(final BehavioralCharacteristics behavioralCharacteristics,
127             final SortedSet<HeadwayConflict> conflicts, final SortedSet<HeadwayGTU> leaders,
128             final CarFollowingModel carFollowingModel, final Length vehicleLength, final Speed speed,
129             final Acceleration acceleration, final SpeedLimitInfo speedLimitInfo, final ConflictPlans conflictPlans,
130             final LaneBasedGTU gtu) throws GTUException, ParameterException
131     {
132 
133         conflictPlans.cleanPlans();
134 
135         Acceleration a = Acceleration.POS_MAXVALUE;
136         if (conflicts.isEmpty())
137         {
138             return a;
139         }
140 
141         List<Length> prevStarts = new ArrayList<>();
142         List<Length> prevEnds = new ArrayList<>();
143         List<Class<? extends ConflictRule>> conflictRuleTypes = new ArrayList<>();
144 
145         for (HeadwayConflict conflict : conflicts)
146         {
147 
148             // adjust acceleration for situations where stopping might not be required
149             if (conflict.isCrossing())
150             {
151                 // avoid collision if crossing is occupied
152                 a = Acceleration.min(a,
153                         avoidCrossingCollision(behavioralCharacteristics, conflict, carFollowingModel, speed, speedLimitInfo));
154             }
155             else
156             {
157                 // follow leading GTUs on merge or split
158                 a = Acceleration.min(a, followConflictingLeaderOnMergeOrSplit(conflict, behavioralCharacteristics,
159                         carFollowingModel, speed, speedLimitInfo));
160             }
161             if (conflict.getDistance().lt0())
162             {
163                 // ignore conflicts we are on (i.e. negative distance to start of conflict)
164                 continue;
165             }
166 
167             // indicator if bus
168             if (gtu.getStrategicalPlanner().getRoute() instanceof BusSchedule && gtu.getGTUType().isOfType(RoadGTUTypes.BUS)
169                     && conflict.getConflictRuleType().equals(BusStopConflictRule.class))
170             {
171                 BusSchedule busSchedule = (BusSchedule) gtu.getStrategicalPlanner().getRoute();
172                 Time actualDeparture = busSchedule.getActualDepartureConflict(conflict.getId());
173                 if (actualDeparture != null && actualDeparture.si < gtu.getSimulator().getSimulatorTime().getTime().si
174                         + behavioralCharacteristics.getParameter(TI).si)
175                 {
176                     // TODO depending on left/right-hand traffic
177                     conflictPlans.setIndicatorIntent(TurnIndicatorIntent.LEFT, conflict.getDistance());
178                 }
179             }
180 
181             // determine if we need to stop
182             boolean stop;
183             switch (conflict.getConflictPriority())
184             {
185                 case PRIORITY:
186                 {
187                     stop = stopForPriorityConflict(conflict, leaders, speed, vehicleLength, behavioralCharacteristics,
188                             conflictPlans);
189                     break;
190                 }
191                 case GIVE_WAY:
192                 {
193                     stop = stopForGiveWayConflict(conflict, leaders, speed, acceleration, vehicleLength,
194                             behavioralCharacteristics, speedLimitInfo, carFollowingModel);
195                     break;
196                 }
197                 case STOP:
198                 {
199                     stop = stopForStopConflict(conflict, leaders, speed, acceleration, vehicleLength, behavioralCharacteristics,
200                             speedLimitInfo, carFollowingModel);
201                     break;
202                 }
203                 case ALL_STOP:
204                 {
205                     stop = stopForAllStopConflict(conflict, conflictPlans);
206                     break;
207                 }
208                 case SPLIT:
209                 {
210                     stop = false; // skipped anyway
211                     break;
212                 }
213                 default:
214                 {
215                     throw new GTUException("Unsupported conflict rule encountered while approaching conflicts.");
216                 }
217             }
218 
219             // stop if required, account for upstream conflicts to keep clear
220             if (!conflict.getConflictType().equals(ConflictType.SPLIT))
221             {
222 
223                 if (stop)
224                 {
225                     prevStarts.add(conflict.getDistance());
226                     conflictRuleTypes.add(conflict.getConflictRuleType());
227                     // stop for first conflict looking upstream of this blocked conflict that allows sufficient space
228                     int j = 0; // most upstream conflict if not in between conflicts
229                     for (int i = prevEnds.size() - 1; i >= 0; i--) // downstream to upstream
230                     {
231                         // note, at this point prevStarts contains one more conflict than prevEnds
232                         if (prevStarts.get(i + 1).minus(prevEnds.get(i))
233                                 .gt(passableDistance(vehicleLength, behavioralCharacteristics)))
234                         {
235                             j = i + 1;
236                             break;
237                         }
238                     }
239 
240                     // TODO
241                     // if this lowers our acceleration, we need to check if we are able to pass upstream conflicts still in time
242 
243                     // stop for j'th conflict, if deceleration is too strong, for next one
244                     behavioralCharacteristics.setParameter(ParameterTypes.S0, behavioralCharacteristics.getParameter(S0_CONF));
245                     Acceleration aCF = new Acceleration(-Double.MAX_VALUE, AccelerationUnit.SI);
246                     while (aCF.si < -6.0 && j < prevStarts.size())
247                     {
248                         if (prevStarts.get(j).lt(behavioralCharacteristics.getParameter(S0_CONF)))
249                         {
250                             // TODO what to do when we happen to be in the stopping distance? Stopping might be reasonable,
251                             // while car-following might give strong deceleration due to s < s0.
252                             aCF = Acceleration.max(aCF, new Acceleration(-6.0, AccelerationUnit.SI));
253                         }
254                         else
255                         {
256                             Acceleration aStop = CarFollowingUtil.stop(carFollowingModel, behavioralCharacteristics, speed,
257                                     speedLimitInfo, prevStarts.get(j));
258                             if (conflictRuleTypes.get(j).equals(BusStopConflictRule.class)
259                                     && aStop.lt(behavioralCharacteristics.getParameter(ParameterTypes.BCRIT).neg()))
260                             {
261                                 aStop = Acceleration.POS_MAXVALUE;
262                             }
263                             aCF = Acceleration.max(aCF, aStop);
264                         }
265                         j++;
266                     }
267                     behavioralCharacteristics.resetParameter(ParameterTypes.S0);
268                     a = Acceleration.min(a, aCF);
269                     break;
270                 }
271 
272                 // keep conflict clear (when stopping for another conflict), if there are conflicting vehicles
273                 if (!conflict.getUpstreamConflictingGTUs().isEmpty())
274                 {
275                     prevStarts.add(conflict.getDistance());
276                     conflictRuleTypes.add(conflict.getConflictRuleType());
277                     prevEnds.add(conflict.getDistance().plus(conflict.getLength()));
278                 }
279             }
280 
281         }
282 
283         if (a.si < -6.0)
284         {
285             System.err.println("Deceleration from conflict util stronger than 6m/s^2.");
286             // return IGNORE;
287         }
288         return a;
289     }
290 
291     /**
292      * Determines acceleration for following conflicting vehicles <i>on</i> a merge or split conflict.
293      * @param conflict merge or split conflict
294      * @param behavioralCharacteristics behavioral characteristics
295      * @param carFollowingModel car-following model
296      * @param speed current speed
297      * @param speedLimitInfo speed limit info
298      * @return acceleration for following conflicting vehicles <i>on</i> a merge or split conflict
299      * @throws ParameterException if a parameter is not given or out of bounds
300      */
301     private static Acceleration followConflictingLeaderOnMergeOrSplit(final HeadwayConflict conflict,
302             final BehavioralCharacteristics behavioralCharacteristics, final CarFollowingModel carFollowingModel,
303             final Speed speed, final SpeedLimitInfo speedLimitInfo) throws ParameterException
304     {
305         // ignore if no conflicting GTU's
306         if (conflict.getDownstreamConflictingGTUs().isEmpty())
307         {
308             return Acceleration.POS_MAXVALUE;
309         }
310         // get the most upstream GTU to consider
311         HeadwayGTU c = conflict.getDownstreamConflictingGTUs().first();
312         if (c.isAhead())
313         {
314             // conflict GTU completely downstream of conflict (i.e. regular car-following, ignore here)
315             return Acceleration.POS_MAXVALUE;
316         }
317         // conflict GTU (partially) on the conflict
318         // {@formatter:off}
319         // ______________________________________________ 
320         //   ___      virtual headway   |  ___  |
321         //  |___|(-----------------------)|___|(vehicle from south, on lane from south)
322         // _____________________________|_______|________
323         //                              /       / 
324         //                             /       /
325         // {@formatter:on}
326         Length virtualHeadway = conflict.getDistance().plus(c.getOverlapRear());
327         if (virtualHeadway.le0() && conflict.getDistance().le0())
328         {
329             // TODO what about long conflicts where we need to follow the second conflicting downstream vehicle?
330             // conflict GTU downstream of start of conflict, but upstream of us
331             return Acceleration.POS_MAXVALUE;
332         }
333         // follow leader
334         SortedMap<Length, Speed> leaders = new TreeMap<>();
335         leaders.put(virtualHeadway, c.getSpeed());
336         Acceleration a = carFollowingModel.followingAcceleration(behavioralCharacteristics, speed, speedLimitInfo, leaders);
337         // if conflicting GTU is partially upstream of the conflict and at (near) stand-still, stop for the conflict rather than
338         // following the tail of the conflicting GTU
339         if (conflict.isMerge() && virtualHeadway.lt(conflict.getDistance()))
340         {
341             // {@formatter:off}
342             /*
343              * ______________________________________________
344              *    ___    stop for conflict  |       | 
345              *   |___|(--------------------)|   ___ |
346              * _____________________________|__/  /_|________ 
347              *                              / /__/  /
348              *                             /       /
349              */
350             // {@formatter:on}
351             behavioralCharacteristics.setParameter(ParameterTypes.S0, behavioralCharacteristics.getParameter(S0_CONF));
352             Acceleration aStop = CarFollowingUtil.stop(carFollowingModel, behavioralCharacteristics, speed, speedLimitInfo,
353                     conflict.getDistance());
354             behavioralCharacteristics.resetParameter(ParameterTypes.S0);
355             a = Acceleration.max(a, aStop); // max, which ever allows the largest acceleration
356         }
357         return a;
358     }
359 
360     /**
361      * Determines an acceleration required to avoid a collision with GTUs <i>on</i> a crossing conflict.
362      * @param behavioralCharacteristics behavioral characteristics
363      * @param conflict conflict
364      * @param carFollowingModel car-following model
365      * @param speed current speed
366      * @param speedLimitInfo speed limit info
367      * @return acceleration required to avoid a collision
368      * @throws ParameterException if parameter is not defined
369      */
370     private static Acceleration avoidCrossingCollision(final BehavioralCharacteristics behavioralCharacteristics,
371             final HeadwayConflict conflict, final CarFollowingModel carFollowingModel, final Speed speed,
372             final SpeedLimitInfo speedLimitInfo) throws ParameterException
373     {
374 
375         // TODO only within visibility
376         List<HeadwayGTU> conflictingGTUs = new ArrayList<>();
377         for (HeadwayGTU gtu : conflict.getUpstreamConflictingGTUs())
378         {
379             if (isOnRoute(conflict.getConflictingLink(), gtu))
380             {
381                 // first upstream vehicle on route to this conflict
382                 conflictingGTUs.add(gtu);
383                 break;
384             }
385         }
386         for (HeadwayGTU gtu : conflict.getDownstreamConflictingGTUs())
387         {
388             if (gtu.isParallel())
389             {
390                 conflictingGTUs.add(gtu);
391             }
392             else
393             {
394                 // vehicles beyond conflict are not a thread
395                 break;
396             }
397         }
398 
399         if (conflictingGTUs.isEmpty())
400         {
401             return Acceleration.POS_MAXVALUE;
402         }
403 
404         Acceleration a = Acceleration.POS_MAXVALUE;
405         for (HeadwayGTU conflictingGTU : conflictingGTUs)
406         {
407             AnticipationInfo tteC;
408             Length distance;
409             if (conflictingGTU.isParallel())
410             {
411                 tteC = new AnticipationInfo(Duration.ZERO, conflictingGTU.getSpeed());
412                 distance = conflictingGTU.getOverlapRear().abs().plus(conflictingGTU.getOverlap())
413                         .plus(conflictingGTU.getOverlapFront().abs());
414             }
415             else
416             {
417                 tteC = AnticipationInfo.anticipateMovement(conflictingGTU.getDistance(), conflictingGTU.getSpeed(),
418                         Acceleration.ZERO);
419                 distance = conflictingGTU.getDistance().plus(conflict.getLength()).plus(conflictingGTU.getLength());
420             }
421             AnticipationInfo ttcC = AnticipationInfo.anticipateMovement(distance, conflictingGTU.getSpeed(), Acceleration.ZERO);
422             AnticipationInfo tteO = AnticipationInfo.anticipateMovementFreeAcceleration(conflict.getDistance(), speed,
423                     behavioralCharacteristics, carFollowingModel, speedLimitInfo, TIME_STEP);
424             // enter before cleared
425             // TODO safety factor?
426             if (tteC.getDuration().lt(tteO.getDuration()) && tteO.getDuration().lt(ttcC.getDuration()))
427             {
428                 if (!conflictingGTU.getSpeed().eq0())
429                 {
430                     // solve parabolic speed profile s = v*t + .5*a*t*t, a =
431                     double acc = 2 * (conflict.getDistance().si - speed.si * ttcC.getDuration().si)
432                             / (ttcC.getDuration().si * ttcC.getDuration().si);
433                     // time till zero speed > time to avoid conflict?
434                     if (speed.si / -acc > ttcC.getDuration().si)
435                     {
436                         a = Acceleration.min(a, new Acceleration(acc, AccelerationUnit.SI));
437                     }
438                     else
439                     {
440                         // will reach zero speed ourselves
441                         a = Acceleration.min(a, CarFollowingUtil.stop(carFollowingModel, behavioralCharacteristics, speed,
442                                 speedLimitInfo, conflict.getDistance()));
443                     }
444                 }
445                 // conflicting vehicle stand-still, ignore even at conflict
446             }
447         }
448         return a;
449     }
450 
451     /**
452      * Approach a priority conflict. Stopping is applied to give way to conflicting traffic in case congestion is present on the
453      * own lane. This is courtesy yielding.
454      * @param conflict conflict to approach
455      * @param leaders leading vehicles in own lane
456      * @param speed current speed
457      * @param vehicleLength vehicle length
458      * @param behavioralCharacteristics behavioral characteristics
459      * @param yieldPlans set of plans for yielding with priority
460      * @return whether to stop for this conflict
461      * @throws ParameterException if parameter B is not defined
462      */
463     public static boolean stopForPriorityConflict(final HeadwayConflict conflict, final SortedSet<HeadwayGTU> leaders,
464             final Speed speed, final Length vehicleLength, final BehavioralCharacteristics behavioralCharacteristics,
465             final ConflictPlans yieldPlans) throws ParameterException
466     {
467 
468         if (leaders.isEmpty() || conflict.getUpstreamConflictingGTUs().isEmpty())
469         {
470             // no leader, or no conflicting vehicle
471             return false;
472         }
473 
474         // Stop as long as some leader is standing still, and leader is not leaving sufficient space yet
475         // use start of conflict on merge, end of conflict on crossing
476         Length typeCorrection = conflict.isCrossing() ? conflict.getLength() : Length.ZERO;
477         // distance leader has to cover before we can pass the conflict
478         Length distance = conflict.getDistance().minus(leaders.first().getDistance())
479                 .plus(passableDistance(vehicleLength, behavioralCharacteristics)).plus(typeCorrection);
480         if (distance.gt0())
481         {
482             Length required = conflict.getDistance().plus(typeCorrection)
483                     .plus(passableDistance(vehicleLength, behavioralCharacteristics)); // for ourselves
484             for (HeadwayGTU leader : leaders)
485             {
486                 if (leader.getSpeed().eq0())
487                 {
488                     // first stand-still leader is not fully upstream of the conflict (in that case, ignore), and does not
489                     // allow sufficient space for all vehicles in between
490                     return leader.getDistance().ge(conflict.getDistance()) && required.ge(leader.getDistance());
491                 }
492                 required = required // add required distance for leaders
493                         .plus(passableDistance(leader.getLength(), leader.getBehavioralCharacteristics()));
494             }
495         }
496         return false;
497 
498     }
499 
500     /**
501      * Approach a give-way conflict.
502      * @param conflict conflict
503      * @param leaders leaders
504      * @param speed current speed
505      * @param acceleration current acceleration
506      * @param vehicleLength vehicle length
507      * @param behavioralCharacteristics behavioral characteristics
508      * @param speedLimitInfo speed limit info
509      * @param carFollowingModel car-following model
510      * @return whether to stop for this conflict
511      * @throws ParameterException if a parameter is not defined
512      */
513     @SuppressWarnings("checkstyle:parameternumber")
514     public static boolean stopForGiveWayConflict(final HeadwayConflict conflict, final SortedSet<HeadwayGTU> leaders,
515             final Speed speed, final Acceleration acceleration, final Length vehicleLength,
516             final BehavioralCharacteristics behavioralCharacteristics, final SpeedLimitInfo speedLimitInfo,
517             final CarFollowingModel carFollowingModel) throws ParameterException
518     {
519 
520         // TODO conflicting vehicle on crossing conflict, but will leave sooner then we enter, so no problem?
521         // TODO more generally, also upstream conflicting vehicles at crossings may leave the conflict before we enter
522         if (conflict.getConflictType().equals(ConflictType.CROSSING) && !conflict.getDownstreamConflictingGTUs().isEmpty()
523                 && conflict.getDownstreamConflictingGTUs().first().isParallel())
524         {
525             // vehicle on the conflict
526             return true;
527         }
528 
529         // Get data independent of conflicting vehicle
530         // parameters
531         Acceleration b = behavioralCharacteristics.getParameter(ParameterTypes.B).neg();
532         double f = behavioralCharacteristics.getParameter(TIME_FACTOR);
533         Duration gap = behavioralCharacteristics.getParameter(MIN_GAP);
534         // time till conflict is cleared
535         Length distance = conflict.getDistance().plus(vehicleLength);
536         if (conflict.isCrossing())
537         {
538             // merge is cleared at start, crossing at end
539             distance = distance.plus(conflict.getLength());
540         }
541         // based on acceleration, limited by free acceleration
542         AnticipationInfo ttcOa = AnticipationInfo.anticipateMovementFreeAcceleration(distance, speed, behavioralCharacteristics,
543                 carFollowingModel, speedLimitInfo, TIME_STEP);
544         // time till downstream vehicle will make the conflict passible, under constant speed or safe deceleration
545         AnticipationInfo ttpDz = null;
546         AnticipationInfo ttpDs = null;
547         if (conflict.isCrossing())
548         {
549             if (!leaders.isEmpty())
550             {
551                 distance = conflict.getDistance().minus(leaders.first().getDistance()).plus(conflict.getLength())
552                         .plus(passableDistance(vehicleLength, behavioralCharacteristics));
553                 ttpDz = AnticipationInfo.anticipateMovement(distance, leaders.first().getSpeed(), Acceleration.ZERO);
554                 ttpDs = AnticipationInfo.anticipateMovement(distance, leaders.first().getSpeed(), b);
555             }
556             else
557             {
558                 // no leader so conflict is passible within a duration of 0
559                 ttpDz = new AnticipationInfo(Duration.ZERO, Speed.ZERO);
560                 ttpDs = new AnticipationInfo(Duration.ZERO, Speed.ZERO);
561             }
562         }
563 
564         List<HeadwayGTU> conflictingVehicles = new ArrayList<>();
565         if (!conflict.getUpstreamConflictingGTUs().isEmpty())
566         {
567             for (HeadwayGTU vehicle : conflict.getUpstreamConflictingGTUs())
568             {
569                 if (conflict.getConflictingTrafficLightDistance() != null && !conflict.isPermitted()
570                         && vehicle.getDistance().gt(conflict.getConflictingTrafficLightDistance()))
571                 {
572                     break;
573                 }
574                 if (isOnRoute(conflict.getConflictingLink(), vehicle))
575                 {
576                     conflictingVehicles.add(vehicle);
577                 }
578             }
579         }
580         else if (conflict.getConflictingTrafficLightDistance() == null)
581         {
582             // none within visibility, assume a conflicting vehicle just outside of visibility driving at speed limit
583             try
584             {
585                 HeadwayGTUSimple conflictGtu = new HeadwayGTUSimple("virtual " + UUID.randomUUID().toString(), RoadGTUTypes.CAR,
586                         conflict.getConflictingVisibility(), new Length(4.0, LengthUnit.SI),
587                         conflict.getConflictingSpeedLimit(), Acceleration.ZERO);
588                 conflictingVehicles.add(conflictGtu);
589             }
590             catch (GTUException exception)
591             {
592                 throw new RuntimeException("Could not create a virtual conflicting vehicle at visibility range.", exception);
593             }
594         }
595 
596         // Do not stop if conflicting vehicle is standing still
597         if (conflictingVehicles.isEmpty() || conflictingVehicles.get(0).getSpeed().eq0())
598         {
599             return false;
600         }
601 
602         // Loop over conflicting vehicles
603         for (HeadwayGTU conflictingVehicle : conflictingVehicles)
604         {
605 
606             // time till conflict vehicle will enter, under free acceleration and safe deceleration
607             AnticipationInfo tteCa;
608             if (conflictingVehicle instanceof HeadwayGTUSimple)
609             {
610                 tteCa = AnticipationInfo.anticipateMovement(conflictingVehicle.getDistance(), conflictingVehicle.getSpeed(),
611                         conflictingVehicle.getAcceleration());
612             }
613             else
614             {
615                 BehavioralCharacteristics bc = conflictingVehicle.getBehavioralCharacteristics();
616                 SpeedLimitInfo sli = conflictingVehicle.getSpeedLimitInfo();
617                 CarFollowingModel cfm = conflictingVehicle.getCarFollowingModel();
618                 // Constant acceleration creates inf at stand still, triggering passing trough a congested stream
619                 tteCa = AnticipationInfo.anticipateMovementFreeAcceleration(conflictingVehicle.getDistance(),
620                         conflictingVehicle.getSpeed(), bc, cfm, sli, TIME_STEP);
621             }
622             AnticipationInfo tteCs =
623                     AnticipationInfo.anticipateMovement(conflictingVehicle.getDistance(), conflictingVehicle.getSpeed(), b);
624 
625             // check gap
626             if (conflict.isMerge())
627             {
628 
629                 // Merge, will be each others followers, add time to overcome speed difference
630                 double vSelf = ttcOa.getEndSpeed().si;
631                 double speedDiff = conflictingVehicle.getSpeed().si - vSelf;
632                 speedDiff = speedDiff > 0 ? speedDiff : 0;
633                 Duration additionalTime = new Duration(speedDiff / -b.si, TimeUnit.SI);
634                 // check if conflict vehicle will be upstream after that time, position beyond conflict after additional time
635                 double followerFront = conflictingVehicle.getSpeed().si * ttcOa.getDuration().si
636                         - conflictingVehicle.getDistance().si + (conflictingVehicle.getSpeed().si * additionalTime.si
637                                 + 0.5 * b.si * additionalTime.si * additionalTime.si);
638                 double ownRear = vSelf * additionalTime.si; // constant speed after clearing
639                 Duration tMax = behavioralCharacteristics.getParameter(ParameterTypes.TMAX);
640                 Length s0 = behavioralCharacteristics.getParameter(ParameterTypes.S0);
641                 // 1) will clear the conflict after the conflict vehicle enters
642                 // 2) not sufficient time to overcome speed difference
643                 // 3) conflict vehicle will be too near after adjusting speed
644                 if (ttcOa.getDuration().multiplyBy(f).plus(gap).gt(tteCa.getDuration())
645                         || ttcOa.getDuration().plus(additionalTime).multiplyBy(f).plus(gap).gt(tteCs.getDuration())
646                         || ownRear < (followerFront + (tMax.si + gap.si) * vSelf + s0.si) * f)
647                 {
648                     return true;
649                 }
650 
651             }
652             else if (conflict.isCrossing())
653             {
654 
655                 // Crossing, stop if order of events is not ok
656                 // Should go before the conflict vehicle
657                 // 1) downstream vehicle must supply sufficient space before conflict vehicle will enter
658                 // 2) must clear the conflict before the conflict vehicle will enter
659                 // 3) if leader decelerates with b, conflict vehicle should be able to safely delay entering conflict
660                 if (ttpDz.getDuration().multiplyBy(f).plus(gap).gt(tteCa.getDuration())
661                         || ttcOa.getDuration().multiplyBy(f).plus(gap).gt(tteCa.getDuration())
662                         || ttpDs.getDuration().multiplyBy(f).plus(gap).gt(tteCs.getDuration()))
663                 {
664                     return true;
665                 }
666 
667             }
668             else
669             {
670                 throw new RuntimeException(
671                         "Conflict is of unknown type " + conflict.getConflictType() + ", which is not merge nor a crossing.");
672             }
673         }
674 
675         // No conflict vehicle triggered stopping
676         return false;
677 
678     }
679 
680     /**
681      * Approach a stop conflict. Currently this is equal to approaching a give-way conflict.
682      * @param conflict conflict
683      * @param leaders leaders
684      * @param speed current speed
685      * @param acceleration current acceleration
686      * @param vehicleLength vehicle length
687      * @param behavioralCharacteristics behavioral characteristics
688      * @param speedLimitInfo speed limit info
689      * @param carFollowingModel car-following model
690      * @return whether to stop for this conflict
691      * @throws ParameterException if a parameter is not defined
692      */
693     @SuppressWarnings("checkstyle:parameternumber")
694     public static boolean stopForStopConflict(final HeadwayConflict conflict, final SortedSet<HeadwayGTU> leaders,
695             final Speed speed, final Acceleration acceleration, final Length vehicleLength,
696             final BehavioralCharacteristics behavioralCharacteristics, final SpeedLimitInfo speedLimitInfo,
697             final CarFollowingModel carFollowingModel) throws ParameterException
698     {
699         return stopForGiveWayConflict(conflict, leaders, speed, acceleration, vehicleLength, behavioralCharacteristics,
700                 speedLimitInfo, carFollowingModel);
701     }
702 
703     /**
704      * Approach an all-stop conflict.
705      * @param conflict conflict to approach
706      * @param conflictPlans set of plans for conflict
707      * @return whether to stop for this conflict
708      */
709     public static boolean stopForAllStopConflict(final HeadwayConflict conflict, final ConflictPlans conflictPlans)
710     {
711         // TODO all-stop behavior
712 
713         if (conflictPlans.isStopPhaseRun(conflict.getStopLine()))
714         {
715             return false;
716         }
717 
718         return false;
719     }
720 
721     /**
722      * Returns whether the conflicting link is on the route of the given gtu.
723      * @param conflictingLink CrossSectionLink; conflicting link
724      * @param gtu HeadwayGTU; gtu
725      * @return whether the conflict is on the route of the given gtu
726      */
727     private static boolean isOnRoute(final CrossSectionLink conflictingLink, final HeadwayGTU gtu)
728     {
729         try
730         {
731             Route route = gtu.getRoute();
732             if (route == null)
733             {
734                 // conservative assumption: it's on the route (gtu should be upstream of the conflict)
735                 return true;
736             }
737             Node startNode = conflictingLink.getStartNode();
738             Node endNode = conflictingLink.getEndNode();
739             return route.contains(startNode) && route.contains(endNode)
740                     && Math.abs(route.indexOf(endNode) - route.indexOf(startNode)) == 1;
741         }
742         catch (@SuppressWarnings("unused") UnsupportedOperationException uoe)
743         {
744             // conservative assumption: it's on the route (gtu should be upstream of the conflict)
745             return true;
746         }
747     }
748 
749     /**
750      * Returns a speed dependent distance needed behind the leader to completely pass the conflict.
751      * @param vehicleLength vehicle length
752      * @param behavioralCharacteristics behavioral characteristics
753      * @return speed dependent distance needed behind the leader to completely pass the conflict
754      * @throws ParameterException if parameter is not available
755      */
756     private static Length passableDistance(final Length vehicleLength,
757             final BehavioralCharacteristics behavioralCharacteristics) throws ParameterException
758     {
759         return behavioralCharacteristics.getParameter(ParameterTypes.S0).plus(vehicleLength);
760     }
761 
762     /**
763      * Holds the tactical plans of a driver considering conflicts. These are remembered for consistency. For instance, if the
764      * decision is made to yield as current deceleration suggests it's safe to do so, but the trajectory for stopping in front
765      * of the conflict results in deceleration slightly above what is considered safe deceleration, the plan should not be
766      * abandoned. Decelerations above what is considered safe deceleration may result due to numerical overshoot or other factor
767      * coming into play in car-following models. Many other examples exist where a driver sticks to a certain plan.
768      * <p>
769      * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
770      * <br>
771      * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
772      * <p>
773      * @version $Revision$, $LastChangedDate$, by $Author$, initial version Jun 7, 2016 <br>
774      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
775      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
776      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
777      */
778     public static final class ConflictPlans implements Serializable
779     {
780 
781         /** */
782         private static final long serialVersionUID = 20160811L;
783 
784         /** Phases of navigating an all-stop intersection per intersection. */
785         private final HashMap<String, StopPhase> stopPhases = new HashMap<>();
786 
787         /** Estimated arrival times of vehicles at all-stop intersection. */
788         private final HashMap<String, Time> arrivalTimes = new HashMap<>();
789 
790         /** Indicator intent. */
791         private TurnIndicatorIntent indicatorIntent = TurnIndicatorIntent.NONE;
792 
793         /** Distance to object causing turn indicator intent. */
794         private Length indicatorObjectDistance = null;
795 
796         /**
797          * Clean any yield plan that was no longer kept active in the last evaluation of conflicts.
798          */
799         void cleanPlans()
800         {
801             this.indicatorIntent = TurnIndicatorIntent.NONE;
802             this.indicatorObjectDistance = null;
803         }
804 
805         /**
806          * Sets the estimated arrival time of a GTU.
807          * @param gtu GTU
808          * @param time estimated arrival time
809          */
810         void setArrivalTime(final AbstractHeadwayGTU gtu, final Time time)
811         {
812             this.arrivalTimes.put(gtu.getId(), time);
813         }
814 
815         /**
816          * Returns the estimated arrival time of given GTU.
817          * @param gtu GTU
818          * @return estimated arrival time of given GTU
819          */
820         Time getArrivalTime(final AbstractHeadwayGTU gtu)
821         {
822             return this.arrivalTimes.get(gtu.getId());
823         }
824 
825         /**
826          * Sets the current phase to 'approach' for the given stop line.
827          * @param stopLine stop line
828          */
829         void setStopPhaseApproach(final HeadwayStopLine stopLine)
830         {
831             this.stopPhases.put(stopLine.getId(), StopPhase.APPROACH);
832         }
833 
834         /**
835          * Sets the current phase to 'yield' for the given stop line.
836          * @param stopLine stop line
837          * @throws RuntimeException if the phase was not set to approach before
838          */
839         void setStopPhaseYield(final HeadwayStopLine stopLine)
840         {
841             Throw.when(
842                     !this.stopPhases.containsKey(stopLine.getId())
843                             || !this.stopPhases.get(stopLine.getId()).equals(StopPhase.APPROACH),
844                     RuntimeException.class, "Yield stop phase is set for stop line that was not approached.");
845             this.stopPhases.put(stopLine.getId(), StopPhase.YIELD);
846         }
847 
848         /**
849          * Sets the current phase to 'run' for the given stop line.
850          * @param stopLine stop line
851          * @throws RuntimeException if the phase was not set to approach before
852          */
853         void setStopPhaseRun(final HeadwayStopLine stopLine)
854         {
855             Throw.when(!this.stopPhases.containsKey(stopLine.getId()), RuntimeException.class,
856                     "Run stop phase is set for stop line that was not approached.");
857             this.stopPhases.put(stopLine.getId(), StopPhase.YIELD);
858         }
859 
860         /**
861          * @param stopLine stop line
862          * @return whether the current phase is 'approach' for the given stop line
863          */
864         boolean isStopPhaseApproach(final HeadwayStopLine stopLine)
865         {
866             return this.stopPhases.containsKey(stopLine.getId())
867                     && this.stopPhases.get(stopLine.getId()).equals(StopPhase.APPROACH);
868         }
869 
870         /**
871          * @param stopLine stop line
872          * @return whether the current phase is 'yield' for the given stop line
873          */
874         boolean isStopPhaseYield(final HeadwayStopLine stopLine)
875         {
876             return this.stopPhases.containsKey(stopLine.getId())
877                     && this.stopPhases.get(stopLine.getId()).equals(StopPhase.YIELD);
878         }
879 
880         /**
881          * @param stopLine stop line
882          * @return whether the current phase is 'run' for the given stop line
883          */
884         boolean isStopPhaseRun(final HeadwayStopLine stopLine)
885         {
886             return this.stopPhases.containsKey(stopLine.getId()) && this.stopPhases.get(stopLine.getId()).equals(StopPhase.RUN);
887         }
888 
889         /** {@inheritDoc} */
890         @Override
891         public String toString()
892         {
893             return "ConflictPlans";
894         }
895 
896         /**
897          * @return indicatorIntent.
898          */
899         public TurnIndicatorIntent getIndicatorIntent()
900         {
901             return this.indicatorIntent;
902         }
903 
904         /**
905          * @return indicatorObjectDistance.
906          */
907         public Length getIndicatorObjectDistance()
908         {
909             return this.indicatorObjectDistance;
910         }
911 
912         /**
913          * @param intent indicator intent
914          * @param distance distance to object pertaining to the turn indicator intent
915          */
916         public void setIndicatorIntent(final TurnIndicatorIntent intent, final Length distance)
917         {
918             if (this.indicatorObjectDistance == null || this.indicatorObjectDistance.gt(distance))
919             {
920                 this.indicatorIntent = intent;
921                 this.indicatorObjectDistance = distance;
922             }
923         }
924 
925     }
926 
927     /**
928      * Phases of navigating an all-stop intersection.
929      * <p>
930      * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
931      * <br>
932      * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
933      * <p>
934      * @version $Revision$, $LastChangedDate$, by $Author$, initial version Jun 30, 2016 <br>
935      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
936      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
937      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
938      */
939     private enum StopPhase
940     {
941         /** Approaching stop intersection. */
942         APPROACH,
943 
944         /** Yielding for stop intersection. */
945         YIELD,
946 
947         /** Running over stop intersection. */
948         RUN;
949     }
950 
951 }