View Javadoc
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   * Demonstrate the FundamentalDiagram plot.
57   * <p>
58   * Copyright (c) 2013-2014 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
59   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
60   * <p>
61   * @version 17 dec. 2014 <br>
62   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
63   */
64  public class FundamentalDiagrams implements WrappableSimulation
65  {
66      /** The properties exhibited by this simulation. */
67      private ArrayList<AbstractProperty<?>> properties = new ArrayList<AbstractProperty<?>>();
68  
69      /** Create a FundamentalDiagrams simulation. */
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       * Main program.
93       * @param args String[]; the command line arguments (not used)
94       * @throws SimRuntimeException
95       * @throws RemoteException
96       */
97      public static void main(final String[] args) throws RemoteException, SimRuntimeException
98      {
99          // Create the simulation and wrap its panel in a JFrame. It does not get much easier/shorter than this...
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      * @return SimpleSimulator
121      * @throws SimRuntimeException
122      * @throws RemoteException
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      * make the stand-alone plots for the model and put them in the statistics panel.
140      * @param model FundamentalDiagramPlotsModel; the model.
141      * @param panel DSOLPanel
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      * @param panel DSOLPanel
165      */
166     private static void addInfoTab(
167         final DSOLPanel<DoubleScalar.Abs<TimeUnit>, DoubleScalar.Rel<TimeUnit>, OTSSimTimeDouble> panel)
168     {
169         // Let's find some content for our infoscreen and add it to our tabbedPane
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     /** {@inheritDoc} */
188     @Override
189     public String shortName()
190     {
191         return "Fundamental Diagrams";
192     }
193 
194     /** {@inheritDoc} */
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     /** {@inheritDoc} */
207     @Override
208     public ArrayList<AbstractProperty<?>> getProperties()
209     {
210         return this.properties;
211     }
212 }
213 
214 /**
215  * Simulate a single lane road of 5 km length. Vehicles are generated at a constant rate of 1500 veh/hour. At time 300s a
216  * blockade is inserted at position 4 km; this blockade is removed at time 500s. The used car following algorithm is IDM+ <a
217  * href="http://opentrafficsim.org/downloads/MOTUS%20reference.pdf"><i>Integrated Lane Change Model with Relaxation and
218  * Synchronization</i>, by Wouter J. Schakel, Victor L. Knoop and Bart van Arem, 2012</a>. <br>
219  * Output is a set of FundamentalDiagram plots for various point along the lane.
220  * <p>
221  * Copyright (c) 2013-2014 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
222  * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
223  * <p>
224  * @version Aug 1, 2014 <br>
225  * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
226  */
227 class FundamentalDiagramPlotsModel implements OTSModelInterface
228 {
229     /** */
230     private static final long serialVersionUID = 20140820L;
231 
232     /** the simulator. */
233     private OTSDEVSSimulatorInterface simulator;
234 
235     /** the headway (inter-vehicle time). */
236     private DoubleScalar.Rel<TimeUnit> headway;
237 
238     /** number of cars created. */
239     private int carsCreated = 0;
240 
241     /** the car following model, e.g. IDM Plus for cars. */
242     protected GTUFollowingModel carFollowingModelCars;
243 
244     /** the car following model, e.g. IDM Plus for trucks. */
245     protected GTUFollowingModel carFollowingModelTrucks;
246 
247     /** The probability that the next generated GTU is a passenger car. */
248     double carProbability;
249 
250     /** cars in the model. */
251     ArrayList<Car<Integer>> cars = new ArrayList<Car<Integer>>();
252 
253     /** The blocking car. */
254     Car<Integer> block = null;
255 
256     /** minimum distance. */
257     private DoubleScalar.Rel<LengthUnit> minimumDistance = new DoubleScalar.Rel<LengthUnit>(0, LengthUnit.METER);
258 
259     /** maximum distance. */
260     private DoubleScalar.Rel<LengthUnit> maximumDistance = new DoubleScalar.Rel<LengthUnit>(5000, LengthUnit.METER);
261 
262     /** The Lane containing the simulated Cars. */
263     Lane lane;
264 
265     /** the speed limit. */
266     DoubleScalar.Abs<SpeedUnit> speedLimit = new DoubleScalar.Abs<SpeedUnit>(100, SpeedUnit.KM_PER_HOUR);
267 
268     /** the fundamental diagram plots. */
269     private ArrayList<FundamentalDiagram> fundamentalDiagrams = new ArrayList<FundamentalDiagram>();
270 
271     /** User settable properties */
272     ArrayList<AbstractProperty<?>> properties = null;
273 
274     /** The random number generator used to decide what kind of GTU to generate. */
275     Random randomGenerator = new Random(12345);
276 
277     /**
278      * @param properties
279      */
280     public FundamentalDiagramPlotsModel(ArrayList<AbstractProperty<?>> properties)
281     {
282         this.properties = properties;
283     }
284 
285     /** {@inheritDoc} */
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         // 1500 [veh / hour] == 2.4s headway
367         this.headway = new DoubleScalar.Rel<TimeUnit>(3600.0 / 1500.0, TimeUnit.SECOND);
368 
369         try
370         {
371             // Schedule creation of the first car (this will re-schedule itself one headway later, etc.).
372             this.simulator.scheduleEventAbs(new DoubleScalar.Abs<TimeUnit>(0.0, TimeUnit.SECOND), this, this, "generateCar",
373                 null);
374             // Create a block at t = 5 minutes
375             this.simulator.scheduleEventAbs(new DoubleScalar.Abs<TimeUnit>(300, TimeUnit.SECOND), this, this, "createBlock",
376                 null);
377             // Remove the block at t = 7 minutes
378             this.simulator.scheduleEventAbs(new DoubleScalar.Abs<TimeUnit>(420, TimeUnit.SECOND), this, this, "removeBlock",
379                 null);
380             // Schedule regular updates of the graph
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      * Set up the block.
395      * @throws RemoteException on communications failure
396      * @throws SimRuntimeException 
397      * @throws NetworkException 
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      * Remove the block.
419      */
420     protected final void removeBlock()
421     {
422         this.block = null;
423     }
424 
425     /**
426      * Generate cars at a fixed rate (implemented by re-scheduling this method).
427      * @throws NamingException on ???
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         // Notify the Fundamental Diagram plots that the underlying data has changed
459         for (FundamentalDiagram fd : this.fundamentalDiagrams)
460         {
461             fd.reGraph();
462         }
463     }
464 
465     /** {@inheritDoc} */
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      * @return fundamentalDiagramPlots
475      */
476     public final ArrayList<FundamentalDiagram> getFundamentalDiagrams()
477     {
478         return this.fundamentalDiagrams;
479     }
480 
481     /**
482      * @return minimumDistance
483      */
484     public final DoubleScalar.Rel<LengthUnit> getMinimumDistance()
485     {
486         return this.minimumDistance;
487     }
488 
489     /**
490      * @return maximumDistance
491      */
492     public final DoubleScalar.Rel<LengthUnit> getMaximumDistance()
493     {
494         return this.maximumDistance;
495     }
496 
497     /** Inner class IDMCar. */
498     protected class IDMCar extends Car<Integer>
499     {
500         /** */
501         private static final long serialVersionUID = 20141031L;
502 
503         /**
504          * Create a new IDMCar.
505          * @param id integer; the id of the new IDMCar
506          * @param gtuType GTUType&lt;String&gt;; the type of the GTU
507          * @param simulator OTSDEVSSimulator; the simulator that runs the new IDMCar
508          * @param carFollowingModel CarFollowingModel; the car following model of the new IDMCar
509          * @param vehicleLength DoubleScalar.Rel&lt;LengthUnit&gt;; the length of the new IDMCar
510          * @param initialTime DoubleScalar.Abs&lt;TimeUnit&gt;; the time of first evaluation of the new IDMCar
511          * @param initialLongitudinalPositions Map&lt;Lane, DoubleScalar.Rel&lt;LengthUnit&gt;&gt;; the initial lane positions
512          *            of the new IDMCar
513          * @param initialSpeed DoubleScalar.Abs&lt;SpeedUnit&gt;; the initial speed of the new IDMCar
514          * @throws NamingException on ???
515          * @throws RemoteException on communication failure
516          * @throws SimRuntimeException 
517          * @throws NetworkException 
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          * @throws RemoteException on communication failure
540          * @throws NetworkException on network inconsistency
541          * @throws SimRuntimeException on ???
542          */
543         protected final void move() throws RemoteException, NetworkException, SimRuntimeException
544         {
545             // System.out.println("move " + this.getId());
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             // FIXME: there should be a much easier way to obtain the leader; we should not have to maintain our own
558             // list
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             // Add the movement of this Car to the Fundamental Diagram plots
581             addToFundamentalDiagramPlots(this);
582             getSimulator().scheduleEventRel(new DoubleScalar.Rel<TimeUnit>(0.5, TimeUnit.SECOND), this, this, "move", null);
583         }
584 
585         /**
586          * @param idmCar IDMCar
587          * @throws NetworkException on network inconsistency
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                     // This car passes the detector; add the movement of this Car to the fundamental diagram plot
603                     // Figure out at what time the car passes the detector.
604                     // For this demo we use bisection to converge to the correct time.
605                     final double maximumTimeError = 0.01; // [s]
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 }