1   package org.opentrafficsim.road.gtu.lane.tactical.toledo;
2   
3   import java.io.Serializable;
4   import java.util.Iterator;
5   import java.util.Random;
6   
7   import org.djunits.unit.AccelerationUnit;
8   import org.djunits.unit.LengthUnit;
9   import org.djunits.unit.LinearDensityUnit;
10  import org.djunits.value.vdouble.scalar.Acceleration;
11  import org.djunits.value.vdouble.scalar.Length;
12  import org.djunits.value.vdouble.scalar.LinearDensity;
13  import org.djunits.value.vdouble.scalar.Speed;
14  import org.djunits.value.vdouble.scalar.Time;
15  import org.opentrafficsim.base.parameters.ParameterException;
16  import org.opentrafficsim.base.parameters.ParameterTypeLength;
17  import org.opentrafficsim.base.parameters.ParameterTypes;
18  import org.opentrafficsim.base.parameters.Parameters;
19  import org.opentrafficsim.core.geometry.OTSGeometryException;
20  import org.opentrafficsim.core.gtu.GTUException;
21  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
22  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
23  import org.opentrafficsim.core.network.LateralDirectionality;
24  import org.opentrafficsim.core.network.NetworkException;
25  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
26  import org.opentrafficsim.road.gtu.lane.perception.CategoricalLanePerception;
27  import org.opentrafficsim.road.gtu.lane.perception.InfrastructureLaneChangeInfo;
28  import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
29  import org.opentrafficsim.road.gtu.lane.perception.PerceptionCollectable;
30  import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
31  import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.DirectNeighborsPerception;
32  import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.HeadwayGtuType;
33  import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.NeighborsPerception;
34  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTU;
35  import org.opentrafficsim.road.gtu.lane.plan.operational.LaneChange;
36  import org.opentrafficsim.road.gtu.lane.plan.operational.LaneOperationalPlanBuilder;
37  import org.opentrafficsim.road.gtu.lane.tactical.AbstractLaneBasedTacticalPlanner;
38  import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
39  import org.opentrafficsim.road.network.speed.SpeedLimitInfo;
40  import org.opentrafficsim.road.network.speed.SpeedLimitProspect;
41  
42  import nl.tudelft.simulation.language.d3.DirectedPoint;
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  public class Toledo extends AbstractLaneBasedTacticalPlanner
59  {
60  
61      
62      private static final long serialVersionUID = 20160711L;
63  
64      
65      protected static final ParameterTypeLength LOOKAHEAD = ParameterTypes.LOOKAHEAD;
66  
67      
68      static final Length MAX_LIGHT_VEHICLE_LENGTH = new Length(9.14, LengthUnit.METER);
69  
70      
71      static final Length TAILGATE_LENGTH = new Length(10, LengthUnit.METER);
72  
73      
74      static final LinearDensity LOS_DENSITY = new LinearDensity(16, LinearDensityUnit.PER_KILOMETER);
75  
76      
77      public static final Random RANDOM = new Random();
78  
79      
80      private final LaneChange laneChange = new LaneChange();
81  
82      
83  
84  
85  
86  
87      public Toledo(final CarFollowingModel carFollowingModel, final LaneBasedGTU gtu)
88      {
89          super(carFollowingModel, gtu, new CategoricalLanePerception(gtu));
90          getPerception().addPerceptionCategory(new ToledoPerception(getPerception()));
91          getPerception().addPerceptionCategory(new DirectNeighborsPerception(getPerception(), HeadwayGtuType.WRAP));
92      }
93  
94      
95      @Override
96      public final OperationalPlan generateOperationalPlan(final Time startTime, final DirectedPoint locationAtStartTime)
97              throws OperationalPlanException, GTUException, NetworkException, ParameterException
98      {
99  
100         
101         LanePerception perception = getPerception();
102         perception.perceive();
103         SpeedLimitProspect slp =
104                 perception.getPerceptionCategory(ToledoPerception.class).getSpeedLimitProspect(RelativeLane.CURRENT);
105         SpeedLimitInfo sli = slp.getSpeedLimitInfo(Length.ZERO);
106         Parameters params = getGtu().getParameters();
107 
108         Acceleration acceleration = null;
109 
110         
111         LateralDirectionality initiatedLaneChange;
112         NeighborsPerception neighbors = perception.getPerceptionCategory(NeighborsPerception.class);
113         if (!this.laneChange.isChangingLane())
114         {
115             
116 
117             
118             
119             GapInfo gapFwdL = getGapInfo(params, perception, Gap.FORWARD, RelativeLane.LEFT);
120             GapInfo gapAdjL = getGapInfo(params, perception, Gap.ADJACENT, RelativeLane.LEFT);
121             GapInfo gapBckL = getGapInfo(params, perception, Gap.BACKWARD, RelativeLane.LEFT);
122             GapInfo gapFwdR = getGapInfo(params, perception, Gap.FORWARD, RelativeLane.RIGHT);
123             GapInfo gapAdjR = getGapInfo(params, perception, Gap.ADJACENT, RelativeLane.RIGHT);
124             GapInfo gapBckR = getGapInfo(params, perception, Gap.BACKWARD, RelativeLane.RIGHT);
125             double emuTgL =
126                     Math.log(Math.exp(gapFwdL.getUtility()) + Math.exp(gapAdjL.getUtility()) + Math.exp(gapBckL.getUtility()));
127             double emuTgR =
128                     Math.log(Math.exp(gapFwdR.getUtility()) + Math.exp(gapAdjR.getUtility()) + Math.exp(gapBckR.getUtility()));
129 
130             
131             
132             double eLead = RANDOM.nextGaussian() * Math.pow(params.getParameter(ToledoLaneChangeParameters.SIGMA_LEAD), 2);
133             double eLag = RANDOM.nextGaussian() * Math.pow(params.getParameter(ToledoLaneChangeParameters.SIGMA_LAG), 2);
134             GapAcceptanceInfo gapAcceptL =
135                     getGapAcceptanceInfo(getGtu(), params, perception, emuTgL, eLead, eLag, RelativeLane.LEFT);
136             GapAcceptanceInfo gapAcceptR =
137                     getGapAcceptanceInfo(getGtu(), params, perception, emuTgR, eLead, eLag, RelativeLane.RIGHT);
138 
139             
140             double vL = laneUtility(getGtu(), params, perception, gapAcceptL.getEmu(), sli, RelativeLane.LEFT);
141             double vC = laneUtility(getGtu(), params, perception, 0, sli, RelativeLane.CURRENT);
142             double vR = laneUtility(getGtu(), params, perception, gapAcceptR.getEmu(), sli, RelativeLane.RIGHT);
143 
144             
145             if (vL > vR && vL > vC && gapAcceptL.isAcceptable())
146             {
147                 initiatedLaneChange = LateralDirectionality.LEFT;
148             }
149             else if (vR > vL && vR > vC && gapAcceptR.isAcceptable())
150             {
151                 initiatedLaneChange = LateralDirectionality.RIGHT;
152             }
153             else
154             {
155                 initiatedLaneChange = LateralDirectionality.NONE;
156             }
157 
158             
159             if (initiatedLaneChange.isNone())
160             {
161                 if ((vC > vR && vC > vL)
162                         || (!neighbors.getLeaders(RelativeLane.CURRENT).isEmpty() && neighbors.getLeaders(RelativeLane.CURRENT)
163                                 .first().getDistance().lt(getCarFollowingModel().desiredHeadway(params, getGtu().getSpeed()))))
164                 {
165                     acceleration = getCarFollowingModel().followingAcceleration(params, getGtu().getSpeed(), sli,
166                             neighbors.getLeaders(RelativeLane.CURRENT));
167                 }
168                 else
169                 {
170                     GapInfo gapAdj;
171                     GapInfo gapFwd;
172                     GapInfo gapBck;
173                     if (vL > vR && vL > vC)
174                     {
175                         gapAdj = gapAdjL;
176                         gapFwd = gapFwdL;
177                         gapBck = gapBckL;
178                     }
179                     else
180                     {
181                         gapAdj = gapAdjR;
182                         gapFwd = gapFwdR;
183                         gapBck = gapBckR;
184                     }
185                     if (gapAdj.getUtility() > gapFwd.getUtility() && gapAdj.getUtility() > gapBck.getUtility())
186                     {
187                         
188                         double eadj = Toledo.RANDOM.nextGaussian() * params.getParameter(ToledoLaneChangeParameters.SIGMA_ADJ)
189                                 * params.getParameter(ToledoLaneChangeParameters.SIGMA_ADJ);
190                         acceleration =
191                                 new Acceleration(
192                                         params.getParameter(ToledoLaneChangeParameters.C_ADJ_ACC)
193                                                 * (params.getParameter(ToledoLaneChangeParameters.BETA_DP)
194                                                         * gapAdj.getLength().si - gapAdj.getDistance().si)
195                                                 + eadj,
196                                         AccelerationUnit.SI);
197                     }
198                     else if (gapFwd.getUtility() > gapAdj.getUtility() && gapFwd.getUtility() > gapBck.getUtility())
199                     {
200                         
201                         Length desiredPosition = new Length(
202                                 gapFwd.getDistance().si
203                                         + params.getParameter(ToledoLaneChangeParameters.BETA_DP) * gapFwd.getLength().si,
204                                 LengthUnit.SI);
205                         double deltaV = gapFwd.getSpeed().si > getGtu().getSpeed().si
206                                 ? params.getParameter(ToledoLaneChangeParameters.LAMBDA_FWD_POS)
207                                         * (gapFwd.getSpeed().si - getGtu().getSpeed().si)
208                                 : params.getParameter(ToledoLaneChangeParameters.LAMBDA_FWD_NEG)
209                                         * (getGtu().getSpeed().si - gapFwd.getSpeed().si);
210                         double efwd = Toledo.RANDOM.nextGaussian() * params.getParameter(ToledoLaneChangeParameters.SIGMA_FWD)
211                                 * params.getParameter(ToledoLaneChangeParameters.SIGMA_FWD);
212                         acceleration = new Acceleration(params.getParameter(ToledoLaneChangeParameters.C_FWD_ACC)
213                                 * Math.pow(desiredPosition.si, params.getParameter(ToledoLaneChangeParameters.BETA_FWD))
214                                 * Math.exp(deltaV) + efwd, AccelerationUnit.SI);
215                     }
216                     else
217                     {
218                         
219                         Length desiredPosition = new Length(
220                                 gapBck.getDistance().si
221                                         + (1 - params.getParameter(ToledoLaneChangeParameters.BETA_DP)) * gapBck.getLength().si,
222                                 LengthUnit.SI);
223                         double deltaV = gapBck.getSpeed().si > getGtu().getSpeed().si
224                                 ? params.getParameter(ToledoLaneChangeParameters.LAMBDA_BCK_POS)
225                                         * (gapBck.getSpeed().si - getGtu().getSpeed().si)
226                                 : params.getParameter(ToledoLaneChangeParameters.LAMBDA_BCK_NEG)
227                                         * (getGtu().getSpeed().si - gapBck.getSpeed().si);
228                         double ebck = Toledo.RANDOM.nextGaussian() * params.getParameter(ToledoLaneChangeParameters.SIGMA_BCK)
229                                 * params.getParameter(ToledoLaneChangeParameters.SIGMA_BCK);
230                         acceleration = new Acceleration(params.getParameter(ToledoLaneChangeParameters.C_BCK_ACC)
231                                 * Math.pow(desiredPosition.si, params.getParameter(ToledoLaneChangeParameters.BETA_BCK))
232                                 * Math.exp(deltaV) + ebck, AccelerationUnit.SI);
233                     }
234                 }
235             }
236         }
237         else
238         {
239             initiatedLaneChange = LateralDirectionality.NONE;
240         }
241 
242         if (initiatedLaneChange.isLeft())
243         {
244             
245             getCarFollowingModel().followingAcceleration(params, getGtu().getSpeed(), sli,
246                     neighbors.getLeaders(RelativeLane.LEFT));
247         }
248         else if (initiatedLaneChange.isRight())
249         {
250             
251             getCarFollowingModel().followingAcceleration(params, getGtu().getSpeed(), sli,
252                     neighbors.getLeaders(RelativeLane.RIGHT));
253         }
254 
255         if (acceleration == null)
256         {
257             throw new Error("Acceleration from toledo model is null.");
258         }
259 
260         
261         if (initiatedLaneChange.isNone())
262         {
263             try
264             {
265                 return LaneOperationalPlanBuilder.buildAccelerationPlan(getGtu(), startTime, getGtu().getSpeed(), acceleration,
266                         params.getParameter(ToledoLaneChangeParameters.DT));
267             }
268             catch (OTSGeometryException exception)
269             {
270                 throw new OperationalPlanException(exception);
271             }
272         }
273 
274         try
275         {
276             OperationalPlan plan = LaneOperationalPlanBuilder.buildAccelerationLaneChangePlan(getGtu(), initiatedLaneChange,
277                     getGtu().getLocation(), startTime, getGtu().getSpeed(), acceleration,
278                     params.getParameter(ToledoLaneChangeParameters.DT), this.laneChange);
279             return plan;
280         }
281         catch (OTSGeometryException exception)
282         {
283             throw new OperationalPlanException(exception);
284         }
285     }
286 
287     
288 
289 
290 
291 
292 
293 
294 
295 
296 
297     private GapInfo getGapInfo(final Parameters params, final LanePerception perception, final Gap gap, final RelativeLane lane)
298             throws ParameterException, OperationalPlanException
299     {
300 
301         
302         NeighborsPerception neighbors = perception.getPerceptionCategory(NeighborsPerception.class);
303         if (gap.equals(Gap.FORWARD) && (neighbors.getLeaders(lane) == null || neighbors.getLeaders(lane).isEmpty()))
304         {
305             
306             return new GapInfo(Double.NEGATIVE_INFINITY, null, null, null);
307         }
308         if (gap.equals(Gap.BACKWARD) && (neighbors.getFollowers(lane) == null || neighbors.getFollowers(lane).isEmpty()))
309         {
310             
311             return new GapInfo(Double.NEGATIVE_INFINITY, null, null, null);
312         }
313 
314         double constant; 
315         double alpha; 
316         Length distanceToGap; 
317         int deltaFrontVehicle; 
318         Length putativeLength; 
319         Length putativeDistance; 
320         Speed putativeSpeed; 
321         Length leaderDist; 
322         Speed leaderSpeed;
323         Length followerDist; 
324         Speed followerSpeed;
325 
326         if (gap.equals(Gap.FORWARD))
327         {
328             constant = params.getParameter(ToledoLaneChangeParameters.C_FWD_TG);
329             alpha = 0;
330             PerceptionCollectable<HeadwayGTU, LaneBasedGTU> itAble = neighbors.getLeaders(lane);
331             HeadwayGTU second = null;
332             if (!itAble.isEmpty())
333             {
334                 Iterator<HeadwayGTU> it = itAble.iterator();
335                 it.next();
336                 second = it.next();
337             }
338             if (second != null)
339             {
340                 
341                 Iterator<HeadwayGTU> it = neighbors.getLeaders(lane).iterator();
342                 it.next();
343                 HeadwayGTU leader = it.next();
344                 leaderDist = leader.getDistance();
345                 leaderSpeed = leader.getSpeed();
346                 putativeLength = leaderDist.minus(neighbors.getLeaders(lane).first().getDistance())
347                         .minus(neighbors.getLeaders(lane).first().getLength());
348             }
349             else
350             {
351                 
352                 
353                 leaderDist = Length.POSITIVE_INFINITY;
354                 leaderSpeed = Speed.POSITIVE_INFINITY;
355                 putativeLength = leaderDist;
356             }
357             
358             followerDist =
359                     neighbors.getLeaders(lane).first().getDistance().plus(neighbors.getLeaders(lane).first().getLength());
360             followerSpeed = neighbors.getLeaders(lane).first().getSpeed();
361             distanceToGap = followerDist; 
362             putativeDistance = distanceToGap;
363             putativeSpeed = followerSpeed;
364         }
365         else if (gap.equals(Gap.ADJACENT))
366         {
367             constant = 0;
368             alpha = params.getParameter(ToledoLaneChangeParameters.ALPHA_ADJ);
369             distanceToGap = Length.ZERO;
370             if (neighbors.getLeaders(lane) != null && !neighbors.getLeaders(lane).isEmpty())
371             {
372                 leaderDist = neighbors.getLeaders(lane).first().getDistance();
373                 leaderSpeed = neighbors.getLeaders(lane).first().getSpeed();
374             }
375             else
376             {
377                 leaderDist = Length.POS_MAXVALUE;
378                 leaderSpeed = Speed.POS_MAXVALUE;
379             }
380             if (neighbors.getFollowers(lane) != null && !neighbors.getFollowers(lane).isEmpty())
381             {
382                 
383                 followerDist = neighbors.getFollowers(lane).first().getDistance().plus(getGtu().getLength());
384                 followerSpeed = neighbors.getFollowers(lane).first().getSpeed();
385                 putativeDistance = followerDist;
386             }
387             else
388             {
389                 followerDist = Length.NEG_MAXVALUE;
390                 followerSpeed = Speed.NEG_MAXVALUE;
391                 putativeDistance = Length.POS_MAXVALUE;
392             }
393             putativeSpeed = null;
394             putativeLength = leaderDist.plus(followerDist).plus(getGtu().getLength());
395         }
396         else
397         {
398             constant = params.getParameter(ToledoLaneChangeParameters.C_BCK_TG);
399             alpha = params.getParameter(ToledoLaneChangeParameters.ALPHA_BCK);
400             deltaFrontVehicle = 0;
401             PerceptionCollectable<HeadwayGTU, LaneBasedGTU> itAble = neighbors.getFollowers(lane);
402             HeadwayGTU second = null;
403             if (!itAble.isEmpty())
404             {
405                 Iterator<HeadwayGTU> it = itAble.iterator();
406                 it.next();
407                 second = it.next();
408             }
409             if (second != null)
410             {
411                 
412                 Iterator<HeadwayGTU> it = neighbors.getFollowers(lane).iterator();
413                 it.next();
414                 HeadwayGTU follower = it.next();
415                 followerDist = follower.getDistance();
416                 followerSpeed = follower.getSpeed();
417                 putativeLength = followerDist.minus(neighbors.getFollowers(lane).first().getDistance())
418                         .minus(neighbors.getFollowers(lane).first().getLength());
419             }
420             else
421             {
422                 
423                 followerDist = Length.NEGATIVE_INFINITY;
424                 followerSpeed = Speed.NEGATIVE_INFINITY;
425                 putativeLength = followerDist;
426             }
427             
428             leaderDist =
429                     neighbors.getFollowers(lane).first().getDistance().plus(neighbors.getLeaders(lane).first().getLength());
430             leaderSpeed = neighbors.getFollowers(lane).first().getSpeed();
431             distanceToGap = neighbors.getFollowers(lane).first().getDistance().plus(getGtu().getLength()); 
432             putativeDistance = distanceToGap.plus(neighbors.getFollowers(lane).first().getLength());
433             putativeSpeed = leaderSpeed;
434         }
435 
436         
437         if (!gap.equals(Gap.BACKWARD) && !neighbors.getLeaders(RelativeLane.CURRENT).isEmpty()
438                 && neighbors.getLeaders(RelativeLane.CURRENT).first().getDistance().lt(leaderDist))
439         {
440             leaderDist = neighbors.getLeaders(RelativeLane.CURRENT).first().getDistance();
441             leaderSpeed = neighbors.getLeaders(RelativeLane.CURRENT).first().getSpeed();
442             deltaFrontVehicle = 1;
443         }
444         else
445         {
446             deltaFrontVehicle = 0;
447         }
448 
449         
450         Speed dV = followerSpeed.minus(leaderSpeed);
451         Length effectiveGap = leaderDist.minus(followerDist);
452         
453         
454         double util = constant 
455                 + params.getParameter(ToledoLaneChangeParameters.BETA_DTG) * distanceToGap.si
456                 + params.getParameter(ToledoLaneChangeParameters.BETA_EG) * effectiveGap.si
457                 + params.getParameter(ToledoLaneChangeParameters.BETA_FV) * deltaFrontVehicle
458                 + params.getParameter(ToledoLaneChangeParameters.BETA_RGS) * dV.si
459                 + alpha * params.getParameter(ToledoLaneChangeParameters.ERROR_TERM);
460         
461         return new GapInfo(util, putativeLength, putativeDistance, putativeSpeed);
462 
463     }
464 
465     
466 
467 
468 
469 
470 
471 
472 
473 
474 
475 
476 
477 
478     private GapAcceptanceInfo getGapAcceptanceInfo(final LaneBasedGTU gtu, final Parameters params,
479             final LanePerception perception, final double emuTg, final double eLead, final double eLag, final RelativeLane lane)
480             throws ParameterException, OperationalPlanException
481     {
482 
483         
484         double vLead;
485         double vLag;
486         boolean acceptLead;
487         boolean acceptLag;
488 
489         NeighborsPerception neighbors = perception.getPerceptionCategory(NeighborsPerception.class);
490         if (neighbors.getLeaders(lane) != null && !neighbors.getLeaders(lane).isEmpty())
491         {
492             Speed dVLead = neighbors.getLeaders(lane).first().getSpeed().minus(gtu.getSpeed());
493             Length sLead = neighbors.getLeaders(lane).first().getDistance();
494             
495             Length gLead =
496                     new Length(
497                             Math.exp(params.getParameter(ToledoLaneChangeParameters.C_LEAD)
498                                     + params.getParameter(ToledoLaneChangeParameters.BETA_POS_LEAD)
499                                             * Speed.max(dVLead, Speed.ZERO).si
500                                     + params.getParameter(ToledoLaneChangeParameters.BETA_NEG_LEAD)
501                                             * Speed.min(dVLead, Speed.ZERO).si
502                                     + params.getParameter(ToledoLaneChangeParameters.BETA_EMU_LEAD) * emuTg
503                                     + params.getParameter(ToledoLaneChangeParameters.ALPHA_TL_LEAD)
504                                             * params.getParameter(ToledoLaneChangeParameters.ERROR_TERM)
505                                     + eLead),
506                             LengthUnit.SI);
507             vLead = Math.log(gLead.si) - (params.getParameter(ToledoLaneChangeParameters.C_LEAD)
508                     + params.getParameter(ToledoLaneChangeParameters.BETA_POS_LEAD) * Speed.max(dVLead, Speed.ZERO).si
509                     + params.getParameter(ToledoLaneChangeParameters.BETA_NEG_LEAD) * Speed.min(dVLead, Speed.ZERO).si
510                     + params.getParameter(ToledoLaneChangeParameters.BETA_EMU_LEAD) * emuTg);
511             acceptLead = gLead.lt(sLead);
512         }
513         else
514         {
515             vLead = 0;
516             acceptLead = false;
517         }
518 
519         if (neighbors.getFollowers(lane) != null && !neighbors.getFollowers(lane).isEmpty())
520         {
521             Speed dVLag = neighbors.getFollowers(lane).first().getSpeed().minus(gtu.getSpeed());
522             Length sLag = neighbors.getFollowers(lane).first().getDistance();
523             
524             Length gLag =
525                     new Length(
526                             Math.exp(params.getParameter(ToledoLaneChangeParameters.C_LAG)
527                                     + params.getParameter(ToledoLaneChangeParameters.BETA_POS_LAG)
528                                             * Speed.max(dVLag, Speed.ZERO).si
529                                     + params.getParameter(ToledoLaneChangeParameters.BETA_EMU_LAG) * emuTg
530                                     + params.getParameter(ToledoLaneChangeParameters.ALPHA_TL_LAG)
531                                             * params.getParameter(ToledoLaneChangeParameters.ERROR_TERM)
532                                     + eLag),
533                             LengthUnit.SI);
534             vLag = Math.log(gLag.si) - (params.getParameter(ToledoLaneChangeParameters.C_LAG)
535                     + params.getParameter(ToledoLaneChangeParameters.BETA_POS_LAG) * Speed.max(dVLag, Speed.ZERO).si
536                     + params.getParameter(ToledoLaneChangeParameters.BETA_EMU_LAG) * emuTg);
537             acceptLag = gLag.lt(sLag);
538         }
539         else
540         {
541             vLag = 0;
542             acceptLag = false;
543         }
544 
545         
546         double vRatLead = vLead / params.getParameter(ToledoLaneChangeParameters.SIGMA_LEAD);
547         double vRatLag = vLag / params.getParameter(ToledoLaneChangeParameters.SIGMA_LAG);
548         double emuGa = vLead * cumNormDist(vRatLead)
549                 + params.getParameter(ToledoLaneChangeParameters.SIGMA_LEAD) * normDist(vRatLead) + vLag * cumNormDist(vRatLag)
550                 + params.getParameter(ToledoLaneChangeParameters.SIGMA_LAG) * normDist(vRatLag);
551 
552         
553         return new GapAcceptanceInfo(emuGa, acceptLead && acceptLag);
554 
555     }
556 
557     
558 
559 
560 
561 
562 
563 
564 
565 
566 
567 
568 
569     private double laneUtility(final LaneBasedGTU gtu, final Parameters params, final LanePerception perception,
570             final double emuGa, final SpeedLimitInfo sli, final RelativeLane lane)
571             throws ParameterException, OperationalPlanException
572     {
573 
574         ToledoPerception toledo = perception.getPerceptionCategory(ToledoPerception.class);
575         if (!perception.getLaneStructure().getExtendedCrossSection().contains(lane))
576         {
577             return 0.0;
578         }
579 
580         
581         boolean takeNextOffRamp = false;
582         for (InfrastructureLaneChangeInfoToledo info : toledo.getInfrastructureLaneChangeInfo(RelativeLane.CURRENT))
583         {
584             if (info.getSplitNumber() == 1)
585             {
586                 takeNextOffRamp = true;
587             }
588         }
589         int deltaNextExit = takeNextOffRamp ? 1 : 0;
590 
591         Length dExit;
592         if (!toledo.getInfrastructureLaneChangeInfo(RelativeLane.CURRENT).isEmpty())
593         {
594             dExit = toledo.getInfrastructureLaneChangeInfo(RelativeLane.CURRENT).first().getRemainingDistance();
595         }
596         else
597         {
598             dExit = Length.POSITIVE_INFINITY;
599         }
600 
601         int[] delta = new int[3];
602         int deltaAdd = 0;
603         if (!toledo.getInfrastructureLaneChangeInfo(lane).isEmpty())
604         {
605             InfrastructureLaneChangeInfo ilciLef = toledo.getInfrastructureLaneChangeInfo(lane).first();
606             if (ilciLef.getRequiredNumberOfLaneChanges() > 1 && ilciLef.getRequiredNumberOfLaneChanges() < 5)
607             {
608                 deltaAdd = ilciLef.getRequiredNumberOfLaneChanges() - 2;
609                 delta[deltaAdd] = 1;
610             }
611         }
612 
613         
614         NeighborsPerception neighbors = perception.getPerceptionCategory(NeighborsPerception.class);
615         Length leaderLength = !lane.isCurrent() && !neighbors.getFirstLeaders(lane.getLateralDirectionality()).isEmpty()
616                 ? neighbors.getFirstLeaders(lane.getLateralDirectionality()).first().getLength() : Length.ZERO;
617         Length followerLength = !lane.isCurrent() && !neighbors.getFirstFollowers(lane.getLateralDirectionality()).isEmpty()
618                 ? neighbors.getFirstFollowers(lane.getLateralDirectionality()).first().getDistance() : Length.ZERO;
619         int deltaHeavy = leaderLength.gt(MAX_LIGHT_VEHICLE_LENGTH) || followerLength.gt(MAX_LIGHT_VEHICLE_LENGTH) ? 1 : 0;
620 
621         
622         LinearDensity d = getDensityInLane(gtu, perception, lane);
623 
624         
625         int deltaTailgate = 0;
626         if (lane.equals(RelativeLane.CURRENT) && !neighbors.getFollowers(RelativeLane.CURRENT).isEmpty()
627                 && neighbors.getFollowers(RelativeLane.CURRENT).first().getDistance().le(TAILGATE_LENGTH))
628         {
629             LinearDensity dL = getDensityInLane(gtu, perception, RelativeLane.LEFT);
630             LinearDensity dR = getDensityInLane(gtu, perception, RelativeLane.RIGHT);
631             LinearDensity dRoad = new LinearDensity((dL.si + d.si + dR.si) / 3, LinearDensityUnit.SI);
632             if (dRoad.le(LOS_DENSITY))
633             {
634                 deltaTailgate = 1;
635             }
636         }
637 
638         
639         
640         int deltaRightMost = 0;
641         if (lane.equals(RelativeLane.CURRENT) && !toledo.getCrossSection().contains(RelativeLane.RIGHT))
642         {
643             deltaRightMost = 1;
644         }
645         else if (lane.equals(RelativeLane.RIGHT) && toledo.getCrossSection().contains(RelativeLane.RIGHT)
646                 && !toledo.getCrossSection().contains(RelativeLane.SECOND_RIGHT))
647         {
648             deltaRightMost = 1;
649         }
650 
651         
652         Speed vFront;
653         Length sFront;
654         if (lane.equals(RelativeLane.CURRENT))
655         {
656             if (!neighbors.getLeaders(RelativeLane.CURRENT).isEmpty())
657             {
658                 vFront = neighbors.getLeaders(RelativeLane.CURRENT).first().getSpeed();
659                 sFront = neighbors.getLeaders(RelativeLane.CURRENT).first().getDistance();
660             }
661             else
662             {
663                 vFront = getCarFollowingModel().desiredSpeed(params, sli);
664                 sFront = getCarFollowingModel().desiredHeadway(params, gtu.getSpeed());
665             }
666         }
667         else
668         {
669             vFront = Speed.ZERO;
670             sFront = Length.ZERO;
671         }
672 
673         
674         double constant = 0;
675         double error = 0;
676         if (lane.equals(RelativeLane.CURRENT))
677         {
678             constant = params.getParameter(ToledoLaneChangeParameters.C_CL);
679             error = params.getParameter(ToledoLaneChangeParameters.ALPHA_CL)
680                     * params.getParameter(ToledoLaneChangeParameters.ERROR_TERM);
681         }
682         else if (lane.equals(RelativeLane.RIGHT))
683         {
684             constant = params.getParameter(ToledoLaneChangeParameters.C_RL);
685             error = params.getParameter(ToledoLaneChangeParameters.ALPHA_RL)
686                     * params.getParameter(ToledoLaneChangeParameters.ERROR_TERM);
687         }
688         
689         return constant
690                 + params.getParameter(ToledoLaneChangeParameters.BETA_RIGHT_MOST) * deltaRightMost 
691                 + params.getParameter(ToledoLaneChangeParameters.BETA_VFRONT) * vFront.si 
692                 + params.getParameter(ToledoLaneChangeParameters.BETA_SFRONT) * sFront.si 
693                 + params.getParameter(ToledoLaneChangeParameters.BETA_DENSITY) * d.getInUnit(LinearDensityUnit.PER_KILOMETER)
694                 + params.getParameter(ToledoLaneChangeParameters.BETA_HEAVY_NEIGHBOUR) * deltaHeavy
695                 + params.getParameter(ToledoLaneChangeParameters.BETA_TAILGATE) * deltaTailgate
696                 + Math.pow(dExit.getInUnit(LengthUnit.KILOMETER), params.getParameter(ToledoLaneChangeParameters.THETA_MLC)) 
697                 * (params.getParameter(ToledoLaneChangeParameters.BETA1) * delta[0] 
698                         + params.getParameter(ToledoLaneChangeParameters.BETA2) * delta[1] 
699                         + params.getParameter(ToledoLaneChangeParameters.BETA3) * delta[2])
700                 + params.getParameter(ToledoLaneChangeParameters.BETA_NEXT_EXIT) * deltaNextExit
701                 + params.getParameter(ToledoLaneChangeParameters.BETA_ADD) * deltaAdd
702                 + params.getParameter(ToledoLaneChangeParameters.BETA_EMU_GA) * emuGa 
703                 + error; 
704         
705     }
706 
707     
708 
709 
710 
711 
712 
713 
714 
715     private LinearDensity getDensityInLane(final LaneBasedGTU gtu, final LanePerception perception, final RelativeLane lane)
716             throws OperationalPlanException
717     {
718         int nVehicles = 0;
719         NeighborsPerception neighbors = perception.getPerceptionCategory(NeighborsPerception.class);
720         Length up = Length.ZERO;
721         Length down = Length.ZERO;
722         for (HeadwayGTU neighbor : neighbors.getFollowers(lane))
723         {
724             nVehicles++;
725             down = neighbor.getDistance();
726         }
727         for (HeadwayGTU neighbor : neighbors.getLeaders(lane))
728         {
729             nVehicles++;
730             up = neighbor.getDistance();
731         }
732         if (nVehicles > 0)
733         {
734             return new LinearDensity(nVehicles / up.plus(down).getInUnit(LengthUnit.KILOMETER),
735                     LinearDensityUnit.PER_KILOMETER);
736         }
737         return LinearDensity.ZERO;
738     }
739 
740     
741 
742 
743 
744 
745     private static double cumNormDist(final double x)
746     {
747         return .5 * (1 + erf(x / Math.sqrt(2)));
748     }
749 
750     
751 
752 
753 
754 
755     private static double erf(final double x)
756     {
757         double t = 1.0 / (1.0 + 0.5 * Math.abs(x));
758         
759         
760         double tau = t * Math.exp(-x * x - 1.26551223 + t * (1.00002368 + t * (0.37409196 
761                 + t * (0.09678418 + t * (0.18628806 + t * (0.27886807 + t * (-1.13520398 
762                         + t * (1.48851587 + t * (-0.82215223 + t * (0.17087277))))))))));
763         
764         return x >= 0 ? 1 - tau : tau - 1;
765     }
766 
767     
768 
769 
770 
771 
772     private static double normDist(final double x)
773     {
774         return Math.exp(-x * x / 2) / Math.sqrt(2 * Math.PI);
775     }
776 
777     
778 
779 
780 
781 
782 
783 
784 
785 
786 
787 
788 
789     private enum Gap
790     {
791         
792         FORWARD,
793 
794         
795         ADJACENT,
796 
797         
798         BACKWARD;
799     }
800 
801     
802 
803 
804 
805 
806 
807 
808 
809 
810 
811 
812 
813     private class GapInfo implements Serializable
814     {
815 
816         
817         private static final long serialVersionUID = 20160811L;
818 
819         
820         private final double utility;
821 
822         
823         private final Length length;
824 
825         
826         private final Length distance;
827 
828         
829         private final Speed speed;
830 
831         
832 
833 
834 
835 
836 
837         GapInfo(final double utility, final Length length, final Length distance, final Speed speed)
838         {
839             this.utility = utility;
840             this.length = length;
841             this.distance = distance;
842             this.speed = speed;
843         }
844 
845         
846 
847 
848         public final double getUtility()
849         {
850             return this.utility;
851         }
852 
853         
854 
855 
856         public final Length getLength()
857         {
858             return this.length;
859         }
860 
861         
862 
863 
864         public final Length getDistance()
865         {
866             return this.distance;
867         }
868 
869         
870 
871 
872         public final Speed getSpeed()
873         {
874             return this.speed;
875         }
876 
877         
878         @Override
879         public String toString()
880         {
881             return "GapAcceptanceInfo [utility = " + this.utility + ", length = " + this.length + ", distance = "
882                     + this.distance + ", speed = " + this.speed + "]";
883         }
884 
885     }
886 
887     
888 
889 
890 
891 
892 
893 
894 
895 
896 
897 
898 
899     private class GapAcceptanceInfo implements Serializable
900     {
901 
902         
903         private static final long serialVersionUID = 20160811L;
904 
905         
906         private final double emu;
907 
908         
909         private final boolean acceptable;
910 
911         
912 
913 
914 
915         GapAcceptanceInfo(final double emu, final boolean acceptable)
916         {
917             this.emu = emu;
918             this.acceptable = acceptable;
919         }
920 
921         
922 
923 
924         public final double getEmu()
925         {
926             return this.emu;
927         }
928 
929         
930 
931 
932         public final boolean isAcceptable()
933         {
934             return this.acceptable;
935         }
936 
937         
938         @Override
939         public String toString()
940         {
941             return "GapAcceptanceInfo [emu = " + this.emu + ", acceptable = " + this.acceptable + "]";
942         }
943 
944     }
945 
946     
947     @Override
948     public final String toString()
949     {
950         return "Toledo tactical planner.";
951     }
952 
953 }