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