View Javadoc
1   package org.opentrafficsim.road.gtu;
2   
3   import static org.junit.jupiter.api.Assertions.assertEquals;
4   import static org.junit.jupiter.api.Assertions.assertTrue;
5   
6   import java.util.ArrayList;
7   import java.util.LinkedHashSet;
8   import java.util.List;
9   import java.util.Set;
10  
11  import org.djunits.unit.DurationUnit;
12  import org.djunits.unit.LengthUnit;
13  import org.djunits.unit.util.UNITS;
14  import org.djunits.value.vdouble.scalar.Acceleration;
15  import org.djunits.value.vdouble.scalar.Direction;
16  import org.djunits.value.vdouble.scalar.Duration;
17  import org.djunits.value.vdouble.scalar.Length;
18  import org.djunits.value.vdouble.scalar.Speed;
19  import org.djunits.value.vdouble.scalar.Time;
20  import org.djutils.draw.point.Point2d;
21  import org.junit.jupiter.api.Test;
22  import org.opentrafficsim.base.parameters.Parameters;
23  import org.opentrafficsim.core.definitions.DefaultsNl;
24  import org.opentrafficsim.core.dsol.AbstractOtsModel;
25  import org.opentrafficsim.core.dsol.OtsModelInterface;
26  import org.opentrafficsim.core.dsol.OtsSimulator;
27  import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
28  import org.opentrafficsim.core.gtu.GtuType;
29  import org.opentrafficsim.core.gtu.RelativePosition;
30  import org.opentrafficsim.core.network.Node;
31  import org.opentrafficsim.core.network.route.Route;
32  import org.opentrafficsim.core.perception.HistoryManagerDevs;
33  import org.opentrafficsim.road.DefaultTestParameters;
34  import org.opentrafficsim.road.FixedCarFollowing;
35  import org.opentrafficsim.road.definitions.DefaultsRoadNl;
36  import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
37  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.Lmrs;
38  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.LmrsFactory;
39  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.LmrsFactory.Setting;
40  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlanner;
41  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalRoutePlanner;
42  import org.opentrafficsim.road.network.RoadNetwork;
43  import org.opentrafficsim.road.network.factory.LaneFactory;
44  import org.opentrafficsim.road.network.lane.Lane;
45  import org.opentrafficsim.road.network.lane.LanePosition;
46  import org.opentrafficsim.road.network.lane.LaneType;
47  
48  import nl.tudelft.simulation.dsol.SimRuntimeException;
49  
50  /**
51   * Test the various methods of an AbstractLaneBasedGtu.<br>
52   * As abstract classes cannot be directly
53   * <p>
54   * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
55   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
56   * </p>
57   * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
58   * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
59   */
60  public final class AbstractLaneBasedGtuTest implements UNITS
61  {
62  
63      /** */
64      private AbstractLaneBasedGtuTest()
65      {
66          // do not instantiate test class
67      }
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 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          RoadNetwork network = new RoadNetwork("lane base gtu test network", simulator);
82          OtsModelInterface model = new DummyModel(simulator);
83          simulator.initialize(Time.ZERO, Duration.ZERO, new Duration(1, DurationUnit.HOUR), model,
84                  HistoryManagerDevs.noHistory(simulator));
85          Node nodeAFrom = new Node(network, "AFrom", new Point2d(0, 0), Direction.ZERO);
86          Node nodeATo = new Node(network, "ATo", new Point2d(1000, 0), Direction.ZERO);
87          GtuType gtuType = DefaultsNl.CAR;
88          LaneType laneType = DefaultsRoadNl.TWO_WAY_LANE;
89  
90          Lane[] lanesGroupA = LaneFactory.makeMultiLane(network, "A", nodeAFrom, nodeATo, null, 3, laneType,
91                  new Speed(100, KM_PER_HOUR), simulator, DefaultsNl.VEHICLE);
92          // A GTU can exist on several lanes at once; create another lane group to test that
93          // Node nodeBFrom = new Node(network, "BFrom", new Point2d(10, 0), Direction.ZERO);
94          // Node nodeBTo = new Node(network, "BTo", new Point2d(1000, 0), Direction.ZERO);
95          // Lane[] lanesGroupB = LaneFactory.makeMultiLane(network, "B", nodeBFrom, nodeBTo, null, 3, laneType,
96          // new Speed(100, KM_PER_HOUR), simulator, DefaultsNl.VEHICLE);
97          Set<LanePosition> initialLongitudinalPositions = new LinkedHashSet<>(2);
98  
99          Length positionA = new Length(100, METER);
100         initialLongitudinalPositions.add(new LanePosition(lanesGroupA[1], positionA));
101         // Length positionB = new Length(90, METER);
102         // initialLongitudinalPositions.add(new LanePosition(lanesGroupB[1], positionB));
103         // A Car needs a CarFollowingModel
104         Acceleration acceleration = new Acceleration(2, METER_PER_SECOND_2);
105         Duration validFor = new Duration(0.5, SECOND);
106         // A Car needs a lane change model
107         // AbstractLaneChangeModel laneChangeModel = new Egoistic();
108         // A Car needs an initial speed
109         Speed initialSpeed = new Speed(50, KM_PER_HOUR);
110         // Length of the Car
111         Length carLength = new Length(4, METER);
112         // Width of the Car
113         Length carWidth = new Length(1.8, METER);
114         // Maximum speed of the Car
115         Speed maximumSpeed = new Speed(200, KM_PER_HOUR);
116         // ID of the Car
117         String carID = "theCar";
118         // List of Nodes visited by the Car
119         List<Node> nodeList = new ArrayList<Node>();
120         nodeList.add(nodeAFrom);
121         nodeList.add(nodeATo);
122         // Route of the Car
123         Route route = new Route("Route", gtuType, nodeList);
124         // Now we can make a GTU
125         Parameters parameters = DefaultTestParameters.create(); // new
126                                                                 // BehavioralCharacteristics();
127         // LaneBasedBehavioralCharacteristics drivingCharacteristics =
128         // new LaneBasedBehavioralCharacteristics(gfm, laneChangeModel);
129         LaneBasedGtu car = new LaneBasedGtu(carID, gtuType, carLength, carWidth, maximumSpeed, carLength.times(0.5), network);
130         LaneBasedStrategicalPlanner strategicalPlanner = new LaneBasedStrategicalRoutePlanner(
131                 new LmrsFactory<>(Lmrs::new)
132                         .set(Setting.CAR_FOLLOWING_MODEL, (h, v) -> new FixedCarFollowing(acceleration).get()).create(car),
133                 car);
134         car.setParameters(parameters);
135         car.init(strategicalPlanner, new LanePosition(lanesGroupA[1], positionA).getLocation(), initialSpeed);
136         // Now we can verify the various fields in the newly created Car
137         assertEquals(carID, car.getId(), "ID of the car should be identical to the provided one");
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(carWidth, car.getWidth(), "Width should be identical to the provided width");
142         assertEquals(carLength, car.getLength(), "Length should be identical to the provided length");
143         assertEquals(gtuType, car.getType(), "GTU type should be identical to the provided one");
144         assertEquals(positionA.getSI(), car.getPosition(lanesGroupA[1], car.getReference()).getSI(), 0.0001,
145                 "front in lanesGroupA[1] is positionA");
146         // assertEquals("acceleration is 0", 0, car.getAcceleration().getSI(), 0.00001);
147         // edit wouter schakel: fixed acceleration model has a=2.0m/s^2, first plan is made during initialization
148         assertEquals(2.0, car.getAcceleration().getSI(), 0.00001, "acceleration is 2");
149         assertEquals(initialSpeed.getSI(), car.getSpeed().getSI(), 0.00001, "longitudinal speed is " + initialSpeed);
150         assertEquals(0, car.getOperationalPlan().getStartTime().getSI(), 0.00001, "lastEvaluation time is 0");
151         // Test the position(Lane, RelativePosition) method
152         // WS: Removed as null check has been removed from position(...)
153         // try
154         // {
155         // car.position(null, car.getFront());
156         // fail("position on null lane should have thrown a NetworkException");
157         // }
158         // catch (GTUException ne)
159         // {
160         // // Ignore
161         // }
162         for (Lane[] laneGroup : new Lane[][] {lanesGroupA})// , lanesGroupB})
163         {
164             for (int laneIndex = 0; laneIndex < laneGroup.length; laneIndex++)
165             {
166                 Lane lane = laneGroup[laneIndex];
167                 for (RelativePosition relativePosition : new RelativePosition[] {car.getFront(), car.getReference(),
168                         car.getRear()})
169                 {
170                     // System.out.println("lane:" + lane + ", expectedException: " + expectException
171                     // + ", relativePostion: " + relativePosition);
172                     Length position = car.getPosition(lane, relativePosition);
173                     Length expectedPosition = positionA;// laneGroup == lanesGroupA ? positionA : positionB;
174                     expectedPosition = expectedPosition.plus(relativePosition.dx());
175                     // System.out.println("reported position: " + position);
176                     // System.out.println("expected position: " + expectedPosition);
177                     assertEquals(expectedPosition.getSI(), position.getSI(), 0.0001, "Position should match initial position");
178                 }
179             }
180         }
181         // Assign a movement to the car (10 seconds of acceleration of 2 m/s/s)
182         // scheduled event that moves the car at t=0
183         assertEquals(0, car.getOperationalPlan().getStartTime().getSI(), 0.00001, "lastEvaluation time is 0");
184         // assertEquals("nextEvaluation time is 0", 0, car.getOperationalPlan().getEndTime().getSI(), 0.00001);
185         // edit wouter schakel: fixed acceleration model has t=10s, first plan is made during initialization
186         assertEquals(0.5, car.getOperationalPlan().getEndTime().getSI(), 0.00001, "nextEvaluation time is 10");
187         // Increase the simulator clock in small steps and verify the both positions on all lanes at each step
188         double step = 0.01d;
189         for (int i = 0;; i++)
190         {
191             Duration stepTime = Duration.ofSI(i * step);
192             if (stepTime.getSI() > validFor.getSI())
193             {
194                 break;
195             }
196             if (stepTime.getSI() > 0.5)
197             {
198                 step = 0.1; // Reduce testing time by increasing the step size
199             }
200             // System.out.println("Simulating until " + stepTime.getSI());
201             simulator.runUpTo(stepTime);
202             while (simulator.isStartingOrRunning())
203             {
204                 try
205                 {
206                     Thread.sleep(1);
207                 }
208                 catch (InterruptedException ie)
209                 {
210                     ie = null; // ignore
211                 }
212             }
213             // Debugging code that helped locate a problem in the DSOL runUpTo code.
214             // System.out.println("stepTime is " + stepTime);
215             // System.out.println("Car simulator time " + car.getSimulator().getSimulatorTime());
216             // System.out.println("Simulator time is now " + simulator.getSimulatorTime());
217             // if (simulator != car.getSimulator())
218             // {
219             // System.err.println("Car runs on a different simulator!");
220             // }
221             // System.out.println("operational plan is " + car.getOperationalPlan());
222             // System.out.println("operational plan end time is " + car.getOperationalPlan().getEndTime());
223             // car.getOperationalPlan().getEndTime();
224             // if (stepTime.getSI() > 0)
225             // {
226             // assertEquals("nextEvaluation time is " + validFor, validFor.getSI(),
227             // car.getOperationalPlan().getEndTime().getSI(), 0.0001);
228             // assertEquals("acceleration is " + acceleration, acceleration.getSI(), car.getAcceleration().getSI(), 0.00001);
229             // }
230             Speed longitudinalSpeed = car.getSpeed();
231             double expectedLongitudinalSpeed = initialSpeed.getSI() + stepTime.getSI() * acceleration.getSI();
232             assertEquals(expectedLongitudinalSpeed, longitudinalSpeed.getSI(), 0.00001,
233                     "longitudinal speed is " + expectedLongitudinalSpeed);
234             for (RelativePosition relativePosition : new RelativePosition[] {car.getFront(), car.getRear()})
235             {
236                 LanePosition pos = car.getPosition();
237                 // System.out.println("Fractional positions: " + positions);
238                 assertTrue(null != pos, "Car should be in lane 1 of lane group A");
239                 assertEquals(pos.getFraction() + relativePosition.dx().si / lanesGroupA[1].getLength().si,
240                         car.getPosition(lanesGroupA[1], relativePosition).si / lanesGroupA[1].getLength().si, 0.0000001,
241                         "fractional position should be equal to result of fractionalPosition(lane, ...)");
242             }
243             for (Lane[] laneGroup : new Lane[][] {lanesGroupA})// , lanesGroupB})
244             {
245                 for (int laneIndex = 0; laneIndex < laneGroup.length; laneIndex++)
246                 {
247                     Lane lane = laneGroup[laneIndex];
248                     for (RelativePosition relativePosition : new RelativePosition[] {car.getFront(), car.getReference(),
249                             car.getRear()})
250                     {
251                         // System.out.println("lane:" + lane + ", expectedException: " + expectException
252                         // + ", relativePostion: " + relativePosition);
253                         Length position = car.getPosition(lane, relativePosition);
254                         Length expectedPosition = positionA;// laneGroup == lanesGroupA ? positionA : positionB;
255                         expectedPosition =
256                                 expectedPosition.plus(new Length(stepTime.getSI() * initialSpeed.getSI(), LengthUnit.SI));
257                         expectedPosition = expectedPosition.plus(
258                                 new Length(0.5 * acceleration.getSI() * stepTime.getSI() * stepTime.getSI(), LengthUnit.SI));
259                         expectedPosition = expectedPosition.plus(relativePosition.dx());
260                         // System.out.println("reported position: " + position);
261                         // System.out.println("expected position: " + expectedPosition);
262                         assertEquals(expectedPosition.getSI(), position.getSI(), 0.01,
263                                 "Position should match initial position");
264                         double fractionalPosition = car.getPosition(lane, relativePosition).si / lane.getLength().si;
265                         expectedPosition = positionA;// laneGroup == lanesGroupA ? positionA : positionB;
266                         expectedPosition =
267                                 expectedPosition.plus(new Length(stepTime.getSI() * initialSpeed.getSI(), LengthUnit.SI));
268                         expectedPosition = expectedPosition.plus(
269                                 new Length(0.5 * acceleration.getSI() * stepTime.getSI() * stepTime.getSI(), LengthUnit.SI));
270                         expectedPosition = expectedPosition.plus(relativePosition.dx());
271                         // System.out.println("reported position: " + position);
272                         // System.out.println("expected position: " + expectedPosition);
273                         double expectedFractionalPosition = expectedPosition.getSI() / lane.getLength().getSI();
274                         assertEquals(expectedFractionalPosition, fractionalPosition, 0.01,
275                                 "Position should match initial position");
276                     }
277                 }
278             }
279         }
280         // A GTU can exist on several lanes at once; create another lane group to test that
281         Node nodeCFrom = new Node(network, "CFrom", new Point2d(10, 100), Direction.ZERO);
282         Node nodeCTo = new Node(network, "CTo", new Point2d(1000, 0), Direction.ZERO);
283         Lane[] lanesGroupC = LaneFactory.makeMultiLane(network, "C", nodeCFrom, nodeCTo, null, 3, laneType,
284                 new Speed(100, KM_PER_HOUR), simulator, DefaultsNl.VEHICLE);
285         for (RelativePosition relativePosition : new RelativePosition[] {car.getFront(), car.getRear()})
286         {
287             LanePosition pos = car.getPosition();
288             assertTrue(null != pos, "Car should be in lane 1 of lane group A");
289             assertEquals(pos.getFraction() + relativePosition.dx().si / lanesGroupA[1].getLength().si,
290                     car.getPosition(lanesGroupA[1], relativePosition).si / lanesGroupA[1].getLength().si, 0.0000001,
291                     "fractional position should be equal to result of fractionalPosition(lane, ...)");
292         }
293         // TODO removeLane should throw an Error when the car is not on that lane (currently this is silently ignored)
294         // TODO figure out why the added lane has a non-zero position
295     }
296 }
297 
298 /**
299  * Dummy OtsModelInterface.
300  * <p>
301  * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
302  * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
303  * </p>
304  * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
305  */
306 class DummyModel extends AbstractOtsModel
307 {
308     /**
309      * Constructor.
310      * @param simulator the simulator to use
311      */
312     DummyModel(final OtsSimulatorInterface simulator)
313     {
314         super(simulator);
315     }
316 
317     @Override
318     public final void constructModel() throws SimRuntimeException
319     {
320         //
321     }
322 
323     @Override
324     public final RoadNetwork getNetwork()
325     {
326         return null;
327     }
328 
329 }