View Javadoc
1   package org.opentrafficsim.road.gtu.lane.tactical.util.lmrs;
2   
3   import java.util.LinkedHashSet;
4   import java.util.SortedMap;
5   import java.util.SortedSet;
6   import java.util.TreeMap;
7   
8   import org.djunits.unit.AccelerationUnit;
9   import org.djunits.unit.TimeUnit;
10  import org.djunits.value.vdouble.scalar.Acceleration;
11  import org.djunits.value.vdouble.scalar.Duration;
12  import org.djunits.value.vdouble.scalar.Length;
13  import org.djunits.value.vdouble.scalar.Speed;
14  import org.djunits.value.vdouble.scalar.Time;
15  import org.opentrafficsim.core.gtu.GTUException;
16  import org.opentrafficsim.core.gtu.TurnIndicatorIntent;
17  import org.opentrafficsim.core.gtu.behavioralcharacteristics.BehavioralCharacteristics;
18  import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterException;
19  import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypes;
20  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
21  import org.opentrafficsim.core.network.LateralDirectionality;
22  import org.opentrafficsim.core.network.NetworkException;
23  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
24  import org.opentrafficsim.road.gtu.lane.Break;
25  import org.opentrafficsim.road.gtu.lane.perception.InfrastructureLaneChangeInfo;
26  import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
27  import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
28  import org.opentrafficsim.road.gtu.lane.perception.categories.InfrastructurePerception;
29  import org.opentrafficsim.road.gtu.lane.perception.categories.IntersectionPerception;
30  import org.opentrafficsim.road.gtu.lane.perception.categories.NeighborsPerception;
31  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTU;
32  import org.opentrafficsim.road.gtu.lane.plan.operational.LaneOperationalPlanBuilder.LaneChange;
33  import org.opentrafficsim.road.gtu.lane.plan.operational.SimpleOperationalPlan;
34  import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
35  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.IncentiveGetInLane;
36  import org.opentrafficsim.road.gtu.lane.tactical.util.CarFollowingUtil;
37  import org.opentrafficsim.road.network.speed.SpeedLimitInfo;
38  import org.opentrafficsim.road.network.speed.SpeedLimitProspect;
39  
40  /**
41   * <p>
42   * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
43   * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
44   * <p>
45   * @version $Revision$, $LastChangedDate$, by $Author$, initial version Jul 26, 2016 <br>
46   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
47   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
48   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
49   */
50  public final class LmrsUtil implements LmrsParameters
51  {
52  
53      /**
54       * Do not instantiate.
55       */
56      private LmrsUtil()
57      {
58          //
59      }
60  
61      /**
62       * Determines a simple representation of an operational plan.
63       * @param gtu gtu
64       * @param startTime start time
65       * @param carFollowingModel car-following model
66       * @param laneChange lane change status
67       * @param lmrsData LMRS data
68       * @param perception perception
69       * @param mandatoryIncentives set of mandatory lane change incentives
70       * @param voluntaryIncentives set of voluntary lane change incentives
71       * @return simple operational plan
72       * @throws GTUException gtu exception
73       * @throws NetworkException network exception
74       * @throws ParameterException parameter exception
75       * @throws OperationalPlanException operational plan exception
76       */
77      @SuppressWarnings("checkstyle:parameternumber")
78      public static SimpleOperationalPlan determinePlan(final LaneBasedGTU gtu, final Time startTime,
79              final CarFollowingModel carFollowingModel, final LaneChange laneChange, final LmrsData lmrsData,
80              final LanePerception perception, final LinkedHashSet<MandatoryIncentive> mandatoryIncentives,
81              final LinkedHashSet<VoluntaryIncentive> voluntaryIncentives)
82              throws GTUException, NetworkException, ParameterException, OperationalPlanException
83      {
84  
85          // TODO this is a hack to prevent right lane changes of all vehicles on the left lane when placed in network at t=0
86          if (startTime.si == 0.0)
87          {
88              return new SimpleOperationalPlan(Acceleration.ZERO, LateralDirectionality.NONE);
89          }
90  
91          // obtain objects to get info
92          SpeedLimitProspect slp =
93                  perception.getPerceptionCategory(InfrastructurePerception.class).getSpeedLimitProspect(RelativeLane.CURRENT);
94          SpeedLimitInfo sli = slp.getSpeedLimitInfo(Length.ZERO);
95          BehavioralCharacteristics bc = gtu.getBehavioralCharacteristics();
96  
97          // regular car-following
98          Speed speed = gtu.getSpeed();
99          SortedSet<HeadwayGTU> leaders =
100                 perception.getPerceptionCategory(NeighborsPerception.class).getLeaders(RelativeLane.CURRENT);
101         if (!leaders.isEmpty() && lmrsData.isNewLeader(leaders.first()))
102         {
103             initHeadwayRelaxation(bc, leaders.first());
104         }
105         Acceleration a;
106         CarFollowingModel regularFollowing = carFollowingModel;
107         SortedSet<HeadwayGTU> followers =
108                 perception.getPerceptionCategory(NeighborsPerception.class).getFollowers(RelativeLane.CURRENT);
109         if (!followers.isEmpty())
110         {
111             HeadwayGTU follower = followers.first();
112             Speed desiredSpeedFollower = follower.getCarFollowingModel().desiredSpeed(follower.getBehavioralCharacteristics(),
113                     follower.getSpeedLimitInfo());
114             Speed desiredSpeed = carFollowingModel.desiredSpeed(bc, sli);
115             if (desiredSpeed.lt(desiredSpeedFollower))
116             {
117                 // wrap car-following model with adjusted desired speed
118                 CarFollowingModel carFollowingModelWrapped = new CarFollowingModelWrapper(carFollowingModel,
119                         Speed.interpolate(desiredSpeed, desiredSpeedFollower, bc.getParameter(HIERARCHY)));
120                 a = CarFollowingUtil.followLeaders(carFollowingModelWrapped, bc, speed, sli, leaders);
121                 // remember this wrapper for when following the second leader during a lane change
122                 regularFollowing = carFollowingModelWrapped;
123             }
124             else
125             {
126                 a = CarFollowingUtil.followLeaders(carFollowingModel, bc, speed, sli, leaders);
127             }
128         }
129         else
130         {
131             a = CarFollowingUtil.followLeaders(carFollowingModel, bc, speed, sli, leaders);
132         }
133 
134         // stop for end
135         // TODO remove or move, should not be needed with new form of synchronization
136         Length remainingDist = null;
137         for (InfrastructureLaneChangeInfo ili : perception.getPerceptionCategory(InfrastructurePerception.class)
138                 .getInfrastructureLaneChangeInfo(RelativeLane.CURRENT))
139         {
140             if (remainingDist == null || remainingDist.gt(ili.getRemainingDistance()))
141             {
142                 remainingDist = ili.getRemainingDistance();
143             }
144         }
145         if (remainingDist != null)
146         {
147             Acceleration bCrit = bc.getParameter(ParameterTypes.BCRIT);
148             remainingDist = remainingDist.minus(bc.getParameter(ParameterTypes.S0)).minus(gtu.getFront().getDx());
149             if (remainingDist.le0())
150             {
151                 if (speed.gt0())
152                 {
153                     a = Acceleration.min(a, bCrit.neg());
154                 }
155                 else
156                 {
157                     a = Acceleration.min(a, Acceleration.ZERO);
158                 }
159             }
160             else
161             {
162                 Acceleration bMin = new Acceleration(.5 * speed.si * speed.si / remainingDist.si, AccelerationUnit.SI);
163                 if (bMin.ge(bCrit))
164                 {
165                     a = Acceleration.min(a, bMin.neg());
166                 }
167             }
168         }
169 
170         // during a lane change, both leaders are followed
171         LateralDirectionality initiatedLaneChange;
172         TurnIndicatorIntent turnIndicatorStatus = TurnIndicatorIntent.NONE;
173         if (laneChange.isChangingLane())
174         {
175             RelativeLane secondLane = laneChange.getSecondLane(gtu);
176             initiatedLaneChange = LateralDirectionality.NONE;
177             SortedSet<HeadwayGTU> secondLeaders =
178                     perception.getPerceptionCategory(NeighborsPerception.class).getLeaders(secondLane);
179             Acceleration aSecond = CarFollowingUtil.followLeaders(regularFollowing, bc, speed, sli, secondLeaders);
180             if (!secondLeaders.isEmpty() && lmrsData.isNewLeader(secondLeaders.first()))
181             {
182                 initHeadwayRelaxation(bc, secondLeaders.first());
183             }
184             a = Acceleration.min(a, aSecond);
185         }
186         else
187         {
188 
189             // determine lane change desire based on incentives
190             Desire desire = getLaneChangeDesire(bc, perception, carFollowingModel, mandatoryIncentives, voluntaryIncentives);
191 
192             // gap acceptance
193             boolean acceptLeft =
194                     acceptGap(perception, bc, sli, carFollowingModel, desire.getLeft(), speed, LateralDirectionality.LEFT);
195             boolean acceptRight =
196                     acceptGap(perception, bc, sli, carFollowingModel, desire.getRight(), speed, LateralDirectionality.RIGHT);
197 
198             // lane change decision
199             double dFree = bc.getParameter(DFREE);
200             double dSync = bc.getParameter(DSYNC);
201             double dCoop = bc.getParameter(DCOOP);
202             // decide
203 
204             if (desire.leftIsLargerOrEqual() && desire.getLeft() >= dFree && acceptLeft)
205             {
206                 // change left
207                 initiatedLaneChange = LateralDirectionality.LEFT;
208                 turnIndicatorStatus = TurnIndicatorIntent.LEFT;
209                 bc.setParameter(DLC, desire.getLeft());
210                 setDesiredHeadway(bc, desire.getLeft());
211                 leaders = perception.getPerceptionCategory(NeighborsPerception.class).getLeaders(RelativeLane.LEFT);
212                 if (!leaders.isEmpty())
213                 {
214                     // don't respond on its lane change desire, but remember it such that it isn't a new leader in the next step
215                     lmrsData.isNewLeader(leaders.first());
216                 }
217                 a = Acceleration.min(a, CarFollowingUtil.followLeaders(regularFollowing, bc, speed, sli,
218                         perception.getPerceptionCategory(NeighborsPerception.class).getLeaders(RelativeLane.LEFT)));
219             }
220             else if (!desire.leftIsLargerOrEqual() && desire.getRight() >= dFree && acceptRight)
221             {
222                 // change right
223                 initiatedLaneChange = LateralDirectionality.RIGHT;
224                 turnIndicatorStatus = TurnIndicatorIntent.RIGHT;
225                 bc.setParameter(DLC, desire.getRight());
226                 setDesiredHeadway(bc, desire.getRight());
227                 leaders = perception.getPerceptionCategory(NeighborsPerception.class).getLeaders(RelativeLane.RIGHT);
228                 if (!leaders.isEmpty())
229                 {
230                     // don't respond on its lane change desire, but remember it such that it isn't a new leader in the next step
231                     lmrsData.isNewLeader(leaders.first());
232                 }
233                 a = Acceleration.min(a, CarFollowingUtil.followLeaders(regularFollowing, bc, speed, sli,
234                         perception.getPerceptionCategory(NeighborsPerception.class).getLeaders(RelativeLane.RIGHT)));
235             }
236             else
237             {
238                 initiatedLaneChange = LateralDirectionality.NONE;
239                 turnIndicatorStatus = TurnIndicatorIntent.NONE;
240             }
241             laneChange.setLaneChangeDuration(gtu.getBehavioralCharacteristics().getParameter(ParameterTypes.LCDUR));
242 
243             // take action if we cannot change lane
244             Acceleration aSync;
245             if (initiatedLaneChange.equals(LateralDirectionality.NONE))
246             {
247                 // synchronize
248                 if (desire.leftIsLargerOrEqual() && desire.getLeft() >= dSync)
249                 {
250                     aSync = lmrsData.getSynchronization().synchronize(perception, bc, sli, carFollowingModel, desire.getLeft(),
251                             LateralDirectionality.LEFT, lmrsData);
252                     a = Acceleration.min(a, aSync);
253                 }
254                 else if (!desire.leftIsLargerOrEqual() && desire.getRight() >= dSync)
255                 {
256                     aSync = lmrsData.getSynchronization().synchronize(perception, bc, sli, carFollowingModel, desire.getRight(),
257                             LateralDirectionality.RIGHT, lmrsData);
258                     a = Acceleration.min(a, aSync);
259                 }
260                 // use indicators to indicate lane change need
261                 if (desire.leftIsLargerOrEqual() && desire.getLeft() >= dCoop)
262                 {
263                     // switch on left indicator
264                     turnIndicatorStatus = TurnIndicatorIntent.LEFT;
265                 }
266                 else if (!desire.leftIsLargerOrEqual() && desire.getRight() >= dCoop)
267                 {
268                     // switch on right indicator
269                     turnIndicatorStatus = TurnIndicatorIntent.RIGHT;
270                 }
271                 bc.setParameter(DLEFT, desire.getLeft());
272                 bc.setParameter(DRIGHT, desire.getRight());
273             }
274             else
275             {
276                 bc.setParameter(DLEFT, 0.0);
277                 bc.setParameter(DRIGHT, 0.0);
278             }
279 
280             // cooperate
281             aSync = lmrsData.getSynchronization().cooperate(perception, bc, sli, carFollowingModel, LateralDirectionality.LEFT);
282             a = Acceleration.min(a, aSync);
283             aSync = lmrsData.getSynchronization().cooperate(perception, bc, sli, carFollowingModel,
284                     LateralDirectionality.RIGHT);
285             a = Acceleration.min(a, aSync);
286 
287             // relaxation
288             exponentialHeadwayRelaxation(bc);
289 
290         }
291         lmrsData.finalizeStep();
292 
293         SimpleOperationalPlan simplePlan = new SimpleOperationalPlan(a, initiatedLaneChange);
294         if (turnIndicatorStatus.isLeft())
295         {
296             simplePlan.setIndicatorIntentLeft();
297         }
298         else if (turnIndicatorStatus.isRight())
299         {
300             simplePlan.setIndicatorIntentRight();
301         }
302         return simplePlan;
303 
304     }
305 
306     /**
307      * Sets the headway as a response to a new leader.
308      * @param bc behavioral characteristics
309      * @param leader leader
310      * @throws ParameterException if DLC is not present
311      */
312     private static void initHeadwayRelaxation(final BehavioralCharacteristics bc, final HeadwayGTU leader)
313             throws ParameterException
314     {
315         if (leader.getBehavioralCharacteristics().contains(DLC))
316         {
317             setDesiredHeadway(bc, leader.getBehavioralCharacteristics().getParameter(DLC));
318         }
319         // else could not be perceived
320     }
321 
322     /**
323      * Updates the desired headway following an exponential shape approximated with fixed time step <tt>DT</tt>.
324      * @param bc behavioral characteristics
325      * @throws ParameterException in case of a parameter exception
326      */
327     private static void exponentialHeadwayRelaxation(final BehavioralCharacteristics bc) throws ParameterException
328     {
329         double ratio = bc.getParameter(ParameterTypes.DT).si / bc.getParameter(ParameterTypes.TAU).si;
330         bc.setParameter(ParameterTypes.T, Duration.interpolate(bc.getParameter(ParameterTypes.T),
331                 bc.getParameter(ParameterTypes.TMAX), ratio <= 1.0 ? ratio : 1.0));
332     }
333 
334     /**
335      * Determines lane change desire for the given RSU. Mandatory desire is deduced as the maximum of a set of mandatory
336      * incentives, while voluntary desires are added. Depending on the level of mandatory lane change desire, voluntary desire
337      * may be included partially. If both are positive or negative, voluntary desire is fully included. Otherwise, voluntary
338      * desire is less considered within the range dSync &lt; |mandatory| &lt; dCoop. The absolute value is used as large
339      * negative mandatory desire may also dominate voluntary desire.
340      * @param behavioralCharacteristics behavioral characteristics
341      * @param perception perception
342      * @param carFollowingModel car-following model
343      * @param mandatoryIncentives mandatory incentives
344      * @param voluntaryIncentives voluntary incentives
345      * @return lane change desire for gtu
346      * @throws ParameterException if a parameter is not defined
347      * @throws GTUException if there is no mandatory incentive, the model requires at least one
348      * @throws OperationalPlanException perception exception
349      */
350     private static Desire getLaneChangeDesire(final BehavioralCharacteristics behavioralCharacteristics,
351             final LanePerception perception, final CarFollowingModel carFollowingModel,
352             final LinkedHashSet<MandatoryIncentive> mandatoryIncentives,
353             final LinkedHashSet<VoluntaryIncentive> voluntaryIncentives)
354             throws ParameterException, GTUException, OperationalPlanException
355     {
356 
357         double dSync = behavioralCharacteristics.getParameter(DSYNC);
358         double dCoop = behavioralCharacteristics.getParameter(DCOOP);
359 
360         // Mandatory desire
361         double dLeftMandatory = 0.0;
362         double dRightMandatory = 0.0;
363         Desire mandatoryDesire = new Desire(dLeftMandatory, dRightMandatory);
364         for (MandatoryIncentive incentive : mandatoryIncentives)
365         {
366             Desire d = incentive.determineDesire(behavioralCharacteristics, perception, carFollowingModel, mandatoryDesire);
367             if (incentive instanceof IncentiveGetInLane && d.getLeft() >= 1.0 && d.getRight() >= 1.0)
368             {
369                 incentive.determineDesire(behavioralCharacteristics, perception, carFollowingModel, mandatoryDesire);
370             }
371             dLeftMandatory = Math.abs(d.getLeft()) > Math.abs(dLeftMandatory) ? d.getLeft() : dLeftMandatory;
372             dRightMandatory = Math.abs(d.getRight()) > Math.abs(dRightMandatory) ? d.getRight() : dRightMandatory;
373             mandatoryDesire = new Desire(dLeftMandatory, dRightMandatory);
374         }
375 
376         // Voluntary desire
377         double dLeftVoluntary = 0;
378         double dRightVoluntary = 0;
379         Desire voluntaryDesire = new Desire(dLeftVoluntary, dRightVoluntary);
380         for (VoluntaryIncentive incentive : voluntaryIncentives)
381         {
382             Desire d = incentive.determineDesire(behavioralCharacteristics, perception, carFollowingModel, mandatoryDesire,
383                     voluntaryDesire);
384             dLeftVoluntary += d.getLeft();
385             dRightVoluntary += d.getRight();
386             voluntaryDesire = new Desire(dLeftVoluntary, dRightVoluntary);
387         }
388 
389         // Total desire
390         double thetaLeft = 0;
391         if (dLeftMandatory <= dSync || dLeftMandatory * dLeftVoluntary >= 0)
392         {
393             // low mandatory desire, or same sign
394             thetaLeft = 1;
395         }
396         else if (dSync < dLeftMandatory && dLeftMandatory < dCoop && dLeftMandatory * dLeftVoluntary < 0)
397         {
398             // linear from 1 at dSync to 0 at dCoop
399             thetaLeft = (dCoop - Math.abs(dLeftMandatory)) / (dCoop - dSync);
400         }
401         double thetaRight = 0;
402         if (dRightMandatory <= dSync || dRightMandatory * dRightVoluntary >= 0)
403         {
404             // low mandatory desire, or same sign
405             thetaRight = 1;
406         }
407         else if (dSync < dRightMandatory && dRightMandatory < dCoop && dRightMandatory * dRightVoluntary < 0)
408         {
409             // linear from 1 at dSync to 0 at dCoop
410             thetaRight = (dCoop - Math.abs(dRightMandatory)) / (dCoop - dSync);
411         }
412 
413         return new Desire(dLeftMandatory + thetaLeft * dLeftVoluntary, dRightMandatory + thetaRight * dRightVoluntary);
414 
415     }
416 
417     /**
418      * Determine whether a gap is acceptable.
419      * @param perception perception
420      * @param bc behavioral characteristics
421      * @param sli speed limit info
422      * @param cfm car-following model
423      * @param desire level of lane change desire
424      * @param ownSpeed own speed
425      * @param lat lateral direction for synchronization
426      * @return whether a gap is acceptable
427      * @throws ParameterException if a parameter is not defined
428      * @throws OperationalPlanException perception exception
429      */
430     private static boolean acceptGap(final LanePerception perception, final BehavioralCharacteristics bc,
431             final SpeedLimitInfo sli, final CarFollowingModel cfm, final double desire, final Speed ownSpeed,
432             final LateralDirectionality lat) throws ParameterException, OperationalPlanException
433     {
434 
435         // check whether there is a lane
436         if (!perception.getLaneStructure().canChange(lat, perception))
437         {
438             return false;
439         }
440 
441         // legal?
442         if (perception.getPerceptionCategory(InfrastructurePerception.class).getLegalLaneChangePossibility(RelativeLane.CURRENT,
443                 lat).si <= 0.0)
444         {
445             return false;
446         }
447 
448         // conflicts alongside?
449         if (perception.contains(IntersectionPerception.class))
450         {
451             if ((lat.isLeft() && perception.getPerceptionCategory(IntersectionPerception.class).isAlongsideConflictLeft())
452                     || (lat.isRight()
453                             && perception.getPerceptionCategory(IntersectionPerception.class).isAlongsideConflictRight()))
454             {
455                 return false;
456             }
457         }
458 
459         // safe regarding neighbors?
460         if (perception.getPerceptionCategory(NeighborsPerception.class).isGtuAlongside(lat))
461         {
462             // gtu alongside
463             return false;
464         }
465 
466         Acceleration b = bc.getParameter(ParameterTypes.B);
467         Acceleration aFollow = new Acceleration(Double.POSITIVE_INFINITY, AccelerationUnit.SI);
468         for (
469 
470         HeadwayGTU follower : perception.getPerceptionCategory(NeighborsPerception.class).getFirstFollowers(lat))
471         {
472             if (follower.getSpeed().gt0())
473             {
474                 Acceleration a = singleAcceleration(follower.getDistance(), follower.getSpeed(), ownSpeed, desire,
475                         follower.getBehavioralCharacteristics(), follower.getSpeedLimitInfo(), follower.getCarFollowingModel());
476                 aFollow = Acceleration.min(aFollow, a);
477             }
478         }
479 
480         Acceleration aSelf = new Acceleration(Double.POSITIVE_INFINITY, AccelerationUnit.SI);
481         if (ownSpeed.gt0())
482         {
483             for (
484 
485             HeadwayGTU leader : perception.getPerceptionCategory(NeighborsPerception.class).getFirstLeaders(lat))
486             {
487                 Acceleration a = singleAcceleration(leader.getDistance(), ownSpeed, leader.getSpeed(), desire, bc, sli, cfm);
488                 aSelf = Acceleration.min(aSelf, a);
489             }
490         }
491 
492         Acceleration threshold = b.multiplyBy(-desire);
493         return aFollow.ge(threshold) && aSelf.ge(threshold);
494     }
495 
496     /**
497      * Sets value for T depending on level of lane change desire.
498      * @param bc behavioral characteristics
499      * @param desire lane change desire
500      * @throws ParameterException if T, TMIN or TMAX is not in the behavioral characteristics
501      */
502     static void setDesiredHeadway(final BehavioralCharacteristics bc, final double desire) throws ParameterException
503     {
504         double limitedDesire = desire < 0 ? 0 : desire > 1 ? 1 : desire;
505         double tDes = limitedDesire * bc.getParameter(ParameterTypes.TMIN).si
506                 + (1 - limitedDesire) * bc.getParameter(ParameterTypes.TMAX).si;
507         double t = bc.getParameter(ParameterTypes.T).si;
508         bc.setParameter(ParameterTypes.T, new Duration(tDes < t ? tDes : t, TimeUnit.SI));
509     }
510 
511     /**
512      * Resets value for T depending on level of lane change desire.
513      * @param bc behavioral characteristics
514      * @throws ParameterException if T is not in the behavioral characteristics
515      */
516     static void resetDesiredHeadway(final BehavioralCharacteristics bc) throws ParameterException
517     {
518         bc.resetParameter(ParameterTypes.T);
519     }
520 
521     /**
522      * Determine acceleration from car-following.
523      * @param distance distance from follower to leader
524      * @param followerSpeed speed of follower
525      * @param leaderSpeed speed of leader
526      * @param desire level of lane change desire
527      * @param bc behavioral characteristics
528      * @param sli speed limit info
529      * @param cfm car-following model
530      * @return acceleration from car-following
531      * @throws ParameterException if a parameter is not defined
532      */
533     static Acceleration singleAcceleration(final Length distance, final Speed followerSpeed, final Speed leaderSpeed,
534             final double desire, final BehavioralCharacteristics bc, final SpeedLimitInfo sli, final CarFollowingModel cfm)
535             throws ParameterException
536     {
537         // set T
538         setDesiredHeadway(bc, desire);
539         // calculate acceleration
540         SortedMap<Length, Speed> leaders = new TreeMap<>();
541         leaders.put(distance, leaderSpeed);
542         Acceleration a = cfm.followingAcceleration(bc, followerSpeed, sli, leaders);
543         // reset T
544         resetDesiredHeadway(bc);
545         return a;
546     }
547 
548     /**
549      * Wrapper for car-following model to adjust desired speed.
550      * <p>
551      * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
552      * <br>
553      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
554      * <p>
555      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 3 apr. 2017 <br>
556      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
557      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
558      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
559      */
560     private static final class CarFollowingModelWrapper implements CarFollowingModel
561     {
562 
563         /** Wrapped car-following model. */
564         private final CarFollowingModel carFollowingModel;
565 
566         /** Desired speed. */
567         private final Speed desiredSpeed;
568 
569         /**
570          * @param carFollowingModel car-following model
571          * @param desiredSpeed desired speed
572          */
573         CarFollowingModelWrapper(final CarFollowingModel carFollowingModel, final Speed desiredSpeed)
574         {
575             this.carFollowingModel = carFollowingModel;
576             this.desiredSpeed = desiredSpeed;
577         }
578 
579         /** {@inheritDoc} */
580         @Override
581         public Speed desiredSpeed(final BehavioralCharacteristics behavioralCharacteristics, final SpeedLimitInfo speedInfo)
582                 throws ParameterException
583         {
584             return this.desiredSpeed;
585         }
586 
587         /** {@inheritDoc} */
588         @Override
589         public Length desiredHeadway(final BehavioralCharacteristics behavioralCharacteristics, final Speed speed)
590                 throws ParameterException
591         {
592             return this.carFollowingModel.desiredHeadway(behavioralCharacteristics, speed);
593         }
594 
595         /** {@inheritDoc} */
596         @Override
597         public Acceleration followingAcceleration(final BehavioralCharacteristics behavioralCharacteristics, final Speed speed,
598                 final SpeedLimitInfo speedLimitInfo, final SortedMap<Length, Speed> leaders) throws ParameterException
599         {
600             return this.carFollowingModel.followingAcceleration(behavioralCharacteristics, speed, speedLimitInfo, leaders);
601         }
602 
603         /** {@inheritDoc} */
604         @Override
605         public String getName()
606         {
607             return this.carFollowingModel.getName();
608         }
609 
610         /** {@inheritDoc} */
611         @Override
612         public String getLongName()
613         {
614             return this.carFollowingModel.getLongName();
615         }
616 
617     }
618 
619 }