1 package org.opentrafficsim.demo.carFollowing;
2
3 import java.awt.Frame;
4 import java.awt.geom.Rectangle2D;
5 import java.io.IOException;
6 import java.net.URL;
7 import java.rmi.RemoteException;
8 import java.util.ArrayList;
9 import java.util.Collection;
10 import java.util.HashMap;
11 import java.util.Map;
12 import java.util.Random;
13
14 import javax.naming.NamingException;
15 import javax.swing.JScrollPane;
16 import javax.swing.SwingUtilities;
17
18 import nl.tudelft.simulation.dsol.SimRuntimeException;
19 import nl.tudelft.simulation.dsol.gui.swing.DSOLPanel;
20 import nl.tudelft.simulation.dsol.gui.swing.HTMLPanel;
21 import nl.tudelft.simulation.dsol.gui.swing.TablePanel;
22 import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
23
24 import org.opentrafficsim.car.Car;
25 import org.opentrafficsim.core.dsol.OTSDEVSSimulatorInterface;
26 import org.opentrafficsim.core.dsol.OTSModelInterface;
27 import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
28 import org.opentrafficsim.core.gtu.GTUType;
29 import org.opentrafficsim.core.gtu.following.GTUFollowingModel;
30 import org.opentrafficsim.core.gtu.following.GTUFollowingModel.GTUFollowingModelResult;
31 import org.opentrafficsim.core.gtu.following.IDM;
32 import org.opentrafficsim.core.gtu.following.IDMPlus;
33 import org.opentrafficsim.core.network.NetworkException;
34 import org.opentrafficsim.core.network.factory.LaneFactory;
35 import org.opentrafficsim.core.network.factory.Node;
36 import org.opentrafficsim.core.network.lane.Lane;
37 import org.opentrafficsim.core.network.lane.LaneType;
38 import org.opentrafficsim.core.unit.AccelerationUnit;
39 import org.opentrafficsim.core.unit.LengthUnit;
40 import org.opentrafficsim.core.unit.SpeedUnit;
41 import org.opentrafficsim.core.unit.TimeUnit;
42 import org.opentrafficsim.core.value.vdouble.scalar.DoubleScalar;
43 import org.opentrafficsim.graphs.FundamentalDiagram;
44 import org.opentrafficsim.simulationengine.AbstractProperty;
45 import org.opentrafficsim.simulationengine.ControlPanel;
46 import org.opentrafficsim.simulationengine.PropertyException;
47 import org.opentrafficsim.simulationengine.ProbabilityDistributionProperty;
48 import org.opentrafficsim.simulationengine.SelectionProperty;
49 import org.opentrafficsim.simulationengine.SimpleSimulator;
50 import org.opentrafficsim.simulationengine.SimulatorFrame;
51 import org.opentrafficsim.simulationengine.WrappableSimulation;
52
53 import com.vividsolutions.jts.geom.Coordinate;
54
55
56
57
58
59
60
61
62
63
64 public class FundamentalDiagrams implements WrappableSimulation
65 {
66
67 private ArrayList<AbstractProperty<?>> properties = new ArrayList<AbstractProperty<?>>();
68
69
70 public FundamentalDiagrams()
71 {
72 try
73 {
74 this.properties
75 .add(new SelectionProperty(
76 "Car following model",
77 "<html>The car following model determines "
78 + "the acceleration that a vehicle will make taking into account nearby vehicles, infrastructural "
79 + "restrictions (e.g. speed limit, curvature of the road) capabilities of the vehicle and "
80 + "personality of the driver.</html>", new String[]{"IDM", "IDM+"}, 1, false, 500));
81 this.properties.add(new ProbabilityDistributionProperty("Traffic composition",
82 "<html>Mix of passenger cars and trucks</html>", new String[]{"passenger car", "truck"},
83 new Double[]{0.8, 0.2}, false, 10));
84 }
85 catch (PropertyException exception)
86 {
87 exception.printStackTrace();
88 }
89 }
90
91
92
93
94
95
96
97 public static void main(final String[] args) throws RemoteException, SimRuntimeException
98 {
99
100 SwingUtilities.invokeLater(new Runnable()
101 {
102 @Override
103 public void run()
104 {
105 try
106 {
107 FundamentalDiagrams fundamentalDiagrams = new FundamentalDiagrams();
108 new SimulatorFrame("Fundamental Diagrams animation", fundamentalDiagrams.buildSimulator(
109 fundamentalDiagrams.getProperties()).getPanel());
110 }
111 catch (RemoteException | SimRuntimeException exception)
112 {
113 exception.printStackTrace();
114 }
115 }
116 });
117 }
118
119
120
121
122
123
124 public SimpleSimulator buildSimulator(ArrayList<AbstractProperty<?>> userModifiedProperties)
125 throws RemoteException, SimRuntimeException
126 {
127 FundamentalDiagramPlotsModel model = new FundamentalDiagramPlotsModel(userModifiedProperties);
128 SimpleSimulator result =
129 new SimpleSimulator(new OTSSimTimeDouble(new DoubleScalar.Abs<TimeUnit>(0.0, TimeUnit.SECOND)),
130 new DoubleScalar.Rel<TimeUnit>(0.0, TimeUnit.SECOND),
131 new DoubleScalar.Rel<TimeUnit>(1800.0, TimeUnit.SECOND), model, new Rectangle2D.Double(0, -100, 5000, 200));
132 new ControlPanel(result);
133 makePlots(model, result.getPanel());
134 addInfoTab(result.getPanel());
135 return result;
136 }
137
138
139
140
141
142
143 private static void makePlots(final FundamentalDiagramPlotsModel model,
144 final DSOLPanel<DoubleScalar.Abs<TimeUnit>, DoubleScalar.Rel<TimeUnit>, OTSSimTimeDouble> panel)
145 {
146 final int panelsPerRow = 3;
147 TablePanel charts = new TablePanel(4, panelsPerRow);
148 panel.getTabbedPane().addTab("statistics", charts);
149 for (int plotNumber = 0; plotNumber < 10; plotNumber++)
150 {
151 DoubleScalar.Abs<LengthUnit> detectorLocation =
152 new DoubleScalar.Abs<LengthUnit>(400 + 500 * plotNumber, LengthUnit.METER);
153 FundamentalDiagram fd =
154 new FundamentalDiagram("Fundamental Diagram at " + detectorLocation.getSI() + "m", 1,
155 new DoubleScalar.Rel<TimeUnit>(1, TimeUnit.MINUTE), detectorLocation);
156 fd.setTitle("Density Contour Graph");
157 fd.setExtendedState(Frame.MAXIMIZED_BOTH);
158 model.getFundamentalDiagrams().add(fd);
159 charts.setCell(fd.getContentPane(), plotNumber / panelsPerRow, plotNumber % panelsPerRow);
160 }
161 }
162
163
164
165
166 private static void addInfoTab(
167 final DSOLPanel<DoubleScalar.Abs<TimeUnit>, DoubleScalar.Rel<TimeUnit>, OTSSimTimeDouble> panel)
168 {
169
170 String helpSource = "/" + FundamentalDiagramPlotsModel.class.getPackage().getName().replace('.', '/') + "/package.html";
171 URL page = FundamentalDiagramPlotsModel.class.getResource(helpSource);
172 if (page != null)
173 {
174 HTMLPanel htmlPanel;
175 try
176 {
177 htmlPanel = new HTMLPanel(page);
178 panel.getTabbedPane().addTab("info", new JScrollPane(htmlPanel));
179 }
180 catch (IOException exception)
181 {
182 exception.printStackTrace();
183 }
184 }
185 }
186
187
188 @Override
189 public String shortName()
190 {
191 return "Fundamental Diagrams";
192 }
193
194
195 @Override
196 public String description()
197 {
198 return "<html><h1>Fundamental Diagram Plots</H1>"
199 + "Simulation of a single lane road of 5 km length. Vechicles are generated at a constant rate of "
200 + "1500 veh/hour. At time 300s a blockade is inserted at position 4km; this blockade is removed at time "
201 + "500s. This blockade simulates a bridge opening.<br/>"
202 + "The blockade causes a traffic jam that slowly dissolves after the blockade is removed.<br />"
203 + "Output is a set of Diagrams that plot observed density, flow and speed plots against each other.</html>";
204 }
205
206
207 @Override
208 public ArrayList<AbstractProperty<?>> getProperties()
209 {
210 return this.properties;
211 }
212 }
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227 class FundamentalDiagramPlotsModel implements OTSModelInterface
228 {
229
230 private static final long serialVersionUID = 20140820L;
231
232
233 private OTSDEVSSimulatorInterface simulator;
234
235
236 private DoubleScalar.Rel<TimeUnit> headway;
237
238
239 private int carsCreated = 0;
240
241
242 protected GTUFollowingModel carFollowingModelCars;
243
244
245 protected GTUFollowingModel carFollowingModelTrucks;
246
247
248 double carProbability;
249
250
251 ArrayList<Car<Integer>> cars = new ArrayList<Car<Integer>>();
252
253
254 Car<Integer> block = null;
255
256
257 private DoubleScalar.Rel<LengthUnit> minimumDistance = new DoubleScalar.Rel<LengthUnit>(0, LengthUnit.METER);
258
259
260 private DoubleScalar.Rel<LengthUnit> maximumDistance = new DoubleScalar.Rel<LengthUnit>(5000, LengthUnit.METER);
261
262
263 Lane lane;
264
265
266 DoubleScalar.Abs<SpeedUnit> speedLimit = new DoubleScalar.Abs<SpeedUnit>(100, SpeedUnit.KM_PER_HOUR);
267
268
269 private ArrayList<FundamentalDiagram> fundamentalDiagrams = new ArrayList<FundamentalDiagram>();
270
271
272 ArrayList<AbstractProperty<?>> properties = null;
273
274
275 Random randomGenerator = new Random(12345);
276
277
278
279
280 public FundamentalDiagramPlotsModel(ArrayList<AbstractProperty<?>> properties)
281 {
282 this.properties = properties;
283 }
284
285
286 @Override
287 public final void constructModel(
288 final SimulatorInterface<DoubleScalar.Abs<TimeUnit>, DoubleScalar.Rel<TimeUnit>, OTSSimTimeDouble> theSimulator)
289 throws SimRuntimeException, RemoteException
290 {
291 this.simulator = (OTSDEVSSimulatorInterface) theSimulator;
292 Node from = new Node("From", new Coordinate(getMinimumDistance().getSI(), 0, 0));
293 Node to = new Node("To", new Coordinate(getMaximumDistance().getSI(), 0, 0));
294 LaneType<String> laneType = new LaneType<String>("CarLane");
295 try
296 {
297 this.lane = LaneFactory.makeLane("Lane", from, to, null, laneType, this.simulator);
298 }
299 catch (NamingException | NetworkException exception)
300 {
301 exception.printStackTrace();
302 }
303
304 for (AbstractProperty<?> p : this.properties)
305 {
306 if (p instanceof SelectionProperty)
307 {
308 SelectionProperty sp = (SelectionProperty) p;
309 if ("Car following model".equals(sp.getShortName()))
310 {
311 String modelName = sp.getValue();
312 if (modelName.equals("IDM"))
313 {
314 this.carFollowingModelCars =
315 new IDM(new DoubleScalar.Abs<AccelerationUnit>(1, AccelerationUnit.METER_PER_SECOND_2),
316 new DoubleScalar.Abs<AccelerationUnit>(1.5, AccelerationUnit.METER_PER_SECOND_2),
317 new DoubleScalar.Rel<LengthUnit>(2, LengthUnit.METER), new DoubleScalar.Rel<TimeUnit>(1,
318 TimeUnit.SECOND), 1d);
319 this.carFollowingModelTrucks =
320 new IDM(new DoubleScalar.Abs<AccelerationUnit>(0.5, AccelerationUnit.METER_PER_SECOND_2),
321 new DoubleScalar.Abs<AccelerationUnit>(1.5, AccelerationUnit.METER_PER_SECOND_2),
322 new DoubleScalar.Rel<LengthUnit>(2, LengthUnit.METER), new DoubleScalar.Rel<TimeUnit>(1,
323 TimeUnit.SECOND), 1d);
324 }
325 else if (modelName.equals("IDM+"))
326 {
327 this.carFollowingModelCars =
328 new IDMPlus(new DoubleScalar.Abs<AccelerationUnit>(1, AccelerationUnit.METER_PER_SECOND_2),
329 new DoubleScalar.Abs<AccelerationUnit>(1.5, AccelerationUnit.METER_PER_SECOND_2),
330 new DoubleScalar.Rel<LengthUnit>(2, LengthUnit.METER), new DoubleScalar.Rel<TimeUnit>(1,
331 TimeUnit.SECOND), 1d);
332 this.carFollowingModelTrucks =
333 new IDMPlus(new DoubleScalar.Abs<AccelerationUnit>(0.5, AccelerationUnit.METER_PER_SECOND_2),
334 new DoubleScalar.Abs<AccelerationUnit>(1.5, AccelerationUnit.METER_PER_SECOND_2),
335 new DoubleScalar.Rel<LengthUnit>(2, LengthUnit.METER), new DoubleScalar.Rel<TimeUnit>(1,
336 TimeUnit.SECOND), 1d);
337 }
338 else
339 {
340 throw new Error("Car following model " + modelName + " not implemented");
341 }
342 }
343 else
344 {
345 throw new Error("Unhandled SelectionProperty " + p.getShortName());
346 }
347 }
348 else if (p instanceof ProbabilityDistributionProperty)
349 {
350 ProbabilityDistributionProperty pdp = (ProbabilityDistributionProperty) p;
351 String modelName = p.getShortName();
352 if (modelName.equals("Traffic composition"))
353 {
354 this.carProbability = pdp.getValue()[0];
355 }
356 else
357 {
358 throw new Error("Unhandled ProbabilityDistributionProperty " + p.getShortName());
359 }
360 }
361 else
362 {
363 throw new Error("Unhandled property: " + p);
364 }
365 }
366
367 this.headway = new DoubleScalar.Rel<TimeUnit>(3600.0 / 1500.0, TimeUnit.SECOND);
368
369 try
370 {
371
372 this.simulator.scheduleEventAbs(new DoubleScalar.Abs<TimeUnit>(0.0, TimeUnit.SECOND), this, this, "generateCar",
373 null);
374
375 this.simulator.scheduleEventAbs(new DoubleScalar.Abs<TimeUnit>(300, TimeUnit.SECOND), this, this, "createBlock",
376 null);
377
378 this.simulator.scheduleEventAbs(new DoubleScalar.Abs<TimeUnit>(420, TimeUnit.SECOND), this, this, "removeBlock",
379 null);
380
381 for (int t = 1; t <= 1800; t++)
382 {
383 this.simulator.scheduleEventAbs(new DoubleScalar.Abs<TimeUnit>(t - 0.001, TimeUnit.SECOND), this, this,
384 "drawGraphs", null);
385 }
386 }
387 catch (RemoteException | SimRuntimeException exception)
388 {
389 exception.printStackTrace();
390 }
391 }
392
393
394
395
396
397
398
399 protected final void createBlock() throws RemoteException, NetworkException, SimRuntimeException
400 {
401 DoubleScalar.Rel<LengthUnit> initialPosition = new DoubleScalar.Rel<LengthUnit>(4000, LengthUnit.METER);
402 Map<Lane, DoubleScalar.Rel<LengthUnit>> initialPositions = new HashMap<Lane, DoubleScalar.Rel<LengthUnit>>();
403 initialPositions.put(this.lane, initialPosition);
404 try
405 {
406 this.block =
407 new IDMCar(999999, null, this.simulator, this.carFollowingModelCars, new DoubleScalar.Rel<LengthUnit>(4,
408 LengthUnit.METER), this.simulator.getSimulatorTime().get(), initialPositions,
409 new DoubleScalar.Abs<SpeedUnit>(0, SpeedUnit.KM_PER_HOUR));
410 }
411 catch (NamingException exception)
412 {
413 exception.printStackTrace();
414 }
415 }
416
417
418
419
420 protected final void removeBlock()
421 {
422 this.block = null;
423 }
424
425
426
427
428
429 protected final void generateCar()
430 {
431 boolean generateTruck = this.randomGenerator.nextDouble() > this.carProbability;
432 DoubleScalar.Rel<LengthUnit> initialPosition = new DoubleScalar.Rel<LengthUnit>(0, LengthUnit.METER);
433 DoubleScalar.Abs<SpeedUnit> initialSpeed = new DoubleScalar.Abs<SpeedUnit>(100, SpeedUnit.KM_PER_HOUR);
434 Map<Lane, DoubleScalar.Rel<LengthUnit>> initialPositions = new HashMap<Lane, DoubleScalar.Rel<LengthUnit>>();
435 initialPositions.put(this.lane, initialPosition);
436 try
437 {
438 DoubleScalar.Rel<LengthUnit> vehicleLength =
439 new DoubleScalar.Rel<LengthUnit>(generateTruck ? 15 : 4, LengthUnit.METER);
440 IDMCar car =
441 new IDMCar(++this.carsCreated, null, this.simulator, generateTruck ? this.carFollowingModelTrucks
442 : this.carFollowingModelCars, vehicleLength, this.simulator.getSimulatorTime().get(), initialPositions,
443 initialSpeed);
444 this.cars.add(0, car);
445 this.simulator.scheduleEventRel(this.headway, this, this, "generateCar", null);
446 }
447 catch (RemoteException | SimRuntimeException | NamingException | NetworkException exception)
448 {
449 exception.printStackTrace();
450 }
451 }
452
453
454
455
456 protected final void drawGraphs()
457 {
458
459 for (FundamentalDiagram fd : this.fundamentalDiagrams)
460 {
461 fd.reGraph();
462 }
463 }
464
465
466 @Override
467 public final SimulatorInterface<DoubleScalar.Abs<TimeUnit>, DoubleScalar.Rel<TimeUnit>, OTSSimTimeDouble> getSimulator()
468 throws RemoteException
469 {
470 return null;
471 }
472
473
474
475
476 public final ArrayList<FundamentalDiagram> getFundamentalDiagrams()
477 {
478 return this.fundamentalDiagrams;
479 }
480
481
482
483
484 public final DoubleScalar.Rel<LengthUnit> getMinimumDistance()
485 {
486 return this.minimumDistance;
487 }
488
489
490
491
492 public final DoubleScalar.Rel<LengthUnit> getMaximumDistance()
493 {
494 return this.maximumDistance;
495 }
496
497
498 protected class IDMCar extends Car<Integer>
499 {
500
501 private static final long serialVersionUID = 20141031L;
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519 public IDMCar(final int id, GTUType<String> gtuType, final OTSDEVSSimulatorInterface simulator,
520 final GTUFollowingModel carFollowingModel, DoubleScalar.Rel<LengthUnit> vehicleLength,
521 final DoubleScalar.Abs<TimeUnit> initialTime,
522 final Map<Lane, DoubleScalar.Rel<LengthUnit>> initialLongitudinalPositions,
523 final DoubleScalar.Abs<SpeedUnit> initialSpeed) throws RemoteException, NamingException, NetworkException, SimRuntimeException
524 {
525 super(id, gtuType, carFollowingModel, initialLongitudinalPositions, initialSpeed, vehicleLength,
526 new DoubleScalar.Rel<LengthUnit>(1.8, LengthUnit.METER), new DoubleScalar.Abs<SpeedUnit>(200,
527 SpeedUnit.KM_PER_HOUR), simulator);
528 try
529 {
530 simulator.scheduleEventAbs(simulator.getSimulatorTime(), this, this, "move", null);
531 }
532 catch (SimRuntimeException exception)
533 {
534 exception.printStackTrace();
535 }
536 }
537
538
539
540
541
542
543 protected final void move() throws RemoteException, NetworkException, SimRuntimeException
544 {
545
546 if (this == FundamentalDiagramPlotsModel.this.block)
547 {
548 return;
549 }
550 Lane lane = positions(getFront()).keySet().iterator().next();
551 if (position(lane, getFront()).getSI() > getMaximumDistance().getSI())
552 {
553 FundamentalDiagramPlotsModel.this.cars.remove(this);
554 return;
555 }
556 Collection<Car<Integer>> leaders = new ArrayList<Car<Integer>>();
557
558
559 int carIndex = FundamentalDiagramPlotsModel.this.cars.indexOf(this);
560 if (carIndex < FundamentalDiagramPlotsModel.this.cars.size() - 1)
561 {
562 leaders.add(FundamentalDiagramPlotsModel.this.cars.get(carIndex + 1));
563 }
564 GTUFollowingModelResult cfmr =
565 getGTUFollowingModel().computeAcceleration(this, leaders, FundamentalDiagramPlotsModel.this.speedLimit);
566 if (null != FundamentalDiagramPlotsModel.this.block)
567 {
568 leaders.clear();
569 leaders.add(FundamentalDiagramPlotsModel.this.block);
570 GTUFollowingModelResult blockCFMR =
571 getGTUFollowingModel().computeAcceleration(this, leaders, FundamentalDiagramPlotsModel.this.speedLimit);
572 if (blockCFMR.getAcceleration().getSI() < cfmr.getAcceleration().getSI()
573 && blockCFMR.getAcceleration().getSI() >= -5)
574 {
575 cfmr = blockCFMR;
576 }
577 }
578 setState(cfmr);
579
580
581 addToFundamentalDiagramPlots(this);
582 getSimulator().scheduleEventRel(new DoubleScalar.Rel<TimeUnit>(0.5, TimeUnit.SECOND), this, this, "move", null);
583 }
584
585
586
587
588
589 private void addToFundamentalDiagramPlots(final IDMCar idmCar) throws RemoteException, NetworkException
590 {
591 DoubleScalar.Abs<TimeUnit> lowerBound = idmCar.getLastEvaluationTime();
592 DoubleScalar.Abs<TimeUnit> upperBound = idmCar.getNextEvaluationTime();
593 DoubleScalar.Rel<LengthUnit> beginPosition =
594 idmCar.position(FundamentalDiagramPlotsModel.this.lane, idmCar.getFront(), lowerBound);
595 DoubleScalar.Rel<LengthUnit> endPosition =
596 idmCar.position(FundamentalDiagramPlotsModel.this.lane, idmCar.getFront(), upperBound);
597 for (FundamentalDiagram fd : getFundamentalDiagrams())
598 {
599 DoubleScalar.Abs<LengthUnit> detectorPosition = fd.getPosition();
600 if (beginPosition.getSI() <= detectorPosition.getSI() && endPosition.getSI() > detectorPosition.getSI())
601 {
602
603
604
605 final double maximumTimeError = 0.01;
606 DoubleScalar.Abs<TimeUnit> passingTime = lowerBound;
607 while (upperBound.getSI() - lowerBound.getSI() > maximumTimeError)
608 {
609 passingTime =
610 new DoubleScalar.Abs<TimeUnit>((lowerBound.getSI() + upperBound.getSI()) / 2, TimeUnit.SECOND);
611 DoubleScalar.Rel<LengthUnit> position =
612 idmCar.position(FundamentalDiagramPlotsModel.this.lane, idmCar.getFront(), passingTime);
613 if (position.getSI() > detectorPosition.getSI())
614 {
615 lowerBound = passingTime;
616 }
617 else
618 {
619 upperBound = passingTime;
620 }
621 }
622 fd.addData(0, idmCar, passingTime);
623 }
624 }
625 }
626 }
627
628 }