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