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