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