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