package org.opentrafficsim.road.gtu.following;
import java.util.Collection;
import org.djunits.unit.AccelerationUnit;
import org.djunits.unit.LengthUnit;
import org.djunits.unit.TimeUnit;
import org.djunits.value.vdouble.scalar.Acceleration;
import org.djunits.value.vdouble.scalar.Length;
import org.djunits.value.vdouble.scalar.Speed;
import org.djunits.value.vdouble.scalar.Time;
import org.opentrafficsim.core.network.NetworkException;
import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
/**
* Code shared between various car following models.
*
* Copyright (c) 2013-2015 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
* BSD-style license. See OpenTrafficSim License.
*
* @version $Revision: 1408 $, $LastChangedDate: 2015-09-24 15:17:25 +0200 (Thu, 24 Sep 2015) $, by $Author: pknoppers $,
* initial version 19 feb. 2015
* @author Alexander Verbraeck
* @author Hans van Lint
* @author Peter Knoppers
* @author Guus Tamminga
* @author Yufei Yuan
*/
public abstract class AbstractGTUFollowingModel implements GTUFollowingModel
{
/** Prohibitive deceleration used to construct the TOODANGEROUS result below. */
private static final AccelerationStep PROHIBITIVEACCELERATIONSTEP = new AccelerationStep(new Acceleration(
Double.NEGATIVE_INFINITY, AccelerationUnit.SI), new Time.Abs(Double.NaN, TimeUnit.SI));
/** Return value if lane change causes immediate collision. */
public static final DualAccelerationStep TOODANGEROUS = new DualAccelerationStep(PROHIBITIVEACCELERATIONSTEP,
PROHIBITIVEACCELERATIONSTEP);
/** {@inheritDoc} */
@Override
public final DualAccelerationStep computeAcceleration(final LaneBasedGTU referenceGTU,
final Collection otherGTUs, final Speed speedLimit) throws NetworkException
{
Time.Abs when = referenceGTU.getSimulator().getSimulatorTime().getTime();
// Find out if there is an immediate collision
for (HeadwayGTU headwayGTU : otherGTUs)
{
if (headwayGTU.getOtherGTU() != referenceGTU && null == headwayGTU.getDistance())
{
return TOODANGEROUS;
}
}
AccelerationStep followerAccelerationStep = null;
AccelerationStep referenceGTUAccelerationStep = null;
GTUFollowingModel gfm = referenceGTU.getGTUFollowingModel();
// Find the leader and the follower that cause/experience the least positive (most negative) acceleration.
for (HeadwayGTU headwayGTU : otherGTUs)
{
if (null == headwayGTU.getOtherGTU())
{
System.out.println("FollowAcceleration.acceleration: Cannot happen");
}
if (headwayGTU.getOtherGTU() == referenceGTU)
{
continue;
}
if (headwayGTU.getDistanceSI() < 0)
{
// This one is behind
AccelerationStep as =
gfm.computeAcceleration(headwayGTU.getOtherGTU(), referenceGTU.getLongitudinalVelocity(when),
new Length.Rel(-headwayGTU.getDistanceSI(), LengthUnit.SI), speedLimit);
if (null == followerAccelerationStep
|| as.getAcceleration().lt(followerAccelerationStep.getAcceleration()))
{
// if (as.getAcceleration().getSI() < -gfm.maximumSafeDeceleration().getSI())
// {
// return TOODANGEROUS;
// }
followerAccelerationStep = as;
}
}
else
{
// This one is ahead
AccelerationStep as =
gfm.computeAcceleration(referenceGTU, headwayGTU.getOtherGTU().getLongitudinalVelocity(when),
headwayGTU.getDistance(), speedLimit);
if (null == referenceGTUAccelerationStep
|| as.getAcceleration().lt(referenceGTUAccelerationStep.getAcceleration()))
{
referenceGTUAccelerationStep = as;
}
}
}
if (null == followerAccelerationStep)
{
followerAccelerationStep = gfm.computeAccelerationWithNoLeader(referenceGTU, speedLimit);
}
if (null == referenceGTUAccelerationStep)
{
referenceGTUAccelerationStep = gfm.computeAccelerationWithNoLeader(referenceGTU, speedLimit);
}
return new DualAccelerationStep(referenceGTUAccelerationStep, followerAccelerationStep);
}
/** {@inheritDoc} */
@Override
public final AccelerationStep computeAcceleration(final LaneBasedGTU follower, final Speed leaderSpeed,
final Length.Rel headway, final Speed speedLimit)
{
final Time.Abs currentTime = follower.getNextEvaluationTime();
final Speed followerSpeed = follower.getLongitudinalVelocity(currentTime);
final Speed followerMaximumSpeed = follower.getMaximumVelocity();
Acceleration newAcceleration =
computeAcceleration(followerSpeed, followerMaximumSpeed, leaderSpeed, headway, speedLimit);
Time.Abs nextEvaluationTime = currentTime;
nextEvaluationTime = nextEvaluationTime.plus(getStepSize());
return new AccelerationStep(newAcceleration, nextEvaluationTime);
}
/** {@inheritDoc} */
@Override
public final AccelerationStep computeAccelerationWithNoLeader(final LaneBasedGTU gtu, final Speed speedLimit)
throws NetworkException
{
Length.Rel stopDistance =
new Length.Rel(gtu.getMaximumVelocity().si * gtu.getMaximumVelocity().si
/ (2.0 * maximumSafeDeceleration().si), LengthUnit.SI);
return computeAcceleration(gtu, gtu.getLongitudinalVelocity(), stopDistance, speedLimit);
/*-
return computeAcceleration(gtu, gtu.getLongitudinalVelocity(), Calc.speedSquaredDividedByDoubleAcceleration(gtu
.getMaximumVelocity(), maximumSafeDeceleration()), speedLimit);
*/
}
/** {@inheritDoc} */
@Override
public final Length.Rel minimumHeadway(final Speed followerSpeed, final Speed leaderSpeed,
final Length.Rel precision, final Speed speedLimit, final Speed followerMaximumSpeed)
{
if (precision.getSI() <= 0)
{
throw new Error("Precision has bad value (must be > 0; got " + precision + ")");
}
double maximumDeceleration = -maximumSafeDeceleration().getSI();
// Find a decent interval to bisect
double minimumSI = 0;
double minimumSIDeceleration =
computeAcceleration(followerSpeed, followerSpeed, leaderSpeed, new Length.Rel(minimumSI, LengthUnit.SI),
speedLimit).getSI();
if (minimumSIDeceleration >= maximumDeceleration)
{
// Weird... The GTU following model allows zero headway
return new Length.Rel(0, LengthUnit.SI);
}
double maximumSI = 1; // this is - deliberately - way too small
double maximumSIDeceleration = Double.NaN;
// Double the value of maximumSI until the resulting deceleration is less severe than the maximum
for (int step = 0; step < 20; step++)
{
maximumSIDeceleration =
computeAcceleration(followerSpeed, followerMaximumSpeed, leaderSpeed,
new Length.Rel(maximumSI, LengthUnit.SI), speedLimit).getSI();
if (maximumSIDeceleration > maximumDeceleration)
{
break;
}
maximumSI *= 2;
}
if (maximumSIDeceleration < maximumDeceleration)
{
throw new Error("Cannot find headway that results in an acceptable deceleration");
}
// Now bisect until the error is less than the requested precision
final int maximumStep = (int) Math.ceil(Math.log((maximumSI - minimumSI) / precision.getSI()) / Math.log(2));
for (int step = 0; step < maximumStep; step++)
{
double midSI = (minimumSI + maximumSI) / 2;
double midSIAcceleration =
computeAcceleration(followerSpeed, followerMaximumSpeed, leaderSpeed,
new Length.Rel(midSI, LengthUnit.SI), speedLimit).getSI();
if (midSIAcceleration < maximumDeceleration)
{
minimumSI = midSI;
}
else
{
maximumSI = midSI;
}
}
Length.Rel result = new Length.Rel((minimumSI + maximumSI) / 2, LengthUnit.SI);
computeAcceleration(followerSpeed, followerMaximumSpeed, leaderSpeed,
new Length.Rel(result.getSI(), LengthUnit.SI), speedLimit).getSI();
return result;
}
}