LaneOperationalPlanBuilder.java

  1. package org.opentrafficsim.road.gtu.lane.plan.operational;

  2. import java.util.ArrayList;
  3. import java.util.Arrays;
  4. import java.util.Iterator;
  5. import java.util.List;
  6. import java.util.Map;

  7. import org.djunits.unit.AccelerationUnit;
  8. import org.djunits.unit.DurationUnit;
  9. import org.djunits.unit.LengthUnit;
  10. import org.djunits.unit.SpeedUnit;
  11. import org.djunits.value.ValueRuntimeException;
  12. import org.djunits.value.vdouble.scalar.Acceleration;
  13. import org.djunits.value.vdouble.scalar.Duration;
  14. import org.djunits.value.vdouble.scalar.Length;
  15. import org.djunits.value.vdouble.scalar.Speed;
  16. import org.djunits.value.vdouble.scalar.Time;
  17. import org.djutils.exceptions.Throw;
  18. import org.djutils.logger.CategoryLogger;
  19. import org.opentrafficsim.base.parameters.ParameterException;
  20. import org.opentrafficsim.core.geometry.OTSGeometryException;
  21. import org.opentrafficsim.core.geometry.OTSLine3D;
  22. import org.opentrafficsim.core.geometry.OTSPoint3D;
  23. import org.opentrafficsim.core.gtu.GTUException;
  24. import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
  25. import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan.Segment;
  26. import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan.SpeedSegment;
  27. import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
  28. import org.opentrafficsim.core.math.Solver;
  29. import org.opentrafficsim.core.network.LateralDirectionality;
  30. import org.opentrafficsim.core.network.NetworkException;
  31. import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
  32. import org.opentrafficsim.road.network.lane.DirectedLanePosition;
  33. import org.opentrafficsim.road.network.lane.Lane;
  34. import org.opentrafficsim.road.network.lane.LaneDirection;
  35. import org.opentrafficsim.road.network.lane.object.sensor.SingleSensor;
  36. import org.opentrafficsim.road.network.lane.object.sensor.SinkSensor;

  37. import nl.tudelft.simulation.dsol.SimRuntimeException;
  38. import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEventInterface;
  39. import nl.tudelft.simulation.dsol.simtime.SimTimeDoubleUnit;
  40. import nl.tudelft.simulation.language.d3.DirectedPoint;

  41. /**
  42.  * Builder for several often used operational plans. E.g., decelerate to come to a full stop at the end of a shape; accelerate
  43.  * to reach a certain speed at the end of a curve; drive constant on a curve; decelerate or accelerate to reach a given end
  44.  * speed at the end of a curve, etc.<br>
  45.  * TODO driving with negative speeds (backward driving) is not yet supported.
  46.  * <p>
  47.  * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
  48.  * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  49.  * </p>
  50.  * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
  51.  * initial version Nov 15, 2015 <br>
  52.  * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
  53.  * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
  54.  */
  55. public final class LaneOperationalPlanBuilder // class package private for scheduling static method on an instance
  56. {

  57.     /** Maximum acceleration for unbounded accelerations: 1E12 m/s2. */
  58.     private static final Acceleration MAX_ACCELERATION = new Acceleration(1E12, AccelerationUnit.SI);

  59.     /** Maximum deceleration for unbounded accelerations: -1E12 m/s2. */
  60.     private static final Acceleration MAX_DECELERATION = new Acceleration(-1E12, AccelerationUnit.SI);

  61.     /**
  62.      * Minimum distance of an operational plan path; anything shorter will be truncated to 0. <br>
  63.      * If objects related to e.g. molecular movements are simulated using this code, a setter for this parameter will be needed.
  64.      */
  65.     private static final Length MINIMUM_CREDIBLE_PATH_LENGTH = new Length(0.001, LengthUnit.METER);

  66.     /** Constructor. */
  67.     LaneOperationalPlanBuilder()
  68.     {
  69.         // class should not be instantiated
  70.     }

  71.     /**
  72.      * Build a plan with a path and a given start speed to try to reach a provided end speed, exactly at the end of the curve.
  73.      * The acceleration (and deceleration) are capped by maxAcceleration and maxDeceleration. Therefore, there is no guarantee
  74.      * that the end speed is actually reached by this plan.
  75.      * @param gtu LaneBasedGTU; the GTU for debugging purposes
  76.      * @param distance Length; distance to drive for reaching the end speed
  77.      * @param startTime Time; the current time or a time in the future when the plan should start
  78.      * @param startSpeed Speed; the speed at the start of the path
  79.      * @param endSpeed Speed; the required end speed
  80.      * @param maxAcceleration Acceleration; the maximum acceleration that can be applied, provided as a POSITIVE number
  81.      * @param maxDeceleration Acceleration; the maximum deceleration that can be applied, provided as a NEGATIVE number
  82.      * @return the operational plan to accomplish the given end speed
  83.      * @throws OperationalPlanException when the plan cannot be generated, e.g. because of a path that is too short
  84.      * @throws OperationalPlanException when the length of the path and the calculated driven distance implied by the
  85.      *             constructed segment list differ more than a given threshold
  86.      * @throws OTSGeometryException in case the lanes are not connected or firstLanePosition is larger than the length of the
  87.      *             first lane
  88.      */
  89.     public static LaneBasedOperationalPlan buildGradualAccelerationPlan(final LaneBasedGTU gtu, final Length distance,
  90.             final Time startTime, final Speed startSpeed, final Speed endSpeed, final Acceleration maxAcceleration,
  91.             final Acceleration maxDeceleration) throws OperationalPlanException, OTSGeometryException
  92.     {
  93.         OTSLine3D path = createPathAlongCenterLine(gtu, distance);
  94.         Segment segment;
  95.         if (startSpeed.eq(endSpeed))
  96.         {
  97.             segment = new SpeedSegment(distance.divide(startSpeed));
  98.         }
  99.         else
  100.         {
  101.             try
  102.             {
  103.                 // t = 2x / (vt + v0); a = (vt - v0) / t
  104.                 Duration duration = distance.times(2.0).divide(endSpeed.plus(startSpeed));
  105.                 Acceleration acceleration = endSpeed.minus(startSpeed).divide(duration);
  106.                 if (acceleration.si < 0.0 && acceleration.lt(maxDeceleration))
  107.                 {
  108.                     acceleration = maxDeceleration;
  109.                     duration = new Duration(Solver.firstSolutionAfter(0, acceleration.si / 2, startSpeed.si, -distance.si),
  110.                             DurationUnit.SI);
  111.                 }
  112.                 if (acceleration.si > 0.0 && acceleration.gt(maxAcceleration))
  113.                 {
  114.                     acceleration = maxAcceleration;
  115.                     duration = new Duration(Solver.firstSolutionAfter(0, acceleration.si / 2, startSpeed.si, -distance.si),
  116.                             DurationUnit.SI);
  117.                 }
  118.                 segment = new OperationalPlan.AccelerationSegment(duration, acceleration);
  119.             }
  120.             catch (ValueRuntimeException ve)
  121.             {
  122.                 throw new OperationalPlanException(ve);
  123.             }
  124.         }
  125.         ArrayList<Segment> segmentList = new ArrayList<>();
  126.         segmentList.add(segment);
  127.         return new LaneBasedOperationalPlan(gtu, path, startTime, startSpeed, segmentList, false);
  128.     }

  129.     /**
  130.      * Build a plan with a path and a given start speed to reach a provided end speed, exactly at the end of the curve.
  131.      * Acceleration and deceleration are virtually unbounded (1E12 m/s2) to reach the end speed (e.g., to come to a complete
  132.      * stop).
  133.      * @param gtu LaneBasedGTU; the GTU for debugging purposes
  134.      * @param distance Length; distance to drive for reaching the end speed
  135.      * @param startTime Time; the current time or a time in the future when the plan should start
  136.      * @param startSpeed Speed; the speed at the start of the path
  137.      * @param endSpeed Speed; the required end speed
  138.      * @return the operational plan to accomplish the given end speed
  139.      * @throws OperationalPlanException when the length of the path and the calculated driven distance implied by the
  140.      *             constructed segment list differ more than a given threshold
  141.      * @throws OTSGeometryException in case the lanes are not connected or firstLanePositiion is larger than the length of the
  142.      *             first lane
  143.      */
  144.     public static LaneBasedOperationalPlan buildGradualAccelerationPlan(final LaneBasedGTU gtu, final Length distance,
  145.             final Time startTime, final Speed startSpeed, final Speed endSpeed)
  146.             throws OperationalPlanException, OTSGeometryException
  147.     {
  148.         return buildGradualAccelerationPlan(gtu, distance, startTime, startSpeed, endSpeed, MAX_ACCELERATION, MAX_DECELERATION);
  149.     }

  150.     /**
  151.      * Build a plan with a path and a given start speed to try to reach a provided end speed. Acceleration or deceleration is as
  152.      * provided, until the end speed is reached. After this, constant end speed is used to reach the end point of the path.
  153.      * There is no guarantee that the end speed is actually reached by this plan. If the end speed is zero, and it is reached
  154.      * before completing the path, a truncated path that ends where the GTU stops is used instead.
  155.      * @param gtu LaneBasedGTU; the GTU for debugging purposes
  156.      * @param distance Length; distance to drive for reaching the end speed
  157.      * @param startTime Time; the current time or a time in the future when the plan should start
  158.      * @param startSpeed Speed; the speed at the start of the path
  159.      * @param endSpeed Speed; the required end speed
  160.      * @param acceleration Acceleration; the acceleration to use if endSpeed &gt; startSpeed, provided as a POSITIVE number
  161.      * @param deceleration Acceleration; the deceleration to use if endSpeed &lt; startSpeed, provided as a NEGATIVE number
  162.      * @return the operational plan to accomplish the given end speed
  163.      * @throws OperationalPlanException when the construction of the operational path fails
  164.      * @throws OTSGeometryException in case the lanes are not connected or firstLanePositiion is larger than the length of the
  165.      *             first lane
  166.      */
  167.     public static LaneBasedOperationalPlan buildMaximumAccelerationPlan(final LaneBasedGTU gtu, final Length distance,
  168.             final Time startTime, final Speed startSpeed, final Speed endSpeed, final Acceleration acceleration,
  169.             final Acceleration deceleration) throws OperationalPlanException, OTSGeometryException
  170.     {
  171.         OTSLine3D path = createPathAlongCenterLine(gtu, distance);
  172.         ArrayList<Segment> segmentList = new ArrayList<>();
  173.         if (startSpeed.eq(endSpeed))
  174.         {
  175.             segmentList.add(new OperationalPlan.SpeedSegment(distance.divide(startSpeed)));
  176.         }
  177.         else
  178.         {
  179.             try
  180.             {
  181.                 if (endSpeed.gt(startSpeed))
  182.                 {
  183.                     Duration t = endSpeed.minus(startSpeed).divide(acceleration);
  184.                     Length x = startSpeed.times(t).plus(acceleration.times(0.5).times(t).times(t));
  185.                     if (x.ge(distance))
  186.                     {
  187.                         // we cannot reach the end speed in the given distance with the given acceleration
  188.                         Duration duration =
  189.                                 new Duration(Solver.firstSolutionAfter(0, acceleration.si / 2, startSpeed.si, -distance.si),
  190.                                         DurationUnit.SI);
  191.                         segmentList.add(new OperationalPlan.AccelerationSegment(duration, acceleration));
  192.                     }
  193.                     else
  194.                     {
  195.                         // we reach the (higher) end speed before the end of the segment. Make two segments.
  196.                         segmentList.add(new OperationalPlan.AccelerationSegment(t, acceleration));
  197.                         Duration duration = distance.minus(x).divide(endSpeed);
  198.                         segmentList.add(new OperationalPlan.SpeedSegment(duration));
  199.                     }
  200.                 }
  201.                 else
  202.                 {
  203.                     Duration t = endSpeed.minus(startSpeed).divide(deceleration);
  204.                     Length x = startSpeed.times(t).plus(deceleration.times(0.5).times(t).times(t));
  205.                     if (x.ge(distance))
  206.                     {
  207.                         // we cannot reach the end speed in the given distance with the given deceleration
  208.                         Duration duration =
  209.                                 new Duration(Solver.firstSolutionAfter(0, deceleration.si / 2, startSpeed.si, -distance.si),
  210.                                         DurationUnit.SI);
  211.                         segmentList.add(new OperationalPlan.AccelerationSegment(duration, deceleration));
  212.                     }
  213.                     else
  214.                     {
  215.                         if (endSpeed.si == 0.0)
  216.                         {
  217.                             // if endSpeed == 0, we cannot reach the end of the path. Therefore, build a partial path.
  218.                             OTSLine3D partialPath = path.truncate(x.si);
  219.                             segmentList.add(new OperationalPlan.AccelerationSegment(t, deceleration));
  220.                             return new LaneBasedOperationalPlan(gtu, partialPath, startTime, startSpeed, segmentList, false);
  221.                         }
  222.                         // we reach the (lower) end speed, larger than zero, before the end of the segment. Make two segments.
  223.                         segmentList.add(new OperationalPlan.AccelerationSegment(t, deceleration));
  224.                         Duration duration = distance.minus(x).divide(endSpeed);
  225.                         segmentList.add(new OperationalPlan.SpeedSegment(duration));
  226.                     }
  227.                 }
  228.             }
  229.             catch (ValueRuntimeException ve)
  230.             {
  231.                 throw new OperationalPlanException(ve);
  232.             }

  233.         }
  234.         return new LaneBasedOperationalPlan(gtu, path, startTime, startSpeed, segmentList, false);
  235.     }

  236.     /**
  237.      * Build a plan with a path and a given start speed to try to reach a provided end speed. Acceleration or deceleration is as
  238.      * provided, until the end speed is reached. After this, constant end speed is used to reach the end point of the path.
  239.      * There is no guarantee that the end speed is actually reached by this plan. If the end speed is zero, and it is reached
  240.      * before completing the path, a truncated path that ends where the GTU stops is used instead.
  241.      * @param gtu LaneBasedGTU; the GTU for debugging purposes
  242.      * @param startTime Time; the current time or a time in the future when the plan should start
  243.      * @param startSpeed Speed; the speed at the start of the path
  244.      * @param acceleration Acceleration; the acceleration to use
  245.      * @param timeStep Duration; time step for the plan
  246.      * @param deviative boolean; whether the plan is deviative
  247.      * @return the operational plan to accomplish the given end speed
  248.      * @throws OperationalPlanException when the construction of the operational path fails
  249.      * @throws OTSGeometryException in case the lanes are not connected or firstLanePositiion is larger than the length of the
  250.      *             first lane
  251.      */
  252.     public static LaneBasedOperationalPlan buildAccelerationPlan(final LaneBasedGTU gtu, final Time startTime,
  253.             final Speed startSpeed, final Acceleration acceleration, final Duration timeStep, final boolean deviative)
  254.             throws OperationalPlanException, OTSGeometryException
  255.     {
  256.         if (startSpeed.si <= OperationalPlan.DRIFTING_SPEED_SI && acceleration.le(Acceleration.ZERO))
  257.         {
  258.             return new LaneBasedOperationalPlan(gtu, gtu.getLocation(), startTime, timeStep, deviative);
  259.         }

  260.         Duration brakingTime = brakingTime(acceleration, startSpeed, timeStep);
  261.         Length distance =
  262.                 Length.instantiateSI(startSpeed.si * brakingTime.si + .5 * acceleration.si * brakingTime.si * brakingTime.si);
  263.         List<Segment> segmentList = createAccelerationSegments(startSpeed, acceleration, brakingTime, timeStep);
  264.         if (distance.le(MINIMUM_CREDIBLE_PATH_LENGTH))
  265.         {
  266.             return new LaneBasedOperationalPlan(gtu, gtu.getLocation(), startTime, timeStep, deviative);
  267.         }
  268.         OTSLine3D path = createPathAlongCenterLine(gtu, distance);
  269.         return new LaneBasedOperationalPlan(gtu, path, startTime, startSpeed, segmentList, deviative);
  270.     }

  271.     /**
  272.      * Creates a path along lane center lines.
  273.      * @param gtu LaneBasedGTU; gtu
  274.      * @param distance Length; minimum distance
  275.      * @return OTSLine3D; path along lane center lines
  276.      * @throws OTSGeometryException when any of the OTSLine3D operations fails
  277.      */
  278.     public static OTSLine3D createPathAlongCenterLine(final LaneBasedGTU gtu, final Length distance) throws OTSGeometryException
  279.     {
  280.         OTSLine3D path = null;
  281.         try
  282.         {
  283.             DirectedLanePosition ref = gtu.getReferencePosition();
  284.             double f = ref.getLane().fraction(ref.getPosition());
  285.             if (ref.getGtuDirection().isPlus() && f < 1.0)
  286.             {
  287.                 if (f >= 0.0)
  288.                 {
  289.                     path = ref.getLane().getCenterLine().extractFractional(f, 1.0);
  290.                 }
  291.                 else
  292.                 {
  293.                     path = ref.getLane().getCenterLine().extractFractional(0.0, 1.0);
  294.                 }
  295.             }
  296.             else if (ref.getGtuDirection().isMinus() && f > 0.0)
  297.             {
  298.                 if (f <= 1.0)
  299.                 {
  300.                     path = ref.getLane().getCenterLine().extractFractional(0.0, f).reverse();
  301.                 }
  302.                 else
  303.                 {
  304.                     path = ref.getLane().getCenterLine().extractFractional(0.0, 1.0).reverse();
  305.                 }
  306.             }
  307.             LaneDirection prevFrom = null;
  308.             LaneDirection from = ref.getLaneDirection();
  309.             int n = 1;
  310.             while (path == null || path.getLength().si < distance.si + n * Lane.MARGIN.si)
  311.             {
  312.                 n++;
  313.                 prevFrom = from;
  314.                 from = from.getNextLaneDirection(gtu);
  315.                 if (from == null)
  316.                 {
  317.                     // check sink sensor
  318.                     Length pos = prevFrom.getDirection().isPlus() ? prevFrom.getLength() : Length.ZERO;
  319.                     for (SingleSensor sensor : prevFrom.getLane().getSensors(pos, pos, gtu.getGTUType(),
  320.                             prevFrom.getDirection()))
  321.                     {
  322.                         // XXX for now, the same is not done for the DestinationSensor (e.g., decrease speed for parking)
  323.                         if (sensor instanceof SinkSensor)
  324.                         {
  325.                             // just add some length so the GTU is happy to go to the sink
  326.                             DirectedPoint end = path.getLocationExtendedSI(distance.si + n * Lane.MARGIN.si);
  327.                             List<OTSPoint3D> points = new ArrayList<>(Arrays.asList(path.getPoints()));
  328.                             points.add(new OTSPoint3D(end));
  329.                             return new OTSLine3D(points);
  330.                         }
  331.                     }
  332.                     CategoryLogger.always().error("GTU {} has nowhere to go and no sink sensor either", gtu);
  333.                     gtu.destroy();
  334.                     return path;
  335.                 }
  336.                 if (path == null)
  337.                 {
  338.                     path = from.getDirection().isPlus() ? from.getLane().getCenterLine()
  339.                             : from.getLane().getCenterLine().reverse();
  340.                 }
  341.                 else
  342.                 {
  343.                     path = OTSLine3D.concatenate(Lane.MARGIN.si, path, from.getDirection().isPlus()
  344.                             ? from.getLane().getCenterLine() : from.getLane().getCenterLine().reverse());
  345.                 }
  346.             }
  347.         }
  348.         catch (GTUException exception)
  349.         {
  350.             throw new RuntimeException("Error during creation of path.", exception);
  351.         }
  352.         return path;
  353.     }

  354.     /**
  355.      * Build a plan with a path and a given start speed to try to reach a provided end speed. Acceleration or deceleration is as
  356.      * provided, until the end speed is reached. After this, constant end speed is used to reach the end point of the path.
  357.      * There is no guarantee that the end speed is actually reached by this plan. If the end speed is zero, and it is reached
  358.      * before completing the path, a truncated path that ends where the GTU stops is used instead.
  359.      * @param gtu LaneBasedGTU; the GTU for debugging purposes
  360.      * @param laneChangeDirectionality LateralDirectionality; direction of lane change (on initiation only, after that not
  361.      *            important)
  362.      * @param startPosition DirectedPoint; current position
  363.      * @param startTime Time; the current time or a time in the future when the plan should start
  364.      * @param startSpeed Speed; the speed at the start of the path
  365.      * @param acceleration Acceleration; the acceleration to use
  366.      * @param timeStep Duration; time step for the plan
  367.      * @param laneChange LaneChange; lane change status
  368.      * @return the operational plan to accomplish the given end speed
  369.      * @throws OperationalPlanException when the construction of the operational path fails
  370.      * @throws OTSGeometryException in case the lanes are not connected or firstLanePositiion is larger than the length of the
  371.      *             first lane
  372.      */
  373.     @SuppressWarnings("checkstyle:parameternumber")
  374.     public static LaneBasedOperationalPlan buildAccelerationLaneChangePlan(final LaneBasedGTU gtu,
  375.             final LateralDirectionality laneChangeDirectionality, final DirectedPoint startPosition, final Time startTime,
  376.             final Speed startSpeed, final Acceleration acceleration, final Duration timeStep, final LaneChange laneChange)
  377.             throws OperationalPlanException, OTSGeometryException
  378.     {

  379.         // on first call during lane change, use laneChangeDirectionality as laneChange.getDirection() is NONE
  380.         // on successive calls, use laneChange.getDirection() as laneChangeDirectionality is NONE (i.e. no LC initiated)
  381.         LateralDirectionality direction = laneChange.isChangingLane() ? laneChange.getDirection() : laneChangeDirectionality;

  382.         Duration brakingTime = brakingTime(acceleration, startSpeed, timeStep);
  383.         Length planDistance =
  384.                 Length.instantiateSI(startSpeed.si * brakingTime.si + .5 * acceleration.si * brakingTime.si * brakingTime.si);
  385.         List<Segment> segmentList = createAccelerationSegments(startSpeed, acceleration, brakingTime, timeStep);

  386.         try
  387.         {
  388.             // get position on from lane
  389.             Map<Lane, Length> positions = gtu.positions(gtu.getReference());
  390.             DirectedLanePosition ref = gtu.getReferencePosition();
  391.             Iterator<Lane> iterator = ref.getLane()
  392.                     .accessibleAdjacentLanesPhysical(direction, gtu.getGTUType(), ref.getGtuDirection()).iterator();
  393.             Lane adjLane = iterator.hasNext() ? iterator.next() : null;
  394.             DirectedLanePosition from = null;
  395.             if (laneChange.getDirection() == null || (adjLane != null && positions.containsKey(adjLane)))
  396.             {
  397.                 // reference lane is from lane, this is ok
  398.                 from = ref;
  399.             }
  400.             else
  401.             {
  402.                 // reference lane is to lane, this should be accounted for
  403.                 for (Lane lane : positions.keySet())
  404.                 {
  405.                     if (lane.accessibleAdjacentLanesPhysical(direction, gtu.getGTUType(), ref.getGtuDirection())
  406.                             .contains(ref.getLane()))
  407.                     {
  408.                         from = new DirectedLanePosition(lane, positions.get(lane), ref.getGtuDirection());
  409.                         break;
  410.                     }
  411.                 }
  412.             }
  413.             Throw.when(from == null, RuntimeException.class, "From lane could not be determined during lane change.");

  414.             // get path and make plan
  415.             OTSLine3D path = laneChange.getPath(timeStep, gtu, from, startPosition, planDistance, direction);
  416.             LaneBasedOperationalPlan plan = new LaneBasedOperationalPlan(gtu, path, startTime, startSpeed, segmentList, true);
  417.             return plan;
  418.         }
  419.         catch (GTUException exception)
  420.         {
  421.             throw new RuntimeException("Error during creation of lane change plan.", exception);
  422.         }
  423.     }

  424.     /**
  425.      * Returns the effective braking time, which stops if stand-still is reached.
  426.      * @param acceleration Acceleration; acceleration
  427.      * @param startSpeed Speed; start speed
  428.      * @param time Duration; intended time step
  429.      * @return Duration; effective braking time
  430.      */
  431.     public static Duration brakingTime(final Acceleration acceleration, final Speed startSpeed, final Duration time)
  432.     {
  433.         if (acceleration.ge0())
  434.         {
  435.             return time;
  436.         }
  437.         double t = startSpeed.si / -acceleration.si;
  438.         if (t >= time.si)
  439.         {
  440.             return time;
  441.         }
  442.         return Duration.instantiateSI(t);
  443.     }

  444.     /**
  445.      * Creates 1 or 2 segments in an operational plan. Two segments are returned of stand-still is reached within the time step.
  446.      * @param startSpeed Speed; start speed
  447.      * @param acceleration Acceleration; acceleration
  448.      * @param brakingTime Duration; braking time until stand-still
  449.      * @param timeStep Duration; time step
  450.      * @return 1 or 2 segments in an operational plan
  451.      */
  452.     private static List<Segment> createAccelerationSegments(final Speed startSpeed, final Acceleration acceleration,
  453.             final Duration brakingTime, final Duration timeStep)
  454.     {
  455.         List<Segment> segmentList = new ArrayList<>();
  456.         if (brakingTime.si < timeStep.si)
  457.         {
  458.             if (brakingTime.si > 0.0)
  459.             {
  460.                 segmentList.add(new OperationalPlan.AccelerationSegment(brakingTime, acceleration));
  461.             }
  462.             segmentList.add(new OperationalPlan.SpeedSegment(timeStep.minus(brakingTime)));
  463.         }
  464.         else
  465.         {
  466.             segmentList.add(new OperationalPlan.AccelerationSegment(timeStep, acceleration));
  467.         }
  468.         return segmentList;
  469.     }

  470.     /**
  471.      * Build an operational plan based on a simple operational plan and status info.
  472.      * @param gtu LaneBasedGTU; gtu
  473.      * @param startTime Time; start time for plan
  474.      * @param simplePlan SimpleOperationalPlan; simple operational plan
  475.      * @param laneChange LaneChange; lane change status
  476.      * @return operational plan
  477.      * @throws ParameterException if parameter is not defined
  478.      * @throws GTUException gtu exception
  479.      * @throws NetworkException network exception
  480.      * @throws OperationalPlanException operational plan exeption
  481.      */
  482.     public static LaneBasedOperationalPlan buildPlanFromSimplePlan(final LaneBasedGTU gtu, final Time startTime,
  483.             final SimpleOperationalPlan simplePlan, final LaneChange laneChange)
  484.             throws ParameterException, GTUException, NetworkException, OperationalPlanException
  485.     {
  486.         Acceleration acc = gtu.getVehicleModel().boundAcceleration(simplePlan.getAcceleration(), gtu);

  487.         if (gtu.isInstantaneousLaneChange())
  488.         {
  489.             if (simplePlan.isLaneChange())
  490.             {
  491.                 gtu.changeLaneInstantaneously(simplePlan.getLaneChangeDirection());
  492.             }
  493.             try
  494.             {
  495.                 return LaneOperationalPlanBuilder.buildAccelerationPlan(gtu, startTime, gtu.getSpeed(), acc,
  496.                         simplePlan.getDuration(), false);
  497.             }
  498.             catch (OTSGeometryException exception)
  499.             {
  500.                 throw new OperationalPlanException(exception);
  501.             }
  502.         }

  503.         // gradual lane change
  504.         try
  505.         {
  506.             if (!simplePlan.isLaneChange() && !laneChange.isChangingLane())
  507.             {
  508.                 return LaneOperationalPlanBuilder.buildAccelerationPlan(gtu, startTime, gtu.getSpeed(), acc,
  509.                         simplePlan.getDuration(), true);
  510.             }
  511.             if (gtu.getSpeed().si == 0.0 && acc.si <= 0.0)
  512.             {
  513.                 return LaneOperationalPlanBuilder.buildAccelerationPlan(gtu, startTime, gtu.getSpeed(), acc,
  514.                         simplePlan.getDuration(), false);
  515.             }
  516.             return LaneOperationalPlanBuilder.buildAccelerationLaneChangePlan(gtu, simplePlan.getLaneChangeDirection(),
  517.                     gtu.getLocation(), startTime, gtu.getSpeed(), acc, simplePlan.getDuration(), laneChange);
  518.         }
  519.         catch (OTSGeometryException exception)
  520.         {
  521.             throw new OperationalPlanException(exception);
  522.         }
  523.     }

  524.     /**
  525.      * Schedules a lane change finalization after the given distance is covered. This distance is known as the plan is created,
  526.      * but at that point no time can be derived as the plan is required for that. Hence, this method can be scheduled at the
  527.      * same time (sequentially after creation of the plan) to then schedule the actual finalization by deriving time from
  528.      * distance with the plan.
  529.      * @param gtu LaneBasedGTU; gtu
  530.      * @param distance Length; distance
  531.      * @param laneChangeDirection LateralDirectionality; lane change direction
  532.      * @throws SimRuntimeException on bad time
  533.      */
  534.     public static void scheduleLaneChangeFinalization(final LaneBasedGTU gtu, final Length distance,
  535.             final LateralDirectionality laneChangeDirection) throws SimRuntimeException
  536.     {
  537.         Time time = gtu.getOperationalPlan().timeAtDistance(distance);
  538.         if (Double.isNaN(time.si))
  539.         {
  540.             // rounding...
  541.             time = gtu.getOperationalPlan().getEndTime();
  542.         }
  543.         SimEventInterface<SimTimeDoubleUnit> event = gtu.getSimulator().scheduleEventAbs(time, (short) 6, gtu, gtu,
  544.                 "finalizeLaneChange", new Object[] { laneChangeDirection });
  545.         gtu.setFinalizeLaneChangeEvent(event);
  546.     }

  547.     /**
  548.      * Build a plan with a path and a given start speed to try to come to a stop with a given deceleration. If the GTU can stop
  549.      * before completing the given path, a truncated path that ends where the GTU stops is used instead. There is no guarantee
  550.      * that the OperationalPlan will lead to a complete stop.
  551.      * @param gtu LaneBasedGTU; the GTU for debugging purposes
  552.      * @param distance Length; distance to drive for reaching the end speed
  553.      * @param startTime Time; the current time or a time in the future when the plan should start
  554.      * @param startSpeed Speed; the speed at the start of the path
  555.      * @param deceleration Acceleration; the deceleration to use if endSpeed &lt; startSpeed, provided as a NEGATIVE number
  556.      * @return the operational plan to accomplish the given end speed
  557.      * @throws OperationalPlanException when construction of the operational path fails
  558.      * @throws OTSGeometryException in case the lanes are not connected or firstLanePositiion is larger than the length of the
  559.      *             first lane
  560.      */
  561.     public static LaneBasedOperationalPlan buildStopPlan(final LaneBasedGTU gtu, final Length distance, final Time startTime,
  562.             final Speed startSpeed, final Acceleration deceleration) throws OperationalPlanException, OTSGeometryException
  563.     {
  564.         return buildMaximumAccelerationPlan(gtu, distance, startTime, startSpeed, new Speed(0.0, SpeedUnit.SI),
  565.                 new Acceleration(1.0, AccelerationUnit.SI), deceleration);
  566.     }

  567. }