View Javadoc
1   package org.opentrafficsim.road.gtu.tactical.lmrs;
2   
3   import java.util.LinkedHashMap;
4   import java.util.LinkedHashSet;
5   import java.util.Map;
6   import java.util.Optional;
7   
8   import org.djunits.value.vdouble.scalar.Acceleration;
9   import org.djunits.value.vdouble.scalar.Length;
10  import org.djutils.immutablecollections.Immutable;
11  import org.djutils.immutablecollections.ImmutableLinkedHashMap;
12  import org.djutils.immutablecollections.ImmutableLinkedHashSet;
13  import org.djutils.immutablecollections.ImmutableSet;
14  import org.opentrafficsim.base.parameters.ParameterException;
15  import org.opentrafficsim.core.gtu.GtuException;
16  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
17  import org.opentrafficsim.road.gtu.LaneBasedGtu;
18  import org.opentrafficsim.road.gtu.perception.LanePerception;
19  import org.opentrafficsim.road.gtu.perception.RelativeLane;
20  import org.opentrafficsim.road.gtu.tactical.AbstractLaneBasedTacticalPlanner;
21  import org.opentrafficsim.road.gtu.tactical.TacticalContextEgo;
22  import org.opentrafficsim.road.gtu.tactical.following.CarFollowingModel;
23  import org.opentrafficsim.road.gtu.tactical.util.lmrs.Desire;
24  import org.opentrafficsim.road.gtu.tactical.util.lmrs.Incentive;
25  import org.opentrafficsim.road.gtu.tactical.util.lmrs.MandatoryIncentive;
26  import org.opentrafficsim.road.gtu.tactical.util.lmrs.VoluntaryIncentive;
27  
28  /**
29   * Tactical planner with mandatory lane change incentives, voluntary lane change incentives, and acceleration incentives. The
30   * order in which the incentives are given is relevant. A later incentive can request the result from an earlier incentive at
31   * the same time instance. Otherwise, it can only obtain the value of another incentive from the previous time step.
32   * <p>
33   * Copyright (c) 2013-2026 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
34   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
35   * </p>
36   * @author Alexander Verbraeck
37   * @author Peter Knoppers
38   * @author Wouter Schakel
39   */
40  public abstract class AbstractIncentivesTacticalPlanner extends AbstractLaneBasedTacticalPlanner
41          implements DesireBased, AccelerationBased
42  {
43  
44      /** Set of mandatory lane change incentives. */
45      private final LinkedHashSet<MandatoryIncentive> mandatoryIncentives = new LinkedHashSet<>();
46  
47      /** Set of voluntary lane change incentives. */
48      private final LinkedHashSet<VoluntaryIncentive> voluntaryIncentives = new LinkedHashSet<>();
49  
50      /** Set of acceleration incentives. */
51      private final LinkedHashSet<AccelerationIncentive> accelerationIncentives = new LinkedHashSet<>();
52  
53      /** Immutable set of mandatory lane change incentives. */
54      private ImmutableSet<MandatoryIncentive> mandatoryIncentivesImmutable =
55              new ImmutableLinkedHashSet<>(this.mandatoryIncentives, Immutable.WRAP);
56  
57      /** Immutable set of voluntary lane change incentives. */
58      private ImmutableSet<VoluntaryIncentive> voluntaryIncentivesImmutable =
59              new ImmutableLinkedHashSet<>(this.voluntaryIncentives, Immutable.WRAP);
60  
61      /** Immutable set of acceleration incentives. */
62      private ImmutableSet<AccelerationIncentive> accelerationIncentivesImmutable =
63              new ImmutableLinkedHashSet<>(this.accelerationIncentives, Immutable.WRAP);
64  
65      /** Map of mandatory desire. */
66      private Map<Class<? extends MandatoryIncentive>, Desire> mandatoryDesire = new LinkedHashMap<>();
67  
68      /** Map of voluntary desire. */
69      private Map<Class<? extends VoluntaryIncentive>, Desire> voluntaryDesire = new LinkedHashMap<>();
70  
71      /** Map of acceleration. */
72      private Map<Class<? extends AccelerationIncentive>, Acceleration> acceleration = new LinkedHashMap<>();
73  
74      /** Immutable map of mandatory desire. */
75      private ImmutableLinkedHashMap<Class<? extends MandatoryIncentive>, Desire> mandatoryDesireImmutable =
76              new ImmutableLinkedHashMap<>(this.mandatoryDesire, Immutable.WRAP);
77  
78      /** Immutable map of voluntary desire. */
79      private ImmutableLinkedHashMap<Class<? extends VoluntaryIncentive>, Desire> voluntaryDesireImmutable =
80              new ImmutableLinkedHashMap<>(this.voluntaryDesire, Immutable.WRAP);
81  
82      /** Latest total mandatory desire. */
83      private Desire mandatory;
84  
85      /** Latest total voluntary desire. */
86      private Desire voluntary;
87  
88      /**
89       * Constructor.
90       * @param carFollowingModel Car-following model.
91       * @param gtu GTU
92       * @param lanePerception perception
93       */
94      public AbstractIncentivesTacticalPlanner(final CarFollowingModel carFollowingModel, final LaneBasedGtu gtu,
95              final LanePerception lanePerception)
96      {
97          super(carFollowingModel, gtu, lanePerception);
98      }
99  
100     /**
101      * Adds a mandatory incentive. Ignores {@code null}.
102      * @param incentive Incentive to add.
103      */
104     public void addMandatoryIncentive(final MandatoryIncentive incentive)
105     {
106         if (incentive != null)
107         {
108             this.mandatoryIncentives.add(incentive);
109         }
110     }
111 
112     /**
113      * Adds a voluntary incentive. Ignores {@code null}.
114      * @param incentive Incentive to add.
115      */
116     public void addVoluntaryIncentive(final VoluntaryIncentive incentive)
117     {
118         if (incentive != null)
119         {
120             this.voluntaryIncentives.add(incentive);
121         }
122     }
123 
124     /**
125      * Adds an acceleration incentive. Ignores {@code null}.
126      * @param incentive Incentive to add.
127      */
128     public void addAccelerationIncentive(final AccelerationIncentive incentive)
129     {
130         if (incentive != null)
131         {
132             this.accelerationIncentives.add(incentive);
133         }
134     }
135 
136     /**
137      * Returns the mandatory incentives.
138      * @return mandatory incentives
139      */
140     public ImmutableSet<MandatoryIncentive> getMandatoryIncentives()
141     {
142         return this.mandatoryIncentivesImmutable;
143     }
144 
145     /**
146      * Returns the voluntary incentives.
147      * @return voluntary incentives
148      */
149     public ImmutableSet<VoluntaryIncentive> getVoluntaryIncentives()
150     {
151         return this.voluntaryIncentivesImmutable;
152     }
153 
154     /**
155      * Returns the acceleration incentives.
156      * @return acceleration incentives
157      */
158     public ImmutableSet<AccelerationIncentive> getAccelerationIncentives()
159     {
160         return this.accelerationIncentivesImmutable;
161     }
162 
163     /**
164      * Determines level of lane change desire for mandatory lane change incentives.
165      * @param context tactical information such as parameters and car-following model
166      * @return level of lane change desire for this incentive
167      * @throws ParameterException if a parameter is not given or out of bounds
168      * @throws OperationalPlanException in case of a perception exception
169      */
170     public Desire getMandatoryDesire(final TacticalContextEgo context) throws OperationalPlanException, ParameterException
171     {
172         double dLeftMandatory = 0.0;
173         double dRightMandatory = 0.0;
174         for (MandatoryIncentive incentive : this.mandatoryIncentives)
175         {
176             Desire d = incentive.determineDesire(context, this.mandatoryDesireImmutable);
177             this.mandatoryDesire.put(incentive.getClass(), d);
178             dLeftMandatory = Math.abs(d.left()) > Math.abs(dLeftMandatory) ? d.left() : dLeftMandatory;
179             dRightMandatory = Math.abs(d.right()) > Math.abs(dRightMandatory) ? d.right() : dRightMandatory;
180         }
181         this.mandatory = new Desire(dLeftMandatory, dRightMandatory);
182         return this.mandatory;
183     }
184 
185     /**
186      * Determines level of lane change desire for voluntary lane change incentives.
187      * @param context tactical information such as parameters and car-following model
188      * @return level of lane change desire for this incentive
189      * @throws ParameterException if a parameter is not given or out of bounds
190      * @throws OperationalPlanException in case of a perception exception
191      */
192     public Desire getVoluntaryDesire(final TacticalContextEgo context) throws OperationalPlanException, ParameterException
193     {
194         double dLeftVoluntary = 0;
195         double dRightVoluntary = 0;
196         for (VoluntaryIncentive incentive : this.voluntaryIncentives)
197         {
198             Desire d = incentive.determineDesire(context, this.mandatory, this.voluntaryDesireImmutable);
199             this.voluntaryDesire.put(incentive.getClass(), d);
200             dLeftVoluntary += d.left();
201             dRightVoluntary += d.right();
202         }
203         this.voluntary = new Desire(dLeftVoluntary, dRightVoluntary);
204         return this.voluntary;
205     }
206 
207     /**
208      * Returns the latest total mandatory desire.
209      * @return latest total mandatory desire
210      */
211     public Desire getLatestMandatoryDesire()
212     {
213         return this.mandatory;
214     }
215 
216     /**
217      * Returns the latest total voluntary desire.
218      * @return latest total voluntary desire
219      */
220     public Desire getLatestVoluntaryDesire()
221     {
222         return this.voluntary;
223     }
224 
225     @Override
226     public Optional<Desire> getLatestDesire(final Class<? extends Incentive> incentive)
227     {
228         if (this.voluntaryDesire.containsKey(incentive))
229         {
230             return Optional.ofNullable(this.voluntaryDesire.get(incentive));
231         }
232         return Optional.ofNullable(this.mandatoryDesire.get(incentive));
233     }
234 
235     /**
236      * Determine acceleration.
237      * @param context tactical information such as parameters and car-following model
238      * @param lane lane on which to consider the acceleration
239      * @param mergeDistance distance over which a lane change is impossible towards the lane, zero if current lane
240      * @return acceleration
241      * @throws ParameterException on missing parameter
242      * @throws GtuException when there is a problem with the state of the GTU when planning a path
243      */
244     public Acceleration getAcceleration(final TacticalContextEgo context, final RelativeLane lane, final Length mergeDistance)
245             throws ParameterException, GtuException
246     {
247         Acceleration a = Acceleration.POS_MAXVALUE;
248         for (AccelerationIncentive incentive : this.accelerationIncentives)
249         {
250             Acceleration aIncentive = incentive.accelerate(context, lane, mergeDistance);
251             this.acceleration.put(incentive.getClass(), aIncentive);
252             a = Acceleration.min(a, aIncentive);
253         }
254         return a;
255     }
256 
257     @Override
258     public Optional<Acceleration> getLatestAcceleration(final Class<? extends AccelerationIncentive> incentiveClass)
259     {
260         return Optional.ofNullable(this.acceleration.get(incentiveClass));
261     }
262 
263 }