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