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