Toledo.java

  1. package org.opentrafficsim.road.gtu.lane.tactical.toledo;

  2. import java.io.Serializable;
  3. import java.util.Iterator;
  4. import java.util.Random;

  5. import org.djunits.unit.AccelerationUnit;
  6. import org.djunits.unit.LengthUnit;
  7. import org.djunits.unit.LinearDensityUnit;
  8. import org.djunits.value.vdouble.scalar.Acceleration;
  9. import org.djunits.value.vdouble.scalar.Length;
  10. import org.djunits.value.vdouble.scalar.LinearDensity;
  11. import org.djunits.value.vdouble.scalar.Speed;
  12. import org.djunits.value.vdouble.scalar.Time;
  13. import org.opentrafficsim.base.parameters.ParameterException;
  14. import org.opentrafficsim.base.parameters.ParameterTypeLength;
  15. import org.opentrafficsim.base.parameters.ParameterTypes;
  16. import org.opentrafficsim.base.parameters.Parameters;
  17. import org.opentrafficsim.core.geometry.OTSGeometryException;
  18. import org.opentrafficsim.core.gtu.GTUException;
  19. import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
  20. import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
  21. import org.opentrafficsim.core.network.LateralDirectionality;
  22. import org.opentrafficsim.core.network.NetworkException;
  23. import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
  24. import org.opentrafficsim.road.gtu.lane.perception.CategoricalLanePerception;
  25. import org.opentrafficsim.road.gtu.lane.perception.InfrastructureLaneChangeInfo;
  26. import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
  27. import org.opentrafficsim.road.gtu.lane.perception.PerceptionCollectable;
  28. import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
  29. import org.opentrafficsim.road.gtu.lane.perception.categories.DirectNeighborsPerception;
  30. import org.opentrafficsim.road.gtu.lane.perception.categories.HeadwayGtuType;
  31. import org.opentrafficsim.road.gtu.lane.perception.categories.NeighborsPerception;
  32. import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTU;
  33. import org.opentrafficsim.road.gtu.lane.plan.operational.LaneChange;
  34. import org.opentrafficsim.road.gtu.lane.plan.operational.LaneOperationalPlanBuilder;
  35. import org.opentrafficsim.road.gtu.lane.tactical.AbstractLaneBasedTacticalPlanner;
  36. import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
  37. import org.opentrafficsim.road.network.speed.SpeedLimitInfo;
  38. import org.opentrafficsim.road.network.speed.SpeedLimitProspect;

  39. import nl.tudelft.simulation.language.d3.DirectedPoint;

  40. /**
  41.  * Implementation of the model of Toledo (2003).<br>
  42.  * <br>
  43.  * Tomer Toledo (2003) "Integrated Driving Behavior Modeling", Massachusetts Institute of Technology.
  44.  * <p>
  45.  * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
  46.  * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
  47.  * <p>
  48.  * @version $Revision$, $LastChangedDate$, by $Author$, initial version Jun 21, 2016 <br>
  49.  * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
  50.  * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
  51.  * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
  52.  */

  53. public class Toledo extends AbstractLaneBasedTacticalPlanner
  54. {

  55.     /** */
  56.     private static final long serialVersionUID = 20160711L;

  57.     /** Look ahead parameter type. */
  58.     protected static final ParameterTypeLength LOOKAHEAD = ParameterTypes.LOOKAHEAD;

  59.     /** Defines light vs heavy vehicles. */
  60.     static final Length MAX_LIGHT_VEHICLE_LENGTH = new Length(9.14, LengthUnit.METER);

  61.     /** Distance defining tail gating. */
  62.     static final Length TAILGATE_LENGTH = new Length(10, LengthUnit.METER);

  63.     /** Density for tail gating (Level Of Service (LOS) A, B or C). */
  64.     static final LinearDensity LOS_DENSITY = new LinearDensity(16, LinearDensityUnit.PER_KILOMETER);

  65.     /** Random number generator. */
  66.     public static final Random RANDOM = new Random();

  67.     /** Lane change status. */
  68.     private final LaneChange laneChange = new LaneChange();

  69.     /**
  70.      * Constructor.
  71.      * @param carFollowingModel Car-following model.
  72.      * @param gtu GTU
  73.      */
  74.     public Toledo(final CarFollowingModel carFollowingModel, final LaneBasedGTU gtu)
  75.     {
  76.         super(carFollowingModel, gtu, new CategoricalLanePerception(gtu));
  77.         getPerception().addPerceptionCategory(new ToledoPerception(getPerception()));
  78.         getPerception().addPerceptionCategory(new DirectNeighborsPerception(getPerception(), HeadwayGtuType.WRAP));
  79.     }

  80.     /** {@inheritDoc} */
  81.     @Override
  82.     public final OperationalPlan generateOperationalPlan(final Time startTime, final DirectedPoint locationAtStartTime)
  83.             throws OperationalPlanException, GTUException, NetworkException, ParameterException
  84.     {

  85.         // obtain objects to get info
  86.         LanePerception perception = getPerception();
  87.         perception.perceive();
  88.         SpeedLimitProspect slp =
  89.                 perception.getPerceptionCategory(ToledoPerception.class).getSpeedLimitProspect(RelativeLane.CURRENT);
  90.         SpeedLimitInfo sli = slp.getSpeedLimitInfo(Length.ZERO);
  91.         Parameters params = getGtu().getParameters();

  92.         Acceleration acceleration = null;

  93.         // if (this.laneChangeDirectionality == null)
  94.         LateralDirectionality initiatedLaneChange;
  95.         NeighborsPerception neighbors = perception.getPerceptionCategory(NeighborsPerception.class);
  96.         if (!this.laneChange.isChangingLane())
  97.         {
  98.             // not changing lane

  99.             // 3rd layer of model: Target gap model
  100.             // TODO vehicle not ahead and not backwards but completely adjacent
  101.             GapInfo gapFwdL = getGapInfo(params, perception, Gap.FORWARD, RelativeLane.LEFT);
  102.             GapInfo gapAdjL = getGapInfo(params, perception, Gap.ADJACENT, RelativeLane.LEFT);
  103.             GapInfo gapBckL = getGapInfo(params, perception, Gap.BACKWARD, RelativeLane.LEFT);
  104.             GapInfo gapFwdR = getGapInfo(params, perception, Gap.FORWARD, RelativeLane.RIGHT);
  105.             GapInfo gapAdjR = getGapInfo(params, perception, Gap.ADJACENT, RelativeLane.RIGHT);
  106.             GapInfo gapBckR = getGapInfo(params, perception, Gap.BACKWARD, RelativeLane.RIGHT);
  107.             double emuTgL =
  108.                     Math.log(Math.exp(gapFwdL.getUtility()) + Math.exp(gapAdjL.getUtility()) + Math.exp(gapBckL.getUtility()));
  109.             double emuTgR =
  110.                     Math.log(Math.exp(gapFwdR.getUtility()) + Math.exp(gapAdjR.getUtility()) + Math.exp(gapBckR.getUtility()));

  111.             // 2nd layer of model: Gap-acceptance model
  112.             // gap acceptance random terms (variable over time, equal for left and right)
  113.             double eLead = RANDOM.nextGaussian() * Math.pow(params.getParameter(ToledoLaneChangeParameters.SIGMA_LEAD), 2);
  114.             double eLag = RANDOM.nextGaussian() * Math.pow(params.getParameter(ToledoLaneChangeParameters.SIGMA_LAG), 2);
  115.             GapAcceptanceInfo gapAcceptL =
  116.                     getGapAcceptanceInfo(getGtu(), params, perception, emuTgL, eLead, eLag, RelativeLane.LEFT);
  117.             GapAcceptanceInfo gapAcceptR =
  118.                     getGapAcceptanceInfo(getGtu(), params, perception, emuTgR, eLead, eLag, RelativeLane.RIGHT);

  119.             // 1st layer of model: Target lane model
  120.             double vL = laneUtility(getGtu(), params, perception, gapAcceptL.getEmu(), sli, RelativeLane.LEFT);
  121.             double vC = laneUtility(getGtu(), params, perception, 0, sli, RelativeLane.CURRENT);
  122.             double vR = laneUtility(getGtu(), params, perception, gapAcceptR.getEmu(), sli, RelativeLane.RIGHT);

  123.             // change lane?
  124.             if (vL > vR && vL > vC && gapAcceptL.isAcceptable())
  125.             {
  126.                 initiatedLaneChange = LateralDirectionality.LEFT;
  127.             }
  128.             else if (vR > vL && vR > vC && gapAcceptR.isAcceptable())
  129.             {
  130.                 initiatedLaneChange = LateralDirectionality.RIGHT;
  131.             }
  132.             else
  133.             {
  134.                 initiatedLaneChange = LateralDirectionality.NONE;
  135.             }

  136.             // accelerate for gap selection
  137.             if (initiatedLaneChange.isNone())
  138.             {
  139.                 if ((vC > vR && vC > vL)
  140.                         || (!neighbors.getLeaders(RelativeLane.CURRENT).isEmpty() && neighbors.getLeaders(RelativeLane.CURRENT)
  141.                                 .first().getDistance().lt(getCarFollowingModel().desiredHeadway(params, getGtu().getSpeed()))))
  142.                 {
  143.                     acceleration = getCarFollowingModel().followingAcceleration(params, getGtu().getSpeed(), sli,
  144.                             neighbors.getLeaders(RelativeLane.CURRENT));
  145.                 }
  146.                 else
  147.                 {
  148.                     GapInfo gapAdj;
  149.                     GapInfo gapFwd;
  150.                     GapInfo gapBck;
  151.                     if (vL > vR && vL > vC)
  152.                     {
  153.                         gapAdj = gapAdjL;
  154.                         gapFwd = gapFwdL;
  155.                         gapBck = gapBckL;
  156.                     }
  157.                     else
  158.                     {
  159.                         gapAdj = gapAdjR;
  160.                         gapFwd = gapFwdR;
  161.                         gapBck = gapBckR;
  162.                     }
  163.                     if (gapAdj.getUtility() > gapFwd.getUtility() && gapAdj.getUtility() > gapBck.getUtility())
  164.                     {
  165.                         // adjacent gap selected
  166.                         double eadj = Toledo.RANDOM.nextGaussian() * params.getParameter(ToledoLaneChangeParameters.SIGMA_ADJ)
  167.                                 * params.getParameter(ToledoLaneChangeParameters.SIGMA_ADJ);
  168.                         acceleration =
  169.                                 new Acceleration(
  170.                                         params.getParameter(ToledoLaneChangeParameters.C_ADJ_ACC)
  171.                                                 * (params.getParameter(ToledoLaneChangeParameters.BETA_DP)
  172.                                                         * gapAdj.getLength().si - gapAdj.getDistance().si)
  173.                                                 + eadj,
  174.                                         AccelerationUnit.SI);
  175.                     }
  176.                     else if (gapFwd.getUtility() > gapAdj.getUtility() && gapFwd.getUtility() > gapBck.getUtility())
  177.                     {
  178.                         // forward gap selected
  179.                         Length desiredPosition = new Length(
  180.                                 gapFwd.getDistance().si
  181.                                         + params.getParameter(ToledoLaneChangeParameters.BETA_DP) * gapFwd.getLength().si,
  182.                                 LengthUnit.SI);
  183.                         double deltaV = gapFwd.getSpeed().si > getGtu().getSpeed().si
  184.                                 ? params.getParameter(ToledoLaneChangeParameters.LAMBDA_FWD_POS)
  185.                                         * (gapFwd.getSpeed().si - getGtu().getSpeed().si)
  186.                                 : params.getParameter(ToledoLaneChangeParameters.LAMBDA_FWD_NEG)
  187.                                         * (getGtu().getSpeed().si - gapFwd.getSpeed().si);
  188.                         double efwd = Toledo.RANDOM.nextGaussian() * params.getParameter(ToledoLaneChangeParameters.SIGMA_FWD)
  189.                                 * params.getParameter(ToledoLaneChangeParameters.SIGMA_FWD);
  190.                         acceleration = new Acceleration(params.getParameter(ToledoLaneChangeParameters.C_FWD_ACC)
  191.                                 * Math.pow(desiredPosition.si, params.getParameter(ToledoLaneChangeParameters.BETA_FWD))
  192.                                 * Math.exp(deltaV) + efwd, AccelerationUnit.SI);
  193.                     }
  194.                     else
  195.                     {
  196.                         // backward gap selected
  197.                         Length desiredPosition = new Length(
  198.                                 gapBck.getDistance().si
  199.                                         + (1 - params.getParameter(ToledoLaneChangeParameters.BETA_DP)) * gapBck.getLength().si,
  200.                                 LengthUnit.SI);
  201.                         double deltaV = gapBck.getSpeed().si > getGtu().getSpeed().si
  202.                                 ? params.getParameter(ToledoLaneChangeParameters.LAMBDA_BCK_POS)
  203.                                         * (gapBck.getSpeed().si - getGtu().getSpeed().si)
  204.                                 : params.getParameter(ToledoLaneChangeParameters.LAMBDA_BCK_NEG)
  205.                                         * (getGtu().getSpeed().si - gapBck.getSpeed().si);
  206.                         double ebck = Toledo.RANDOM.nextGaussian() * params.getParameter(ToledoLaneChangeParameters.SIGMA_BCK)
  207.                                 * params.getParameter(ToledoLaneChangeParameters.SIGMA_BCK);
  208.                         acceleration = new Acceleration(params.getParameter(ToledoLaneChangeParameters.C_BCK_ACC)
  209.                                 * Math.pow(desiredPosition.si, params.getParameter(ToledoLaneChangeParameters.BETA_BCK))
  210.                                 * Math.exp(deltaV) + ebck, AccelerationUnit.SI);
  211.                     }
  212.                 }
  213.             }
  214.         }
  215.         else
  216.         {
  217.             initiatedLaneChange = LateralDirectionality.NONE;
  218.         }

  219.         if (initiatedLaneChange.isLeft())
  220.         {
  221.             // changing left
  222.             getCarFollowingModel().followingAcceleration(params, getGtu().getSpeed(), sli,
  223.                     neighbors.getLeaders(RelativeLane.LEFT));
  224.         }
  225.         else if (initiatedLaneChange.isRight())
  226.         {
  227.             // changing right
  228.             getCarFollowingModel().followingAcceleration(params, getGtu().getSpeed(), sli,
  229.                     neighbors.getLeaders(RelativeLane.RIGHT));
  230.         }

  231.         if (acceleration == null)
  232.         {
  233.             throw new Error("Acceleration from toledo model is null.");
  234.         }

  235.         // operational plan
  236.         if (initiatedLaneChange.isNone())
  237.         {
  238.             try
  239.             {
  240.                 return LaneOperationalPlanBuilder.buildAccelerationPlan(getGtu(), startTime, getGtu().getSpeed(), acceleration,
  241.                         params.getParameter(ToledoLaneChangeParameters.DT));
  242.             }
  243.             catch (OTSGeometryException exception)
  244.             {
  245.                 throw new OperationalPlanException(exception);
  246.             }
  247.         }

  248.         try
  249.         {
  250.             OperationalPlan plan = LaneOperationalPlanBuilder.buildAccelerationLaneChangePlan(getGtu(), initiatedLaneChange,
  251.                     getGtu().getLocation(), startTime, getGtu().getSpeed(), acceleration,
  252.                     params.getParameter(ToledoLaneChangeParameters.DT), this.laneChange);
  253.             return plan;
  254.         }
  255.         catch (OTSGeometryException exception)
  256.         {
  257.             throw new OperationalPlanException(exception);
  258.         }
  259.     }

  260.     /**
  261.      * Returns info regarding a gap.
  262.      * @param params parameters
  263.      * @param perception perception
  264.      * @param gap gap
  265.      * @param lane lane
  266.      * @return utility of gap
  267.      * @throws ParameterException if parameter is not given
  268.      * @throws OperationalPlanException perception exception
  269.      */
  270.     private GapInfo getGapInfo(final Parameters params, final LanePerception perception, final Gap gap, final RelativeLane lane)
  271.             throws ParameterException, OperationalPlanException
  272.     {

  273.         // capture no leaders/follower cases for forward and backward gaps
  274.         NeighborsPerception neighbors = perception.getPerceptionCategory(NeighborsPerception.class);
  275.         if (gap.equals(Gap.FORWARD) && (neighbors.getLeaders(lane) == null || neighbors.getLeaders(lane).isEmpty()))
  276.         {
  277.             // no leaders
  278.             return new GapInfo(Double.NEGATIVE_INFINITY, null, null, null);
  279.         }
  280.         if (gap.equals(Gap.BACKWARD) && (neighbors.getFollowers(lane) == null || neighbors.getFollowers(lane).isEmpty()))
  281.         {
  282.             // no followers
  283.             return new GapInfo(Double.NEGATIVE_INFINITY, null, null, null);
  284.         }

  285.         double constant; // utility function: constant
  286.         double alpha; // utility function: alpha parameter
  287.         Length distanceToGap; // utility function: distance to gap
  288.         int deltaFrontVehicle; // utility function: dummy whether the gap is limited by vehicle in front
  289.         Length putativeLength; // acceleration model: gap length, not affected by vehicle in front
  290.         Length putativeDistance; // acceleration model: distance to gap, different from 'distanceToGap' in utility function
  291.         Speed putativeSpeed; // acceleration model: speed of putative follower or leader
  292.         Length leaderDist; // leader of (effective) gap
  293.         Speed leaderSpeed;
  294.         Length followerDist; // follower of (effective) gap
  295.         Speed followerSpeed;

  296.         if (gap.equals(Gap.FORWARD))
  297.         {
  298.             constant = params.getParameter(ToledoLaneChangeParameters.C_FWD_TG);
  299.             alpha = 0;
  300.             PerceptionCollectable<HeadwayGTU, LaneBasedGTU> itAble = neighbors.getLeaders(lane);
  301.             HeadwayGTU second = null;
  302.             if (!itAble.isEmpty())
  303.             {
  304.                 Iterator<HeadwayGTU> it = itAble.iterator();
  305.                 it.next();
  306.                 second = it.next();
  307.             }
  308.             if (second != null)
  309.             {
  310.                 // two leaders
  311.                 Iterator<HeadwayGTU> it = neighbors.getLeaders(lane).iterator();
  312.                 it.next();
  313.                 HeadwayGTU leader = it.next();
  314.                 leaderDist = leader.getDistance();
  315.                 leaderSpeed = leader.getSpeed();
  316.                 putativeLength = leaderDist.minus(neighbors.getLeaders(lane).first().getDistance())
  317.                         .minus(neighbors.getLeaders(lane).first().getLength());
  318.             }
  319.             else
  320.             {
  321.                 // TODO infinite -> some limited space, speed also
  322.                 // one leader
  323.                 leaderDist = Length.POSITIVE_INFINITY;
  324.                 leaderSpeed = Speed.POSITIVE_INFINITY;
  325.                 putativeLength = leaderDist;
  326.             }
  327.             // distance to nose, so add length
  328.             followerDist =
  329.                     neighbors.getLeaders(lane).first().getDistance().plus(neighbors.getLeaders(lane).first().getLength());
  330.             followerSpeed = neighbors.getLeaders(lane).first().getSpeed();
  331.             distanceToGap = followerDist; // same as distance to nose of first leader
  332.             putativeDistance = distanceToGap;
  333.             putativeSpeed = followerSpeed;
  334.         }
  335.         else if (gap.equals(Gap.ADJACENT))
  336.         {
  337.             constant = 0;
  338.             alpha = params.getParameter(ToledoLaneChangeParameters.ALPHA_ADJ);
  339.             distanceToGap = Length.ZERO;
  340.             if (neighbors.getLeaders(lane) != null && !neighbors.getLeaders(lane).isEmpty())
  341.             {
  342.                 leaderDist = neighbors.getLeaders(lane).first().getDistance();
  343.                 leaderSpeed = neighbors.getLeaders(lane).first().getSpeed();
  344.             }
  345.             else
  346.             {
  347.                 leaderDist = Length.POS_MAXVALUE;
  348.                 leaderSpeed = Speed.POS_MAXVALUE;
  349.             }
  350.             if (neighbors.getFollowers(lane) != null && !neighbors.getFollowers(lane).isEmpty())
  351.             {
  352.                 // plus own vehicle length for distance from nose of own vehicle (and then whole negative)
  353.                 followerDist = neighbors.getFollowers(lane).first().getDistance().plus(getGtu().getLength());
  354.                 followerSpeed = neighbors.getFollowers(lane).first().getSpeed();
  355.                 putativeDistance = followerDist;
  356.             }
  357.             else
  358.             {
  359.                 followerDist = Length.NEG_MAXVALUE;
  360.                 followerSpeed = Speed.NEG_MAXVALUE;
  361.                 putativeDistance = Length.POS_MAXVALUE;
  362.             }
  363.             putativeSpeed = null;
  364.             putativeLength = leaderDist.plus(followerDist).plus(getGtu().getLength());
  365.         }
  366.         else
  367.         {
  368.             constant = params.getParameter(ToledoLaneChangeParameters.C_BCK_TG);
  369.             alpha = params.getParameter(ToledoLaneChangeParameters.ALPHA_BCK);
  370.             deltaFrontVehicle = 0;
  371.             PerceptionCollectable<HeadwayGTU, LaneBasedGTU> itAble = neighbors.getFollowers(lane);
  372.             HeadwayGTU second = null;
  373.             if (!itAble.isEmpty())
  374.             {
  375.                 Iterator<HeadwayGTU> it = itAble.iterator();
  376.                 it.next();
  377.                 second = it.next();
  378.             }
  379.             if (second != null)
  380.             {
  381.                 // two followers
  382.                 Iterator<HeadwayGTU> it = neighbors.getFollowers(lane).iterator();
  383.                 it.next();
  384.                 HeadwayGTU follower = it.next();
  385.                 followerDist = follower.getDistance();
  386.                 followerSpeed = follower.getSpeed();
  387.                 putativeLength = followerDist.minus(neighbors.getFollowers(lane).first().getDistance())
  388.                         .minus(neighbors.getFollowers(lane).first().getLength());
  389.             }
  390.             else
  391.             {
  392.                 // one follower
  393.                 followerDist = Length.NEGATIVE_INFINITY;
  394.                 followerSpeed = Speed.NEGATIVE_INFINITY;
  395.                 putativeLength = followerDist;
  396.             }
  397.             // add vehicle length to get distance to tail of 1st follower (and then whole negative)
  398.             leaderDist =
  399.                     neighbors.getFollowers(lane).first().getDistance().plus(neighbors.getLeaders(lane).first().getLength());
  400.             leaderSpeed = neighbors.getFollowers(lane).first().getSpeed();
  401.             distanceToGap = neighbors.getFollowers(lane).first().getDistance().plus(getGtu().getLength()); // own nose to nose
  402.             putativeDistance = distanceToGap.plus(neighbors.getFollowers(lane).first().getLength());
  403.             putativeSpeed = leaderSpeed;
  404.         }

  405.         // limit by leader in current lane
  406.         if (!gap.equals(Gap.BACKWARD) && !neighbors.getLeaders(RelativeLane.CURRENT).isEmpty()
  407.                 && neighbors.getLeaders(RelativeLane.CURRENT).first().getDistance().lt(leaderDist))
  408.         {
  409.             leaderDist = neighbors.getLeaders(RelativeLane.CURRENT).first().getDistance();
  410.             leaderSpeed = neighbors.getLeaders(RelativeLane.CURRENT).first().getSpeed();
  411.             deltaFrontVehicle = 1;
  412.         }
  413.         else
  414.         {
  415.             deltaFrontVehicle = 0;
  416.         }

  417.         // calculate relative gap speed and effective gap
  418.         Speed dV = followerSpeed.minus(leaderSpeed);
  419.         Length effectiveGap = leaderDist.minus(followerDist);
  420.         // {@formatter:off}
  421.         // calculate utility
  422.         double util = constant
  423.                 + params.getParameter(ToledoLaneChangeParameters.BETA_DTG) * distanceToGap.si
  424.                 + params.getParameter(ToledoLaneChangeParameters.BETA_EG) * effectiveGap.si
  425.                 + params.getParameter(ToledoLaneChangeParameters.BETA_FV) * deltaFrontVehicle
  426.                 + params.getParameter(ToledoLaneChangeParameters.BETA_RGS) * dV.si
  427.                 + alpha * params.getParameter(ToledoLaneChangeParameters.ERROR_TERM);
  428.         // {@formatter:on}
  429.         return new GapInfo(util, putativeLength, putativeDistance, putativeSpeed);

  430.     }

  431.     /**
  432.      * Returns info regarding gap-acceptance.
  433.      * @param gtu GTU
  434.      * @param params parameters
  435.      * @param perception perception
  436.      * @param emuTg emu from target gap model
  437.      * @param eLead lead error
  438.      * @param eLag lag error
  439.      * @param lane lane to evaluate
  440.      * @return info regarding gap-acceptance
  441.      * @throws ParameterException if parameter is not defined
  442.      * @throws OperationalPlanException perception exception
  443.      */
  444.     private GapAcceptanceInfo getGapAcceptanceInfo(final LaneBasedGTU gtu, final Parameters params,
  445.             final LanePerception perception, final double emuTg, final double eLead, final double eLag, final RelativeLane lane)
  446.             throws ParameterException, OperationalPlanException
  447.     {

  448.         // get lead and lag utility and acceptance
  449.         double vLead;
  450.         double vLag;
  451.         boolean acceptLead;
  452.         boolean acceptLag;

  453.         NeighborsPerception neighbors = perception.getPerceptionCategory(NeighborsPerception.class);
  454.         if (neighbors.getLeaders(lane) != null && !neighbors.getLeaders(lane).isEmpty())
  455.         {
  456.             Speed dVLead = neighbors.getLeaders(lane).first().getSpeed().minus(gtu.getSpeed());
  457.             Length sLead = neighbors.getLeaders(lane).first().getDistance();
  458.             // critical gap
  459.             Length gLead =
  460.                     new Length(
  461.                             Math.exp(params.getParameter(ToledoLaneChangeParameters.C_LEAD)
  462.                                     + params.getParameter(ToledoLaneChangeParameters.BETA_POS_LEAD)
  463.                                             * Speed.max(dVLead, Speed.ZERO).si
  464.                                     + params.getParameter(ToledoLaneChangeParameters.BETA_NEG_LEAD)
  465.                                             * Speed.min(dVLead, Speed.ZERO).si
  466.                                     + params.getParameter(ToledoLaneChangeParameters.BETA_EMU_LEAD) * emuTg
  467.                                     + params.getParameter(ToledoLaneChangeParameters.ALPHA_TL_LEAD)
  468.                                             * params.getParameter(ToledoLaneChangeParameters.ERROR_TERM)
  469.                                     + eLead),
  470.                             LengthUnit.SI);
  471.             vLead = Math.log(gLead.si) - (params.getParameter(ToledoLaneChangeParameters.C_LEAD)
  472.                     + params.getParameter(ToledoLaneChangeParameters.BETA_POS_LEAD) * Speed.max(dVLead, Speed.ZERO).si
  473.                     + params.getParameter(ToledoLaneChangeParameters.BETA_NEG_LEAD) * Speed.min(dVLead, Speed.ZERO).si
  474.                     + params.getParameter(ToledoLaneChangeParameters.BETA_EMU_LEAD) * emuTg);
  475.             acceptLead = gLead.lt(sLead);
  476.         }
  477.         else
  478.         {
  479.             vLead = 0;
  480.             acceptLead = false;
  481.         }

  482.         if (neighbors.getFollowers(lane) != null && !neighbors.getFollowers(lane).isEmpty())
  483.         {
  484.             Speed dVLag = neighbors.getFollowers(lane).first().getSpeed().minus(gtu.getSpeed());
  485.             Length sLag = neighbors.getFollowers(lane).first().getDistance();
  486.             // critical gap
  487.             Length gLag =
  488.                     new Length(
  489.                             Math.exp(params.getParameter(ToledoLaneChangeParameters.C_LAG)
  490.                                     + params.getParameter(ToledoLaneChangeParameters.BETA_POS_LAG)
  491.                                             * Speed.max(dVLag, Speed.ZERO).si
  492.                                     + params.getParameter(ToledoLaneChangeParameters.BETA_EMU_LAG) * emuTg
  493.                                     + params.getParameter(ToledoLaneChangeParameters.ALPHA_TL_LAG)
  494.                                             * params.getParameter(ToledoLaneChangeParameters.ERROR_TERM)
  495.                                     + eLag),
  496.                             LengthUnit.SI);
  497.             vLag = Math.log(gLag.si) - (params.getParameter(ToledoLaneChangeParameters.C_LAG)
  498.                     + params.getParameter(ToledoLaneChangeParameters.BETA_POS_LAG) * Speed.max(dVLag, Speed.ZERO).si
  499.                     + params.getParameter(ToledoLaneChangeParameters.BETA_EMU_LAG) * emuTg);
  500.             acceptLag = gLag.lt(sLag);
  501.         }
  502.         else
  503.         {
  504.             vLag = 0;
  505.             acceptLag = false;
  506.         }

  507.         // gap acceptance emu
  508.         double vRatLead = vLead / params.getParameter(ToledoLaneChangeParameters.SIGMA_LEAD);
  509.         double vRatLag = vLag / params.getParameter(ToledoLaneChangeParameters.SIGMA_LAG);
  510.         double emuGa = vLead * cumNormDist(vRatLead)
  511.                 + params.getParameter(ToledoLaneChangeParameters.SIGMA_LEAD) * normDist(vRatLead) + vLag * cumNormDist(vRatLag)
  512.                 + params.getParameter(ToledoLaneChangeParameters.SIGMA_LAG) * normDist(vRatLag);

  513.         // return info
  514.         return new GapAcceptanceInfo(emuGa, acceptLead && acceptLag);

  515.     }

  516.     /**
  517.      * Returns the utility of a lane.
  518.      * @param gtu GTU
  519.      * @param params parameters
  520.      * @param perception perception
  521.      * @param emuGa emu from gap acceptance model
  522.      * @param sli speed limit info
  523.      * @param lane lane to evaluate
  524.      * @return utility of lane
  525.      * @throws ParameterException if parameter is not defined
  526.      * @throws OperationalPlanException perception exception
  527.      */
  528.     private double laneUtility(final LaneBasedGTU gtu, final Parameters params, final LanePerception perception,
  529.             final double emuGa, final SpeedLimitInfo sli, final RelativeLane lane)
  530.             throws ParameterException, OperationalPlanException
  531.     {

  532.         ToledoPerception toledo = perception.getPerceptionCategory(ToledoPerception.class);
  533.         if (!perception.getLaneStructure().getExtendedCrossSection().contains(lane))
  534.         {
  535.             return 0.0;
  536.         }

  537.         // get infrastructure info
  538.         boolean takeNextOffRamp = false;
  539.         for (InfrastructureLaneChangeInfoToledo info : toledo.getInfrastructureLaneChangeInfo(RelativeLane.CURRENT))
  540.         {
  541.             if (info.getSplitNumber() == 1)
  542.             {
  543.                 takeNextOffRamp = true;
  544.             }
  545.         }
  546.         int deltaNextExit = takeNextOffRamp ? 1 : 0;

  547.         Length dExit;
  548.         if (!toledo.getInfrastructureLaneChangeInfo(RelativeLane.CURRENT).isEmpty())
  549.         {
  550.             dExit = toledo.getInfrastructureLaneChangeInfo(RelativeLane.CURRENT).first().getRemainingDistance();
  551.         }
  552.         else
  553.         {
  554.             dExit = Length.POSITIVE_INFINITY;
  555.         }

  556.         int[] delta = new int[3];
  557.         int deltaAdd = 0;
  558.         if (!toledo.getInfrastructureLaneChangeInfo(lane).isEmpty())
  559.         {
  560.             InfrastructureLaneChangeInfo ilciLef = toledo.getInfrastructureLaneChangeInfo(lane).first();
  561.             if (ilciLef.getRequiredNumberOfLaneChanges() > 1 && ilciLef.getRequiredNumberOfLaneChanges() < 5)
  562.             {
  563.                 deltaAdd = ilciLef.getRequiredNumberOfLaneChanges() - 2;
  564.                 delta[deltaAdd] = 1;
  565.             }
  566.         }

  567.         // heavy neighbor
  568.         NeighborsPerception neighbors = perception.getPerceptionCategory(NeighborsPerception.class);
  569.         Length leaderLength = !lane.isCurrent() && !neighbors.getFirstLeaders(lane.getLateralDirectionality()).isEmpty()
  570.                 ? neighbors.getFirstLeaders(lane.getLateralDirectionality()).first().getLength() : Length.ZERO;
  571.         Length followerLength = !lane.isCurrent() && !neighbors.getFirstFollowers(lane.getLateralDirectionality()).isEmpty()
  572.                 ? neighbors.getFirstFollowers(lane.getLateralDirectionality()).first().getDistance() : Length.ZERO;
  573.         int deltaHeavy = leaderLength.gt(MAX_LIGHT_VEHICLE_LENGTH) || followerLength.gt(MAX_LIGHT_VEHICLE_LENGTH) ? 1 : 0;

  574.         // get density
  575.         LinearDensity d = getDensityInLane(gtu, perception, lane);

  576.         // tail gating
  577.         int deltaTailgate = 0;
  578.         if (lane.equals(RelativeLane.CURRENT) && !neighbors.getFollowers(RelativeLane.CURRENT).isEmpty()
  579.                 && neighbors.getFollowers(RelativeLane.CURRENT).first().getDistance().le(TAILGATE_LENGTH))
  580.         {
  581.             LinearDensity dL = getDensityInLane(gtu, perception, RelativeLane.LEFT);
  582.             LinearDensity dR = getDensityInLane(gtu, perception, RelativeLane.RIGHT);
  583.             LinearDensity dRoad = new LinearDensity((dL.si + d.si + dR.si) / 3, LinearDensityUnit.SI);
  584.             if (dRoad.le(LOS_DENSITY))
  585.             {
  586.                 deltaTailgate = 1;
  587.             }
  588.         }

  589.         // right most
  590.         // TODO definition of 'right most lane' does not account for ramps and weaving sections
  591.         int deltaRightMost = 0;
  592.         if (lane.equals(RelativeLane.CURRENT) && !toledo.getCrossSection().contains(RelativeLane.RIGHT))
  593.         {
  594.             deltaRightMost = 1;
  595.         }
  596.         else if (lane.equals(RelativeLane.RIGHT) && toledo.getCrossSection().contains(RelativeLane.RIGHT)
  597.                 && !toledo.getCrossSection().contains(RelativeLane.SECOND_RIGHT))
  598.         {
  599.             deltaRightMost = 1;
  600.         }

  601.         // current lane traffic
  602.         Speed vFront;
  603.         Length sFront;
  604.         if (lane.equals(RelativeLane.CURRENT))
  605.         {
  606.             if (!neighbors.getLeaders(RelativeLane.CURRENT).isEmpty())
  607.             {
  608.                 vFront = neighbors.getLeaders(RelativeLane.CURRENT).first().getSpeed();
  609.                 sFront = neighbors.getLeaders(RelativeLane.CURRENT).first().getDistance();
  610.             }
  611.             else
  612.             {
  613.                 vFront = getCarFollowingModel().desiredSpeed(params, sli);
  614.                 sFront = getCarFollowingModel().desiredHeadway(params, gtu.getSpeed());
  615.             }
  616.         }
  617.         else
  618.         {
  619.             vFront = Speed.ZERO;
  620.             sFront = Length.ZERO;
  621.         }

  622.         // target lane utility
  623.         double constant = 0;
  624.         double error = 0;
  625.         if (lane.equals(RelativeLane.CURRENT))
  626.         {
  627.             constant = params.getParameter(ToledoLaneChangeParameters.C_CL);
  628.             error = params.getParameter(ToledoLaneChangeParameters.ALPHA_CL)
  629.                     * params.getParameter(ToledoLaneChangeParameters.ERROR_TERM);
  630.         }
  631.         else if (lane.equals(RelativeLane.RIGHT))
  632.         {
  633.             constant = params.getParameter(ToledoLaneChangeParameters.C_RL);
  634.             error = params.getParameter(ToledoLaneChangeParameters.ALPHA_RL)
  635.                     * params.getParameter(ToledoLaneChangeParameters.ERROR_TERM);
  636.         }
  637.         // {@formatter:off}
  638.         return constant
  639.                 + params.getParameter(ToledoLaneChangeParameters.BETA_RIGHT_MOST) * deltaRightMost // 0 for LEFT
  640.                 + params.getParameter(ToledoLaneChangeParameters.BETA_VFRONT) * vFront.si // 0 for LEFT/RIGHT
  641.                 + params.getParameter(ToledoLaneChangeParameters.BETA_SFRONT) * sFront.si // 0 for LEFT/RIGHT
  642.                 + params.getParameter(ToledoLaneChangeParameters.BETA_DENSITY) * d.getInUnit(LinearDensityUnit.PER_KILOMETER)
  643.                 + params.getParameter(ToledoLaneChangeParameters.BETA_HEAVY_NEIGHBOUR) * deltaHeavy
  644.                 + params.getParameter(ToledoLaneChangeParameters.BETA_TAILGATE) * deltaTailgate
  645.                 + Math.pow(dExit.getInUnit(LengthUnit.KILOMETER), params.getParameter(ToledoLaneChangeParameters.THETA_MLC))
  646.                 * (params.getParameter(ToledoLaneChangeParameters.BETA1) * delta[0]
  647.                         + params.getParameter(ToledoLaneChangeParameters.BETA2) * delta[1]
  648.                         + params.getParameter(ToledoLaneChangeParameters.BETA3) * delta[2])
  649.                 + params.getParameter(ToledoLaneChangeParameters.BETA_NEXT_EXIT) * deltaNextExit
  650.                 + params.getParameter(ToledoLaneChangeParameters.BETA_ADD) * deltaAdd
  651.                 + params.getParameter(ToledoLaneChangeParameters.BETA_EMU_GA) * emuGa // 0 for CURRENT (given correct input)
  652.                 + error; // 0 for LEFT
  653.         // {@formatter:on}
  654.     }

  655.     /**
  656.      * Returns the density in the given lane based on the following and leading vehicles.
  657.      * @param gtu subject GTU
  658.      * @param perception perception
  659.      * @param lane lane to get density of
  660.      * @return density in the given lane based on the following and leading vehicles
  661.      * @throws OperationalPlanException perception exception
  662.      */
  663.     private LinearDensity getDensityInLane(final LaneBasedGTU gtu, final LanePerception perception, final RelativeLane lane)
  664.             throws OperationalPlanException
  665.     {
  666.         int nVehicles = 0;
  667.         NeighborsPerception neighbors = perception.getPerceptionCategory(NeighborsPerception.class);
  668.         Length up = Length.ZERO;
  669.         Length down = Length.ZERO;
  670.         for (HeadwayGTU neighbor : neighbors.getFollowers(lane))
  671.         {
  672.             nVehicles++;
  673.             down = neighbor.getDistance();
  674.         }
  675.         for (HeadwayGTU neighbor : neighbors.getLeaders(lane))
  676.         {
  677.             nVehicles++;
  678.             up = neighbor.getDistance();
  679.         }
  680.         if (nVehicles > 0)
  681.         {
  682.             return new LinearDensity(nVehicles / up.plus(down).getInUnit(LengthUnit.KILOMETER),
  683.                     LinearDensityUnit.PER_KILOMETER);
  684.         }
  685.         return LinearDensity.ZERO;
  686.     }

  687.     /**
  688.      * Returns the cumulative density function (CDF) at given value of the standard normal distribution.
  689.      * @param x value
  690.      * @return cumulative density function (CDF) at given value of the standard normal distribution
  691.      */
  692.     private static double cumNormDist(final double x)
  693.     {
  694.         return .5 * (1 + erf(x / Math.sqrt(2)));
  695.     }

  696.     /**
  697.      * Error function approximation using Horner's method.
  698.      * @param x value
  699.      * @return error function approximation
  700.      */
  701.     private static double erf(final double x)
  702.     {
  703.         double t = 1.0 / (1.0 + 0.5 * Math.abs(x));
  704.         // use Horner's method
  705.         // {@formatter:off}
  706.         double tau = t * Math.exp(-x * x - 1.26551223 + t * (1.00002368 + t * (0.37409196
  707.                 + t * (0.09678418 + t * (0.18628806 + t * (0.27886807 + t * (-1.13520398
  708.                         + t * (1.48851587 + t * (-0.82215223 + t * (0.17087277))))))))));
  709.         // {@formatter:on}
  710.         return x >= 0 ? 1 - tau : tau - 1;
  711.     }

  712.     /**
  713.      * Returns the probability density function (PDF) at given value of the standard normal distribution.
  714.      * @param x value
  715.      * @return probability density function (PDF) at given value of the standard normal distribution
  716.      */
  717.     private static double normDist(final double x)
  718.     {
  719.         return Math.exp(-x * x / 2) / Math.sqrt(2 * Math.PI);
  720.     }

  721.     /**
  722.      * Gap indicator in adjacent lane.
  723.      * <p>
  724.      * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
  725.      * <br>
  726.      * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
  727.      * <p>
  728.      * @version $Revision$, $LastChangedDate$, by $Author$, initial version Jul 11, 2016 <br>
  729.      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
  730.      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
  731.      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
  732.      */
  733.     private enum Gap
  734.     {
  735.         /** Gap in front of leader in adjacent lane. */
  736.         FORWARD,

  737.         /** Gap between follower and leader in adjacent lane. */
  738.         ADJACENT,

  739.         /** Gap behind follower in adjacent lane. */
  740.         BACKWARD;
  741.     }

  742.     /**
  743.      * Contains info regarding an adjacent gap.
  744.      * <p>
  745.      * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
  746.      * <br>
  747.      * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
  748.      * <p>
  749.      * @version $Revision$, $LastChangedDate$, by $Author$, initial version Jul 11, 2016 <br>
  750.      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
  751.      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
  752.      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
  753.      */
  754.     private class GapInfo implements Serializable
  755.     {

  756.         /** */
  757.         private static final long serialVersionUID = 20160811L;

  758.         /** Utility of the gap. */
  759.         private final double utility;

  760.         /** Length of the gap. */
  761.         private final Length length;

  762.         /** Distance towards the gap. */
  763.         private final Length distance;

  764.         /** Speed of the vehicle in front or behind the gap, always the closest. */
  765.         private final Speed speed;

  766.         /**
  767.          * @param utility utility of gap
  768.          * @param length length of the gap
  769.          * @param distance distance towards the gap
  770.          * @param speed speed of the vehicle in front or behind the gap, always the closest
  771.          */
  772.         GapInfo(final double utility, final Length length, final Length distance, final Speed speed)
  773.         {
  774.             this.utility = utility;
  775.             this.length = length;
  776.             this.distance = distance;
  777.             this.speed = speed;
  778.         }

  779.         /**
  780.          * @return utility
  781.          */
  782.         public final double getUtility()
  783.         {
  784.             return this.utility;
  785.         }

  786.         /**
  787.          * @return length
  788.          */
  789.         public final Length getLength()
  790.         {
  791.             return this.length;
  792.         }

  793.         /**
  794.          * @return distance
  795.          */
  796.         public final Length getDistance()
  797.         {
  798.             return this.distance;
  799.         }

  800.         /**
  801.          * @return speed
  802.          */
  803.         public final Speed getSpeed()
  804.         {
  805.             return this.speed;
  806.         }

  807.         /** {@inheritDoc} */
  808.         @Override
  809.         public String toString()
  810.         {
  811.             return "GapAcceptanceInfo [utility = " + this.utility + ", length = " + this.length + ", distance = "
  812.                     + this.distance + ", speed = " + this.speed + "]";
  813.         }

  814.     }

  815.     /**
  816.      * Contains info regarding gap-acceptance.
  817.      * <p>
  818.      * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
  819.      * <br>
  820.      * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
  821.      * <p>
  822.      * @version $Revision$, $LastChangedDate$, by $Author$, initial version Jul 11, 2016 <br>
  823.      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
  824.      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
  825.      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
  826.      */
  827.     private class GapAcceptanceInfo implements Serializable
  828.     {

  829.         /** */
  830.         private static final long serialVersionUID = 20160811L;

  831.         /** Emu value of gap-acceptance. */
  832.         private final double emu;

  833.         /** Whether gap is acceptable. */
  834.         private final boolean acceptable;

  835.         /**
  836.          * @param emu emu
  837.          * @param acceptable whether gap is acceptable
  838.          */
  839.         GapAcceptanceInfo(final double emu, final boolean acceptable)
  840.         {
  841.             this.emu = emu;
  842.             this.acceptable = acceptable;
  843.         }

  844.         /**
  845.          * @return emu
  846.          */
  847.         public final double getEmu()
  848.         {
  849.             return this.emu;
  850.         }

  851.         /**
  852.          * @return acceptable
  853.          */
  854.         public final boolean isAcceptable()
  855.         {
  856.             return this.acceptable;
  857.         }

  858.         /** {@inheritDoc} */
  859.         @Override
  860.         public String toString()
  861.         {
  862.             return "GapAcceptanceInfo [emu = " + this.emu + ", acceptable = " + this.acceptable + "]";
  863.         }

  864.     }

  865.     /** {@inheritDoc} */
  866.     @Override
  867.     public final String toString()
  868.     {
  869.         return "Toledo tactical planner.";
  870.     }

  871. }