View Javadoc
1   package org.opentrafficsim.demo;
2   
3   import java.io.IOException;
4   import java.util.ArrayList;
5   import java.util.List;
6   import java.util.Random;
7   
8   import org.djunits.unit.DirectionUnit;
9   import org.djunits.unit.LengthUnit;
10  import org.djunits.unit.util.UNITS;
11  import org.djunits.value.vdouble.scalar.Acceleration;
12  import org.djunits.value.vdouble.scalar.Direction;
13  import org.djunits.value.vdouble.scalar.Duration;
14  import org.djunits.value.vdouble.scalar.Length;
15  import org.djunits.value.vdouble.scalar.Speed;
16  import org.djutils.draw.point.Point2d;
17  import org.djutils.traceverifier.TraceVerifier;
18  import org.opentrafficsim.base.parameters.Parameters;
19  import org.opentrafficsim.core.definitions.DefaultsNl;
20  import org.opentrafficsim.core.dsol.AbstractOtsModel;
21  import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
22  import org.opentrafficsim.core.gtu.Gtu;
23  import org.opentrafficsim.core.gtu.GtuException;
24  import org.opentrafficsim.core.gtu.GtuType;
25  import org.opentrafficsim.core.network.NetworkException;
26  import org.opentrafficsim.core.network.Node;
27  import org.opentrafficsim.core.network.route.Route;
28  import org.opentrafficsim.road.definitions.DefaultsRoadNl;
29  import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
30  import org.opentrafficsim.road.gtu.lane.LaneBookkeeping;
31  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.Lmrs;
32  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.LmrsFactory;
33  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlanner;
34  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlannerFactory;
35  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalRoutePlannerFactory;
36  import org.opentrafficsim.road.network.RoadNetwork;
37  import org.opentrafficsim.road.network.factory.LaneFactory;
38  import org.opentrafficsim.road.network.lane.Lane;
39  import org.opentrafficsim.road.network.lane.LanePosition;
40  import org.opentrafficsim.road.network.lane.LaneType;
41  
42  import nl.tudelft.simulation.dsol.SimRuntimeException;
43  import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterBoolean;
44  import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterDouble;
45  import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterDoubleScalar;
46  import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterException;
47  import nl.tudelft.simulation.dsol.model.inputparameters.InputParameterMap;
48  import nl.tudelft.simulation.jstats.streams.MersenneTwister;
49  import nl.tudelft.simulation.jstats.streams.StreamInterface;
50  
51  /**
52   * Simulate traffic on a circular, two-lane road.
53   * <p>
54   * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
55   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
56   * </p>
57   * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
58   */
59  public class CircularRoadModel extends AbstractOtsModel implements UNITS
60  {
61      /** Number of cars created. */
62      private int carsCreated = 0;
63  
64      /** The probability that the next generated GTU is a passenger car. */
65      private double carProbability;
66  
67      /** Minimum distance. */
68      private Length minimumDistance = new Length(0, METER);
69  
70      /** The speed limit. */
71      private Speed speedLimit = new Speed(100, KM_PER_HOUR);
72  
73      /** The sequence of Lanes that all vehicles will follow. */
74      private List<List<Lane>> paths = new ArrayList<>();
75  
76      /** The random number generator used to decide what kind of GTU to generate etc. */
77      private StreamInterface stream = new MersenneTwister(12345);
78  
79      /** Strategical planner generator for cars. */
80      private LaneBasedStrategicalPlannerFactory<?> strategicalPlannerGeneratorCars = null;
81  
82      /** Strategical planner generator for trucks. */
83      private LaneBasedStrategicalPlannerFactory<?> strategicalPlannerGeneratorTrucks = null;
84  
85      /** Car parameters. */
86      private Parameters parametersCar;
87  
88      /** Truck parameters. */
89      private Parameters parametersTruck;
90  
91      /** The RoadNetwork. */
92      private final RoadNetwork network;
93  
94      /**
95       * Constructor.
96       * @param simulator the simulator for this model
97       */
98      public CircularRoadModel(final OtsSimulatorInterface simulator)
99      {
100         super(simulator);
101         this.network = new RoadNetwork("network", simulator);
102         makeInputParameterMap();
103     }
104 
105     /**
106      * Make a map of input parameters for this demo.
107      */
108     public void makeInputParameterMap()
109     {
110         try
111         {
112             InputParameterHelper.makeInputParameterMapCarTruck(this.inputParameterMap, 1.0);
113 
114             InputParameterMap genericMap = null;
115             if (this.inputParameterMap.getValue().containsKey("generic"))
116             {
117                 genericMap = (InputParameterMap) this.inputParameterMap.get("generic");
118             }
119             else
120             {
121                 genericMap = new InputParameterMap("generic", "Generic", "Generic parameters", 1.0);
122                 this.inputParameterMap.add(genericMap);
123             }
124 
125             genericMap.add(new InputParameterDoubleScalar<LengthUnit, Length>("trackLength", "Track length",
126                     "Track length (circumfence of the track)", Length.ofSI(1000.0), Length.ofSI(500.0), Length.ofSI(2000.0),
127                     true, true, "%.0f", 1.5));
128             genericMap.add(new InputParameterDouble("densityMean", "Mean density (veh / km)",
129                     "mean density of the vehicles (vehicles per kilometer)", 30.0, 5.0, 45.0, true, true, "%.0f", 2.0));
130             genericMap.add(new InputParameterDouble("densityVariability", "Density variability",
131                     "Variability of the denisty: variability * (headway - 20) meters", 0.0, 0.0, 1.0, true, true, "%.00f",
132                     3.0));
133             genericMap.add(new InputParameterBoolean("gradualLaneChange", "Gradual lane change",
134                     "Gradual lane change when true; instantaneous lane change when false", true, 4.0));
135         }
136         catch (InputParameterException exception)
137         {
138             exception.printStackTrace();
139         }
140     }
141 
142     /**
143      * Returns path.
144      * @param index the rank number of the path
145      * @return the set of lanes for the specified index
146      */
147     public List<Lane> getPath(final int index)
148     {
149         return this.paths.get(index);
150     }
151 
152     /**
153      * Sample the state of the simulation.
154      * @param tv sampler or verifier of the state
155      */
156     public void sample(final TraceVerifier tv)
157     {
158         try
159         {
160             StringBuilder state = new StringBuilder();
161             for (Gtu gtu : this.network.getGTUs())
162             {
163                 LaneBasedGtu lbg = (LaneBasedGtu) gtu;
164                 state.append(String.format("%s: %130.130s ", lbg.getId(), lbg.getLocation().toString()));
165             }
166 
167             tv.sample(this.simulator.getSimulatorTime().toString(), state.toString());
168             this.simulator.scheduleEventRel(Duration.ONE, () -> sample(tv));
169         }
170         catch (IOException e)
171         {
172             e.printStackTrace();
173         }
174     }
175 
176     @Override
177     public void constructModel() throws SimRuntimeException
178     {
179         try
180         {
181             // TraceVerifier tv = new TraceVerifier("C:/Temp/circularRoadTrace.txt");
182             // this.simulator.scheduleEventRel(new Duration(1, DurationUnit.SECOND), this, this, "sample", new Object[] { tv });
183             // TraceVerifier tv = new TraceVerifier("C:/Temp/circularRoadTraceEndState.txt");
184             // this.simulator.scheduleEventRel(new Duration(3599.99, DurationUnit.SECOND), this, this, "sample",
185             // new Object[] { tv });
186             final int laneCount = 2;
187             for (int laneIndex = 0; laneIndex < laneCount; laneIndex++)
188             {
189                 this.paths.add(new ArrayList<Lane>());
190             }
191 
192             this.carProbability = (double) getInputParameter("generic.carProbability");
193             double radius = ((Length) getInputParameter("generic.trackLength")).si / 2 / Math.PI;
194             double headway = 1000.0 / (double) getInputParameter("generic.densityMean");
195             double headwayVariability = (double) getInputParameter("generic.densityVariability");
196 
197             this.parametersCar = InputParameterHelper.getParametersCar(getInputParameterMap());
198             this.parametersTruck = InputParameterHelper.getParametersTruck(getInputParameterMap());
199 
200             this.strategicalPlannerGeneratorCars =
201                     new LaneBasedStrategicalRoutePlannerFactory(new LmrsFactory<>(Lmrs::new).setStream(this.stream));
202             this.strategicalPlannerGeneratorTrucks =
203                     new LaneBasedStrategicalRoutePlannerFactory(new LmrsFactory<>(Lmrs::new).setStream(this.stream));
204 
205             GtuType gtuType = DefaultsNl.CAR;
206             LaneType laneType = DefaultsRoadNl.TWO_WAY_LANE;
207             Node start = new Node(this.network, "Start", new Point2d(radius, 0), new Direction(90, DirectionUnit.EAST_DEGREE));
208             Node halfway =
209                     new Node(this.network, "Halfway", new Point2d(-radius, 0), new Direction(270, DirectionUnit.EAST_DEGREE));
210 
211             Point2d[] coordsHalf1 = new Point2d[127];
212             for (int i = 0; i < coordsHalf1.length; i++)
213             {
214                 double angle = Math.PI * i / (coordsHalf1.length - 1);
215                 coordsHalf1[i] = new Point2d(radius * Math.cos(angle), radius * Math.sin(angle));
216             }
217             Lane[] lanes1 = LaneFactory.makeMultiLane(this.network, "FirstHalf", start, halfway, coordsHalf1, laneCount,
218                     laneType, this.speedLimit, this.simulator, DefaultsNl.VEHICLE);
219             Point2d[] coordsHalf2 = new Point2d[127];
220             for (int i = 0; i < coordsHalf2.length; i++)
221             {
222                 double angle = Math.PI + Math.PI * i / (coordsHalf2.length - 1);
223                 coordsHalf2[i] = new Point2d(radius * Math.cos(angle), radius * Math.sin(angle));
224             }
225             Lane[] lanes2 = LaneFactory.makeMultiLane(this.network, "SecondHalf", halfway, start, coordsHalf2, laneCount,
226                     laneType, this.speedLimit, this.simulator, DefaultsNl.VEHICLE);
227             for (int laneIndex = 0; laneIndex < laneCount; laneIndex++)
228             {
229                 this.paths.get(laneIndex).add(lanes1[laneIndex]);
230                 this.paths.get(laneIndex).add(lanes2[laneIndex]);
231             }
232             // Put the (not very evenly spaced) cars on the track
233             double variability = (headway - 20) * headwayVariability;
234             Random random = new Random(12345);
235             for (int laneIndex = 0; laneIndex < laneCount; laneIndex++)
236             {
237                 double lane1Length = lanes1[laneIndex].getLength().getSI();
238                 double trackLength = lane1Length + lanes2[laneIndex].getLength().getSI();
239                 for (double pos = 0; pos <= trackLength - headway - variability;)
240                 {
241                     Lane lane = pos >= lane1Length ? lanes2[laneIndex] : lanes1[laneIndex];
242                     // Actual headway is uniformly distributed around headway
243                     double laneRelativePos = pos > lane1Length ? pos - lane1Length : pos;
244                     double actualHeadway = headway + (random.nextDouble() * 2 - 1) * variability;
245                     generateGTU(new Length(laneRelativePos, METER), lane, gtuType);
246                     pos += actualHeadway;
247                 }
248             }
249         }
250         catch (Exception exception)
251         {
252             exception.printStackTrace();
253         }
254     }
255 
256     /**
257      * Generate one gtu.
258      * @param initialPosition the initial position of the new cars
259      * @param lane the lane on which the new cars are placed
260      * @param gtuType the type of the new cars
261      * @throws SimRuntimeException cannot happen
262      * @throws NetworkException on network inconsistency
263      * @throws GtuException when something goes wrong during construction of the car
264      * @throws InputParameterException when generic.gradualLaneChange is not set
265      */
266     protected final void generateGTU(final Length initialPosition, final Lane lane, final GtuType gtuType)
267             throws GtuException, NetworkException, SimRuntimeException, InputParameterException
268     {
269         // GTU itself
270         boolean generateTruck = this.stream.nextDouble() > this.carProbability;
271         Length vehicleLength = new Length(generateTruck ? 15 : 4, METER);
272         LaneBasedGtu gtu = new LaneBasedGtu("" + (++this.carsCreated), gtuType, vehicleLength, new Length(1.8, METER),
273                 new Speed(200, KM_PER_HOUR), vehicleLength.times(0.5), this.network);
274         gtu.setParameters(generateTruck ? this.parametersTruck : this.parametersCar);
275         gtu.setNoLaneChangeDistance(Length.ZERO);
276         gtu.setBookkeeping(
277                 ((boolean) getInputParameter("generic.gradualLaneChange")) ? LaneBookkeeping.EDGE : LaneBookkeeping.INSTANT);
278         gtu.setMaximumAcceleration(Acceleration.ofSI(3.0));
279         gtu.setMaximumDeceleration(Acceleration.ofSI(-8.0));
280 
281         // strategical planner
282         LaneBasedStrategicalPlanner strategicalPlanner;
283         Route route = null;
284         if (!generateTruck)
285         {
286             strategicalPlanner = this.strategicalPlannerGeneratorCars.create(gtu, route, null, null);
287         }
288         else
289         {
290             strategicalPlanner = this.strategicalPlannerGeneratorTrucks.create(gtu, route, null, null);
291         }
292 
293         // init
294         Speed initialSpeed = new Speed(0, KM_PER_HOUR);
295         gtu.init(strategicalPlanner, new LanePosition(lane, initialPosition).getLocation(), initialSpeed);
296     }
297 
298     @Override
299     public RoadNetwork getNetwork()
300     {
301         return this.network;
302     }
303 
304     /**
305      * Returns the minimum distance.
306      * @return minimumDistance
307      */
308     public final Length getMinimumDistance()
309     {
310         return this.minimumDistance;
311     }
312 
313 }