View Javadoc
1   package org.opentrafficsim.demo;
2   
3   import java.io.BufferedWriter;
4   import java.io.IOException;
5   import java.rmi.RemoteException;
6   import java.util.ArrayList;
7   import java.util.LinkedHashMap;
8   import java.util.List;
9   import java.util.Map;
10  import java.util.Set;
11  
12  import org.djunits.unit.FrequencyUnit;
13  import org.djunits.unit.SpeedUnit;
14  import org.djunits.unit.TimeUnit;
15  import org.djunits.value.storage.StorageType;
16  import org.djunits.value.vdouble.scalar.Acceleration;
17  import org.djunits.value.vdouble.scalar.Direction;
18  import org.djunits.value.vdouble.scalar.Duration;
19  import org.djunits.value.vdouble.scalar.Length;
20  import org.djunits.value.vdouble.scalar.Speed;
21  import org.djunits.value.vdouble.scalar.Time;
22  import org.djunits.value.vdouble.vector.FrequencyVector;
23  import org.djunits.value.vdouble.vector.TimeVector;
24  import org.djunits.value.vdouble.vector.base.DoubleVector;
25  import org.djutils.cli.CliUtil;
26  import org.djutils.exceptions.Throw;
27  import org.djutils.exceptions.Try;
28  import org.opentrafficsim.base.CompressedFileWriter;
29  import org.opentrafficsim.base.parameters.ParameterException;
30  import org.opentrafficsim.base.parameters.ParameterSet;
31  import org.opentrafficsim.base.parameters.ParameterTypeDuration;
32  import org.opentrafficsim.base.parameters.ParameterTypes;
33  import org.opentrafficsim.base.parameters.Parameters;
34  import org.opentrafficsim.base.parameters.constraint.NumericConstraint;
35  import org.opentrafficsim.core.animation.gtu.colorer.AccelerationGTUColorer;
36  import org.opentrafficsim.core.animation.gtu.colorer.GTUColorer;
37  import org.opentrafficsim.core.animation.gtu.colorer.IDGTUColorer;
38  import org.opentrafficsim.core.animation.gtu.colorer.SpeedGTUColorer;
39  import org.opentrafficsim.core.animation.gtu.colorer.SwitchableGTUColorer;
40  import org.opentrafficsim.core.compatibility.Compatible;
41  import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
42  import org.opentrafficsim.core.geometry.OTSPoint3D;
43  import org.opentrafficsim.core.gtu.GTU;
44  import org.opentrafficsim.core.gtu.GTUCharacteristics;
45  import org.opentrafficsim.core.gtu.GTUDirectionality;
46  import org.opentrafficsim.core.gtu.GTUException;
47  import org.opentrafficsim.core.gtu.GTUType;
48  import org.opentrafficsim.core.gtu.perception.DirectEgoPerception;
49  import org.opentrafficsim.core.gtu.perception.EgoPerception;
50  import org.opentrafficsim.core.gtu.perception.Perception;
51  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
52  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
53  import org.opentrafficsim.core.network.LateralDirectionality;
54  import org.opentrafficsim.core.network.LinkType;
55  import org.opentrafficsim.core.network.Network;
56  import org.opentrafficsim.core.network.NetworkException;
57  import org.opentrafficsim.core.network.Node;
58  import org.opentrafficsim.core.network.route.Route;
59  import org.opentrafficsim.core.parameters.ParameterFactoryByType;
60  import org.opentrafficsim.road.gtu.colorer.GTUTypeColorer;
61  import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGTUCharacteristics;
62  import org.opentrafficsim.road.gtu.generator.od.DefaultGTUCharacteristicsGeneratorOD;
63  import org.opentrafficsim.road.gtu.generator.od.GTUCharacteristicsGeneratorOD;
64  import org.opentrafficsim.road.gtu.generator.od.ODApplier;
65  import org.opentrafficsim.road.gtu.generator.od.ODOptions;
66  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
67  import org.opentrafficsim.road.gtu.lane.VehicleModel;
68  import org.opentrafficsim.road.gtu.lane.perception.CategoricalLanePerception;
69  import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
70  import org.opentrafficsim.road.gtu.lane.perception.PerceptionCollectable;
71  import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
72  import org.opentrafficsim.road.gtu.lane.perception.categories.AnticipationTrafficPerception;
73  import org.opentrafficsim.road.gtu.lane.perception.categories.DirectInfrastructurePerception;
74  import org.opentrafficsim.road.gtu.lane.perception.categories.DirectIntersectionPerception;
75  import org.opentrafficsim.road.gtu.lane.perception.categories.InfrastructurePerception;
76  import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.DirectNeighborsPerception;
77  import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.HeadwayGtuType;
78  import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.NeighborsPerception;
79  import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTU;
80  import org.opentrafficsim.road.gtu.lane.plan.operational.LaneChange;
81  import org.opentrafficsim.road.gtu.lane.plan.operational.LaneOperationalPlanBuilder;
82  import org.opentrafficsim.road.gtu.lane.plan.operational.SimpleOperationalPlan;
83  import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedTacticalPlanner;
84  import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedTacticalPlannerFactory;
85  import org.opentrafficsim.road.gtu.lane.tactical.following.AbstractIDM;
86  import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
87  import org.opentrafficsim.road.gtu.lane.tactical.following.IDMPlus;
88  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.AbstractIncentivesTacticalPlanner;
89  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.AccelerationIncentive;
90  import org.opentrafficsim.road.gtu.lane.tactical.util.CarFollowingUtil;
91  import org.opentrafficsim.road.gtu.lane.tactical.util.TrafficLightUtil;
92  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.Cooperation;
93  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.Desire;
94  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.Incentive;
95  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.LmrsParameters;
96  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.LmrsUtil;
97  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlanner;
98  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlannerFactory;
99  import org.opentrafficsim.road.gtu.strategical.od.Categorization;
100 import org.opentrafficsim.road.gtu.strategical.od.Category;
101 import org.opentrafficsim.road.gtu.strategical.od.Interpolation;
102 import org.opentrafficsim.road.gtu.strategical.od.ODMatrix;
103 import org.opentrafficsim.road.gtu.strategical.route.LaneBasedStrategicalRoutePlannerFactory;
104 import org.opentrafficsim.road.network.OTSRoadNetwork;
105 import org.opentrafficsim.road.network.control.rampmetering.CycleTimeLightController;
106 import org.opentrafficsim.road.network.control.rampmetering.RampMetering;
107 import org.opentrafficsim.road.network.control.rampmetering.RampMeteringLightController;
108 import org.opentrafficsim.road.network.control.rampmetering.RampMeteringSwitch;
109 import org.opentrafficsim.road.network.control.rampmetering.RwsSwitch;
110 import org.opentrafficsim.road.network.factory.LaneFactory;
111 import org.opentrafficsim.road.network.lane.Lane;
112 import org.opentrafficsim.road.network.lane.LaneType;
113 import org.opentrafficsim.road.network.lane.OTSRoadNode;
114 import org.opentrafficsim.road.network.lane.Stripe.Permeable;
115 import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy;
116 import org.opentrafficsim.road.network.lane.object.sensor.Detector;
117 import org.opentrafficsim.road.network.lane.object.sensor.Detector.CompressionMethod;
118 import org.opentrafficsim.road.network.lane.object.sensor.SinkSensor;
119 import org.opentrafficsim.road.network.lane.object.trafficlight.SimpleTrafficLight;
120 import org.opentrafficsim.road.network.lane.object.trafficlight.TrafficLight;
121 import org.opentrafficsim.road.network.speed.SpeedLimitInfo;
122 import org.opentrafficsim.road.network.speed.SpeedLimitProspect;
123 import org.opentrafficsim.swing.script.AbstractSimulationScript;
124 
125 import nl.tudelft.simulation.dsol.logger.SimLogger;
126 import nl.tudelft.simulation.event.EventInterface;
127 import nl.tudelft.simulation.jstats.distributions.DistNormal;
128 import nl.tudelft.simulation.jstats.streams.StreamInterface;
129 import nl.tudelft.simulation.language.d3.DirectedPoint;
130 import picocli.CommandLine.Option;
131 
132 /**
133  * <p>
134  * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
135  * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
136  * <p>
137  * @version $Revision$, $LastChangedDate$, by $Author$, initial version 12 jun. 2019 <br>
138  * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
139  * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
140  * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
141  */
142 public class RampMeteringDemo extends AbstractSimulationScript
143 {
144 
145     /** Controlled car GTU type id. */
146     private static final String CONTROLLED_CAR_ID = "controlledCar";
147 
148     /** Parameter factory. */
149     private ParameterFactoryByType parameterFactory = new ParameterFactoryByType();
150 
151     /** Ramp metering. */
152     @Option(names = {"-r", "--rampMetering"}, description = "Ramp metering on or off", defaultValue = "true")
153     private boolean rampMetering;
154 
155     /** Whether to generate output. */
156     @Option(names = "--output", description = "Generate output.", negatable = true, defaultValue = "false")
157     private boolean output;
158 
159     /** Accepted gap. */
160     @Option(names = "--acceptedGap", description = "Accepted gap.", defaultValue = "0.5s")
161     private Duration acceptedGap;
162 
163     /** Main demand. */
164     private FrequencyVector mainDemand;
165 
166     /** Main demand string. */
167     @Option(names = "--mainDemand", description = "Main demand in veh/h.", defaultValue = "2000,3000,3900,3900,3000")
168     private String mainDemandString;
169 
170     /** Ramp demand. */
171     private FrequencyVector rampDemand;
172 
173     /** Ramp demand string. */
174     @Option(names = "--rampDemand", description = "Ramp demand in veh/h.", defaultValue = "500,500,500,500,500")
175     private String rampDemandString;
176 
177     /** Demand time. */
178     private TimeVector demandTime;
179 
180     /** Demand time string. */
181     @Option(names = "--demandTime", description = "Demand time in min.", defaultValue = "0,10,40,50,70")
182     private String demandTimeString;
183 
184     /** Scenario. */
185     @Option(names = "--scenario", description = "Scenario name.", defaultValue = "test")
186     private String scenario;
187 
188     /** GTUs in simulation. */
189     private Map<String, Double> gtusInSimulation = new LinkedHashMap<>();
190 
191     /** Total travel time, accumulated. */
192     private double totalTravelTime = 0.0;
193 
194     /** Total travel time delay, accumulated. */
195     private double totalTravelTimeDelay = 0.0;
196 
197     /**
198      * Constructor.
199      */
200     protected RampMeteringDemo()
201     {
202         super("Ramp metering 1", "Ramp metering 2");
203     }
204 
205     /**
206      * @param args String[] command line arguments
207      * @throws Exception any exception
208      */
209     public static void main(final String[] args) throws Exception
210     {
211         RampMeteringDemoo.html#RampMeteringDemo">RampMeteringDemo demo = new RampMeteringDemo();
212         CliUtil.changeOptionDefault(demo, "simulationTime", "4200s");
213         CliUtil.execute(demo, args);
214         demo.mainDemand =
215                 DoubleVector.instantiate(arrayFromString(demo.mainDemandString), FrequencyUnit.PER_HOUR, StorageType.DENSE);
216         demo.rampDemand =
217                 DoubleVector.instantiate(arrayFromString(demo.rampDemandString), FrequencyUnit.PER_HOUR, StorageType.DENSE);
218         demo.demandTime =
219                 DoubleVector.instantiate(arrayFromString(demo.demandTimeString), TimeUnit.BASE_MINUTE, StorageType.DENSE);
220         demo.start();
221     }
222 
223     /**
224      * Returns an array from a String.
225      * @param str String; string
226      * @return double[] array
227      */
228     private static double[] arrayFromString(final String str)
229     {
230         int n = 0;
231         for (String part : str.split(","))
232         {
233             n++;
234         }
235         double[] out = new double[n];
236         int i = 0;
237         for (String part : str.split(","))
238         {
239             out[i] = Double.valueOf(part);
240             i++;
241         }
242         return out;
243     }
244 
245     /** {@inheritDoc} */
246     @Override
247     protected OTSRoadNetwork setupSimulation(final OTSSimulatorInterface sim) throws Exception
248     {
249         SimLogger.setSimulator(sim);
250 
251         OTSRoadNetwork network = new OTSRoadNetwork("RampMetering", true);
252         if (this.output)
253         {
254             network.addListener(this, Network.GTU_ADD_EVENT);
255             network.addListener(this, Network.GTU_REMOVE_EVENT);
256         }
257         GTUType car = network.getGtuType(GTUType.DEFAULTS.CAR);
258         GTUType controlledCar = new GTUType(CONTROLLED_CAR_ID, car);
259 
260         GTUColorer[] colorers =
261                 new GTUColorer[] {new IDGTUColorer(), new SpeedGTUColorer(new Speed(150, SpeedUnit.KM_PER_HOUR)),
262                         new AccelerationGTUColorer(Acceleration.instantiateSI(-6.0), Acceleration.instantiateSI(2)),
263                         new GTUTypeColorer().add(car).add(controlledCar)};
264         SwitchableGTUColorer colorer = new SwitchableGTUColorer(0, colorers);
265         setGtuColorer(colorer);
266 
267         // parameters
268         StreamInterface stream = sim.getReplication().getStream("generation");
269         this.parameterFactory.addParameter(ParameterTypes.FSPEED, new DistNormal(stream, 123.7 / 120.0, 12.0 / 1200));
270 
271         OTSRoadNode nodeA = new OTSRoadNode(network, "A", new OTSPoint3D(0, 0), Direction.ZERO);
272         OTSRoadNode nodeB = new OTSRoadNode(network, "B", new OTSPoint3D(3000, 0), Direction.ZERO);
273         OTSRoadNode nodeC = new OTSRoadNode(network, "C", new OTSPoint3D(3250, 0), Direction.ZERO);
274         OTSRoadNode nodeD = new OTSRoadNode(network, "D", new OTSPoint3D(6000, 0), Direction.ZERO);
275         OTSRoadNode nodeE = new OTSRoadNode(network, "E", new OTSPoint3D(2000, -25), Direction.ZERO);
276         OTSRoadNode nodeF = new OTSRoadNode(network, "F", new OTSPoint3D(2750, 0.0), Direction.ZERO);
277 
278         LinkType freeway = network.getLinkType(LinkType.DEFAULTS.FREEWAY);
279         LaneKeepingPolicy policy = LaneKeepingPolicy.KEEPRIGHT;
280         Length laneWidth = Length.instantiateSI(3.6);
281         LaneType freewayLane = network.getLaneType(LaneType.DEFAULTS.FREEWAY);
282         Speed speedLimit = new Speed(120, SpeedUnit.KM_PER_HOUR);
283         Speed rampSpeedLimit = new Speed(70, SpeedUnit.KM_PER_HOUR);
284         List<Lane> lanesAB = new LaneFactory(network, nodeA, nodeB, freeway, sim, policy)
285                 .leftToRight(1.0, laneWidth, freewayLane, speedLimit).addLanes(Permeable.BOTH).getLanes();
286         List<Lane> lanesBC = new LaneFactory(network, nodeB, nodeC, freeway, sim, policy)
287                 .leftToRight(1.0, laneWidth, freewayLane, speedLimit).addLanes(Permeable.BOTH, Permeable.LEFT).getLanes();
288         List<Lane> lanesCD = new LaneFactory(network, nodeC, nodeD, freeway, sim, policy)
289                 .leftToRight(1.0, laneWidth, freewayLane, speedLimit).addLanes(Permeable.BOTH).getLanes();
290         List<Lane> lanesEF =
291                 new LaneFactory(network, nodeE, nodeF, freeway, sim, policy).setOffsetEnd(laneWidth.times(1.5).neg())
292                         .leftToRight(0.5, laneWidth, freewayLane, rampSpeedLimit).addLanes().getLanes();
293         List<Lane> lanesFB = new LaneFactory(network, nodeF, nodeB, freeway, sim, policy)
294                 .setOffsetStart(laneWidth.times(1.5).neg()).setOffsetEnd(laneWidth.times(1.5).neg())
295                 .leftToRight(0.5, laneWidth, freewayLane, speedLimit).addLanes().getLanes();
296         for (Lane lane : lanesCD)
297         {
298             new SinkSensor(lane, lane.getLength().minus(Length.instantiateSI(50)), GTUDirectionality.DIR_PLUS, sim);
299         }
300         // detectors
301         Duration agg = Duration.instantiateSI(60.0);
302         // TODO: detector length affects occupancy, which length to use?
303         Length detectorLength = Length.ZERO;
304         Detector det1 = new Detector("1", lanesAB.get(0), Length.instantiateSI(2900), detectorLength, sim, agg,
305                 Detector.MEAN_SPEED, Detector.OCCUPANCY);
306         Detector det2 = new Detector("2", lanesAB.get(1), Length.instantiateSI(2900), detectorLength, sim, agg,
307                 Detector.MEAN_SPEED, Detector.OCCUPANCY);
308         Detector det3 = new Detector("3", lanesCD.get(0), Length.instantiateSI(100), detectorLength, sim, agg,
309                 Detector.MEAN_SPEED, Detector.OCCUPANCY);
310         Detector det4 = new Detector("4", lanesCD.get(1), Length.instantiateSI(100), detectorLength, sim, agg,
311                 Detector.MEAN_SPEED, Detector.OCCUPANCY);
312         List<Detector> detectors12 = new ArrayList<>();
313         detectors12.add(det1);
314         detectors12.add(det2);
315         List<Detector> detectors34 = new ArrayList<>();
316         detectors34.add(det3);
317         detectors34.add(det4);
318         if (this.rampMetering)
319         {
320             // traffic light
321             TrafficLight light = new SimpleTrafficLight("light", lanesEF.get(0), lanesEF.get(0).getLength(), sim);
322             List<TrafficLight> lightList = new ArrayList<>();
323             lightList.add(light);
324             // ramp metering
325             RampMeteringSwitch rampSwitch = new RwsSwitch(detectors12);
326             RampMeteringLightController rampLightController =
327                     new CycleTimeLightController(sim, lightList, Compatible.EVERYTHING);
328             new RampMetering(sim, rampSwitch, rampLightController);
329         }
330 
331         // OD
332         List<OTSRoadNode> origins = new ArrayList<>();
333         origins.add(nodeA);
334         origins.add(nodeE);
335         List<OTSRoadNode> destinations = new ArrayList<>();
336         destinations.add(nodeD);
337         Categorization categorization = new Categorization("cat", GTUType.class);// , Lane.class);
338         Interpolation globalInterpolation = Interpolation.LINEAR;
339         ODMatrix od = new ODMatrix("rampMetering", origins, destinations, categorization, this.demandTime, globalInterpolation);
340         // Category carCatMainLeft = new Category(categorization, car, lanesAB.get(0));
341         // Category carCatMainRight = new Category(categorization, car, lanesAB.get(1));
342         Category carCatRamp = new Category(categorization, car);// , lanesEB.get(0));
343         Category controlledCarCat = new Category(categorization, controlledCar);
344         // double fLeft = 0.6;
345         od.putDemandVector(nodeA, nodeD, carCatRamp, this.mainDemand, 0.6);
346         od.putDemandVector(nodeA, nodeD, controlledCarCat, this.mainDemand, 0.4);
347         // od.putDemandVector(nodeA, nodeD, carCatMainLeft, mainDemand, fLeft);
348         // od.putDemandVector(nodeA, nodeD, carCatMainRight, mainDemand, 1.0 - fLeft);
349         od.putDemandVector(nodeE, nodeD, carCatRamp, this.rampDemand, 0.6);
350         od.putDemandVector(nodeE, nodeD, controlledCarCat, this.rampDemand, 0.4);
351         ODOptions odOptions = new ODOptions();
352         odOptions.set(ODOptions.GTU_TYPE, new ControlledStrategicalPlannerGenerator()).set(ODOptions.INSTANT_LC, true);
353         ODApplier.applyOD(network, od, sim, odOptions);
354 
355         return network;
356     }
357 
358     /**
359      * Returns the parameter factory.
360      * @return ParameterFactoryByType; parameter factory
361      */
362     final ParameterFactoryByType getParameterFactory()
363     {
364         return this.parameterFactory;
365     }
366 
367     /** {@inheritDoc} */
368     @Override
369     public void notify(final EventInterface event) throws RemoteException
370     {
371         if (event.getType().equals(Network.GTU_ADD_EVENT))
372         {
373             this.gtusInSimulation.put((String) event.getContent(), getSimulator().getSimulatorTime().si);
374         }
375         else if (event.getType().equals(Network.GTU_REMOVE_EVENT))
376         {
377             measureTravelTime((String) event.getContent());
378         }
379         else
380         {
381             super.notify(event);
382         }
383     }
384 
385     /**
386      * Adds travel time and delay for a single GTU.
387      * @param id String; id of the GTU
388      */
389     private void measureTravelTime(final String id)
390     {
391         double tt = getSimulator().getSimulatorTime().si - this.gtusInSimulation.get(id);
392         double x = getNetwork().getGTU(id).getOdometer().si;
393         // TODO: we assume 120km/h everywhere, including the slower ramps
394         double ttd = tt - (x / (120 / 3.6));
395         this.totalTravelTime += tt;
396         this.totalTravelTimeDelay += ttd;
397         this.gtusInSimulation.remove(id);
398     }
399 
400     /** {@inheritDoc} */
401     @Override
402     protected void onSimulationEnd()
403     {
404         if (this.output)
405         {
406             // detector data
407             String file = String.format("%s_%02d_detectors.txt", this.scenario, getSeed());
408             Detector.writeToFile(getNetwork(), file, true, "%.3f", CompressionMethod.NONE);
409 
410             // travel time data
411             for (GTU gtu : getNetwork().getGTUs())
412             {
413                 measureTravelTime(gtu.getId());
414             }
415             Throw.when(!this.gtusInSimulation.isEmpty(), RuntimeException.class,
416                     "GTUs remain in simulation that are not measured.");
417             file = String.format("%s_%02d_time.txt", this.scenario, getSeed());
418             BufferedWriter bw = CompressedFileWriter.create(file, false);
419             try
420             {
421                 bw.write(String.format("Total travel time: %.3fs", this.totalTravelTime));
422                 bw.newLine();
423                 bw.write(String.format("Total travel time delay: %.3fs", this.totalTravelTimeDelay));
424                 bw.close();
425             }
426             catch (IOException exception)
427             {
428                 throw new RuntimeException(exception);
429             }
430             finally
431             {
432                 try
433                 {
434                     if (bw != null)
435                     {
436                         bw.close();
437                     }
438                 }
439                 catch (IOException ex)
440                 {
441                     throw new RuntimeException(ex);
442                 }
443             }
444         }
445     }
446 
447     /**
448      * Strategical planner generator. This class can be used as input in {@code ODOptions} to generate the right models with
449      * different GTU types.
450      */
451     private class ControlledStrategicalPlannerGenerator implements GTUCharacteristicsGeneratorOD
452     {
453 
454         /** Default generator. */
455         private DefaultGTUCharacteristicsGeneratorOD defaultGenerator = new DefaultGTUCharacteristicsGeneratorOD();
456 
457         /** Controlled planner factory. */
458         private LaneBasedStrategicalPlannerFactory<LaneBasedStrategicalPlanner> controlledPlannerFactory;
459 
460         /** Constructor. */
461         ControlledStrategicalPlannerGenerator()
462         {
463             // anonymous factory to create tactical planners for controlled GTU's
464             LaneBasedTacticalPlannerFactory<?> tacticalPlannerFactory =
465                     new LaneBasedTacticalPlannerFactory<LaneBasedTacticalPlanner>()
466                     {
467                         @Override
468                         public Parameters getParameters() throws ParameterException
469                         {
470                             ParameterSet set = new ParameterSet();
471                             set.setDefaultParameter(ParameterTypes.PERCEPTION);
472                             set.setDefaultParameter(ParameterTypes.LOOKBACK);
473                             set.setDefaultParameter(ParameterTypes.LOOKAHEAD);
474                             set.setDefaultParameter(ParameterTypes.S0);
475                             set.setDefaultParameter(ParameterTypes.TMIN);
476                             set.setDefaultParameter(ParameterTypes.TMAX);
477                             set.setDefaultParameter(ParameterTypes.DT);
478                             set.setDefaultParameter(ParameterTypes.VCONG);
479                             set.setDefaultParameter(ParameterTypes.T0);
480                             set.setDefaultParameter(TrafficLightUtil.B_YELLOW);
481                             set.setDefaultParameters(LmrsParameters.class);
482                             set.setDefaultParameters(AbstractIDM.class);
483                             return set;
484                         }
485 
486                         @SuppressWarnings("synthetic-access")
487                         @Override
488                         public LaneBasedTacticalPlanner create(final LaneBasedGTU gtu) throws GTUException
489                         {
490                             // here the lateral control system is initiated
491                             ParameterSet settings = new ParameterSet();
492                             try
493                             {
494                                 // system operation settings
495                                 settings.setParameter(SyncAndAccept.SYNCTIME, Duration.instantiateSI(1.0));
496                                 settings.setParameter(SyncAndAccept.COOPTIME, Duration.instantiateSI(2.0));
497                                 // parameters used in car-following model for gap-acceptance
498                                 settings.setParameter(AbstractIDM.DELTA, 1.0);
499                                 settings.setParameter(ParameterTypes.S0, Length.instantiateSI(3.0));
500                                 settings.setParameter(ParameterTypes.A, Acceleration.instantiateSI(2.0));
501                                 settings.setParameter(ParameterTypes.B, Acceleration.instantiateSI(2.0));
502                                 settings.setParameter(ParameterTypes.T, RampMeteringDemo.this.acceptedGap);
503                                 settings.setParameter(ParameterTypes.FSPEED, 1.0);
504                                 settings.setParameter(ParameterTypes.B0, Acceleration.instantiateSI(0.5));
505                                 settings.setParameter(ParameterTypes.VCONG, new Speed(60, SpeedUnit.KM_PER_HOUR));
506                             }
507                             catch (ParameterException exception)
508                             {
509                                 throw new GTUException(exception);
510                             }
511                             return new ControlledTacticalPlanner(gtu, new SyncAndAccept(gtu, new IDMPlus(), settings));
512                         }
513                     };
514             // standard strategical planner factory using the tactical factory and the simulation-wide parameter factory
515             this.controlledPlannerFactory = new LaneBasedStrategicalRoutePlannerFactory(tacticalPlannerFactory,
516                     RampMeteringDemo.this.getParameterFactory());
517         }
518 
519         /** {@inheritDoc} */
520         @Override
521         public LaneBasedGTUCharacteristics draw(final Node origin, final Node destination, final Category category,
522                 final StreamInterface randomStream) throws GTUException
523         {
524             GTUType gtuType = category.get(GTUType.class);
525             // if GTU type is a controlled car, create characteristics for a controlled car
526             if (gtuType.equals(getNetwork().getGtuType(CONTROLLED_CAR_ID)))
527             {
528                 Route route = null;
529                 VehicleModel vehicleModel = VehicleModel.MINMAX;
530                 GTUCharacteristics gtuCharacteristics =
531                         GTUType.defaultCharacteristics(gtuType, origin.getNetwork(), randomStream);
532                 return new LaneBasedGTUCharacteristics(gtuCharacteristics, this.controlledPlannerFactory, route, origin,
533                         destination, vehicleModel);
534             }
535             // otherwise generate default characteristics
536             return this.defaultGenerator.draw(origin, destination, category, randomStream);
537         }
538 
539     }
540 
541     /** Tactical planner. */
542     private static class ControlledTacticalPlanner extends AbstractIncentivesTacticalPlanner
543     {
544         /** */
545         private static final long serialVersionUID = 20190731L;
546 
547         /** Lane change system. */
548         private AutomaticLaneChangeSystem laneChangeSystem;
549 
550         /** Lane change status. */
551         private final LaneChange laneChange;
552 
553         /** Map that {@code getLaneChangeDesire} writes current desires in. This is not used here. */
554         private Map<Class<? extends Incentive>, Desire> dummyMap = new LinkedHashMap<>();
555 
556         /**
557          * Constructor.
558          * @param gtu LaneBasedGTU; gtu
559          * @param laneChangeSystem AutomaticLaneChangeSystem; lane change system
560          */
561         ControlledTacticalPlanner(final LaneBasedGTU gtu, final AutomaticLaneChangeSystem laneChangeSystem)
562         {
563             super(new IDMPlus(), gtu, generatePerception(gtu));
564             setDefaultIncentives();
565             this.laneChangeSystem = laneChangeSystem;
566             this.laneChange = Try.assign(() -> new LaneChange(gtu), "Parameter LCDUR is required.", GTUException.class);
567         }
568 
569         /**
570          * Helper method to create perception.
571          * @param gtu LaneBasedGTU; gtu
572          * @return LanePerception lane perception
573          */
574         private static LanePerception generatePerception(final LaneBasedGTU gtu)
575         {
576             CategoricalLanePerception perception = new CategoricalLanePerception(gtu);
577             perception.addPerceptionCategory(new DirectEgoPerception<LaneBasedGTU, Perception<LaneBasedGTU>>(perception));
578             perception.addPerceptionCategory(new DirectInfrastructurePerception(perception));
579             // TODO: perceived GTUs as first type
580             perception.addPerceptionCategory(new DirectNeighborsPerception(perception, HeadwayGtuType.WRAP));
581             perception.addPerceptionCategory(new AnticipationTrafficPerception(perception));
582             perception.addPerceptionCategory(new DirectIntersectionPerception(perception, HeadwayGtuType.WRAP));
583             return perception;
584         }
585 
586         /** {@inheritDoc} */
587         @Override
588         public OperationalPlan generateOperationalPlan(final Time startTime, final DirectedPoint locationAtStartTime)
589                 throws OperationalPlanException, GTUException, NetworkException, ParameterException
590         {
591             // get some general input
592             Speed speed = getPerception().getPerceptionCategory(EgoPerception.class).getSpeed();
593             SpeedLimitProspect slp = getPerception().getPerceptionCategory(InfrastructurePerception.class)
594                     .getSpeedLimitProspect(RelativeLane.CURRENT);
595             SpeedLimitInfo sli = slp.getSpeedLimitInfo(Length.ZERO);
596 
597             // LMRS desire
598             Desire desire = LmrsUtil.getLaneChangeDesire(getGtu().getParameters(), getPerception(), getCarFollowingModel(),
599                     getMandatoryIncentives(), getVoluntaryIncentives(), this.dummyMap);
600 
601             // other vehicles respond to these 'interpreted' levels of lane change desire
602             getGtu().getParameters().setParameter(LmrsParameters.DLEFT, desire.getLeft());
603             getGtu().getParameters().setParameter(LmrsParameters.DRIGHT, desire.getRight());
604 
605             // car-following
606             Acceleration a = getGtu().getCarFollowingAcceleration();
607 
608             // cooperation
609             Acceleration aCoop = Cooperation.PASSIVE.cooperate(getPerception(), getGtu().getParameters(), sli,
610                     getCarFollowingModel(), LateralDirectionality.LEFT, desire);
611             a = Acceleration.min(a, aCoop);
612             aCoop = Cooperation.PASSIVE.cooperate(getPerception(), getGtu().getParameters(), sli, getCarFollowingModel(),
613                     LateralDirectionality.RIGHT, desire);
614             a = Acceleration.min(a, aCoop);
615 
616             // compose human plan
617             SimpleOperationalPlan simplePlan =
618                     new SimpleOperationalPlan(a, getGtu().getParameters().getParameter(ParameterTypes.DT));
619             for (AccelerationIncentive incentive : getAccelerationIncentives())
620             {
621                 incentive.accelerate(simplePlan, RelativeLane.CURRENT, Length.ZERO, getGtu(), getPerception(),
622                         getCarFollowingModel(), speed, getGtu().getParameters(), sli);
623             }
624 
625             // add lane change control
626             if (!this.laneChange.isChangingLane())
627             {
628                 double dFree = getGtu().getParameters().getParameter(LmrsParameters.DFREE);
629                 if (this.laneChangeSystem.initiatedLaneChange().isNone())
630                 {
631                     if (desire.leftIsLargerOrEqual() && desire.getLeft() > dFree)
632                     {
633                         this.laneChangeSystem.initiateLaneChange(LateralDirectionality.LEFT);
634                     }
635                     else if (desire.getRight() > dFree)
636                     {
637                         this.laneChangeSystem.initiateLaneChange(LateralDirectionality.RIGHT);
638                     }
639                 }
640                 else
641                 {
642                     if ((this.laneChangeSystem.initiatedLaneChange().isLeft() && desire.getLeft() < dFree)
643                             || (this.laneChangeSystem.initiatedLaneChange().isRight() && desire.getRight() < dFree))
644                     {
645                         this.laneChangeSystem.initiateLaneChange(LateralDirectionality.NONE);
646                     }
647                 }
648             }
649             simplePlan = this.laneChangeSystem.operate(simplePlan, getGtu().getParameters());
650             simplePlan.setTurnIndicator(getGtu());
651 
652             // create plan
653             return LaneOperationalPlanBuilder.buildPlanFromSimplePlan(getGtu(), startTime, simplePlan, this.laneChange);
654         }
655     }
656 
657     /** Interface allowing tactical planners to use an automatic lane change system. */
658     private interface AutomaticLaneChangeSystem
659     {
660 
661         /**
662          * Update operational plan with actions to change lane. This method should be called by the tactical planner always.
663          * @param simplePlan SimpleOperationalPlan; plan
664          * @param parameters Parameters; parameters
665          * @return SimpleOperationalPlan; adapted plan
666          * @throws OperationalPlanException if the system runs in to an error
667          * @throws ParameterException if a parameter is missing
668          */
669         SimpleOperationalPlan operate(SimpleOperationalPlan simplePlan, Parameters parameters)
670                 throws OperationalPlanException, ParameterException;
671 
672         /**
673          * Returns the direction in which the system was initiated to perform a lane change.
674          * @return LateralDirectionality; direction in which the system was initiated to perform a lane change, {@code NONE} if
675          *         none
676          */
677         LateralDirectionality initiatedLaneChange();
678 
679         /**
680          * Initiate a lane change.
681          * @param dir LateralDirectionality; direction, use {@code NONE} to cancel
682          */
683         void initiateLaneChange(LateralDirectionality dir);
684 
685     }
686 
687     /** Implementation of an automatic lane change system. */
688     private static class SyncAndAccept implements AutomaticLaneChangeSystem
689     {
690         /** Parameter of time after lane change command when the system will start synchronization. */
691         public static final ParameterTypeDuration SYNCTIME = new ParameterTypeDuration("tSync",
692                 "Time after which synchronization starts.", Duration.instantiateSI(1.0), NumericConstraint.POSITIVE);
693 
694         /** Parameter of time after lane change command when the system will start cooperation (indicator). */
695         public static final ParameterTypeDuration COOPTIME = new ParameterTypeDuration("tCoop",
696                 "Time after which cooperation starts (indicator).", Duration.instantiateSI(2.0), NumericConstraint.POSITIVE);
697 
698         /** GTU. */
699         private final LaneBasedGTU gtu;
700 
701         /** Car-following model for gap-acceptance. */
702         private final CarFollowingModel carFollowingModel;
703 
704         /** Parameters containing the system settings. */
705         private final Parameters settings;
706 
707         /** Initiated lane change direction. */
708         private LateralDirectionality direction = LateralDirectionality.NONE;
709 
710         /** Time when the lane change was initiated. */
711         private Time initiationTime;
712 
713         /**
714          * Constructor.
715          * @param gtu LaneBasedGTU; GTU
716          * @param carFollowingModel CarFollowingModel; car-following model
717          * @param settings Parameters; system settings
718          */
719         SyncAndAccept(final LaneBasedGTU gtu, final CarFollowingModel carFollowingModel, final Parameters settings)
720         {
721             this.gtu = gtu;
722             this.carFollowingModel = carFollowingModel;
723             this.settings = settings;
724         }
725 
726         /** {@inheritDoc} */
727         @Override
728         public SimpleOperationalPlan operate(final SimpleOperationalPlan simplePlan, final Parameters parameters)
729                 throws OperationalPlanException, ParameterException
730         {
731             // active?
732             if (this.direction.isNone())
733             {
734                 return simplePlan;
735             }
736 
737             // check gap
738             InfrastructurePerception infra =
739                     this.gtu.getTacticalPlanner().getPerception().getPerceptionCategory(InfrastructurePerception.class);
740             SpeedLimitInfo sli = infra.getSpeedLimitProspect(RelativeLane.CURRENT).getSpeedLimitInfo(Length.ZERO);
741             NeighborsPerception neighbors =
742                     this.gtu.getTacticalPlanner().getPerception().getPerceptionCategory(NeighborsPerception.class);
743             if (infra.getLegalLaneChangePossibility(RelativeLane.CURRENT, this.direction).gt0()
744                     && !neighbors.isGtuAlongside(this.direction)
745                     && acceptGap(neighbors.getFirstFollowers(this.direction), sli, false)
746                     && acceptGap(neighbors.getFirstLeaders(this.direction), sli, true))
747             {
748                 // gaps accepted, start lane change
749                 SimpleOperationalPlan plan =
750                         new SimpleOperationalPlan(simplePlan.getAcceleration(), simplePlan.getDuration(), this.direction);
751                 this.direction = LateralDirectionality.NONE;
752                 this.initiationTime = null;
753                 return plan;
754             }
755 
756             // synchronization
757             Duration since = this.gtu.getSimulator().getSimulatorTime().minus(this.initiationTime);
758             if (since.gt(this.settings.getParameter(SYNCTIME))
759                     || this.gtu.getSpeed().lt(this.settings.getParameter(ParameterTypes.VCONG)))
760             {
761                 PerceptionCollectable<HeadwayGTU, LaneBasedGTU> leaders =
762                         neighbors.getLeaders(new RelativeLane(this.direction, 1));
763                 if (!leaders.isEmpty())
764                 {
765                     HeadwayGTU leader = leaders.first();
766                     Acceleration a = CarFollowingUtil.followSingleLeader(this.carFollowingModel, this.settings,
767                             this.gtu.getSpeed(), sli, leader);
768                     a = Acceleration.max(a, this.settings.getParameter(ParameterTypes.B).neg());
769                     simplePlan.minimizeAcceleration(a);
770                 }
771             }
772 
773             // cooperation
774             if (since.gt(this.settings.getParameter(COOPTIME))
775                     || this.gtu.getSpeed().lt(this.settings.getParameter(ParameterTypes.VCONG)))
776             {
777                 if (this.direction.isLeft())
778                 {
779                     simplePlan.setIndicatorIntentLeft();
780                 }
781                 else
782                 {
783                     simplePlan.setIndicatorIntentRight();
784                 }
785             }
786 
787             // return
788             return simplePlan;
789         }
790 
791         /**
792          * Checks whether a gap can be accepted.
793          * @param neighbors Set&lt;HeadwayGTU&gt;; neighbors
794          * @param sli SpeedLimitInfo; speed limit info
795          * @param leaders boolean; whether we are dealing with leaders, or followers
796          * @return boolean; whether the gap is accepted
797          * @throws ParameterException if a parameter is not defined
798          */
799         private boolean acceptGap(final Set<HeadwayGTU> neighbors, final SpeedLimitInfo sli, final boolean leaders)
800                 throws ParameterException
801         {
802             for (HeadwayGTU neighbor : neighbors)
803             {
804                 Acceleration a = CarFollowingUtil.followSingleLeader(this.carFollowingModel, this.settings,
805                         leaders ? this.gtu.getSpeed() : neighbor.getSpeed(), sli, neighbor.getDistance(),
806                         leaders ? neighbor.getSpeed() : this.gtu.getSpeed());
807                 if (a.lt(this.settings.getParameter(ParameterTypes.B).neg()))
808                 {
809                     return false;
810                 }
811             }
812             return true;
813         }
814 
815         /** {@inheritDoc} */
816         @Override
817         public LateralDirectionality initiatedLaneChange()
818         {
819             return this.direction;
820         }
821 
822         /** {@inheritDoc} */
823         @Override
824         public void initiateLaneChange(final LateralDirectionality dir)
825         {
826             this.direction = dir;
827             if (!dir.isNone())
828             {
829                 this.initiationTime = this.gtu.getSimulator().getSimulatorTime();
830             }
831             else
832             {
833                 this.initiationTime = null;
834             }
835         }
836     }
837 
838 }