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