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