View Javadoc
1   package org.opentrafficsim.demo.carFollowing;
2   
3   import java.awt.Frame;
4   import java.awt.geom.Rectangle2D;
5   import java.rmi.RemoteException;
6   import java.util.ArrayList;
7   import java.util.LinkedHashMap;
8   import java.util.Map;
9   import java.util.Random;
10  
11  import javax.naming.NamingException;
12  import javax.swing.JPanel;
13  import javax.swing.SwingUtilities;
14  
15  import nl.tudelft.simulation.dsol.SimRuntimeException;
16  import nl.tudelft.simulation.dsol.gui.swing.TablePanel;
17  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
18  
19  import org.djunits.unit.TimeUnit;
20  import org.djunits.value.vdouble.scalar.DoubleScalar;
21  import org.opentrafficsim.core.OTS_SCALAR;
22  import org.opentrafficsim.core.dsol.OTSDEVSSimulatorInterface;
23  import org.opentrafficsim.core.dsol.OTSModelInterface;
24  import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
25  import org.opentrafficsim.core.geometry.OTSGeometryException;
26  import org.opentrafficsim.core.geometry.OTSPoint3D;
27  import org.opentrafficsim.core.gtu.GTUException;
28  import org.opentrafficsim.core.gtu.GTUType;
29  import org.opentrafficsim.core.gtu.animation.GTUColorer;
30  import org.opentrafficsim.core.network.LongitudinalDirectionality;
31  import org.opentrafficsim.core.network.NetworkException;
32  import org.opentrafficsim.core.network.OTSNode;
33  import org.opentrafficsim.core.network.route.CompleteRoute;
34  import org.opentrafficsim.graphs.FundamentalDiagram;
35  import org.opentrafficsim.road.car.LaneBasedIndividualCar;
36  import org.opentrafficsim.road.gtu.animation.DefaultCarAnimation;
37  import org.opentrafficsim.road.gtu.following.GTUFollowingModel;
38  import org.opentrafficsim.road.gtu.following.IDM;
39  import org.opentrafficsim.road.gtu.following.IDMPlus;
40  import org.opentrafficsim.road.gtu.lane.changing.AbstractLaneChangeModel;
41  import org.opentrafficsim.road.gtu.lane.changing.Egoistic;
42  import org.opentrafficsim.road.network.factory.LaneFactory;
43  import org.opentrafficsim.road.network.lane.CrossSectionLink;
44  import org.opentrafficsim.road.network.lane.Lane;
45  import org.opentrafficsim.road.network.lane.LaneType;
46  import org.opentrafficsim.road.network.lane.Sensor;
47  import org.opentrafficsim.road.network.lane.SinkSensor;
48  import org.opentrafficsim.road.network.lane.changing.OvertakingConditions;
49  import org.opentrafficsim.road.network.route.CompleteLaneBasedRouteNavigator;
50  import org.opentrafficsim.simulationengine.AbstractWrappableAnimation;
51  import org.opentrafficsim.simulationengine.properties.AbstractProperty;
52  import org.opentrafficsim.simulationengine.properties.ProbabilityDistributionProperty;
53  import org.opentrafficsim.simulationengine.properties.PropertyException;
54  import org.opentrafficsim.simulationengine.properties.SelectionProperty;
55  
56  /**
57   * Demonstrate the FundamentalDiagram plot.
58   * <p>
59   * Copyright (c) 2013-2015 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
60   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
61   * <p>
62   * $LastChangedDate: 2015-09-24 19:14:49 +0200 (Thu, 24 Sep 2015) $, @version $Revision: 1430 $, by $Author: averbraeck $,
63   * initial version 17 dec. 2014 <br>
64   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
65   */
66  public class FundamentalDiagrams extends AbstractWrappableAnimation
67  {
68      /** the model. */
69      private FundamentalDiagramPlotsModel model;
70  
71      /** Create a FundamentalDiagrams simulation. */
72      public FundamentalDiagrams()
73      {
74          try
75          {
76              this.properties.add(new SelectionProperty("Car following model",
77                  "<html>The car following model determines "
78                      + "the acceleration that a vehicle will make taking into account nearby vehicles, "
79                      + "infrastructural restrictions (e.g. speed limit, curvature of the road) "
80                      + "capabilities of the vehicle and personality of the driver.</html>", new String[]{"IDM", "IDM+"},
81                  1, false, 500));
82              this.properties.add(new ProbabilityDistributionProperty("Traffic composition",
83                  "<html>Mix of passenger cars and trucks</html>", new String[]{"passenger car", "truck"}, new Double[]{
84                      0.8, 0.2}, false, 10));
85          }
86          catch (PropertyException exception)
87          {
88              exception.printStackTrace();
89          }
90      }
91  
92      /** {@inheritDoc} */
93      @Override
94      public final void stopTimersThreads()
95      {
96          super.stopTimersThreads();
97          this.model = null;
98      }
99  
100     /**
101      * Main program.
102      * @param args String[]; the command line arguments (not used)
103      * @throws SimRuntimeException on ???
104      */
105     public static void main(final String[] args) throws SimRuntimeException
106     {
107         // Create the simulation and wrap its panel in a JFrame. It does not get much easier/shorter than this...
108         SwingUtilities.invokeLater(new Runnable()
109         {
110             @Override
111             public void run()
112             {
113                 try
114                 {
115                     FundamentalDiagrams fundamentalDiagrams = new FundamentalDiagrams();
116                     fundamentalDiagrams.buildAnimator(new Time.Abs(0.0, SECOND), new Time.Rel(0.0, SECOND),
117                         new Time.Rel(3600.0, SECOND), fundamentalDiagrams.getProperties(), null, true);
118                 }
119                 catch (SimRuntimeException | NamingException exception)
120                 {
121                     exception.printStackTrace();
122                 }
123             }
124         });
125     }
126 
127     /** {@inheritDoc} */
128     @Override
129     protected final OTSModelInterface makeModel(final GTUColorer colorer)
130     {
131         this.model = new FundamentalDiagramPlotsModel(this.savedUserModifiedProperties, colorer);
132         return this.model;
133     }
134 
135     /** {@inheritDoc} */
136     @Override
137     protected final Rectangle2D.Double makeAnimationRectangle()
138     {
139         return new Rectangle2D.Double(0, -100, 5000, 200);
140     }
141 
142     /** {@inheritDoc} */
143     @Override
144     protected final JPanel makeCharts()
145     {
146         final int panelsPerRow = 3;
147         TablePanel charts = new TablePanel(4, panelsPerRow);
148         for (int plotNumber = 0; plotNumber < 10; plotNumber++)
149         {
150             Length.Rel detectorLocation = new Length.Rel(400 + 500 * plotNumber, METER);
151             FundamentalDiagram fd;
152             try
153             {
154                 fd =
155                     new FundamentalDiagram("Fundamental Diagram at " + detectorLocation.getSI() + "m", new Time.Rel(1,
156                         MINUTE), this.model.getLane(), detectorLocation);
157                 fd.setTitle("Density Contour Graph");
158                 fd.setExtendedState(Frame.MAXIMIZED_BOTH);
159                 this.model.getFundamentalDiagrams().add(fd);
160                 charts.setCell(fd.getContentPane(), plotNumber / panelsPerRow, plotNumber % panelsPerRow);
161             }
162             catch (NetworkException exception)
163             {
164                 exception.printStackTrace();
165             }
166         }
167         return charts;
168     }
169 
170     /** {@inheritDoc} */
171     @Override
172     public final String shortName()
173     {
174         return "Fundamental Diagrams";
175     }
176 
177     /** {@inheritDoc} */
178     @Override
179     public final String description()
180     {
181         return "<html><h1>Fundamental Diagram Plots</H1>"
182             + "Simulation of a single lane road of 5 km length. Vechicles are generated at a constant rate of "
183             + "1500 veh/hour. At time 300s a blockade is inserted at position 4km; this blockade is removed at time "
184             + "500s. This blockade simulates a bridge opening.<br>"
185             + "The blockade causes a traffic jam that slowly dissolves after the blockade is removed.<br />"
186             + "Output is a set of Diagrams that plot observed density, flow and speed plots against each other.</html>";
187     }
188 
189     /**
190      * Simulate a single lane road of 5 km length. Vehicles are generated at a constant rate of 1500 veh/hour. At time 300s a
191      * blockade is inserted at position 4 km; this blockade is removed at time 500s. The used car following algorithm is IDM+ <a
192      * href="http://opentrafficsim.org/downloads/MOTUS%20reference.pdf"><i>Integrated Lane Change Model with Relaxation and
193      * Synchronization</i>, by Wouter J. Schakel, Victor L. Knoop and Bart van Arem, 2012</a>. <br>
194      * Output is a set of FundamentalDiagram plots for various point along the lane.
195      * <p>
196      * Copyright (c) 2013-2015 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
197      * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
198      * <p>
199      * $LastChangedDate: 2015-09-24 19:14:49 +0200 (Thu, 24 Sep 2015) $, @version $Revision: 1430 $, by $Author: averbraeck $,
200      * initial version ug 1, 2014 <br>
201      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
202      */
203     class FundamentalDiagramPlotsModel implements OTSModelInterface, OTS_SCALAR
204     {
205         /** */
206         private static final long serialVersionUID = 20140820L;
207 
208         /** the simulator. */
209         private OTSDEVSSimulatorInterface simulator;
210 
211         /** the headway (inter-vehicle time). */
212         private Time.Rel headway;
213 
214         /** number of cars created. */
215         private int carsCreated = 0;
216 
217         /** Type of all GTUs. */
218         private GTUType gtuType = GTUType.makeGTUType("Car");
219 
220         /** the car following model, e.g. IDM Plus for cars. */
221         private GTUFollowingModel carFollowingModelCars;
222 
223         /** the car following model, e.g. IDM Plus for trucks. */
224         private GTUFollowingModel carFollowingModelTrucks;
225 
226         /** The probability that the next generated GTU is a passenger car. */
227         private double carProbability;
228 
229         /** The lane change model. */
230         private AbstractLaneChangeModel laneChangeModel = new Egoistic();
231 
232         /** The blocking car. */
233         private LaneBasedIndividualCar block = null;
234 
235         /** minimum distance. */
236         private Length.Rel minimumDistance = new Length.Rel(0, METER);
237 
238         /** maximum distance. */
239         private Length.Rel maximumDistance = new Length.Rel(5000, METER);
240 
241         /** The Lane containing the simulated Cars. */
242         private Lane lane;
243 
244         /** the speed limit. */
245         private Speed.Abs speedLimit = new Speed.Abs(100, KM_PER_HOUR);
246 
247         /** the fundamental diagram plots. */
248         private ArrayList<FundamentalDiagram> fundamentalDiagrams = new ArrayList<FundamentalDiagram>();
249 
250         /** User settable properties. */
251         private ArrayList<AbstractProperty<?>> properties = null;
252 
253         /** The random number generator used to decide what kind of GTU to generate. */
254         private Random randomGenerator = new Random(12345);
255 
256         /** The GTUColorer for the generated vehicles. */
257         private final GTUColorer gtuColorer;
258 
259         /**
260          * @param properties ArrayList&lt;AbstractProperty&lt;?&gt;&gt;; the properties
261          * @param gtuColorer the default and initial GTUColorer, e.g. a DefaultSwitchableTUColorer.
262          */
263         public FundamentalDiagramPlotsModel(final ArrayList<AbstractProperty<?>> properties, final GTUColorer gtuColorer)
264         {
265             this.properties = properties;
266             this.gtuColorer = gtuColorer;
267         }
268 
269         /** {@inheritDoc} */
270         @Override
271         public final
272             void
273             constructModel(
274                 final SimulatorInterface<DoubleScalar.Abs<TimeUnit>, DoubleScalar.Rel<TimeUnit>, OTSSimTimeDouble> theSimulator)
275                 throws SimRuntimeException, RemoteException
276         {
277             this.simulator = (OTSDEVSSimulatorInterface) theSimulator;
278             OTSNode from = new OTSNode("From", new OTSPoint3D(getMinimumDistance().getSI(), 0, 0));
279             OTSNode to = new OTSNode("To", new OTSPoint3D(getMaximumDistance().getSI(), 0, 0));
280             OTSNode end = new OTSNode("End", new OTSPoint3D(getMaximumDistance().getSI() + 50.0, 0, 0));
281             LaneType laneType = new LaneType("CarLane");
282             laneType.addCompatibility(this.gtuType);
283             try
284             {
285                 this.lane = LaneFactory.makeLane("Lane", from, to, null, laneType, this.speedLimit, this.simulator);
286                 CrossSectionLink endLink = LaneFactory.makeLink("endLink", to, end, null);
287                 // No overtaking, single lane
288                 Lane sinkLane =
289                     new Lane(endLink, "sinkLane", this.lane.getLateralCenterPosition(1.0), this.lane
290                         .getLateralCenterPosition(1.0), this.lane.getWidth(1.0), this.lane.getWidth(1.0), laneType,
291                         LongitudinalDirectionality.FORWARD, this.speedLimit, new OvertakingConditions.None());
292                 Sensor sensor = new SinkSensor(sinkLane, new Length.Rel(10.0, METER), this.simulator);
293                 sinkLane.addSensor(sensor, GTUType.ALL);
294             }
295             catch (NamingException | NetworkException | OTSGeometryException exception)
296             {
297                 exception.printStackTrace();
298             }
299 
300             // create SinkLane
301 
302             for (AbstractProperty<?> p : this.properties)
303             {
304                 if (p instanceof SelectionProperty)
305                 {
306                     SelectionProperty sp = (SelectionProperty) p;
307                     if ("Car following model".equals(sp.getShortName()))
308                     {
309                         String modelName = sp.getValue();
310                         if (modelName.equals("IDM"))
311                         {
312                             this.carFollowingModelCars =
313                                 new IDM(new Acceleration.Abs(1, METER_PER_SECOND_2), new Acceleration.Abs(1.5,
314                                     METER_PER_SECOND_2), new Length.Rel(2, METER), new Time.Rel(1, SECOND), 1d);
315                             this.carFollowingModelTrucks =
316                                 new IDM(new Acceleration.Abs(0.5, METER_PER_SECOND_2), new Acceleration.Abs(1.5,
317                                     METER_PER_SECOND_2), new Length.Rel(2, METER), new Time.Rel(1, SECOND), 1d);
318                         }
319                         else if (modelName.equals("IDM+"))
320                         {
321                             this.carFollowingModelCars =
322                                 new IDMPlus(new Acceleration.Abs(1, METER_PER_SECOND_2), new Acceleration.Abs(1.5,
323                                     METER_PER_SECOND_2), new Length.Rel(2, METER), new Time.Rel(1, SECOND), 1d);
324                             this.carFollowingModelTrucks =
325                                 new IDMPlus(new Acceleration.Abs(0.5, METER_PER_SECOND_2), new Acceleration.Abs(1.5,
326                                     METER_PER_SECOND_2), new Length.Rel(2, METER), new Time.Rel(1, SECOND), 1d);
327                         }
328                         else
329                         {
330                             throw new Error("Car following model " + modelName + " not implemented");
331                         }
332                     }
333                     else
334                     {
335                         throw new Error("Unhandled SelectionProperty " + p.getShortName());
336                     }
337                 }
338                 else if (p instanceof ProbabilityDistributionProperty)
339                 {
340                     ProbabilityDistributionProperty pdp = (ProbabilityDistributionProperty) p;
341                     String modelName = p.getShortName();
342                     if (modelName.equals("Traffic composition"))
343                     {
344                         this.carProbability = pdp.getValue()[0];
345                     }
346                     else
347                     {
348                         throw new Error("Unhandled ProbabilityDistributionProperty " + p.getShortName());
349                     }
350                 }
351                 else
352                 {
353                     throw new Error("Unhandled property: " + p);
354                 }
355             }
356 
357             // 1500 [veh / hour] == 2.4s headway
358             this.headway = new Time.Rel(3600.0 / 1500.0, SECOND);
359 
360             try
361             {
362                 // Schedule creation of the first car (this will re-schedule itself one headway later, etc.).
363                 this.simulator.scheduleEventAbs(new DoubleScalar.Abs<TimeUnit>(0.0, SECOND), this, this, "generateCar",
364                     null);
365                 // Create a block at t = 5 minutes
366                 this.simulator.scheduleEventAbs(new DoubleScalar.Abs<TimeUnit>(300, SECOND), this, this, "createBlock",
367                     null);
368                 // Remove the block at t = 7 minutes
369                 this.simulator.scheduleEventAbs(new DoubleScalar.Abs<TimeUnit>(420, SECOND), this, this, "removeBlock",
370                     null);
371                 // Schedule regular updates of the graph
372                 for (int t = 1; t <= 1800; t++)
373                 {
374                     this.simulator.scheduleEventAbs(new DoubleScalar.Abs<TimeUnit>(t - 0.001, SECOND), this, this,
375                         "drawGraphs", null);
376                 }
377             }
378             catch (SimRuntimeException exception)
379             {
380                 exception.printStackTrace();
381             }
382         }
383 
384         /**
385          * Set up the block.
386          * @throws RemoteException on communications failure
387          */
388         protected final void createBlock() throws RemoteException
389         {
390             Length.Rel initialPosition = new Length.Rel(4000, METER);
391             Map<Lane, Length.Rel> initialPositions = new LinkedHashMap<Lane, Length.Rel>();
392             initialPositions.put(this.getLane(), initialPosition);
393             try
394             {
395                 this.block =
396                     new LaneBasedIndividualCar("999999", this.gtuType, this.carFollowingModelCars,
397                         this.laneChangeModel, initialPositions, new Speed.Abs(0, KM_PER_HOUR),
398                         new Length.Rel(4, METER), new Length.Rel(1.8, METER), new Speed.Abs(0, KM_PER_HOUR),
399                         new CompleteLaneBasedRouteNavigator(new CompleteRoute("")), this.simulator,
400                         DefaultCarAnimation.class, this.gtuColorer);
401             }
402             catch (SimRuntimeException | NamingException | NetworkException | GTUException exception)
403             {
404                 exception.printStackTrace();
405             }
406         }
407 
408         /**
409          * Remove the block.
410          */
411         protected final void removeBlock()
412         {
413             this.block.destroy();
414             this.block = null;
415         }
416 
417         /**
418          * Generate cars at a fixed rate (implemented by re-scheduling this method).
419          */
420         protected final void generateCar()
421         {
422             boolean generateTruck = this.randomGenerator.nextDouble() > this.carProbability;
423             Length.Rel initialPosition = new Length.Rel(0, METER);
424             Speed.Abs initialSpeed = new Speed.Abs(100, KM_PER_HOUR);
425             Map<Lane, Length.Rel> initialPositions = new LinkedHashMap<Lane, Length.Rel>();
426             initialPositions.put(this.getLane(), initialPosition);
427             try
428             {
429                 Length.Rel vehicleLength = new Length.Rel(generateTruck ? 15 : 4, METER);
430                 GTUFollowingModel gtuFollowingModel =
431                     generateTruck ? this.carFollowingModelTrucks : this.carFollowingModelCars;
432                 if (null == gtuFollowingModel)
433                 {
434                     throw new Error("gtuFollowingModel is null");
435                 }
436                 new LaneBasedIndividualCar("" + (++this.carsCreated), this.gtuType, generateTruck
437                     ? this.carFollowingModelTrucks : this.carFollowingModelCars, this.laneChangeModel,
438                     initialPositions, initialSpeed, vehicleLength, new Length.Rel(1.8, METER), new Speed.Abs(200,
439                         KM_PER_HOUR), new CompleteLaneBasedRouteNavigator(new CompleteRoute("")), this.simulator,
440                     DefaultCarAnimation.class, this.gtuColorer);
441                 this.simulator.scheduleEventRel(this.headway, this, this, "generateCar", null);
442             }
443             catch (SimRuntimeException | NamingException | NetworkException | GTUException exception)
444             {
445                 exception.printStackTrace();
446             }
447         }
448 
449         /**
450      * 
451      */
452         protected final void drawGraphs()
453         {
454             // Notify the Fundamental Diagram plots that the underlying data has changed
455             for (FundamentalDiagram fd : this.fundamentalDiagrams)
456             {
457                 fd.reGraph();
458             }
459         }
460 
461         /** {@inheritDoc} */
462         @Override
463         public final SimulatorInterface<DoubleScalar.Abs<TimeUnit>, DoubleScalar.Rel<TimeUnit>, OTSSimTimeDouble>
464             getSimulator() throws RemoteException
465         {
466             return null;
467         }
468 
469         /**
470          * @return fundamentalDiagramPlots
471          */
472         public final ArrayList<FundamentalDiagram> getFundamentalDiagrams()
473         {
474             return this.fundamentalDiagrams;
475         }
476 
477         /**
478          * @return minimumDistance
479          */
480         public final Length.Rel getMinimumDistance()
481         {
482             return this.minimumDistance;
483         }
484 
485         /**
486          * @return maximumDistance
487          */
488         public final Length.Rel getMaximumDistance()
489         {
490             return this.maximumDistance;
491         }
492 
493         /**
494          * @return lane.
495          */
496         public Lane getLane()
497         {
498             return this.lane;
499         }
500     }
501 }