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