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 
134 
135 
136 
137 
138 
139 
140 
141 
142 public class RampMeteringDemo extends AbstractSimulationScript
143 {
144 
145     
146     private static final String CONTROLLED_CAR_ID = "controlledCar";
147 
148     
149     private ParameterFactoryByType parameterFactory = new ParameterFactoryByType();
150 
151     
152     @Option(names = {"-r", "--rampMetering"}, description = "Ramp metering on or off", defaultValue = "true")
153     private boolean rampMetering;
154 
155     
156     @Option(names = "--output", description = "Generate output.", negatable = true, defaultValue = "false")
157     private boolean output;
158 
159     
160     @Option(names = "--acceptedGap", description = "Accepted gap.", defaultValue = "0.5s")
161     private Duration acceptedGap;
162 
163     
164     private FrequencyVector mainDemand;
165 
166     
167     @Option(names = "--mainDemand", description = "Main demand in veh/h.", defaultValue = "2000,3000,3900,3900,3000")
168     private String mainDemandString;
169 
170     
171     private FrequencyVector rampDemand;
172 
173     
174     @Option(names = "--rampDemand", description = "Ramp demand in veh/h.", defaultValue = "500,500,500,500,500")
175     private String rampDemandString;
176 
177     
178     private TimeVector demandTime;
179 
180     
181     @Option(names = "--demandTime", description = "Demand time in min.", defaultValue = "0,10,40,50,70")
182     private String demandTimeString;
183 
184     
185     @Option(names = "--scenario", description = "Scenario name.", defaultValue = "test")
186     private String scenario;
187 
188     
189     private Map<String, Double> gtusInSimulation = new LinkedHashMap<>();
190 
191     
192     private double totalTravelTime = 0.0;
193 
194     
195     private double totalTravelTimeDelay = 0.0;
196 
197     
198 
199 
200     protected RampMeteringDemo()
201     {
202         super("Ramp metering 1", "Ramp metering 2");
203     }
204 
205     
206 
207 
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 
225 
226 
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     
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         
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         
301         Duration agg = Duration.instantiateSI(60.0);
302         
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             
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             
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         
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);
338         Interpolation globalInterpolation = Interpolation.LINEAR;
339         ODMatrix od = new ODMatrix("rampMetering", origins, destinations, categorization, this.demandTime, globalInterpolation);
340         
341         
342         Category carCatRamp = new Category(categorization, car);
343         Category controlledCarCat = new Category(categorization, controlledCar);
344         
345         od.putDemandVector(nodeA, nodeD, carCatRamp, this.mainDemand, 0.6);
346         od.putDemandVector(nodeA, nodeD, controlledCarCat, this.mainDemand, 0.4);
347         
348         
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 
360 
361 
362     final ParameterFactoryByType getParameterFactory()
363     {
364         return this.parameterFactory;
365     }
366 
367     
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 
387 
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         
394         double ttd = tt - (x / (120 / 3.6));
395         this.totalTravelTime += tt;
396         this.totalTravelTimeDelay += ttd;
397         this.gtusInSimulation.remove(id);
398     }
399 
400     
401     @Override
402     protected void onSimulationEnd()
403     {
404         if (this.output)
405         {
406             
407             String file = String.format("%s_%02d_detectors.txt", this.scenario, getSeed());
408             Detector.writeToFile(getNetwork(), file, true, "%.3f", CompressionMethod.NONE);
409 
410             
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 
449 
450 
451     private class ControlledStrategicalPlannerGenerator implements GTUCharacteristicsGeneratorOD
452     {
453 
454         
455         private DefaultGTUCharacteristicsGeneratorOD defaultGenerator = new DefaultGTUCharacteristicsGeneratorOD();
456 
457         
458         private LaneBasedStrategicalPlannerFactory<LaneBasedStrategicalPlanner> controlledPlannerFactory;
459 
460         
461         ControlledStrategicalPlannerGenerator()
462         {
463             
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                             
491                             ParameterSet settings = new ParameterSet();
492                             try
493                             {
494                                 
495                                 settings.setParameter(SyncAndAccept.SYNCTIME, Duration.instantiateSI(1.0));
496                                 settings.setParameter(SyncAndAccept.COOPTIME, Duration.instantiateSI(2.0));
497                                 
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             
515             this.controlledPlannerFactory = new LaneBasedStrategicalRoutePlannerFactory(tacticalPlannerFactory,
516                     RampMeteringDemo.this.getParameterFactory());
517         }
518 
519         
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             
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             
536             return this.defaultGenerator.draw(origin, destination, category, randomStream);
537         }
538 
539     }
540 
541     
542     private static class ControlledTacticalPlanner extends AbstractIncentivesTacticalPlanner
543     {
544         
545         private static final long serialVersionUID = 20190731L;
546 
547         
548         private AutomaticLaneChangeSystem laneChangeSystem;
549 
550         
551         private final LaneChange laneChange;
552 
553         
554         private Map<Class<? extends Incentive>, Desire> dummyMap = new LinkedHashMap<>();
555 
556         
557 
558 
559 
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 
571 
572 
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             
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         
587         @Override
588         public OperationalPlan generateOperationalPlan(final Time startTime, final DirectedPoint locationAtStartTime)
589                 throws OperationalPlanException, GTUException, NetworkException, ParameterException
590         {
591             
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             
598             Desire desire = LmrsUtil.getLaneChangeDesire(getGtu().getParameters(), getPerception(), getCarFollowingModel(),
599                     getMandatoryIncentives(), getVoluntaryIncentives(), this.dummyMap);
600 
601             
602             getGtu().getParameters().setParameter(LmrsParameters.DLEFT, desire.getLeft());
603             getGtu().getParameters().setParameter(LmrsParameters.DRIGHT, desire.getRight());
604 
605             
606             Acceleration a = getGtu().getCarFollowingAcceleration();
607 
608             
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             
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             
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             
653             return LaneOperationalPlanBuilder.buildPlanFromSimplePlan(getGtu(), startTime, simplePlan, this.laneChange);
654         }
655     }
656 
657     
658     private interface AutomaticLaneChangeSystem
659     {
660 
661         
662 
663 
664 
665 
666 
667 
668 
669         SimpleOperationalPlan operate(SimpleOperationalPlan simplePlan, Parameters parameters)
670                 throws OperationalPlanException, ParameterException;
671 
672         
673 
674 
675 
676 
677         LateralDirectionality initiatedLaneChange();
678 
679         
680 
681 
682 
683         void initiateLaneChange(LateralDirectionality dir);
684 
685     }
686 
687     
688     private static class SyncAndAccept implements AutomaticLaneChangeSystem
689     {
690         
691         public static final ParameterTypeDuration SYNCTIME = new ParameterTypeDuration("tSync",
692                 "Time after which synchronization starts.", Duration.instantiateSI(1.0), NumericConstraint.POSITIVE);
693 
694         
695         public static final ParameterTypeDuration COOPTIME = new ParameterTypeDuration("tCoop",
696                 "Time after which cooperation starts (indicator).", Duration.instantiateSI(2.0), NumericConstraint.POSITIVE);
697 
698         
699         private final LaneBasedGTU gtu;
700 
701         
702         private final CarFollowingModel carFollowingModel;
703 
704         
705         private final Parameters settings;
706 
707         
708         private LateralDirectionality direction = LateralDirectionality.NONE;
709 
710         
711         private Time initiationTime;
712 
713         
714 
715 
716 
717 
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         
727         @Override
728         public SimpleOperationalPlan operate(final SimpleOperationalPlan simplePlan, final Parameters parameters)
729                 throws OperationalPlanException, ParameterException
730         {
731             
732             if (this.direction.isNone())
733             {
734                 return simplePlan;
735             }
736 
737             
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                 
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             
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             
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             
788             return simplePlan;
789         }
790 
791         
792 
793 
794 
795 
796 
797 
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         
816         @Override
817         public LateralDirectionality initiatedLaneChange()
818         {
819             return this.direction;
820         }
821 
822         
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 }