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
85
86
87
88
89
90
91
92
93
94 public class SdmSimulation extends AbstractSimulationScript
95 {
96
97 private OTSNetwork network;
98
99
100 private RoadSampler sampler;
101
102
103 private final TimeToCollision ttc = new TimeToCollision();
104
105
106 private final List<SpaceTimeRegion> regions = new ArrayList<>();
107
108
109
110
111
112 protected SdmSimulation(final String[] properties)
113 {
114 super("SDM simulation", "Simulations using the Stochastic Distraction Model", properties);
115
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
128
129
130 public static void main(final String... args)
131 {
132 new SdmSimulation(args).start();
133 }
134
135
136 @Override
137 protected void setDefaultProperties()
138 {
139
140 setProperty("outputFile", "output.txt");
141 setProperty("output", true);
142 setProperty("plots", false);
143
144
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
154 setProperty("allowMultiTasking", "false");
155
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
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
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
185 @Override
186 protected OTSNetwork setupSimulation(final OTSSimulatorInterface sim) throws Exception
187 {
188
189 sim.getReplication().setHistoryManager(
190 new HistoryManagerDEVS(sim, AdaptationSituationalAwareness.TR_MAX.getDefaultValue(), Duration.createSI(10.0)));
191
192
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
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
261 this.animateNetwork(this.network, false);
262
263
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
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
289 return this.network;
290 }
291
292
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
334
335
336
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
345
346
347
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
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
385 @Override
386 protected void onSimulationEnd()
387 {
388 if (getBooleanProperty("output"))
389 {
390 Length preDetectorPosition = Length.createSI(400.0);
391 Length postDetectorPosition = Length.createSI(100.0);
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;
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;
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 }