View Javadoc
1   package org.opentrafficsim.road.gtu;
2   
3   import static org.junit.Assert.assertEquals;
4   import static org.junit.Assert.assertTrue;
5   import static org.junit.Assert.fail;
6   
7   import java.util.ArrayList;
8   import java.util.LinkedHashSet;
9   import java.util.List;
10  import java.util.Map;
11  import java.util.Set;
12  
13  import org.djunits.unit.DurationUnit;
14  import org.djunits.unit.LengthUnit;
15  import org.djunits.unit.TimeUnit;
16  import org.djunits.unit.util.UNITS;
17  import org.djunits.value.vdouble.scalar.Acceleration;
18  import org.djunits.value.vdouble.scalar.Direction;
19  import org.djunits.value.vdouble.scalar.Duration;
20  import org.djunits.value.vdouble.scalar.Length;
21  import org.djunits.value.vdouble.scalar.Speed;
22  import org.djunits.value.vdouble.scalar.Time;
23  import org.junit.Test;
24  import org.opentrafficsim.base.parameters.Parameters;
25  import org.opentrafficsim.core.definitions.DefaultsNl;
26  import org.opentrafficsim.core.dsol.AbstractOtsModel;
27  import org.opentrafficsim.core.dsol.OtsModelInterface;
28  import org.opentrafficsim.core.dsol.OtsSimulator;
29  import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
30  import org.opentrafficsim.core.geometry.OtsPoint3d;
31  import org.opentrafficsim.core.gtu.GtuException;
32  import org.opentrafficsim.core.gtu.GtuType;
33  import org.opentrafficsim.core.gtu.RelativePosition;
34  import org.opentrafficsim.core.network.Node;
35  import org.opentrafficsim.core.network.route.Route;
36  import org.opentrafficsim.road.DefaultTestParameters;
37  import org.opentrafficsim.road.definitions.DefaultsRoadNl;
38  import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
39  import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedCfLcTacticalPlanner;
40  import org.opentrafficsim.road.gtu.lane.tactical.following.FixedAccelerationModel;
41  import org.opentrafficsim.road.gtu.lane.tactical.following.GtuFollowingModelOld;
42  import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.FixedLaneChangeModel;
43  import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.LaneChangeModel;
44  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlanner;
45  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalRoutePlanner;
46  import org.opentrafficsim.road.network.RoadNetwork;
47  import org.opentrafficsim.road.network.factory.LaneFactory;
48  import org.opentrafficsim.road.network.lane.Lane;
49  import org.opentrafficsim.road.network.lane.LanePosition;
50  import org.opentrafficsim.road.network.lane.LaneType;
51  
52  import nl.tudelft.simulation.dsol.SimRuntimeException;
53  
54  /**
55   * Test the various methods of an AbstractLaneBasedGtu.<br>
56   * As abstract classes cannot be directly
57   * <p>
58   * Copyright (c) 2013-2023 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://github.com/averbraeck">Alexander Verbraeck</a>
62   * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
63   */
64  public class AbstractLaneBasedGtuTest implements UNITS
65  {
66      /**
67       * Test that the constructor puts the supplied values in the correct fields, then check the motion of the GTU.
68       * @throws Exception when something goes wrong (should not happen)
69       */
70      @Test
71      public final void abstractLaneBasedGtuTest() throws Exception
72      {
73          // This initialization code should probably be moved to a helper method that will be used in several tests.
74          // First we need a set of Lanes
75          // To create Lanes we need Nodes and a LaneType
76          // And a simulator, but for that we first need something that implements OtsModelInterface
77          OtsSimulatorInterface simulator = new OtsSimulator("abstractLaneBasedGtuTest");
78          RoadNetwork network = new RoadNetwork("lane base gtu test network", simulator);
79          OtsModelInterface model = new DummyModel(simulator);
80          simulator.initialize(Time.ZERO, Duration.ZERO, new Duration(1, DurationUnit.HOUR), model);
81          Node nodeAFrom = new Node(network, "AFrom", new OtsPoint3d(0, 0, 0), Direction.ZERO);
82          Node nodeATo = new Node(network, "ATo", new OtsPoint3d(1000, 0, 0), Direction.ZERO);
83          GtuType gtuType = DefaultsNl.CAR;
84          LaneType laneType = DefaultsRoadNl.TWO_WAY_LANE;
85  
86          Lane[] lanesGroupA = LaneFactory.makeMultiLane(network, "A", nodeAFrom, nodeATo, null, 3, laneType,
87                  new Speed(100, KM_PER_HOUR), simulator, DefaultsNl.VEHICLE);
88          // A GTU can exist on several lanes at once; create another lane group to test that
89          Node nodeBFrom = new Node(network, "BFrom", new OtsPoint3d(10, 0, 0), Direction.ZERO);
90          Node nodeBTo = new Node(network, "BTo", new OtsPoint3d(1000, 0, 0), Direction.ZERO);
91          Lane[] lanesGroupB = LaneFactory.makeMultiLane(network, "B", nodeBFrom, nodeBTo, null, 3, laneType,
92                  new Speed(100, KM_PER_HOUR), simulator, DefaultsNl.VEHICLE);
93          Set<LanePosition> initialLongitudinalPositions = new LinkedHashSet<>(2);
94  
95          Length positionA = new Length(100, METER);
96          initialLongitudinalPositions.add(new LanePosition(lanesGroupA[1], positionA));
97          Length positionB = new Length(90, METER);
98          initialLongitudinalPositions.add(new LanePosition(lanesGroupB[1], positionB));
99          // A Car needs a CarFollowingModel
100         Acceleration acceleration = new Acceleration(2, METER_PER_SECOND_2);
101         Duration validFor = new Duration(10, SECOND);
102         GtuFollowingModelOld gfm = new FixedAccelerationModel(acceleration, validFor);
103         // A Car needs a lane change model
104         // AbstractLaneChangeModel laneChangeModel = new Egoistic();
105         LaneChangeModel laneChangeModel = new FixedLaneChangeModel(null);
106         // A Car needs an initial speed
107         Speed initialSpeed = new Speed(50, KM_PER_HOUR);
108         // Length of the Car
109         Length carLength = new Length(4, METER);
110         // Width of the Car
111         Length carWidth = new Length(1.8, METER);
112         // Maximum speed of the Car
113         Speed maximumSpeed = new Speed(200, KM_PER_HOUR);
114         // ID of the Car
115         String carID = "theCar";
116         // List of Nodes visited by the Car
117         List<Node> nodeList = new ArrayList<Node>();
118         nodeList.add(nodeAFrom);
119         nodeList.add(nodeATo);
120         // Route of the Car
121         Route route = new Route("Route", gtuType, nodeList);
122         // Now we can make a GTU
123         Parameters parameters = DefaultTestParameters.create(); // new
124                                                                 // BehavioralCharacteristics();
125         // LaneBasedBehavioralCharacteristics drivingCharacteristics =
126         // new LaneBasedBehavioralCharacteristics(gfm, laneChangeModel);
127         LaneBasedGtu car = new LaneBasedGtu(carID, gtuType, carLength, carWidth, maximumSpeed, carLength.times(0.5), network);
128         LaneBasedStrategicalPlanner strategicalPlanner =
129                 new LaneBasedStrategicalRoutePlanner(new LaneBasedCfLcTacticalPlanner(gfm, laneChangeModel, car), car);
130         car.setParameters(parameters);
131         car.init(strategicalPlanner, initialLongitudinalPositions, initialSpeed);
132         // Now we can verify the various fields in the newly created Car
133         assertEquals("ID of the car should be identical to the provided one", carID, car.getId());
134         // TODO: Test with gfm as part of tactical planner
135         // assertEquals("GTU following model should be identical to the provided one", gfm, car
136         // .getBehavioralCharacteristics().getGtuFollowingModel());
137         assertEquals("Width should be identical to the provided width", carWidth, car.getWidth());
138         assertEquals("Length should be identical to the provided length", carLength, car.getLength());
139         assertEquals("GTU type should be identical to the provided one", gtuType, car.getType());
140         assertEquals("front in lanesGroupA[1] is positionA", positionA.getSI(),
141                 car.position(lanesGroupA[1], car.getReference()).getSI(), 0.0001);
142         assertEquals("front in lanesGroupB[1] is positionB", positionB.getSI(),
143                 car.position(lanesGroupB[1], car.getReference()).getSI(), 0.0001);
144         // assertEquals("acceleration is 0", 0, car.getAcceleration().getSI(), 0.00001);
145         // edit wouter schakel: fixed acceleration model has a=2.0m/s^2, first plan is made during initialization
146         assertEquals("acceleration is 2", 2.0, car.getAcceleration().getSI(), 0.00001);
147         assertEquals("longitudinal speed is " + initialSpeed, initialSpeed.getSI(), car.getSpeed().getSI(), 0.00001);
148         assertEquals("lastEvaluation time is 0", 0, car.getOperationalPlan().getStartTime().getSI(), 0.00001);
149         // Test the position(Lane, RelativePosition) method
150         // WS: Removed as null check has been removed from position(...)
151         // try
152         // {
153         // car.position(null, car.getFront());
154         // fail("position on null lane should have thrown a NetworkException");
155         // }
156         // catch (GTUException ne)
157         // {
158         // // Ignore
159         // }
160         for (Lane[] laneGroup : new Lane[][] {lanesGroupA, lanesGroupB})
161         {
162             for (int laneIndex = 0; laneIndex < laneGroup.length; laneIndex++)
163             {
164                 Lane lane = laneGroup[laneIndex];
165                 boolean expectException = 1 != laneIndex;
166                 for (RelativePosition relativePosition : new RelativePosition[] {car.getFront(), car.getReference(),
167                         car.getRear()})
168                 {
169                     // System.out.println("lane:" + lane + ", expectedException: " + expectException
170                     // + ", relativePostion: " + relativePosition);
171                     try
172                     {
173                         Length position = car.position(lane, relativePosition);
174                         if (expectException)
175                         {
176                             // System.out.println("position: " + position);
177                             fail("Calling position on lane that the car is NOT on should have thrown a NetworkException");
178                         }
179                         else
180                         {
181                             Length expectedPosition = laneGroup == lanesGroupA ? positionA : positionB;
182                             expectedPosition = expectedPosition.plus(relativePosition.getDx());
183                             // System.out.println("reported position: " + position);
184                             // System.out.println("expected position: " + expectedPosition);
185                             assertEquals("Position should match initial position", expectedPosition.getSI(), position.getSI(),
186                                     0.0001);
187                         }
188                     }
189                     catch (GtuException ne)
190                     {
191                         if (!expectException)
192                         {
193                             System.out.println(ne);
194                             fail("Calling position on lane that the car is on should NOT have thrown a NetworkException");
195                         }
196                     }
197                 }
198             }
199         }
200         // Assign a movement to the car (10 seconds of acceleration of 2 m/s/s)
201         // scheduled event that moves the car at t=0
202         assertEquals("lastEvaluation time is 0", 0, car.getOperationalPlan().getStartTime().getSI(), 0.00001);
203         // assertEquals("nextEvaluation time is 0", 0, car.getOperationalPlan().getEndTime().getSI(), 0.00001);
204         // edit wouter schakel: fixed acceleration model has t=10s, first plan is made during initialization
205         assertEquals("nextEvaluation time is 10", 10.0, car.getOperationalPlan().getEndTime().getSI(), 0.00001);
206         // Increase the simulator clock in small steps and verify the both positions on all lanes at each step
207         double step = 0.01d;
208         for (int i = 0;; i++)
209         {
210             Time stepTime = new Time(i * step, TimeUnit.BASE_SECOND);
211             if (stepTime.getSI() > validFor.getSI())
212             {
213                 break;
214             }
215             if (stepTime.getSI() > 0.5)
216             {
217                 step = 0.1; // Reduce testing time by increasing the step size
218             }
219             // System.out.println("Simulating until " + stepTime.getSI());
220             simulator.runUpTo(stepTime);
221             while (simulator.isStartingOrRunning())
222             {
223                 try
224                 {
225                     Thread.sleep(1);
226                 }
227                 catch (InterruptedException ie)
228                 {
229                     ie = null; // ignore
230                 }
231             }
232             // Debugging code that helped locate a problem in the DSOL runUpTo code.
233             // System.out.println("stepTime is " + stepTime);
234             // System.out.println("Car simulator time " + car.getSimulator().getSimulatorTime());
235             // System.out.println("Simulator time is now " + simulator.getSimulatorTime());
236             // if (simulator != car.getSimulator())
237             // {
238             // System.err.println("Car runs on a different simulator!");
239             // }
240             // System.out.println("operational plan is " + car.getOperationalPlan());
241             // System.out.println("operational plan end time is " + car.getOperationalPlan().getEndTime());
242             // car.getOperationalPlan().getEndTime();
243             // if (stepTime.getSI() > 0)
244             // {
245             // assertEquals("nextEvaluation time is " + validFor, validFor.getSI(),
246             // car.getOperationalPlan().getEndTime().getSI(), 0.0001);
247             // assertEquals("acceleration is " + acceleration, acceleration.getSI(), car.getAcceleration().getSI(), 0.00001);
248             // }
249             Speed longitudinalSpeed = car.getSpeed();
250             double expectedLongitudinalSpeed = initialSpeed.getSI() + stepTime.getSI() * acceleration.getSI();
251             assertEquals("longitudinal speed is " + expectedLongitudinalSpeed, expectedLongitudinalSpeed,
252                     longitudinalSpeed.getSI(), 0.00001);
253             for (RelativePosition relativePosition : new RelativePosition[] {car.getFront(), car.getRear()})
254             {
255                 Map<Lane, Double> positions = car.fractionalPositions(relativePosition);
256                 assertEquals("Car should be in two lanes", 2, positions.size());
257                 Double pos = positions.get(lanesGroupA[1]);
258                 // System.out.println("Fractional positions: " + positions);
259                 assertTrue("Car should be in lane 1 of lane group A", null != pos);
260                 assertEquals("fractional position should be equal to result of fractionalPosition(lane, ...)", pos,
261                         car.fractionalPosition(lanesGroupA[1], relativePosition), 0.0000001);
262                 pos = positions.get(lanesGroupB[1]);
263                 assertTrue("Car should be in lane 1 of lane group B", null != pos);
264                 assertEquals("fractional position should be equal to result of fractionalPosition(lane, ...)", pos,
265                         car.fractionalPosition(lanesGroupB[1], relativePosition), 0.0000001);
266             }
267             for (Lane[] laneGroup : new Lane[][] {lanesGroupA, lanesGroupB})
268             {
269                 for (int laneIndex = 0; laneIndex < laneGroup.length; laneIndex++)
270                 {
271                     Lane lane = laneGroup[laneIndex];
272                     boolean expectException = 1 != laneIndex;
273                     for (RelativePosition relativePosition : new RelativePosition[] {car.getFront(), car.getReference(),
274                             car.getRear()})
275                     {
276                         // System.out.println("lane:" + lane + ", expectedException: " + expectException
277                         // + ", relativePostion: " + relativePosition);
278                         try
279                         {
280                             Length position = car.position(lane, relativePosition);
281                             if (expectException)
282                             {
283                                 // System.out.println("position: " + position);
284                                 fail("Calling position on lane that the car is NOT on should have thrown a "
285                                         + "NetworkException");
286                             }
287                             else
288                             {
289                                 Length expectedPosition = laneGroup == lanesGroupA ? positionA : positionB;
290                                 expectedPosition = expectedPosition
291                                         .plus(new Length(stepTime.getSI() * initialSpeed.getSI(), LengthUnit.SI));
292                                 expectedPosition = expectedPosition.plus(new Length(
293                                         0.5 * acceleration.getSI() * stepTime.getSI() * stepTime.getSI(), LengthUnit.SI));
294                                 expectedPosition = expectedPosition.plus(relativePosition.getDx());
295                                 // System.out.println("reported position: " + position);
296                                 // System.out.println("expected position: " + expectedPosition);
297                                 assertEquals("Position should match initial position", expectedPosition.getSI(),
298                                         position.getSI(), 0.0001);
299                             }
300                         }
301                         catch (GtuException ne)
302                         {
303                             if (!expectException)
304                             {
305                                 System.out.println(ne);
306                                 fail("Calling position on lane that the car is on should NOT have thrown a NetworkException");
307                             }
308                         }
309                         try
310                         {
311                             double fractionalPosition = car.fractionalPosition(lane, relativePosition);
312                             if (expectException)
313                             {
314                                 // System.out.println("position: " + position);
315                                 fail("Calling position on lane that the car is NOT on should have thrown a NetworkException");
316                             }
317                             else
318                             {
319                                 Length expectedPosition = laneGroup == lanesGroupA ? positionA : positionB;
320                                 expectedPosition = expectedPosition
321                                         .plus(new Length(stepTime.getSI() * initialSpeed.getSI(), LengthUnit.SI));
322                                 expectedPosition = expectedPosition.plus(new Length(
323                                         0.5 * acceleration.getSI() * stepTime.getSI() * stepTime.getSI(), LengthUnit.SI));
324                                 expectedPosition = expectedPosition.plus(relativePosition.getDx());
325                                 // System.out.println("reported position: " + position);
326                                 // System.out.println("expected position: " + expectedPosition);
327                                 double expectedFractionalPosition = expectedPosition.getSI() / lane.getLength().getSI();
328                                 assertEquals("Position should match initial position", expectedFractionalPosition,
329                                         fractionalPosition, 0.000001);
330                             }
331                         }
332                         catch (GtuException ne)
333                         {
334                             if (!expectException)
335                             {
336                                 System.out.println(ne);
337                                 fail("Calling fractionalPosition on lane that the car is on should NOT have thrown a "
338                                         + "NetworkException");
339                             }
340                         }
341                     }
342                 }
343             }
344         }
345         // A GTU can exist on several lanes at once; create another lane group to test that
346         Node nodeCFrom = new Node(network, "CFrom", new OtsPoint3d(10, 100, 0), Direction.ZERO);
347         Node nodeCTo = new Node(network, "CTo", new OtsPoint3d(1000, 0, 0), Direction.ZERO);
348         Lane[] lanesGroupC = LaneFactory.makeMultiLane(network, "C", nodeCFrom, nodeCTo, null, 3, laneType,
349                 new Speed(100, KM_PER_HOUR), simulator, DefaultsNl.VEHICLE);
350         for (RelativePosition relativePosition : new RelativePosition[] {car.getFront(), car.getRear()})
351         {
352             Map<Lane, Double> positions = car.fractionalPositions(relativePosition);
353             // wouter schakel assertEquals("Car should be in three lanes", 3, positions.size());
354             Double pos = positions.get(lanesGroupA[1]);
355             assertTrue("Car should be in lane 1 of lane group A", null != pos);
356             assertEquals("fractional position should be equal to result of fractionalPosition(lane, ...)", pos,
357                     car.fractionalPosition(lanesGroupA[1], relativePosition), 0.0000001);
358             pos = positions.get(lanesGroupB[1]);
359             assertTrue("Car should be in lane 1 of lane group B", null != pos);
360             assertEquals("fractional position should be equal to result of fractionalPosition(lane, ...)", pos,
361                     car.fractionalPosition(lanesGroupB[1], relativePosition), 0.0000001);
362             pos = positions.get(lanesGroupC[0]);
363         }
364         // wouter schakel car.leaveLane(lanesGroupA[1]);
365         for (RelativePosition relativePosition : new RelativePosition[] {car.getFront(), car.getRear()})
366         {
367             Map<Lane, Double> positions = car.fractionalPositions(relativePosition);
368             assertEquals("Car should be in two lanes", 2, positions.size());
369             Double pos = positions.get(lanesGroupB[1]);
370             assertTrue("Car should be in lane 1 of lane group B", null != pos);
371             assertEquals("fractional position should be equal to result of fractionalPosition(lane, ...)", pos,
372                     car.fractionalPosition(lanesGroupB[1], relativePosition), 0.0000001);
373             pos = positions.get(lanesGroupC[0]);
374             // wouter schakel assertTrue("Car should be in lane 0 of lane group C", null != pos);
375             // The next one fails - maybe I don't understand something - PK
376             // assertEquals("fractional position should be 0", 0,
377             // car.fractionalPosition(lanesGroupC[0], relativePosition), 0.0000001);
378             // wouter schakel assertEquals("fractional position should be equal to result of fractionalPosition(lane, ...)",
379             // pos,
380             // car.fractionalPosition(lanesGroupC[0], relativePosition), 0.0000001);
381         }
382         // TODO removeLane should throw an Error when the car is not on that lane (currently this is silently ignored)
383         // TODO figure out why the added lane has a non-zero position
384     }
385 }
386 
387 /**
388  * Dummy OtsModelInterface.
389  * <p>
390  * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
391  * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
392  * </p>
393  * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
394  */
395 class DummyModel extends AbstractOtsModel
396 {
397     /** */
398     private static final long serialVersionUID = 20150114L;
399 
400     /**
401      * @param simulator the simulator to use
402      */
403     DummyModel(final OtsSimulatorInterface simulator)
404     {
405         super(simulator);
406     }
407 
408     /** {@inheritDoc} */
409     @Override
410     public final void constructModel() throws SimRuntimeException
411     {
412         //
413     }
414 
415     /** {@inheritDoc} */
416     @Override
417     public final RoadNetwork getNetwork()
418     {
419         return null;
420     }
421 
422 }