1 package org.opentrafficsim.demo.carFollowing;
2
3 import java.awt.Container;
4 import java.awt.Frame;
5 import java.awt.geom.Rectangle2D;
6 import java.rmi.RemoteException;
7 import java.util.ArrayList;
8 import java.util.Iterator;
9 import java.util.LinkedHashSet;
10 import java.util.List;
11 import java.util.Random;
12 import java.util.Set;
13
14 import javax.naming.NamingException;
15 import javax.swing.JPanel;
16 import javax.swing.SwingUtilities;
17
18 import nl.tudelft.simulation.dsol.SimRuntimeException;
19 import nl.tudelft.simulation.dsol.gui.swing.TablePanel;
20 import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
21
22 import org.djunits.unit.TimeUnit;
23 import org.djunits.unit.UNITS;
24 import org.djunits.value.vdouble.scalar.Acceleration;
25 import org.djunits.value.vdouble.scalar.DoubleScalar;
26 import org.djunits.value.vdouble.scalar.DoubleScalar.Abs;
27 import org.djunits.value.vdouble.scalar.DoubleScalar.Rel;
28 import org.djunits.value.vdouble.scalar.Length;
29 import org.djunits.value.vdouble.scalar.Speed;
30 import org.djunits.value.vdouble.scalar.Time;
31 import org.opentrafficsim.core.dsol.OTSDEVSSimulatorInterface;
32 import org.opentrafficsim.core.dsol.OTSModelInterface;
33 import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
34 import org.opentrafficsim.core.geometry.OTSGeometryException;
35 import org.opentrafficsim.core.geometry.OTSPoint3D;
36 import org.opentrafficsim.core.gtu.GTUDirectionality;
37 import org.opentrafficsim.core.gtu.GTUException;
38 import org.opentrafficsim.core.gtu.GTUType;
39 import org.opentrafficsim.core.gtu.animation.GTUColorer;
40 import org.opentrafficsim.core.network.LongitudinalDirectionality;
41 import org.opentrafficsim.core.network.NetworkException;
42 import org.opentrafficsim.core.network.OTSNetwork;
43 import org.opentrafficsim.core.network.OTSNode;
44 import org.opentrafficsim.graphs.AccelerationContourPlot;
45 import org.opentrafficsim.graphs.ContourPlot;
46 import org.opentrafficsim.graphs.DensityContourPlot;
47 import org.opentrafficsim.graphs.FlowContourPlot;
48 import org.opentrafficsim.graphs.LaneBasedGTUSampler;
49 import org.opentrafficsim.graphs.SpeedContourPlot;
50 import org.opentrafficsim.graphs.TrajectoryPlot;
51 import org.opentrafficsim.road.gtu.animation.DefaultCarAnimation;
52 import org.opentrafficsim.road.gtu.lane.LaneBasedIndividualGTU;
53 import org.opentrafficsim.road.gtu.lane.driver.LaneBasedBehavioralCharacteristics;
54 import org.opentrafficsim.road.gtu.lane.perception.LanePerceptionFull;
55 import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedGTUFollowingTacticalPlanner;
56 import org.opentrafficsim.road.gtu.lane.tactical.following.GTUFollowingModelOld;
57 import org.opentrafficsim.road.gtu.lane.tactical.following.IDMOld;
58 import org.opentrafficsim.road.gtu.lane.tactical.following.IDMPlusOld;
59 import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.AbstractLaneChangeModel;
60 import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.Egoistic;
61 import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlanner;
62 import org.opentrafficsim.road.gtu.strategical.route.LaneBasedStrategicalRoutePlanner;
63 import org.opentrafficsim.road.network.factory.LaneFactory;
64 import org.opentrafficsim.road.network.lane.DirectedLanePosition;
65 import org.opentrafficsim.road.network.lane.Lane;
66 import org.opentrafficsim.road.network.lane.LaneType;
67 import org.opentrafficsim.simulationengine.AbstractWrappableAnimation;
68 import org.opentrafficsim.simulationengine.OTSSimulationException;
69 import org.opentrafficsim.simulationengine.properties.AbstractProperty;
70 import org.opentrafficsim.simulationengine.properties.BooleanProperty;
71 import org.opentrafficsim.simulationengine.properties.CompoundProperty;
72 import org.opentrafficsim.simulationengine.properties.ContinuousProperty;
73 import org.opentrafficsim.simulationengine.properties.IDMPropertySet;
74 import org.opentrafficsim.simulationengine.properties.IntegerProperty;
75 import org.opentrafficsim.simulationengine.properties.ProbabilityDistributionProperty;
76 import org.opentrafficsim.simulationengine.properties.PropertyException;
77 import org.opentrafficsim.simulationengine.properties.SelectionProperty;
78
79
80
81
82
83
84
85
86
87
88
89 public class CircularLane extends AbstractWrappableAnimation implements UNITS
90 {
91
92 private LaneSimulationModel model;
93
94
95 public CircularLane()
96 {
97 this.properties.add(new IntegerProperty("Track length", "Circumference of the track", 2000, 500, 6000,
98 "Track length %dm", false, 10));
99 this.properties.add(new ContinuousProperty("Mean density", "Number of vehicles per km", 40.0, 5.0, 45.0,
100 "Density %.1f veh/km", false, 11));
101 this.properties.add(new ContinuousProperty("Density variability",
102 "Variability of the number of vehicles per km", 0.0, 0.0, 1.0, "%.1f", false, 12));
103 ArrayList<AbstractProperty<?>> outputProperties = new ArrayList<AbstractProperty<?>>();
104 outputProperties.add(new BooleanProperty("Density", "Density contour plot", true, false, 0));
105 outputProperties.add(new BooleanProperty("Flow", "Flow contour plot", true, false, 1));
106 outputProperties.add(new BooleanProperty("Speed", "Speed contour plot", true, false, 2));
107 outputProperties.add(new BooleanProperty("Acceleration", "Acceleration contour plot", true, false, 3));
108 outputProperties.add(new BooleanProperty("Trajectories", "Trajectory (time/distance) diagram", true, false, 4));
109 this.properties.add(new CompoundProperty("Output graphs", "Select the graphical output", outputProperties,
110 true, 1000));
111 }
112
113
114 @Override
115 public final void stopTimersThreads()
116 {
117 super.stopTimersThreads();
118 this.model = null;
119 }
120
121
122
123
124
125
126 public static void main(final String[] args) throws SimRuntimeException
127 {
128 SwingUtilities.invokeLater(new Runnable()
129 {
130 @Override
131 public void run()
132 {
133 try
134 {
135 CircularLane circularLane = new CircularLane();
136 ArrayList<AbstractProperty<?>> propertyList = circularLane.getProperties();
137 try
138 {
139 propertyList.add(new ProbabilityDistributionProperty("Traffic composition",
140 "<html>Mix of passenger cars and trucks</html>", new String[]{"passenger car", "truck"},
141 new Double[]{0.8, 0.2}, false, 10));
142 }
143 catch (PropertyException exception)
144 {
145 exception.printStackTrace();
146 }
147 propertyList.add(new SelectionProperty("Car following model",
148 "<html>The car following model determines "
149 + "the acceleration that a vehicle will make taking into account "
150 + "nearby vehicles, infrastructural restrictions (e.g. speed limit, "
151 + "curvature of the road) capabilities of the vehicle and personality "
152 + "of the driver.</html>", new String[]{"IDM", "IDM+"}, 1, false, 1));
153 propertyList.add(IDMPropertySet.makeIDMPropertySet("Car",
154 new Acceleration(1.0, METER_PER_SECOND_2), new Acceleration(1.5, METER_PER_SECOND_2),
155 new Length.Rel(2.0, METER), new Time.Rel(1.0, SECOND), 2));
156 propertyList.add(IDMPropertySet.makeIDMPropertySet("Truck", new Acceleration(0.5,
157 METER_PER_SECOND_2), new Acceleration(1.25, METER_PER_SECOND_2), new Length.Rel(2.0, METER),
158 new Time.Rel(1.0, SECOND), 3));
159 circularLane.buildAnimator(new Time.Abs(0.0, SECOND), new Time.Rel(0.0, SECOND), new Time.Rel(
160 3600.0, SECOND), propertyList, null, true);
161 }
162 catch (SimRuntimeException | NamingException | OTSSimulationException exception)
163 {
164 exception.printStackTrace();
165 }
166 }
167 });
168 }
169
170
171 @Override
172 protected final OTSModelInterface makeModel(final GTUColorer colorer)
173 {
174 this.model = new LaneSimulationModel(this.savedUserModifiedProperties, colorer);
175 return this.model;
176 }
177
178
179 @Override
180 protected final Rectangle2D.Double makeAnimationRectangle()
181 {
182 return new Rectangle2D.Double(-350, -350, 700, 700);
183 }
184
185
186 @Override
187 protected final JPanel makeCharts() throws OTSSimulationException
188 {
189
190 AbstractProperty<?> output =
191 new CompoundProperty("", "", this.properties, false, 0).findByShortName("Output graphs");
192 if (null == output)
193 {
194 throw new OTSSimulationException("Cannot find output properties");
195 }
196 ArrayList<BooleanProperty> graphs = new ArrayList<BooleanProperty>();
197 if (output instanceof CompoundProperty)
198 {
199 CompoundProperty outputProperties = (CompoundProperty) output;
200 for (AbstractProperty<?> ap : outputProperties.getValue())
201 {
202 if (ap instanceof BooleanProperty)
203 {
204 BooleanProperty bp = (BooleanProperty) ap;
205 if (bp.getValue())
206 {
207 graphs.add(bp);
208 }
209 }
210 }
211 }
212 else
213 {
214 throw new OTSSimulationException("output properties should be compound");
215 }
216 int graphCount = graphs.size();
217 int columns = (int) Math.ceil(Math.sqrt(graphCount));
218 int rows = 0 == columns ? 0 : (int) Math.ceil(graphCount * 1.0 / columns);
219 TablePanel charts = new TablePanel(columns, rows);
220
221 for (int i = 0; i < graphCount; i++)
222 {
223 String graphName = graphs.get(i).getShortName();
224 Container container = null;
225 LaneBasedGTUSampler graph;
226 if (graphName.contains("Trajectories"))
227 {
228 TrajectoryPlot tp =
229 new TrajectoryPlot("TrajectoryPlot", new Time.Rel(0.5, SECOND), this.model.getPath());
230 tp.setTitle("Trajectory Graph");
231 tp.setExtendedState(Frame.MAXIMIZED_BOTH);
232 graph = tp;
233 container = tp.getContentPane();
234 }
235 else
236 {
237 ContourPlot cp;
238 if (graphName.contains("Density"))
239 {
240 cp = new DensityContourPlot("DensityPlot", this.model.getPath());
241 cp.setTitle("Density Contour Graph");
242 }
243 else if (graphName.contains("Speed"))
244 {
245 cp = new SpeedContourPlot("SpeedPlot", this.model.getPath());
246 cp.setTitle("Speed Contour Graph");
247 }
248 else if (graphName.contains("Flow"))
249 {
250 cp = new FlowContourPlot("FlowPlot", this.model.getPath());
251 cp.setTitle("Flow Contour Graph");
252 }
253 else if (graphName.contains("Acceleration"))
254 {
255 cp = new AccelerationContourPlot("AccelerationPlot", this.model.getPath());
256 cp.setTitle("Acceleration Contour Graph");
257 }
258 else
259 {
260 throw new OTSSimulationException("Unhandled type of contourplot: " + graphName);
261 }
262 graph = cp;
263 container = cp.getContentPane();
264 }
265
266 charts.setCell(container, i % columns, i / columns);
267 this.model.getPlots().add(graph);
268 }
269 return charts;
270 }
271
272
273 @Override
274 public final String shortName()
275 {
276 return "Circular Lane simulation";
277 }
278
279
280 @Override
281 public final String description()
282 {
283 return "<html><h1>Circular Lane simulation</h1>"
284 + "Vehicles are unequally distributed over a one lane ring road.<br>"
285 + "When simulation starts, all vehicles begin driving and some shockwaves may develop (depending on "
286 + "the selected track length and car following parameters).<br>"
287 + "Selected trajectory and contour plots are generated during the simulation.</html>";
288 }
289
290 }
291
292
293
294
295
296
297
298
299
300
301
302 class LaneSimulationModel implements OTSModelInterface, UNITS
303 {
304
305 private static final long serialVersionUID = 20141121L;
306
307
308 private OTSNetwork network = new OTSNetwork("network");
309
310
311 private OTSDEVSSimulatorInterface simulator;
312
313
314 private int carsCreated = 0;
315
316
317 private GTUType gtuType = GTUType.makeGTUType("Car");
318
319
320 private GTUFollowingModelOld carFollowingModelCars;
321
322
323 private GTUFollowingModelOld carFollowingModelTrucks;
324
325
326 private double carProbability;
327
328
329 private AbstractLaneChangeModel laneChangeModel = new Egoistic();
330
331
332 private Length.Rel minimumDistance = new Length.Rel(0, METER);
333
334
335 private Lane lane1;
336
337
338 private Lane lane2;
339
340
341 private Speed speedLimit = new Speed(100, KM_PER_HOUR);
342
343
344 private ArrayList<LaneBasedGTUSampler> contourPlots = new ArrayList<LaneBasedGTUSampler>();
345
346
347 private ArrayList<TrajectoryPlot> trajectoryPlots = new ArrayList<TrajectoryPlot>();
348
349
350 private ArrayList<AbstractProperty<?>> properties = null;
351
352
353 private Random randomGenerator = new Random(12345);
354
355
356 private List<Lane> path = new ArrayList<Lane>();
357
358
359 private final GTUColorer gtuColorer;
360
361
362
363
364
365 public LaneSimulationModel(final ArrayList<AbstractProperty<?>> properties, final GTUColorer gtuColorer)
366 {
367 this.properties = properties;
368 this.gtuColorer = gtuColorer;
369 }
370
371
372 @Override
373 public void constructModel(final SimulatorInterface<Abs<TimeUnit>, Rel<TimeUnit>, OTSSimTimeDouble> theSimulator)
374 throws SimRuntimeException, RemoteException
375 {
376 this.simulator = (OTSDEVSSimulatorInterface) theSimulator;
377 double radius = 2000 / 2 / Math.PI;
378 double headway = 40;
379 double headwayVariability = 0;
380 try
381 {
382 String carFollowingModelName = null;
383 CompoundProperty propertyContainer = new CompoundProperty("", "", this.properties, false, 0);
384 AbstractProperty<?> cfmp = propertyContainer.findByShortName("Car following model");
385 if (null == cfmp)
386 {
387 throw new SimRuntimeException("Cannot find \"Car following model\" property");
388 }
389 if (cfmp instanceof SelectionProperty)
390 {
391 carFollowingModelName = ((SelectionProperty) cfmp).getValue();
392 }
393 else
394 {
395 throw new SimRuntimeException("\"Car following model\" property has wrong type");
396 }
397 Iterator<AbstractProperty<ArrayList<AbstractProperty<?>>>> iterator =
398 new CompoundProperty("", "", this.properties, false, 0).iterator();
399 while (iterator.hasNext())
400 {
401 AbstractProperty<?> ap = iterator.next();
402
403 if (ap instanceof SelectionProperty)
404 {
405 SelectionProperty sp = (SelectionProperty) ap;
406 if ("Car following model".equals(sp.getShortName()))
407 {
408 carFollowingModelName = sp.getValue();
409 }
410 }
411 else if (ap instanceof ProbabilityDistributionProperty)
412 {
413 ProbabilityDistributionProperty pdp = (ProbabilityDistributionProperty) ap;
414 String modelName = ap.getShortName();
415 if (modelName.equals("Traffic composition"))
416 {
417 this.carProbability = pdp.getValue()[0];
418 }
419 }
420 else if (ap instanceof IntegerProperty)
421 {
422 IntegerProperty ip = (IntegerProperty) ap;
423 if ("Track length".equals(ip.getShortName()))
424 {
425 radius = ip.getValue() / 2 / Math.PI;
426 }
427 }
428 else if (ap instanceof ContinuousProperty)
429 {
430 ContinuousProperty cp = (ContinuousProperty) ap;
431 if (cp.getShortName().equals("Mean density"))
432 {
433 headway = 1000 / cp.getValue();
434 }
435 if (cp.getShortName().equals("Density variability"))
436 {
437 headwayVariability = cp.getValue();
438 }
439 }
440 else if (ap instanceof CompoundProperty)
441 {
442 CompoundProperty cp = (CompoundProperty) ap;
443 if (ap.getShortName().equals("Output graphs"))
444 {
445 continue;
446 }
447 if (ap.getShortName().contains("IDM"))
448 {
449 Acceleration a = IDMPropertySet.getA(cp);
450 Acceleration b = IDMPropertySet.getB(cp);
451 Length.Rel s0 = IDMPropertySet.getS0(cp);
452 Time.Rel tSafe = IDMPropertySet.getTSafe(cp);
453 GTUFollowingModelOld gtuFollowingModel = null;
454 if (carFollowingModelName.equals("IDM"))
455 {
456 gtuFollowingModel = new IDMOld(a, b, s0, tSafe, 1.0);
457 }
458 else if (carFollowingModelName.equals("IDM+"))
459 {
460 gtuFollowingModel = new IDMPlusOld(a, b, s0, tSafe, 1.0);
461 }
462 else
463 {
464 throw new SimRuntimeException("Unknown gtu following model: " + carFollowingModelName);
465 }
466 if (ap.getShortName().contains(" Car "))
467 {
468 this.carFollowingModelCars = gtuFollowingModel;
469 }
470 else if (ap.getShortName().contains(" Truck "))
471 {
472 this.carFollowingModelTrucks = gtuFollowingModel;
473 }
474 else
475 {
476 throw new SimRuntimeException("Cannot determine gtu type for " + ap.getShortName());
477 }
478 }
479 }
480 }
481
482 OTSNode start = new OTSNode("Start", new OTSPoint3D(radius, 0, 0));
483 OTSNode halfway = new OTSNode("Halfway", new OTSPoint3D(-radius, 0, 0));
484 LaneType laneType = new LaneType("CarLane");
485 laneType.addCompatibility(this.gtuType);
486
487 OTSPoint3D[] coordsHalf1 = new OTSPoint3D[127];
488 for (int i = 0; i < coordsHalf1.length; i++)
489 {
490 double angle = Math.PI * (1 + i) / (1 + coordsHalf1.length);
491 coordsHalf1[i] = new OTSPoint3D(radius * Math.cos(angle), radius * Math.sin(angle), 0);
492 }
493 this.lane1 =
494 LaneFactory.makeMultiLane("Lane1", start, halfway, coordsHalf1, 1, laneType, this.speedLimit,
495 this.simulator, LongitudinalDirectionality.DIR_PLUS)[0];
496 this.path.add(this.lane1);
497
498 OTSPoint3D[] coordsHalf2 = new OTSPoint3D[127];
499 for (int i = 0; i < coordsHalf2.length; i++)
500 {
501 double angle = Math.PI + Math.PI * (1 + i) / (1 + coordsHalf2.length);
502 coordsHalf2[i] = new OTSPoint3D(radius * Math.cos(angle), radius * Math.sin(angle), 0);
503 }
504 this.lane2 =
505 LaneFactory.makeMultiLane("Lane2", halfway, start, coordsHalf2, 1, laneType, this.speedLimit,
506 this.simulator, LongitudinalDirectionality.DIR_PLUS)[0];
507 this.path.add(this.lane2);
508
509
510 double trackLength = this.lane1.getLength().getSI();
511 double variability = (headway - 20) * headwayVariability;
512 System.out.println("headway is " + headway + " variability limit is " + variability);
513 Random random = new Random(12345);
514 for (double pos = 0; pos <= trackLength - headway - variability;)
515 {
516
517 double actualHeadway = headway + (random.nextDouble() * 2 - 1) * variability;
518 generateCar(this.lane1, new Length.Rel(pos, METER));
519 pos += actualHeadway;
520 }
521
522 trackLength = this.lane2.getLength().getSI();
523 variability = (headway - 20) * headwayVariability;
524 System.out.println("headway is " + headway + " variability limit is " + variability);
525 random = new Random(54321);
526 for (double pos = 0; pos <= trackLength - headway - variability;)
527 {
528
529 double actualHeadway = headway + (random.nextDouble() * 2 - 1) * variability;
530 generateCar(this.lane2, new Length.Rel(pos, METER));
531 pos += actualHeadway;
532 }
533
534 this.simulator.scheduleEventAbs(new DoubleScalar.Abs<TimeUnit>(0.999, SECOND), this, this, "drawGraphs",
535 null);
536 }
537 catch (SimRuntimeException | NamingException | NetworkException | GTUException | OTSGeometryException exception)
538 {
539 exception.printStackTrace();
540 }
541 }
542
543
544
545
546 public List<Lane> getPath()
547 {
548 return new ArrayList<Lane>(this.path);
549 }
550
551
552
553
554 protected final void drawGraphs()
555 {
556 for (LaneBasedGTUSampler contourPlot : this.contourPlots)
557 {
558 contourPlot.reGraph();
559 }
560 for (TrajectoryPlot trajectoryPlot : this.trajectoryPlots)
561 {
562 trajectoryPlot.reGraph();
563 }
564
565 try
566 {
567 this.simulator.scheduleEventAbs(new Time.Abs(this.simulator.getSimulatorTime().get().getSI() + 1, SECOND),
568 this, this, "drawGraphs", null);
569 }
570 catch (SimRuntimeException exception)
571 {
572 exception.printStackTrace();
573 }
574
575 }
576
577
578
579
580
581
582
583 protected final void generateCar(final Lane lane, final Length.Rel initialPosition) throws GTUException
584 {
585 boolean generateTruck = this.randomGenerator.nextDouble() > this.carProbability;
586 Speed initialSpeed = new Speed(0, KM_PER_HOUR);
587 Set<DirectedLanePosition> initialPositions = new LinkedHashSet<>(1);
588 initialPositions.add(new DirectedLanePosition(lane, initialPosition, GTUDirectionality.DIR_PLUS));
589 try
590 {
591 Length.Rel vehicleLength = new Length.Rel(generateTruck ? 15 : 4, METER);
592 GTUFollowingModelOld gtuFollowingModel =
593 generateTruck ? this.carFollowingModelTrucks : this.carFollowingModelCars;
594 if (null == gtuFollowingModel)
595 {
596 throw new GTUException("gtuFollowingModel is null");
597 }
598 LaneBasedBehavioralCharacteristics drivingCharacteristics =
599 new LaneBasedBehavioralCharacteristics(gtuFollowingModel, this.laneChangeModel);
600 LaneBasedStrategicalPlanner strategicalPlanner =
601 new LaneBasedStrategicalRoutePlanner(drivingCharacteristics, new LaneBasedGTUFollowingTacticalPlanner());
602 new LaneBasedIndividualGTU("" + (++this.carsCreated), this.gtuType, initialPositions, initialSpeed,
603 vehicleLength, new Length.Rel(1.8, METER), new Speed(200, KM_PER_HOUR), this.simulator,
604 strategicalPlanner, new LanePerceptionFull(), DefaultCarAnimation.class, this.gtuColorer, this.network);
605 }
606 catch (NamingException | SimRuntimeException | NetworkException | OTSGeometryException exception)
607 {
608 throw new GTUException(exception);
609 }
610 }
611
612
613 @Override
614 public SimulatorInterface<Abs<TimeUnit>, Rel<TimeUnit>, OTSSimTimeDouble> getSimulator() throws RemoteException
615 {
616 return null;
617 }
618
619
620
621
622 public final ArrayList<LaneBasedGTUSampler> getPlots()
623 {
624 return this.contourPlots;
625 }
626
627
628
629
630 public final ArrayList<TrajectoryPlot> getTrajectoryPlots()
631 {
632 return this.trajectoryPlots;
633 }
634
635
636
637
638 public final Length.Rel getMinimumDistance()
639 {
640 return this.minimumDistance;
641 }
642
643 }