View Javadoc
1   package org.opentrafficsim.demo.cacc;
2   
3   import java.awt.Color;
4   import java.io.BufferedWriter;
5   import java.io.IOException;
6   import java.io.OutputStreamWriter;
7   import java.rmi.RemoteException;
8   import java.util.ArrayList;
9   import java.util.Collections;
10  import java.util.LinkedHashSet;
11  import java.util.List;
12  import java.util.Map;
13  import java.util.Set;
14  
15  import org.djunits.Throw;
16  import org.djunits.unit.DurationUnit;
17  import org.djunits.unit.FrequencyUnit;
18  import org.djunits.unit.SpeedUnit;
19  import org.djunits.unit.TimeUnit;
20  import org.djunits.value.ValueRuntimeException;
21  import org.djunits.value.storage.StorageType;
22  import org.djunits.value.vdouble.scalar.Acceleration;
23  import org.djunits.value.vdouble.scalar.Direction;
24  import org.djunits.value.vdouble.scalar.Duration;
25  import org.djunits.value.vdouble.scalar.Length;
26  import org.djunits.value.vdouble.scalar.Speed;
27  import org.djunits.value.vdouble.scalar.Time;
28  import org.djunits.value.vdouble.vector.FrequencyVector;
29  import org.djunits.value.vdouble.vector.TimeVector;
30  import org.djunits.value.vdouble.vector.base.DoubleVector;
31  import org.djunits.value.vfloat.scalar.FloatDuration;
32  import org.djutils.cli.CliException;
33  import org.djutils.cli.CliUtil;
34  import org.djutils.event.EventInterface;
35  import org.djutils.event.EventListenerInterface;
36  import org.djutils.exceptions.Try;
37  import org.opentrafficsim.base.compressedfiles.CompressionType;
38  import org.opentrafficsim.base.compressedfiles.Writer;
39  import org.opentrafficsim.base.parameters.ParameterException;
40  import org.opentrafficsim.base.parameters.ParameterTypes;
41  import org.opentrafficsim.base.parameters.Parameters;
42  import org.opentrafficsim.core.animation.gtu.colorer.AccelerationGTUColorer;
43  import org.opentrafficsim.core.animation.gtu.colorer.IDGTUColorer;
44  import org.opentrafficsim.core.animation.gtu.colorer.SpeedGTUColorer;
45  import org.opentrafficsim.core.animation.gtu.colorer.SwitchableGTUColorer;
46  import org.opentrafficsim.core.distributions.ConstantGenerator;
47  import org.opentrafficsim.core.distributions.Distribution;
48  import org.opentrafficsim.core.distributions.Distribution.FrequencyAndObject;
49  import org.opentrafficsim.core.distributions.ProbabilityException;
50  import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
51  import org.opentrafficsim.core.geometry.Bezier;
52  import org.opentrafficsim.core.geometry.OTSLine3D;
53  import org.opentrafficsim.core.geometry.OTSPoint3D;
54  import org.opentrafficsim.core.gtu.GTUDirectionality;
55  import org.opentrafficsim.core.gtu.GTUException;
56  import org.opentrafficsim.core.gtu.GTUType;
57  import org.opentrafficsim.core.gtu.TemplateGTUType;
58  import org.opentrafficsim.core.gtu.perception.DirectEgoPerception;
59  import org.opentrafficsim.core.gtu.perception.EgoPerception;
60  import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
61  import org.opentrafficsim.core.network.Link;
62  import org.opentrafficsim.core.network.LinkType;
63  import org.opentrafficsim.core.network.NetworkException;
64  import org.opentrafficsim.core.network.Node;
65  import org.opentrafficsim.core.parameters.ParameterFactory;
66  import org.opentrafficsim.core.parameters.ParameterFactoryByType;
67  import org.opentrafficsim.draw.factory.DefaultAnimationFactory;
68  import org.opentrafficsim.draw.graphs.ContourDataSource;
69  import org.opentrafficsim.draw.graphs.ContourPlotSpeed;
70  import org.opentrafficsim.draw.graphs.GraphPath;
71  import org.opentrafficsim.draw.graphs.road.GraphLaneUtil;
72  import org.opentrafficsim.kpi.sampling.KpiGtuDirectionality;
73  import org.opentrafficsim.kpi.sampling.KpiLaneDirection;
74  import org.opentrafficsim.kpi.sampling.SamplingException;
75  import org.opentrafficsim.kpi.sampling.SpaceTimeRegion;
76  import org.opentrafficsim.kpi.sampling.Trajectory;
77  import org.opentrafficsim.kpi.sampling.TrajectoryGroup;
78  import org.opentrafficsim.kpi.sampling.meta.FilterDataGtuType;
79  import org.opentrafficsim.road.gtu.colorer.DesiredSpeedColorer;
80  import org.opentrafficsim.road.gtu.colorer.FixedColor;
81  import org.opentrafficsim.road.gtu.colorer.GTUTypeColorer;
82  import org.opentrafficsim.road.gtu.colorer.ReactionTimeColorer;
83  import org.opentrafficsim.road.gtu.colorer.SplitColorer;
84  import org.opentrafficsim.road.gtu.colorer.SynchronizationColorer;
85  import org.opentrafficsim.road.gtu.colorer.TaskColorer;
86  import org.opentrafficsim.road.gtu.colorer.TaskSaturationColorer;
87  import org.opentrafficsim.road.gtu.generator.LaneBasedGTUGenerator;
88  import org.opentrafficsim.road.gtu.generator.Platoons;
89  import org.opentrafficsim.road.gtu.generator.od.DefaultGTUCharacteristicsGeneratorOD;
90  import org.opentrafficsim.road.gtu.generator.od.ODApplier;
91  import org.opentrafficsim.road.gtu.generator.od.ODApplier.GeneratorObjects;
92  import org.opentrafficsim.road.gtu.generator.od.ODOptions;
93  import org.opentrafficsim.road.gtu.generator.od.StrategicalPlannerFactorySupplierOD;
94  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
95  import org.opentrafficsim.road.gtu.lane.perception.CategoricalLanePerception;
96  import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
97  import org.opentrafficsim.road.gtu.lane.perception.PerceptionCollectable;
98  import org.opentrafficsim.road.gtu.lane.perception.PerceptionFactory;
99  import org.opentrafficsim.road.gtu.lane.perception.RelativeLane;
100 import org.opentrafficsim.road.gtu.lane.perception.categories.AnticipationTrafficPerception;
101 import org.opentrafficsim.road.gtu.lane.perception.categories.DirectInfrastructurePerception;
102 import org.opentrafficsim.road.gtu.lane.perception.categories.DirectIntersectionPerception;
103 import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.Anticipation;
104 import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.DirectNeighborsPerception;
105 import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.Estimation;
106 import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.HeadwayGtuType;
107 import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.HeadwayGtuType.PerceivedHeadwayGtuType;
108 import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.NeighborsPerception;
109 import org.opentrafficsim.road.gtu.lane.perception.headway.HeadwayGTU;
110 import org.opentrafficsim.road.gtu.lane.perception.mental.AbstractTask;
111 import org.opentrafficsim.road.gtu.lane.perception.mental.AdaptationHeadway;
112 import org.opentrafficsim.road.gtu.lane.perception.mental.AdaptationSituationalAwareness;
113 import org.opentrafficsim.road.gtu.lane.perception.mental.Fuller;
114 import org.opentrafficsim.road.gtu.lane.perception.mental.Fuller.BehavioralAdaptation;
115 import org.opentrafficsim.road.gtu.lane.perception.mental.Task;
116 import org.opentrafficsim.road.gtu.lane.perception.mental.TaskManager;
117 import org.opentrafficsim.road.gtu.lane.tactical.cacc.CaccController;
118 import org.opentrafficsim.road.gtu.lane.tactical.cacc.CaccControllerFactory;
119 import org.opentrafficsim.road.gtu.lane.tactical.cacc.CaccParameters;
120 import org.opentrafficsim.road.gtu.lane.tactical.cacc.CaccTacticalPlanner;
121 import org.opentrafficsim.road.gtu.lane.tactical.cacc.CaccTacticalPlannerFactory;
122 import org.opentrafficsim.road.gtu.lane.tactical.cacc.LongitudinalControllerFactory;
123 import org.opentrafficsim.road.gtu.lane.tactical.cacc.Platoon;
124 import org.opentrafficsim.road.gtu.lane.tactical.following.IDMPlusFactory;
125 import org.opentrafficsim.road.gtu.lane.tactical.lmrs.DefaultLMRSPerceptionFactory;
126 import org.opentrafficsim.road.gtu.lane.tactical.lmrs.LMRSFactory;
127 import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.LmrsParameters;
128 import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlannerFactory;
129 import org.opentrafficsim.road.gtu.strategical.od.Categorization;
130 import org.opentrafficsim.road.gtu.strategical.od.Category;
131 import org.opentrafficsim.road.gtu.strategical.od.Interpolation;
132 import org.opentrafficsim.road.gtu.strategical.od.ODMatrix;
133 import org.opentrafficsim.road.gtu.strategical.route.LaneBasedStrategicalRoutePlannerFactory;
134 import org.opentrafficsim.road.network.OTSRoadNetwork;
135 import org.opentrafficsim.road.network.lane.CrossSectionLink;
136 import org.opentrafficsim.road.network.lane.Lane;
137 import org.opentrafficsim.road.network.lane.LaneDirection;
138 import org.opentrafficsim.road.network.lane.LaneType;
139 import org.opentrafficsim.road.network.lane.OTSRoadNode;
140 import org.opentrafficsim.road.network.lane.Stripe;
141 import org.opentrafficsim.road.network.lane.Stripe.Permeable;
142 import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy;
143 import org.opentrafficsim.road.network.lane.object.sensor.Detector;
144 import org.opentrafficsim.road.network.lane.object.sensor.SinkSensor;
145 import org.opentrafficsim.road.network.sampling.LaneData;
146 import org.opentrafficsim.road.network.sampling.RoadSampler;
147 import org.opentrafficsim.road.network.sampling.data.TimeToCollision;
148 import org.opentrafficsim.swing.graphs.SwingContourPlot;
149 import org.opentrafficsim.swing.gui.OTSSimulationApplication;
150 import org.opentrafficsim.swing.script.AbstractSimulationScript;
151 
152 import nl.tudelft.simulation.dsol.swing.gui.TablePanel;
153 import nl.tudelft.simulation.jstats.streams.StreamInterface;
154 import picocli.CommandLine;
155 import picocli.CommandLine.Option;
156 
157 /**
158  * <p>
159  * Copyright (c) 2013-2017 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
160  * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
161  * <p>
162  * @version $Revision$, $LastChangedDate$, by $Author$, initial version 17 okt. 2018 <br>
163  * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
164  * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
165  * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
166  */
167 public class CaccSimulationAdv extends AbstractSimulationScript
168 {
169 
170     /** ...  */
171     private static final long serialVersionUID = 1L;
172 
173     /** Sampler for statistics. */
174     private RoadSampler sampler;
175 
176     /** Time to collision data type. */
177     private final TimeToCollision ttc = new TimeToCollision();
178 
179     /** Meta GTU type. */
180     private final FilterDataGtuType metaGtu = new FilterDataGtuType();
181 
182     /** The CACC controller. */
183     private CaccController caccController;
184     
185     /** The GTU type with CACC. */
186     private GTUType caccGTUType;
187 
188     /** Space time regions to sample traffic on. */
189     private final List<SpaceTimeRegion> regions = new ArrayList<>();
190 
191     /** List of lane for the graphs. */
192     private List<Lane> graphLanes = new ArrayList<>();
193 
194     // Options
195     
196     /** Network name. */
197 //    @Option(names = "--nn", description = "Network name", defaultValue = "onramp")
198 //    private String networkName;
199 
200     /** Car-following task parameter. */
201     @Option(names = "--hExp", description = "Exponential decay of car-following task by headway", defaultValue = "4.0 s")
202     private Duration hExp;
203     // TODO add check that hExp is positive
204 
205     /** Alpha. */
206     @Option(names = "--alpha", description = "Fraction of primary task that can be reduced by anticipation reliance",
207             defaultValue = "0.8")
208     private double alpha;
209     // TODO add check that alpha is in interval [-1 .. +1]
210 
211     /** Beta. */
212     @Option(names = "--beta", description = "Fraction of auxiliary tasks that can be reduced by anticipation reliance",
213             defaultValue = "0.6")
214     private double beta;
215     // TODO add check that beta is in interval [-1 .. +1]
216 
217     /** Network name. */
218     @Option(names = "--nn", description = "Network name", defaultValue = "onramp")
219     private String networkName;
220 
221     /** Intensity factor. */
222     @Option(names = "--intensity", description = "Intensity factor", defaultValue = "1.00")
223     private double intensity;
224 
225     /** Penetration. */
226     @Option(names = "--penetration", description = "Fraction of vehicles equipped with CACC", defaultValue = "1.00")
227     private double penetration;
228     
229     /** Platoon size. */
230     @Option(names = "--platoon", description = "Platoon size", defaultValue = "3")
231     private int platoonSize;
232     
233     /** Headway. */
234     @Option(names = "--headway", description = "Platoon headway", defaultValue = "0.9")
235     private double headway;
236     
237     /** Synchronization. */
238     @Option(names = "--synchronization", description = "Synchronization", defaultValue = "0.0")
239     private double synchronization;
240     
241     /** Free flow speed of platoon vehicles. */
242     @Option(names = "--setspeed", description = "Free flow speed of platoon vehicles", defaultValue = "80 km/h")
243     private Speed setspeed;
244     
245     /** Time step. */
246     @Option(names = "--sd", description = "Simulation time", defaultValue = "4500s")
247     private Duration simulationDuration;
248     
249     /** Show graphs. */
250     @Option(names = "--graphs", description = "Show graphs", defaultValue = "true")
251     private boolean graphs;
252     
253     /** Number of lanes. */
254     @Option(names = "--nol", description = "Number of lanes", defaultValue = "3")
255     private int numberOfLanes;
256     
257     /** Controller. */
258     @Option(names = "--controller", description = "Controller", defaultValue = "CACC")
259     private String controller;
260     
261     /** Output file for detections (?). */
262     @Option(names = "--outputFileDets", description = "Output file for detections", 
263             defaultValue = "C:\\\\Temp\\\\Journal-Platoon\\\\DetsDefault.csv")
264     private String outputFileDets;
265     
266     /** Output file for trajectories (?). */
267     @Option(names = "--outputFileTraj", description = "Output file for trajectories",
268             defaultValue = "C:\\\\Temp\\\\Journal-Platoon\\\\GenDefault")
269     private String outputFileTraj;
270     
271     /** Output file for Generators (?). */
272     @Option(names = "--outputFileGen", description = "Output file for generators", 
273             defaultValue = "C:\\Temp\\Journal-Platoon\\GenDefault")
274     private String outputFileGen;
275     
276     /** Strategies. */
277     @Option(names = "--strategies", description = "Strategies (boolean)", defaultValue = "false")
278     private boolean strategies;
279 
280     /** Adaptation. */
281     @Option(names = "--adaptation", description = "Adaptation (boolean)", defaultValue = "false")
282     private boolean adaptation;
283 
284     /** Tasks. */
285     @Option(names = "--tasks", description = "Tasks (boolean)", defaultValue = "true")
286     private boolean tasks;
287 
288     /** Fraction underestimation. */
289     @Option(names = "--fractionUnderestimation", description = "Fraction underestimation", defaultValue = "0.75")
290     private double fractionUnderestimation;
291     
292     /** Equilibrium distance. */
293     @Option(names = "--startSpacing", description = "Gross equilibrium distance at 25m/s with s0=3m, l=12m and T=0.3s",
294             defaultValue = "0.5")
295     private double startSpacing;
296 
297     /**
298      * @param properties x
299      * @throws CliException x 
300      * @throws NoSuchFieldException x 
301      */
302     protected CaccSimulationAdv(final String[] properties) throws NoSuchFieldException, CliException
303     {
304         super("Truck CACC", "Simulation of CACC trucks");
305         CommandLine cmd = new CommandLine(this);
306         CliUtil.changeOptionDefault(AbstractSimulationScript.class, "seed", "5");
307         CliUtil.execute(cmd, properties);
308     }
309 
310     /**
311      * Main method that creates and starts a simulation.
312      * @param args String[]; command line arguments
313      * @throws Exception x
314      * @throws CliException x
315      * @throws NoSuchFieldException x 
316      */
317     public static void main(final String[] args) throws NoSuchFieldException, CliException, Exception
318     {
319         new CaccSimulationAdv(args).start();
320     }
321 
322     /** {@inheritDoc} */
323     @Override
324     protected OTSRoadNetwork setupSimulation(final OTSSimulatorInterface sim) throws Exception
325     {
326         OTSRoadNetwork network = new OTSRoadNetwork(this.networkName, true, sim);
327         GTUType carGTUType = network.getGtuType(GTUType.DEFAULTS.CAR);
328         GTUType truckGTUType = network.getGtuType(GTUType.DEFAULTS.TRUCK);
329         this.caccGTUType = new GTUType("CACC", truckGTUType);
330         LinkType freewayLinkType = network.getLinkType(LinkType.DEFAULTS.FREEWAY);
331         LaneType freewayLaneType = network.getLaneType(LaneType.DEFAULTS.FREEWAY);
332         GTUType vehicle = network.getGtuType(GTUType.DEFAULTS.VEHICLE);
333         this.caccController = new CaccController();
334         this.caccController.setCACCGTUType(this.caccGTUType);
335         setGtuColorer(new SwitchableGTUColorer(0, new FixedColor(Color.BLUE, "Blue"),
336                 new GTUTypeColorer().add(carGTUType, Color.BLUE).add(truckGTUType, Color.RED).add(this.caccGTUType,
337                         Color.GREEN),
338                 (new TaskColorer("car-following")), (new TaskColorer("lane-changing")), (new TaskSaturationColorer()),
339                 (new ReactionTimeColorer(Duration.instantiateSI(1.0))), new IDGTUColorer(),
340                 new SpeedGTUColorer(new Speed(150, SpeedUnit.KM_PER_HOUR)),
341                 new DesiredSpeedColorer(new Speed(50, SpeedUnit.KM_PER_HOUR), new Speed(150, SpeedUnit.KM_PER_HOUR)),
342                 new AccelerationGTUColorer(Acceleration.instantiateSI(-6.0), Acceleration.instantiateSI(2)), new SplitColorer(),
343                 new SynchronizationColorer(), new TaskColorer("car-following")));
344 
345         // Factory for settable parameters
346         ParameterFactoryByType parameters = new ParameterFactoryByType();
347         // Parameters
348         parameters.addParameter(CaccParameters.T_SYSTEM_CACC, new Duration(this.headway, DurationUnit.SI));
349         parameters.addParameter(CaccParameters.A_REDUCED, Acceleration.instantiateSI(this.synchronization));
350         parameters.addParameter(CaccParameters.SET_SPEED, this.setspeed);
351 
352         CaccControllerFactory longitudinalControllerFactory;
353         String controllerName = this.controller;
354         if (controllerName.equals("CACC"))
355         {
356             longitudinalControllerFactory = new CaccControllerFactory();
357         }
358         else
359         {
360             throw new RuntimeException("Controller " + controllerName + " not supported.");
361         }
362 
363         Length laneWidth = Length.instantiateSI(3.5);
364         Length stripeWidth = Length.instantiateSI(0.2);
365         Map<String, GeneratorObjects> odApplierOutput;
366 
367         String platoonDetector1;
368 
369         if (networkName.equals("onramp"))
370         {
371             // points
372             OTSPoint3D pointA = new OTSPoint3D(0, 0);
373             OTSPoint3D pointB = new OTSPoint3D(2000, 0); // 700 meters toerit
374             OTSPoint3D pointC = new OTSPoint3D(2330, 0); // 330 meters onramp
375             OTSPoint3D pointD = new OTSPoint3D(3330, 0);
376             OTSPoint3D pointE = new OTSPoint3D(1300, -40);
377 
378             // nodes
379             OTSRoadNode nodeA = new OTSRoadNode(network, "A", pointA, Direction.ZERO);
380             OTSRoadNode nodeB = new OTSRoadNode(network, "B", pointB, Direction.ZERO);
381             OTSRoadNode nodeC = new OTSRoadNode(network, "C", pointC, Direction.ZERO);
382             OTSRoadNode nodeD = new OTSRoadNode(network, "D", pointD, Direction.ZERO);
383             OTSRoadNode nodeE = new OTSRoadNode(network, "E", pointE, Direction.ZERO);
384 
385             // links
386             CrossSectionLink linkAB = new CrossSectionLink(network, "AB", nodeA, nodeB, freewayLinkType,
387                     new OTSLine3D(pointA, pointB), LaneKeepingPolicy.KEEPRIGHT);
388             CrossSectionLink linkBC = new CrossSectionLink(network, "BC", nodeB, nodeC, freewayLinkType,
389                     new OTSLine3D(pointB, pointC), LaneKeepingPolicy.KEEPRIGHT);
390             CrossSectionLink linkCD = new CrossSectionLink(network, "CD", nodeC, nodeD, freewayLinkType,
391                     new OTSLine3D(pointC, pointD), LaneKeepingPolicy.KEEPRIGHT);
392             CrossSectionLink linkEB = new CrossSectionLink(network, "EB", nodeE, nodeB, freewayLinkType,
393                     Bezier.cubic(nodeE.getLocation(), nodeB.getLocation()), LaneKeepingPolicy.KEEPRIGHT);
394 
395             // lanes and stripes
396             int n = this.numberOfLanes;
397             // List<Lane> originLanes = new ArrayList<>();
398             for (int i = 0; i < n; i++)
399             {
400                 for (CrossSectionLink link : new CrossSectionLink[] {linkAB, linkBC, linkCD})
401                 {
402                     Lane lane = new Lane(link, "Lane " + (i + 1), laneWidth.times((0.5 + i)), laneWidth, freewayLaneType,
403                             new Speed(100, SpeedUnit.KM_PER_HOUR));
404                     Length offset = laneWidth.times(i + 1.0);
405                     Stripe stripe = new Stripe(link, offset, offset, stripeWidth);
406                     if (i < n - 1)
407                     {
408                         if (lane.getParentLink().getId().equals("BC"))
409                         {
410                             // stripe.addPermeability(GTUType.VEHICLE, Permeable.LEFT);
411                             stripe.addPermeability(vehicle, Permeable.BOTH);
412                         }
413                         else
414                         {
415                             stripe.addPermeability(vehicle, Permeable.BOTH);
416                         }
417                     }
418                     if (lane.getParentLink().getId().equals("BC"))
419                     {
420                         // new Detector(lane.getFullId(), lane, Length.instantiateSI(150.0), sim);
421                         new Detector(lane.getFullId(), lane, Length.instantiateSI(330.0), sim);
422                     }
423                     // sink sensors
424                     if (lane.getParentLink().getId().equals("CD"))
425                     {
426                         new SinkSensor(lane, lane.getLength().minus(Length.instantiateSI(100.0)), GTUDirectionality.DIR_PLUS,
427                                 sim);
428                         new SinkSensor(lane, lane.getLength().minus(Length.instantiateSI(90.0)), GTUDirectionality.DIR_PLUS,
429                                 sim);
430                         new SinkSensor(lane, lane.getLength().minus(Length.instantiateSI(80.0)), GTUDirectionality.DIR_PLUS,
431                                 sim);
432                         // detectors 100m after on ramp
433                         // new Detector(lane.getFullId(), lane, Length.instantiateSI(0.0), sim); // id equal to lane, may be
434                         // different
435                     }
436                     if (lane.getParentLink().getId().equals("AB"))
437                     {
438                         // originLanes.add(lane);
439                         this.graphLanes.add(lane);
440                     }
441                 }
442             }
443             new Stripe(linkAB, Length.ZERO, Length.ZERO, stripeWidth);
444             Stripe stripe = new Stripe(linkBC, Length.ZERO, Length.ZERO, stripeWidth);
445             stripe.addPermeability(vehicle, Permeable.LEFT);
446             new Stripe(linkCD, Length.ZERO, Length.ZERO, stripeWidth);
447             new Lane(linkBC, "Acceleration lane", laneWidth.times(-0.5), laneWidth, freewayLaneType,
448                     new Speed(100, SpeedUnit.KM_PER_HOUR));
449             Lane onramp = new Lane(linkEB, "Onramp", laneWidth.times(-0.5), laneWidth, freewayLaneType,
450                     new Speed(100, SpeedUnit.KM_PER_HOUR));
451             new Stripe(linkEB, Length.ZERO, Length.ZERO, stripeWidth);
452             new Stripe(linkEB, laneWidth.neg(), laneWidth.neg(), stripeWidth);
453             new Stripe(linkBC, laneWidth.neg(), laneWidth.neg(), stripeWidth);
454 
455             // Detector on onramp
456             // new Detector("Acceleration lane", accel, Length.createSI(200.0), sim);
457 
458             // OD without demand
459             List<OTSRoadNode> origins = new ArrayList<>();
460             origins.add(nodeA);
461             origins.add(nodeE);
462             List<OTSRoadNode> destinations = new ArrayList<>();
463             destinations.add(nodeD);
464             TimeVector timeVector = DoubleVector.instantiate(new double[] {0.0, 0.25, 0.50, 0.75, 1.0, 1.25, 1.50, 1.75, 2.0},
465                     TimeUnit.BASE_HOUR, StorageType.DENSE);
466             Interpolation interpolation = Interpolation.LINEAR; // or STEPWISE
467             Categorization categorization = new Categorization("CACC", GTUType.class, Lane.class);
468             // Category carCategory1 = new Category(categorization, GTUType.CAR, originLanes.get(1));
469             Category carCategory2 = new Category(categorization, carGTUType, this.graphLanes.get(2));
470             Category carCategory1 = new Category(categorization, carGTUType, this.graphLanes.get(1));
471             Category carCategory0 = new Category(categorization, carGTUType, this.graphLanes.get(0));
472             Category carCategoryR = new Category(categorization, carGTUType, onramp);
473             Category truCategory0 = new Category(categorization, truckGTUType, this.graphLanes.get(0));
474             Category truCategoryR = new Category(categorization, truckGTUType, onramp);
475             Category caccCategory = new Category(categorization, caccGTUType, this.graphLanes.get(0));
476             ODMatrix odMatrix = new ODMatrix("CACC OD", origins, destinations, categorization, timeVector, interpolation);
477 
478             double intensityIncrease = this.intensity;
479             double platoonPenetration = this.penetration;
480 
481             float[] mainlineDemand = new float[] {2584, 3028, 3228, 3510, 3228, 0};
482             // float[] mainlineDemand = new float[]{2584, 3028, 3528, 3900, 3428, 0};
483 
484             // float[] rampDemand = new float[]{2900, 2900, 3068, 3532, 3232, 0};
485 
486             // Demand of trucks departing from main road20
487             List<Double> demandList = new ArrayList<Double>();
488             demandList.add((double) mainlineDemand[0] * 0.15 * intensityIncrease);
489             demandList.add((double) mainlineDemand[1] * 0.15 * intensityIncrease);
490             demandList.add(mainlineDemand[2] * 0.15 * intensityIncrease);
491             demandList.add(mainlineDemand[3] * 0.15 * intensityIncrease);
492             demandList.add(mainlineDemand[4] * 0.15 * intensityIncrease);
493             demandList.add(mainlineDemand[5] * 0.15 * intensityIncrease);
494             demandList.add(mainlineDemand[5] * 0.15 * intensityIncrease);
495             demandList.add(mainlineDemand[5] * 0.15 * intensityIncrease);
496             demandList.add((double) 0);
497             demandList.add((double) 0);
498             List<Double> demandPlatoon = new ArrayList<Double>(); // List for number of platoons (not number of platoon
499                                                                   // vehicles!)
500             List<Double> demandPlatoonVehicles = new ArrayList<Double>(); // Demand for platoon vehicles
501 
502             for (int k = 0; k < demandList.size(); k++)
503             {
504                 double platoonVehicles = (demandList.get(k) * platoonPenetration);
505                 double platoonPlatoons = (Math.round(platoonVehicles / platoonSize)) / 4; // Actual generated platoons -> divide
506                                                                                           // by 4 to account for 15 minutes
507                 double newDemandVal = Math.max(demandList.get(k) - platoonVehicles, 0);
508                 demandList.set(k, newDemandVal);
509                 demandPlatoon.add(platoonPlatoons);
510                 platoonVehicles = platoonPlatoons * platoonSize * 4; // Based on actual generated platoons, but used to
511                                                                      // compensate per hour
512                 demandPlatoonVehicles.add(platoonVehicles);
513             }
514 
515             // Platoon definition (platoons = ... divide into multiple for multiple origins)
516             Set<LaneDirection> position = new LinkedHashSet<>();
517             position.add(new LaneDirection(this.graphLanes.get(0), GTUDirectionality.DIR_PLUS));
518             DefaultGTUCharacteristicsGeneratorOD characteristicsGenerator =
519                     getCharacteristicsGenerator(longitudinalControllerFactory, sim, parameters);
520             Platoons<Category> platoons =
521                     Platoons.ofCategory(characteristicsGenerator, sim, sim.getReplication().getStream("generation"), position);
522 
523             platoons.fixInfo(nodeA, nodeD, caccCategory);
524 
525             double dt = this.startSpacing;
526 
527             // Determine platoon generation times (of leading vehicle only) using pseudorandom generation (exponential
528             // distribution)
529             // Then check if interval times do not overlap with whole platoon generation, adjust accordingly
530             List<Double> generationTimes = new ArrayList<Double>();
531             double previous = 0;
532 
533             for (int k = 0; k < demandList.size(); k++)
534             {
535                 for (int i = 0; i < demandPlatoon.get(k); i++)
536                 {
537                     double lambda = (demandPlatoon.get(k) * 4); // Number of platoons to be generated (per hour -> times 4)
538                     StreamInterface rand = sim.getReplication().getStream("generation");
539                     // Random rand = new Random();
540                     double arrival = (Math.log(1 - rand.nextDouble()) / (-lambda) * 3600); // Inter arrival time is in the unit
541                                                                                            // of lambda (here per 15 minutes)
542 
543                     double startTime = (arrival + previous); // (k * 900) + (arrival + previous); // Interarrival plus 15
544                                                              // minutes (900 seconds)
545                     generationTimes.add(startTime);
546                     previous = previous + arrival;
547                 }
548             }
549 
550             // Sorting the generation time to check for overlap in generation
551             Collections.sort(generationTimes);
552 
553             for (int i = 0; i < generationTimes.size() - 1; i++)
554             {
555                 double diff = generationTimes.get(i + 1) - generationTimes.get(i);
556                 double generationDuration = 0.5;
557                 double maxDuration = (generationDuration * platoonSize) + 1.0;
558                 if (diff <= maxDuration)
559                 {
560                     generationTimes.set(i + 1, generationTimes.get(i) + maxDuration);
561                 }
562 
563                 // ((platoonSize-1)*((12.0 + 3.0)/22.22) + 0.3) + ( (12.0 + 3.0)/22.22 + 1.0);
564                 double endTime = generationTimes.get(i) + (generationDuration * platoonSize);
565                 platoons.addPlatoon(Time.instantiateSI(generationTimes.get(i)), Time.instantiateSI(endTime));
566 
567                 for (double t = generationTimes.get(i); t < endTime; t += dt)
568                 {
569                     platoons.addGtu(Time.instantiateSI(t));
570                 }
571             }
572 
573             // OD demand
574             // cars (without compensation we use fraction 0.5 in putDemandVector, otherwise we multiply by laneshare)
575             odMatrix.putDemandVector(nodeA, nodeD, carCategory2,
576                     freq(new double[] {mainlineDemand[0] * 0.85 * (intensityIncrease),
577                             mainlineDemand[1] * 0.85 * (intensityIncrease), mainlineDemand[2] * 0.85 * (intensityIncrease),
578                             mainlineDemand[3] * 0.85 * (intensityIncrease), mainlineDemand[4] * 0.85 * (intensityIncrease),
579                             mainlineDemand[5] * 0.85 * (intensityIncrease), mainlineDemand[5] * 0.85 * (intensityIncrease), 0,
580                             0}),
581                     timeVector, interpolation, 0.26);
582             odMatrix.putDemandVector(nodeA, nodeD, carCategory1,
583                     freq(new double[] {mainlineDemand[0] * 0.85 * (intensityIncrease),
584                             mainlineDemand[1] * 0.85 * (intensityIncrease), mainlineDemand[2] * 0.85 * (intensityIncrease),
585                             mainlineDemand[3] * 0.85 * (intensityIncrease), mainlineDemand[4] * 0.85 * (intensityIncrease),
586                             mainlineDemand[5] * 0.85 * (intensityIncrease), mainlineDemand[5] * 0.85 * (intensityIncrease), 0,
587                             0}),
588                     timeVector, interpolation, 0.32);
589             odMatrix.putDemandVector(nodeA, nodeD, carCategory0,
590                     platoons.compensate(carCategory0, freq(new double[] {mainlineDemand[0] * 0.85 * (intensityIncrease),
591                             mainlineDemand[1] * 0.85 * (intensityIncrease), mainlineDemand[2] * 0.85 * (intensityIncrease),
592                             mainlineDemand[3] * 0.85 * (intensityIncrease), mainlineDemand[4] * 0.85 * (intensityIncrease),
593                             mainlineDemand[5] * 0.85 * (intensityIncrease), mainlineDemand[5] * 0.85 * (intensityIncrease), 0,
594                             0}), timeVector, interpolation),
595                     timeVector, interpolation, 0.32);
596             odMatrix.putDemandVector(nodeE, nodeD, carCategoryR,
597                     freq(new double[] {mainlineDemand[0] * 0.85 * 0.25 * (intensityIncrease),
598                             mainlineDemand[1] * 0.85 * 0.25 * (intensityIncrease),
599                             mainlineDemand[2] * 0.85 * 0.25 * (intensityIncrease),
600                             mainlineDemand[3] * 0.85 * 0.25 * (intensityIncrease),
601                             mainlineDemand[4] * 0.85 * 0.25 * (intensityIncrease),
602                             mainlineDemand[5] * 0.85 * 0.25 * (intensityIncrease),
603                             mainlineDemand[5] * 0.85 * 0.25 * (intensityIncrease), 0, 0}));
604             // trucks
605             odMatrix.putDemandVector(nodeE, nodeD, truCategoryR,
606                     freq(new double[] {mainlineDemand[0] * 0.15 * 0.25 * (intensityIncrease),
607                             mainlineDemand[1] * 0.15 * 0.25 * (intensityIncrease),
608                             mainlineDemand[2] * 0.15 * 0.25 * (intensityIncrease),
609                             mainlineDemand[3] * 0.15 * 0.25 * (intensityIncrease),
610                             mainlineDemand[4] * 0.15 * 0.25 * (intensityIncrease),
611                             mainlineDemand[5] * 0.15 * 0.25 * (intensityIncrease),
612                             mainlineDemand[5] * 0.15 * 0.25 * (intensityIncrease), 0, 0}));
613             // cacc trucks & compensated normal trucks - if there are no, or only, cacc vehicles -> remove demand
614             if (this.penetration != 1.0)
615             {
616                 odMatrix.putDemandVector(nodeA, nodeD, truCategory0, platoons.compensate(truCategory0,
617                         freq(new double[] {demandList.get(0), demandList.get(1), demandList.get(2), demandList.get(3),
618                                 demandList.get(4), demandList.get(5), demandList.get(6), demandList.get(7), demandList.get(8)}),
619                         timeVector, interpolation));
620             }
621             if (this.penetration != 0.0)
622             {
623                 odMatrix.putDemandVector(nodeA, nodeD, caccCategory,
624                         platoons.compensate(caccCategory, freq(new double[] {demandPlatoonVehicles.get(0),
625                                 demandPlatoonVehicles.get(1), demandPlatoonVehicles.get(2), demandPlatoonVehicles.get(3),
626                                 demandPlatoonVehicles.get(4), demandPlatoonVehicles.get(5), demandPlatoonVehicles.get(6),
627                                 demandPlatoonVehicles.get(7), demandPlatoonVehicles.get(8)}), timeVector, interpolation));
628             }
629             // options
630             ODOptions odOptions = new ODOptions().set(ODOptions.NO_LC_DIST, Length.instantiateSI(300.0)).set(ODOptions.GTU_TYPE,
631                     characteristicsGenerator);
632             odApplierOutput = ODApplier.applyOD(network, odMatrix, odOptions);
633 
634             // start platoons
635             platoonDetector1 = "A1";
636             platoons.start(odApplierOutput.get(platoonDetector1).getGenerator());
637 
638             // Write platoon generation times to file
639             BufferedWriter bw;
640             bw = new BufferedWriter(
641                     new OutputStreamWriter(Writer.createOutputStream(this.outputFileGen, CompressionType.NONE)));
642             bw.write(String.format("Platoon generation times [s]: %s", generationTimes));
643             bw.close();
644         }
645         else if (networkName.equals("onrampMERGE"))
646         {
647             // points
648             OTSPoint3D pointA = new OTSPoint3D(0, 0);
649             OTSPoint3D pointB = new OTSPoint3D(2000, 0); // 700 meters toerit (1300 - 2000)
650             OTSPoint3D pointC = new OTSPoint3D(2330, 0); // 330 meters onramp
651             OTSPoint3D pointD = new OTSPoint3D(3330, 0);
652             OTSPoint3D pointE = new OTSPoint3D(1300, -40);
653 
654             // nodes
655             OTSRoadNode nodeA = new OTSRoadNode(network, "A", pointA, Direction.ZERO);
656             OTSRoadNode nodeB = new OTSRoadNode(network, "B", pointB, Direction.ZERO);
657             OTSRoadNode nodeC = new OTSRoadNode(network, "C", pointC, Direction.ZERO);
658             OTSRoadNode nodeD = new OTSRoadNode(network, "D", pointD, Direction.ZERO);
659             OTSRoadNode nodeE = new OTSRoadNode(network, "E", pointE, Direction.ZERO);
660 
661             // links
662             CrossSectionLink linkAB = new CrossSectionLink(network, "AB", nodeA, nodeB, freewayLinkType,
663                     new OTSLine3D(pointA, pointB), LaneKeepingPolicy.KEEPRIGHT);
664             CrossSectionLink linkBC = new CrossSectionLink(network, "BC", nodeB, nodeC, freewayLinkType,
665                     new OTSLine3D(pointB, pointC), LaneKeepingPolicy.KEEPRIGHT);
666             CrossSectionLink linkCD = new CrossSectionLink(network, "CD", nodeC, nodeD, freewayLinkType,
667                     new OTSLine3D(pointC, pointD), LaneKeepingPolicy.KEEPRIGHT);
668             CrossSectionLink linkEB = new CrossSectionLink(network, "EB", nodeE, nodeB, freewayLinkType,
669                     Bezier.cubic(nodeE.getLocation(), nodeB.getLocation()), LaneKeepingPolicy.KEEPRIGHT);
670 
671             // lanes and stripes
672             int n = this.numberOfLanes;
673             // List<Lane> originLanes = new ArrayList<>();
674             for (int i = 0; i < n; i++)
675             {
676                 for (CrossSectionLink link : new CrossSectionLink[] {linkAB, linkBC, linkCD})
677                 {
678                     Lane lane = new Lane(link, "Lane " + (i + 1), laneWidth.times((0.5 + i)), laneWidth, freewayLaneType,
679                             new Speed(100, SpeedUnit.KM_PER_HOUR));
680                     Length offset = laneWidth.times(i + 1.0);
681                     Stripe stripe = new Stripe(link, offset, offset, stripeWidth);
682                     if (i < n - 1)
683                     {
684                         if (lane.getParentLink().getId().equals("BC"))
685                         {
686                             // stripe.addPermeability(GTUType.VEHICLE, Permeable.LEFT);
687                             stripe.addPermeability(vehicle, Permeable.BOTH);
688 
689                         }
690                         else
691                         {
692                             stripe.addPermeability(vehicle, Permeable.BOTH);
693                         }
694                     }
695                     if (lane.getParentLink().getId().equals("BC"))
696                     {
697                         new Detector(lane.getFullId(), lane, Length.instantiateSI(330.0), sim);
698                     }
699                     // sink sensors
700                     if (lane.getParentLink().getId().equals("CD"))
701                     {
702                         new SinkSensor(lane, lane.getLength().minus(Length.instantiateSI(100.0)), GTUDirectionality.DIR_PLUS,
703                                 sim);
704                         // detectors 0m after on ramp
705                         // new Detector(lane.getFullId(), lane, Length.createSI(0.0), GTUDirectionality.DIR_PLUS, sim); 
706                         // id equal to lane, may be different
707                     }
708                     if (lane.getParentLink().getId().equals("AB"))
709                     {
710                         // originLanes.add(lane);
711                         this.graphLanes.add(lane);
712                     }
713                 }
714             }
715             new Stripe(linkAB, Length.ZERO, Length.ZERO, stripeWidth);
716             Stripe stripe = new Stripe(linkBC, Length.ZERO, Length.ZERO, stripeWidth);
717             stripe.addPermeability(vehicle, Permeable.LEFT);
718             new Stripe(linkCD, Length.ZERO, Length.ZERO, stripeWidth);
719             new Lane(linkBC, "Acceleration lane", laneWidth.times(-0.5), laneWidth, freewayLaneType,
720                     new Speed(100, SpeedUnit.KM_PER_HOUR));
721             Lane onramp = new Lane(linkEB, "Onramp", laneWidth.times(-0.5), laneWidth, freewayLaneType,
722                     new Speed(100, SpeedUnit.KM_PER_HOUR));
723             new Stripe(linkEB, Length.ZERO, Length.ZERO, stripeWidth);
724             new Stripe(linkEB, laneWidth.neg(), laneWidth.neg(), stripeWidth);
725             new Stripe(linkBC, laneWidth.neg(), laneWidth.neg(), stripeWidth);
726 
727             // OD without demand
728             List<OTSRoadNode> origins = new ArrayList<>();
729             origins.add(nodeA);
730             origins.add(nodeE);
731             List<OTSRoadNode> destinations = new ArrayList<>();
732             destinations.add(nodeD);
733             TimeVector timeVector = DoubleVector.instantiate(new double[] {0.0, 0.25, 0.50, 0.75, 1.0, 1.25, 1.50, 1.75, 2.0},
734                     TimeUnit.BASE_HOUR, StorageType.DENSE);
735             Interpolation interpolation = Interpolation.LINEAR; // or STEPWISE
736             Categorization categorization = new Categorization("CACC", GTUType.class, Lane.class);
737             Category carCategory1 = new Category(categorization, carGTUType, this.graphLanes.get(1));
738             Category carCategory0 = new Category(categorization, carGTUType, this.graphLanes.get(0));
739             Category carCategoryR = new Category(categorization, carGTUType, onramp);
740             Category truCategory0 = new Category(categorization, truckGTUType, this.graphLanes.get(0));
741             Category truCategoryR = new Category(categorization, truckGTUType, onramp);
742             Category caccCategory = new Category(categorization, caccGTUType, onramp);
743             ODMatrix odMatrix = new ODMatrix("CACC OD", origins, destinations, categorization, timeVector, interpolation);
744 
745             double intensityIncrease = this.intensity;
746             double platoonPenetration = this.penetration;
747 
748             // Demand of trucks departing from onramp
749             List<Double> demandList = new ArrayList<Double>();
750             demandList.add((double) 0);
751             demandList.add((double) 0);
752             demandList.add(45 * (1 + intensityIncrease));
753             demandList.add(45 * (1 + intensityIncrease));
754             demandList.add(45 * (1 + intensityIncrease));
755             demandList.add(45 * (1 + intensityIncrease));
756             demandList.add(45 * (1 + intensityIncrease));
757 
758             demandList.add((double) 0);
759             demandList.add((double) 0);
760             List<Double> demandPlatoon = new ArrayList<Double>(); // List for number of platoons (not number of platoon
761                                                                   // vehicles!)
762             List<Double> demandPlatoonVehicles = new ArrayList<Double>(); // Demand for platoon vehicles
763 
764             for (int k = 0; k < demandList.size(); k++)
765             {
766                 double platoonVehicles = (demandList.get(k) * platoonPenetration);
767                 // double platoonPlatoons = Math.round(platoonVehicles/platoonSize)/4; // Actual generated platoons -> divide by
768                 // 4 to account for 15 minutes
769                 double platoonPlatoons = Math.ceil(Math.abs(platoonVehicles / platoonSize)) / 4; // Rounding up
770                 double newDemandVal = Math.max(demandList.get(k) - platoonVehicles, 0);
771                 demandList.set(k, newDemandVal);
772                 demandPlatoon.add(platoonPlatoons);
773                 platoonVehicles = platoonPlatoons * platoonSize * 4; // Based on actual generated platoons, but used to
774                                                                      // compensate per hour
775                 demandPlatoonVehicles.add(platoonVehicles);
776             }
777 
778             // Platoon definition (platoons = ... divide into multiple for multiple origins)
779             Set<LaneDirection> position = new LinkedHashSet<>();
780             position.add(new LaneDirection(onramp, GTUDirectionality.DIR_PLUS));
781             DefaultGTUCharacteristicsGeneratorOD characteristicsGenerator =
782                     getCharacteristicsGenerator(longitudinalControllerFactory, sim, parameters);
783             Platoons<Category> platoons =
784                     Platoons.ofCategory(characteristicsGenerator, sim, sim.getReplication().getStream("generation"), position);
785 
786             platoons.fixInfo(nodeE, nodeD, caccCategory);
787 
788             double dt = this.startSpacing;
789 
790             // Determine platoon generation times (of leading vehicle only) using pseudorandom generation (exponential
791             // distribution)
792             // Then check if interval times do not overlap with whole platoon generation, adjust accordingly
793             List<Double> generationTimes = new ArrayList<Double>();
794             double previous = 0;
795 
796             for (int k = 0; k < demandList.size(); k++)
797             {
798                 for (int i = 0; i < demandPlatoon.get(k); i++)
799                 {
800                     double lambda = (demandPlatoon.get(k) * 4); // Number of platoons to be generated (per hour -> times 4)
801                     StreamInterface rand = sim.getReplication().getStream("generation");
802                     // Random rand = new Random();
803                     double arrival = (Math.log(1 - rand.nextDouble()) / (-lambda) * 3600); // Inter arrival time is in the unit
804                                                                                            // of lambda (here per 15 minutes)
805 
806                     double startTime = (arrival + previous); // (k * 900) + (arrival + previous); // Interarrival plus 15
807                                                              // minutes (900 seconds)
808                     generationTimes.add(startTime);
809                     previous = previous + arrival;
810                 }
811             }
812 
813             // Sorting the generation time to check for overlap in generation
814             Collections.sort(generationTimes);
815 
816             for (int i = 0; i < generationTimes.size() - 1; i++)
817             {
818                 double diff = generationTimes.get(i + 1) - generationTimes.get(i);
819                 double generationDuration = 0.5;
820                 double maxDuration = (generationDuration * platoonSize) + 1.0;
821                 if (diff <= maxDuration)
822                 {
823                     generationTimes.set(i + 1, generationTimes.get(i) + maxDuration);
824                 }
825 
826                 // ((platoonSize-1)*((12.0 + 3.0)/22.22) + 0.3) + ( (12.0 + 3.0)/22.22 + 1.0);
827                 double endTime = generationTimes.get(i) + (generationDuration * platoonSize);
828                 platoons.addPlatoon(Time.instantiateSI(generationTimes.get(i)), Time.instantiateSI(endTime));
829 
830                 for (double t = generationTimes.get(i); t < endTime; t += dt)
831                 {
832                     platoons.addGtu(Time.instantiateSI(t));
833                 }
834             }
835             // OD demand
836             // cars (without compensation we use fraction 0.5 in putDemandVector, otherwise we multiply by laneshare)
837             odMatrix.putDemandVector(nodeA, nodeD, carCategory1,
838                     freq(new double[] {0, 0, 1000 * (1 + intensityIncrease), 1000 * (1 + intensityIncrease),
839                             1000 * (1 + intensityIncrease), 1000 * (1 + intensityIncrease), 1000 * (1 + intensityIncrease),
840 
841                             0, 0}),
842                     timeVector, interpolation, 0.5);
843             odMatrix.putDemandVector(nodeA, nodeD, carCategory0,
844                     freq(new double[] {0, 0, 1000 * (1 + intensityIncrease), 1000 * (1 + intensityIncrease),
845                             1000 * (1 + intensityIncrease), 1000 * (1 + intensityIncrease), 1000 * (1 + intensityIncrease),
846 
847                             0, 0}),
848                     timeVector, interpolation, 0.5);
849             odMatrix.putDemandVector(nodeE, nodeD, carCategoryR,
850                     platoons.compensate(carCategoryR,
851                             freq(new double[] {0, 0, 250 * (1 + intensityIncrease), 250 * (1 + intensityIncrease),
852                                     250 * (1 + intensityIncrease), 250 * (1 + intensityIncrease), 250 * (1 + intensityIncrease),
853 
854                                     0, 0}),
855                             timeVector, interpolation));
856             // trucks
857             odMatrix.putDemandVector(nodeA, nodeD, truCategory0,
858                     platoons.compensate(truCategory0,
859                             freq(new double[] {0, 0, 180 * (1 + intensityIncrease), 180 * (1 + intensityIncrease),
860                                     180 * (1 + intensityIncrease), 180 * (1 + intensityIncrease), 180 * (1 + intensityIncrease),
861 
862                                     0, 0}),
863                             timeVector, interpolation));
864             // cacc trucks & compensated normal trucks - if there are no, or only, cacc vehicles -> remove demand
865             if (this.penetration != 1.0)
866             {
867                 odMatrix.putDemandVector(nodeE, nodeD, truCategoryR,
868                         freq(new double[] {demandList.get(0), demandList.get(1), demandList.get(2), demandList.get(3),
869                                 demandList.get(4), demandList.get(5), demandList.get(6), demandList.get(7), demandList.get(8)}),
870                         timeVector, interpolation);
871             }
872             if (this.penetration != 0.0)
873             {
874                 odMatrix.putDemandVector(nodeE, nodeD, caccCategory,
875                         platoons.compensate(caccCategory, freq(new double[] {demandPlatoonVehicles.get(0),
876                                 demandPlatoonVehicles.get(1), demandPlatoonVehicles.get(2), demandPlatoonVehicles.get(3),
877                                 demandPlatoonVehicles.get(4), demandPlatoonVehicles.get(5), demandPlatoonVehicles.get(6),
878                                 demandPlatoonVehicles.get(7), demandPlatoonVehicles.get(8)}), timeVector, interpolation));
879             }
880 
881             // options
882             ODOptions odOptions = new ODOptions().set(ODOptions.NO_LC_DIST, Length.instantiateSI(300.0)).set(ODOptions.GTU_TYPE,
883                     characteristicsGenerator);
884             odApplierOutput = ODApplier.applyOD(network, odMatrix, odOptions);
885 
886             // start platoons
887             platoonDetector1 = "E1";
888             platoons.start(odApplierOutput.get(platoonDetector1).getGenerator());
889 
890             // Write platoon generation times to file
891             BufferedWriter bw;
892             bw = new BufferedWriter(
893                     new OutputStreamWriter(Writer.createOutputStream(this.outputFileGen, CompressionType.NONE)));
894             bw.write(String.format("Platoon generation times [s]: %s", generationTimes));
895             bw.close();
896 
897         }
898         else
899         {
900             throw new RuntimeException("Network " + networkName + " not supported.");
901         }
902 
903         // listen to generator so we can add platoon object to the tactical planner (for first direction)
904         odApplierOutput.get(platoonDetector1).getGenerator().addListener(new EventListenerInterface()
905         {
906             /** Current platoon. */
907             private Platoon platoon = null;
908 
909             /** Last generated CACC GTU. */
910             private LaneBasedGTU lastGtu;
911 
912             /** {@inheritDoc} */
913             @Override
914             public void notify(final EventInterface event) throws RemoteException
915             {
916                 if (event.getType().equals(LaneBasedGTUGenerator.GTU_GENERATED_EVENT))
917                 {
918                     LaneBasedGTU gtu = (LaneBasedGTU) event.getContent();
919                     if (gtu.getGTUType().equals(caccGTUType))
920                     {
921                         // If platoon is not within 2 seconds, or platoon is equal or larger than set platoon size -> new
922                         // platoon
923                         // || this.lastGtu.getLocation().distance(gtu.getLocation()) > 2.0 * gtu.getSpeed().si
924 
925                         if (this.lastGtu == null || this.lastGtu.isDestroyed()
926                                 || this.lastGtu.getLocation().distance(gtu.getLocation()) > 3.0 * gtu.getSpeed().si
927                                 || this.platoon.size() >= platoonSize)
928                         {
929                             this.platoon = new Platoon();
930                         }
931                         this.platoon.addGtu(gtu.getId());
932                         ((CaccTacticalPlanner) gtu.getTacticalPlanner()).setPlatoon(this.platoon);
933                         this.lastGtu = gtu;
934                     }
935                 }
936             }
937         }, LaneBasedGTUGenerator.GTU_GENERATED_EVENT);
938 
939         // add animation to network objects
940         DefaultAnimationFactory.animateNetwork(network, sim, getGtuColorer());
941 
942         /** Sampler for statistics. */
943         this.sampler = RoadSampler.build(network).registerExtendedDataType(this.ttc)
944                 .registerFilterDataType(new FilterDataGtuType()).create();
945         
946         // Construct sample time and space window
947         Time start = new Time(0.25, TimeUnit.BASE_HOUR); // TODO set times
948         Time end = new Time(2.00, TimeUnit.BASE_HOUR);
949         for (Link link : network.getLinkMap().values())
950         {
951             for (Lane lane : ((CrossSectionLink) link).getLanes())
952             {
953                 KpiLaneDirection kpiLane = new KpiLaneDirection(new LaneData(lane), KpiGtuDirectionality.DIR_PLUS);
954                 SpaceTimeRegion region = new SpaceTimeRegion(kpiLane, Length.ZERO, lane.getLength(), start, end);
955                 this.regions.add(region);
956                 this.sampler.registerSpaceTimeRegion(region);
957             }
958         }
959 
960         // sampler
961         for (Link link : network.getLinkMap().values())
962         {
963             for (Lane lane : ((CrossSectionLink) link).getLanes())
964             {
965                 KpiLaneDirection kpiLane = new KpiLaneDirection(new LaneData(lane), KpiGtuDirectionality.DIR_PLUS);
966                 SpaceTimeRegion region = new SpaceTimeRegion(kpiLane, Length.ZERO, lane.getLength(), start, end);
967                 this.regions.add(region);
968                 this.sampler.registerSpaceTimeRegion(region);
969             }
970         }
971         return network;
972     }
973 
974     /** {@inheritDoc} */
975     @Override
976     protected void addTabs(final OTSSimulatorInterface sim, final OTSSimulationApplication<?> animation)
977     {
978         if (this.graphs)
979         {
980             // create tab
981             int h = (int) Math.sqrt(this.graphLanes.size());
982             int w = (int) Math.ceil(((double) this.graphLanes.size()) / h);
983             TablePanel charts = new TablePanel(w, h);
984             animation.getAnimationPanel().getTabbedPane().addTab(animation.getAnimationPanel().getTabbedPane().getTabCount(),
985                     "statistics", charts);
986 
987             // create plots per lane
988             int h2 = 0;
989             int w2 = 0;
990             for (int i = 0; i < this.graphLanes.size(); i++)
991             {
992                 GraphPath<KpiLaneDirection> graphPath;
993                 try
994                 {
995                     // starts at the lane and includes downstream lanes
996                     graphPath = GraphLaneUtil.createPath("Lane" + (i + 1),
997                             new LaneDirection(this.graphLanes.get(i), GTUDirectionality.DIR_PLUS));
998                     GraphPath.initRecording(sampler, graphPath);
999                 }
1000                 catch (NetworkException exception)
1001                 {
1002                     throw new RuntimeException(exception);
1003                 }
1004                 ContourDataSource<?> dataPool = new ContourDataSource<>(this.sampler.getSamplerData(), graphPath);
1005                 SwingContourPlot plot = new SwingContourPlot(new ContourPlotSpeed("Speed lane " + (i + 1), sim, dataPool));
1006                 charts.setCell(plot.getContentPane(), w2, h2);
1007                 w2++;
1008                 if (w2 > w)
1009                 {
1010                     w = 0;
1011                     h2++;
1012                 }
1013             }
1014         }
1015     }
1016 
1017     /** {@inheritDoc} */
1018     @Override
1019     protected void onSimulationEnd()
1020     {
1021         // periodic = true; periodic data is stored, if false other mesoscopic data would be stored, but that has not been setup
1022         Detector.writeToFile(getNetwork(), this.outputFileDets, true);
1023 
1024         // this.sampler.writeToFile(getProperty("outputFile"));
1025         double tts = 0.0;
1026         List<Float> ttcList = new ArrayList<>();
1027         List<String> ttcListGtuType = new ArrayList<>();
1028         List<Float> decList = new ArrayList<>();
1029         List<String> decListGtuType = new ArrayList<>();
1030         int[] counts = new int[120];
1031         Length detectorPosition = Length.instantiateSI(100.0);
1032         for (SpaceTimeRegion region : this.regions)
1033         {
1034             TrajectoryGroup<?> trajectoryGroup =
1035                     this.sampler.getSamplerData().getTrajectoryGroup(region.getLaneDirection()).getTrajectoryGroup(
1036                             region.getStartPosition(), region.getEndPosition(), region.getStartTime(), region.getEndTime());
1037             for (Trajectory<?> trajectory : trajectoryGroup)
1038             {
1039                 try
1040                 {
1041                     tts += trajectory.getTotalDuration().si;
1042                     String gtuTypeId = trajectory.getMetaData(metaGtu).getId();
1043                     for (FloatDuration ttcVal : trajectory.getExtendedData(this.ttc))
1044                     {
1045                         if (!Float.isNaN(ttcVal.si) && ttcVal.si < 20)
1046                         {
1047                             ttcList.add(ttcVal.si);
1048                             ttcListGtuType.add(gtuTypeId);
1049                         }
1050                     }
1051                     for (float decVal : trajectory.getA())
1052                     {
1053                         if (decVal < -2.0)
1054                         {
1055                             decList.add(decVal);
1056                             decListGtuType.add(gtuTypeId);
1057                         }
1058                     }
1059                     if (region.getLaneDirection().getLaneData().getLinkData().getId().equals("CD") && trajectory.size() > 1
1060                             && trajectory.getX(0) < detectorPosition.si
1061                             && trajectory.getX(trajectory.size() - 1) > detectorPosition.si)
1062                     {
1063                         double t = trajectory.getTimeAtPosition(detectorPosition).si - region.getStartTime().si;
1064                         counts[(int) (t / 60.0)]++;
1065                     }
1066                 }
1067                 catch (SamplingException exception)
1068                 {
1069                     throw new RuntimeException("Unexpected exception: TimeToCollission not available or index out of bounds.",
1070                             exception);
1071                 }
1072             }
1073         }
1074         int qMax = 0;
1075         for (int i = 0; i < counts.length - 4; i++)
1076         {
1077             int q = 0;
1078             for (int j = i; j < i + 5; j++)
1079             {
1080                 q += counts[j];
1081             }
1082             qMax = qMax > q ? qMax : q;
1083         }
1084         BufferedWriter bw;
1085         try
1086         {
1087             bw = new BufferedWriter(
1088                     new OutputStreamWriter(Writer.createOutputStream(this.outputFileTraj, CompressionType.NONE)));
1089             bw.write(String.format("total time spent [s]: %.0f", tts));
1090             bw.newLine();
1091             bw.write(String.format("maximum flow [veh/5min]: %d", qMax));
1092             bw.newLine();
1093             bw.write(String.format("time to collision [s]: %s", ttcList));
1094             bw.newLine();
1095             bw.write(String.format("time to collision GTU type: %s", ttcListGtuType));
1096             bw.newLine();
1097             bw.write(String.format("strong decelerations [m/s^2]: %s", decList));
1098             bw.newLine();
1099             bw.write(String.format("strong decelerations GTU type: %s", decListGtuType));
1100             bw.close();
1101         }
1102         catch (IOException exception)
1103         {
1104             throw new RuntimeException(exception);
1105         }
1106     }
1107 
1108     /**
1109      * Creates a frequency vector.
1110      * @param array double[]; array in veh/h
1111      * @return FrequencyVector; frequency vector
1112      * @throws ValueRuntimeException on problem
1113      */
1114     private FrequencyVector freq(final double[] array) throws ValueRuntimeException
1115     {
1116         return DoubleVector.instantiate(array, FrequencyUnit.PER_HOUR, StorageType.DENSE);
1117     }
1118 
1119     /**
1120      * Returns an OD compatible characteristics generator that uses the GTUType to select either regular models or CACC specific
1121      * models.
1122      * @param longitudinalControllerFactory LongitudinalControllerFactory&lt;? extends CACC&gt;; longitudinal controller factory
1123      * @param simulator OTSSimulatorInterface; simulator
1124      * @param parameters ParameterFactory parameters
1125      * @return OD compatible characteristics generator
1126      */
1127     private DefaultGTUCharacteristicsGeneratorOD getCharacteristicsGenerator(
1128             final LongitudinalControllerFactory<? extends CaccController> longitudinalControllerFactory,
1129             final OTSSimulatorInterface simulator, final ParameterFactory parameters)
1130     {
1131         // create template for CACC truck properties (this is then used instead of GTUType.defaultCharacteristics(...))
1132         Set<TemplateGTUType> templates = new LinkedHashSet<>();
1133         templates.add(new TemplateGTUType(caccGTUType, new ConstantGenerator<>(Length.instantiateSI(12.0)),
1134                 new ConstantGenerator<>(Length.instantiateSI(2.55)),
1135                 new ConstantGenerator<>(Speed.instantiateSI(this.setspeed.si + 2.78))));
1136         // anonymous class for factory supplier that overwrites the getFactory() method to return one based on the GTU type
1137         return new DefaultGTUCharacteristicsGeneratorOD(templates, new StrategicalPlannerFactorySupplierOD()
1138         {
1139             /** Strategical planner for regular traffic. */
1140             private LaneBasedStrategicalPlannerFactory<?> lmrsFactory = null;
1141 
1142             /** Strategical planner for CACC trucks. */
1143             private LaneBasedStrategicalPlannerFactory<?> caccFactory = null;
1144 
1145             /** {@inheritDoc} */
1146             @Override
1147             public LaneBasedStrategicalPlannerFactory<?> getFactory(final Node origin, final Node destination,
1148                     final Category category, final StreamInterface randomStream) throws GTUException
1149             {
1150                 GTUType gtuType = category.get(GTUType.class);
1151                 if (!gtuType.equals(caccGTUType))
1152                 {
1153                     if (this.lmrsFactory == null)
1154                     {
1155                         // perception factory with estimation distribution (i.e. over- vs. underestimation)
1156                         Distribution<Estimation> estimation;
1157                         try
1158                         {
1159                             estimation = new Distribution<>(randomStream);
1160                             estimation.add(new FrequencyAndObject<>(fractionUnderestimation, Estimation.UNDERESTIMATION));
1161                             estimation.add(new FrequencyAndObject<>(1.0 - fractionUnderestimation, Estimation.OVERESTIMATION));
1162                         }
1163                         catch (ProbabilityException ex)
1164                         {
1165                             throw new GTUException("Random stream is null.", ex);
1166                         }
1167 
1168                         PerceptionFactory perceptionFactory = new LmrsPerceptionFactory(estimation);
1169 
1170                         this.lmrsFactory = new LaneBasedStrategicalRoutePlannerFactory(
1171                                 new LMRSFactory(new IDMPlusFactory(randomStream), perceptionFactory), parameters);
1172                     }
1173                     return this.lmrsFactory;
1174                 }
1175                 if (this.caccFactory == null)
1176                 {
1177                     this.caccFactory = new LaneBasedStrategicalRoutePlannerFactory(
1178                             new CaccTacticalPlannerFactory(new IDMPlusFactory(randomStream), longitudinalControllerFactory,
1179                                     simulator, caccGTUType),
1180                             parameters);
1181                 }
1182                 return this.caccFactory;
1183             }
1184         });
1185     }
1186 
1187     /** ... */
1188     private class LmrsPerceptionFactory extends DefaultLMRSPerceptionFactory
1189     {
1190         /** Estimation distribution. */
1191         private final Distribution<Estimation> estimation;
1192 
1193         /**
1194          * Constructor.
1195          * @param estimation Distribution&lt;Estimation&gt;; estimation distribution
1196          */
1197         LmrsPerceptionFactory(final Distribution<Estimation> estimation)
1198         {
1199             this.estimation = estimation;
1200         }
1201 
1202         /** {@inheritDoc} */
1203         @Override
1204         public LanePerception generatePerception(final LaneBasedGTU gtu)
1205         {
1206             Set<Task> tasksSet = new LinkedHashSet<>();
1207             if (tasks)
1208             {
1209                 tasksSet.add(new CarFollowingTaskAR());
1210                 tasksSet.add(new LaneChangeTaskAR());
1211             }
1212 
1213             Set<BehavioralAdaptation> behavioralAdapatations = new LinkedHashSet<>();
1214             behavioralAdapatations.add(new AdaptationSituationalAwareness());
1215             if (adaptation)
1216             {
1217                 behavioralAdapatations.add(new AdaptationHeadway());
1218             }
1219             LanePerception perception;
1220             if (tasks)
1221             {
1222                 Fuller fuller = new Fuller(tasksSet, behavioralAdapatations, new TaskManagerAR());
1223                 perception = new CategoricalLanePerception(gtu, fuller);
1224             }
1225             else
1226             {
1227                 perception = new CategoricalLanePerception(gtu);
1228             }
1229             perception.addPerceptionCategory(new DirectEgoPerception<>(perception));
1230             perception.addPerceptionCategory(new DirectInfrastructurePerception(perception));
1231             Estimation est = Try.assign(() -> this.estimation.draw(), "Probability exception while drawing estimation.");
1232             perception.addPerceptionCategory(
1233                     new DirectNeighborsPerception(perception, new PerceivedHeadwayGtuType(est, Anticipation.CONSTANT_SPEED)));
1234             perception.addPerceptionCategory(new AnticipationTrafficPerception(perception));
1235             perception.addPerceptionCategory(new DirectIntersectionPerception(perception, HeadwayGtuType.WRAP));
1236             return perception;
1237         }
1238 
1239         /** {@inheritDoc} */
1240         @Override
1241         public Parameters getParameters() throws ParameterException
1242         {
1243             Parameters params = super.getParameters();
1244             //params.setParameter(HEXP, Duration.instantiateSI(4.0));
1245             //params.setParameter(ALPHA, alpha);
1246             //params.setParameter(BETA, beta);
1247             params.setParameter(Fuller.TC, 1.0);
1248             params.setParameter(Fuller.TS_CRIT, 1.0); // Was not changed!
1249             params.setParameter(Fuller.TS_MAX, 2.0);
1250             params.setParameter(AdaptationSituationalAwareness.SA, 1.0);
1251             params.setParameter(AdaptationSituationalAwareness.SA_MAX, 1.0);
1252             params.setParameter(AdaptationSituationalAwareness.SA_MIN, 0.5);
1253             params.setParameter(AdaptationSituationalAwareness.TR_MAX, Duration.instantiateSI(2.0));
1254             params.setParameter(ParameterTypes.TR, Duration.ZERO);
1255             params.setParameter(AdaptationHeadway.BETA_T, 1.0); // Increase?
1256             return params;
1257         }
1258     }
1259 
1260     /** ... */
1261     private class TaskManagerAR implements TaskManager
1262     {
1263         /** {@inheritDoc} */
1264         @Override
1265         public void manage(final Set<Task> tasksMan, final LanePerception perception, final LaneBasedGTU gtu,
1266                 final Parameters parameters) throws ParameterException, GTUException
1267         {
1268             Task primary = null;
1269             Set<Task> auxiliaryTasks = new LinkedHashSet<>();
1270             for (Task task : tasksMan)
1271             {
1272                 if (task.getId().equals("lane-changing"))
1273                 {
1274                     primary = task;
1275                 }
1276                 else
1277                 {
1278                     auxiliaryTasks.add(task);
1279                 }
1280             }
1281             Throw.whenNull(primary, "There is no task with id 'lane-changing'.");
1282             double primaryTaskDemand = primary.calculateTaskDemand(perception, gtu, parameters);
1283             primary.setTaskDemand(primaryTaskDemand);
1284             // max AR is alpha of TD, actual AR approaches 0 for increasing TD
1285             double a = alpha;
1286             double b = beta;
1287             primary.setAnticipationReliance(a * primaryTaskDemand * (1.0 - primaryTaskDemand));
1288             for (Task auxiliary : auxiliaryTasks)
1289             {
1290                 double auxiliaryTaskLoad = auxiliary.calculateTaskDemand(perception, gtu, parameters);
1291                 auxiliary.setTaskDemand(auxiliaryTaskLoad);
1292                 // max AR is beta of TD, actual AR approaches 0 as primary TD approaches 0
1293                 auxiliary.setAnticipationReliance(b * auxiliaryTaskLoad * primaryTaskDemand);
1294             }
1295         }
1296     }
1297 
1298     /** Car-following task. */
1299     private class CarFollowingTaskAR extends AbstractTask
1300     {
1301         /** Constructor. */
1302         CarFollowingTaskAR()
1303         {
1304             super("car-following");
1305         }
1306 
1307         /** {@inheritDoc} */
1308         @Override
1309         public double calculateTaskDemand(final LanePerception perception, final LaneBasedGTU gtuCF,
1310                 final Parameters parameters) throws ParameterException, GTUException
1311         {
1312             try
1313             {
1314                 NeighborsPerception neighbors = perception.getPerceptionCategory(NeighborsPerception.class);
1315                 PerceptionCollectable<HeadwayGTU, LaneBasedGTU> leaders = neighbors.getLeaders(RelativeLane.CURRENT);
1316                 CategoricalLanePerception clp = (CategoricalLanePerception) perception;
1317                 if (null == ((DirectEgoPerception<?, ?>) clp.getPerceptionCategory(EgoPerception.class)).getTimeStampedSpeed())
1318                 {
1319                     // System.out.println("CarFollowingTaskAR for " + perception.getGtu());
1320                     // System.out.println("perception is " + perception);
1321                     try
1322                     {
1323                         clp.getPerceptionCategory(EgoPerception.class).updateAll();
1324                         if (null == clp.getPerceptionCategory(EgoPerception.class).getSpeed())
1325                         {
1326                             System.out.println("fixed perception is " + perception);
1327                         }
1328                     }
1329                     catch (GTUException | NetworkException | ParameterException e)
1330                     {
1331                         e.printStackTrace();
1332                     }
1333                 }
1334                 // boolean state = leaders.isEmpty();
1335                 // System.out.println("Leaders.isEmpty: " + state);
1336                 return leaders.isEmpty() ? 0.0 : Math.exp(-leaders.first().getDistance().si
1337                         / (perception.getGtu().getSpeed().si * hExp.si));
1338             }
1339             catch (OperationalPlanException ex)
1340             {
1341                 throw new GTUException(ex);
1342             }
1343         }
1344     }
1345 
1346     /** Lane change task. */
1347     private class LaneChangeTaskAR extends AbstractTask
1348     {
1349         /** Constructor. */
1350         LaneChangeTaskAR()
1351         {
1352             super("lane-changing");
1353         }
1354 
1355         /** {@inheritDoc} */
1356         @Override
1357         public double calculateTaskDemand(final LanePerception perception, final LaneBasedGTU gtuLC,
1358                 final Parameters parameters) throws ParameterException, GTUException
1359         {
1360             return Math.max(0.0,
1361                     Math.max(parameters.getParameter(LmrsParameters.DLEFT), parameters.getParameter(LmrsParameters.DRIGHT)));
1362         }
1363     }
1364 
1365 }