View Javadoc
1   package org.opentrafficsim.road.gtu.lane.tactical.lmrs;
2   
3   import java.util.ArrayList;
4   import java.util.LinkedHashMap;
5   import java.util.LinkedHashSet;
6   import java.util.List;
7   import java.util.Map;
8   import java.util.Map.Entry;
9   import java.util.Set;
10  import java.util.function.BiFunction;
11  import java.util.function.Function;
12  import java.util.function.Supplier;
13  import java.util.stream.Collectors;
14  import java.util.stream.Stream;
15  
16  import org.djunits.unit.SpeedUnit;
17  import org.djunits.value.vdouble.scalar.Duration;
18  import org.djutils.exceptions.Throw;
19  import org.djutils.exceptions.Try;
20  import org.opentrafficsim.base.parameters.ParameterException;
21  import org.opentrafficsim.base.parameters.ParameterSet;
22  import org.opentrafficsim.base.parameters.ParameterTypes;
23  import org.opentrafficsim.base.parameters.Parameters;
24  import org.opentrafficsim.core.definitions.DefaultsNl;
25  import org.opentrafficsim.core.gtu.GtuException;
26  import org.opentrafficsim.core.gtu.GtuType;
27  import org.opentrafficsim.core.gtu.perception.DirectEgoPerception;
28  import org.opentrafficsim.core.parameters.ParameterFactoryOneShot;
29  import org.opentrafficsim.core.units.distributions.ContinuousDistSpeed;
30  import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
31  import org.opentrafficsim.road.gtu.lane.perception.CategoricalLanePerception;
32  import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
33  import org.opentrafficsim.road.gtu.lane.perception.categories.AnticipationTrafficPerception;
34  import org.opentrafficsim.road.gtu.lane.perception.categories.DirectInfrastructurePerception;
35  import org.opentrafficsim.road.gtu.lane.perception.categories.DirectIntersectionPerception;
36  import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.Anticipation;
37  import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.DirectNeighborsPerception;
38  import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.Estimation;
39  import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.PerceivedGtuType;
40  import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.PerceivedGtuType.AnticipationPerceivedGtuType;
41  import org.opentrafficsim.road.gtu.lane.perception.mental.AdaptationHeadway;
42  import org.opentrafficsim.road.gtu.lane.perception.mental.AdaptationLaneChangeDesire;
43  import org.opentrafficsim.road.gtu.lane.perception.mental.AdaptationSituationalAwareness;
44  import org.opentrafficsim.road.gtu.lane.perception.mental.AdaptationSpeed;
45  import org.opentrafficsim.road.gtu.lane.perception.mental.BehavioralAdaptation;
46  import org.opentrafficsim.road.gtu.lane.perception.mental.FactorEstimation;
47  import org.opentrafficsim.road.gtu.lane.perception.mental.Fuller;
48  import org.opentrafficsim.road.gtu.lane.perception.mental.Mental;
49  import org.opentrafficsim.road.gtu.lane.perception.mental.SumFuller;
50  import org.opentrafficsim.road.gtu.lane.perception.mental.ar.ArFuller;
51  import org.opentrafficsim.road.gtu.lane.perception.mental.ar.ArTask;
52  import org.opentrafficsim.road.gtu.lane.perception.mental.ar.ArTaskCarFollowing;
53  import org.opentrafficsim.road.gtu.lane.perception.mental.ar.ArTaskCarFollowingExp;
54  import org.opentrafficsim.road.gtu.lane.perception.mental.ar.ArTaskLaneChanging;
55  import org.opentrafficsim.road.gtu.lane.perception.mental.ar.ArTaskLaneChangingD;
56  import org.opentrafficsim.road.gtu.lane.perception.mental.ar.ArTaskRoadSideDistraction;
57  import org.opentrafficsim.road.gtu.lane.perception.mental.channel.AdaptationSpeedChannel;
58  import org.opentrafficsim.road.gtu.lane.perception.mental.channel.AdaptationUpdateTime;
59  import org.opentrafficsim.road.gtu.lane.perception.mental.channel.ChannelFuller;
60  import org.opentrafficsim.road.gtu.lane.perception.mental.channel.ChannelMental;
61  import org.opentrafficsim.road.gtu.lane.perception.mental.channel.ChannelTask;
62  import org.opentrafficsim.road.gtu.lane.perception.mental.channel.ChannelTaskAcceleration;
63  import org.opentrafficsim.road.gtu.lane.perception.mental.channel.ChannelTaskCarFollowing;
64  import org.opentrafficsim.road.gtu.lane.perception.mental.channel.ChannelTaskConflict;
65  import org.opentrafficsim.road.gtu.lane.perception.mental.channel.ChannelTaskCooperation;
66  import org.opentrafficsim.road.gtu.lane.perception.mental.channel.ChannelTaskLaneChange;
67  import org.opentrafficsim.road.gtu.lane.perception.mental.channel.ChannelTaskRoadSideDistraction;
68  import org.opentrafficsim.road.gtu.lane.perception.mental.channel.ChannelTaskScan;
69  import org.opentrafficsim.road.gtu.lane.perception.mental.channel.ChannelTaskSignal;
70  import org.opentrafficsim.road.gtu.lane.perception.mental.channel.ChannelTaskTrafficLight;
71  import org.opentrafficsim.road.gtu.lane.perception.mental.channel.IntersectionPerceptionChannel;
72  import org.opentrafficsim.road.gtu.lane.perception.mental.channel.NeighborsPerceptionChannel;
73  import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedTacticalPlannerFactory;
74  import org.opentrafficsim.road.gtu.lane.tactical.following.AbstractIdm;
75  import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
76  import org.opentrafficsim.road.gtu.lane.tactical.following.DesiredHeadwayModel;
77  import org.opentrafficsim.road.gtu.lane.tactical.following.DesiredSpeedModel;
78  import org.opentrafficsim.road.gtu.lane.tactical.following.Idm;
79  import org.opentrafficsim.road.gtu.lane.tactical.following.IdmPlus;
80  import org.opentrafficsim.road.gtu.lane.tactical.util.ConflictUtil;
81  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.Cooperation;
82  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.GapAcceptance;
83  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.LmrsParameters;
84  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.LmrsUtil;
85  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.MandatoryIncentive;
86  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.Synchronization;
87  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.Tailgating;
88  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.VoluntaryIncentive;
89  
90  import nl.tudelft.simulation.jstats.distributions.DistLogNormal;
91  import nl.tudelft.simulation.jstats.distributions.DistNormalTrunc;
92  import nl.tudelft.simulation.jstats.distributions.DistTriangular;
93  import nl.tudelft.simulation.jstats.streams.StreamInterface;
94  import picocli.CommandLine.ITypeConverter;
95  import picocli.CommandLine.Option;
96  
97  /**
98   * This tactical planner is general purpose for the use of the LMRS with any combination of defined sub-components. The class
99   * seconds as a parameter factory. For both model components and parameter values, the factory can be used specific to the GTU
100  * type. Model components can be set through command line arguments, or through calling {@link #set(Setting, Object)}.
101  * Parameters can be set through standard parameter factory methods.
102  * <p>
103  * <b>Usage</b><br>
104  * The factory can be used as:
105  *
106  * <pre>
107  * LmrsFactory&lt;Lmrs&gt; factory = new LmrsFactory&lt;&gt;(List.of(DefaultsNl.CAR, DefaultsNl.VAN), Lmrs::new);
108  *
109  * factory.set(Setting.ADAPTATION_SPEED, false);
110  * </pre>
111  *
112  * The GTU list is optional (default is {@code List.of(DefaultsNl.CAR, DefaultsNl.TRUCK)}), and the second
113  * {@link TacticalPlannerProvider} argument can also be given as a list to specifiy different providers per GTU type. All
114  * settings must be provided either for all GTU types, or for a specific GTU type with which the factory was initialized. GTUs
115  * of other types can be generated within the simulation, so long as any of their parent types is within the list of the
116  * factory.
117  * <p>
118  * <b>Command line arguments</b><br>
119  * This class can be mixed in with any program using command line arguments:
120  *
121  * <pre>
122  * &#64;Mixin
123  * private LmrsFactory&lt;Lmrs&gt; factory = new LmrsFactory&lt;&gt;(Lmrs::new);
124  *
125  * public static void main(String[] args)
126  * {
127  *     Program program = new Program();
128  *     CliUtil.changeOptionDefault(program, "gtuTypes", "NL.CAR|NL.VAN|NL.TRUCK");
129  *     CliUtil.execute(program, args);
130  * }
131  * </pre>
132  *
133  * <i>Note: command line arguments and programmatically setting settings (set method) should in principle not both be used
134  * within the same program. Default command line arguments should be changed by the option default as in the example above,
135  * before the input args are executed.</i>
136  * <p>
137  * Use command line argument {@code --help} to get a list of all available command line arguments. In the example the factory is
138  * initialized using the constructor of {@link Lmrs} as a supplier of the tactical planner class. Within the context of a
139  * program it may be necessary to use a supplier of an another implementation of a tactical planner instead. The supplier can be
140  * any function that follows the {@link TacticalPlannerProvider} signature, supplying an extension of
141  * {@link AbstractIncentivesTacticalPlanner}.
142  * <p>
143  * Command line arguments can be defined to contain values for specific GTU types. The following arguments allow a simulation
144  * with GTU types {@code NL.CAR} and {@code NL.TRUCK}, or any of their sub types. All GTUs will not generally keep to the slow
145  * lane ({@code incentiveKeep}), while trucks will limit themselves on the rightmost two lanes ({@code incentiveStayRight}). GTU
146  * types and the values that apply to them should be given in the same order. The number of values given should either be 1, or
147  * equal to the number of GTU types given.
148  *
149  * <pre>
150  * --gtuTypes=NL.CAR|NL.TRUCK --incentiveKeep=false --incentiveStayRight=false|true
151  * </pre>
152  *
153  * The pipe character ({@code |}) is used to separate values. If the pipe character is part of any value, values can be quoted.
154  * If a quote is part of a any value, the value can be quoted and the quote of the value can be escaped with a backslash \. The
155  * following code will set the {@code --gtuTypes} to {@code [NL.CAR, NL.VAN, NL.T|RUCK]}.
156  *
157  * <pre>
158  * CliUtil.execute(new CommandLine(myProgram).setTrimQuotes(true), new String[] {"--gtuTypes=NL.CAR|NL.VAN|\"NL.T|RUCK\""});
159  * </pre>
160  * <b>One-shot mode</b>
161  * <p>
162  * This class extends {@link ParameterFactoryOneShot} and can thus be used as a parameter factory. It supports one-shot mode.
163  * Any parameter set after {@link #setOneShotMode()} is called and before the next GTU is generated is only applied to said GTU.
164  * At any other moment parameters that are set are fixed. This class implements {@link #setOneShotMode()} to apply the same mode
165  * on model settings, applying only to the next GTU generated, after which settings are reverted to the state when
166  * {@link #setOneShotMode()} was called.
167  * <p>
168  * <b>Settings applicability</b><br>
169  * Many settings on perception components may only apply to some implementations of Fuller, see Table 1.<br>
170  * <br>
171  * <table border="1" style="text-align:center; padding:2px; border-spacing:0px; border-width:1px; border-style:solid">
172  * <caption><i>Table 1: Perception components for Fuller implementations</i></caption>
173  * <tr>
174  * <td style="text-align:left"><b>Setting</b></td>
175  * <td colspan="4" style="text-align:center"><b>Fuller implementation</b> (FULLER_IMPLEMENTATION)</td>
176  * </tr>
177  * <tr>
178  * <td></td>
179  * <td>NONE</td>
180  * <td>SUMMATIVE</td>
181  * <td>ANTICIPATION_RELIANCE</td>
182  * <td>ATTENTION_MATRIX</td>
183  * </tr>
184  * <tr>
185  * <td style="text-align:left">PRIMARY_TASK</td>
186  * <td>-</td>
187  * <td>-</td>
188  * <td>&#10003;</td>
189  * <td>-</td>
190  * </tr>
191  * <tr>
192  * <td style="text-align:left">TEMPORAL_ANTICIPATION</td>
193  * <td>-</td>
194  * <td>&#10003;</td>
195  * <td>&#10003;</td>
196  * <td>&#10003;</td>
197  * </tr>
198  * <tr>
199  * <td style="text-align:left">FRACTION_OVERESTIMATION</td>
200  * <td>-</td>
201  * <td>&#10003;</td>
202  * <td>&#10003;</td>
203  * <td>&#10003;</td>
204  * </tr>
205  * <tr>
206  * <td colspan="5"><i>Tasks for mental model</i></td>
207  * </tr>
208  * <tr>
209  * <td style="text-align:left">TASK_CAR_FOLLOWING</td>
210  * <td>-</td>
211  * <td>&#10003;</td>
212  * <td>&#10003;</td>
213  * <td>&#10003;</td>
214  * </tr>
215  * <tr>
216  * <td style="text-align:left">TASK_FREE_ACCELERATION</td>
217  * <td>-</td>
218  * <td>-</td>
219  * <td>-</td>
220  * <td>&#10003;</td>
221  * </tr>
222  * <tr>
223  * <td style="text-align:left">TASK_TRAFFIC_LIGHTS</td>
224  * <td>-</td>
225  * <td>-</td>
226  * <td>-</td>
227  * <td>&#10003;</td>
228  * </tr>
229  * <tr>
230  * <td style="text-align:left">TASK_SIGNAL</td>
231  * <td>-</td>
232  * <td>-</td>
233  * <td>-</td>
234  * <td>&#10003;</td>
235  * </tr>
236  * <tr>
237  * <td style="text-align:left">TASK_LANE_CHANGE</td>
238  * <td>-</td>
239  * <td>&#10003;</td>
240  * <td>&#10003;</td>
241  * <td>&#10003;</td>
242  * </tr>
243  * <tr>
244  * <td style="text-align:left">TASK_COOPERATION</td>
245  * <td>-</td>
246  * <td>-</td>
247  * <td>-</td>
248  * <td>&#10003;</td>
249  * </tr>
250  * <tr>
251  * <td style="text-align:left">TASK_CONFLICTS</td>
252  * <td>-</td>
253  * <td>-</td>
254  * <td>-</td>
255  * <td>&#10003;</td>
256  * </tr>
257  * <tr>
258  * <td style="text-align:left">TASK_ROADSIDE_DISTRACTION</td>
259  * <td>-</td>
260  * <td>&#10003;</td>
261  * <td>&#10003;</td>
262  * <td>&#10003;</td>
263  * </tr>
264  * <tr>
265  * <td colspan="5"><i>Behavioral adaptations for mental model</i></td>
266  * </tr>
267  * <tr>
268  * <td style="text-align:left">ADAPTATION_SPEED</td>
269  * <td>-</td>
270  * <td>&#10003;</td>
271  * <td>&#10003;</td>
272  * <td>&#10003;</td>
273  * </tr>
274  * <tr>
275  * <td style="text-align:left">ADAPTATION_HEADWAY</td>
276  * <td>-</td>
277  * <td>&#10003;</td>
278  * <td>&#10003;</td>
279  * <td>&#10003;</td>
280  * </tr>
281  * <tr>
282  * <td style="text-align:left">ADAPTATION_LANE_CHANGE</td>
283  * <td>-</td>
284  * <td>&#10003;</td>
285  * <td>&#10003;</td>
286  * <td>&#10003;</td>
287  * </tr>
288  * <tr>
289  * <td style="text-align:left">ADAPTATION_UPDATE_TIME</td>
290  * <td>-</td>
291  * <td>-</td>
292  * <td>-</td>
293  * <td>&#10003;</td>
294  * </tr>
295  * </table>
296  * <p>
297  * Copyright (c) 2026-2026 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
298  * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
299  * </p>
300  * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
301  * @param <T> tactical planner type
302  */
303 public class LmrsFactory<T extends AbstractIncentivesTacticalPlanner> extends ParameterFactoryOneShot
304         implements LaneBasedTacticalPlannerFactory<T>
305 {
306 
307     /** Remembered state to reset in one-shot mode. */
308     private Map<Setting<?>, List<?>> state;
309 
310     /** Random number stream. */
311     private StreamInterface stream;
312 
313     /** Distribution of vGain. */
314     private ContinuousDistSpeed vGainDist;
315 
316     /** Distribution of sigma. */
317     private DistTriangular sigmaDist;
318 
319     /** Distribution of fSpeed. */
320     private DistNormalTrunc fSpeedDist;
321 
322     /** LMRS provider. */
323     private final List<TacticalPlannerProvider<T>> lmrsProvider;
324 
325     /** GTU type IDs in order of multi-valued arguments. */
326     @Option(names = {"--gtuTypes"}, description = "GTU type IDs in order of multi-valued arguments.",
327             defaultValue = "NL.CAR|NL.TRUCK", split = "\\|", splitSynopsisLabel = "|")
328     private final List<String> gtuTypes;
329 
330     // LMRS
331 
332     /** Car-following model: IDM or IDM_PLUS (default). */
333     @Option(names = {"--carFollowingModel"}, description = "Car-following model: IDM or IDM_PLUS.", defaultValue = "IDM_PLUS",
334             split = "\\|", splitSynopsisLabel = "|", converter = CarFollowingModelConverter.class)
335     private List<BiFunction<DesiredHeadwayModel, DesiredSpeedModel, CarFollowingModel>> carFollowingModel =
336             listOf((h, v) -> new IdmPlus(h, v));
337 
338     /** Lane change synchronization: PASSIVE (default), PASSIVE_MOVING, ALIGN_GAP or ACTIVE. */
339     @Option(names = {"--synchronization"},
340             description = "Lane change synchronization: PASSIVE, PASSIVE_MOVING, ALIGN_GAP or ACTIVE.",
341             defaultValue = "PASSIVE", split = "\\|", splitSynopsisLabel = "|", converter = SynchronizationConverter.class)
342     private List<Synchronization> synchronization = listOf(Synchronization.PASSIVE);
343 
344     /** Lane change cooperation: PASSIVE (default), PASSIVE_MOVING or ACTIVE. */
345     @Option(names = {"--cooperation"}, description = "Lane change cooperation: PASSIVE, PASSIVE_MOVING or ACTIVE.",
346             defaultValue = "PASSIVE", split = "\\|", splitSynopsisLabel = "|", converter = CooperationConverter.class)
347     private List<Cooperation> cooperation = listOf(Cooperation.PASSIVE);
348 
349     /** Lane change gap-acceptance: INFORMED (default) or EGO_HEADWAY. */
350     @Option(names = {"--gapAcceptance"}, description = "Lane change gap-acceptance: INFORMED or EGO_HEADWAY.",
351             defaultValue = "INFORMED", split = "\\|", splitSynopsisLabel = "|", converter = GapAcceptanceConverter.class)
352     private List<GapAcceptance> gapAcceptance = listOf(GapAcceptance.INFORMED);
353 
354     // LMRS -> Mandatory incentives
355 
356     /** Mandatory lane change incentive for route (default: true). */
357     @Option(names = {"--incentiveRoute"}, description = "Mandatory lane change incentive for route.", defaultValue = "true",
358             split = "\\|", splitSynopsisLabel = "|", negatable = true)
359     private List<Boolean> incentiveRoute = listOf(true);
360 
361     /** Mandatory lane change incentive to join slow traffic at split and not block other traffic. */
362     @Option(names = {"--incentiveGetInLane"},
363             description = "Mandatory lane change incentive to join slow traffic at split and not block other traffic.",
364             defaultValue = "false", split = "\\|", splitSynopsisLabel = "|", negatable = true)
365     private List<Boolean> incentiveGetInLane = listOf(false);
366 
367     /** Custom mandatory incentives. */
368     private List<Set<Supplier<MandatoryIncentive>>> customMandatoryIncentives = List.of(new LinkedHashSet<>());
369 
370     // LMRS -> Voluntary incentives
371 
372     /** Voluntary lane change incentive for speed with courtesy (default: true). */
373     @Option(names = {"--incentiveSpeedWithCourtesy"}, description = "Voluntary lane change incentive for speed with courtesy.",
374             defaultValue = "true", split = "\\|", splitSynopsisLabel = "|", negatable = true)
375     private List<Boolean> incentiveSpeedWithCourtesy = listOf(true);
376 
377     /** Voluntary lane change incentive for cooperative lane changes (default: false). */
378     @Option(names = {"--incentiveCourtesy"}, description = "Voluntary lane change incentive for cooperative lane changes.",
379             defaultValue = "false", split = "\\|", splitSynopsisLabel = "|", negatable = true)
380     private List<Boolean> incentiveCourtesy = listOf(false);
381 
382     /** Voluntary lane change incentive to join the shortest queue (default: false). */
383     @Option(names = {"--incentiveQueue"}, description = "Voluntary lane change incentive to join the shortest queue.",
384             defaultValue = "false", split = "\\|", splitSynopsisLabel = "|", negatable = true)
385     private List<Boolean> incentiveQueue = listOf(false);
386 
387     /** Voluntary lane change incentive for trucks to stay in slowest rightmost two lanes (default: false). */
388     @Option(names = {"--incentiveStayRight"},
389             description = "Voluntary lane change incentive for trucks to stay in rightmost two lanes.", defaultValue = "false",
390             split = "\\|", splitSynopsisLabel = "|", negatable = true)
391     private List<Boolean> incentiveStayRight = listOf(false);
392 
393     /** Voluntary lane change incentive to keep to the slow lane (default: true). */
394     @Option(names = {"--incentiveKeep"}, description = "Voluntary lane change incentive to keep to the slow lane.",
395             defaultValue = "true", split = "\\|", splitSynopsisLabel = "|", negatable = true)
396     private List<Boolean> incentiveKeep = listOf(true);
397 
398     /** Custom voluntary incentives. */
399     private List<Set<Supplier<VoluntaryIncentive>>> customVoluntaryIncentives = List.of(new LinkedHashSet<>());
400 
401     // LMRS -> Acceleration incentives
402 
403     /** Acceleration incentive to slow down prior to a lower speed limit (default: false). */
404     @Option(names = {"--accelerationSpeedLimitTransition"},
405             description = "Acceleration incentive to slow down prior to a lower speed limit.", defaultValue = "false",
406             split = "\\|", splitSynopsisLabel = "|", negatable = true)
407     private List<Boolean> accelerationSpeedLimitTransition = listOf(false);
408 
409     /** Acceleration incentive to approach traffic lights (default: false). */
410     @Option(names = {"--accelerationTrafficLights"}, description = "Acceleration incentive to approach traffic lights.",
411             defaultValue = "false", split = "\\|", splitSynopsisLabel = "|", negatable = true)
412     private List<Boolean> accelerationTrafficLights = listOf(false);
413 
414     /** Acceleration incentive to approach intersection conflicts (default: false). */
415     @Option(names = {"--accelerationConflicts"}, description = "Acceleration incentive to approach intersection conflicts.",
416             defaultValue = "false", split = "\\|", splitSynopsisLabel = "|", negatable = true)
417     private List<Boolean> accelerationConflicts = listOf(false);
418 
419     /** Acceleration incentive to not overtake traffic in the left lane (default: false). */
420     @Option(names = {"--accelerationNoRightOvertake"},
421             description = "Acceleration incentive to not overtake traffic in the left lane.", defaultValue = "false",
422             split = "\\|", splitSynopsisLabel = "|", negatable = true)
423     private List<Boolean> accelerationNoRightOvertake = listOf(false);
424 
425     /** Custom acceleration incentives. */
426     private List<Set<Supplier<AccelerationIncentive>>> customAccelerationIncentives = List.of(new LinkedHashSet<>());
427 
428     // Fuller
429 
430     /** Implementation of Fuller: NONE (default), SUMMATIVE, ANTICIPATION_RELIANCE or ATTENTION_MATRIX. */
431     @Option(names = {"--fullerImplementation"},
432             description = "Implementation of Fuller: NONE, SUMMATIVE, ANTICIPATION_RELIANCE or ATTENTION_MATRIX.",
433             defaultValue = "ATTENTION_MATRIX", split = "\\|", splitSynopsisLabel = "|")
434     private List<FullerImplementation> fullerImplementation = listOf(FullerImplementation.NONE);
435 
436     /** Id of primary task under ANTICIPATION_RELIANCE (default: lane-changing). */
437     @Option(names = {"--primaryTask"}, description = "Id of primary task under ANTICIPATION_RELIANCE.",
438             defaultValue = "lane-changing", split = "\\|", splitSynopsisLabel = "|")
439     private List<String> primaryTask = listOf("lane-changing");
440 
441     /** Enables temporal constant-speed anticipation (default: true). */
442     @Option(names = {"--anticipation"}, description = "Enables temporal constant-speed anticipation.", defaultValue = "true",
443             split = "\\|", splitSynopsisLabel = "|", negatable = true)
444     private List<Boolean> temporalAnticipation = listOf(true);
445 
446     /** Fraction of drivers over-estimating speed and distance [0..1] (default: 1). */
447     @Option(names = {"--fractionOverEstimation"},
448             description = "Fraction of drivers over-estimating speed and distance [0..1].", defaultValue = "0.0", split = "\\|",
449             splitSynopsisLabel = "|")
450     private List<Double> fractionOverEstimation = listOf(1.0);
451 
452     // Fuller -> Tasks
453 
454     /** Enables car-following task (default: true). */
455     @Option(names = {"--carFollowingTask"}, description = "Enables car-following task.", defaultValue = "true", split = "\\|",
456             splitSynopsisLabel = "|", negatable = true)
457     private List<Boolean> carFollowingTask = listOf(true);
458 
459     /** Enables alternate car-following task under SUMMATIVE and ANTICIPATION_RELIANCE (default: false). */
460     @Option(names = {"--alternateCarFollowingTask"}, description = "Enables alternate car-following task (exponential).",
461             defaultValue = "false", split = "\\|", splitSynopsisLabel = "|", negatable = true)
462     private List<Boolean> alternateCarFollowingTask = listOf(false);
463 
464     /** Enables free acceleration task under ATTENTION_MATRIX (default: false). */
465     @Option(names = {"--freeAccelerationTask"},
466             description = "Enables free acceleration task, useful when updateTimeAdaptation is true.", defaultValue = "false",
467             split = "\\|", splitSynopsisLabel = "|", negatable = true)
468     private List<Boolean> freeAccelerationTask = listOf(false);
469 
470     /** Enables traffic lights task under ATTENTION_MATRIX (default: false). */
471     @Option(names = {"--trafficLightsTask"}, description = "Enables traffic light task.", defaultValue = "false", split = "\\|",
472             splitSynopsisLabel = "|", negatable = true)
473     private List<Boolean> trafficLightsTask = listOf(false);
474 
475     /** Enables signal task under ATTENTION_MATRIX (default: true). */
476     @Option(names = {"--signalTask"}, description = "Enables signal task.", defaultValue = "true", split = "\\|",
477             splitSynopsisLabel = "|", negatable = true)
478     private List<Boolean> signalTask = listOf(true);
479 
480     /** Enables lane-changing task (default: true). */
481     @Option(names = {"--laneChangingTask"}, description = "Enables lane-change task.", defaultValue = "true", split = "\\|",
482             splitSynopsisLabel = "|", negatable = true)
483     private List<Boolean> laneChangingTask = listOf(true);
484 
485     /** Enables alternate lane-changing task under SUMMATIVE and ANTICIPATION_RELIANCE (default: false). */
486     @Option(names = {"--alternateLaneChangingTask"}, description = "Enables alternate lane-changing task (lane change desire).",
487             defaultValue = "false", split = "\\|", splitSynopsisLabel = "|", negatable = true)
488     private List<Boolean> alternateLaneChangingTask = listOf(false);
489 
490     /** Enables cooperation task under ATTENTION_MATRIX (default: true). */
491     @Option(names = {"--cooperationTask"}, description = "Enables cooperation task.", defaultValue = "true", split = "\\|",
492             splitSynopsisLabel = "|", negatable = true)
493     private List<Boolean> cooperationTask = listOf(true);
494 
495     /** Enables conflicts task under ATTENTION_MATRIX (default: false). */
496     @Option(names = {"--conflictsTask"}, description = "Enables conflict task.", defaultValue = "false", split = "\\|",
497             splitSynopsisLabel = "|", negatable = true)
498     private List<Boolean> conflictsTask = listOf(false);
499 
500     /** Enables road-side distraction task (default: false). */
501     @Option(names = {"--roadSideDistractionTask"}, description = "Enables road-side distraction task.", defaultValue = "false",
502             split = "\\|", splitSynopsisLabel = "|", negatable = true)
503     private List<Boolean> roadSideDistractionTask = listOf(false);
504 
505     // Fuller -> Adaptations
506 
507     /** Enables behavioral speed adaptation. */
508     @Option(names = {"--speedAdaptation"}, description = "Enables behavioral speed adaptation.", defaultValue = "true",
509             split = "\\|", splitSynopsisLabel = "|", negatable = true)
510     private List<Boolean> speedAdaptation = listOf(true);
511 
512     /** Enables behavioral headway adaptation. */
513     @Option(names = {"--headwayAdaptation"}, description = "Enables behavioral headway adaptation.", defaultValue = "true",
514             split = "\\|", splitSynopsisLabel = "|", negatable = true)
515     private List<Boolean> headwayAdaptation = listOf(true);
516 
517     /** Enables behavioral voluntary lane change adaptation. */
518     @Option(names = {"--laneChangeAdaptation"}, description = "Enables behavioral voluntary lane change adaptation.",
519             defaultValue = "false", split = "\\|", splitSynopsisLabel = "|", negatable = true)
520     private List<Boolean> laneChangeAdaptation = listOf(false);
521 
522     /** Enables behavioral update time adaptation under ATTENTION_MATRIX. */
523     @Option(names = {"--updateTimeAdaptation"}, description = "Enables behavioral update time adaptation.",
524             defaultValue = "false", split = "\\|", splitSynopsisLabel = "|", negatable = true)
525     private List<Boolean> updateTimeAdaptation = listOf(false);
526 
527     // Social interactions
528 
529     /** Enables tailgating. Without tailgating, any social interaction still results in social pressure. */
530     @Option(names = {"--tailgating"},
531             description = "Enables tailgating. Without tailgating, any social interaction still results in social pressure.",
532             defaultValue = "false", split = "\\|", splitSynopsisLabel = "|", negatable = true)
533     private List<Boolean> tailgating = listOf(false);
534 
535     /** Enables lane changes due to social pressure. */
536     @Option(names = {"--socioLaneChange"}, description = "Enables lane changes due to social pressure.", defaultValue = "false",
537             split = "\\|", splitSynopsisLabel = "|", negatable = true)
538     private List<Boolean> socioLaneChange = listOf(false);
539 
540     /** Enables speed increase due to social pressure. */
541     @Option(names = {"--socioSpeed"}, description = "Enables speed increase due to social pressure.", defaultValue = "false",
542             split = "\\|", splitSynopsisLabel = "|", negatable = true)
543     private List<Boolean> socioSpeed = listOf(false);
544 
545     /**
546      * Shorthand to create a list of default value(s). This is used instead of {@code List.of()} to return a mutable list.
547      * @param <V> value type
548      * @param values values
549      * @return list of default value(s)
550      */
551     @SafeVarargs
552     private static <V> List<V> listOf(final V... values)
553     {
554         return Stream.of(values).collect(Collectors.toCollection(ArrayList::new));
555     }
556 
557     /**
558      * Constructor that will use default NL.CAR and NL.TRUCK GTU types.
559      * @param lmrsProvider provider for LMRS implementation, e.g. {@code Lmrs::new}
560      * @throws NullPointerException when lmrsProvider is null
561      */
562     public LmrsFactory(final TacticalPlannerProvider<T> lmrsProvider)
563     {
564         this(List.of(DefaultsNl.CAR, DefaultsNl.TRUCK), lmrsProvider);
565     }
566 
567     /**
568      * Constructor with given GTU types and default LMRS implementation class.
569      * @param gtuTypes GTU types
570      * @param lmrsProvider provider for LMRS implementation, e.g. {@code Lmrs::new}
571      * @throws NullPointerException when gtuTypes or lmrsProvider is null
572      */
573     public LmrsFactory(final List<GtuType> gtuTypes, final TacticalPlannerProvider<T> lmrsProvider)
574     {
575         Throw.whenNull(gtuTypes, "gtuTypes");
576         Throw.whenNull(lmrsProvider, "lmrsProvider");
577         this.gtuTypes = gtuTypes.stream().map((g) -> g.getId()).collect(Collectors.toCollection(ArrayList::new));
578         this.lmrsProvider = listOf(lmrsProvider);
579     }
580 
581     /**
582      * Constructor with given GTU types and LMRS implementation class per GTU type.
583      * @param gtuTypes GTU types
584      * @param lmrsProviders providers for LMRS implementation for each GTU type, e.g. {@code Lmrs::new}, may contain
585      *            {@code null} values (except first element)
586      * @throws NullPointerException when gtuTypes or lmrsProviders is null
587      * @throws IllegalArgumentException when gtuTypes and lmrsProviders are not of equal size
588      */
589     public LmrsFactory(final List<GtuType> gtuTypes, final List<TacticalPlannerProvider<T>> lmrsProviders)
590     {
591         Throw.whenNull(gtuTypes, "gtuTypes");
592         Throw.whenNull(lmrsProviders, "lmrsProviders");
593         Throw.when(gtuTypes.size() != lmrsProviders.size(), IllegalArgumentException.class,
594                 "gtuTypes and lmrsProviders are not of equal size");
595         this.gtuTypes = gtuTypes.stream().map((g) -> g.getId()).collect(Collectors.toList());
596         this.lmrsProvider = new ArrayList<>(lmrsProviders);
597     }
598 
599     /**
600      * Sets the factory to one-shot mode. All {@linkplain Setting}s, parameters and correlations set or added between a call to
601      * this method and to {@link #create(LaneBasedGtu) create()} are removed from the factory after the call to
602      * {@link #create(LaneBasedGtu) create()}.
603      */
604     @Override
605     public void setOneShotMode()
606     {
607         super.setOneShotMode();
608         this.state = new LinkedHashMap<>();
609     }
610 
611     /**
612      * Resets the factory to the state when {@link #setOneShotMode()} was called. Note that the parameter factory resets its own
613      * state when it has set parameter values.
614      * @param <V> setting value type
615      */
616     @SuppressWarnings("unchecked")
617     protected <V> void resetState()
618     {
619         if (this.state == null)
620         {
621             return;
622         }
623         for (Entry<Setting<?>, List<?>> entry : this.state.entrySet())
624         {
625             List<V> values = (List<V>) entry.getKey().getListFunction().apply(this);
626             values.clear();
627             values.addAll((List<V>) entry.getValue());
628         }
629         this.state = null;
630     }
631 
632     /**
633      * Sets the random number stream.
634      * @param stream random number stream
635      * @return this factory for method chaining
636      */
637     @SuppressWarnings("hiddenfield")
638     public LmrsFactory<T> setStream(final StreamInterface stream)
639     {
640         this.stream = stream;
641         this.vGainDist = new ContinuousDistSpeed(new DistLogNormal(stream, 3.379, 0.4), SpeedUnit.KM_PER_HOUR);
642         this.sigmaDist = new DistTriangular(stream, 0.0, 0.25, 1.0);
643         this.fSpeedDist = new DistNormalTrunc(stream, 123.7 / 120.0, 0.1, 0.8, 50.0);
644         return this;
645     }
646 
647     /**
648      * Sets the setting value for all GTU types.
649      * @param <V> value type
650      * @param setting setting
651      * @param value value used for all GTU types
652      * @return this factory for method chaining
653      */
654     public <V> LmrsFactory<T> set(final Setting<V> setting, final V value)
655     {
656         Throw.whenNull(value, "value");
657         final List<V> values = setting.getListFunction().apply(this);
658         saveState(setting, values);
659         values.clear();
660         values.add(value);
661         return this;
662     }
663 
664     /**
665      * Sets the setting value for given GTU type.
666      * @param <V> value type
667      * @param setting setting
668      * @param value value
669      * @param gtuType GTU type
670      * @return this factory for method chaining
671      * @throws IllegalArgumentException when the GTU type is not known
672      */
673     public <V> LmrsFactory<T> set(final Setting<V> setting, final V value, final GtuType gtuType)
674     {
675         Throw.whenNull(value, "value");
676         Throw.whenNull(gtuType, "gtuType");
677 
678         int gtuTypeIndex = this.gtuTypes.indexOf(gtuType.getId());
679         Throw.when(gtuTypeIndex < 0, IllegalArgumentException.class, "GTU type %s not defined.", gtuType.getId());
680 
681         final List<V> values = setting.getListFunction().apply(this);
682         saveState(setting, values);
683 
684         /*
685          * Append with null values if not going from 1 to N but from M to N. This is a weird state with an incomplete command
686          * line argument, and code setting a setting. In this state a default or global value is unknown. We try to deal with
687          * it, but when the setting is required for any GTU type M+1 through N an exception might arise later. This depends on
688          * whether the setting will also be set for those GTU types, or whether the setting is known for a parent type.
689          */
690         while (values.size() < this.gtuTypes.size())
691         {
692             values.add(null);
693         }
694         values.set(gtuTypeIndex, value);
695         return this;
696     }
697 
698     /**
699      * Saves the state of given setting when in one-shot mode and the setting was not already saved.
700      * @param <V> value type
701      * @param setting setting
702      * @param values value list
703      */
704     protected <V> void saveState(final Setting<V> setting, final List<V> values)
705     {
706         if (this.state != null && !this.state.containsKey(setting))
707         {
708             this.state.put(setting, new ArrayList<>(values));
709         }
710     }
711 
712     /**
713      * Returns value of setting for the GTU type.
714      * @param <V> value type
715      * @param setting setting
716      * @param gtuType GTU type
717      * @return value from value array for the GTU type
718      */
719     // This method is for sub classes, which cannot access the value lists directly
720     protected <V> V get(final Setting<V> setting, final GtuType gtuType)
721     {
722         return get(setting.getListFunction().apply(this), gtuType, gtuType);
723     }
724 
725     /**
726      * Returns value from value array for the GTU type.
727      * @param <V> value type
728      * @param values values
729      * @param gtuType GTU type
730      * @return value from value array for the GTU type
731      */
732     protected <V> V get(final List<V> values, final GtuType gtuType)
733     {
734         return get(values, gtuType, gtuType);
735     }
736 
737     /**
738      * Returns value from value array for the GTU type.
739      * @param <V> value type
740      * @param values values
741      * @param gtuType GTU type
742      * @param originalGtuType original GTU type (only for possible exception message)
743      * @return value from value array for the GTU type
744      */
745     private <V> V get(final List<V> values, final GtuType gtuType, final GtuType originalGtuType)
746     {
747         Throw.when(values.size() > 1 && this.gtuTypes.size() > values.size(), IllegalArgumentException.class,
748                 "Argument has %s values %s but %s GTU types are defined.", values.size(), values, this.gtuTypes.size());
749         if (values.size() == 1)
750         {
751             return values.get(0);
752         }
753         int index = this.gtuTypes.indexOf(gtuType.getId());
754         if (index < 0 || values.get(index) == null)
755         {
756             return get(values,
757                     gtuType.getParent().orElseThrow(() -> new IllegalArgumentException(
758                             "Unable to obtain setting value for GTU type " + originalGtuType.getId() + " or any parent.")),
759                     originalGtuType);
760         }
761         return values.get(index);
762     }
763 
764     @Override
765     public Parameters getParameters(final GtuType gtuType) throws ParameterException
766     {
767         ParameterSet parameters = new ParameterSet();
768         parameters.setDefaultParameters(LmrsUtil.class);
769         parameters.setDefaultParameters(LmrsParameters.class);
770         parameters.setDefaultParameters(AbstractIdm.class);
771         parameters.setDefaultParameters(ConflictUtil.class);
772         parameters.setDefaultParameter(ParameterTypes.T0);
773         parameters.setDefaultParameter(ParameterTypes.LANE_STRUCTURE);
774         parameters.setDefaultParameter(ParameterTypes.LOOKBACK);
775         parameters.setDefaultParameter(ParameterTypes.LOOKAHEAD);
776         parameters.setDefaultParameter(ParameterTypes.VCONG);
777         parameters.setDefaultParameter(ParameterTypes.LCDUR);
778 
779         // Fuller
780         if (!FullerImplementation.NONE.equals(get(this.fullerImplementation, gtuType)))
781         {
782             parameters.setParameter(ParameterTypes.TR, Duration.ZERO);
783             parameters.setDefaultParameter(AdaptationSituationalAwareness.TR_MAX);
784             parameters.setParameter(ChannelFuller.EST_FACTOR, 1.0);
785             parameters.setParameter(Fuller.OVER_EST,
786                     this.stream.nextDouble() <= get(this.fractionOverEstimation, gtuType) ? 1.0 : -1.0);
787             parameters.setDefaultParameter(ChannelTaskScan.TDSCAN);
788             if (get(this.headwayAdaptation, gtuType))
789             {
790                 parameters.setDefaultParameter(AdaptationHeadway.BETA_T);
791             }
792             if (get(this.speedAdaptation, gtuType))
793             {
794                 parameters.setDefaultParameter(AdaptationSpeed.BETA_V0);
795             }
796 
797             if (FullerImplementation.ATTENTION_MATRIX.equals(get(this.fullerImplementation, gtuType)))
798             {
799                 // Attention matrix
800                 parameters.setDefaultParameters(ChannelFuller.class);
801                 parameters.setDefaultParameters(ChannelMental.class);
802                 if (get(this.updateTimeAdaptation, gtuType))
803                 {
804                     parameters.setDefaultParameter(AdaptationUpdateTime.DT_MIN);
805                     parameters.setDefaultParameter(AdaptationUpdateTime.DT_MAX);
806                 }
807                 if (get(this.carFollowingTask, gtuType))
808                 {
809                     parameters.setDefaultParameter(ArTaskCarFollowingExp.HEXP);
810                 }
811                 if (get(this.trafficLightsTask, gtuType) || get(this.conflictsTask, gtuType))
812                 {
813                     // TODO parameters of enhanced intersection model
814                     parameters.setDefaultParameter(ChannelTaskConflict.HEGO);
815                     parameters.setDefaultParameter(ChannelTaskConflict.HCONF);
816                 }
817                 if (get(this.signalTask, gtuType))
818                 {
819                     parameters.setDefaultParameter(ChannelTaskSignal.TDSIGNAL);
820                 }
821             }
822             else
823             {
824                 // Summative or anticipation reliance
825                 parameters.setDefaultParameters(Fuller.class);
826                 parameters.setDefaultParameters(SumFuller.class);
827                 parameters.setDefaultParameter(AdaptationSituationalAwareness.SA);
828                 parameters.setDefaultParameter(AdaptationSituationalAwareness.SA_MIN);
829                 parameters.setDefaultParameter(AdaptationSituationalAwareness.SA_MAX);
830                 if (FullerImplementation.ANTICIPATION_RELIANCE.equals(get(this.fullerImplementation, gtuType)))
831                 {
832                     parameters.setDefaultParameter(ArFuller.ALPHA);
833                     parameters.setDefaultParameter(ArFuller.BETA);
834                 }
835                 if (get(this.alternateCarFollowingTask, gtuType))
836                 {
837                     parameters.setDefaultParameter(ArTaskCarFollowingExp.HEXP);
838                 }
839             }
840 
841         }
842 
843         // Social interactions
844         if (anySocialInteractions())
845         {
846             parameters.setDefaultParameter(Tailgating.RHO);
847         }
848         // Alternate default values in case of social interactions, including distributions for the speed-leading strategy
849         if (get(this.tailgating, gtuType))
850         {
851             parameters.setParameter(ParameterTypes.TMAX, Duration.ofSI(1.6));
852         }
853         if (get(this.socioLaneChange, gtuType) || get(this.socioSpeed, gtuType))
854         {
855             parameters.setParameter(LmrsParameters.VGAIN, this.vGainDist.get());
856             parameters.setParameter(LmrsParameters.SOCIO, this.sigmaDist.draw());
857         }
858         parameters.setParameter(ParameterTypes.FSPEED, this.fSpeedDist.draw());
859         return parameters;
860     }
861 
862     @Override
863     public T create(final LaneBasedGtu gtu) throws GtuException
864     {
865         GtuType gtuType = gtu.getType();
866 
867         // Car-following model
868         DesiredSpeedModel desiredSpeedModel =
869                 get(this.socioSpeed, gtuType) ? new SocioDesiredSpeed(AbstractIdm.DESIRED_SPEED) : AbstractIdm.DESIRED_SPEED;
870         CarFollowingModel cfModel = get(this.carFollowingModel, gtuType).apply(AbstractIdm.HEADWAY, desiredSpeedModel);
871 
872         // Perception
873         LanePerception perception = getPerception(gtu);
874 
875         // Tactical planner
876         Synchronization sync = get(this.synchronization, gtuType);
877         Cooperation coop = get(this.cooperation, gtuType);
878         GapAcceptance gapAccept = get(this.gapAcceptance, gtuType);
879         Tailgating tail = get(this.tailgating, gtuType) ? Tailgating.PRESSURE
880                 : (anySocialInteractions() ? Tailgating.RHO_ONLY : Tailgating.NONE);
881         T tacticalPlanner = get(this.lmrsProvider, gtuType).from(cfModel, gtu, perception, sync, coop, gapAccept, tail);
882 
883         // Mandatory incentives
884         if (get(this.incentiveRoute, gtuType))
885         {
886             tacticalPlanner.addMandatoryIncentive(IncentiveRoute.SINGLETON);
887         }
888         if (get(this.incentiveGetInLane, gtuType))
889         {
890             tacticalPlanner.addMandatoryIncentive(IncentiveGetInLane.SINGLETON);
891         }
892         get(this.customMandatoryIncentives, gtuType).forEach((s) -> tacticalPlanner.addMandatoryIncentive(s.get()));
893 
894         // Voluntary incentives
895         if (get(this.incentiveSpeedWithCourtesy, gtuType))
896         {
897             tacticalPlanner.addVoluntaryIncentive(IncentiveSpeedWithCourtesy.SINGLETON);
898         }
899         if (get(this.incentiveCourtesy, gtuType))
900         {
901             tacticalPlanner.addVoluntaryIncentive(IncentiveCourtesy.SINGLETON);
902         }
903         if (get(this.incentiveQueue, gtuType))
904         {
905             tacticalPlanner.addVoluntaryIncentive(IncentiveQueue.SINGLETON);
906         }
907         if (get(this.incentiveStayRight, gtuType))
908         {
909             tacticalPlanner.addVoluntaryIncentive(IncentiveStayRight.SINGLETON);
910         }
911         if (get(this.incentiveKeep, gtuType))
912         {
913             tacticalPlanner.addVoluntaryIncentive(IncentiveKeep.SINGLETON);
914         }
915         if (get(this.socioLaneChange, gtuType))
916         {
917             tacticalPlanner.addVoluntaryIncentive(IncentiveSocioSpeed.SINGLETON);
918         }
919         get(this.customVoluntaryIncentives, gtuType).forEach((s) -> tacticalPlanner.addVoluntaryIncentive(s.get()));
920 
921         // Acceleration incentives
922         if (get(this.accelerationSpeedLimitTransition, gtuType))
923         {
924             tacticalPlanner.addAccelerationIncentive(AccelerationSpeedLimitTransition.SINGLETON);
925         }
926         if (get(this.accelerationTrafficLights, gtuType))
927         {
928             tacticalPlanner.addAccelerationIncentive(AccelerationTrafficLights.SINGLETON);
929         }
930         if (get(this.accelerationConflicts, gtuType))
931         {
932             tacticalPlanner.addAccelerationIncentive(new AccelerationConflicts());
933         }
934         if (get(this.accelerationNoRightOvertake, gtuType))
935         {
936             tacticalPlanner.addAccelerationIncentive(AccelerationNoRightOvertake.SINGLETON);
937         }
938         get(this.customAccelerationIncentives, gtuType).forEach((s) -> tacticalPlanner.addAccelerationIncentive(s.get()));
939 
940         resetState();
941 
942         return tacticalPlanner;
943     }
944 
945     /**
946      * Returns whether social interactions are at play for any of the GTU types.
947      * @return whether social interactions are at play for any of the GTU types
948      */
949     private boolean anySocialInteractions()
950     {
951         return this.tailgating.contains(true) || this.socioLaneChange.contains(true) || this.socioSpeed.contains(true);
952     }
953 
954     /**
955      * Returns perception for the GTU.
956      * @param gtu GTU
957      * @return perception for the GTU
958      */
959     protected LanePerception getPerception(final LaneBasedGtu gtu)
960     {
961         GtuType gtuType = gtu.getType();
962 
963         Mental mental;
964         Estimation estimation;
965         Anticipation anticipation;
966 
967         if (FullerImplementation.ATTENTION_MATRIX.equals(get(this.fullerImplementation, gtuType)))
968         {
969             // tasks
970             LinkedHashSet<Function<LanePerception, Set<ChannelTask>>> taskSuppliers = new LinkedHashSet<>();
971             addChannelTask(taskSuppliers, get(this.carFollowingTask, gtuType), ChannelTaskCarFollowing.SUPPLIER);
972             addChannelTask(taskSuppliers, get(this.freeAccelerationTask, gtuType), ChannelTaskAcceleration.SUPPLIER);
973             addChannelTask(taskSuppliers, get(this.trafficLightsTask, gtuType), ChannelTaskTrafficLight.SUPPLIER);
974             addChannelTask(taskSuppliers, get(this.signalTask, gtuType), ChannelTaskSignal.SUPPLIER);
975             addChannelTask(taskSuppliers, get(this.laneChangingTask, gtuType), ChannelTaskLaneChange.SUPPLIER);
976             addChannelTask(taskSuppliers, get(this.cooperationTask, gtuType), ChannelTaskCooperation.SUPPLIER);
977             // TODO update conflict channel task to better intersection/infrastructure based model
978             addChannelTask(taskSuppliers, get(this.conflictsTask, gtuType), ChannelTaskConflict.SUPPLIER);
979             addChannelTask(taskSuppliers, get(this.roadSideDistractionTask, gtuType),
980                     new ChannelTaskRoadSideDistraction.Supplier(gtu));
981             addChannelTask(taskSuppliers, true, ChannelTaskScan.SUPPLIER);
982 
983             // behavioral adaptation
984             Set<BehavioralAdaptation> behavioralAdapatations = new LinkedHashSet<>();
985             if (get(this.speedAdaptation, gtuType))
986             {
987                 behavioralAdapatations.add(new AdaptationSpeedChannel());
988             }
989             if (get(this.headwayAdaptation, gtuType))
990             {
991                 behavioralAdapatations.add(new AdaptationHeadway());
992             }
993             if (get(this.laneChangeAdaptation, gtuType))
994             {
995                 behavioralAdapatations.add(AdaptationLaneChangeDesire.SINGLETON);
996             }
997             if (get(this.updateTimeAdaptation, gtuType))
998             {
999                 behavioralAdapatations.add(AdaptationUpdateTime.SINGLETON);
1000             }
1001 
1002             mental = new ChannelFuller(taskSuppliers, behavioralAdapatations);
1003 
1004             estimation = FactorEstimation.SINGLETON;
1005             anticipation = get(this.temporalAnticipation, gtuType) ? Anticipation.CONSTANT_SPEED : Anticipation.NONE;
1006         }
1007         else if (FullerImplementation.NONE.equals(get(this.fullerImplementation, gtuType)))
1008         {
1009             mental = null;
1010             estimation = Estimation.NONE;
1011             anticipation = Anticipation.NONE;
1012         }
1013         else
1014         {
1015             // SUMMATIVE or ANTICIPATION_RELIANCE
1016             Set<ArTask> tasks = new LinkedHashSet<>();
1017             FullerImplementation fullerImpl = get(this.fullerImplementation, gtuType);
1018 
1019             // tasks
1020             if (get(this.carFollowingTask, gtuType))
1021             {
1022                 Throw.when(get(this.alternateCarFollowingTask, gtuType), IllegalStateException.class,
1023                         "Both carFollowingTask and alternateCarFollowingTask are true");
1024                 tasks.add(ArTaskCarFollowing.SINGLETON);
1025             }
1026             else if (get(this.alternateCarFollowingTask, gtuType))
1027             {
1028                 tasks.add(ArTaskCarFollowingExp.SINGLETON);
1029             }
1030             if (get(this.laneChangingTask, gtuType))
1031             {
1032                 Throw.when(get(this.alternateLaneChangingTask, gtuType), IllegalStateException.class,
1033                         "Both laneChangingTask and alternateLaneChangingTask are true");
1034                 tasks.add(ArTaskLaneChanging.SINGLETON);
1035             }
1036             else if (get(this.alternateLaneChangingTask, gtuType))
1037             {
1038                 tasks.add(ArTaskLaneChangingD.SINGLETON);
1039             }
1040             if (get(this.roadSideDistractionTask, gtuType))
1041             {
1042                 tasks.add(new ArTaskRoadSideDistraction(gtu));
1043             }
1044 
1045             // behavioral adaptation
1046             Set<BehavioralAdaptation> behavioralAdapatations = new LinkedHashSet<>();
1047             if (get(this.speedAdaptation, gtuType))
1048             {
1049                 behavioralAdapatations.add(new AdaptationSpeed());
1050             }
1051             if (get(this.headwayAdaptation, gtuType))
1052             {
1053                 behavioralAdapatations.add(new AdaptationHeadway());
1054             }
1055             if (get(this.laneChangeAdaptation, gtuType))
1056             {
1057                 behavioralAdapatations.add(AdaptationLaneChangeDesire.SINGLETON);
1058             }
1059             behavioralAdapatations.add(new AdaptationSituationalAwareness());
1060 
1061             // summative or anticipation reliance
1062             if (FullerImplementation.SUMMATIVE.equals(fullerImpl))
1063             {
1064                 mental = new SumFuller<>(tasks, behavioralAdapatations);
1065             }
1066             else if (FullerImplementation.ANTICIPATION_RELIANCE.equals(fullerImpl))
1067             {
1068                 mental = new ArFuller(tasks, behavioralAdapatations, get(this.primaryTask, gtuType));
1069             }
1070             else
1071             {
1072                 throw new IllegalArgumentException("Unable to load Fuller model from setting " + fullerImpl);
1073             }
1074 
1075             estimation = FactorEstimation.SINGLETON;
1076             anticipation = get(this.temporalAnticipation, gtuType) ? Anticipation.CONSTANT_SPEED : Anticipation.NONE;
1077         }
1078 
1079         // categories
1080         LanePerception perception = new CategoricalLanePerception(gtu, mental);
1081         perception.addPerceptionCategory(new DirectEgoPerception<>(perception));
1082         perception.addPerceptionCategory(new DirectInfrastructurePerception(perception));
1083         perception.addPerceptionCategory(new AnticipationTrafficPerception(perception));
1084         if (FullerImplementation.NONE.equals(get(this.fullerImplementation, gtuType)))
1085         {
1086             perception.addPerceptionCategory(new DirectNeighborsPerception(perception, PerceivedGtuType.WRAP));
1087             perception.addPerceptionCategory(new DirectIntersectionPerception(perception, PerceivedGtuType.WRAP));
1088         }
1089         else if (FullerImplementation.ATTENTION_MATRIX.equals(get(this.fullerImplementation, gtuType)))
1090         {
1091             perception.addPerceptionCategory(new NeighborsPerceptionChannel(perception, estimation, anticipation));
1092             perception.addPerceptionCategory(new IntersectionPerceptionChannel(perception, estimation, anticipation));
1093         }
1094         else
1095         {
1096             PerceivedGtuType headwayGtuType = new AnticipationPerceivedGtuType(estimation, anticipation, () ->
1097             {
1098                 return Try.assign(() -> gtu.getParameters().getParameter(ParameterTypes.TR),
1099                         "Unable to obtain reaction time parameter");
1100             });
1101             perception.addPerceptionCategory(new DirectNeighborsPerception(perception, headwayGtuType));
1102             perception.addPerceptionCategory(new DirectIntersectionPerception(perception, headwayGtuType));
1103         }
1104         return perception;
1105     }
1106 
1107     /**
1108      * Add channel task if required.
1109      * @param taskSuppliers suppliers to add task suppliers to
1110      * @param task whether to add task
1111      * @param supplier supplier for the task
1112      */
1113     private void addChannelTask(final LinkedHashSet<Function<LanePerception, Set<ChannelTask>>> taskSuppliers,
1114             final boolean task, final Function<LanePerception, Set<ChannelTask>> supplier)
1115     {
1116         if (task)
1117         {
1118             taskSuppliers.add(supplier);
1119         }
1120     }
1121 
1122     // *************************************************************************************************************************
1123     // *********************************************** Nested Classes and Enums ************************************************
1124     // *************************************************************************************************************************
1125 
1126     /**
1127      * Provider for the correct tactical planner implementation.
1128      * @param <P> tactical planner class
1129      */
1130     public interface TacticalPlannerProvider<P extends AbstractIncentivesTacticalPlanner>
1131     {
1132         /**
1133          * Constructs an instance of the correct tactical planner class.
1134          * @param carFollowingModel car-following model
1135          * @param gtu GTU
1136          * @param lanePerception perception
1137          * @param synchronization type of synchronization
1138          * @param cooperation type of cooperation
1139          * @param gapAcceptance gap-acceptance
1140          * @param tailgating tailgating
1141          * @return instance of the correct tactical planner class
1142          */
1143         P from(CarFollowingModel carFollowingModel, LaneBasedGtu gtu, LanePerception lanePerception,
1144                 Synchronization synchronization, Cooperation cooperation, GapAcceptance gapAcceptance, Tailgating tailgating);
1145     }
1146 
1147     /**
1148      * Settings class with static instances.
1149      * @param <V> setting value type
1150      */
1151     public static final class Setting<V>
1152     {
1153         // LMRS
1154 
1155         /** Car-following model: IDM or IDM_PLUS (default). */
1156         public static final Setting<BiFunction<DesiredHeadwayModel, DesiredSpeedModel, CarFollowingModel>> CAR_FOLLOWING_MODEL =
1157                 new Setting<>((factory) -> factory.carFollowingModel);
1158 
1159         /** Lane change synchronization: PASSIVE (default), PASSIVE_MOVING, ALIGN_GAP or ACTIVE. */
1160         public static final Setting<Synchronization> SYNCHRONIZATION = new Setting<>((factory) -> factory.synchronization);
1161 
1162         /** Lane change cooperation: PASSIVE (default), PASSIVE_MOVING or ACTIVE. */
1163         public static final Setting<Cooperation> COOPERATION = new Setting<>((factory) -> factory.cooperation);
1164 
1165         /** Lane change gap-acceptance: INFORMED (default) or EGO_HEADWAY. */
1166         public static final Setting<GapAcceptance> GAP_ACCEPTANCE = new Setting<>((factory) -> factory.gapAcceptance);
1167 
1168         // LMRS -> Mandatory incentives
1169 
1170         /** Mandatory lane change incentive for route (default: true). */
1171         public static final Setting<Boolean> INCENTIVE_ROUTE = new Setting<>((factory) -> factory.incentiveRoute);
1172 
1173         /** Mandatory lane change incentive to join slow traffic at split and not block other traffic (default: false). */
1174         public static final Setting<Boolean> INCENTIVE_GET_IN_LANE = new Setting<>((factory) -> factory.incentiveGetInLane);
1175 
1176         /** Custom mandatory lane change incentives. */
1177         public static final Setting<Set<Supplier<MandatoryIncentive>>> CUSTOM_MANDATORY_INCENTIVES =
1178                 new Setting<>((factory) -> factory.customMandatoryIncentives);
1179 
1180         // LMRS -> Voluntary incentives
1181 
1182         /** Voluntary lane change incentive for speed with courtesy (default: true). */
1183         public static final Setting<Boolean> INCENTIVE_SPEED_WITH_COURTESY =
1184                 new Setting<>((factory) -> factory.incentiveSpeedWithCourtesy);
1185 
1186         /** Voluntary lane change incentive for cooperative lane changes (default: false). */
1187         public static final Setting<Boolean> INCENTIVE_COURTESY = new Setting<>((factory) -> factory.incentiveCourtesy);
1188 
1189         /** Voluntary lane change incentive to join the shortest queue (default: false). */
1190         public static final Setting<Boolean> INCENTIVE_QUEUE = new Setting<>((factory) -> factory.incentiveQueue);
1191 
1192         /** Voluntary lane change incentive for trucks to stay in slowest rightmost two lanes (default: false). */
1193         public static final Setting<Boolean> INCENTIVE_STAY_RIGHT = new Setting<>((factory) -> factory.incentiveStayRight);
1194 
1195         /** Voluntary lane change incentive to keep to the slow lane (default: true). */
1196         public static final Setting<Boolean> INCENTIVE_KEEP = new Setting<>((factory) -> factory.incentiveKeep);
1197 
1198         /** Custom voluntary lane change incentives. */
1199         public static final Setting<Set<Supplier<VoluntaryIncentive>>> CUSTOM_VOLUNTARY_INCENTIVES =
1200                 new Setting<>((factory) -> factory.customVoluntaryIncentives);
1201 
1202         // LMRS -> Acceleration incentives
1203 
1204         /** Acceleration incentive to slow down prior to a lower speed limit (default: false). */
1205         public static final Setting<Boolean> ACCELERATION_SPEED_LIMIT_TRANSITION =
1206                 new Setting<>((factory) -> factory.accelerationSpeedLimitTransition);
1207 
1208         /** Acceleration incentive to approach traffic lights (default: false). */
1209         public static final Setting<Boolean> ACCELERATION_TRAFFIC_LIGHTS =
1210                 new Setting<>((factory) -> factory.accelerationTrafficLights);
1211 
1212         /** Acceleration incentive to approach intersection conflicts (default: false). */
1213         public static final Setting<Boolean> ACCELERATION_CONFLICTS = new Setting<>((factory) -> factory.accelerationConflicts);
1214 
1215         /** Acceleration incentive to not overtake traffic in the left lane (default: false). */
1216         public static final Setting<Boolean> ACCELERATION_NO_RIGHT_OVERTAKE =
1217                 new Setting<>((factory) -> factory.accelerationNoRightOvertake);
1218 
1219         /** Custom acceleration incentives. */
1220         public static final Setting<Set<Supplier<AccelerationIncentive>>> CUSTOM_ACCELERATION_INCENTIVES =
1221                 new Setting<>((factory) -> factory.customAccelerationIncentives);
1222 
1223         // Fuller
1224 
1225         /** Implementation of Fuller: NONE (default), SUMMATIVE, ANTICIPATION_RELIANCE or ATTENTION_MATRIX. */
1226         public static final Setting<FullerImplementation> FULLER_IMPLEMENTATION =
1227                 new Setting<>((factory) -> factory.fullerImplementation);
1228 
1229         /** Id of primary task under ANTICIPATION_RELIANCE (default: lane-changing). */
1230         public static final Setting<String> PRIMARY_TASK = new Setting<>((factory) -> factory.primaryTask);
1231 
1232         /** Enables temporal constant-speed anticipation (default: true). */
1233         public static final Setting<Boolean> TEMPORAL_ANTICIPATION = new Setting<>((factory) -> factory.temporalAnticipation);
1234 
1235         /** Fraction of drivers over-estimating speed and distance [0..1] (default: 0). */
1236         public static final Setting<Double> FRACTION_OVERESTIMATION =
1237                 new Setting<>((factory) -> factory.fractionOverEstimation);
1238 
1239         // Fuller -> Tasks
1240 
1241         /** Enables car-following task (default: true). */
1242         public static final Setting<Boolean> TASK_CAR_FOLLOWING = new Setting<>((factory) -> factory.carFollowingTask);
1243 
1244         /** Enables alternate car-following task (default: false). */
1245         public static final Setting<Boolean> TASK_CAR_FOLLOWING_ALTERNATE =
1246                 new Setting<>((factory) -> factory.alternateCarFollowingTask);
1247 
1248         /** Enables free acceleration task under ATTENTION_MATRIX (default: false). */
1249         public static final Setting<Boolean> TASK_FREE_ACCELERATION = new Setting<>((factory) -> factory.freeAccelerationTask);
1250 
1251         /** Enables traffic lights task under ATTENTION_MATRIX (default: false). */
1252         public static final Setting<Boolean> TASK_TRAFFIC_LIGHTS = new Setting<>((factory) -> factory.trafficLightsTask);
1253 
1254         /** Enables signal task under ATTENTION_MATRIX (default: true). */
1255         public static final Setting<Boolean> TASK_SIGNAL = new Setting<>((factory) -> factory.signalTask);
1256 
1257         /** Enables lane-changing task (default: true). */
1258         public static final Setting<Boolean> TASK_LANE_CHANGE = new Setting<>((factory) -> factory.laneChangingTask);
1259 
1260         /** Enables alternate lane-changing task (default: false). */
1261         public static final Setting<Boolean> TASK_LANE_CHANGE_ALTERNATE =
1262                 new Setting<>((factory) -> factory.alternateLaneChangingTask);
1263 
1264         /** Enables cooperation task under ATTENTION_MATRIX (default: true). */
1265         public static final Setting<Boolean> TASK_COOPERATION = new Setting<>((factory) -> factory.cooperationTask);
1266 
1267         /** Enables conflicts task under ATTENTION_MATRIX (default: false). */
1268         public static final Setting<Boolean> TASK_CONFLICTS = new Setting<>((factory) -> factory.conflictsTask);
1269 
1270         /** Enables road-side distraction task (default: false). */
1271         public static final Setting<Boolean> TASK_ROADSIDE_DISTRACTION =
1272                 new Setting<>((factory) -> factory.roadSideDistractionTask);
1273 
1274         // Fuller -> Adaptations
1275 
1276         /** Enables behavioral speed adaptation (default: true). */
1277         public static final Setting<Boolean> ADAPTATION_SPEED = new Setting<>((factory) -> factory.speedAdaptation);
1278 
1279         /** Enables behavioral headway adaptation (default: true). */
1280         public static final Setting<Boolean> ADAPTATION_HEADWAY = new Setting<>((factory) -> factory.headwayAdaptation);
1281 
1282         /** Enables behavioral voluntary lane change adaptation (default: false). */
1283         public static final Setting<Boolean> ADAPTATION_LANE_CHANGE = new Setting<>((factory) -> factory.laneChangeAdaptation);
1284 
1285         /** Enables behavioral update time adaptation under ATTENTION_MATRIX (default: false). */
1286         public static final Setting<Boolean> ADAPTATION_UPDATE_TIME = new Setting<>((factory) -> factory.updateTimeAdaptation);
1287 
1288         // Social interactions
1289 
1290         /** Enables tailgating. Without tailgating, any social interaction still results in social pressure (default: false). */
1291         public static final Setting<Boolean> SOCIO_TAILGATING = new Setting<>((factory) -> factory.tailgating);
1292 
1293         /** Enables lane changes due to social pressure (default: false). */
1294         public static final Setting<Boolean> SOCIO_LANE_CHANGE = new Setting<>((factory) -> factory.socioLaneChange);
1295 
1296         /** Enables speed increase due to social pressure (default: false). */
1297         public static final Setting<Boolean> SOCIO_SPEED = new Setting<>((factory) -> factory.socioSpeed);
1298 
1299         /** Function to return the right list. */
1300         private final Function<LmrsFactory<?>, List<V>> listFunction;
1301 
1302         /**
1303          * Constructor.
1304          * @param listFunction function to return the right list from the factory
1305          */
1306         private Setting(final Function<LmrsFactory<?>, List<V>> listFunction)
1307         {
1308             this.listFunction = listFunction;
1309         }
1310 
1311         /**
1312          * Returns the list function to return the right list from the factory.
1313          * @return list function to return the right list from the factory
1314          */
1315         public Function<LmrsFactory<?>, List<V>> getListFunction()
1316         {
1317             return this.listFunction;
1318         }
1319     }
1320 
1321     /**
1322      * Type of management of different tasks.
1323      */
1324     public enum FullerImplementation
1325     {
1326         /** No perception model. */
1327         NONE,
1328 
1329         /** Task demands are summed. */
1330         SUMMATIVE,
1331 
1332         /**
1333          * Task demand based on one primary and multiple auxiliary tasks. Requires parameters ALPHA and BETA and the id of the
1334          * primary task.
1335          */
1336         ANTICIPATION_RELIANCE,
1337 
1338         /** Task demand as steady-state in the attention matrix. */
1339         ATTENTION_MATRIX;
1340     }
1341 
1342     /**
1343      * Argument converter for car-following supplier.
1344      */
1345     private static class CarFollowingModelConverter
1346             implements ITypeConverter<BiFunction<DesiredHeadwayModel, DesiredSpeedModel, CarFollowingModel>>
1347     {
1348         /**
1349          * Constructor.
1350          */
1351         @SuppressWarnings({"unused", "redundantModifier"}) // referred to in @Option annotation
1352         public CarFollowingModelConverter()
1353         {
1354             //
1355         }
1356 
1357         @Override
1358         public BiFunction<DesiredHeadwayModel, DesiredSpeedModel, CarFollowingModel> convert(final String value)
1359                 throws Exception
1360         {
1361             switch (value.toLowerCase())
1362             {
1363                 case "idm":
1364                     return (h, v) -> new Idm(h, v);
1365                 case "idm_plus":
1366                     return (h, v) -> new IdmPlus(h, v);
1367                 default:
1368                     throw new IllegalArgumentException(
1369                             "Unable to parse car following model " + value + ". Use any of IDM or IDM_PLUS.");
1370             }
1371         }
1372     }
1373 
1374     /**
1375      * Argument converter for {@link Synchronization}.
1376      */
1377     private static class SynchronizationConverter implements ITypeConverter<Synchronization>
1378     {
1379         /**
1380          * Constructor.
1381          */
1382         @SuppressWarnings({"unused", "redundantModifier"}) // referred to in @Option annotation
1383         public SynchronizationConverter()
1384         {
1385             //
1386         }
1387 
1388         @Override
1389         public Synchronization convert(final String value) throws Exception
1390         {
1391             switch (value.toLowerCase())
1392             {
1393                 case "passive":
1394                     return Synchronization.PASSIVE;
1395                 case "passive_moving":
1396                     return Synchronization.PASSIVE_MOVING;
1397                 case "align_gap":
1398                     return Synchronization.ALIGN_GAP;
1399                 case "active":
1400                     return Synchronization.ACTIVE;
1401                 default:
1402                     throw new IllegalArgumentException("Unable to parse synchronization " + value
1403                             + ". Use any of PASSIVE, PASSIVE_MOVING, ALIGN_GAP or ACTIVE.");
1404             }
1405         }
1406     }
1407 
1408     /**
1409      * Argument converter for {@link Cooperation}.
1410      */
1411     private static class CooperationConverter implements ITypeConverter<Cooperation>
1412     {
1413         /**
1414          * Constructor.
1415          */
1416         @SuppressWarnings({"unused", "redundantModifier"}) // referred to in @Option annotation
1417         public CooperationConverter()
1418         {
1419             //
1420         }
1421 
1422         @Override
1423         public Cooperation convert(final String value) throws Exception
1424         {
1425             switch (value.toLowerCase())
1426             {
1427                 case "passive":
1428                     return Cooperation.PASSIVE;
1429                 case "passive_moving":
1430                     return Cooperation.PASSIVE_MOVING;
1431                 case "active":
1432                     return Cooperation.ACTIVE;
1433                 default:
1434                     throw new IllegalArgumentException(
1435                             "Unable to parse cooperation " + value + ". Use any of PASSIVE, PASSIVE_MOVING or ACTIVE.");
1436             }
1437         }
1438     }
1439 
1440     /**
1441      * Argument converter for {@link GapAcceptance}.
1442      */
1443     private static class GapAcceptanceConverter implements ITypeConverter<GapAcceptance>
1444     {
1445         /**
1446          * Constructor.
1447          */
1448         @SuppressWarnings({"unused", "redundantModifier"}) // referred to in @Option annotation
1449         public GapAcceptanceConverter()
1450         {
1451             //
1452         }
1453 
1454         @Override
1455         public GapAcceptance convert(final String value) throws Exception
1456         {
1457             switch (value.toLowerCase())
1458             {
1459                 case "informed":
1460                     return GapAcceptance.INFORMED;
1461                 case "ego_headway":
1462                     return GapAcceptance.EGO_HEADWAY;
1463                 default:
1464                     throw new IllegalArgumentException(
1465                             "Unable to parse gap-acceptance " + value + ". Use any of INFORMED or EGO_HEADWAY.");
1466             }
1467         }
1468     }
1469 
1470 }