View Javadoc
1   package org.opentrafficsim.road.gtu.lane.plan.operational;
2   
3   import java.util.ArrayList;
4   import java.util.Arrays;
5   import java.util.Iterator;
6   import java.util.List;
7   import java.util.Map;
8   
9   import org.djunits.unit.AccelerationUnit;
10  import org.djunits.unit.DurationUnit;
11  import org.djunits.unit.LengthUnit;
12  import org.djunits.unit.SpeedUnit;
13  import org.djunits.value.ValueException;
14  import org.djunits.value.vdouble.scalar.Acceleration;
15  import org.djunits.value.vdouble.scalar.Duration;
16  import org.djunits.value.vdouble.scalar.Length;
17  import org.djunits.value.vdouble.scalar.Speed;
18  import org.djunits.value.vdouble.scalar.Time;
19  import org.djutils.exceptions.Throw;
20  import org.opentrafficsim.base.parameters.ParameterException;
21  import org.opentrafficsim.core.geometry.OTSGeometryException;
22  import org.opentrafficsim.core.geometry.OTSLine3D;
23  import org.opentrafficsim.core.geometry.OTSPoint3D;
24  import org.opentrafficsim.core.gtu.GTUException;
25  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
26  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan.Segment;
27  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan.SpeedSegment;
28  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
29  import org.opentrafficsim.core.math.Solver;
30  import org.opentrafficsim.core.network.LateralDirectionality;
31  import org.opentrafficsim.core.network.NetworkException;
32  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
33  import org.opentrafficsim.road.network.lane.DirectedLanePosition;
34  import org.opentrafficsim.road.network.lane.Lane;
35  import org.opentrafficsim.road.network.lane.LaneDirection;
36  import org.opentrafficsim.road.network.lane.object.sensor.SingleSensor;
37  import org.opentrafficsim.road.network.lane.object.sensor.SinkSensor;
38  
39  import nl.tudelft.simulation.dsol.SimRuntimeException;
40  import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEventInterface;
41  import nl.tudelft.simulation.dsol.simtime.SimTimeDoubleUnit;
42  import nl.tudelft.simulation.language.d3.DirectedPoint;
43  
44  /**
45   * Builder for several often used operational plans. E.g., decelerate to come to a full stop at the end of a shape; accelerate
46   * to reach a certain speed at the end of a curve; drive constant on a curve; decelerate or accelerate to reach a given end
47   * speed at the end of a curve, etc.<br>
48   * TODO driving with negative speeds (backward driving) is not yet supported.
49   * <p>
50   * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
51   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
52   * </p>
53   * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
54   * initial version Nov 15, 2015 <br>
55   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
56   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
57   */
58  public final class LaneOperationalPlanBuilder // class package private for scheduling static method on an instance
59  {
60  
61      /** Use instant lane changes. */
62      public static boolean INSTANT_LANE_CHANGES = true;
63  
64      /** Maximum acceleration for unbounded accelerations: 1E12 m/s2. */
65      private static final Acceleration MAX_ACCELERATION = new Acceleration(1E12, AccelerationUnit.SI);
66  
67      /** Maximum deceleration for unbounded accelerations: -1E12 m/s2. */
68      private static final Acceleration MAX_DECELERATION = new Acceleration(-1E12, AccelerationUnit.SI);
69  
70      /**
71       * Minimum distance of an operational plan path; anything shorter will be truncated to 0. <br>
72       * If objects related to e.g. molecular movements are simulated using this code, a setter for this parameter will be needed.
73       */
74      private static final Length MINIMUM_CREDIBLE_PATH_LENGTH = new Length(0.001, LengthUnit.METER);
75  
76      /** Constructor. */
77      LaneOperationalPlanBuilder()
78      {
79          // class should not be instantiated
80      }
81  
82      /**
83       * 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.
84       * The acceleration (and deceleration) are capped by maxAcceleration and maxDeceleration. Therefore, there is no guarantee
85       * that the end speed is actually reached by this plan.
86       * @param gtu LaneBasedGTU; the GTU for debugging purposes
87       * @param distance Length; distance to drive for reaching the end speed
88       * @param startTime Time; the current time or a time in the future when the plan should start
89       * @param startSpeed Speed; the speed at the start of the path
90       * @param endSpeed Speed; the required end speed
91       * @param maxAcceleration Acceleration; the maximum acceleration that can be applied, provided as a POSITIVE number
92       * @param maxDeceleration Acceleration; the maximum deceleration that can be applied, provided as a NEGATIVE number
93       * @return the operational plan to accomplish the given end speed
94       * @throws OperationalPlanException when the plan cannot be generated, e.g. because of a path that is too short
95       * @throws OperationalPlanException when the length of the path and the calculated driven distance implied by the
96       *             constructed segment list differ more than a given threshold
97       * @throws OTSGeometryException in case the lanes are not connected or firstLanePosition is larger than the length of the
98       *             first lane
99       */
100     public static LaneBasedOperationalPlan buildGradualAccelerationPlan(final LaneBasedGTU gtu, final Length distance,
101             final Time startTime, final Speed startSpeed, final Speed endSpeed, final Acceleration maxAcceleration,
102             final Acceleration maxDeceleration) throws OperationalPlanException, OTSGeometryException
103     {
104         OTSLine3D path = createPathAlongCenterLine(gtu, distance);
105         Segment segment;
106         if (startSpeed.eq(endSpeed))
107         {
108             segment = new SpeedSegment(distance.divideBy(startSpeed));
109         }
110         else
111         {
112             try
113             {
114                 // t = 2x / (vt + v0); a = (vt - v0) / t
115                 Duration duration = distance.multiplyBy(2.0).divideBy(endSpeed.plus(startSpeed));
116                 Acceleration acceleration = endSpeed.minus(startSpeed).divideBy(duration);
117                 if (acceleration.si < 0.0 && acceleration.lt(maxDeceleration))
118                 {
119                     acceleration = maxDeceleration;
120                     duration = new Duration(Solver.firstSolutionAfter(0, acceleration.si / 2, startSpeed.si, -distance.si),
121                             DurationUnit.SI);
122                 }
123                 if (acceleration.si > 0.0 && acceleration.gt(maxAcceleration))
124                 {
125                     acceleration = maxAcceleration;
126                     duration = new Duration(Solver.firstSolutionAfter(0, acceleration.si / 2, startSpeed.si, -distance.si),
127                             DurationUnit.SI);
128                 }
129                 segment = new OperationalPlan.AccelerationSegment(duration, acceleration);
130             }
131             catch (ValueException ve)
132             {
133                 throw new OperationalPlanException(ve);
134             }
135         }
136         ArrayList<Segment> segmentList = new ArrayList<>();
137         segmentList.add(segment);
138         return new LaneBasedOperationalPlan(gtu, path, startTime, startSpeed, segmentList, false);
139     }
140 
141     /**
142      * Build a plan with a path and a given start speed to reach a provided end speed, exactly at the end of the curve.
143      * Acceleration and deceleration are virtually unbounded (1E12 m/s2) to reach the end speed (e.g., to come to a complete
144      * stop).
145      * @param gtu LaneBasedGTU; the GTU for debugging purposes
146      * @param distance Length; distance to drive for reaching the end speed
147      * @param startTime Time; the current time or a time in the future when the plan should start
148      * @param startSpeed Speed; the speed at the start of the path
149      * @param endSpeed Speed; the required end speed
150      * @return the operational plan to accomplish the given end speed
151      * @throws OperationalPlanException when the length of the path and the calculated driven distance implied by the
152      *             constructed segment list differ more than a given threshold
153      * @throws OTSGeometryException in case the lanes are not connected or firstLanePositiion is larger than the length of the
154      *             first lane
155      */
156     public static LaneBasedOperationalPlan buildGradualAccelerationPlan(final LaneBasedGTU gtu, final Length distance,
157             final Time startTime, final Speed startSpeed, final Speed endSpeed)
158             throws OperationalPlanException, OTSGeometryException
159     {
160         return buildGradualAccelerationPlan(gtu, distance, startTime, startSpeed, endSpeed, MAX_ACCELERATION, MAX_DECELERATION);
161     }
162 
163     /**
164      * Build a plan with a path and a given start speed to try to reach a provided end speed. Acceleration or deceleration is as
165      * provided, until the end speed is reached. After this, constant end speed is used to reach the end point of the path.
166      * There is no guarantee that the end speed is actually reached by this plan. If the end speed is zero, and it is reached
167      * before completing the path, a truncated path that ends where the GTU stops is used instead.
168      * @param gtu LaneBasedGTU; the GTU for debugging purposes
169      * @param distance Length; distance to drive for reaching the end speed
170      * @param startTime Time; the current time or a time in the future when the plan should start
171      * @param startSpeed Speed; the speed at the start of the path
172      * @param endSpeed Speed; the required end speed
173      * @param acceleration Acceleration; the acceleration to use if endSpeed &gt; startSpeed, provided as a POSITIVE number
174      * @param deceleration Acceleration; the deceleration to use if endSpeed &lt; startSpeed, provided as a NEGATIVE number
175      * @return the operational plan to accomplish the given end speed
176      * @throws OperationalPlanException when the construction of the operational path fails
177      * @throws OTSGeometryException in case the lanes are not connected or firstLanePositiion is larger than the length of the
178      *             first lane
179      */
180     public static LaneBasedOperationalPlan buildMaximumAccelerationPlan(final LaneBasedGTU gtu, final Length distance,
181             final Time startTime, final Speed startSpeed, final Speed endSpeed, final Acceleration acceleration,
182             final Acceleration deceleration) throws OperationalPlanException, OTSGeometryException
183     {
184         OTSLine3D path = createPathAlongCenterLine(gtu, distance);
185         ArrayList<Segment> segmentList = new ArrayList<>();
186         if (startSpeed.eq(endSpeed))
187         {
188             segmentList.add(new OperationalPlan.SpeedSegment(distance.divideBy(startSpeed)));
189         }
190         else
191         {
192             try
193             {
194                 if (endSpeed.gt(startSpeed))
195                 {
196                     Duration t = endSpeed.minus(startSpeed).divideBy(acceleration);
197                     Length x = startSpeed.multiplyBy(t).plus(acceleration.multiplyBy(0.5).multiplyBy(t).multiplyBy(t));
198                     if (x.ge(distance))
199                     {
200                         // we cannot reach the end speed in the given distance with the given acceleration
201                         Duration duration =
202                                 new Duration(Solver.firstSolutionAfter(0, acceleration.si / 2, startSpeed.si, -distance.si),
203                                         DurationUnit.SI);
204                         segmentList.add(new OperationalPlan.AccelerationSegment(duration, acceleration));
205                     }
206                     else
207                     {
208                         // we reach the (higher) end speed before the end of the segment. Make two segments.
209                         segmentList.add(new OperationalPlan.AccelerationSegment(t, acceleration));
210                         Duration duration = distance.minus(x).divideBy(endSpeed);
211                         segmentList.add(new OperationalPlan.SpeedSegment(duration));
212                     }
213                 }
214                 else
215                 {
216                     Duration t = endSpeed.minus(startSpeed).divideBy(deceleration);
217                     Length x = startSpeed.multiplyBy(t).plus(deceleration.multiplyBy(0.5).multiplyBy(t).multiplyBy(t));
218                     if (x.ge(distance))
219                     {
220                         // we cannot reach the end speed in the given distance with the given deceleration
221                         Duration duration =
222                                 new Duration(Solver.firstSolutionAfter(0, deceleration.si / 2, startSpeed.si, -distance.si),
223                                         DurationUnit.SI);
224                         segmentList.add(new OperationalPlan.AccelerationSegment(duration, deceleration));
225                     }
226                     else
227                     {
228                         if (endSpeed.si == 0.0)
229                         {
230                             // if endSpeed == 0, we cannot reach the end of the path. Therefore, build a partial path.
231                             OTSLine3D partialPath = path.truncate(x.si);
232                             segmentList.add(new OperationalPlan.AccelerationSegment(t, deceleration));
233                             return new LaneBasedOperationalPlan(gtu, partialPath, startTime, startSpeed, segmentList, false);
234                         }
235                         // we reach the (lower) end speed, larger than zero, before the end of the segment. Make two segments.
236                         segmentList.add(new OperationalPlan.AccelerationSegment(t, deceleration));
237                         Duration duration = distance.minus(x).divideBy(endSpeed);
238                         segmentList.add(new OperationalPlan.SpeedSegment(duration));
239                     }
240                 }
241             }
242             catch (ValueException ve)
243             {
244                 throw new OperationalPlanException(ve);
245             }
246 
247         }
248         return new LaneBasedOperationalPlan(gtu, path, startTime, startSpeed, segmentList, false);
249     }
250 
251     /**
252      * Build a plan with a path and a given start speed to try to reach a provided end speed. Acceleration or deceleration is as
253      * provided, until the end speed is reached. After this, constant end speed is used to reach the end point of the path.
254      * There is no guarantee that the end speed is actually reached by this plan. If the end speed is zero, and it is reached
255      * before completing the path, a truncated path that ends where the GTU stops is used instead.
256      * @param gtu LaneBasedGTU; the GTU for debugging purposes
257      * @param startTime Time; the current time or a time in the future when the plan should start
258      * @param startSpeed Speed; the speed at the start of the path
259      * @param acceleration Acceleration; the acceleration to use
260      * @param timeStep Duration; time step for the plan
261      * @return the operational plan to accomplish the given end speed
262      * @throws OperationalPlanException when the construction of the operational path fails
263      * @throws OTSGeometryException in case the lanes are not connected or firstLanePositiion is larger than the length of the
264      *             first lane
265      */
266     public static LaneBasedOperationalPlan buildAccelerationPlan(final LaneBasedGTU gtu, final Time startTime,
267             final Speed startSpeed, final Acceleration acceleration, final Duration timeStep)
268             throws OperationalPlanException, OTSGeometryException
269     {
270         if (startSpeed.si <= OperationalPlan.DRIFTING_SPEED_SI && acceleration.le(Acceleration.ZERO))
271         {
272             return new LaneBasedOperationalPlan(gtu, gtu.getLocation(), startTime, timeStep, false);
273         }
274 
275         Acceleration acc = gtuCapabilities(acceleration, gtu);
276         Duration brakingTime = brakingTime(acc, startSpeed, timeStep);
277         Length distance = Length.createSI(startSpeed.si * brakingTime.si + .5 * acc.si * brakingTime.si * brakingTime.si);
278         List<Segment> segmentList = createAccelerationSegments(startSpeed, acc, brakingTime, timeStep);
279         if (distance.le(MINIMUM_CREDIBLE_PATH_LENGTH))
280         {
281             return new LaneBasedOperationalPlan(gtu, gtu.getLocation(), startTime, timeStep, false);
282         }
283         OTSLine3D path = createPathAlongCenterLine(gtu, distance);
284         return new LaneBasedOperationalPlan(gtu, path, startTime, startSpeed, segmentList, false);
285     }
286 
287     /**
288      * Creates a path along lane center lines.
289      * @param gtu LaneBasedGTU; gtu
290      * @param distance Length; minimum distance
291      * @return OTSLine3D; path along lane center lines
292      * @throws OTSGeometryException when any of the OTSLine3D operations fails
293      */
294     public static OTSLine3D createPathAlongCenterLine(final LaneBasedGTU gtu, final Length distance) throws OTSGeometryException
295     {
296         OTSLine3D path = null;
297         try
298         {
299             DirectedLanePosition ref = gtu.getReferencePosition();
300             double f = ref.getLane().fraction(ref.getPosition());
301             if (ref.getGtuDirection().isPlus() && f < 1.0)
302             {
303                 path = ref.getLane().getCenterLine().extractFractional(f, 1.0);
304             }
305             else if (ref.getGtuDirection().isMinus() && f > 0.0)
306             {
307                 path = ref.getLane().getCenterLine().extractFractional(0.0, f).reverse();
308             }
309             LaneDirection prevFrom = null;
310             LaneDirection from = ref.getLaneDirection();
311             int n = 1;
312             while (path == null || path.getLength().si < distance.si + n * Lane.MARGIN.si)
313             {
314                 n++;
315                 prevFrom = from;
316                 from = from.getNextLaneDirection(gtu);
317                 if (from == null)
318                 {
319                     // check sink sensor
320                     Length pos = prevFrom.getDirection().isPlus() ? prevFrom.getLength() : Length.ZERO;
321                     for (SingleSensor sensor : prevFrom.getLane().getSensors(pos, pos, gtu.getGTUType(),
322                             prevFrom.getDirection()))
323                     {
324                         if (sensor instanceof SinkSensor)
325                         {
326                             // just add some length so the GTU is happy to go to the sink
327                             DirectedPoint end = path.getLocationExtendedSI(distance.si + n * Lane.MARGIN.si);
328                             List<OTSPoint3D> points = new ArrayList<>(Arrays.asList(path.getPoints()));
329                             points.add(new OTSPoint3D(end));
330                             return new OTSLine3D(points);
331                         }
332                     }
333                 }
334                 if (path == null)
335                 {
336                     path = from.getDirection().isPlus() ? from.getLane().getCenterLine()
337                             : from.getLane().getCenterLine().reverse();
338                 }
339                 else
340                 {
341                     path = OTSLine3D.concatenate(Lane.MARGIN.si, path, from.getDirection().isPlus()
342                             ? from.getLane().getCenterLine() : from.getLane().getCenterLine().reverse());
343                 }
344             }
345         }
346         catch (GTUException exception)
347         {
348             throw new RuntimeException("Error during creation of path.", exception);
349         }
350         return path;
351     }
352 
353     /**
354      * Build a plan with a path and a given start speed to try to reach a provided end speed. Acceleration or deceleration is as
355      * provided, until the end speed is reached. After this, constant end speed is used to reach the end point of the path.
356      * There is no guarantee that the end speed is actually reached by this plan. If the end speed is zero, and it is reached
357      * before completing the path, a truncated path that ends where the GTU stops is used instead.
358      * @param gtu LaneBasedGTU; the GTU for debugging purposes
359      * @param laneChangeDirectionality LateralDirectionality; direction of lane change (on initiation only, after that not
360      *            important)
361      * @param startPosition DirectedPoint; current position
362      * @param startTime Time; the current time or a time in the future when the plan should start
363      * @param startSpeed Speed; the speed at the start of the path
364      * @param acceleration Acceleration; the acceleration to use
365      * @param timeStep Duration; time step for the plan
366      * @param laneChange LaneChange; lane change status
367      * @return the operational plan to accomplish the given end speed
368      * @throws OperationalPlanException when the construction of the operational path fails
369      * @throws OTSGeometryException in case the lanes are not connected or firstLanePositiion is larger than the length of the
370      *             first lane
371      */
372     @SuppressWarnings("checkstyle:parameternumber")
373     public static LaneBasedOperationalPlan buildAccelerationLaneChangePlan(final LaneBasedGTU gtu,
374             final LateralDirectionality laneChangeDirectionality, final DirectedPoint startPosition, final Time startTime,
375             final Speed startSpeed, final Acceleration acceleration, final Duration timeStep, final LaneChange laneChange)
376             throws OperationalPlanException, OTSGeometryException
377     {
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 
383         Duration brakingTime = brakingTime(acceleration, startSpeed, timeStep);
384         Length planDistance =
385                 Length.createSI(startSpeed.si * brakingTime.si + .5 * acceleration.si * brakingTime.si * brakingTime.si);
386         List<Segment> segmentList = createAccelerationSegments(startSpeed, acceleration, brakingTime, timeStep);
387 
388         try
389         {
390             // get position on from lane
391             Map<Lane, Length> positions = gtu.positions(gtu.getReference());
392             DirectedLanePosition ref = gtu.getReferencePosition();
393             Iterator<Lane> iterator = ref.getLane()
394                     .accessibleAdjacentLanesPhysical(direction, gtu.getGTUType(), ref.getGtuDirection()).iterator();
395             Lane adjLane = iterator.hasNext() ? iterator.next() : null;
396             DirectedLanePosition from = null;
397             if (laneChange.getDirection() == null || (adjLane != null && positions.containsKey(adjLane)))
398             {
399                 // reference lane is from lane, this is ok
400                 from = ref;
401             }
402             else
403             {
404                 // reference lane is to lane, this should be accounted for
405                 for (Lane lane : positions.keySet())
406                 {
407                     if (lane.accessibleAdjacentLanesPhysical(direction, gtu.getGTUType(), ref.getGtuDirection())
408                             .contains(ref.getLane()))
409                     {
410                         from = new DirectedLanePosition(lane, positions.get(lane), ref.getGtuDirection());
411                         break;
412                     }
413                 }
414             }
415             Throw.when(from == null, RuntimeException.class, "From lane could not be determined during lane change.");
416 
417             // get path and make plan
418             OTSLine3D path = laneChange.getPath(timeStep, gtu, from, startPosition, planDistance, direction);
419             LaneBasedOperationalPlan plan = new LaneBasedOperationalPlan(gtu, path, startTime, startSpeed, segmentList, true);
420             return plan;
421         }
422         catch (GTUException exception)
423         {
424             throw new RuntimeException("Error during creation of lane change plan.", exception);
425         }
426     }
427 
428     /**
429      * Makes the acceleration adhere to GTU capabilities.
430      * @param acceleration Acceleration; desired acceleration
431      * @param gtu LaneBasedGTU; gtu
432      * @return Acceleration; possible acceleration
433      */
434     private static Acceleration gtuCapabilities(final Acceleration acceleration, final LaneBasedGTU gtu)
435     {
436         return acceleration; // .gt(gtu.getMaximumDeceleration())
437         // ? (acceleration.lt(gtu.getMaximumAcceleration()) ? acceleration : gtu.getMaximumAcceleration())
438         // : gtu.getMaximumDeceleration();
439     }
440 
441     /**
442      * Returns the effective braking time, which stops if stand-still is reached.
443      * @param acceleration Acceleration; acceleration
444      * @param startSpeed Speed; start speed
445      * @param time Duration; intended time step
446      * @return Duration; effective braking time
447      */
448     public static Duration brakingTime(final Acceleration acceleration, final Speed startSpeed, final Duration time)
449     {
450         if (acceleration.ge0())
451         {
452             return time;
453         }
454         double t = startSpeed.si / -acceleration.si;
455         if (t >= time.si)
456         {
457             return time;
458         }
459         return Duration.createSI(t);
460     }
461 
462     /**
463      * Creates 1 or 2 segments in an operational plan. Two segments are returned of stand-still is reached within the time step.
464      * @param startSpeed Speed; start speed
465      * @param acceleration Acceleration; acceleration
466      * @param brakingTime Duration; braking time until stand-still
467      * @param timeStep Duration; time step
468      * @return 1 or 2 segments in an operational plan
469      */
470     private static List<Segment> createAccelerationSegments(final Speed startSpeed, final Acceleration acceleration,
471             final Duration brakingTime, final Duration timeStep)
472     {
473         List<Segment> segmentList = new ArrayList<>();
474         if (brakingTime.si < timeStep.si)
475         {
476             if (brakingTime.si > 0.0)
477             {
478                 segmentList.add(new OperationalPlan.AccelerationSegment(brakingTime, acceleration));
479             }
480             segmentList.add(new OperationalPlan.SpeedSegment(timeStep.minus(brakingTime)));
481         }
482         else
483         {
484             segmentList.add(new OperationalPlan.AccelerationSegment(timeStep, acceleration));
485         }
486         return segmentList;
487     }
488 
489     /**
490      * Build an operational plan based on a simple operational plan and status info.
491      * @param gtu LaneBasedGTU; gtu
492      * @param startTime Time; start time for plan
493      * @param simplePlan SimpleOperationalPlan; simple operational plan
494      * @param laneChange LaneChange; lane change status
495      * @return operational plan
496      * @throws ParameterException if parameter is not defined
497      * @throws GTUException gtu exception
498      * @throws NetworkException network exception
499      * @throws OperationalPlanException operational plan exeption
500      */
501     public static LaneBasedOperationalPlan buildPlanFromSimplePlan(final LaneBasedGTU gtu, final Time startTime,
502             final SimpleOperationalPlan simplePlan, final LaneChange laneChange)
503             throws ParameterException, GTUException, NetworkException, OperationalPlanException
504     {
505         Acceleration acc = gtu.getVehicleModel().boundAcceleration(simplePlan.getAcceleration(), gtu);
506 
507         if (INSTANT_LANE_CHANGES)
508         {
509             if (simplePlan.isLaneChange())
510             {
511                 gtu.changeLaneInstantaneously(simplePlan.getLaneChangeDirection());
512             }
513             try
514             {
515                 return LaneOperationalPlanBuilder.buildAccelerationPlan(gtu, startTime, gtu.getSpeed(), acc,
516                         simplePlan.getDuration());
517             }
518             catch (OTSGeometryException exception)
519             {
520                 throw new OperationalPlanException(exception);
521             }
522         }
523 
524         // gradual lane change
525         try
526         {
527             if ((!simplePlan.isLaneChange() && !laneChange.isChangingLane()) || (gtu.getSpeed().si == 0.0 && acc.si <= 0.0))
528             {
529                 return LaneOperationalPlanBuilder.buildAccelerationPlan(gtu, startTime, gtu.getSpeed(), acc,
530                         simplePlan.getDuration());
531             }
532             return LaneOperationalPlanBuilder.buildAccelerationLaneChangePlan(gtu, simplePlan.getLaneChangeDirection(),
533                     gtu.getLocation(), startTime, gtu.getSpeed(), acc, simplePlan.getDuration(), laneChange);
534         }
535         catch (OTSGeometryException exception)
536         {
537             throw new OperationalPlanException(exception);
538         }
539     }
540 
541     /**
542      * Schedules a lane change finalization after the given distance is covered. This distance is known as the plan is created,
543      * but at that point no time can be derived as the plan is required for that. Hence, this method can be scheduled at the
544      * same time (sequentially after creation of the plan) to then schedule the actual finalization by deriving time from
545      * distance with the plan.
546      * @param gtu LaneBasedGTU; gtu
547      * @param distance Length; distance
548      * @param laneChangeDirection LateralDirectionality; lane change direction
549      * @throws SimRuntimeException on bad time
550      */
551     public static void scheduleLaneChangeFinalization(final LaneBasedGTU gtu, final Length distance,
552             final LateralDirectionality laneChangeDirection) throws SimRuntimeException
553     {
554         Time time = gtu.getOperationalPlan().timeAtDistance(distance);
555         if (Double.isNaN(time.si))
556         {
557             // rounding...
558             time = gtu.getOperationalPlan().getEndTime();
559         }
560         SimEventInterface<SimTimeDoubleUnit> event = gtu.getSimulator().scheduleEventAbs(time, (short) 6, gtu, gtu,
561                 "finalizeLaneChange", new Object[] { laneChangeDirection });
562         gtu.setFinalizeLaneChangeEvent(event);
563     }
564 
565     /**
566      * 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
567      * before completing the given path, a truncated path that ends where the GTU stops is used instead. There is no guarantee
568      * that the OperationalPlan will lead to a complete stop.
569      * @param gtu LaneBasedGTU; the GTU for debugging purposes
570      * @param distance Length; distance to drive for reaching the end speed
571      * @param startTime Time; the current time or a time in the future when the plan should start
572      * @param startSpeed Speed; the speed at the start of the path
573      * @param deceleration Acceleration; the deceleration to use if endSpeed &lt; startSpeed, provided as a NEGATIVE number
574      * @return the operational plan to accomplish the given end speed
575      * @throws OperationalPlanException when construction of the operational path fails
576      * @throws OTSGeometryException in case the lanes are not connected or firstLanePositiion is larger than the length of the
577      *             first lane
578      */
579     public static LaneBasedOperationalPlan buildStopPlan(final LaneBasedGTU gtu, final Length distance, final Time startTime,
580             final Speed startSpeed, final Acceleration deceleration) throws OperationalPlanException, OTSGeometryException
581     {
582         return buildMaximumAccelerationPlan(gtu, distance, startTime, startSpeed, new Speed(0.0, SpeedUnit.SI),
583                 new Acceleration(1.0, AccelerationUnit.SI), deceleration);
584     }
585 
586 }