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