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.StorageType;
14  import org.djunits.value.ValueException;
15  import org.djunits.value.vdouble.scalar.Acceleration;
16  import org.djunits.value.vdouble.scalar.Duration;
17  import org.djunits.value.vdouble.scalar.Length;
18  import org.djunits.value.vdouble.scalar.Speed;
19  import org.djunits.value.vdouble.scalar.Time;
20  import org.djunits.value.vdouble.vector.FrequencyVector;
21  import org.djunits.value.vdouble.vector.TimeVector;
22  import org.djunits.value.vfloat.scalar.FloatDuration;
23  import org.opentrafficsim.base.compressedfiles.CompressionType;
24  import org.opentrafficsim.base.compressedfiles.Writer;
25  import org.opentrafficsim.core.animation.gtu.colorer.AccelerationGTUColorer;
26  import org.opentrafficsim.core.animation.gtu.colorer.SpeedGTUColorer;
27  import org.opentrafficsim.core.animation.gtu.colorer.SwitchableGTUColorer;
28  import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
29  import org.opentrafficsim.core.geometry.OTSPoint3D;
30  import org.opentrafficsim.core.gtu.GTUDirectionality;
31  import org.opentrafficsim.core.gtu.GTUType;
32  import org.opentrafficsim.core.network.LinkType;
33  import org.opentrafficsim.core.network.NetworkException;
34  import org.opentrafficsim.core.network.OTSNetwork;
35  import org.opentrafficsim.core.network.OTSNode;
36  import org.opentrafficsim.core.perception.HistoryManagerDEVS;
37  import org.opentrafficsim.draw.graphs.ContourDataSource;
38  import org.opentrafficsim.draw.graphs.ContourPlotSpeed;
39  import org.opentrafficsim.draw.graphs.GraphPath;
40  import org.opentrafficsim.draw.graphs.road.GraphLaneUtil;
41  import org.opentrafficsim.kpi.sampling.KpiGtuDirectionality;
42  import org.opentrafficsim.kpi.sampling.KpiLaneDirection;
43  import org.opentrafficsim.kpi.sampling.SamplingException;
44  import org.opentrafficsim.kpi.sampling.SpaceTimeRegion;
45  import org.opentrafficsim.kpi.sampling.Trajectory;
46  import org.opentrafficsim.kpi.sampling.TrajectoryGroup;
47  import org.opentrafficsim.road.gtu.colorer.DesiredHeadwayColorer;
48  import org.opentrafficsim.road.gtu.colorer.DistractionColorer;
49  import org.opentrafficsim.road.gtu.colorer.FixedColor;
50  import org.opentrafficsim.road.gtu.colorer.SynchronizationColorer;
51  import org.opentrafficsim.road.gtu.colorer.TaskSaturationColorer;
52  import org.opentrafficsim.road.gtu.generator.od.DefaultGTUCharacteristicsGeneratorOD;
53  import org.opentrafficsim.road.gtu.generator.od.ODApplier;
54  import org.opentrafficsim.road.gtu.generator.od.ODOptions;
55  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
56  import org.opentrafficsim.road.gtu.lane.perception.mental.AdaptationSituationalAwareness;
57  import org.opentrafficsim.road.gtu.lane.perception.mental.Fuller.Task;
58  import org.opentrafficsim.road.gtu.lane.perception.mental.sdm.DefaultDistraction;
59  import org.opentrafficsim.road.gtu.lane.perception.mental.sdm.DistractionFactory;
60  import org.opentrafficsim.road.gtu.lane.perception.mental.sdm.StochasticDistractionModel;
61  import org.opentrafficsim.road.gtu.lane.perception.mental.sdm.TaskSupplier;
62  import org.opentrafficsim.road.gtu.strategical.od.Categorization;
63  import org.opentrafficsim.road.gtu.strategical.od.Category;
64  import org.opentrafficsim.road.gtu.strategical.od.Interpolation;
65  import org.opentrafficsim.road.gtu.strategical.od.ODMatrix;
66  import org.opentrafficsim.road.network.factory.LaneFactory;
67  import org.opentrafficsim.road.network.lane.CrossSectionLink;
68  import org.opentrafficsim.road.network.lane.Lane;
69  import org.opentrafficsim.road.network.lane.LaneDirection;
70  import org.opentrafficsim.road.network.lane.LaneType;
71  import org.opentrafficsim.road.network.lane.Stripe.Permeable;
72  import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy;
73  import org.opentrafficsim.road.network.lane.object.sensor.SinkSensor;
74  import org.opentrafficsim.road.network.sampling.LaneData;
75  import org.opentrafficsim.road.network.sampling.RoadSampler;
76  import org.opentrafficsim.road.network.sampling.data.TimeToCollision;
77  import org.opentrafficsim.swing.gui.OTSSwingApplication;
78  import org.opentrafficsim.swing.script.AbstractSimulationScript;
79  
80  import nl.tudelft.simulation.dsol.swing.gui.TablePanel;
81  import nl.tudelft.simulation.jstats.streams.StreamInterface;
82  
83  /**
84   * Script to run a simulation with the Stochastic Distraction Model.
85   * <p>
86   * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
87   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
88   * <p>
89   * @version $Revision$, $LastChangedDate$, by $Author$, initial version 5 nov. 2018 <br>
90   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
91   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
92   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
93   */
94  public class SdmSimulation extends AbstractSimulationScript
95  {
96      /** Network. */
97      private OTSNetwork network;
98  
99      /** Sampler for statistics. */
100     private RoadSampler sampler;
101 
102     /** Time to collision data type. */
103     private final TimeToCollision ttc = new TimeToCollision();
104 
105     /** Space time regions to sample traffic on. */
106     private final List<SpaceTimeRegion> regions = new ArrayList<>();
107 
108     /**
109      * Constructor.
110      * @param properties String[]; command line arguments
111      */
112     protected SdmSimulation(final String[] properties)
113     {
114         super("SDM simulation", "Simulations using the Stochastic Distraction Model", properties);
115         // set GTU colorers to use
116         setGtuColorer(SwitchableGTUColorer.builder().addActiveColorer(new FixedColor(Color.BLUE, "Blue"))
117                 .addColorer(new SynchronizationColorer())
118                 .addColorer(new DistractionColorer(DefaultDistraction.ANSWERING_CELL_PHONE, DefaultDistraction.CONVERSING,
119                         DefaultDistraction.MANIPULATING_AUDIO_CONTROLS, DefaultDistraction.EXTERNAL_DISTRACTION))
120                 .addColorer(new SpeedGTUColorer(new Speed(150, SpeedUnit.KM_PER_HOUR)))
121                 .addColorer(new AccelerationGTUColorer(Acceleration.createSI(-6.0), Acceleration.createSI(2)))
122                 .addColorer(new DesiredHeadwayColorer(Duration.createSI(0.56), Duration.createSI(2.4)))
123                 .addColorer(new TaskSaturationColorer()).build());
124     }
125 
126     /**
127      * Start a simulation.
128      * @param args String...; command line arguments
129      */
130     public static void main(final String... args)
131     {
132         new SdmSimulation(args).start();
133     }
134 
135     /** {@inheritDoc} */
136     @Override
137     protected void setDefaultProperties()
138     {
139         // output
140         setProperty("outputFile", "output.txt");
141         setProperty("output", true);
142         setProperty("plots", false);
143 
144         // traffic
145         setProperty("startTime", "0");
146         setProperty("warmupTime", "300");
147         setProperty("simulationTime", "3900");
148         setProperty("truckFraction", 0.05);
149         setProperty("leftDemand", 3600);
150         setProperty("rightDemand", 3600);
151         setProperty("startDemandFactor", 0.45);
152 
153         // distractions
154         setProperty("allowMultiTasking", "false");
155         // 1=TALKING_CELL_PHONE,12=CONVERSING,5=MANIPULATING_AUDIO_CONTROLS,16=EXTERNAL_DISTRACTION
156         setProperty("distractions", "1,12,5,16");
157         setProperty("phoneInit", 1.0);
158         setProperty("phoneFinal", 0.3);
159         setProperty("phoneTau", 10.0);
160         setProperty("conversing", 0.3);
161         setProperty("audio", 0.3);
162         setProperty("externalBase", 0.2);
163         setProperty("externalVar", 0.3);
164 
165         // basic behavioral parameters
166         setProperty("DT", 0.5);
167         setProperty("TMIN", 0.56);
168         setProperty("TMAX", 1.2);
169         setProperty("A_CAR", 1.25);
170         setProperty("A_TRUCK", 0.8);
171         setProperty("B", 2.09);
172 
173         // human factors
174         setProperty("SA_MIN", 0.5);
175         setProperty("SA_MAX", 1.0);
176         setProperty("TR_MAX", 2.0);
177         setProperty("TC", 1.0);
178         setProperty("TS_CRIT", 0.8);
179         setProperty("TS_MAX", 2.0);
180         setProperty("BETA_T", 1.0);
181 
182     }
183 
184     /** {@inheritDoc} */
185     @Override
186     protected OTSNetwork setupSimulation(final OTSSimulatorInterface sim) throws Exception
187     {
188         // manager of historic information to allow a reaction time
189         sim.getReplication().setHistoryManager(
190                 new HistoryManagerDEVS(sim, AdaptationSituationalAwareness.TR_MAX.getDefaultValue(), Duration.createSI(10.0)));
191 
192         // Network
193         this.network = new OTSNetwork("SDM");
194         OTSPoint3D pointA = new OTSPoint3D(0.0, 0.0);
195         OTSPoint3D pointB = new OTSPoint3D(0.0, -20.0);
196         OTSPoint3D pointC = new OTSPoint3D(1600.0, -20.0);
197         OTSPoint3D pointD = new OTSPoint3D(2000.0, 0.0);
198         OTSPoint3D pointE = new OTSPoint3D(2500.0, 0.0);
199         OTSPoint3D pointF = new OTSPoint3D(3500.0, 0.0);
200         OTSNode nodeA = new OTSNode(this.network, "A", pointA);
201         OTSNode nodeB = new OTSNode(this.network, "B", pointB);
202         OTSNode nodeC = new OTSNode(this.network, "C", pointC);
203         OTSNode nodeD = new OTSNode(this.network, "D", pointD);
204         OTSNode nodeE = new OTSNode(this.network, "E", pointE);
205         OTSNode nodeF = new OTSNode(this.network, "F", pointF);
206         LinkType type = LinkType.FREEWAY;
207         LaneKeepingPolicy policy = LaneKeepingPolicy.KEEP_RIGHT;
208         Length laneWidth = Length.createSI(3.5);
209         LaneType laneType = LaneType.FREEWAY;
210         Speed speedLimit = new Speed(120.0, SpeedUnit.KM_PER_HOUR);
211         List<Lane> allLanes = new ArrayList<>();
212         allLanes.addAll(new LaneFactory(this.network, nodeA, nodeD, type, sim, policy)
213                 .leftToRight(2.0, laneWidth, laneType, speedLimit).addLanes(Permeable.BOTH).getLanes());
214         allLanes.addAll(new LaneFactory(this.network, nodeB, nodeC, type, sim, policy)
215                 .leftToRight(0.0, laneWidth, laneType, speedLimit).addLanes(Permeable.BOTH).getLanes());
216         allLanes.addAll(new LaneFactory(this.network, nodeC, nodeD, type, sim, policy)
217                 .leftToRight(0.0, laneWidth, laneType, speedLimit).addLanes(Permeable.BOTH).getLanes());
218         allLanes.addAll(
219                 new LaneFactory(this.network, nodeD, nodeE, type, sim, policy).leftToRight(2.0, laneWidth, laneType, speedLimit)
220                         .addLanes(Permeable.BOTH, Permeable.BOTH, Permeable.BOTH).getLanes());
221         List<Lane> lanesEF = new LaneFactory(this.network, nodeE, nodeF, type, sim, policy)
222                 .leftToRight(1.0, laneWidth, laneType, speedLimit).addLanes(Permeable.BOTH, Permeable.BOTH).getLanes();
223         allLanes.addAll(lanesEF);
224         for (Lane lane : lanesEF)
225         {
226             new SinkSensor(lane, lane.getLength().minus(Length.createSI(50)), sim);
227         }
228 
229         // OD
230         List<OTSNode> origins = new ArrayList<>();
231         origins.add(nodeA);
232         origins.add(nodeB);
233         List<OTSNode> destinations = new ArrayList<>();
234         destinations.add(nodeF);
235         double wut = sim.getReplication().getTreatment().getWarmupPeriod().si;
236         double rl = sim.getReplication().getTreatment().getRunLength().si;
237         TimeVector timeVector =
238                 new TimeVector(new double[] { 0.0, wut, wut + (rl - wut) * 0.5, rl }, TimeUnit.BASE, StorageType.DENSE);
239         Interpolation interpolation = Interpolation.LINEAR;
240         Categorization categorization = new Categorization("GTU categorization", GTUType.class);
241         ODMatrix odMatrix = new ODMatrix("OD", origins, destinations, categorization, timeVector, interpolation);
242         Category carCategory = new Category(categorization, GTUType.CAR);
243         Category truCategory = new Category(categorization, GTUType.TRUCK);
244         double f1 = getDoubleProperty("truckFraction");
245         double f2 = 1.0 - f1;
246         double left2 = getDoubleProperty("leftDemand");
247         double right2 = getDoubleProperty("rightDemand");
248         double startDemandFactor = getDoubleProperty("startDemandFactor");
249         double left1 = left2 * startDemandFactor;
250         double right1 = right2 * startDemandFactor;
251         odMatrix.putDemandVector(nodeA, nodeF, carCategory, freq(new double[] { f2 * left1, f2 * left1, f2 * left2, 0.0 }));
252         odMatrix.putDemandVector(nodeA, nodeF, truCategory, freq(new double[] { f1 * left1, f1 * left1, f1 * left2, 0.0 }));
253         odMatrix.putDemandVector(nodeB, nodeF, carCategory, freq(new double[] { f2 * right1, f2 * right1, f2 * right2, 0.0 }));
254         odMatrix.putDemandVector(nodeB, nodeF, truCategory, freq(new double[] { f1 * right1, f1 * right1, f1 * right2, 0.0 }));
255         ODOptions odOptions = new ODOptions().set(ODOptions.NO_LC_DIST, Length.createSI(200)).set(ODOptions.GTU_TYPE,
256                 new DefaultGTUCharacteristicsGeneratorOD(
257                         new SdmStrategicalPlannerFactory(sim.getReplication().getStream("generation"), this)));
258         ODApplier.applyOD(this.network, odMatrix, sim, odOptions);
259 
260         // animation
261         this.animateNetwork(this.network, false);
262 
263         // setup the SDM
264         DistractionFactory distFactory = new DistractionFactory(sim.getReplication().getStream("default"));
265         for (String distraction : getProperty("distractions").split(","))
266         {
267             DefaultDistraction dist = DefaultDistraction.values()[Integer.parseInt(distraction) - 1];
268             distFactory.addDistraction(dist, getTaskSupplier(dist, sim.getReplication().getStream("default")));
269         }
270         new StochasticDistractionModel(getBooleanProperty("allowMultiTasking"), distFactory.build(), sim, this.network);
271 
272         // sampler
273         if (getBooleanProperty("output"))
274         {
275             this.sampler = new RoadSampler(sim);
276             Time start = new Time(0.05, TimeUnit.BASE_HOUR);
277             Time end = new Time(1.05, TimeUnit.BASE_HOUR);
278             for (Lane lane : allLanes)
279             {
280                 KpiLaneDirection kpiLane = new KpiLaneDirection(new LaneData(lane), KpiGtuDirectionality.DIR_PLUS);
281                 SpaceTimeRegion region = new SpaceTimeRegion(kpiLane, Length.ZERO, lane.getLength(), start, end);
282                 this.regions.add(region);
283                 this.sampler.registerSpaceTimeRegion(region);
284             }
285             this.sampler.registerExtendedDataType(this.ttc);
286         }
287 
288         // return network
289         return this.network;
290     }
291 
292     /** {@inheritDoc} */
293     @Override
294     protected void addTabs(final OTSSimulatorInterface sim, final OTSSwingApplication animation)
295     {
296         if (!getBooleanProperty("output") || !getBooleanProperty("plots"))
297         {
298             return;
299         }
300         try
301         {
302             TablePanel charts = new TablePanel(2, 2);
303             GraphPath<KpiLaneDirection> path1 = GraphLaneUtil.createPath("Left road, left lane",
304                     new LaneDirection((Lane) ((CrossSectionLink) this.network.getLink("AD")).getCrossSectionElement("Lane 1"),
305                             GTUDirectionality.DIR_PLUS));
306             GraphPath<KpiLaneDirection> path2 = GraphLaneUtil.createPath("Left road, right lane",
307                     new LaneDirection((Lane) ((CrossSectionLink) this.network.getLink("AD")).getCrossSectionElement("Lane 2"),
308                             GTUDirectionality.DIR_PLUS));
309             GraphPath<KpiLaneDirection> path3 = GraphLaneUtil.createPath("Right road, left lane",
310                     new LaneDirection((Lane) ((CrossSectionLink) this.network.getLink("BC")).getCrossSectionElement("Lane 1"),
311                             GTUDirectionality.DIR_PLUS));
312             GraphPath<KpiLaneDirection> path4 = GraphLaneUtil.createPath("Right road, right lane",
313                     new LaneDirection((Lane) ((CrossSectionLink) this.network.getLink("BC")).getCrossSectionElement("Lane 2"),
314                             GTUDirectionality.DIR_PLUS));
315             charts.setCell(new ContourPlotSpeed("Left road, left lane", sim, new ContourDataSource<>(this.sampler, path1))
316                     .getContentPane(), 0, 0);
317             charts.setCell(new ContourPlotSpeed("Left road, right lane", sim, new ContourDataSource<>(this.sampler, path2))
318                     .getContentPane(), 1, 0);
319             charts.setCell(new ContourPlotSpeed("Right road, left lane", sim, new ContourDataSource<>(this.sampler, path3))
320                     .getContentPane(), 0, 1);
321             charts.setCell(new ContourPlotSpeed("Right road, right lane", sim, new ContourDataSource<>(this.sampler, path4))
322                     .getContentPane(), 1, 1);
323             this.animationPanel.getTabbedPane().addTab(this.animationPanel.getTabbedPane().getTabCount(), "statistics ",
324                     charts);
325         }
326         catch (NetworkException exception)
327         {
328             throw new RuntimeException(exception);
329         }
330     }
331 
332     /**
333      * Creates a frequency vector.
334      * @param array double[]; array in veh/h
335      * @return FrequencyVector; frequency vector
336      * @throws ValueException on problem
337      */
338     private FrequencyVector freq(final double[] array) throws ValueException
339     {
340         return new FrequencyVector(array, FrequencyUnit.PER_HOUR, StorageType.DENSE);
341     }
342 
343     /**
344      * Returns a task supplier for a distraction. These are specific to the SDM simulations.
345      * @param distraction DefaultDistraction; distraction
346      * @param stream StreamInterface; random number stream for randomized aspects of the distraction
347      * @return TaskSupplier; task supplier
348      */
349     private TaskSupplier getTaskSupplier(final DefaultDistraction distraction, final StreamInterface stream)
350     {
351         switch (distraction)
352         {
353             case TALKING_CELL_PHONE:
354             {
355                 return new TaskSupplier()
356                 {
357                     /** {@inheritDoc} */
358                     @Override
359                     public Task getTask(final LaneBasedGTU gtu)
360                     {
361                         return new Task.Exponential(distraction.getId(), getDoubleProperty("phoneInit"),
362                                 getDoubleProperty("phoneFinal"), getDoubleProperty("phoneTau"), gtu.getSimulator());
363                     }
364                 };
365             }
366             case CONVERSING:
367             {
368                 return new TaskSupplier.Constant(distraction.getId(), getDoubleProperty("conversing"));
369             }
370             case MANIPULATING_AUDIO_CONTROLS:
371             {
372                 return new TaskSupplier.Constant(distraction.getId(), getDoubleProperty("audio"));
373             }
374             case EXTERNAL_DISTRACTION:
375             {
376                 return new TaskSupplier.Constant(distraction.getId(),
377                         getDoubleProperty("externalBase") + getDoubleProperty("externalVar") * stream.nextDouble());
378             }
379             default:
380                 throw new IllegalArgumentException("Distraction " + distraction + " is not recognized.");
381         }
382     }
383 
384     /** {@inheritDoc} */
385     @Override
386     protected void onSimulationEnd()
387     {
388         if (getBooleanProperty("output"))
389         {
390             Length preDetectorPosition = Length.createSI(400.0); // on link DE, upstream of lane drop
391             Length postDetectorPosition = Length.createSI(100.0); // on link EF, downstream of lane drop
392             double tts = 0.0;
393             List<Float> ttcList = new ArrayList<>();
394             List<Float> decList = new ArrayList<>();
395             int[] counts = new int[60];
396             int[] speedCounts = new int[60];
397             double[] speedSum = new double[60];
398             for (SpaceTimeRegion region : this.regions)
399             {
400                 TrajectoryGroup<?> trajectoryGroup =
401                         this.sampler.getTrajectoryGroup(region.getLaneDirection()).getTrajectoryGroup(region.getStartPosition(),
402                                 region.getEndPosition(), region.getStartTime(), region.getEndTime());
403                 for (Trajectory<?> trajectory : trajectoryGroup)
404                 {
405                     try
406                     {
407                         tts += trajectory.getTotalDuration().si;
408                         for (FloatDuration ttcVal : trajectory.getExtendedData(this.ttc))
409                         {
410                             if (!Float.isNaN(ttcVal.si) && ttcVal.si < 20)
411                             {
412                                 ttcList.add(ttcVal.si);
413                             }
414                         }
415                         for (float decVal : trajectory.getA())
416                         {
417                             if (decVal < -2.0)
418                             {
419                                 decList.add(decVal);
420                             }
421                         }
422                         if (region.getLaneDirection().getLaneData().getLinkData().getId().equals("DE") && trajectory.size() > 1
423                                 && trajectory.getX(0) < preDetectorPosition.si
424                                 && trajectory.getX(trajectory.size() - 1) > preDetectorPosition.si)
425                         {
426                             double t = trajectory.getTimeAtPosition(postDetectorPosition).si - region.getStartTime().si;
427                             double v = trajectory.getSpeedAtPosition(postDetectorPosition).si;
428                             speedCounts[(int) (t / 60.0)]++;
429                             speedSum[(int) (t / 60.0)] += v;
430                         }
431                         if (region.getLaneDirection().getLaneData().getLinkData().getId().equals("EF") && trajectory.size() > 1
432                                 && trajectory.getX(0) < postDetectorPosition.si
433                                 && trajectory.getX(trajectory.size() - 1) > postDetectorPosition.si)
434                         {
435                             double t = trajectory.getTimeAtPosition(postDetectorPosition).si - region.getStartTime().si;
436                             counts[(int) (t / 60.0)]++;
437                         }
438                     }
439                     catch (SamplingException exception)
440                     {
441                         throw new RuntimeException(
442                                 "Unexpected exception: TimeToCollission not available or index out of bounds.", exception);
443                     }
444                 }
445             }
446             double qMax = 0;
447             for (int i = 0; i < counts.length - 4; i++)
448             {
449                 int q = 0;
450                 for (int j = i; j < i + 5; j++)
451                 {
452                     q += counts[j];
453                 }
454                 qMax = qMax > q ? qMax : q;
455             }
456             qMax *= 12; // twelve periods of 5min in an hour
457             int n = 0;
458             int countSum = 0;
459             for (int i = 0; i < counts.length; i++)
460             {
461                 double v = speedSum[i] / speedCounts[i];
462                 if (v < 80 / 3.6)
463                 {
464                     countSum += counts[i];
465                     n++;
466                 }
467             }
468             double qSat = n == 0 ? Double.NaN : 60.0 * countSum / n; // per min -> per hour
469             BufferedWriter bw;
470             try
471             {
472                 bw = new BufferedWriter(
473                         new OutputStreamWriter(Writer.createOutputStream(getProperty("outputFile"), CompressionType.ZIP)));
474                 bw.write(String.format("total time spent [s]: %.0f", tts));
475                 bw.newLine();
476                 bw.write(String.format("maximum flow [veh/h]: %.3f", qMax));
477                 bw.newLine();
478                 bw.write(String.format("saturation flow [veh/h]: %.3f", qSat));
479                 bw.newLine();
480                 bw.write(String.format("time to collision [s]: %s", ttcList));
481                 bw.newLine();
482                 bw.write(String.format("strong decelerations [m/s^2]: %s", decList));
483                 bw.close();
484             }
485             catch (IOException exception)
486             {
487                 throw new RuntimeException(exception);
488             }
489         }
490     }
491 
492 }