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