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