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
48
49
50
51
52
53
54
55
56 public final class LmrsUtil
57 {
58
59
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
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
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
126 public static final ParameterTypeDouble DLEFT = new ParameterTypeDouble("dLeft", "Left lane change desire.", 0.0);
127
128
129 public static final ParameterTypeDouble DRIGHT = new ParameterTypeDouble("dRight", "Right lane change desire.", 0.0);
130
131
132 public static final ParameterTypeDouble DLC = new ParameterTypeDouble("dLaneChange", "Desire of current lane change.", 0.0);
133
134
135
136
137 private LmrsUtil()
138 {
139
140 }
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
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
167 if (startTime.si == 0.0)
168 {
169 return new SimpleOperationalPlan(Acceleration.ZERO, LateralDirectionality.NONE);
170 }
171
172
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
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
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
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
228 Desire desire = getLaneChangeDesire(bc, perception, carFollowingModel, mandatoryIncentives, voluntaryIncentives);
229
230
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
237 double dFree = bc.getParameter(DFREE);
238 double dSync = bc.getParameter(DSYNC);
239 double dCoop = bc.getParameter(DCOOP);
240
241 TurnIndicatorStatus turnIndicatorStatus;
242 if (desire.leftIsLargerOrEqual() && desire.getLeft() >= dFree && acceptLeft)
243 {
244
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
253 lmrsData.isNewLeader(leaders.first());
254 }
255 }
256 else if (!desire.leftIsLargerOrEqual() && desire.getRight() >= dFree && acceptRight)
257 {
258
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
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
278 Acceleration aSync;
279 if (initiatedLaneChange.equals(LateralDirectionality.NONE))
280 {
281
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
295 if (desire.leftIsLargerOrEqual() && desire.getLeft() >= dCoop)
296 {
297
298 turnIndicatorStatus = TurnIndicatorStatus.LEFT;
299 }
300 else if (!desire.leftIsLargerOrEqual() && desire.getRight() >= dCoop)
301 {
302
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
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
322 exponentialHeadwayRelaxation(bc);
323
324 }
325 lmrsData.finalizeStep();
326
327 return new SimpleOperationalPlan(a, initiatedLaneChange);
328
329 }
330
331
332
333
334
335
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
345 }
346
347
348
349
350
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
361
362
363
364
365
366
367
368
369
370
371
372
373
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
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
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
411 double thetaLeft = 0;
412 if (dLeftMandatory <= dSync || dLeftMandatory * dLeftVoluntary >= 0)
413 {
414
415 thetaLeft = 1;
416 }
417 else if (dSync < dLeftMandatory && dLeftMandatory < dCoop && dLeftMandatory * dLeftVoluntary < 0)
418 {
419
420 thetaLeft = (dCoop - Math.abs(dLeftMandatory)) / (dCoop - dSync);
421 }
422 double thetaRight = 0;
423 if (dRightMandatory <= dSync || dRightMandatory * dRightVoluntary >= 0)
424 {
425
426 thetaRight = 1;
427 }
428 else if (dSync < dRightMandatory && dRightMandatory < dCoop && dRightMandatory * dRightVoluntary < 0)
429 {
430
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
440
441
442
443
444
445
446
447
448
449
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
481
482
483
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
496
497
498
499 private static void resetDesiredHeadway(final BehavioralCharacteristics bc) throws ParameterException
500 {
501 bc.resetParameter(ParameterTypes.T);
502 }
503
504
505
506
507
508
509
510
511
512
513
514
515
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
543
544
545
546
547
548
549
550
551
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
586
587
588
589
590
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
618
619
620
621
622
623
624
625
626
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
633 setDesiredHeadway(bc, desire);
634
635 SortedMap<Length, Speed> leaders = new TreeMap<>();
636 leaders.put(distance, leaderSpeed);
637 Acceleration a = cfm.followingAcceleration(bc, followerSpeed, sli, leaders);
638
639 resetDesiredHeadway(bc);
640 return a;
641 }
642
643
644
645
646
647
648
649
650
651
652
653
654
655 public static final class LmrsData
656 {
657
658
659 private final Set<String> leaders = new HashSet<>();
660
661
662 private final Set<String> tempLeaders = new HashSet<>();
663
664
665
666
667
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
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 }