package org.opentrafficsim.demo.sdm; import java.awt.Color; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStreamWriter; import java.util.ArrayList; import java.util.List; import org.djunits.unit.FrequencyUnit; import org.djunits.unit.SpeedUnit; import org.djunits.unit.TimeUnit; import org.djunits.value.ValueRuntimeException; import org.djunits.value.storage.StorageType; import org.djunits.value.vdouble.scalar.Acceleration; import org.djunits.value.vdouble.scalar.Direction; import org.djunits.value.vdouble.scalar.Duration; import org.djunits.value.vdouble.scalar.Frequency; import org.djunits.value.vdouble.scalar.Length; import org.djunits.value.vdouble.scalar.Speed; import org.djunits.value.vdouble.scalar.Time; import org.djunits.value.vdouble.vector.FrequencyVector; import org.djunits.value.vdouble.vector.TimeVector; import org.djunits.value.vdouble.vector.base.DoubleVector; import org.djunits.value.vfloat.scalar.FloatDuration; import org.djutils.cli.CliException; import org.djutils.cli.CliUtil; import org.djutils.exceptions.Throw; import org.opentrafficsim.base.compressedfiles.CompressionType; import org.opentrafficsim.base.compressedfiles.Writer; import org.opentrafficsim.core.animation.gtu.colorer.AccelerationGTUColorer; import org.opentrafficsim.core.animation.gtu.colorer.SpeedGTUColorer; import org.opentrafficsim.core.animation.gtu.colorer.SwitchableGTUColorer; import org.opentrafficsim.core.compatibility.Compatible; import org.opentrafficsim.core.dsol.OTSSimulatorInterface; import org.opentrafficsim.core.geometry.OTSPoint3D; import org.opentrafficsim.core.gtu.GTUDirectionality; import org.opentrafficsim.core.gtu.GTUType; import org.opentrafficsim.core.network.LinkType; import org.opentrafficsim.core.network.NetworkException; import org.opentrafficsim.core.network.OTSNode; import org.opentrafficsim.core.perception.HistoryManagerDEVS; import org.opentrafficsim.draw.graphs.ContourDataSource; import org.opentrafficsim.draw.graphs.ContourPlotSpeed; import org.opentrafficsim.draw.graphs.GraphPath; import org.opentrafficsim.draw.graphs.road.GraphLaneUtil; import org.opentrafficsim.kpi.sampling.KpiGtuDirectionality; import org.opentrafficsim.kpi.sampling.KpiLaneDirection; import org.opentrafficsim.kpi.sampling.SamplingException; import org.opentrafficsim.kpi.sampling.SpaceTimeRegion; import org.opentrafficsim.kpi.sampling.Trajectory; import org.opentrafficsim.kpi.sampling.TrajectoryGroup; import org.opentrafficsim.road.gtu.colorer.DesiredHeadwayColorer; import org.opentrafficsim.road.gtu.colorer.DistractionColorer; import org.opentrafficsim.road.gtu.colorer.FixedColor; import org.opentrafficsim.road.gtu.colorer.SynchronizationColorer; import org.opentrafficsim.road.gtu.colorer.TaskSaturationColorer; import org.opentrafficsim.road.gtu.generator.od.DefaultGTUCharacteristicsGeneratorOD; import org.opentrafficsim.road.gtu.generator.od.ODApplier; import org.opentrafficsim.road.gtu.generator.od.ODOptions; import org.opentrafficsim.road.gtu.lane.LaneBasedGTU; import org.opentrafficsim.road.gtu.lane.perception.mental.AdaptationSituationalAwareness; import org.opentrafficsim.road.gtu.lane.perception.mental.ExponentialTask; import org.opentrafficsim.road.gtu.lane.perception.mental.Task; import org.opentrafficsim.road.gtu.lane.perception.mental.sdm.DefaultDistraction; import org.opentrafficsim.road.gtu.lane.perception.mental.sdm.DistractionFactory; import org.opentrafficsim.road.gtu.lane.perception.mental.sdm.StochasticDistractionModel; import org.opentrafficsim.road.gtu.lane.perception.mental.sdm.TaskSupplier; import org.opentrafficsim.road.gtu.strategical.od.Categorization; import org.opentrafficsim.road.gtu.strategical.od.Category; import org.opentrafficsim.road.gtu.strategical.od.Interpolation; import org.opentrafficsim.road.gtu.strategical.od.ODMatrix; import org.opentrafficsim.road.network.OTSRoadNetwork; import org.opentrafficsim.road.network.factory.LaneFactory; import org.opentrafficsim.road.network.lane.CrossSectionLink; import org.opentrafficsim.road.network.lane.Lane; import org.opentrafficsim.road.network.lane.LaneDirection; import org.opentrafficsim.road.network.lane.LaneType; import org.opentrafficsim.road.network.lane.OTSRoadNode; import org.opentrafficsim.road.network.lane.Stripe.Permeable; import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy; import org.opentrafficsim.road.network.lane.object.sensor.SinkSensor; import org.opentrafficsim.road.network.sampling.LaneData; import org.opentrafficsim.road.network.sampling.RoadSampler; import org.opentrafficsim.road.network.sampling.data.TimeToCollision; import org.opentrafficsim.swing.graphs.SwingContourPlot; import org.opentrafficsim.swing.graphs.SwingPlot; import org.opentrafficsim.swing.gui.OTSSimulationApplication; import org.opentrafficsim.swing.script.AbstractSimulationScript; import org.opentrafficsim.swing.script.IdmOptions; import nl.tudelft.simulation.dsol.swing.gui.TablePanel; import nl.tudelft.simulation.jstats.streams.StreamInterface; import picocli.CommandLine.Mixin; import picocli.CommandLine.Option; /** * Script to run a simulation with the Stochastic Distraction Model. *

* Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
* BSD-style license. See OpenTrafficSim License. *

* @version $Revision$, $LastChangedDate$, by $Author$, initial version 5 nov. 2018
* @author Alexander Verbraeck * @author Peter Knoppers * @author Wouter Schakel */ public class SdmSimulation extends AbstractSimulationScript { /** */ private static final long serialVersionUID = 20200516L; /** Network. */ private OTSRoadNetwork network; /** Sampler for statistics. */ private RoadSampler sampler; /** Time to collision data type. */ private final TimeToCollision ttc = new TimeToCollision(); /** Space time regions to sample traffic on. */ private final List regions = new ArrayList<>(); // Options /** IDM parameters. */ @Mixin private IdmOptions idmOptions; /** Output file. */ @Option(names = "--outputFile", description = "Output file", defaultValue = "output.txt") private String outputFile; /** Output. */ @Option(names = {"-o", "--output"}, description = "Create output", negatable = true, defaultValue = "true") private boolean output; /** Plots. */ @Option(names = {"-p", "--plots"}, description = "Create plots", negatable = true, defaultValue = "false") private boolean plots; /** Fraction of trucks. */ @Option(names = "--truckFraction", description = "Fraction of trucks", defaultValue = "0.05") private double truckFraction; /** Demand left. */ @Option(names = "--demandLeft", description = "Demand left", defaultValue = "3600/h") private Frequency demandLeft; /** Demand right. */ @Option(names = "--demandRight", description = "Demand right", defaultValue = "3600/h") private Frequency demandRight; /** Start demand factor. */ @Option(names = "--startDemandFctor", description = "Factor on demand at start", defaultValue = "0.45") private double startDemandFctor; /** Allow multitasking. */ @Option(names = "--multitasking", description = "Multitasking", negatable = true, defaultValue = "false") private boolean multitasking; /** Allow multitasking. */ @Option(names = "--distractions", split = ",", description = "Distraction, 1=TALKING_CELL_PHONE,12=CONVERSING,5=MANIPULATING_AUDIO_CONTROLS,16=EXTERNAL_DISTRACTION", defaultValue = "1,12,5,16") private String[] distractions; /** Initial task demand when talking on the phone. */ @Option(names = "--phoneInit", description = "Initial task demand when talking on the phone", defaultValue = "1.0") private double phoneInit; /** Final task demand when talking on the phone. */ @Option(names = "--phoneFinal", description = "Final task demand when talking on the phone", defaultValue = "0.3") private double phoneFinal; /** Exponential duration if task demand reduction when talking on the phone. */ @Option(names = "--phoneTau", description = "Exponential duration if task demand reduction when talking on the phone", defaultValue = "10s") private Duration phoneTau; /** Task demand when conversing. */ @Option(names = "--conversing", description = "Task demand when conversing", defaultValue = "0.3") private double conversing; /** Task demand when controlling audio. */ @Option(names = "--audio", description = "Task demand when controlling audio", defaultValue = "0.3") private double audio; /** Fixed (minimum) task demand for external distraction. */ @Option(names = "--externalBase", description = "Fixed (minimum) task demand for external distraction", defaultValue = "0.2") private double externalBase; /** Variable task demand for external distraction. */ @Option(names = "--externalVar", description = "Variable task demand for external distraction (random fraction added externalBase)", defaultValue = "0.3") private double externalVar; /** Time step. */ @Option(names = "--dt", description = "Time step", defaultValue = "0.5s") private Duration dt; /** Minimum situational awareness. */ @Option(names = "--saMin", description = "Minimum situational awareness", defaultValue = "0.5") private double saMin; /** Maximum situational awareness. */ @Option(names = "--saMax", description = "Maximum situational awareness", defaultValue = "1.0") private double saMax; /** Task capability. */ @Option(names = "--tc", description = "Task capability", defaultValue = "1.0") private double tc; /** Critical task saturation. */ @Option(names = "--tsCrit", description = "Critical task saturation", defaultValue = "0.8") private double tsCrit; /** Maximum task saturation. */ @Option(names = "--tsMax", description = "Maximum task saturation", defaultValue = "2.0") private double tsMax; /** Maximum reaction time. */ @Option(names = "--trMax", description = "Maximum reaction time", defaultValue = "2.0s") private Duration trMax; /** Maximum additional factor on headway for adaptation. */ @Option(names = "--betaT", description = "Maximum additional factor on headway for adaptation", defaultValue = "1.0") private double betaT; /** * Constructor. */ protected SdmSimulation() { super("SDM simulation", "Simulations using the Stochastic Distraction Model"); // set GTU colorers to use setGtuColorer(SwitchableGTUColorer.builder().addActiveColorer(new FixedColor(Color.BLUE, "Blue")).addColorer( new SynchronizationColorer()).addColorer(new DistractionColorer(DefaultDistraction.ANSWERING_CELL_PHONE, DefaultDistraction.CONVERSING, DefaultDistraction.MANIPULATING_AUDIO_CONTROLS, DefaultDistraction.EXTERNAL_DISTRACTION)).addColorer(new SpeedGTUColorer(new Speed(150, SpeedUnit.KM_PER_HOUR))) .addColorer(new AccelerationGTUColorer(Acceleration.instantiateSI(-6.0), Acceleration.instantiateSI(2))).addColorer( new DesiredHeadwayColorer(Duration.instantiateSI(0.56), Duration.instantiateSI(2.4))).addColorer( new TaskSaturationColorer()).build()); try { CliUtil.changeOptionDefault(this, "warmupTime", "300s"); CliUtil.changeOptionDefault(this, "simulationTime", "3900s"); CliUtil.changeOptionDefault(IdmOptions.class, "aTruck", "0.8m/s^2"); } catch (NoSuchFieldException | IllegalStateException | IllegalArgumentException | CliException exception) { exception.printStackTrace(); } } /** * Start a simulation. * @param args String...; command line arguments */ public static void main(final String... args) { try { SdmSimulation sim = new SdmSimulation(); CliUtil.execute(sim, args); sim.start(); } catch (Exception ex) { ex.printStackTrace(); } } /** {@inheritDoc} */ @Override public void check() throws Exception { super.check(); Throw.when(this.truckFraction < 0.0 || this.truckFraction > 1.0, IllegalArgumentException.class, "Truck fraction %f is below 0.0 or above 1.0."); } /** {@inheritDoc} */ @Override protected OTSRoadNetwork setupSimulation(final OTSSimulatorInterface sim) throws Exception { // manager of historic information to allow a reaction time sim.getReplication().setHistoryManager(new HistoryManagerDEVS(sim, AdaptationSituationalAwareness.TR_MAX .getDefaultValue(), Duration.instantiateSI(10.0))); // Network this.network = new OTSRoadNetwork("SDM", true, getSimulator()); OTSPoint3D pointA = new OTSPoint3D(0.0, 0.0); OTSPoint3D pointB = new OTSPoint3D(0.0, -20.0); OTSPoint3D pointC = new OTSPoint3D(1600.0, -20.0); OTSPoint3D pointD = new OTSPoint3D(2000.0, 0.0); OTSPoint3D pointE = new OTSPoint3D(2500.0, 0.0); OTSPoint3D pointF = new OTSPoint3D(3500.0, 0.0); OTSRoadNode nodeA = new OTSRoadNode(this.network, "A", pointA, Direction.ZERO); OTSRoadNode nodeB = new OTSRoadNode(this.network, "B", pointB, Direction.ZERO); OTSRoadNode nodeC = new OTSRoadNode(this.network, "C", pointC, Direction.ZERO); OTSRoadNode nodeD = new OTSRoadNode(this.network, "D", pointD, Direction.ZERO); OTSRoadNode nodeE = new OTSRoadNode(this.network, "E", pointE, Direction.ZERO); OTSRoadNode nodeF = new OTSRoadNode(this.network, "F", pointF, Direction.ZERO); LinkType type = this.network.getLinkType(LinkType.DEFAULTS.FREEWAY); LaneKeepingPolicy policy = LaneKeepingPolicy.KEEPRIGHT; Length laneWidth = Length.instantiateSI(3.5); LaneType laneType = this.network.getLaneType(LaneType.DEFAULTS.FREEWAY); Speed speedLimit = new Speed(120.0, SpeedUnit.KM_PER_HOUR); List allLanes = new ArrayList<>(); allLanes.addAll(new LaneFactory(this.network, nodeA, nodeD, type, sim, policy).leftToRight(2.0, laneWidth, laneType, speedLimit).addLanes(Permeable.BOTH).getLanes()); allLanes.addAll(new LaneFactory(this.network, nodeB, nodeC, type, sim, policy).leftToRight(0.0, laneWidth, laneType, speedLimit).addLanes(Permeable.BOTH).getLanes()); allLanes.addAll(new LaneFactory(this.network, nodeC, nodeD, type, sim, policy).leftToRight(0.0, laneWidth, laneType, speedLimit).addLanes(Permeable.BOTH).getLanes()); allLanes.addAll(new LaneFactory(this.network, nodeD, nodeE, type, sim, policy).leftToRight(2.0, laneWidth, laneType, speedLimit).addLanes(Permeable.BOTH, Permeable.BOTH, Permeable.BOTH).getLanes()); List lanesEF = new LaneFactory(this.network, nodeE, nodeF, type, sim, policy).leftToRight(1.0, laneWidth, laneType, speedLimit).addLanes(Permeable.BOTH, Permeable.BOTH).getLanes(); allLanes.addAll(lanesEF); for (Lane lane : lanesEF) { new SinkSensor(lane, lane.getLength().minus(Length.instantiateSI(50)), Compatible.EVERYTHING, sim); } // OD List origins = new ArrayList<>(); origins.add(nodeA); origins.add(nodeB); List destinations = new ArrayList<>(); destinations.add(nodeF); double wut = sim.getReplication().getTreatment().getWarmupPeriod().si; double rl = sim.getReplication().getTreatment().getRunLength().si; TimeVector timeVector = DoubleVector.instantiate(new double[] {0.0, wut, wut + (rl - wut) * 0.5, rl}, TimeUnit.DEFAULT, StorageType.DENSE); Interpolation interpolation = Interpolation.LINEAR; Categorization categorization = new Categorization("GTU categorization", GTUType.class); ODMatrix odMatrix = new ODMatrix("OD", origins, destinations, categorization, timeVector, interpolation); Category carCategory = new Category(categorization, this.network.getGtuType(GTUType.DEFAULTS.CAR)); Category truCategory = new Category(categorization, this.network.getGtuType(GTUType.DEFAULTS.TRUCK)); double f1 = this.truckFraction; double f2 = 1.0 - f1; double left2 = this.demandLeft.getInUnit(FrequencyUnit.PER_HOUR); double right2 = this.demandRight.getInUnit(FrequencyUnit.PER_HOUR); double startDemandFactor = this.startDemandFctor; double left1 = left2 * startDemandFactor; double right1 = right2 * startDemandFactor; odMatrix.putDemandVector(nodeA, nodeF, carCategory, freq(new double[] {f2 * left1, f2 * left1, f2 * left2, 0.0})); odMatrix.putDemandVector(nodeA, nodeF, truCategory, freq(new double[] {f1 * left1, f1 * left1, f1 * left2, 0.0})); odMatrix.putDemandVector(nodeB, nodeF, carCategory, freq(new double[] {f2 * right1, f2 * right1, f2 * right2, 0.0})); odMatrix.putDemandVector(nodeB, nodeF, truCategory, freq(new double[] {f1 * right1, f1 * right1, f1 * right2, 0.0})); ODOptions odOptions = new ODOptions().set(ODOptions.NO_LC_DIST, Length.instantiateSI(200)).set(ODOptions.GTU_TYPE, new DefaultGTUCharacteristicsGeneratorOD(new SdmStrategicalPlannerFactory(this.network, sim.getReplication() .getStream("generation"), this))); ODApplier.applyOD(this.network, odMatrix, odOptions); // setup the SDM DistractionFactory distFactory = new DistractionFactory(sim.getReplication().getStream("default")); for (String distraction : this.distractions) { DefaultDistraction dist = DefaultDistraction.values()[Integer.parseInt(distraction) - 1]; distFactory.addDistraction(dist, getTaskSupplier(dist, sim.getReplication().getStream("default"))); } new StochasticDistractionModel(this.multitasking, distFactory.build(), sim, this.network); // sampler if (this.output) { this.sampler = RoadSampler.build(this.network).registerExtendedDataType(this.ttc).create(); Time start = new Time(0.05, TimeUnit.BASE_HOUR); Time end = new Time(1.05, TimeUnit.BASE_HOUR); for (Lane lane : allLanes) { KpiLaneDirection kpiLane = new KpiLaneDirection(new LaneData(lane), KpiGtuDirectionality.DIR_PLUS); SpaceTimeRegion region = new SpaceTimeRegion(kpiLane, Length.ZERO, lane.getLength(), start, end); this.regions.add(region); this.sampler.registerSpaceTimeRegion(region); } } // return network return this.network; } /** {@inheritDoc} */ @Override protected void addTabs(final OTSSimulatorInterface sim, final OTSSimulationApplication animation) { if (!this.output || !this.plots) { return; } try { TablePanel charts = new TablePanel(2, 2); GraphPath path1 = GraphLaneUtil.createPath("Left road, left lane", new LaneDirection( (Lane) ((CrossSectionLink) this.network.getLink("AD")).getCrossSectionElement("Lane 1"), GTUDirectionality.DIR_PLUS)); GraphPath path2 = GraphLaneUtil.createPath("Left road, right lane", new LaneDirection( (Lane) ((CrossSectionLink) this.network.getLink("AD")).getCrossSectionElement("Lane 2"), GTUDirectionality.DIR_PLUS)); GraphPath path3 = GraphLaneUtil.createPath("Right road, left lane", new LaneDirection( (Lane) ((CrossSectionLink) this.network.getLink("BC")).getCrossSectionElement("Lane 1"), GTUDirectionality.DIR_PLUS)); GraphPath path4 = GraphLaneUtil.createPath("Right road, right lane", new LaneDirection( (Lane) ((CrossSectionLink) this.network.getLink("BC")).getCrossSectionElement("Lane 2"), GTUDirectionality.DIR_PLUS)); GraphPath.initRecording(this.sampler, path1); GraphPath.initRecording(this.sampler, path2); GraphPath.initRecording(this.sampler, path3); GraphPath.initRecording(this.sampler, path4); SwingPlot plot = null; plot = new SwingContourPlot(new ContourPlotSpeed("Left road, left lane", sim, new ContourDataSource<>(this.sampler .getSamplerData(), path1))); charts.setCell(plot.getContentPane(), 0, 0); plot = new SwingContourPlot(new ContourPlotSpeed("Left road, right lane", sim, new ContourDataSource<>(this.sampler .getSamplerData(), path2))); charts.setCell(plot.getContentPane(), 1, 0); plot = new SwingContourPlot(new ContourPlotSpeed("Right road, left lane", sim, new ContourDataSource<>(this.sampler .getSamplerData(), path3))); charts.setCell(plot.getContentPane(), 0, 1); plot = new SwingContourPlot(new ContourPlotSpeed("Right road, right lane", sim, new ContourDataSource<>(this.sampler .getSamplerData(), path4))); charts.setCell(plot.getContentPane(), 1, 1); animation.getAnimationPanel().getTabbedPane().addTab(animation.getAnimationPanel().getTabbedPane().getTabCount(), "statistics ", charts); } catch (NetworkException exception) { throw new RuntimeException(exception); } } /** * Creates a frequency vector. * @param array double[]; array in veh/h * @return FrequencyVector; frequency vector * @throws ValueRuntimeException on problem */ private FrequencyVector freq(final double[] array) throws ValueRuntimeException { return DoubleVector.instantiate(array, FrequencyUnit.PER_HOUR, StorageType.DENSE); } /** * Returns a task supplier for a distraction. These are specific to the SDM simulations. * @param distraction DefaultDistraction; distraction * @param stream StreamInterface; random number stream for randomized aspects of the distraction * @return TaskSupplier; task supplier */ private TaskSupplier getTaskSupplier(final DefaultDistraction distraction, final StreamInterface stream) { switch (distraction) { case TALKING_CELL_PHONE: { return new TaskSupplier() { /** {@inheritDoc} */ @SuppressWarnings("synthetic-access") @Override public Task getTask(final LaneBasedGTU gtu) { return new ExponentialTask(distraction.getId(), SdmSimulation.this.phoneInit, SdmSimulation.this.phoneFinal, SdmSimulation.this.phoneTau, gtu.getSimulator()); } }; } case CONVERSING: { return new TaskSupplier.Constant(distraction.getId(), SdmSimulation.this.conversing); } case MANIPULATING_AUDIO_CONTROLS: { return new TaskSupplier.Constant(distraction.getId(), SdmSimulation.this.audio); } case EXTERNAL_DISTRACTION: { return new TaskSupplier.Constant(distraction.getId(), SdmSimulation.this.externalBase + SdmSimulation.this.externalVar * stream.nextDouble()); } default: throw new IllegalArgumentException("Distraction " + distraction + " is not recognized."); } } /** {@inheritDoc} */ @Override protected void onSimulationEnd() { if (this.output) { Length preDetectorPosition = Length.instantiateSI(400.0); // on link DE, upstream of lane drop Length postDetectorPosition = Length.instantiateSI(100.0); // on link EF, downstream of lane drop double tts = 0.0; List ttcList = new ArrayList<>(); List decList = new ArrayList<>(); int[] counts = new int[60]; int[] speedCounts = new int[60]; double[] speedSum = new double[60]; for (SpaceTimeRegion region : this.regions) { TrajectoryGroup trajectoryGroup = this.sampler.getSamplerData().getTrajectoryGroup(region.getLaneDirection()) .getTrajectoryGroup(region.getStartPosition(), region.getEndPosition(), region.getStartTime(), region .getEndTime()); for (Trajectory trajectory : trajectoryGroup) { try { tts += trajectory.getTotalDuration().si; for (FloatDuration ttcVal : trajectory.getExtendedData(this.ttc)) { if (!Float.isNaN(ttcVal.si) && ttcVal.si < 20) { ttcList.add(ttcVal.si); } } for (float decVal : trajectory.getA()) { if (decVal < -2.0) { decList.add(decVal); } } if (region.getLaneDirection().getLaneData().getLinkData().getId().equals("DE") && trajectory.size() > 1 && trajectory.getX(0) < preDetectorPosition.si && trajectory.getX(trajectory.size() - 1) > preDetectorPosition.si) { double t = trajectory.getTimeAtPosition(postDetectorPosition).si - region.getStartTime().si; double v = trajectory.getSpeedAtPosition(postDetectorPosition).si; speedCounts[(int) (t / 60.0)]++; speedSum[(int) (t / 60.0)] += v; } if (region.getLaneDirection().getLaneData().getLinkData().getId().equals("EF") && trajectory.size() > 1 && trajectory.getX(0) < postDetectorPosition.si && trajectory.getX(trajectory.size() - 1) > postDetectorPosition.si) { double t = trajectory.getTimeAtPosition(postDetectorPosition).si - region.getStartTime().si; counts[(int) (t / 60.0)]++; } } catch (SamplingException exception) { throw new RuntimeException( "Unexpected exception: TimeToCollission not available or index out of bounds.", exception); } } } double qMax = 0; for (int i = 0; i < counts.length - 4; i++) { int q = 0; for (int j = i; j < i + 5; j++) { q += counts[j]; } qMax = qMax > q ? qMax : q; } qMax *= 12; // twelve periods of 5min in an hour int n = 0; int countSum = 0; for (int i = 0; i < counts.length; i++) { double v = speedSum[i] / speedCounts[i]; if (v < 80 / 3.6) { countSum += counts[i]; n++; } } double qSat = n == 0 ? Double.NaN : 60.0 * countSum / n; // per min -> per hour BufferedWriter bw; try { bw = new BufferedWriter(new OutputStreamWriter(Writer.createOutputStream(this.outputFile, CompressionType.ZIP))); bw.write(String.format("total time spent [s]: %.0f", tts)); bw.newLine(); bw.write(String.format("maximum flow [veh/h]: %.3f", qMax)); bw.newLine(); bw.write(String.format("saturation flow [veh/h]: %.3f", qSat)); bw.newLine(); bw.write(String.format("time to collision [s]: %s", ttcList)); bw.newLine(); bw.write(String.format("strong decelerations [m/s^2]: %s", decList)); bw.close(); } catch (IOException exception) { throw new RuntimeException(exception); } } } /** * @return idmOptions. */ public IdmOptions getIdmOptions() { return this.idmOptions; } /** * @return dt. */ public Duration getDt() { return this.dt; } /** * @return saMin. */ public double getSaMin() { return this.saMin; } /** * @return saMax. */ public double getSaMax() { return this.saMax; } /** * @return tc. */ public double getTc() { return this.tc; } /** * @return tsCrit. */ public double getTsCrit() { return this.tsCrit; } /** * @return tsMax. */ public double getTsMax() { return this.tsMax; } /** * @return trMax. */ public Duration getTrMax() { return this.trMax; } /** * @return betaT. */ public double getBetaT() { return this.betaT; } }