ConflictUtil.java

  1. package org.opentrafficsim.road.gtu.lane.tactical.util;

  2. import java.io.Serializable;
  3. import java.util.ArrayList;
  4. import java.util.Iterator;
  5. import java.util.LinkedHashMap;
  6. import java.util.List;
  7. import java.util.SortedMap;
  8. import java.util.TreeMap;
  9. import java.util.UUID;

  10. import org.djunits.unit.AccelerationUnit;
  11. import org.djunits.unit.DurationUnit;
  12. import org.djunits.unit.LengthUnit;
  13. import org.djunits.value.vdouble.scalar.Acceleration;
  14. import org.djunits.value.vdouble.scalar.Duration;
  15. import org.djunits.value.vdouble.scalar.Length;
  16. import org.djunits.value.vdouble.scalar.Speed;
  17. import org.djunits.value.vdouble.scalar.Time;
  18. import org.djutils.exceptions.Throw;
  19. import org.opentrafficsim.base.parameters.ParameterException;
  20. import org.opentrafficsim.base.parameters.ParameterTypeAcceleration;
  21. import org.opentrafficsim.base.parameters.ParameterTypeDouble;
  22. import org.opentrafficsim.base.parameters.ParameterTypeDuration;
  23. import org.opentrafficsim.base.parameters.ParameterTypeLength;
  24. import org.opentrafficsim.base.parameters.ParameterTypes;
  25. import org.opentrafficsim.base.parameters.Parameters;
  26. import org.opentrafficsim.base.parameters.constraint.ConstraintInterface;
  27. import org.opentrafficsim.core.gtu.GTUException;
  28. import org.opentrafficsim.core.gtu.GTUType;
  29. import org.opentrafficsim.core.gtu.TurnIndicatorIntent;
  30. import org.opentrafficsim.core.network.Node;
  31. import org.opentrafficsim.core.network.route.Route;
  32. import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
  33. import org.opentrafficsim.road.gtu.lane.perception.PerceptionCollectable;
  34. import org.opentrafficsim.road.gtu.lane.perception.PerceptionIterable;
  35. import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
  36. import org.opentrafficsim.road.gtu.lane.perception.headway.AbstractHeadwayGTU;
  37. import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayConflict;
  38. import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTU;
  39. import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTUSimple;
  40. import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayStopLine;
  41. import org.opentrafficsim.road.gtu.lane.tactical.Blockable;
  42. import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
  43. import org.opentrafficsim.road.gtu.lane.tactical.pt.BusSchedule;
  44. import org.opentrafficsim.road.network.RoadNetwork;
  45. import org.opentrafficsim.road.network.lane.CrossSectionLink;
  46. import org.opentrafficsim.road.network.lane.conflict.BusStopConflictRule;
  47. import org.opentrafficsim.road.network.lane.conflict.ConflictRule;
  48. import org.opentrafficsim.road.network.lane.conflict.ConflictType;
  49. import org.opentrafficsim.road.network.speed.SpeedLimitInfo;

  50. /**
  51.  * This class implements default behavior for intersection conflicts for use in tactical planners.
  52.  * <p>
  53.  * Copyright (c) 2013-2022 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.     /** Minimum time gap between events. */
  70.     public static final ParameterTypeDuration MIN_GAP = new ParameterTypeDuration("minGap", "Minimum gap for conflicts",
  71.             new Duration(0.000001, DurationUnit.SECOND), ConstraintInterface.POSITIVE);

  72.     /** Comfortable deceleration. */
  73.     public static final ParameterTypeAcceleration B = ParameterTypes.B;

  74.     /** Critical deceleration. */
  75.     public static final ParameterTypeAcceleration BCRIT = ParameterTypes.BCRIT;

  76.     /** Stopping distance. */
  77.     public static final ParameterTypeLength S0 = ParameterTypes.S0;

  78.     /** Stopping distance at conflicts. */
  79.     public static final ParameterTypeLength S0_CONF = new ParameterTypeLength("s0conf", "Stopping distance at conflicts",
  80.             new Length(1.5, LengthUnit.METER), ConstraintInterface.POSITIVE);

  81.     /** Multiplication factor on time for conservative assessment. */
  82.     public static final ParameterTypeDouble TIME_FACTOR =
  83.             new ParameterTypeDouble("timeFactor", "Safety factor on estimated time", 1.25, ConstraintInterface.ATLEASTONE);

  84.     /** Area before stop line where one is considered arrived at the intersection. */
  85.     public static final ParameterTypeLength STOP_AREA =
  86.             new ParameterTypeLength("stopArea", "Area before stop line where one is considered arrived at the intersection",
  87.                     new Length(4, LengthUnit.METER), ConstraintInterface.POSITIVE);

  88.     /** Parameter of how much time before departure a bus indicates its departure to get priority. */
  89.     public static final ParameterTypeDuration TI = new ParameterTypeDuration("ti", "Indicator time before bus departure",
  90.             Duration.instantiateSI(3.0), ConstraintInterface.POSITIVE);

  91.     /** Time step for free acceleration anticipation. */
  92.     private static final Duration TIME_STEP = new Duration(0.5, DurationUnit.SI);

  93.     /** Cross standing vehicles on crossings. We allow this to prevent dead-locks. A better model should render this useless. */
  94.     private static boolean CROSSSTANDING = true;

  95.     /**
  96.      * Do not instantiate.
  97.      */
  98.     private ConflictUtil()
  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 <code>YieldPlans</code>. By forwarding the same <code>YieldPlans</code> 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 parameters Parameters; parameters
  108.      * @param conflicts PerceptionCollectable&lt;HeadwayConflict,Conflict&gt;; set of conflicts to approach
  109.      * @param leaders PerceptionCollectable&lt;HeadwayGTU,LaneBasedGTU&gt;; leading vehicles
  110.      * @param carFollowingModel CarFollowingModel; car-following model
  111.      * @param vehicleLength Length; length of vehicle
  112.      * @param vehicleWidth Length; width of vehicle
  113.      * @param speed Speed; current speed
  114.      * @param acceleration Acceleration; current acceleration
  115.      * @param speedLimitInfo SpeedLimitInfo; speed limit info
  116.      * @param conflictPlans ConflictPlans; set of plans for conflict
  117.      * @param gtu LaneBasedGTU; gtu
  118.      * @param lane RelativeLane; lane
  119.      * @return acceleration appropriate for approaching the conflicts
  120.      * @throws GTUException in case of an unsupported conflict rule
  121.      * @throws ParameterException if a parameter is not defined or out of bounds
  122.      */
  123.     @SuppressWarnings("checkstyle:parameternumber")
  124.     public static Acceleration approachConflicts(final Parameters parameters, final Iterable<HeadwayConflict> conflicts,
  125.             final PerceptionCollectable<HeadwayGTU, LaneBasedGTU> leaders, final CarFollowingModel carFollowingModel,
  126.             final Length vehicleLength, final Length vehicleWidth, final Speed speed, final Acceleration acceleration,
  127.             final SpeedLimitInfo speedLimitInfo, final ConflictPlans conflictPlans, final LaneBasedGTU gtu,
  128.             final RelativeLane lane) throws GTUException, ParameterException
  129.     {

  130.         conflictPlans.cleanPlans();

  131.         Acceleration a = Acceleration.POS_MAXVALUE;
  132.         Length stoppingDistance = Length.instantiateSI(
  133.                 parameters.getParameter(S0).si + vehicleLength.si + .5 * speed.si * speed.si / parameters.getParameter(B).si);
  134.         Iterator<HeadwayConflict> it = conflicts.iterator();
  135.         if (it.hasNext() && it.next().getDistance().gt(stoppingDistance))
  136.         {
  137.             conflictPlans.setBlocking(false);
  138.             return a;
  139.         }

  140.         List<Length> prevStarts = new ArrayList<>();
  141.         List<Length> prevEnds = new ArrayList<>();
  142.         List<Class<? extends ConflictRule>> conflictRuleTypes = new ArrayList<>();
  143.         boolean blocking = false;

  144.         for (HeadwayConflict conflict : conflicts)
  145.         {

  146.             // adjust acceleration for situations where stopping might not be required
  147.             if (conflict.isCrossing())
  148.             {
  149.                 // avoid collision if crossing is occupied
  150.                 a = Acceleration.min(a, avoidCrossingCollision(parameters, conflict, carFollowingModel, speed, speedLimitInfo));
  151.             }
  152.             else
  153.             {
  154.                 if (conflict.isMerge() && !lane.isCurrent() && conflict.getConflictPriority().isPriority())
  155.                 {
  156.                     // probably evaluation for a lane-change
  157.                     a = Acceleration.min(a,
  158.                             avoidMergeCollision(parameters, conflict, carFollowingModel, speed, speedLimitInfo));
  159.                 }
  160.                 // follow leading GTUs on merge or split
  161.                 a = Acceleration.min(a, followConflictingLeaderOnMergeOrSplit(conflict, parameters, carFollowingModel, speed,
  162.                         speedLimitInfo, vehicleWidth));
  163.             }

  164.             // indicator if bus
  165.             if (lane.isCurrent())
  166.             {
  167.                 if (gtu.getStrategicalPlanner().getRoute() instanceof BusSchedule
  168.                         && gtu.getGTUType().isOfType(GTUType.DEFAULTS.BUS)
  169.                         && conflict.getConflictRuleType().equals(BusStopConflictRule.class))
  170.                 {
  171.                     BusSchedule busSchedule = (BusSchedule) gtu.getStrategicalPlanner().getRoute();
  172.                     Time actualDeparture = busSchedule.getActualDepartureConflict(conflict.getId());
  173.                     if (actualDeparture != null
  174.                             && actualDeparture.si < gtu.getSimulator().getSimulatorTime().si + parameters.getParameter(TI).si)
  175.                     {
  176.                         // TODO depending on left/right-hand traffic
  177.                         conflictPlans.setIndicatorIntent(TurnIndicatorIntent.LEFT, conflict.getDistance());
  178.                     }
  179.                 }
  180.             }

  181.             // blocking and ignoring
  182.             if (conflict.getDistance().lt0() && lane.isCurrent())
  183.             {
  184.                 if (conflict.getConflictType().isCrossing() && !conflict.getConflictPriority().isPriority())
  185.                 {
  186.                     // note that we are blocking a conflict
  187.                     blocking = true;
  188.                 }
  189.                 // ignore conflicts we are on (i.e. negative distance to start of conflict)
  190.                 continue;
  191.             }

  192.             // determine if we need to stop
  193.             boolean stop;
  194.             switch (conflict.getConflictPriority())
  195.             {
  196.                 case PRIORITY:
  197.                 {
  198.                     Length prevEnd = prevEnds.isEmpty() ? null : prevEnds.get(prevEnds.size() - 1);
  199.                     stop = stopForPriorityConflict(conflict, leaders, speed, vehicleLength, parameters, prevEnd);
  200.                     break;
  201.                 }
  202.                 case YIELD: // TODO depending on rules, we may need to stop and not just yield
  203.                 case TURN_ON_RED:
  204.                 {
  205.                     Length prevEnd = prevEnds.isEmpty() ? null : prevEnds.get(prevEnds.size() - 1);
  206.                     stop = stopForGiveWayConflict(conflict, leaders, speed, acceleration, vehicleLength, parameters,
  207.                             speedLimitInfo, carFollowingModel, blocking ? BCRIT : B, prevEnd);
  208.                     break;
  209.                 }
  210.                 case STOP:
  211.                 {
  212.                     Length prevEnd = prevEnds.isEmpty() ? null : prevEnds.get(prevEnds.size() - 1);
  213.                     stop = stopForStopConflict(conflict, leaders, speed, acceleration, vehicleLength, parameters,
  214.                             speedLimitInfo, carFollowingModel, blocking ? BCRIT : B, prevEnd);
  215.                     break;
  216.                 }
  217.                 case ALL_STOP:
  218.                 {
  219.                     stop = stopForAllStopConflict(conflict, conflictPlans);
  220.                     break;
  221.                 }
  222.                 case SPLIT:
  223.                 {
  224.                     stop = false; // skipped anyway
  225.                     break;
  226.                 }
  227.                 default:
  228.                 {
  229.                     throw new GTUException("Unsupported conflict rule encountered while approaching conflicts.");
  230.                 }
  231.             }

  232.             // stop if required, account for upstream conflicts to keep clear
  233.             if (!conflict.getConflictType().equals(ConflictType.SPLIT))
  234.             {

  235.                 if (stop)
  236.                 {
  237.                     prevStarts.add(conflict.getDistance());
  238.                     conflictRuleTypes.add(conflict.getConflictRuleType());
  239.                     // stop for first conflict looking upstream of this blocked conflict that allows sufficient space
  240.                     int j = 0; // most upstream conflict if not in between conflicts
  241.                     for (int i = prevEnds.size() - 1; i >= 0; i--) // downstream to upstream
  242.                     {
  243.                         // note, at this point prevStarts contains one more conflict than prevEnds
  244.                         if (prevStarts.get(i + 1).minus(prevEnds.get(i)).gt(passableDistance(vehicleLength, parameters)))
  245.                         {
  246.                             j = i + 1;
  247.                             break;
  248.                         }
  249.                     }
  250.                     if (blocking && j == 0)
  251.                     {
  252.                         // we are blocking a conflict, let's not stop more upstream than the conflict that forces our stop
  253.                         j = prevStarts.size() - 1;
  254.                     }

  255.                     // TODO
  256.                     // if this lowers our acceleration, we need to check if we are able to pass upstream conflicts still in time

  257.                     // stop for j'th conflict, if deceleration is too strong, for next one
  258.                     parameters.setParameterResettable(S0, parameters.getParameter(S0_CONF));
  259.                     Acceleration aCF = new Acceleration(-Double.MAX_VALUE, AccelerationUnit.SI);
  260.                     while (aCF.si < -6.0 && j < prevStarts.size())
  261.                     {
  262.                         if (prevStarts.get(j).lt(parameters.getParameter(S0_CONF)))
  263.                         {
  264.                             // TODO what to do when we happen to be in the stopping distance? Stopping might be reasonable,
  265.                             // while car-following might give strong deceleration due to s < s0.
  266.                             aCF = Acceleration.max(aCF, new Acceleration(-6.0, AccelerationUnit.SI));
  267.                         }
  268.                         else
  269.                         {
  270.                             Acceleration aStop = CarFollowingUtil.stop(carFollowingModel, parameters, speed, speedLimitInfo,
  271.                                     prevStarts.get(j));
  272.                             if (conflictRuleTypes.get(j).equals(BusStopConflictRule.class)
  273.                                     && aStop.lt(parameters.getParameter(ParameterTypes.BCRIT).neg()))
  274.                             {
  275.                                 // as it may suddenly switch state, i.e. ignore like a yellow traffic light
  276.                                 aStop = Acceleration.POS_MAXVALUE;
  277.                             }
  278.                             aCF = Acceleration.max(aCF, aStop);
  279.                         }
  280.                         j++;
  281.                     }
  282.                     parameters.resetParameter(S0);
  283.                     a = Acceleration.min(a, aCF);
  284.                     break;
  285.                 }

  286.                 // keep conflict clear (when stopping for another conflict)
  287.                 prevStarts.add(conflict.getDistance());
  288.                 conflictRuleTypes.add(conflict.getConflictRuleType());
  289.                 prevEnds.add(conflict.getDistance().plus(conflict.getLength()));
  290.             }

  291.         }
  292.         conflictPlans.setBlocking(blocking);

  293.         if (a.si < -6.0 && speed.si > 5 / 3.6)
  294.         {
  295.             System.err.println("Deceleration from conflict util stronger than 6m/s^2.");
  296.             // return Acceleration.POSITIVE_INFINITY;
  297.         }
  298.         return a;
  299.     }

  300.     /**
  301.      * Determines acceleration for following conflicting vehicles <i>on</i> a merge or split conflict.
  302.      * @param conflict HeadwayConflict; merge or split conflict
  303.      * @param parameters Parameters; parameters
  304.      * @param carFollowingModel CarFollowingModel; car-following model
  305.      * @param speed Speed; current speed
  306.      * @param speedLimitInfo SpeedLimitInfo; speed limit info
  307.      * @param vehicleWidth Length; own width
  308.      * @return acceleration for following conflicting vehicles <i>on</i> a merge or split conflict
  309.      * @throws ParameterException if a parameter is not given or out of bounds
  310.      */
  311.     private static Acceleration followConflictingLeaderOnMergeOrSplit(final HeadwayConflict conflict,
  312.             final Parameters parameters, final CarFollowingModel carFollowingModel, final Speed speed,
  313.             final SpeedLimitInfo speedLimitInfo, final Length vehicleWidth) throws ParameterException
  314.     {
  315.         // ignore if no conflicting GTU's, or if first is downstream of conflict
  316.         PerceptionIterable<HeadwayGTU> downstreamGTUs = conflict.getDownstreamConflictingGTUs();
  317.         if (downstreamGTUs.isEmpty() || downstreamGTUs.first().isAhead())
  318.         {
  319.             return Acceleration.POS_MAXVALUE;
  320.         }
  321.         // get the most upstream GTU to consider
  322.         HeadwayGTU c = null;
  323.         Length virtualHeadway = null;
  324.         if (conflict.getDistance().gt0())
  325.         {
  326.             c = downstreamGTUs.first();
  327.             virtualHeadway = conflict.getDistance().plus(c.getOverlapRear());
  328.         }
  329.         else
  330.         {
  331.             for (HeadwayGTU con : downstreamGTUs)
  332.             {
  333.                 if (con.isAhead())
  334.                 {
  335.                     // conflict GTU completely downstream of conflict (i.e. regular car-following, ignore here)
  336.                     return Acceleration.POS_MAXVALUE;
  337.                 }
  338.                 // conflict GTU (partially) on the conflict
  339.                 // {@formatter:off}
  340.                 // ______________________________________________
  341.                 //   ___      virtual headway   |  ___  |
  342.                 //  |___|(-----------------------)|___|(vehicle from south, on lane from south)
  343.                 // _____________________________|_______|________
  344.                 //                              /       /
  345.                 //                             /       /
  346.                 // {@formatter:on}
  347.                 virtualHeadway = conflict.getDistance().plus(con.getOverlapRear());
  348.                 if (virtualHeadway.gt0())
  349.                 {
  350.                     if (conflict.isSplit())
  351.                     {
  352.                         double conflictWidth = conflict.getWidthAtFraction(
  353.                                 (-conflict.getDistance().si + virtualHeadway.si) / conflict.getConflictingLength().si).si;
  354.                         double gtuWidth = con.getWidth().si + vehicleWidth.si;
  355.                         if (conflictWidth > gtuWidth)
  356.                         {
  357.                             continue;
  358.                         }
  359.                     }
  360.                     // found first downstream GTU on conflict
  361.                     c = con;
  362.                     break;
  363.                 }
  364.             }
  365.         }
  366.         if (c == null)
  367.         {
  368.             // conflict GTU downstream of start of conflict, but upstream of us
  369.             return Acceleration.POS_MAXVALUE;
  370.         }
  371.         // follow leader
  372.         SortedMap<Length, Speed> leaders = new TreeMap<>();
  373.         leaders.put(virtualHeadway, c.getSpeed());
  374.         Acceleration a = CarFollowingUtil.followSingleLeader(carFollowingModel, parameters, speed, speedLimitInfo,
  375.                 virtualHeadway, c.getSpeed());
  376.         // if conflicting GTU is partially upstream of the conflict and at (near) stand-still, stop for the conflict rather than
  377.         // following the tail of the conflicting GTU
  378.         if (conflict.isMerge() && virtualHeadway.lt(conflict.getDistance()))
  379.         {
  380.             // {@formatter:off}
  381.             /*
  382.              * ______________________________________________
  383.              *    ___    stop for conflict  |       |
  384.              *   |___|(--------------------)|   ___ |
  385.              * _____________________________|__/  /_|________
  386.              *                              / /__/  /
  387.              *                             /       /
  388.              */
  389.             // {@formatter:on}
  390.             parameters.setParameterResettable(S0, parameters.getParameter(S0_CONF));
  391.             Acceleration aStop =
  392.                     CarFollowingUtil.stop(carFollowingModel, parameters, speed, speedLimitInfo, conflict.getDistance());
  393.             parameters.resetParameter(S0);
  394.             a = Acceleration.max(a, aStop); // max, which ever allows the largest acceleration
  395.         }
  396.         return a;
  397.     }

  398.     /**
  399.      * Determines an acceleration required to avoid a collision with GTUs <i>on</i> a crossing conflict.
  400.      * @param parameters Parameters; parameters
  401.      * @param conflict HeadwayConflict; conflict
  402.      * @param carFollowingModel CarFollowingModel; car-following model
  403.      * @param speed Speed; current speed
  404.      * @param speedLimitInfo SpeedLimitInfo; speed limit info
  405.      * @return acceleration required to avoid a collision
  406.      * @throws ParameterException if parameter is not defined
  407.      */
  408.     private static Acceleration avoidCrossingCollision(final Parameters parameters, final HeadwayConflict conflict,
  409.             final CarFollowingModel carFollowingModel, final Speed speed, final SpeedLimitInfo speedLimitInfo)
  410.             throws ParameterException
  411.     {

  412.         // TODO only within visibility
  413.         List<HeadwayGTU> conflictingGTUs = new ArrayList<>();
  414.         for (HeadwayGTU gtu : conflict.getUpstreamConflictingGTUs())
  415.         {
  416.             if (isOnRoute(conflict.getConflictingLink(), gtu))
  417.             {
  418.                 // first upstream vehicle on route to this conflict
  419.                 conflictingGTUs.add(gtu);
  420.                 break;
  421.             }
  422.         }
  423.         for (HeadwayGTU gtu : conflict.getDownstreamConflictingGTUs())
  424.         {
  425.             if (gtu.isParallel())
  426.             {
  427.                 conflictingGTUs.add(gtu);
  428.             }
  429.             else
  430.             {
  431.                 // vehicles beyond conflict are not a thread
  432.                 break;
  433.             }
  434.         }

  435.         if (conflictingGTUs.isEmpty())
  436.         {
  437.             return Acceleration.POS_MAXVALUE;
  438.         }

  439.         Acceleration a = Acceleration.POS_MAXVALUE;
  440.         for (HeadwayGTU conflictingGTU : conflictingGTUs)
  441.         {
  442.             AnticipationInfo tteC;
  443.             Length distance;
  444.             if (conflictingGTU.isParallel())
  445.             {
  446.                 tteC = new AnticipationInfo(Duration.ZERO, conflictingGTU.getSpeed());
  447.                 distance = conflictingGTU.getOverlapRear().abs().plus(conflictingGTU.getOverlap())
  448.                         .plus(conflictingGTU.getOverlapFront().abs());
  449.             }
  450.             else
  451.             {
  452.                 tteC = AnticipationInfo.anticipateMovement(conflictingGTU.getDistance(), conflictingGTU.getSpeed(),
  453.                         Acceleration.ZERO);
  454.                 distance = conflictingGTU.getDistance().plus(conflict.getLength()).plus(conflictingGTU.getLength());
  455.             }
  456.             AnticipationInfo ttcC = AnticipationInfo.anticipateMovement(distance, conflictingGTU.getSpeed(), Acceleration.ZERO);
  457.             AnticipationInfo tteO = AnticipationInfo.anticipateMovementFreeAcceleration(conflict.getDistance(), speed,
  458.                     parameters, carFollowingModel, speedLimitInfo, TIME_STEP);
  459.             // enter before cleared
  460.             // TODO safety factor?
  461.             if (tteC.getDuration().lt(tteO.getDuration()) && tteO.getDuration().lt(ttcC.getDuration()))
  462.             {
  463.                 if (!conflictingGTU.getSpeed().eq0() || !CROSSSTANDING)
  464.                 {
  465.                     // solve parabolic speed profile s = v*t + .5*a*t*t, a =
  466.                     double acc = 2 * (conflict.getDistance().si - speed.si * ttcC.getDuration().si)
  467.                             / (ttcC.getDuration().si * ttcC.getDuration().si);
  468.                     // time till zero speed > time to avoid conflict?
  469.                     if (speed.si / -acc > ttcC.getDuration().si)
  470.                     {
  471.                         a = Acceleration.min(a, new Acceleration(acc, AccelerationUnit.SI));
  472.                     }
  473.                     else
  474.                     {
  475.                         // will reach zero speed ourselves
  476.                         a = Acceleration.min(a, CarFollowingUtil.stop(carFollowingModel, parameters, speed, speedLimitInfo,
  477.                                 conflict.getDistance()));
  478.                     }
  479.                 }
  480.                 // conflicting vehicle stand-still, ignore even at conflict
  481.             }
  482.         }
  483.         return a;
  484.     }

  485.     /**
  486.      * Avoid collision at merge. This method assumes the GTU has priority.
  487.      * @param parameters Parameters; parameters
  488.      * @param conflict HeadwayConflict; conflict
  489.      * @param carFollowingModel CarFollowingModel; car-following model
  490.      * @param speed Speed; current speed
  491.      * @param speedLimitInfo SpeedLimitInfo; speed limit info
  492.      * @return acceleration required to avoid a collision
  493.      * @throws ParameterException if parameter is not defined
  494.      */
  495.     private static Acceleration avoidMergeCollision(final Parameters parameters, final HeadwayConflict conflict,
  496.             final CarFollowingModel carFollowingModel, final Speed speed, final SpeedLimitInfo speedLimitInfo)
  497.             throws ParameterException
  498.     {
  499.         PerceptionCollectable<HeadwayGTU, LaneBasedGTU> conflicting = conflict.getUpstreamConflictingGTUs();
  500.         if (conflicting.isEmpty() || conflicting.first().isParallel())
  501.         {
  502.             return Acceleration.POS_MAXVALUE;
  503.         }
  504.         // TODO: this check is simplistic, designed quick and dirty
  505.         HeadwayGTU conflictingGtu = conflicting.first();
  506.         double tteC = conflictingGtu.getDistance().si / conflictingGtu.getSpeed().si;
  507.         if (tteC < conflict.getDistance().si / speed.si + 3.0)
  508.         {
  509.             return CarFollowingUtil.stop(carFollowingModel, parameters, speed, speedLimitInfo, conflict.getDistance());
  510.         }
  511.         return Acceleration.POS_MAXVALUE;
  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.      * @param prevEnd Length; distance to end of previous conflict that should not be blocked, {@code null} if none
  522.      * @return whether to stop for this conflict
  523.      * @throws ParameterException if parameter B is not defined
  524.      */
  525.     public static boolean stopForPriorityConflict(final HeadwayConflict conflict,
  526.             final PerceptionCollectable<HeadwayGTU, LaneBasedGTU> leaders, final Speed speed, final Length vehicleLength,
  527.             final Parameters parameters, final Length prevEnd) throws ParameterException
  528.     {

  529.         // check if we should stop as there is not sufficient space on the merge, to leave a previous conflict unblocked
  530.         Length passable = passableDistance(vehicleLength, parameters);
  531.         if (prevEnd != null && conflict.isMerge() && !conflict.getDownstreamConflictingGTUs().isEmpty())
  532.         {
  533.             HeadwayGTU conflictingGTU = conflict.getDownstreamConflictingGTUs().first();
  534.             Acceleration b = parameters.getParameter(BCRIT);
  535.             double t = conflictingGTU.getSpeed().divide(b).si;
  536.             Length stopDistance = Length.instantiateSI(conflictingGTU.getSpeed().si * t - .5 * b.si * t * t);
  537.             Length room = conflict
  538.                     .getDistance().plus(stopDistance).plus(conflictingGTU.isAhead()
  539.                             ? conflict.getLength().plus(conflictingGTU.getDistance()) : conflictingGTU.getOverlapRear())
  540.                     .minus(prevEnd);
  541.             if (room.lt(passable))
  542.             {
  543.                 return true;
  544.             }
  545.         }

  546.         // some quick -no need to stop-'s
  547.         if (leaders.isEmpty())
  548.         {
  549.             // no leader
  550.             return false;
  551.         }
  552.         if (conflict.getUpstreamConflictingGTUs().isEmpty())
  553.         {
  554.             // no conflicting vehicles
  555.             return false;
  556.         }
  557.         else
  558.         {
  559.             HeadwayGTU conflictingGTU = conflict.getUpstreamConflictingGTUs().first();
  560.             if (conflictingGTU.getSpeed().eq0() && conflictingGTU.isAhead()
  561.                     && conflictingGTU.getDistance().gt(parameters.getParameter(S0)))
  562.             {
  563.                 // conflicting vehicle too far away
  564.                 return false;
  565.             }
  566.         }

  567.         // Stop as long as some leader is standing still, and this leader is not leaving sufficient space yet
  568.         // use start of conflict on merge, end of conflict on crossing
  569.         Length typeCorrection = conflict.isCrossing() ? conflict.getLength() : Length.ZERO;
  570.         // distance leader has to cover before we can pass the conflict
  571.         Length distance = conflict.getDistance().minus(leaders.first().getDistance()).plus(passable).plus(typeCorrection);
  572.         if (distance.gt0())
  573.         {
  574.             // for ourselves
  575.             Length required = conflict.getDistance().plus(typeCorrection).plus(passableDistance(vehicleLength, parameters));
  576.             for (HeadwayGTU leader : leaders)
  577.             {
  578.                 if (leader.getSpeed().eq0())
  579.                 {
  580.                     // first stand-still leader is not fully upstream of the conflict (in that case, ignore), and does not
  581.                     // allow sufficient space for all vehicles in between
  582.                     return leader.getDistance().ge(conflict.getDistance()) && required.ge(leader.getDistance());
  583.                 }
  584.                 required = required // add required distance for leaders
  585.                         .plus(passableDistance(leader.getLength(), leader.getParameters()));
  586.             }
  587.         }

  588.         // no reason found to stop
  589.         return false;
  590.     }

  591.     /**
  592.      * Approach a give-way conflict.
  593.      * @param conflict HeadwayConflict; conflict
  594.      * @param leaders PerceptionCollectable&lt;HeadwayGTU,LaneBasedGTU&gt;; leaders
  595.      * @param speed Speed; current speed
  596.      * @param acceleration Acceleration; current acceleration
  597.      * @param vehicleLength Length; vehicle length
  598.      * @param parameters Parameters; parameters
  599.      * @param speedLimitInfo SpeedLimitInfo; speed limit info
  600.      * @param carFollowingModel CarFollowingModel; car-following model
  601.      * @param bType ParameterTypeAcceleration; parameter type for considered deceleration
  602.      * @param prevEnd Length; distance to end of previous conflict that should not be blocked, {@code null} if none
  603.      * @return whether to stop for this conflict
  604.      * @throws ParameterException if a parameter is not defined
  605.      */
  606.     @SuppressWarnings({ "checkstyle:parameternumber", "checkstyle:methodlength" })
  607.     public static boolean stopForGiveWayConflict(final HeadwayConflict conflict,
  608.             final PerceptionCollectable<HeadwayGTU, LaneBasedGTU> leaders, final Speed speed, final Acceleration acceleration,
  609.             final Length vehicleLength, final Parameters parameters, final SpeedLimitInfo speedLimitInfo,
  610.             final CarFollowingModel carFollowingModel, final ParameterTypeAcceleration bType, final Length prevEnd)
  611.             throws ParameterException
  612.     {

  613.         // TODO conflicting vehicle on crossing conflict, but will leave sooner then we enter, so no problem?
  614.         // TODO more generally, also upstream conflicting vehicles at crossings may leave the conflict before we enter
  615.         if (conflict.getConflictType().isCrossing() && !conflict.getDownstreamConflictingGTUs().isEmpty()
  616.                 && conflict.getDownstreamConflictingGTUs().first().isParallel())
  617.         {
  618.             // vehicle on the conflict
  619.             return true;
  620.         }

  621.         // Get data independent of conflicting vehicle
  622.         // parameters
  623.         Acceleration b = parameters.getParameter(bType).neg();
  624.         double f = parameters.getParameter(TIME_FACTOR);
  625.         Duration gap = parameters.getParameter(MIN_GAP);
  626.         // time till conflict is cleared
  627.         Length distance = conflict.getDistance().plus(vehicleLength);
  628.         if (conflict.isCrossing())
  629.         {
  630.             // merge is cleared at start, crossing at end
  631.             distance = distance.plus(conflict.getLength());
  632.         }
  633.         // based on acceleration, limited by free acceleration
  634.         AnticipationInfo ttcOa = AnticipationInfo.anticipateMovementFreeAcceleration(distance, speed, parameters,
  635.                 carFollowingModel, speedLimitInfo, TIME_STEP);
  636.         // time till downstream vehicle will make the conflict passible, under constant speed or safe deceleration
  637.         AnticipationInfo ttpDz = null;
  638.         AnticipationInfo ttpDs = null;
  639.         if (conflict.isCrossing())
  640.         {
  641.             if (!leaders.isEmpty())
  642.             {
  643.                 distance = conflict.getDistance().minus(leaders.first().getDistance()).plus(conflict.getLength())
  644.                         .plus(passableDistance(vehicleLength, parameters));
  645.                 ttpDz = AnticipationInfo.anticipateMovement(distance, leaders.first().getSpeed(), Acceleration.ZERO);
  646.                 ttpDs = AnticipationInfo.anticipateMovement(distance, leaders.first().getSpeed(), b);
  647.             }
  648.             else
  649.             {
  650.                 // no leader so conflict is passible within a duration of 0
  651.                 ttpDz = new AnticipationInfo(Duration.ZERO, Speed.ZERO);
  652.                 ttpDs = new AnticipationInfo(Duration.ZERO, Speed.ZERO);
  653.             }
  654.         }
  655.         else if (conflict.isMerge() && prevEnd != null)
  656.         {
  657.             // stop for merge (and previous conflict) if we are likely to stop partially on the previous conflict
  658.             Length preGap = conflict.getDistance().minus(prevEnd);
  659.             PerceptionCollectable<HeadwayGTU, LaneBasedGTU> downs = conflict.getDownstreamConflictingGTUs();
  660.             if (!downs.isEmpty() && downs.first().isParallel())
  661.             {
  662.                 distance = passableDistance(vehicleLength, parameters).minus(preGap).minus(downs.first().getOverlapRear());
  663.                 ttpDs = AnticipationInfo.anticipateMovement(distance, downs.first().getSpeed(), b);
  664.                 if (ttpDs.getDuration().equals(Duration.POSITIVE_INFINITY))
  665.                 {
  666.                     // vehicle on conflict will not leave sufficient space
  667.                     return true;
  668.                 }
  669.             }
  670.             else if (!leaders.isEmpty())
  671.             {
  672.                 distance = conflict.getDistance().plus(passableDistance(vehicleLength, parameters)).minus(preGap)
  673.                         .minus(leaders.first().getDistance());
  674.                 ttpDs = AnticipationInfo.anticipateMovement(distance, leaders.first().getSpeed(), b);
  675.                 if (ttpDs.getDuration().equals(Duration.POSITIVE_INFINITY))
  676.                 {
  677.                     // vehicle on conflict will not leave sufficient space
  678.                     return true;
  679.                 }
  680.             }
  681.         }

  682.         PerceptionCollectable<HeadwayGTU, LaneBasedGTU> conflictingVehiclesCollectable = conflict.getUpstreamConflictingGTUs();
  683.         Iterable<HeadwayGTU> conflictingVehicles;
  684.         if (conflictingVehiclesCollectable.isEmpty())
  685.         {
  686.             if (conflict.getConflictingTrafficLightDistance() == null)
  687.             {
  688.                 // none within visibility, assume a conflicting vehicle just outside of visibility driving at speed limit
  689.                 try
  690.                 {
  691.                     RoadNetwork network = conflict.getConflictingLink().getNetwork();
  692.                     HeadwayGTUSimple conflictGtu = new HeadwayGTUSimple("virtual " + UUID.randomUUID().toString(),
  693.                             network.getGtuType(GTUType.DEFAULTS.CAR), conflict.getConflictingVisibility(),
  694.                             new Length(4.0, LengthUnit.SI), new Length(2.0, LengthUnit.SI), conflict.getConflictingSpeedLimit(),
  695.                             Acceleration.ZERO, Speed.ZERO);
  696.                     List<HeadwayGTU> conflictingVehiclesList = new ArrayList<>();
  697.                     conflictingVehiclesList.add(conflictGtu);
  698.                     conflictingVehicles = conflictingVehiclesList;
  699.                 }
  700.                 catch (GTUException exception)
  701.                 {
  702.                     throw new RuntimeException("Could not create a virtual conflicting vehicle at visibility range.",
  703.                             exception);
  704.                 }
  705.             }
  706.             else
  707.             {
  708.                 // no conflicting vehicles
  709.                 return false;
  710.             }
  711.         }
  712.         else
  713.         {
  714.             HeadwayGTU conflicting = conflictingVehiclesCollectable.first();
  715.             if (conflict.getConflictingTrafficLightDistance() != null && conflicting.isAhead()
  716.                     && conflict.getConflictingTrafficLightDistance().lt(conflicting.getDistance())
  717.                     && (conflicting.getSpeed().eq0() || conflicting.getAcceleration().lt0()))
  718.             {
  719.                 // conflicting traffic upstream of traffic light
  720.                 return false;
  721.             }
  722.             conflictingVehicles = conflictingVehiclesCollectable;
  723.         }

  724.         // Loop over conflicting vehicles
  725.         boolean first = true;
  726.         boolean ignoreBeyondFirst = false;
  727.         for (HeadwayGTU conflictingVehicle : conflictingVehicles)
  728.         {

  729.             // skip if not on route
  730.             if (!isOnRoute(conflict.getConflictingLink(), conflictingVehicle))
  731.             {
  732.                 continue;
  733.             }

  734.             // time till conflict vehicle will enter, under free acceleration and safe deceleration
  735.             AnticipationInfo tteCa;
  736.             AnticipationInfo tteCs;
  737.             if (first && conflictingVehicle.getSpeed().eq0() && conflictingVehicle.isAhead())
  738.             // && conflictingVehicle.getDistance().lt(parameters.getParameter(S0_CONF)))
  739.             {
  740.                 // do not stop if conflicting vehicle is standing still
  741.                 tteCa = new AnticipationInfo(Duration.POSITIVE_INFINITY, Speed.ZERO);
  742.                 tteCs = new AnticipationInfo(Duration.POSITIVE_INFINITY, Speed.ZERO);
  743.                 ignoreBeyondFirst = true;
  744.             }
  745.             else
  746.             {
  747.                 if (conflictingVehicle instanceof HeadwayGTUSimple)
  748.                 {
  749.                     tteCa = AnticipationInfo.anticipateMovement(conflictingVehicle.getDistance(), conflictingVehicle.getSpeed(),
  750.                             conflictingVehicle.getAcceleration());
  751.                 }
  752.                 else
  753.                 {
  754.                     Parameters params = conflictingVehicle.getParameters();
  755.                     SpeedLimitInfo sli = conflictingVehicle.getSpeedLimitInfo();
  756.                     CarFollowingModel cfm = conflictingVehicle.getCarFollowingModel();
  757.                     // Constant acceleration creates inf at stand still, triggering passing trough a congested stream
  758.                     if (conflictingVehicle.isAhead())
  759.                     {
  760.                         tteCa = AnticipationInfo.anticipateMovementFreeAcceleration(conflictingVehicle.getDistance(),
  761.                                 conflictingVehicle.getSpeed(), params, cfm, sli, TIME_STEP);
  762.                     }
  763.                     else
  764.                     {
  765.                         tteCa = new AnticipationInfo(Duration.ZERO, conflictingVehicle.getSpeed());
  766.                     }
  767.                 }
  768.                 if (conflictingVehicle.isAhead())
  769.                 {
  770.                     tteCs = AnticipationInfo.anticipateMovement(conflictingVehicle.getDistance(), conflictingVehicle.getSpeed(),
  771.                             b);
  772.                 }
  773.                 else
  774.                 {
  775.                     tteCs = new AnticipationInfo(Duration.ZERO, conflictingVehicle.getSpeed());
  776.                 }
  777.             }

  778.             // check gap
  779.             if (conflict.isMerge())
  780.             {

  781.                 // Merge, will be each others followers, add time to overcome speed difference
  782.                 double vSelf = ttcOa.getEndSpeed().si;
  783.                 double speedDiff = conflictingVehicle.getSpeed().si - vSelf;
  784.                 speedDiff = speedDiff > 0 ? speedDiff : 0;
  785.                 Duration additionalTime = new Duration(speedDiff / -b.si, DurationUnit.SI);
  786.                 // check if conflict vehicle will be upstream after that time, position beyond conflict after additional time
  787.                 double followerFront = conflictingVehicle.isAhead() ? conflictingVehicle.getSpeed().si * ttcOa.getDuration().si
  788.                         - conflictingVehicle.getDistance().si + (conflictingVehicle.getSpeed().si * additionalTime.si
  789.                                 + 0.5 * b.si * additionalTime.si * additionalTime.si)
  790.                         : 0.0;
  791.                 double ownRear = vSelf * additionalTime.si; // constant speed after clearing
  792.                 Duration tMax = parameters.getParameter(ParameterTypes.TMAX);
  793.                 Length s0 = parameters.getParameter(S0);
  794.                 // 1) will clear the conflict after the conflict vehicle enters
  795.                 // 2) not sufficient time to overcome speed difference
  796.                 // 3) conflict vehicle will be too near after adjusting speed
  797.                 if (ttcOa.getDuration().times(f).plus(gap).gt(tteCa.getDuration())
  798.                         || ttcOa.getDuration().plus(additionalTime).times(f).plus(gap).gt(tteCs.getDuration())
  799.                         || (!Double.isInfinite(tteCa.getDuration().si) && tteCa.getDuration().si > 0.0
  800.                                 && ownRear < (followerFront + (tMax.si + gap.si) * vSelf + s0.si) * f))
  801.                 {
  802.                     return true;
  803.                 }

  804.             }
  805.             else if (conflict.isCrossing())
  806.             {

  807.                 // Crossing, stop if order of events is not ok
  808.                 // Should go before the conflict vehicle
  809.                 // 1) downstream vehicle must supply sufficient space before conflict vehicle will enter
  810.                 // 2) must clear the conflict before the conflict vehicle will enter
  811.                 // 3) if leader decelerates with b, conflict vehicle should be able to safely delay entering conflict
  812.                 // 4) conflict vehicle will never leave enough space beyond the conflict
  813.                 if (ttpDz.getDuration().times(f).plus(gap).gt(tteCa.getDuration())
  814.                         || ttcOa.getDuration().times(f).plus(gap).gt(tteCa.getDuration())
  815.                         || ttpDs.getDuration().times(f).plus(gap).gt(tteCs.getDuration())
  816.                         || ttpDs.getDuration().equals(Duration.POSITIVE_INFINITY))
  817.                 {
  818.                     return true;
  819.                 }

  820.             }
  821.             else
  822.             {
  823.                 throw new RuntimeException(
  824.                         "Conflict is of unknown type " + conflict.getConflictType() + ", which is not merge nor a crossing.");
  825.             }

  826.             if (first && ignoreBeyondFirst)
  827.             {
  828.                 return false;
  829.             }
  830.             first = false;
  831.         }

  832.         // No conflict vehicle triggered stopping
  833.         return false;

  834.     }

  835.     /**
  836.      * Approach a stop conflict. Currently this is equal to approaching a give-way conflict.
  837.      * @param conflict HeadwayConflict; conflict
  838.      * @param leaders PerceptionCollectable&lt;HeadwayGTU,LaneBasedGTU&gt;; leaders
  839.      * @param speed Speed; current speed
  840.      * @param acceleration Acceleration; current acceleration
  841.      * @param vehicleLength Length; vehicle length
  842.      * @param parameters Parameters; parameters
  843.      * @param speedLimitInfo SpeedLimitInfo; speed limit info
  844.      * @param carFollowingModel CarFollowingModel; car-following model
  845.      * @param bType ParameterTypeAcceleration; parameter type for considered deceleration
  846.      * @param prevEnd Length; distance to end of previous conflict that should not be blocked, {@code null} if none
  847.      * @return whether to stop for this conflict
  848.      * @throws ParameterException if a parameter is not defined
  849.      */
  850.     @SuppressWarnings("checkstyle:parameternumber")
  851.     public static boolean stopForStopConflict(final HeadwayConflict conflict,
  852.             final PerceptionCollectable<HeadwayGTU, LaneBasedGTU> leaders, final Speed speed, final Acceleration acceleration,
  853.             final Length vehicleLength, final Parameters parameters, final SpeedLimitInfo speedLimitInfo,
  854.             final CarFollowingModel carFollowingModel, final ParameterTypeAcceleration bType, final Length prevEnd)
  855.             throws ParameterException
  856.     {
  857.         // TODO stopping
  858.         return stopForGiveWayConflict(conflict, leaders, speed, acceleration, vehicleLength, parameters, speedLimitInfo,
  859.                 carFollowingModel, bType, prevEnd);
  860.     }

  861.     /**
  862.      * Approach an all-stop conflict.
  863.      * @param conflict HeadwayConflict; conflict to approach
  864.      * @param conflictPlans ConflictPlans; set of plans for conflict
  865.      * @return whether to stop for this conflict
  866.      */
  867.     public static boolean stopForAllStopConflict(final HeadwayConflict conflict, final ConflictPlans conflictPlans)
  868.     {
  869.         // TODO all-stop behavior

  870.         if (conflictPlans.isStopPhaseRun(conflict.getStopLine()))
  871.         {
  872.             return false;
  873.         }

  874.         return false;
  875.     }

  876.     /**
  877.      * Returns whether the conflicting link is on the route of the given gtu.
  878.      * @param conflictingLink CrossSectionLink; conflicting link
  879.      * @param gtu HeadwayGTU; gtu
  880.      * @return whether the conflict is on the route of the given gtu
  881.      */
  882.     private static boolean isOnRoute(final CrossSectionLink conflictingLink, final HeadwayGTU gtu)
  883.     {
  884.         try
  885.         {
  886.             Route route = gtu.getRoute();
  887.             if (route == null)
  888.             {
  889.                 // conservative assumption: it's on the route (gtu should be upstream of the conflict)
  890.                 return true;
  891.             }
  892.             Node startNode = conflictingLink.getStartNode();
  893.             Node endNode = conflictingLink.getEndNode();
  894.             return route.contains(startNode) && route.contains(endNode)
  895.                     && Math.abs(route.indexOf(endNode) - route.indexOf(startNode)) == 1;
  896.         }
  897.         catch (UnsupportedOperationException uoe)
  898.         {
  899.             // conservative assumption: it's on the route (gtu should be upstream of the conflict)
  900.             return true;
  901.         }
  902.     }

  903.     /**
  904.      * Returns a speed dependent distance needed behind the leader to completely pass the conflict.
  905.      * @param vehicleLength Length; vehicle length
  906.      * @param parameters Parameters; parameters
  907.      * @return speed dependent distance needed behind the leader to completely pass the conflict
  908.      * @throws ParameterException if parameter is not available
  909.      */
  910.     private static Length passableDistance(final Length vehicleLength, final Parameters parameters) throws ParameterException
  911.     {
  912.         return parameters.getParameter(S0).plus(vehicleLength);
  913.     }

  914.     /**
  915.      * Holds the tactical plans of a driver considering conflicts. These are remembered for consistency. For instance, if the
  916.      * decision is made to yield as current deceleration suggests it's safe to do so, but the trajectory for stopping in front
  917.      * of the conflict results in deceleration slightly above what is considered safe deceleration, the plan should not be
  918.      * abandoned. Decelerations above what is considered safe deceleration may result due to numerical overshoot or other factor
  919.      * coming into play in car-following models. Many other examples exist where a driver sticks to a certain plan.
  920.      * <p>
  921.      * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
  922.      * <br>
  923.      * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
  924.      * <p>
  925.      * @version $Revision$, $LastChangedDate$, by $Author$, initial version Jun 7, 2016 <br>
  926.      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
  927.      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
  928.      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
  929.      */
  930.     public static final class ConflictPlans implements Blockable, Serializable
  931.     {

  932.         /** */
  933.         private static final long serialVersionUID = 20160811L;

  934.         /** Phases of navigating an all-stop intersection per intersection. */
  935.         private final LinkedHashMap<String, StopPhase> stopPhases = new LinkedHashMap<>();

  936.         /** Estimated arrival times of vehicles at all-stop intersection. */
  937.         private final LinkedHashMap<String, Time> arrivalTimes = new LinkedHashMap<>();

  938.         /** Indicator intent. */
  939.         private TurnIndicatorIntent indicatorIntent = TurnIndicatorIntent.NONE;

  940.         /** Distance to object causing turn indicator intent. */
  941.         private Length indicatorObjectDistance = null;

  942.         /** Whether the GTU is blocking conflicts. */
  943.         private boolean blocking;

  944.         /**
  945.          * Clean any yield plan that was no longer kept active in the last evaluation of conflicts.
  946.          */
  947.         void cleanPlans()
  948.         {
  949.             this.indicatorIntent = TurnIndicatorIntent.NONE;
  950.             this.indicatorObjectDistance = null;
  951.         }

  952.         /**
  953.          * Sets the estimated arrival time of a GTU.
  954.          * @param gtu AbstractHeadwayGTU; GTU
  955.          * @param time Time; estimated arrival time
  956.          */
  957.         void setArrivalTime(final AbstractHeadwayGTU gtu, final Time time)
  958.         {
  959.             this.arrivalTimes.put(gtu.getId(), time);
  960.         }

  961.         /**
  962.          * Returns the estimated arrival time of given GTU.
  963.          * @param gtu AbstractHeadwayGTU; GTU
  964.          * @return estimated arrival time of given GTU
  965.          */
  966.         Time getArrivalTime(final AbstractHeadwayGTU gtu)
  967.         {
  968.             return this.arrivalTimes.get(gtu.getId());
  969.         }

  970.         /**
  971.          * Sets the current phase to 'approach' for the given stop line.
  972.          * @param stopLine HeadwayStopLine; stop line
  973.          */
  974.         void setStopPhaseApproach(final HeadwayStopLine stopLine)
  975.         {
  976.             this.stopPhases.put(stopLine.getId(), StopPhase.APPROACH);
  977.         }

  978.         /**
  979.          * Sets the current phase to 'yield' for the given stop line.
  980.          * @param stopLine HeadwayStopLine; stop line
  981.          * @throws RuntimeException if the phase was not set to approach before
  982.          */
  983.         void setStopPhaseYield(final HeadwayStopLine stopLine)
  984.         {
  985.             Throw.when(
  986.                     !this.stopPhases.containsKey(stopLine.getId())
  987.                             || !this.stopPhases.get(stopLine.getId()).equals(StopPhase.APPROACH),
  988.                     RuntimeException.class, "Yield stop phase is set for stop line that was not approached.");
  989.             this.stopPhases.put(stopLine.getId(), StopPhase.YIELD);
  990.         }

  991.         /**
  992.          * Sets the current phase to 'run' for the given stop line.
  993.          * @param stopLine HeadwayStopLine; stop line
  994.          * @throws RuntimeException if the phase was not set to approach before
  995.          */
  996.         void setStopPhaseRun(final HeadwayStopLine stopLine)
  997.         {
  998.             Throw.when(!this.stopPhases.containsKey(stopLine.getId()), RuntimeException.class,
  999.                     "Run stop phase is set for stop line that was not approached.");
  1000.             this.stopPhases.put(stopLine.getId(), StopPhase.YIELD);
  1001.         }

  1002.         /**
  1003.          * @param stopLine HeadwayStopLine; stop line
  1004.          * @return whether the current phase is 'approach' for the given stop line
  1005.          */
  1006.         boolean isStopPhaseApproach(final HeadwayStopLine stopLine)
  1007.         {
  1008.             return this.stopPhases.containsKey(stopLine.getId())
  1009.                     && this.stopPhases.get(stopLine.getId()).equals(StopPhase.APPROACH);
  1010.         }

  1011.         /**
  1012.          * @param stopLine HeadwayStopLine; stop line
  1013.          * @return whether the current phase is 'yield' for the given stop line
  1014.          */
  1015.         boolean isStopPhaseYield(final HeadwayStopLine stopLine)
  1016.         {
  1017.             return this.stopPhases.containsKey(stopLine.getId())
  1018.                     && this.stopPhases.get(stopLine.getId()).equals(StopPhase.YIELD);
  1019.         }

  1020.         /**
  1021.          * @param stopLine HeadwayStopLine; stop line
  1022.          * @return whether the current phase is 'run' for the given stop line
  1023.          */
  1024.         boolean isStopPhaseRun(final HeadwayStopLine stopLine)
  1025.         {
  1026.             return this.stopPhases.containsKey(stopLine.getId()) && this.stopPhases.get(stopLine.getId()).equals(StopPhase.RUN);
  1027.         }

  1028.         /** {@inheritDoc} */
  1029.         @Override
  1030.         public String toString()
  1031.         {
  1032.             return "ConflictPlans";
  1033.         }

  1034.         /**
  1035.          * @return indicatorIntent.
  1036.          */
  1037.         public TurnIndicatorIntent getIndicatorIntent()
  1038.         {
  1039.             return this.indicatorIntent;
  1040.         }

  1041.         /**
  1042.          * @return indicatorObjectDistance.
  1043.          */
  1044.         public Length getIndicatorObjectDistance()
  1045.         {
  1046.             return this.indicatorObjectDistance;
  1047.         }

  1048.         /**
  1049.          * @param intent TurnIndicatorIntent; indicator intent
  1050.          * @param distance Length; distance to object pertaining to the turn indicator intent
  1051.          */
  1052.         public void setIndicatorIntent(final TurnIndicatorIntent intent, final Length distance)
  1053.         {
  1054.             if (this.indicatorObjectDistance == null || this.indicatorObjectDistance.gt(distance))
  1055.             {
  1056.                 this.indicatorIntent = intent;
  1057.                 this.indicatorObjectDistance = distance;
  1058.             }
  1059.         }

  1060.         /** {@inheritDoc} */
  1061.         @Override
  1062.         public boolean isBlocking()
  1063.         {
  1064.             return this.blocking;
  1065.         }

  1066.         /**
  1067.          * Sets the GTU as blocking conflicts or not.
  1068.          * @param blocking boolean; whether the GTU is blocking conflicts
  1069.          */
  1070.         public void setBlocking(final boolean blocking)
  1071.         {
  1072.             this.blocking = blocking;
  1073.         }

  1074.     }

  1075.     /**
  1076.      * Phases of navigating an all-stop intersection.
  1077.      * <p>
  1078.      * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
  1079.      * <br>
  1080.      * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
  1081.      * <p>
  1082.      * @version $Revision$, $LastChangedDate$, by $Author$, initial version Jun 30, 2016 <br>
  1083.      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
  1084.      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
  1085.      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
  1086.      */
  1087.     private enum StopPhase
  1088.     {
  1089.         /** Approaching stop intersection. */
  1090.         APPROACH,

  1091.         /** Yielding for stop intersection. */
  1092.         YIELD,

  1093.         /** Running over stop intersection. */
  1094.         RUN;
  1095.     }

  1096. }