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