View Javadoc
1   package org.opentrafficsim.demo.sdm;
2   
3   import java.awt.Color;
4   import java.io.BufferedWriter;
5   import java.io.IOException;
6   import java.io.OutputStreamWriter;
7   import java.util.ArrayList;
8   import java.util.List;
9   
10  import org.djunits.unit.FrequencyUnit;
11  import org.djunits.unit.SpeedUnit;
12  import org.djunits.unit.TimeUnit;
13  import org.djunits.value.ValueRuntimeException;
14  import org.djunits.value.storage.StorageType;
15  import org.djunits.value.vdouble.scalar.Acceleration;
16  import org.djunits.value.vdouble.scalar.Direction;
17  import org.djunits.value.vdouble.scalar.Duration;
18  import org.djunits.value.vdouble.scalar.Frequency;
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.djunits.value.vfloat.scalar.FloatDuration;
26  import org.djutils.cli.CliException;
27  import org.djutils.cli.CliUtil;
28  import org.djutils.exceptions.Throw;
29  import org.opentrafficsim.base.compressedfiles.CompressionType;
30  import org.opentrafficsim.base.compressedfiles.Writer;
31  import org.opentrafficsim.core.animation.gtu.colorer.AccelerationGTUColorer;
32  import org.opentrafficsim.core.animation.gtu.colorer.SpeedGTUColorer;
33  import org.opentrafficsim.core.animation.gtu.colorer.SwitchableGTUColorer;
34  import org.opentrafficsim.core.compatibility.Compatible;
35  import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
36  import org.opentrafficsim.core.geometry.OTSPoint3D;
37  import org.opentrafficsim.core.gtu.GTUDirectionality;
38  import org.opentrafficsim.core.gtu.GTUType;
39  import org.opentrafficsim.core.network.LinkType;
40  import org.opentrafficsim.core.network.NetworkException;
41  import org.opentrafficsim.core.network.OTSNode;
42  import org.opentrafficsim.core.perception.HistoryManagerDEVS;
43  import org.opentrafficsim.draw.graphs.ContourDataSource;
44  import org.opentrafficsim.draw.graphs.ContourPlotSpeed;
45  import org.opentrafficsim.draw.graphs.GraphPath;
46  import org.opentrafficsim.draw.graphs.road.GraphLaneUtil;
47  import org.opentrafficsim.kpi.sampling.KpiGtuDirectionality;
48  import org.opentrafficsim.kpi.sampling.KpiLaneDirection;
49  import org.opentrafficsim.kpi.sampling.SamplingException;
50  import org.opentrafficsim.kpi.sampling.SpaceTimeRegion;
51  import org.opentrafficsim.kpi.sampling.Trajectory;
52  import org.opentrafficsim.kpi.sampling.TrajectoryGroup;
53  import org.opentrafficsim.road.gtu.colorer.DesiredHeadwayColorer;
54  import org.opentrafficsim.road.gtu.colorer.DistractionColorer;
55  import org.opentrafficsim.road.gtu.colorer.FixedColor;
56  import org.opentrafficsim.road.gtu.colorer.SynchronizationColorer;
57  import org.opentrafficsim.road.gtu.colorer.TaskSaturationColorer;
58  import org.opentrafficsim.road.gtu.generator.od.DefaultGTUCharacteristicsGeneratorOD;
59  import org.opentrafficsim.road.gtu.generator.od.ODApplier;
60  import org.opentrafficsim.road.gtu.generator.od.ODOptions;
61  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
62  import org.opentrafficsim.road.gtu.lane.perception.mental.AdaptationSituationalAwareness;
63  import org.opentrafficsim.road.gtu.lane.perception.mental.ExponentialTask;
64  import org.opentrafficsim.road.gtu.lane.perception.mental.Task;
65  import org.opentrafficsim.road.gtu.lane.perception.mental.sdm.DefaultDistraction;
66  import org.opentrafficsim.road.gtu.lane.perception.mental.sdm.DistractionFactory;
67  import org.opentrafficsim.road.gtu.lane.perception.mental.sdm.StochasticDistractionModel;
68  import org.opentrafficsim.road.gtu.lane.perception.mental.sdm.TaskSupplier;
69  import org.opentrafficsim.road.gtu.strategical.od.Categorization;
70  import org.opentrafficsim.road.gtu.strategical.od.Category;
71  import org.opentrafficsim.road.gtu.strategical.od.Interpolation;
72  import org.opentrafficsim.road.gtu.strategical.od.ODMatrix;
73  import org.opentrafficsim.road.network.OTSRoadNetwork;
74  import org.opentrafficsim.road.network.factory.LaneFactory;
75  import org.opentrafficsim.road.network.lane.CrossSectionLink;
76  import org.opentrafficsim.road.network.lane.Lane;
77  import org.opentrafficsim.road.network.lane.LaneDirection;
78  import org.opentrafficsim.road.network.lane.LaneType;
79  import org.opentrafficsim.road.network.lane.OTSRoadNode;
80  import org.opentrafficsim.road.network.lane.Stripe.Permeable;
81  import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy;
82  import org.opentrafficsim.road.network.lane.object.sensor.SinkSensor;
83  import org.opentrafficsim.road.network.sampling.LaneData;
84  import org.opentrafficsim.road.network.sampling.RoadSampler;
85  import org.opentrafficsim.road.network.sampling.data.TimeToCollision;
86  import org.opentrafficsim.swing.graphs.SwingContourPlot;
87  import org.opentrafficsim.swing.graphs.SwingPlot;
88  import org.opentrafficsim.swing.gui.OTSSimulationApplication;
89  import org.opentrafficsim.swing.script.AbstractSimulationScript;
90  import org.opentrafficsim.swing.script.IdmOptions;
91  
92  import nl.tudelft.simulation.dsol.swing.gui.TablePanel;
93  import nl.tudelft.simulation.jstats.streams.StreamInterface;
94  import picocli.CommandLine.Mixin;
95  import picocli.CommandLine.Option;
96  
97  /**
98   * Script to run a simulation with the Stochastic Distraction Model.
99   * <p>
100  * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
101  * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
102  * <p>
103  * @version $Revision$, $LastChangedDate$, by $Author$, initial version 5 nov. 2018 <br>
104  * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
105  * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
106  * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
107  */
108 public class SdmSimulation extends AbstractSimulationScript
109 {
110     /** */
111     private static final long serialVersionUID = 20200516L;
112 
113     /** Network. */
114     private OTSRoadNetwork network;
115 
116     /** Sampler for statistics. */
117     private RoadSampler sampler;
118 
119     /** Time to collision data type. */
120     private final TimeToCollision ttc = new TimeToCollision();
121 
122     /** Space time regions to sample traffic on. */
123     private final List<SpaceTimeRegion> regions = new ArrayList<>();
124 
125     // Options
126 
127     /** IDM parameters. */
128     @Mixin
129     private IdmOptions idmOptions;
130 
131     /** Output file. */
132     @Option(names = "--outputFile", description = "Output file", defaultValue = "output.txt")
133     private String outputFile;
134 
135     /** Output. */
136     @Option(names = {"-o", "--output"}, description = "Create output", negatable = true, defaultValue = "true")
137     private boolean output;
138 
139     /** Plots. */
140     @Option(names = {"-p", "--plots"}, description = "Create plots", negatable = true, defaultValue = "false")
141     private boolean plots;
142 
143     /** Fraction of trucks. */
144     @Option(names = "--truckFraction", description = "Fraction of trucks", defaultValue = "0.05")
145     private double truckFraction;
146 
147     /** Demand left. */
148     @Option(names = "--demandLeft", description = "Demand left", defaultValue = "3600/h")
149     private Frequency demandLeft;
150 
151     /** Demand right. */
152     @Option(names = "--demandRight", description = "Demand right", defaultValue = "3600/h")
153     private Frequency demandRight;
154 
155     /** Start demand factor. */
156     @Option(names = "--startDemandFctor", description = "Factor on demand at start", defaultValue = "0.45")
157     private double startDemandFctor;
158 
159     /** Allow multitasking. */
160     @Option(names = "--multitasking", description = "Multitasking", negatable = true, defaultValue = "false")
161     private boolean multitasking;
162 
163     /** Allow multitasking. */
164     @Option(names = "--distractions", split = ",",
165             description = "Distraction, 1=TALKING_CELL_PHONE,12=CONVERSING,5=MANIPULATING_AUDIO_CONTROLS,16=EXTERNAL_DISTRACTION",
166             defaultValue = "1,12,5,16")
167     private String[] distractions;
168 
169     /** Initial task demand when talking on the phone. */
170     @Option(names = "--phoneInit", description = "Initial task demand when talking on the phone", defaultValue = "1.0")
171     private double phoneInit;
172 
173     /** Final task demand when talking on the phone. */
174     @Option(names = "--phoneFinal", description = "Final task demand when talking on the phone", defaultValue = "0.3")
175     private double phoneFinal;
176 
177     /** Exponential duration if task demand reduction when talking on the phone. */
178     @Option(names = "--phoneTau", description = "Exponential duration if task demand reduction when talking on the phone",
179             defaultValue = "10s")
180     private Duration phoneTau;
181 
182     /** Task demand when conversing. */
183     @Option(names = "--conversing", description = "Task demand when conversing", defaultValue = "0.3")
184     private double conversing;
185 
186     /** Task demand when controlling audio. */
187     @Option(names = "--audio", description = "Task demand when controlling audio", defaultValue = "0.3")
188     private double audio;
189 
190     /** Fixed (minimum) task demand for external distraction. */
191     @Option(names = "--externalBase", description = "Fixed (minimum) task demand for external distraction",
192             defaultValue = "0.2")
193     private double externalBase;
194 
195     /** Variable task demand for external distraction. */
196     @Option(names = "--externalVar",
197             description = "Variable task demand for external distraction (random fraction added externalBase)",
198             defaultValue = "0.3")
199     private double externalVar;
200 
201     /** Time step. */
202     @Option(names = "--dt", description = "Time step", defaultValue = "0.5s")
203     private Duration dt;
204 
205     /** Minimum situational awareness. */
206     @Option(names = "--saMin", description = "Minimum situational awareness", defaultValue = "0.5")
207     private double saMin;
208 
209     /** Maximum situational awareness. */
210     @Option(names = "--saMax", description = "Maximum situational awareness", defaultValue = "1.0")
211     private double saMax;
212 
213     /** Task capability. */
214     @Option(names = "--tc", description = "Task capability", defaultValue = "1.0")
215     private double tc;
216 
217     /** Critical task saturation. */
218     @Option(names = "--tsCrit", description = "Critical task saturation", defaultValue = "0.8")
219     private double tsCrit;
220 
221     /** Maximum task saturation. */
222     @Option(names = "--tsMax", description = "Maximum task saturation", defaultValue = "2.0")
223     private double tsMax;
224 
225     /** Maximum reaction time. */
226     @Option(names = "--trMax", description = "Maximum reaction time", defaultValue = "2.0s")
227     private Duration trMax;
228 
229     /** Maximum additional factor on headway for adaptation. */
230     @Option(names = "--betaT", description = "Maximum additional factor on headway for adaptation", defaultValue = "1.0")
231     private double betaT;
232 
233     /**
234      * Constructor.
235      */
236     protected SdmSimulation()
237     {
238         super("SDM simulation", "Simulations using the Stochastic Distraction Model");
239         // set GTU colorers to use
240         setGtuColorer(SwitchableGTUColorer.builder().addActiveColorer(new FixedColor(Color.BLUE, "Blue")).addColorer(
241             new SynchronizationColorer()).addColorer(new DistractionColorer(DefaultDistraction.ANSWERING_CELL_PHONE,
242                 DefaultDistraction.CONVERSING, DefaultDistraction.MANIPULATING_AUDIO_CONTROLS,
243                 DefaultDistraction.EXTERNAL_DISTRACTION)).addColorer(new SpeedGTUColorer(new Speed(150, SpeedUnit.KM_PER_HOUR)))
244             .addColorer(new AccelerationGTUColorer(Acceleration.instantiateSI(-6.0), Acceleration.instantiateSI(2))).addColorer(
245                 new DesiredHeadwayColorer(Duration.instantiateSI(0.56), Duration.instantiateSI(2.4))).addColorer(
246                     new TaskSaturationColorer()).build());
247         try
248         {
249             CliUtil.changeOptionDefault(this, "warmupTime", "300s");
250             CliUtil.changeOptionDefault(this, "simulationTime", "3900s");
251             CliUtil.changeOptionDefault(IdmOptions.class, "aTruck", "0.8m/s^2");
252         }
253         catch (NoSuchFieldException | IllegalStateException | IllegalArgumentException | CliException exception)
254         {
255             exception.printStackTrace();
256         }
257     }
258 
259     /**
260      * Start a simulation.
261      * @param args String...; command line arguments
262      */
263     public static void main(final String... args)
264     {
265         try
266         {
267             SdmSimulationimulation.html#SdmSimulation">SdmSimulation sim = new SdmSimulation();
268             CliUtil.execute(sim, args);
269             sim.start();
270         }
271         catch (Exception ex)
272         {
273             ex.printStackTrace();
274         }
275     }
276 
277     /** {@inheritDoc} */
278     @Override
279     public void check() throws Exception
280     {
281         super.check();
282         Throw.when(this.truckFraction < 0.0 || this.truckFraction > 1.0, IllegalArgumentException.class,
283             "Truck fraction %f is below 0.0 or above 1.0.");
284     }
285 
286     /** {@inheritDoc} */
287     @Override
288     protected OTSRoadNetwork setupSimulation(final OTSSimulatorInterface sim) throws Exception
289     {
290         // manager of historic information to allow a reaction time
291         sim.getReplication().setHistoryManager(new HistoryManagerDEVS(sim, AdaptationSituationalAwareness.TR_MAX
292             .getDefaultValue(), Duration.instantiateSI(10.0)));
293 
294         // Network
295         this.network = new OTSRoadNetwork("SDM", true, getSimulator());
296         OTSPoint3D pointA = new OTSPoint3D(0.0, 0.0);
297         OTSPoint3D pointB = new OTSPoint3D(0.0, -20.0);
298         OTSPoint3D pointC = new OTSPoint3D(1600.0, -20.0);
299         OTSPoint3D pointD = new OTSPoint3D(2000.0, 0.0);
300         OTSPoint3D pointE = new OTSPoint3D(2500.0, 0.0);
301         OTSPoint3D pointF = new OTSPoint3D(3500.0, 0.0);
302         OTSRoadNode nodeA = new OTSRoadNode(this.network, "A", pointA, Direction.ZERO);
303         OTSRoadNode nodeB = new OTSRoadNode(this.network, "B", pointB, Direction.ZERO);
304         OTSRoadNode nodeC = new OTSRoadNode(this.network, "C", pointC, Direction.ZERO);
305         OTSRoadNode nodeD = new OTSRoadNode(this.network, "D", pointD, Direction.ZERO);
306         OTSRoadNode nodeE = new OTSRoadNode(this.network, "E", pointE, Direction.ZERO);
307         OTSRoadNode nodeF = new OTSRoadNode(this.network, "F", pointF, Direction.ZERO);
308         LinkType type = this.network.getLinkType(LinkType.DEFAULTS.FREEWAY);
309         LaneKeepingPolicy policy = LaneKeepingPolicy.KEEPRIGHT;
310         Length laneWidth = Length.instantiateSI(3.5);
311         LaneType laneType = this.network.getLaneType(LaneType.DEFAULTS.FREEWAY);
312         Speed speedLimit = new Speed(120.0, SpeedUnit.KM_PER_HOUR);
313         List<Lane> allLanes = new ArrayList<>();
314         allLanes.addAll(new LaneFactory(this.network, nodeA, nodeD, type, sim, policy).leftToRight(2.0, laneWidth, laneType,
315             speedLimit).addLanes(Permeable.BOTH).getLanes());
316         allLanes.addAll(new LaneFactory(this.network, nodeB, nodeC, type, sim, policy).leftToRight(0.0, laneWidth, laneType,
317             speedLimit).addLanes(Permeable.BOTH).getLanes());
318         allLanes.addAll(new LaneFactory(this.network, nodeC, nodeD, type, sim, policy).leftToRight(0.0, laneWidth, laneType,
319             speedLimit).addLanes(Permeable.BOTH).getLanes());
320         allLanes.addAll(new LaneFactory(this.network, nodeD, nodeE, type, sim, policy).leftToRight(2.0, laneWidth, laneType,
321             speedLimit).addLanes(Permeable.BOTH, Permeable.BOTH, Permeable.BOTH).getLanes());
322         List<Lane> lanesEF = new LaneFactory(this.network, nodeE, nodeF, type, sim, policy).leftToRight(1.0, laneWidth,
323             laneType, speedLimit).addLanes(Permeable.BOTH, Permeable.BOTH).getLanes();
324         allLanes.addAll(lanesEF);
325         for (Lane lane : lanesEF)
326         {
327             new SinkSensor(lane, lane.getLength().minus(Length.instantiateSI(50)), Compatible.EVERYTHING, sim);
328         }
329 
330         // OD
331         List<OTSNode> origins = new ArrayList<>();
332         origins.add(nodeA);
333         origins.add(nodeB);
334         List<OTSNode> destinations = new ArrayList<>();
335         destinations.add(nodeF);
336         double wut = sim.getReplication().getTreatment().getWarmupPeriod().si;
337         double rl = sim.getReplication().getTreatment().getRunLength().si;
338         TimeVector timeVector = DoubleVector.instantiate(new double[] {0.0, wut, wut + (rl - wut) * 0.5, rl}, TimeUnit.DEFAULT,
339             StorageType.DENSE);
340         Interpolation interpolation = Interpolation.LINEAR;
341         Categorization categorization = new Categorization("GTU categorization", GTUType.class);
342         ODMatrix odMatrix = new ODMatrix("OD", origins, destinations, categorization, timeVector, interpolation);
343         Category carCategory = new Category(categorization, this.network.getGtuType(GTUType.DEFAULTS.CAR));
344         Category truCategory = new Category(categorization, this.network.getGtuType(GTUType.DEFAULTS.TRUCK));
345         double f1 = this.truckFraction;
346         double f2 = 1.0 - f1;
347         double left2 = this.demandLeft.getInUnit(FrequencyUnit.PER_HOUR);
348         double right2 = this.demandRight.getInUnit(FrequencyUnit.PER_HOUR);
349         double startDemandFactor = this.startDemandFctor;
350         double left1 = left2 * startDemandFactor;
351         double right1 = right2 * startDemandFactor;
352         odMatrix.putDemandVector(nodeA, nodeF, carCategory, freq(new double[] {f2 * left1, f2 * left1, f2 * left2, 0.0}));
353         odMatrix.putDemandVector(nodeA, nodeF, truCategory, freq(new double[] {f1 * left1, f1 * left1, f1 * left2, 0.0}));
354         odMatrix.putDemandVector(nodeB, nodeF, carCategory, freq(new double[] {f2 * right1, f2 * right1, f2 * right2, 0.0}));
355         odMatrix.putDemandVector(nodeB, nodeF, truCategory, freq(new double[] {f1 * right1, f1 * right1, f1 * right2, 0.0}));
356         ODOptions odOptions = new ODOptions().set(ODOptions.NO_LC_DIST, Length.instantiateSI(200)).set(ODOptions.GTU_TYPE,
357             new DefaultGTUCharacteristicsGeneratorOD(new SdmStrategicalPlannerFactory(this.network, sim.getReplication()
358                 .getStream("generation"), this)));
359         ODApplier.applyOD(this.network, odMatrix, odOptions);
360 
361         // setup the SDM
362         DistractionFactory distFactory = new DistractionFactory(sim.getReplication().getStream("default"));
363         for (String distraction : this.distractions)
364         {
365             DefaultDistraction dist = DefaultDistraction.values()[Integer.parseInt(distraction) - 1];
366             distFactory.addDistraction(dist, getTaskSupplier(dist, sim.getReplication().getStream("default")));
367         }
368         new StochasticDistractionModel(this.multitasking, distFactory.build(), sim, this.network);
369 
370         // sampler
371         if (this.output)
372         {
373             this.sampler = RoadSampler.build(this.network).registerExtendedDataType(this.ttc).create();
374             Time start = new Time(0.05, TimeUnit.BASE_HOUR);
375             Time end = new Time(1.05, TimeUnit.BASE_HOUR);
376             for (Lane lane : allLanes)
377             {
378                 KpiLaneDirection kpiLane = new KpiLaneDirection(new LaneData(lane), KpiGtuDirectionality.DIR_PLUS);
379                 SpaceTimeRegion region = new SpaceTimeRegion(kpiLane, Length.ZERO, lane.getLength(), start, end);
380                 this.regions.add(region);
381                 this.sampler.registerSpaceTimeRegion(region);
382             }
383         }
384 
385         // return network
386         return this.network;
387     }
388 
389     /** {@inheritDoc} */
390     @Override
391     protected void addTabs(final OTSSimulatorInterface sim, final OTSSimulationApplication<?> animation)
392     {
393         if (!this.output || !this.plots)
394         {
395             return;
396         }
397         try
398         {
399             TablePanel charts = new TablePanel(2, 2);
400             GraphPath<KpiLaneDirection> path1 = GraphLaneUtil.createPath("Left road, left lane", new LaneDirection(
401                 (Lane) ((CrossSectionLink) this.network.getLink("AD")).getCrossSectionElement("Lane 1"),
402                 GTUDirectionality.DIR_PLUS));
403             GraphPath<KpiLaneDirection> path2 = GraphLaneUtil.createPath("Left road, right lane", new LaneDirection(
404                 (Lane) ((CrossSectionLink) this.network.getLink("AD")).getCrossSectionElement("Lane 2"),
405                 GTUDirectionality.DIR_PLUS));
406             GraphPath<KpiLaneDirection> path3 = GraphLaneUtil.createPath("Right road, left lane", new LaneDirection(
407                 (Lane) ((CrossSectionLink) this.network.getLink("BC")).getCrossSectionElement("Lane 1"),
408                 GTUDirectionality.DIR_PLUS));
409             GraphPath<KpiLaneDirection> path4 = GraphLaneUtil.createPath("Right road, right lane", new LaneDirection(
410                 (Lane) ((CrossSectionLink) this.network.getLink("BC")).getCrossSectionElement("Lane 2"),
411                 GTUDirectionality.DIR_PLUS));
412             GraphPath.initRecording(this.sampler, path1);
413             GraphPath.initRecording(this.sampler, path2);
414             GraphPath.initRecording(this.sampler, path3);
415             GraphPath.initRecording(this.sampler, path4);
416             SwingPlot plot = null;
417             plot = new SwingContourPlot(new ContourPlotSpeed("Left road, left lane", sim, new ContourDataSource<>(this.sampler
418                 .getSamplerData(), path1)));
419             charts.setCell(plot.getContentPane(), 0, 0);
420             plot = new SwingContourPlot(new ContourPlotSpeed("Left road, right lane", sim, new ContourDataSource<>(this.sampler
421                 .getSamplerData(), path2)));
422             charts.setCell(plot.getContentPane(), 1, 0);
423             plot = new SwingContourPlot(new ContourPlotSpeed("Right road, left lane", sim, new ContourDataSource<>(this.sampler
424                 .getSamplerData(), path3)));
425             charts.setCell(plot.getContentPane(), 0, 1);
426             plot = new SwingContourPlot(new ContourPlotSpeed("Right road, right lane", sim, new ContourDataSource<>(this.sampler
427                 .getSamplerData(), path4)));
428             charts.setCell(plot.getContentPane(), 1, 1);
429             animation.getAnimationPanel().getTabbedPane().addTab(animation.getAnimationPanel().getTabbedPane().getTabCount(),
430                 "statistics ", charts);
431         }
432         catch (NetworkException exception)
433         {
434             throw new RuntimeException(exception);
435         }
436     }
437 
438     /**
439      * Creates a frequency vector.
440      * @param array double[]; array in veh/h
441      * @return FrequencyVector; frequency vector
442      * @throws ValueRuntimeException on problem
443      */
444     private FrequencyVector freq(final double[] array) throws ValueRuntimeException
445     {
446         return DoubleVector.instantiate(array, FrequencyUnit.PER_HOUR, StorageType.DENSE);
447     }
448 
449     /**
450      * Returns a task supplier for a distraction. These are specific to the SDM simulations.
451      * @param distraction DefaultDistraction; distraction
452      * @param stream StreamInterface; random number stream for randomized aspects of the distraction
453      * @return TaskSupplier; task supplier
454      */
455     private TaskSupplier getTaskSupplier(final DefaultDistraction distraction, final StreamInterface stream)
456     {
457         switch (distraction)
458         {
459             case TALKING_CELL_PHONE:
460             {
461                 return new TaskSupplier()
462                 {
463                     /** {@inheritDoc} */
464                     @SuppressWarnings("synthetic-access")
465                     @Override
466                     public Task getTask(final LaneBasedGTU gtu)
467                     {
468                         return new ExponentialTask(distraction.getId(), SdmSimulation.this.phoneInit,
469                             SdmSimulation.this.phoneFinal, SdmSimulation.this.phoneTau, gtu.getSimulator());
470                     }
471                 };
472             }
473             case CONVERSING:
474             {
475                 return new TaskSupplier.Constant(distraction.getId(), SdmSimulation.this.conversing);
476             }
477             case MANIPULATING_AUDIO_CONTROLS:
478             {
479                 return new TaskSupplier.Constant(distraction.getId(), SdmSimulation.this.audio);
480             }
481             case EXTERNAL_DISTRACTION:
482             {
483                 return new TaskSupplier.Constant(distraction.getId(), SdmSimulation.this.externalBase
484                     + SdmSimulation.this.externalVar * stream.nextDouble());
485             }
486             default:
487                 throw new IllegalArgumentException("Distraction " + distraction + " is not recognized.");
488         }
489     }
490 
491     /** {@inheritDoc} */
492     @Override
493     protected void onSimulationEnd()
494     {
495         if (this.output)
496         {
497             Length preDetectorPosition = Length.instantiateSI(400.0); // on link DE, upstream of lane drop
498             Length postDetectorPosition = Length.instantiateSI(100.0); // on link EF, downstream of lane drop
499             double tts = 0.0;
500             List<Float> ttcList = new ArrayList<>();
501             List<Float> decList = new ArrayList<>();
502             int[] counts = new int[60];
503             int[] speedCounts = new int[60];
504             double[] speedSum = new double[60];
505             for (SpaceTimeRegion region : this.regions)
506             {
507                 TrajectoryGroup<?> trajectoryGroup = this.sampler.getSamplerData().getTrajectoryGroup(region.getLaneDirection())
508                     .getTrajectoryGroup(region.getStartPosition(), region.getEndPosition(), region.getStartTime(), region
509                         .getEndTime());
510                 for (Trajectory<?> trajectory : trajectoryGroup)
511                 {
512                     try
513                     {
514                         tts += trajectory.getTotalDuration().si;
515                         for (FloatDuration ttcVal : trajectory.getExtendedData(this.ttc))
516                         {
517                             if (!Float.isNaN(ttcVal.si) && ttcVal.si < 20)
518                             {
519                                 ttcList.add(ttcVal.si);
520                             }
521                         }
522                         for (float decVal : trajectory.getA())
523                         {
524                             if (decVal < -2.0)
525                             {
526                                 decList.add(decVal);
527                             }
528                         }
529                         if (region.getLaneDirection().getLaneData().getLinkData().getId().equals("DE") && trajectory.size() > 1
530                             && trajectory.getX(0) < preDetectorPosition.si && trajectory.getX(trajectory.size()
531                                 - 1) > preDetectorPosition.si)
532                         {
533                             double t = trajectory.getTimeAtPosition(postDetectorPosition).si - region.getStartTime().si;
534                             double v = trajectory.getSpeedAtPosition(postDetectorPosition).si;
535                             speedCounts[(int) (t / 60.0)]++;
536                             speedSum[(int) (t / 60.0)] += v;
537                         }
538                         if (region.getLaneDirection().getLaneData().getLinkData().getId().equals("EF") && trajectory.size() > 1
539                             && trajectory.getX(0) < postDetectorPosition.si && trajectory.getX(trajectory.size()
540                                 - 1) > postDetectorPosition.si)
541                         {
542                             double t = trajectory.getTimeAtPosition(postDetectorPosition).si - region.getStartTime().si;
543                             counts[(int) (t / 60.0)]++;
544                         }
545                     }
546                     catch (SamplingException exception)
547                     {
548                         throw new RuntimeException(
549                             "Unexpected exception: TimeToCollission not available or index out of bounds.", exception);
550                     }
551                 }
552             }
553             double qMax = 0;
554             for (int i = 0; i < counts.length - 4; i++)
555             {
556                 int q = 0;
557                 for (int j = i; j < i + 5; j++)
558                 {
559                     q += counts[j];
560                 }
561                 qMax = qMax > q ? qMax : q;
562             }
563             qMax *= 12; // twelve periods of 5min in an hour
564             int n = 0;
565             int countSum = 0;
566             for (int i = 0; i < counts.length; i++)
567             {
568                 double v = speedSum[i] / speedCounts[i];
569                 if (v < 80 / 3.6)
570                 {
571                     countSum += counts[i];
572                     n++;
573                 }
574             }
575             double qSat = n == 0 ? Double.NaN : 60.0 * countSum / n; // per min -> per hour
576             BufferedWriter bw;
577             try
578             {
579                 bw = new BufferedWriter(new OutputStreamWriter(Writer.createOutputStream(this.outputFile,
580                     CompressionType.ZIP)));
581                 bw.write(String.format("total time spent [s]: %.0f", tts));
582                 bw.newLine();
583                 bw.write(String.format("maximum flow [veh/h]: %.3f", qMax));
584                 bw.newLine();
585                 bw.write(String.format("saturation flow [veh/h]: %.3f", qSat));
586                 bw.newLine();
587                 bw.write(String.format("time to collision [s]: %s", ttcList));
588                 bw.newLine();
589                 bw.write(String.format("strong decelerations [m/s^2]: %s", decList));
590                 bw.close();
591             }
592             catch (IOException exception)
593             {
594                 throw new RuntimeException(exception);
595             }
596         }
597     }
598 
599     /**
600      * @return idmOptions.
601      */
602     public IdmOptions getIdmOptions()
603     {
604         return this.idmOptions;
605     }
606 
607     /**
608      * @return dt.
609      */
610     public Duration getDt()
611     {
612         return this.dt;
613     }
614 
615     /**
616      * @return saMin.
617      */
618     public double getSaMin()
619     {
620         return this.saMin;
621     }
622 
623     /**
624      * @return saMax.
625      */
626     public double getSaMax()
627     {
628         return this.saMax;
629     }
630 
631     /**
632      * @return tc.
633      */
634     public double getTc()
635     {
636         return this.tc;
637     }
638 
639     /**
640      * @return tsCrit.
641      */
642     public double getTsCrit()
643     {
644         return this.tsCrit;
645     }
646 
647     /**
648      * @return tsMax.
649      */
650     public double getTsMax()
651     {
652         return this.tsMax;
653     }
654 
655     /**
656      * @return trMax.
657      */
658     public Duration getTrMax()
659     {
660         return this.trMax;
661     }
662 
663     /**
664      * @return betaT.
665      */
666     public double getBetaT()
667     {
668         return this.betaT;
669     }
670 
671 }