1 package org.opentrafficsim.road.gtu.lane.tactical.util;
2
3 import static org.opentrafficsim.core.gtu.behavioralcharacteristics.AbstractParameterType.Check.ATLEASTONE;
4 import static org.opentrafficsim.core.gtu.behavioralcharacteristics.AbstractParameterType.Check.POSITIVE;
5
6 import java.io.Serializable;
7 import java.util.ArrayList;
8 import java.util.HashMap;
9 import java.util.HashSet;
10 import java.util.Iterator;
11 import java.util.List;
12 import java.util.Map;
13 import java.util.Set;
14 import java.util.SortedMap;
15 import java.util.SortedSet;
16 import java.util.TreeMap;
17 import java.util.UUID;
18
19 import org.djunits.unit.AccelerationUnit;
20 import org.djunits.unit.LengthUnit;
21 import org.djunits.unit.TimeUnit;
22 import org.djunits.value.vdouble.scalar.Acceleration;
23 import org.djunits.value.vdouble.scalar.Duration;
24 import org.djunits.value.vdouble.scalar.Length;
25 import org.djunits.value.vdouble.scalar.Speed;
26 import org.djunits.value.vdouble.scalar.Time;
27 import org.opentrafficsim.core.gtu.GTUException;
28 import org.opentrafficsim.core.gtu.behavioralcharacteristics.BehavioralCharacteristics;
29 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterException;
30 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypeDouble;
31 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypeDuration;
32 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypeLength;
33 import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypes;
34 import org.opentrafficsim.core.network.Node;
35 import org.opentrafficsim.core.network.route.Route;
36 import org.opentrafficsim.road.gtu.lane.RoadGTUTypes;
37 import org.opentrafficsim.road.gtu.lane.perception.headway.AbstractHeadwayGTU;
38 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayConflict;
39 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTUSimple;
40 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayStopLine;
41 import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
42 import org.opentrafficsim.road.network.lane.CrossSectionLink;
43 import org.opentrafficsim.road.network.lane.conflict.ConflictRule;
44 import org.opentrafficsim.road.network.lane.conflict.ConflictType;
45 import org.opentrafficsim.road.network.speed.SpeedLimitInfo;
46
47 import nl.tudelft.simulation.language.Throw;
48
49 /**
50 * This class implements default behavior for intersection conflicts for use in tactical planners.
51 * <p>
52 * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
53 * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
54 * <p>
55 * @version $Revision$, $LastChangedDate$, by $Author$, initial version Jun 3, 2016 <br>
56 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
57 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
58 * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
59 */
60 // TODO conflict over multiple lanes (longitudinal in own direction)
61 // TODO stop length depending on speed?
62 // TODO vehicle generation at speed in T-junction demo
63 // TODO turbo roundabout demo
64 // TODO a) yielding while having priority happens only when leaders is standing still on conflict (then its useless...)
65 // b) two vehicles can remain upstream of merge if vehicle stands on merge but leaves some space to move
66 // probably 1 is yielding, and 1 is courtesy yielding as the other stands still
67 // c) they might start moving together and collide further down (no response to negative headway on merge)
68 public final class ConflictUtil
69 {
70
71 /** Minimum time gap between events. */
72 public static final ParameterTypeDuration MIN_GAP = new ParameterTypeDuration("minGap", "Minimum gap for conflicts.",
73 new Duration(0.000001, TimeUnit.SECOND), POSITIVE);
74
75 /** Stopping distance at conflicts. */
76 public static final ParameterTypeLength S0_CONF =
77 new ParameterTypeLength("s0 conf", "Stopping distance at conflicts.", new Length(1.5, LengthUnit.METER), POSITIVE);
78
79 /** Multiplication factor on time for conservative assessment. */
80 public static final ParameterTypeDouble TIME_FACTOR =
81 new ParameterTypeDouble("timeFactor", "Safety factor on estimated time.", 1.1, ATLEASTONE);
82
83 /** Area before stop line where one is considered arrived at the intersection. */
84 public static final ParameterTypeLength STOP_AREA =
85 new ParameterTypeLength("stopArea", "Area before stop line where one is considered arrived at the intersection.",
86 new Length(4, LengthUnit.METER), POSITIVE);
87
88 /** Time step for free acceleration anticipation. */
89 private static final Duration TIME_STEP = new Duration(0.5, TimeUnit.SI);
90
91 /** Acceleration that is equal to ignoring. */
92 private static final Acceleration IGNORE = new Acceleration(Double.MAX_VALUE, AccelerationUnit.SI);
93
94 /**
95 * Do not instantiate.
96 */
97 private ConflictUtil()
98 {
99 //
100 }
101
102 /**
103 * Approach conflicts by applying appropriate acceleration (or deceleration). The model may yield for a vehicle even while
104 * having priority. Such a 'yield plan' is remembered in <tt>YieldPlans</tt>. By forwarding the same <tt>YieldPlans</tt> for
105 * a GTU consistency of such plans is provided. If any conflict is not accepted to pass, stopping before a more upstream
106 * conflict is applied if there not sufficient stopping length in between conflicts.
107 * @param behavioralCharacteristics behavioral characteristics
108 * @param conflicts set of conflicts to approach
109 * @param leaders leading vehicles
110 * @param carFollowingModel car-following model
111 * @param vehicleLength length of vehicle
112 * @param speed current speed
113 * @param acceleration current acceleration
114 * @param speedLimitInfo speed limit info
115 * @param conflictPlans set of plans for conflict
116 * @return acceleration appropriate for approaching the conflicts
117 * @throws GTUException in case of an unsupported conflict rule
118 * @throws ParameterException if a parameter is not defined or out of bounds
119 */
120 @SuppressWarnings("checkstyle:parameternumber")
121 public static Acceleration approachConflicts(final BehavioralCharacteristics behavioralCharacteristics,
122 final SortedSet<HeadwayConflict> conflicts, final SortedSet<AbstractHeadwayGTU> leaders,
123 final CarFollowingModel carFollowingModel, final Length vehicleLength, final Speed speed,
124 final Acceleration acceleration, final SpeedLimitInfo speedLimitInfo, final ConflictPlans conflictPlans)
125 throws GTUException, ParameterException
126 {
127
128 Acceleration a = IGNORE;
129 if (conflicts.isEmpty())
130 {
131 return a;
132 }
133
134 conflictPlans.cleanYieldPlans();
135 List<Length> prevStarts = new ArrayList<>();
136 List<Length> prevEnds = new ArrayList<>();
137
138 for (HeadwayConflict conflict : conflicts)
139 {
140
141 // adjust acceleration for situations where stopping might not be required
142 if (conflict.isCrossing())
143 {
144 // avoid collision if crossing is occupied
145 a = Acceleration.min(a,
146 avoidCrossingCollision(behavioralCharacteristics, conflict, carFollowingModel, speed, speedLimitInfo));
147 }
148 else
149 {
150 // follow leading GTUs on merge or split
151 a = Acceleration.min(a, followConflictingLeaderOnMergeOrSplit(conflict, behavioralCharacteristics,
152 carFollowingModel, speed, speedLimitInfo));
153 }
154 if (conflict.getDistance().lt(Length.ZERO))
155 {
156 // ignore conflicts we are on (i.e. negative distance to start of conflict)
157 continue;
158 }
159
160 // determine if we need to stop
161 boolean stop;
162 switch (conflict.getConflictRule())
163 {
164 case PRIORITY:
165 {
166 stop = stopForPriorityConflict(conflict, leaders, speed, vehicleLength, behavioralCharacteristics,
167 conflictPlans);
168 break;
169 }
170 case GIVE_WAY:
171 {
172 stop = stopForGiveWayConflict(conflict, leaders, speed, acceleration, vehicleLength,
173 behavioralCharacteristics, speedLimitInfo, carFollowingModel);
174 break;
175 }
176 case STOP:
177 {
178 stop = stopForStopConflict(conflict, leaders, speed, acceleration, vehicleLength, behavioralCharacteristics,
179 speedLimitInfo, carFollowingModel);
180 break;
181 }
182 case ALL_STOP:
183 {
184 stop = stopForAllStopConflict(conflict, conflictPlans);
185 break;
186 }
187 case SPLIT:
188 {
189 stop = false; // skipped anyway
190 break;
191 }
192 default:
193 {
194 throw new GTUException("Unsupported conflict rule encountered while approaching conflicts.");
195 }
196 }
197
198 // stop if required, account for upstream conflicts to keep clear
199 if (!conflict.getConflictType().equals(ConflictType.SPLIT))
200 {
201
202 if (stop)
203 {
204 prevStarts.add(conflict.getDistance());
205 // stop for first conflict looking upstream of this blocked conflict that allows sufficient space
206 int j = 0; // most upstream conflict if not in between conflicts
207 for (int i = prevEnds.size() - 1; i >= 0; i--) // downstream to upstream
208 {
209 // note, at this point prevStarts contains one more conflict than prevEnds
210 if (prevStarts.get(i + 1).minus(prevEnds.get(i))
211 .gt(passableDistance(Speed.ZERO, vehicleLength, behavioralCharacteristics)))
212 {
213 j = i + 1;
214 break;
215 }
216 }
217
218 // TODO
219 // if this lowers our acceleration, we need to check if we are able to pass upstream conflicts still in time
220
221 // stop for j'th conflict, if deceleration is too strong, for next one
222 behavioralCharacteristics.setParameter(ParameterTypes.S0, behavioralCharacteristics.getParameter(S0_CONF));
223 Acceleration aCF = new Acceleration(Double.NEGATIVE_INFINITY, AccelerationUnit.SI);
224 while (aCF.si < -6.0 && j < prevStarts.size())
225 {
226 aCF = Acceleration.max(aCF, CarFollowingUtil.stop(carFollowingModel, behavioralCharacteristics, speed,
227 speedLimitInfo, prevStarts.get(j)));
228 j++;
229 }
230 behavioralCharacteristics.resetParameter(ParameterTypes.S0);
231 a = Acceleration.min(a, aCF);
232 break;
233 }
234 if (conflict.getConflictRule().equals(ConflictRule.GIVE_WAY)
235 || conflict.getConflictRule().equals(ConflictRule.STOP))
236 {
237 prevStarts.add(conflict.getDistance());
238 prevEnds.add(conflict.getDistance().plus(conflict.getLength()));
239 }
240 }
241
242 }
243
244 if (a.si < -6.0)
245 {
246 return IGNORE;
247 }
248 return a;
249 }
250
251 /**
252 * Determines acceleration for following conflicting vehicles <i>on</i> a merge or split conflict.
253 * @param conflict merge or split conflict
254 * @param behavioralCharacteristics behavioral characteristics
255 * @param carFollowingModel car-following model
256 * @param speed current speed
257 * @param speedLimitInfo speed limit info
258 * @return acceleration for following conflicting vehicles <i>on</i> a merge or split conflict
259 * @throws ParameterException if a parameter is not given or out of bounds
260 */
261 private static Acceleration followConflictingLeaderOnMergeOrSplit(final HeadwayConflict conflict,
262 final BehavioralCharacteristics behavioralCharacteristics, final CarFollowingModel carFollowingModel,
263 final Speed speed, final SpeedLimitInfo speedLimitInfo) throws ParameterException
264 {
265 // ignore if no conflicting GTU's
266 if (conflict.getDownstreamConflictingGTUs().isEmpty())
267 {
268 return IGNORE;
269 }
270 // get the most upstream GTU to consider
271 AbstractHeadwayGTU c = conflict.getDownstreamConflictingGTUs().first();
272 if (c.isAhead())
273 {
274 // conflict GTU completely downstream of conflict (i.e. regular car-following, ignore here)
275 return IGNORE;
276 }
277 // conflict GTU (partially) on the conflict
278 // {@formatter:off}
279 // ______________________________________________
280 // ___ virtual headway | ___ |
281 // |___|(-----------------------)|___|(vehicle from south, on lane from south)
282 // _____________________________|_______|________
283 // / /
284 // / /
285 // {@formatter:on}
286 Length virtualHeadway = conflict.getDistance().plus(c.getOverlapRear());
287 if (virtualHeadway.le(Length.ZERO) && conflict.getDistance().le(Length.ZERO))
288 {
289 // conflict GTU downstream of start of conflict, but upstream of us
290 return IGNORE;
291 }
292 // follow leader
293 SortedMap<Length, Speed> leaders = new TreeMap<>();
294 leaders.put(virtualHeadway, c.getSpeed());
295 Acceleration a = carFollowingModel.followingAcceleration(behavioralCharacteristics, speed, speedLimitInfo, leaders);
296 // if conflicting GTU is partially upstream of the conflict and at (near) stand-still, stop for the conflict rather than
297 // following the tail of the conflicting GTU
298 if (conflict.isMerge() && virtualHeadway.lt(conflict.getDistance()))
299 {
300 // {@formatter:off}
301 /*
302 * ______________________________________________
303 * ___ stop for conflict | |
304 * |___|(--------------------)| ___ |
305 * _____________________________|__/ /_|________
306 * / /__/ /
307 * / /
308 */
309 // {@formatter:on}
310 behavioralCharacteristics.setParameter(ParameterTypes.S0, behavioralCharacteristics.getParameter(S0_CONF));
311 Acceleration aStop = CarFollowingUtil.stop(carFollowingModel, behavioralCharacteristics, speed, speedLimitInfo,
312 conflict.getDistance());
313 behavioralCharacteristics.resetParameter(ParameterTypes.S0);
314 a = Acceleration.max(a, aStop); // max, which ever allows the largest acceleration
315 }
316 return a;
317 }
318
319 /**
320 * Determines an acceleration required to avoid a collision with GTUs <i>on</i> a crossing conflict.
321 * @param behavioralCharacteristics behavioral characteristics
322 * @param conflict conflict
323 * @param carFollowingModel car-following model
324 * @param speed current speed
325 * @param speedLimitInfo speed limit info
326 * @return acceleration required to avoid a collision
327 * @throws ParameterException if parameter is not defined
328 */
329 private static Acceleration avoidCrossingCollision(final BehavioralCharacteristics behavioralCharacteristics,
330 final HeadwayConflict conflict, final CarFollowingModel carFollowingModel, final Speed speed,
331 final SpeedLimitInfo speedLimitInfo) throws ParameterException
332 {
333
334 // TODO only within visibility
335 List<AbstractHeadwayGTU> conflictingGTUs = new ArrayList<>();
336 for (AbstractHeadwayGTU gtu : conflict.getUpstreamConflictingGTUs())
337 {
338 if (isOnRoute(conflict.getConflictingLink(), gtu))
339 {
340 // first upstream vehicle on route to this conflict
341 conflictingGTUs.add(gtu);
342 break;
343 }
344 }
345 for (AbstractHeadwayGTU gtu : conflict.getDownstreamConflictingGTUs())
346 {
347 if (gtu.isParallel())
348 {
349 conflictingGTUs.add(gtu);
350 }
351 else
352 {
353 // vehicles beyond conflict are not a thread
354 break;
355 }
356 }
357
358 if (conflictingGTUs.isEmpty())
359 {
360 return IGNORE;
361 }
362
363 Acceleration a = IGNORE;
364 for (AbstractHeadwayGTU conflictingGTU : conflictingGTUs)
365 {
366 AnticipationInfo tteC;
367 Length distance;
368 if (conflictingGTU.isParallel())
369 {
370 tteC = new AnticipationInfo(Duration.ZERO, conflictingGTU.getSpeed());
371 distance = conflictingGTU.getOverlapRear().abs().plus(conflictingGTU.getOverlap())
372 .plus(conflictingGTU.getOverlapFront().abs());
373 }
374 else
375 {
376 tteC = AnticipationInfo.anticipateMovement(conflictingGTU.getDistance(), conflictingGTU.getSpeed(),
377 Acceleration.ZERO);
378 distance = conflictingGTU.getDistance().plus(conflict.getLength()).plus(conflictingGTU.getLength());
379 }
380 AnticipationInfo ttcC = AnticipationInfo.anticipateMovement(distance, conflictingGTU.getSpeed(), Acceleration.ZERO);
381 AnticipationInfo tteO = AnticipationInfo.anticipateMovementFreeAcceleration(conflict.getDistance(), speed,
382 behavioralCharacteristics, carFollowingModel, speedLimitInfo, TIME_STEP);
383 // enter before cleared
384 // TODO safety factor?
385 if (tteC.getDuration().lt(tteO.getDuration()) && tteO.getDuration().lt(ttcC.getDuration()))
386 {
387 if (!conflictingGTU.getSpeed().eq(Speed.ZERO))
388 {
389 // solve parabolic speed profile s = v*t + .5*a*t*t, a =
390 double acc = 2 * (conflict.getDistance().si - speed.si * ttcC.getDuration().si)
391 / (ttcC.getDuration().si * ttcC.getDuration().si);
392 // time till zero speed > time to avoid conflict?
393 if (speed.si / -acc > ttcC.getDuration().si)
394 {
395 a = Acceleration.min(a, new Acceleration(acc, AccelerationUnit.SI));
396 }
397 else
398 {
399 // will reach zero speed ourselves
400 a = Acceleration.min(a, CarFollowingUtil.stop(carFollowingModel, behavioralCharacteristics, speed,
401 speedLimitInfo, conflict.getDistance()));
402 }
403 }
404 // conflicting vehicle stand-still, ignore even at conflict
405 }
406 }
407 return a;
408 }
409
410 /**
411 * Approach a priority conflict. Stopping is applied to give way to conflicting traffic in case congestion is present on the
412 * own lane. This is courtesy yielding.
413 * @param conflict conflict to approach
414 * @param leaders leading vehicles in own lane
415 * @param speed current speed
416 * @param vehicleLength vehicle length
417 * @param behavioralCharacteristics behavioral characteristics
418 * @param yieldPlans set of plans for yielding with priority
419 * @return whether to stop for this conflict
420 * @throws ParameterException if parameter B is not defined
421 */
422 private static boolean stopForPriorityConflict(final HeadwayConflict conflict, final SortedSet<AbstractHeadwayGTU> leaders,
423 final Speed speed, final Length vehicleLength, final BehavioralCharacteristics behavioralCharacteristics,
424 final ConflictPlans yieldPlans) throws ParameterException
425 {
426
427 // Escape if no conflicting traffic or traffic ahead
428 if (leaders.isEmpty() || conflict.getUpstreamConflictingGTUs().isEmpty())
429 {
430 // no leader, or no conflicting vehicle
431 return false;
432 }
433
434 // Consider factors where we don't yield, even if this was our plan before
435 Length distance = conflict.getDistance().minus(leaders.first().getDistance())
436 .plus(passableDistance(speed, vehicleLength, behavioralCharacteristics));
437 if (conflict.isCrossing())
438 {
439 // Merge is cleared at start, crossing at end
440 distance = distance.plus(conflict.getLength());
441 }
442 AnticipationInfo ttpD =
443 AnticipationInfo.anticipateMovement(distance, leaders.first().getSpeed(), leaders.first().getAcceleration());
444 AbstractHeadwayGTU first = conflict.getUpstreamConflictingGTUs().first();
445 boolean conflictGtuCanReachConflict =
446 first.getDistance().lt(behavioralCharacteristics.getParameter(STOP_AREA)) || first.getSpeed().gt(Speed.ZERO);
447 boolean leaderStandsOnConflict = leaders.first().getSpeed().eq(Speed.ZERO) && !leaders.first().isAhead();
448 // Do not courtesy yield, even if this was the plan, if:
449 // 1) Leader leaves conflict passable now
450 // 2) Leader stands still on conflict (no point in yielding, conflict is blocked anyway)
451 // 3) Conflict vehicle stand still away from conflict (i.e. blocked by something else)
452 if (ttpD.getDuration().eq(Duration.ZERO) || leaderStandsOnConflict || !conflictGtuCanReachConflict)
453 {
454 return false;
455 }
456
457 // If yielding was the plan, keep yielding
458 if (yieldPlans.isYieldPlan(conflict, first))
459 {
460 yieldPlans.setYieldPlan(conflict, first); // this makes sure the plan is not cleared
461 return true;
462 }
463
464 // Check factors to initiate a courtesy yield plan (in parts, to not calculate it all if not needed)
465 // 1) Conflict vehicle does not stand still away from conflict (i.e. might recht conflict)
466 // 2) Leader is ahead of conflict or still moving
467 if (conflictGtuCanReachConflict && !leaderStandsOnConflict)
468 {
469 AnticipationInfo ttcC = AnticipationInfo.anticipateMovementFreeAcceleration(
470 first.getDistance().plus(conflict.getConflictingLength()).plus(first.getLength()), first.getSpeed(),
471 first.getBehavioralCharacteristics(), first.getCarFollowingModel(), first.getSpeedLimitInfo(), TIME_STEP);
472 // 3) Conflict vehicle will pass conflict before leader makes it passable, we may courtesy yield without loss
473 if (ttpD.getDuration().ge(ttcC.getDuration()))// .multiplyBy(behavioralCharacteristics.getParameter(TIME_FACTOR))
474 // .plus(behavioralCharacteristics.getParameter(MIN_GAP))))
475 {
476 // 4) Acceleration (constant) is comfortable (at initialization of courtesy yield)
477 Acceleration b = behavioralCharacteristics.getParameter(ParameterTypes.B);
478 Acceleration bReq = new Acceleration(.5 * speed.si * speed.si / conflict.getDistance().si, AccelerationUnit.SI);
479 if (bReq.lt(b))
480 {
481 yieldPlans.setYieldPlan(conflict, first);
482 return true;
483 }
484 }
485 }
486
487 // No initialization of courtesy yield plan
488 return false;
489
490 }
491
492 /**
493 * Approach a give-way conflict.
494 * @param conflict conflict
495 * @param leaders leaders
496 * @param speed current speed
497 * @param acceleration current acceleration
498 * @param vehicleLength vehicle length
499 * @param behavioralCharacteristics behavioral characteristics
500 * @param speedLimitInfo speed limit info
501 * @param carFollowingModel car-following model
502 * @return whether to stop for this conflict
503 * @throws ParameterException if a parameter is not defined
504 */
505 @SuppressWarnings("checkstyle:parameternumber")
506 private static boolean stopForGiveWayConflict(final HeadwayConflict conflict, final SortedSet<AbstractHeadwayGTU> leaders,
507 final Speed speed, final Acceleration acceleration, final Length vehicleLength,
508 final BehavioralCharacteristics behavioralCharacteristics, final SpeedLimitInfo speedLimitInfo,
509 final CarFollowingModel carFollowingModel) throws ParameterException
510 {
511
512 // TODO conflicting vehicle on conflict, but will leave sooner then we enter, so no problem?
513 if (conflict.getConflictType().equals(ConflictType.CROSSING) && !conflict.getDownstreamConflictingGTUs().isEmpty()
514 && conflict.getDownstreamConflictingGTUs().first().isParallel())
515 {
516 // vehicle on the conflict
517 return true;
518 }
519
520 // Get data independent of conflicting vehicle
521 // parameters
522 Acceleration b = behavioralCharacteristics.getParameter(ParameterTypes.B).neg();
523 double f = behavioralCharacteristics.getParameter(TIME_FACTOR);
524 Duration gap = behavioralCharacteristics.getParameter(MIN_GAP);
525 // time till conflict is cleared
526 Length distance = conflict.getDistance().plus(vehicleLength);
527 if (conflict.isCrossing())
528 {
529 // merge is cleared at start, crossing at end
530 distance = distance.plus(conflict.getLength());
531 }
532 // based on current acceleration, but limited by free acceleration
533 // TODO maybe only constant acceleration? What did MOTUS do?
534 AnticipationInfo ttcOa = AnticipationInfo.anticipateMovementFreeAcceleration(distance, speed, behavioralCharacteristics,
535 carFollowingModel, speedLimitInfo, TIME_STEP);
536 AnticipationInfo ttcO;
537 if (acceleration.lt(Acceleration.ZERO))
538 {
539 AnticipationInfo ttcOb = AnticipationInfo.anticipateMovement(distance, speed, acceleration);
540 ttcO = ttcOa.getDuration().gt(ttcOb.getDuration()) ? ttcOa : ttcOb;
541 }
542 else
543 {
544 ttcO = ttcOa;
545 }
546 // time till downstream vehicle will make the conflict passible, under constant speed or safe deceleration
547 AnticipationInfo ttpDz = null;
548 AnticipationInfo ttpDs = null;
549 if (conflict.isCrossing())
550 {
551 if (!leaders.isEmpty())
552 {
553 distance = conflict.getDistance().minus(leaders.first().getDistance()).plus(conflict.getLength())
554 .plus(passableDistance(Speed.ZERO, vehicleLength, behavioralCharacteristics));
555 ttpDz = AnticipationInfo.anticipateMovement(distance, leaders.first().getSpeed(), Acceleration.ZERO);
556 // TODO not b but b_critical?
557 ttpDs = AnticipationInfo.anticipateMovement(distance, leaders.first().getSpeed(), b);
558 if (Double.isInfinite(ttpDs.getDuration().si))
559 {
560 // if leader decelerates to stand-still, not sufficient space after conflict
561 // stop, even without conflicting vehicles
562 return true;
563 }
564 }
565 else
566 {
567 // no leader so conflict is passible within a duration of 0
568 ttpDz = new AnticipationInfo(Duration.ZERO, Speed.ZERO);
569 ttpDs = new AnticipationInfo(Duration.ZERO, Speed.ZERO);
570 }
571 }
572
573 List<AbstractHeadwayGTU> conflictingVehicles = new ArrayList<>();
574 if (!conflict.getUpstreamConflictingGTUs().isEmpty())
575 {
576 for (AbstractHeadwayGTU vehicle : conflict.getUpstreamConflictingGTUs())
577 {
578 if (isOnRoute(conflict.getConflictingLink(), vehicle))
579 {
580 conflictingVehicles.add(vehicle);
581 }
582 }
583 }
584 else
585 {
586 // none within visibility, assume a conflicting vehicle just outside of visibility driving at speed limit
587 try
588 {
589 HeadwayGTUSimple conflictGtu = new HeadwayGTUSimple("virtual " + UUID.randomUUID().toString(), RoadGTUTypes.CAR,
590 conflict.getConflictingVisibility(), new Length(4.0, LengthUnit.SI),
591 conflict.getConflictingSpeedLimit(), Acceleration.ZERO);
592 conflictingVehicles.add(conflictGtu);
593 }
594 catch (GTUException exception)
595 {
596 throw new RuntimeException("Could not create a virtual conflicting vehicle at visibility range.", exception);
597 }
598 }
599
600 // Loop over conflicting vehicles
601 for (AbstractHeadwayGTU conflictingVehicle : conflictingVehicles)
602 {
603
604 // time till conflict vehicle will enter, under free acceleration and safe deceleration
605 // TODO what does the c stand for? not constant acceleration, i.e. also free acceleration is used
606 AnticipationInfo tteCc;
607 if (conflictingVehicle instanceof HeadwayGTUSimple)
608 {
609 tteCc = AnticipationInfo.anticipateMovement(conflictingVehicle.getDistance(), conflictingVehicle.getSpeed(),
610 conflictingVehicle.getAcceleration());
611 }
612 else
613 {
614 BehavioralCharacteristics bc = conflictingVehicle.getBehavioralCharacteristics();
615 SpeedLimitInfo sli = conflictingVehicle.getSpeedLimitInfo();
616 CarFollowingModel cfm = conflictingVehicle.getCarFollowingModel();
617 // Constant acceleration creates inf at stand still, triggering passing trough a congested stream
618 // tteCc = AnticipationInfo.anticipateMovement(conflictingVehicle.getDistance(), conflictingVehicle.getSpeed(),
619 // conflictingVehicle.getAcceleration());
620 tteCc = AnticipationInfo.anticipateMovementFreeAcceleration(conflictingVehicle.getDistance(),
621 conflictingVehicle.getSpeed(), bc, cfm, sli, TIME_STEP);
622 }
623 AnticipationInfo tteCs =
624 AnticipationInfo.anticipateMovement(conflictingVehicle.getDistance(), conflictingVehicle.getSpeed(), b);
625
626 // check gap
627 if (conflict.isMerge())
628 {
629
630 // Merge, will be each others followers, add time to overcome speed difference
631 double vSelf = ttcO.getEndSpeed().si;
632 double speedDiff = conflictingVehicle.getSpeed().si - vSelf;
633 speedDiff = speedDiff > 0 ? speedDiff : 0;
634 Duration additionalTime = new Duration(speedDiff / -b.si, TimeUnit.SI);
635 // check if conflict vehicle will be upstream after that time, position beyond conflict after additional time
636 double followerFront = conflictingVehicle.getSpeed().si * ttcO.getDuration().si
637 - conflictingVehicle.getDistance().si + (conflictingVehicle.getSpeed().si * additionalTime.si
638 + 0.5 * b.si * additionalTime.si * additionalTime.si);
639 double ownRear = vSelf * additionalTime.si; // constant speed after clearing
640 Duration tMax = behavioralCharacteristics.getParameter(ParameterTypes.TMAX);
641 Length s0 = behavioralCharacteristics.getParameter(ParameterTypes.S0);
642 // 1) will clear the conflict after the conflict vehicle enters
643 // 2) not sufficient time to overcome speed difference
644 // 3) conflict vehicle will be too near after adjusting speed
645 if (ttcO.getDuration().multiplyBy(f).plus(gap).gt(tteCc.getDuration())
646 || ttcOa.getDuration().plus(additionalTime).multiplyBy(f).plus(gap).gt(tteCs.getDuration())
647 || ownRear < (followerFront + (tMax.si + gap.si) * vSelf + s0.si) * f)
648 {
649 return true;
650 }
651
652 }
653 else if (conflict.isCrossing())
654 {
655
656 // Crossing, stop if order of events is not ok
657 // 1) downstream vehicle must supply sufficient space before conflict vehicle will enter
658 // 2) must clear the conflict before the conflict vehicle will enter
659 // 3) if leader decelerates with b??, conflict vehicle should be able to safely delay entering conflict
660 if (ttpDz.getDuration().multiplyBy(f).plus(gap).gt(tteCc.getDuration())
661 || ttcOa.getDuration().multiplyBy(f).plus(gap).gt(tteCc.getDuration())
662 || ttpDs.getDuration().multiplyBy(f).plus(gap).gt(tteCs.getDuration()))
663 {
664 return true;
665 }
666
667 }
668 else
669 {
670 throw new RuntimeException(
671 "Conflict is of unknown type " + conflict.getConflictType() + ", which is not merge nor a crossing.");
672 }
673 }
674
675 // No conflict vehicle triggered stopping
676 return false;
677
678 }
679
680 /**
681 * Approach a stop conflict. Currently this is equal to approaching a give-way conflict.
682 * @param conflict conflict
683 * @param leaders leaders
684 * @param speed current speed
685 * @param acceleration current acceleration
686 * @param vehicleLength vehicle length
687 * @param behavioralCharacteristics behavioral characteristics
688 * @param speedLimitInfo speed limit info
689 * @param carFollowingModel car-following model
690 * @return whether to stop for this conflict
691 * @throws ParameterException if a parameter is not defined
692 */
693 @SuppressWarnings("checkstyle:parameternumber")
694 private static boolean stopForStopConflict(final HeadwayConflict conflict, final SortedSet<AbstractHeadwayGTU> leaders,
695 final Speed speed, final Acceleration acceleration, final Length vehicleLength,
696 final BehavioralCharacteristics behavioralCharacteristics, final SpeedLimitInfo speedLimitInfo,
697 final CarFollowingModel carFollowingModel) throws ParameterException
698 {
699 return stopForGiveWayConflict(conflict, leaders, speed, acceleration, vehicleLength, behavioralCharacteristics,
700 speedLimitInfo, carFollowingModel);
701 }
702
703 /**
704 * Approach an all-stop conflict.
705 * @param conflict conflict to approach
706 * @param conflictPlans set of plans for conflict
707 * @return whether to stop for this conflict
708 */
709 private static boolean stopForAllStopConflict(final HeadwayConflict conflict, final ConflictPlans conflictPlans)
710 {
711 // TODO all-stop behavior
712
713 if (conflictPlans.isStopPhaseRun(conflict.getStopLine()))
714 {
715 return false;
716 }
717
718 return false;
719 }
720
721 /**
722 * Returns whether the conflicting link is on the route of the given gtu.
723 * @param conflictingLink CrossSectionLink; conflicting link
724 * @param gtu AbstractHeadwayGTU; gtu
725 * @return whether the conflict is on the route of the given gtu
726 */
727 private static boolean isOnRoute(final CrossSectionLink conflictingLink, final AbstractHeadwayGTU gtu)
728 {
729 try
730 {
731 Route route = gtu.getRoute();
732 if (route == null)
733 {
734 // conservative assumption: it's on the route (gtu should be upstream of the conflict)
735 return true;
736 }
737 Node startNode = conflictingLink.getStartNode();
738 Node endNode = conflictingLink.getEndNode();
739 return route.contains(startNode) && route.contains(endNode)
740 && Math.abs(route.indexOf(endNode) - route.indexOf(startNode)) == 1;
741 }
742 catch (@SuppressWarnings("unused") UnsupportedOperationException uoe)
743 {
744 // conservative assumption: it's on the route (gtu should be upstream of the conflict)
745 return true;
746 }
747 }
748
749 /**
750 * Returns a speed dependent distance needed behind the leader to completely pass the conflict.
751 * @param speed speed
752 * @param vehicleLength vehicle length
753 * @param behavioralCharacteristics behavioral characteristics
754 * @return speed dependent distance needed behind the leader to completely pass the conflict
755 * @throws ParameterException if parameter is not available
756 */
757 private static Length passableDistance(final Speed speed, final Length vehicleLength,
758 final BehavioralCharacteristics behavioralCharacteristics) throws ParameterException
759 {
760 return behavioralCharacteristics.getParameter(ParameterTypes.T).multiplyBy(speed)
761 .plus(behavioralCharacteristics.getParameter(ParameterTypes.S0)).plus(vehicleLength);
762 }
763
764 /**
765 * Holds the tactical plans of a driver considering conflicts. These are remembered for consistency. For instance, if the
766 * decision is made to yield as current deceleration suggests it's safe to do so, but the trajectory for stopping in front
767 * of the conflict results in deceleration slightly above what is considered safe deceleration, the plan should not be
768 * abandoned. Decelerations above what is considered safe deceleration may result due to numerical overshoot or other factor
769 * coming into play in car-following models. Many other examples exist where a driver sticks to a certain plan.
770 * <p>
771 * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
772 * <br>
773 * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
774 * <p>
775 * @version $Revision$, $LastChangedDate$, by $Author$, initial version Jun 7, 2016 <br>
776 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
777 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
778 * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
779 */
780 public static final class ConflictPlans implements Serializable
781 {
782
783 /** */
784 private static final long serialVersionUID = 20160811L;
785
786 /** Set of current plans. */
787 private final Map<String, String> yieldPlans = new HashMap<>();
788
789 /** Set of conflicts that are still actively considered for a yield plan. */
790 private final Set<String> activeYieldPlans = new HashSet<>();
791
792 /** Phases of navigating an all-stop intersection per intersection. */
793 private final HashMap<String, StopPhase> stopPhases = new HashMap<>();
794
795 /** Estimated arrival times of vehicles at all-stop intersection. */
796 private final HashMap<String, Time> arrivalTimes = new HashMap<>();
797
798 /**
799 * Returns whether a plan exists for yielding at the conflict for the given conflict GTU.
800 * @param conflict conflict
801 * @param gtu conflicting GTU
802 * @return whether a plan exists for yielding at the conflict for the given conflict GTU
803 */
804 boolean isYieldPlan(final HeadwayConflict conflict, final AbstractHeadwayGTU gtu)
805 {
806 return this.yieldPlans.containsKey(conflict.getId()) && this.yieldPlans.get(conflict.getId()).equals(gtu.getId());
807 }
808
809 /**
810 * Sets or maintains the plan to yield at the conflict for the given conflict RSU.
811 * @param conflict conflict to yield at
812 * @param gtu conflicting GTU
813 */
814 void setYieldPlan(final HeadwayConflict conflict, final AbstractHeadwayGTU gtu)
815 {
816 this.yieldPlans.put(conflict.getId(), gtu.getId());
817 this.activeYieldPlans.add(conflict.getId());
818 }
819
820 /**
821 * Clears any yield plan that was no longer kept active in the last evaluation of conflicts.
822 */
823 void cleanYieldPlans()
824 {
825 // remove any plan not represented in activePlans
826 Iterator<String> iterator = this.yieldPlans.keySet().iterator();
827 while (iterator.hasNext())
828 {
829 String conflictId = iterator.next();
830 if (!this.activeYieldPlans.contains(conflictId))
831 {
832 iterator.remove();
833 }
834 }
835 // clear the activePlans for the next consideration of conflicts
836 this.activeYieldPlans.clear();
837 }
838
839 /**
840 * Sets the estimated arrival time of a GTU.
841 * @param gtu GTU
842 * @param time estimated arrival time
843 */
844 void setArrivalTime(final AbstractHeadwayGTU gtu, final Time time)
845 {
846 this.arrivalTimes.put(gtu.getId(), time);
847 }
848
849 /**
850 * Returns the estimated arrival time of given GTU.
851 * @param gtu GTU
852 * @return estimated arrival time of given GTU
853 */
854 Time getArrivalTime(final AbstractHeadwayGTU gtu)
855 {
856 return this.arrivalTimes.get(gtu.getId());
857 }
858
859 /**
860 * Sets the current phase to 'approach' for the given stop line.
861 * @param stopLine stop line
862 */
863 void setStopPhaseApproach(final HeadwayStopLine stopLine)
864 {
865 this.stopPhases.put(stopLine.getId(), StopPhase.APPROACH);
866 }
867
868 /**
869 * Sets the current phase to 'yield' for the given stop line.
870 * @param stopLine stop line
871 * @throws RuntimeException if the phase was not set to approach before
872 */
873 void setStopPhaseYield(final HeadwayStopLine stopLine)
874 {
875 Throw.when(
876 !this.stopPhases.containsKey(stopLine.getId())
877 || !this.stopPhases.get(stopLine.getId()).equals(StopPhase.APPROACH),
878 RuntimeException.class, "Yield stop phase is set for stop line that was not approached.");
879 this.stopPhases.put(stopLine.getId(), StopPhase.YIELD);
880 }
881
882 /**
883 * Sets the current phase to 'run' for the given stop line.
884 * @param stopLine stop line
885 * @throws RuntimeException if the phase was not set to approach before
886 */
887 void setStopPhaseRun(final HeadwayStopLine stopLine)
888 {
889 Throw.when(!this.stopPhases.containsKey(stopLine.getId()), RuntimeException.class,
890 "Run stop phase is set for stop line that was not approached.");
891 this.stopPhases.put(stopLine.getId(), StopPhase.YIELD);
892 }
893
894 /**
895 * @param stopLine stop line
896 * @return whether the current phase is 'approach' for the given stop line
897 */
898 boolean isStopPhaseApproach(final HeadwayStopLine stopLine)
899 {
900 return this.stopPhases.containsKey(stopLine.getId())
901 && this.stopPhases.get(stopLine.getId()).equals(StopPhase.APPROACH);
902 }
903
904 /**
905 * @param stopLine stop line
906 * @return whether the current phase is 'yield' for the given stop line
907 */
908 boolean isStopPhaseYield(final HeadwayStopLine stopLine)
909 {
910 return this.stopPhases.containsKey(stopLine.getId())
911 && this.stopPhases.get(stopLine.getId()).equals(StopPhase.YIELD);
912 }
913
914 /**
915 * @param stopLine stop line
916 * @return whether the current phase is 'run' for the given stop line
917 */
918 boolean isStopPhaseRun(final HeadwayStopLine stopLine)
919 {
920 return this.stopPhases.containsKey(stopLine.getId()) && this.stopPhases.get(stopLine.getId()).equals(StopPhase.RUN);
921 }
922
923 /** {@inheritDoc} */
924 @Override
925 public String toString()
926 {
927 return "ConflictPlans";
928 }
929
930 }
931
932 /**
933 * Phases of navigating an all-stop intersection.
934 * <p>
935 * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
936 * <br>
937 * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
938 * <p>
939 * @version $Revision$, $LastChangedDate$, by $Author$, initial version Jun 30, 2016 <br>
940 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
941 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
942 * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
943 */
944 private enum StopPhase
945 {
946 /** Approaching stop intersection. */
947 APPROACH,
948
949 /** Yielding for stop intersection. */
950 YIELD,
951
952 /** Running over stop intersection. */
953 RUN;
954 }
955
956 }