View Javadoc
1   package org.opentrafficsim.road.gtu.lane.tactical.following;
2   
3   import java.util.Collection;
4   
5   import org.djunits.unit.AccelerationUnit;
6   import org.djunits.unit.DurationUnit;
7   import org.djunits.unit.LengthUnit;
8   import org.djunits.unit.TimeUnit;
9   import org.djunits.value.vdouble.scalar.Acceleration;
10  import org.djunits.value.vdouble.scalar.Duration;
11  import org.djunits.value.vdouble.scalar.Length;
12  import org.djunits.value.vdouble.scalar.Speed;
13  import org.djunits.value.vdouble.scalar.Time;
14  import org.opentrafficsim.core.gtu.GtuException;
15  import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
16  import org.opentrafficsim.road.gtu.lane.perception.headway.Headway;
17  import org.opentrafficsim.road.gtu.lane.tactical.AbstractLaneBasedTacticalPlanner;
18  import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedTacticalPlanner;
19  
20  /**
21   * Code shared between various car following models. <br>
22   * Note: many of the methods have a maxDistance, which may be "behind" the location of the next GTU, or stand-alone. Note that
23   * the maxDistance is equivalent to a GTU with zero speed, and not equivalent to a moving GTU.
24   * <p>
25   * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
26   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
27   * </p>
28   * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
29   * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
30   */
31  public abstract class AbstractGtuFollowingModelMobil implements GtuFollowingModelOld
32  {
33  
34      /** Prohibitive deceleration used to construct the TOODANGEROUS result below. */
35      private static final AccelerationStep PROHIBITIVEACCELERATIONSTEP =
36              new AccelerationStep(new Acceleration(Double.NEGATIVE_INFINITY, AccelerationUnit.SI),
37                      new Time(Double.NaN, TimeUnit.DEFAULT), new Duration(Double.NaN, DurationUnit.SI));
38  
39      /** Return value if lane change causes immediate collision. */
40      public static final DualAccelerationStep TOODANGEROUS =
41              new DualAccelerationStep(PROHIBITIVEACCELERATIONSTEP, PROHIBITIVEACCELERATIONSTEP);
42  
43      /** {@inheritDoc} */
44      @Override
45      public final DualAccelerationStep computeDualAccelerationStep(final LaneBasedGtu referenceGTU,
46              final Collection<Headway> otherGTUs, final Length maxDistance, final Speed speedLimit) throws GtuException
47      {
48          return computeDualAccelerationStep(referenceGTU, otherGTUs, maxDistance, speedLimit, getStepSize());
49      }
50  
51      /** {@inheritDoc} */
52      @Override
53      public final DualAccelerationStep computeDualAccelerationStep(final LaneBasedGtu referenceGTU,
54              final Collection<Headway> otherHeadways, final Length maxDistance, final Speed speedLimit, final Duration stepSize)
55              throws GtuException
56      {
57          // Find out if there is an immediate collision
58          for (Headway headway : otherHeadways)
59          {
60              // XXX: Under which circumstances can getDistance() be NULL? Should that indeed result in TOODANGEROUS?
61              if (headway.getDistance() == null)
62              {
63                  return TOODANGEROUS;
64              }
65              if (!headway.getId().equals(referenceGTU.getId()) && Double.isNaN(headway.getDistance().si))
66              {
67                  return TOODANGEROUS;
68              }
69          }
70          AccelerationStep followerAccelerationStep = null;
71          AccelerationStep referenceGTUAccelerationStep = null;
72          LaneBasedTacticalPlanner tp = referenceGTU.getTacticalPlanner();
73          if (null == tp)
74          {
75              referenceGTU.getTacticalPlanner();
76              System.err.println("tactical planner is null");
77          }
78          GtuFollowingModelOld gfm = (GtuFollowingModelOld) ((AbstractLaneBasedTacticalPlanner) referenceGTU.getTacticalPlanner())
79                  .getCarFollowingModel();
80          // Find the leader and the follower that cause/experience the least positive (most negative) acceleration.
81          for (Headway headway : otherHeadways)
82          {
83              if (headway.getId().equals(referenceGTU.getId()))
84              {
85                  continue;
86              }
87              if (headway.getDistance().si < 0)
88              {
89                  // This one is behind; assume our CFM holds also for the GTU behind us
90                  AccelerationStep as = gfm.computeAccelerationStep(headway.getSpeed(), referenceGTU.getSpeed(),
91                          new Length(-headway.getDistance().si, LengthUnit.SI), speedLimit,
92                          referenceGTU.getSimulator().getSimulatorAbsTime(), stepSize);
93                  if (null == followerAccelerationStep || as.getAcceleration().lt(followerAccelerationStep.getAcceleration()))
94                  {
95                      followerAccelerationStep = as;
96                  }
97              }
98              else
99              {
100                 // This one is ahead
101                 AccelerationStep as = gfm.computeAccelerationStep(referenceGTU, headway.getSpeed(), headway.getDistance(),
102                         maxDistance, speedLimit, stepSize);
103                 if (null == referenceGTUAccelerationStep
104                         || as.getAcceleration().lt(referenceGTUAccelerationStep.getAcceleration()))
105                 {
106                     referenceGTUAccelerationStep = as;
107                 }
108             }
109         }
110         if (null == followerAccelerationStep)
111         {
112             followerAccelerationStep = gfm.computeAccelerationStepWithNoLeader(referenceGTU, maxDistance, speedLimit, stepSize);
113         }
114         if (null == referenceGTUAccelerationStep)
115         {
116             referenceGTUAccelerationStep =
117                     gfm.computeAccelerationStepWithNoLeader(referenceGTU, maxDistance, speedLimit, stepSize);
118         }
119         return new DualAccelerationStep(referenceGTUAccelerationStep, followerAccelerationStep);
120     }
121 
122     /** {@inheritDoc} */
123     @Override
124     public final AccelerationStep computeAccelerationStep(final LaneBasedGtu gtu, final Speed leaderSpeed, final Length headway,
125             final Length maxDistance, final Speed speedLimit) throws GtuException
126     {
127         return computeAccelerationStep(gtu, leaderSpeed, headway, maxDistance, speedLimit, getStepSize());
128     }
129 
130     /** {@inheritDoc} */
131     @Override
132     public final AccelerationStep computeAccelerationStep(final LaneBasedGtu gtu, final Speed leaderSpeed, final Length headway,
133             final Length maxDistance, final Speed speedLimit, final Duration stepSize) throws GtuException
134     {
135         Length distance;
136         Speed leaderOrBlockSpeed;
137         if (maxDistance.lt(headway) || headway == null)
138         {
139             distance = maxDistance;
140             leaderOrBlockSpeed = Speed.ZERO;
141         }
142         else
143         {
144             distance = headway;
145             leaderOrBlockSpeed = leaderSpeed;
146         }
147         final Speed followerSpeed = gtu.getSpeed();
148         final Speed followerMaximumSpeed = gtu.getMaximumSpeed();
149         Acceleration newAcceleration =
150                 computeAcceleration(followerSpeed, followerMaximumSpeed, leaderOrBlockSpeed, distance, speedLimit, stepSize);
151         Time nextEvaluationTime = gtu.getSimulator().getSimulatorAbsTime().plus(stepSize);
152         return new AccelerationStep(newAcceleration, nextEvaluationTime, stepSize);
153     }
154 
155     /** {@inheritDoc} */
156     @Override
157     public final AccelerationStep computeAccelerationStep(final Speed followerSpeed, final Speed leaderSpeed,
158             final Length headway, final Speed speedLimit, final Time currentTime)
159     {
160         return computeAccelerationStep(followerSpeed, leaderSpeed, headway, speedLimit, currentTime, getStepSize());
161     }
162 
163     /** {@inheritDoc} */
164     @Override
165     public final AccelerationStep computeAccelerationStep(final Speed followerSpeed, final Speed leaderSpeed,
166             final Length headway, final Speed speedLimit, final Time currentTime, final Duration stepSize)
167     {
168         final Speed followerMaximumSpeed = speedLimit; // the best approximation we can do...
169         Acceleration newAcceleration =
170                 computeAcceleration(followerSpeed, followerMaximumSpeed, leaderSpeed, headway, speedLimit, stepSize);
171         Time nextEvaluationTime = currentTime.plus(stepSize);
172         return new AccelerationStep(newAcceleration, nextEvaluationTime, stepSize);
173     }
174 
175     /** {@inheritDoc} */
176     @Override
177     public final AccelerationStep computeAccelerationStepWithNoLeader(final LaneBasedGtu gtu, final Length maxDistance,
178             final Speed speedLimit) throws GtuException
179     {
180         return computeAccelerationStepWithNoLeader(gtu, maxDistance, speedLimit, getStepSize());
181     }
182 
183     /** {@inheritDoc} */
184     @Override
185     public final AccelerationStep computeAccelerationStepWithNoLeader(final LaneBasedGtu gtu, final Length maxDistance,
186             final Speed speedLimit, final Duration stepSize) throws GtuException
187     {
188         Length stopDistance = new Length(
189                 gtu.getMaximumSpeed().si * gtu.getMaximumSpeed().si / (2.0 * getMaximumSafeDeceleration().si), LengthUnit.SI);
190         return computeAccelerationStep(gtu, gtu.getSpeed(), stopDistance, maxDistance, speedLimit, stepSize);
191         /*-
192         return computeAcceleration(gtu, gtu.getSpeed(), Calc.speedSquaredDividedByDoubleAcceleration(gtu
193             .getMaximumSpeed(), maximumSafeDeceleration()), speedLimit);
194          */
195     }
196 
197     /** {@inheritDoc} */
198     @Override
199     public final Length minimumHeadway(final Speed followerSpeed, final Speed leaderSpeed, final Length precision,
200             final Length maxDistance, final Speed speedLimit, final Speed followerMaximumSpeed)
201     {
202         if (precision.getSI() <= 0)
203         {
204             throw new Error("Precision has bad value (must be > 0; got " + precision + ")");
205         }
206         double maximumDeceleration = -getMaximumSafeDeceleration().getSI();
207         // Find a decent interval to bisect
208         double minimumSI = 0;
209         double minimumSIDeceleration =
210                 computeAcceleration(followerSpeed, followerSpeed, leaderSpeed, new Length(minimumSI, LengthUnit.SI), speedLimit)
211                         .getSI();
212         if (minimumSIDeceleration >= maximumDeceleration)
213         {
214             // Weird... The GTU following model allows zero headway
215             return Length.ZERO;
216         }
217         double maximumSI = 1; // this is - deliberately - way too small
218         double maximumSIDeceleration = Double.NaN;
219         // Double the value of maximumSI until the resulting deceleration is less severe than the maximum
220         for (int step = 0; step < 20; step++)
221         {
222             maximumSIDeceleration = computeAcceleration(followerSpeed, followerMaximumSpeed, leaderSpeed,
223                     new Length(maximumSI, LengthUnit.SI), speedLimit).getSI();
224             if (maximumSIDeceleration > maximumDeceleration)
225             {
226                 break;
227             }
228             maximumSI *= 2;
229         }
230         if (maximumSIDeceleration < maximumDeceleration)
231         {
232             System.out.println();
233             maximumSIDeceleration = computeAcceleration(followerSpeed, followerMaximumSpeed, leaderSpeed,
234                     new Length(maximumSI, LengthUnit.SI), speedLimit).getSI();
235             throw new Error("Cannot find headway that results in an acceptable deceleration");
236         }
237         // Now bisect until the error is less than the requested precision
238         final int maximumStep = (int) Math.ceil(Math.log((maximumSI - minimumSI) / precision.getSI()) / Math.log(2));
239         for (int step = 0; step < maximumStep; step++)
240         {
241             double midSI = (minimumSI + maximumSI) / 2;
242             double midSIAcceleration = computeAcceleration(followerSpeed, followerMaximumSpeed, leaderSpeed,
243                     new Length(midSI, LengthUnit.SI), speedLimit).getSI();
244             if (midSIAcceleration < maximumDeceleration)
245             {
246                 minimumSI = midSI;
247             }
248             else
249             {
250                 maximumSI = midSI;
251             }
252         }
253         Length result = new Length(Math.min((minimumSI + maximumSI) / 2, maxDistance.si), LengthUnit.SI);
254         return result;
255     }
256 
257 }