View Javadoc
1   package org.opentrafficsim.road.gtu.lane.tactical.util.lmrs;
2   
3   import static org.opentrafficsim.core.gtu.behavioralcharacteristics.AbstractParameterType.Check.UNITINTERVAL;
4   
5   import java.io.Serializable;
6   import java.util.HashSet;
7   import java.util.LinkedHashSet;
8   import java.util.Set;
9   import java.util.SortedMap;
10  import java.util.TreeMap;
11  
12  import org.djunits.unit.AccelerationUnit;
13  import org.djunits.value.vdouble.scalar.Acceleration;
14  import org.djunits.value.vdouble.scalar.Duration;
15  import org.djunits.value.vdouble.scalar.Length;
16  import org.djunits.value.vdouble.scalar.Speed;
17  import org.djunits.value.vdouble.scalar.Time;
18  import org.opentrafficsim.core.gtu.GTUException;
19  import org.opentrafficsim.core.gtu.TurnIndicatorStatus;
20  import org.opentrafficsim.core.gtu.behavioralcharacteristics.BehavioralCharacteristics;
21  import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterException;
22  import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypeDouble;
23  import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypes;
24  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
25  import org.opentrafficsim.core.network.LateralDirectionality;
26  import org.opentrafficsim.core.network.NetworkException;
27  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
28  import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
29  import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
30  import org.opentrafficsim.road.gtu.lane.perception.categories.InfrastructurePerception;
31  import org.opentrafficsim.road.gtu.lane.perception.categories.NeighborsPerception;
32  import org.opentrafficsim.road.gtu.lane.perception.headway.AbstractHeadwayGTU;
33  import org.opentrafficsim.road.gtu.lane.plan.operational.LaneOperationalPlanBuilder.LaneChange;
34  import org.opentrafficsim.road.gtu.lane.plan.operational.SimpleOperationalPlan;
35  import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
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  import nl.tudelft.simulation.language.Throw;
41  
42  /**
43   * <p>
44   * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
45   * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
46   * <p>
47   * @version $Revision$, $LastChangedDate$, by $Author$, initial version Jul 26, 2016 <br>
48   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
49   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
50   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
51   */
52  public final class LmrsUtil
53  {
54  
55      /** Free lane change desire threshold. */
56      public static final ParameterTypeDouble DFREE =
57              new ParameterTypeDouble("dFree", "Free lane change desire threshold.", 0.365, UNITINTERVAL)
58              {
59                  /** */
60                  private static final long serialVersionUID = 20160413L;
61  
62                  public void check(final double value, final BehavioralCharacteristics bc) throws ParameterException
63                  {
64                      if (bc.contains(DSYNC))
65                      {
66                          Throw.when(value >= bc.getParameter(DSYNC), ParameterException.class,
67                                  "Value of dFree is above or equal to dSync.");
68                      }
69                      if (bc.contains(DCOOP))
70                      {
71                          Throw.when(value >= bc.getParameter(DCOOP), ParameterException.class,
72                                  "Value of dFree is above or equal to dCoop.");
73                      }
74                  }
75              };
76  
77      /** Synchronized lane change desire threshold. */
78      public static final ParameterTypeDouble DSYNC =
79              new ParameterTypeDouble("dSync", "Synchronized lane change desire threshold.", 0.577, UNITINTERVAL)
80              {
81                  /** */
82                  private static final long serialVersionUID = 20160413L;
83  
84                  public void check(final double value, final BehavioralCharacteristics bc) throws ParameterException
85                  {
86                      if (bc.contains(DFREE))
87                      {
88                          Throw.when(value <= bc.getParameter(DFREE), ParameterException.class,
89                                  "Value of dSync is below or equal to dFree.");
90                      }
91                      if (bc.contains(DCOOP))
92                      {
93                          Throw.when(value >= bc.getParameter(DCOOP), ParameterException.class,
94                                  "Value of dSync is above or equal to dCoop.");
95                      }
96                  }
97              };
98  
99      /** Cooperative lane change desire threshold. */
100     public static final ParameterTypeDouble DCOOP =
101             new ParameterTypeDouble("dCoop", "Cooperative lane change desire threshold.", 0.788, UNITINTERVAL)
102             {
103                 /** */
104                 private static final long serialVersionUID = 20160413L;
105 
106                 public void check(final double value, final BehavioralCharacteristics bc) throws ParameterException
107                 {
108                     if (bc.contains(DFREE))
109                     {
110                         Throw.when(value <= bc.getParameter(DFREE), ParameterException.class,
111                                 "Value of dCoop is below or equal to dFree.");
112                     }
113                     if (bc.contains(DSYNC))
114                     {
115                         Throw.when(value <= bc.getParameter(DSYNC), ParameterException.class,
116                                 "Value of dCoop is below or equal to dSync.");
117                     }
118                 }
119             };
120 
121     /** Current left lane change desire. */
122     public static final ParameterTypeDouble DLEFT = new ParameterTypeDouble("dLeft", "Left lane change desire.", 0);
123 
124     /** Current right lane change desire. */
125     public static final ParameterTypeDouble DRIGHT = new ParameterTypeDouble("dLeft", "Left lane change desire.", 0);
126 
127     /**
128      * Do not instantiate.
129      */
130     private LmrsUtil()
131     {
132         //
133     }
134 
135     /**
136      * Determines a simple representation of an operational plan.
137      * @param gtu gtu
138      * @param startTime start time
139      * @param lmrsStatus LMRS status
140      * @param carFollowingModel car-following model
141      * @param laneChange lane change status
142      * @param perception perception
143      * @param mandatoryIncentives set of mandatory lane change incentives
144      * @param voluntaryIncentives set of voluntary lane change incentives
145      * @return simple operational plan
146      * @throws GTUException gtu exception
147      * @throws NetworkException network exception
148      * @throws ParameterException parameter exception
149      * @throws OperationalPlanException operational plan exception
150      */
151     @SuppressWarnings("checkstyle:parameternumber")
152     public static SimpleOperationalPlan determinePlan(final LaneBasedGTU gtu, final Time startTime, final LmrsStatus lmrsStatus,
153             final CarFollowingModel carFollowingModel, final LaneChange laneChange, final LanePerception perception,
154             final LinkedHashSet<MandatoryIncentive> mandatoryIncentives,
155             final LinkedHashSet<VoluntaryIncentive> voluntaryIncentives)
156             throws GTUException, NetworkException, ParameterException, OperationalPlanException
157     {
158 
159         // obtain objects to get info
160         SpeedLimitProspect slp =
161                 perception.getPerceptionCategory(InfrastructurePerception.class).getSpeedLimitProspect(RelativeLane.CURRENT);
162         SpeedLimitInfo sli = slp.getSpeedLimitInfo(Length.ZERO);
163         BehavioralCharacteristics bc = gtu.getBehavioralCharacteristics();
164 
165         // update T as response to new leader
166         Set<AbstractHeadwayGTU> leaders =
167                 perception.getPerceptionCategory(NeighborsPerception.class).getLeaders(RelativeLane.CURRENT);
168         for (AbstractHeadwayGTU leader : leaders)
169         {
170             if (!lmrsStatus.getLastLeaders().contains(leader))
171             {
172                 // TODO updated headway based on (estimated) desire of other vehicle
173                 // uses current headway, this results in larger deceleration than using the desire
174                 // we require a desire where T = desire*Tmin + (1-desire)*Tmax gives a deceleration of b*desire
175                 //
176                 // idea: acceleration for actual T
177                 // acceleration for Tmin
178                 // interpolate appropriately (which is... ? )
179                 // Duration tLead = Duration.max(bc.getParameter(ParameterTypes.TMIN),
180                 // leader.getDistance().minus(bc.getParameter(ParameterTypes.S0)).divideBy(gtu.getSpeed()));
181                 // if (tLead.lt(bc.getParameter(ParameterTypes.T)))
182                 // {
183                 // bc.setParameter(ParameterTypes.T, tLead);
184                 // }
185             }
186         }
187         lmrsStatus.setLastLeaders(leaders);
188 
189         // regular car-following
190         Speed speed = gtu.getSpeed();
191         Acceleration a = CarFollowingUtil.followLeaders(carFollowingModel, bc, speed, sli,
192                 perception.getPerceptionCategory(NeighborsPerception.class).getLeaders(RelativeLane.CURRENT));
193 
194         // during a lane change, both leaders are followed
195         LateralDirectionality initiatedLaneChange;
196         if (laneChange.isChangingLane())
197         {
198             // RelativeLane tar = this.laneChangeDirectionality.isLeft() ? RelativeLane.LEFT : RelativeLane.RIGHT;
199             RelativeLane tar = laneChange.getTargetLane();
200             initiatedLaneChange = tar.getLateralDirectionality();
201             Acceleration aTar = CarFollowingUtil.followLeaders(carFollowingModel, bc, speed, sli,
202                     perception.getPerceptionCategory(NeighborsPerception.class).getLeaders(tar));
203             a = Acceleration.min(a, aTar);
204 
205         }
206         else
207         {
208             // relaxation
209             exponentialHeadwayRelaxation(bc);
210 
211             // determine lane change desire based on incentives
212             Desire desire = getLaneChangeDesire(bc, perception, carFollowingModel, mandatoryIncentives, voluntaryIncentives);
213 
214             // gap acceptance
215             boolean acceptLeft = perception.getLaneStructure().getCrossSection().contains(RelativeLane.LEFT)
216                     && acceptGap(perception, bc, sli, carFollowingModel, desire.getLeft(), speed, LateralDirectionality.LEFT);
217             boolean acceptRight = perception.getLaneStructure().getCrossSection().contains(RelativeLane.RIGHT)
218                     && acceptGap(perception, bc, sli, carFollowingModel, desire.getRight(), speed, LateralDirectionality.RIGHT);
219 
220             // lane change decision
221             double dFree = bc.getParameter(DFREE);
222             double dSync = bc.getParameter(DSYNC);
223             double dCoop = bc.getParameter(DCOOP);
224             // decide
225             TurnIndicatorStatus turnIndicatorStatus;
226             if (desire.leftIsLargerOrEqual() && desire.getLeft() >= dFree && acceptLeft)
227             {
228                 // change left
229                 initiatedLaneChange = LateralDirectionality.LEFT;
230                 turnIndicatorStatus = TurnIndicatorStatus.LEFT;
231                 Duration tRel = Duration.interpolate(bc.getParameter(ParameterTypes.TMAX), bc.getParameter(ParameterTypes.TMIN),
232                         desire.getLeft());
233                 if (tRel.lt(bc.getParameter(ParameterTypes.T)))
234                 {
235                     bc.setParameter(ParameterTypes.T, tRel);
236                 }
237                 // TODO headway of other driver...
238             }
239             else if (!desire.leftIsLargerOrEqual() && desire.getRight() >= dFree && acceptRight)
240             {
241                 // change right
242                 initiatedLaneChange = LateralDirectionality.RIGHT;
243                 turnIndicatorStatus = TurnIndicatorStatus.RIGHT;
244                 Duration tRel = Duration.interpolate(bc.getParameter(ParameterTypes.TMAX), bc.getParameter(ParameterTypes.TMIN),
245                         desire.getRight());
246                 if (tRel.lt(bc.getParameter(ParameterTypes.T)))
247                 {
248                     bc.setParameter(ParameterTypes.T, tRel);
249                 }
250                 // TODO headway of other driver...
251             }
252             else
253             {
254                 initiatedLaneChange = null;
255                 turnIndicatorStatus = TurnIndicatorStatus.NONE;
256             }
257             laneChange.setLaneChangeDuration(gtu.getBehavioralCharacteristics().getParameter(ParameterTypes.LCDUR));
258 
259             // take action if we cannot change lane
260             Acceleration aSync;
261             if (initiatedLaneChange == null)
262             {
263                 // synchronize
264                 if (desire.leftIsLargerOrEqual() && desire.getLeft() >= dSync)
265                 {
266                     aSync = synchronize(perception, bc, sli, carFollowingModel, desire.getLeft(), speed,
267                             LateralDirectionality.LEFT);
268                     a = Acceleration.min(a, aSync);
269                 }
270                 else if (!desire.leftIsLargerOrEqual() && desire.getRight() >= dSync)
271                 {
272                     aSync = synchronize(perception, bc, sli, carFollowingModel, desire.getRight(), speed,
273                             LateralDirectionality.RIGHT);
274                     a = Acceleration.min(a, aSync);
275                 }
276                 // use indicators to indicate lane change need
277                 if (desire.leftIsLargerOrEqual() && desire.getLeft() >= dCoop)
278                 {
279                     // switch on left indicator
280                     turnIndicatorStatus = TurnIndicatorStatus.LEFT;
281                 }
282                 else if (!desire.leftIsLargerOrEqual() && desire.getRight() >= dCoop)
283                 {
284                     // switch on right indicator
285                     turnIndicatorStatus = TurnIndicatorStatus.RIGHT;
286                 }
287             }
288             gtu.setTurnIndicatorStatus(turnIndicatorStatus);
289 
290             // cooperate
291             aSync = cooperate(perception, bc, sli, carFollowingModel, speed, LateralDirectionality.LEFT);
292             a = Acceleration.min(a, aSync);
293             aSync = cooperate(perception, bc, sli, carFollowingModel, speed, LateralDirectionality.RIGHT);
294             a = Acceleration.min(a, aSync);
295 
296         }
297 
298         return new SimpleOperationalPlan(a, initiatedLaneChange);
299 
300     }
301 
302     /**
303      * Updates the desired headway following an exponential shape approximated with fixed time step <tt>DT</tt>.
304      * @param bc Behavioral characteristics.
305      * @throws ParameterException In case of a parameter exception.
306      */
307     private static void exponentialHeadwayRelaxation(final BehavioralCharacteristics bc) throws ParameterException
308     {
309         double ratio = bc.getParameter(ParameterTypes.DT).si / bc.getParameter(ParameterTypes.TAU).si;
310         bc.setParameter(ParameterTypes.T, Duration.interpolate(bc.getParameter(ParameterTypes.T),
311                 bc.getParameter(ParameterTypes.TMAX), ratio <= 1.0 ? ratio : 1.0));
312     }
313 
314     /**
315      * Determines lane change desire for the given RSU. Mandatory desire is deduced as the maximum of a set of mandatory
316      * incentives, while voluntary desires are added. Depending on the level of mandatory lane change desire, voluntary desire
317      * may be included partially. If both are positive or negative, voluntary desire is fully included. Otherwise, voluntary
318      * desire is less considered within the range dSync &lt; |mandatory| &lt; dCoop. The absolute value is used as large
319      * negative mandatory desire may also dominate voluntary desire.
320      * @param behavioralCharacteristics behavioral characteristics
321      * @param perception perception
322      * @param carFollowingModel car-following model
323      * @param mandatoryIncentives mandatory incentives
324      * @param voluntaryIncentives voluntary incentives
325      * @return lane change desire for gtu
326      * @throws ParameterException if a parameter is not defined
327      * @throws GTUException if there is no mandatory incentive, the model requires at least one
328      * @throws OperationalPlanException perception exception
329      */
330     private static Desire getLaneChangeDesire(final BehavioralCharacteristics behavioralCharacteristics,
331             final LanePerception perception, final CarFollowingModel carFollowingModel,
332             final LinkedHashSet<MandatoryIncentive> mandatoryIncentives,
333             final LinkedHashSet<VoluntaryIncentive> voluntaryIncentives)
334             throws ParameterException, GTUException, OperationalPlanException
335     {
336 
337         double dSync = behavioralCharacteristics.getParameter(DSYNC);
338         double dCoop = behavioralCharacteristics.getParameter(DCOOP);
339 
340         // Mandatory desire
341         double dLeftMandatory = Double.NEGATIVE_INFINITY;
342         double dRightMandatory = Double.NEGATIVE_INFINITY;
343         Desire mandatoryDesire = new Desire(dLeftMandatory, dRightMandatory);
344         for (MandatoryIncentive incentive : mandatoryIncentives)
345         {
346             Desire d = incentive.determineDesire(behavioralCharacteristics, perception, carFollowingModel, mandatoryDesire);
347             dLeftMandatory = d.getLeft() > dLeftMandatory ? d.getLeft() : dLeftMandatory;
348             dRightMandatory = d.getRight() > dRightMandatory ? d.getRight() : dRightMandatory;
349             mandatoryDesire = new Desire(dLeftMandatory, dRightMandatory);
350         }
351 
352         // Voluntary desire
353         double dLeftVoluntary = 0;
354         double dRightVoluntary = 0;
355         Desire voluntaryDesire = new Desire(dLeftVoluntary, dRightVoluntary);
356         for (VoluntaryIncentive incentive : voluntaryIncentives)
357         {
358             Desire d = incentive.determineDesire(behavioralCharacteristics, perception, carFollowingModel, mandatoryDesire,
359                     voluntaryDesire);
360             dLeftVoluntary += d.getLeft();
361             dRightVoluntary += d.getRight();
362             voluntaryDesire = new Desire(dLeftVoluntary, dRightVoluntary);
363         }
364 
365         // Total desire
366         double thetaLeft = 0;
367         if (dLeftMandatory <= dSync || dLeftMandatory * dLeftVoluntary >= 0)
368         {
369             // low mandatory desire, or same sign
370             thetaLeft = 1;
371         }
372         else if (dSync < dLeftMandatory && dLeftMandatory < dCoop && dLeftMandatory * dLeftVoluntary < 0)
373         {
374             // linear from 1 at dSync to 0 at dCoop
375             thetaLeft = (dCoop - Math.abs(dLeftMandatory)) / (dCoop - dSync);
376         }
377         double thetaRight = 0;
378         if (dRightMandatory <= dSync || dRightMandatory * dRightVoluntary >= 0)
379         {
380             // low mandatory desire, or same sign
381             thetaRight = 1;
382         }
383         else if (dSync < dRightMandatory && dRightMandatory < dCoop && dRightMandatory * dRightVoluntary < 0)
384         {
385             // linear from 1 at dSync to 0 at dCoop
386             thetaRight = (dCoop - Math.abs(dRightMandatory)) / (dCoop - dSync);
387         }
388 
389         return new Desire(dLeftMandatory + thetaLeft * dLeftVoluntary, dRightMandatory + thetaRight * dRightVoluntary);
390 
391     }
392 
393     /**
394      * Determine whether a gap is acceptable.
395      * @param perception perception
396      * @param bc behavioral characteristics
397      * @param sli speed limit info
398      * @param cfm car-following model
399      * @param desire level of lane change desire
400      * @param ownSpeed own speed
401      * @param lat lateral direction for synchronization
402      * @return whether a gap is acceptable
403      * @throws ParameterException if a parameter is not defined
404      * @throws OperationalPlanException perception exception
405      */
406     private static boolean acceptGap(final LanePerception perception, final BehavioralCharacteristics bc,
407             final SpeedLimitInfo sli, final CarFollowingModel cfm, final double desire, final Speed ownSpeed,
408             final LateralDirectionality lat) throws ParameterException, OperationalPlanException
409     {
410         Acceleration b = bc.getParameter(ParameterTypes.B);
411         if (perception.getPerceptionCategory(InfrastructurePerception.class).getLegalLaneChangePossibility(RelativeLane.CURRENT,
412                 lat).si > 0 && !perception.getPerceptionCategory(NeighborsPerception.class).isGtuAlongside(lat))
413         {
414             Acceleration aFollow = new Acceleration(Double.POSITIVE_INFINITY, AccelerationUnit.SI);
415             for (AbstractHeadwayGTU follower : perception.getPerceptionCategory(NeighborsPerception.class)
416                     .getFirstFollowers(lat))
417             {
418                 Acceleration a = singleAcceleration(follower.getDistance(), follower.getSpeed(), ownSpeed, desire,
419                         follower.getBehavioralCharacteristics(), follower.getSpeedLimitInfo(), follower.getCarFollowingModel());
420                 aFollow = Acceleration.min(aFollow, a);
421             }
422             Acceleration aSelf = new Acceleration(Double.POSITIVE_INFINITY, AccelerationUnit.SI);
423             for (AbstractHeadwayGTU leader : perception.getPerceptionCategory(NeighborsPerception.class).getFirstLeaders(lat))
424             {
425                 Acceleration a = singleAcceleration(leader.getDistance(), ownSpeed, leader.getSpeed(), desire, bc, sli, cfm);
426                 aSelf = Acceleration.min(aSelf, a);
427             }
428             Acceleration threshold = b.multiplyBy(-desire);
429             return aFollow.ge(threshold) && aSelf.ge(threshold);
430         }
431         return false;
432     }
433 
434     /**
435      * Determine acceleration for synchronization.
436      * @param perception perception
437      * @param bc behavioral characteristics
438      * @param sli speed limit info
439      * @param cfm car-following model
440      * @param desire level of lane change desire
441      * @param ownSpeed own speed
442      * @param lat lateral direction for synchronization
443      * @return acceleration for synchronization
444      * @throws ParameterException if a parameter is not defined
445      * @throws OperationalPlanException perception exception
446      */
447     private static Acceleration synchronize(final LanePerception perception, final BehavioralCharacteristics bc,
448             final SpeedLimitInfo sli, final CarFollowingModel cfm, final double desire, final Speed ownSpeed,
449             final LateralDirectionality lat) throws ParameterException, OperationalPlanException
450     {
451         Acceleration b = bc.getParameter(ParameterTypes.B);
452         Acceleration a = new Acceleration(Double.POSITIVE_INFINITY, AccelerationUnit.SI);
453         for (AbstractHeadwayGTU leader : perception.getPerceptionCategory(NeighborsPerception.class).getFirstLeaders(lat))
454         {
455             Acceleration aSingle = singleAcceleration(leader.getDistance(), ownSpeed, leader.getSpeed(), desire, bc, sli, cfm);
456             a = Acceleration.min(a, aSingle);
457         }
458         return Acceleration.max(a, b.multiplyBy(-1));
459     }
460 
461     /**
462      * Determine acceleration for cooperation.
463      * @param perception perception
464      * @param bc behavioral characteristics
465      * @param sli speed limit info
466      * @param cfm car-following model
467      * @param ownSpeed own speed
468      * @param lat lateral direction for cooperation
469      * @return acceleration for synchronization
470      * @throws ParameterException if a parameter is not defined
471      * @throws OperationalPlanException perception exception
472      */
473     private static Acceleration cooperate(final LanePerception perception, final BehavioralCharacteristics bc,
474             final SpeedLimitInfo sli, final CarFollowingModel cfm, final Speed ownSpeed, final LateralDirectionality lat)
475             throws ParameterException, OperationalPlanException
476     {
477         if (!perception.getLaneStructure().getCrossSection().contains(new RelativeLane(lat, 1)))
478         {
479             return new Acceleration(Double.MAX_VALUE, AccelerationUnit.SI);
480         }
481         Acceleration b = bc.getParameter(ParameterTypes.B);
482         Acceleration a = new Acceleration(Double.POSITIVE_INFINITY, AccelerationUnit.SI);
483         for (AbstractHeadwayGTU leader : perception.getPerceptionCategory(NeighborsPerception.class).getFirstLeaders(lat))
484         {
485             if ((lat == LateralDirectionality.LEFT && leader.isRightTurnIndicatorOn())
486                     || (lat == LateralDirectionality.RIGHT && leader.isLeftTurnIndicatorOn()))
487             {
488                 BehavioralCharacteristics bc2 = leader.getBehavioralCharacteristics();
489                 double desire = lat == LateralDirectionality.LEFT && bc2.contains(DRIGHT) ? bc2.getParameter(DRIGHT)
490                         : lat == LateralDirectionality.RIGHT && bc2.contains(DLEFT) ? bc2.getParameter(DLEFT) : 0;
491                 Acceleration aSingle =
492                         singleAcceleration(leader.getDistance(), ownSpeed, leader.getSpeed(), desire, bc, sli, cfm);
493                 a = Acceleration.min(a, aSingle);
494             }
495         }
496         return Acceleration.max(a, b.multiplyBy(-1));
497     }
498 
499     /**
500      * Determine acceleration from car-following.
501      * @param distance distance from follower to leader
502      * @param followerSpeed speed of follower
503      * @param leaderSpeed speed of leader
504      * @param desire level of lane change desire
505      * @param bc behavioral characteristics
506      * @param sli speed limit info
507      * @param cfm car-following model
508      * @return acceleration from car-following
509      * @throws ParameterException if a parameter is not defined
510      */
511     private static Acceleration singleAcceleration(final Length distance, final Speed followerSpeed, final Speed leaderSpeed,
512             final double desire, final BehavioralCharacteristics bc, final SpeedLimitInfo sli, final CarFollowingModel cfm)
513             throws ParameterException
514     {
515         // set T
516         bc.setParameter(ParameterTypes.T,
517                 Duration.interpolate(bc.getParameter(ParameterTypes.TMAX), bc.getParameter(ParameterTypes.TMIN), desire));
518         // calculate acceleration
519         SortedMap<Length, Speed> leaders = new TreeMap<>();
520         leaders.put(distance, leaderSpeed);
521         Acceleration a = cfm.followingAcceleration(bc, followerSpeed, sli, leaders);
522         // reset T
523         bc.resetParameter(ParameterTypes.T);
524         return a;
525     }
526 
527     /**
528      * Status of LMRS.
529      * <p>
530      * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
531      * <br>
532      * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
533      * <p>
534      * @version $Revision$, $LastChangedDate$, by $Author$, initial version Jul 27, 2016 <br>
535      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
536      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
537      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
538      */
539     public static class LmrsStatus implements Serializable
540     {
541 
542         /** */
543         private static final long serialVersionUID = 20160811L;
544 
545         /** Remembered leaders. */
546         private Set<AbstractHeadwayGTU> lastLeaders = new HashSet<>();
547 
548         /**
549          * @return lastLeaders.
550          */
551         public final Set<AbstractHeadwayGTU> getLastLeaders()
552         {
553             return new HashSet<>(this.lastLeaders);
554         }
555 
556         /**
557          * @param lastLeaders set lastLeaders.
558          */
559         public final void setLastLeaders(final Set<AbstractHeadwayGTU> lastLeaders)
560         {
561             this.lastLeaders.clear();
562             this.lastLeaders.addAll(lastLeaders);
563         }
564 
565         /** {@inheritDoc} */
566         @Override
567         public final String toString()
568         {
569             return "LmrsStatus";
570         }
571 
572     }
573 
574 }