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