View Javadoc
1   package org.opentrafficsim.road.gtu;
2   
3   import static org.junit.Assert.assertEquals;
4   import static org.junit.Assert.assertFalse;
5   import static org.junit.Assert.assertTrue;
6   import static org.junit.Assert.fail;
7   
8   import java.util.ArrayList;
9   import java.util.Collection;
10  import java.util.HashSet;
11  import java.util.LinkedHashSet;
12  import java.util.Set;
13  
14  import nl.tudelft.simulation.dsol.SimRuntimeException;
15  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
16  
17  import org.djunits.unit.TimeUnit;
18  import org.djunits.unit.UNITS;
19  import org.djunits.value.vdouble.scalar.Acceleration;
20  import org.djunits.value.vdouble.scalar.DoubleScalar;
21  import org.djunits.value.vdouble.scalar.Duration;
22  import org.djunits.value.vdouble.scalar.Length;
23  import org.djunits.value.vdouble.scalar.Speed;
24  import org.djunits.value.vdouble.scalar.Time;
25  import org.junit.Test;
26  import org.opentrafficsim.core.dsol.OTSModelInterface;
27  import org.opentrafficsim.core.dsol.OTSSimTimeDouble;
28  import org.opentrafficsim.core.geometry.OTSPoint3D;
29  import org.opentrafficsim.core.gtu.GTUDirectionality;
30  import org.opentrafficsim.core.gtu.GTUException;
31  import org.opentrafficsim.core.gtu.GTUType;
32  import org.opentrafficsim.core.gtu.behavioralcharacteristics.BehavioralCharacteristics;
33  import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterTypes;
34  import org.opentrafficsim.core.idgenerator.IdGenerator;
35  import org.opentrafficsim.core.network.LongitudinalDirectionality;
36  import org.opentrafficsim.core.network.OTSNetwork;
37  import org.opentrafficsim.core.network.OTSNode;
38  import org.opentrafficsim.road.DefaultTestParameters;
39  import org.opentrafficsim.road.gtu.lane.LaneBasedIndividualGTU;
40  import org.opentrafficsim.road.gtu.lane.perception.categories.DefaultSimplePerception;
41  import org.opentrafficsim.road.gtu.lane.perception.headway.Headway;
42  import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedCFLCTacticalPlanner;
43  import org.opentrafficsim.road.gtu.lane.tactical.following.FixedAccelerationModel;
44  import org.opentrafficsim.road.gtu.lane.tactical.following.GTUFollowingModelOld;
45  import org.opentrafficsim.road.gtu.lane.tactical.following.IDMPlusOld;
46  import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.FixedLaneChangeModel;
47  import org.opentrafficsim.road.gtu.lane.tactical.lanechangemobil.LaneChangeModel;
48  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlanner;
49  import org.opentrafficsim.road.gtu.strategical.route.LaneBasedStrategicalRoutePlanner;
50  import org.opentrafficsim.road.network.factory.LaneFactory;
51  import org.opentrafficsim.road.network.lane.CrossSectionElement;
52  import org.opentrafficsim.road.network.lane.CrossSectionLink;
53  import org.opentrafficsim.road.network.lane.DirectedLanePosition;
54  import org.opentrafficsim.road.network.lane.Lane;
55  import org.opentrafficsim.road.network.lane.LaneType;
56  import org.opentrafficsim.simulationengine.SimpleSimulator;
57  
58  /**
59   * Test the LaneBasedGTU class.
60   * <p>
61   * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
62   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
63   * <p>
64   * $LastChangedDate: 2015-09-14 01:33:02 +0200 (Mon, 14 Sep 2015) $, @version $Revision: 1401 $, by $Author: averbraeck $,
65   * initial version 27 jan. 2015 <br>
66   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
67   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
68   */
69  public class LaneBasedGTUTest implements UNITS
70  {
71      /** The network. */
72      private OTSNetwork network = new OTSNetwork("leader follower parallel gtu test network");
73  
74      /** Id generator. */
75      private IdGenerator idGenerator = new IdGenerator("id");
76  
77      /**
78       * Test if a Truck covering a specified range of lanes can <i>see</i> a Car covering a specified range of lanes. <br>
79       * The network is a linear array of Nodes connected by 5-Lane Links. In the middle, the Nodes are very closely spaced. A
80       * truck is positioned over those center Nodes ensuring it covers several of the short Lanes in succession.
81       * @param truckFromLane int; lowest rank of lane range of the truck
82       * @param truckUpToLane int; highest rank of lane range of the truck
83       * @param carLanesCovered int; number of lanes that the car covers
84       * @throws Exception when something goes wrong (should not happen)
85       */
86      private void leaderFollowerParallel(int truckFromLane, int truckUpToLane, int carLanesCovered) throws Exception
87      {
88          // Perform a few sanity checks
89          if (carLanesCovered < 1)
90          {
91              fail("carLanesCovered must be >= 1 (got " + carLanesCovered + ")");
92          }
93          if (truckUpToLane < truckFromLane)
94          {
95              fail("truckUpToLane must be >= truckFromLane");
96          }
97          OTSModelInterface model = new Model();
98          SimpleSimulator simulator =
99                  new SimpleSimulator(new Time(0.0, SECOND), new Duration(0.0, SECOND), new Duration(3600.0, SECOND), model);
100         GTUType carType = new GTUType("car");
101         GTUType truckType = new GTUType("truck");
102         Set<GTUType> compatibility = new HashSet<GTUType>();
103         compatibility.add(carType);
104         compatibility.add(truckType);
105         LaneType laneType = new LaneType("CarLane", compatibility);
106         // Create a series of Nodes (some closely bunched together)
107         ArrayList<OTSNode> nodes = new ArrayList<OTSNode>();
108         int[] linkBoundaries = { 0, 25, 50, 100, 101, 102, 103, 104, 105, 150, 175, 200 };
109         for (int xPos : linkBoundaries)
110         {
111             nodes.add(new OTSNode(this.network, "Node at " + xPos, new OTSPoint3D(xPos, 20, 0)));
112         }
113         // Now we can build a series of Links with Lanes on them
114         ArrayList<CrossSectionLink> links = new ArrayList<CrossSectionLink>();
115         final int laneCount = 5;
116         for (int i = 1; i < nodes.size(); i++)
117         {
118             OTSNode fromNode = nodes.get(i - 1);
119             OTSNode toNode = nodes.get(i);
120             String linkName = fromNode.getId() + "-" + toNode.getId();
121             Lane[] lanes =
122                     LaneFactory.makeMultiLane(this.network, linkName, fromNode, toNode, null, laneCount, laneType, new Speed(
123                             100, KM_PER_HOUR), simulator, LongitudinalDirectionality.DIR_PLUS);
124             links.add(lanes[0].getParentLink());
125         }
126         // Create a long truck with its front (reference) one meter in the last link on the 3rd lane
127         Length truckPosition = new Length(99.5, METER);
128         Length truckLength = new Length(15, METER);
129 
130         Set<DirectedLanePosition> truckPositions =
131                 buildPositionsSet(truckPosition, truckLength, links, truckFromLane, truckUpToLane);
132         Speed truckSpeed = new Speed(0, KM_PER_HOUR);
133         Length truckWidth = new Length(2.5, METER);
134         LaneChangeModel laneChangeModel = new FixedLaneChangeModel(null);
135         Speed maximumSpeed = new Speed(120, KM_PER_HOUR);
136         GTUFollowingModelOld gtuFollowingModel = new IDMPlusOld();
137         BehavioralCharacteristics behavioralCharacteristics = DefaultTestParameters.create();
138 
139         LaneBasedIndividualGTU truck =
140                 new LaneBasedIndividualGTU("Truck", truckType, truckLength, truckWidth, maximumSpeed, simulator, this.network);
141         LaneBasedStrategicalPlanner strategicalPlanner =
142                 new LaneBasedStrategicalRoutePlanner(behavioralCharacteristics, new LaneBasedCFLCTacticalPlanner(
143                         gtuFollowingModel, laneChangeModel, truck), truck);
144         truck.init(strategicalPlanner, truckPositions, truckSpeed);
145         // Verify that the truck is registered on the correct Lanes
146         int lanesChecked = 0;
147         int found = 0;
148         for (CrossSectionLink link : links)
149         {
150             for (CrossSectionElement cse : link.getCrossSectionElementList())
151             {
152                 if (cse instanceof Lane)
153                 {
154                     Lane lane = (Lane) cse;
155                     boolean truckPositionsOnLane = false;
156                     for (DirectedLanePosition pos : truckPositions)
157                     {
158                         if (pos.getLane().equals(lane))
159                         {
160                             truckPositionsOnLane = true;
161                         }
162                     }
163                     if (truckPositionsOnLane)
164                     {
165                         assertTrue("Truck should be registered on Lane " + lane, lane.getGtuList().contains(truck));
166                         found++;
167                     }
168                     else
169                     {
170                         assertFalse("Truck should NOT be registered on Lane " + lane, lane.getGtuList().contains(truck));
171                     }
172                     lanesChecked++;
173                 }
174             }
175         }
176         // Make sure we tested them all
177         assertEquals("lanesChecked should equals the number of Links times the number of lanes on each Link",
178                 laneCount * links.size(), lanesChecked);
179         assertEquals("Truck should be registered in " + truckPositions.size() + " lanes", truckPositions.size(), found);
180         Length forwardMaxDistance = truck.getBehavioralCharacteristics().getParameter(ParameterTypes.LOOKAHEAD);
181         // TODO see how we can ask the vehicle to look this far ahead
182         truck.getTacticalPlanner().getPerception().perceive();
183         Headway leader =
184                 truck.getTacticalPlanner().getPerception().getPerceptionCategory(DefaultSimplePerception.class).getForwardHeadway();
185         assertTrue(
186                 "With one vehicle in the network forward headway should return a value larger than zero, and smaller than maxDistance",
187                 forwardMaxDistance.getSI() >= leader.getDistance().si && leader.getDistance().si > 0);
188         assertEquals("With one vehicle in the network forward headwayGTU should return null", null, leader.getId());
189         // TODO see how we can ask the vehicle to look this far behind
190         Length reverseMaxDistance = truck.getBehavioralCharacteristics().getParameter(ParameterTypes.LOOKBACKOLD);
191         Headway follower =
192                 truck.getTacticalPlanner().getPerception().getPerceptionCategory(DefaultSimplePerception.class).getBackwardHeadway();
193         assertTrue(
194                 "With one vehicle in the network reverse headway should return a value less than zero, and smaller than |maxDistance|",
195                 Math.abs(reverseMaxDistance.getSI()) >= Math.abs(follower.getDistance().si) && follower.getDistance().si < 0);
196         assertEquals("With one vehicle in the network reverse headwayGTU should return null", null, follower.getId());
197         Length carLength = new Length(4, METER);
198         Length carWidth = new Length(1.8, METER);
199         Speed carSpeed = new Speed(0, KM_PER_HOUR);
200         int maxStep = linkBoundaries[linkBoundaries.length - 1];
201         for (int laneRank = 0; laneRank < laneCount + 1 - carLanesCovered; laneRank++)
202         {
203             for (int step = 0; step < maxStep; step += 5)
204             {
205                 if (laneRank >= truckFromLane && laneRank <= truckUpToLane
206                         && step >= truckPosition.getSI() - truckLength.getSI()
207                         && step - carLength.getSI() <= truckPosition.getSI())
208                 {
209                     continue; // Truck and car would overlap; the result of that placement is not defined :-)
210                 }
211                 Length carPosition = new Length(step, METER);
212                 Set<DirectedLanePosition> carPositions =
213                         buildPositionsSet(carPosition, carLength, links, laneRank, laneRank + carLanesCovered - 1);
214                 behavioralCharacteristics = DefaultTestParameters.create();
215 
216                 LaneBasedIndividualGTU car =
217                         new LaneBasedIndividualGTU("Car", carType, carLength, carWidth, maximumSpeed, simulator, this.network);
218                 strategicalPlanner =
219                         new LaneBasedStrategicalRoutePlanner(behavioralCharacteristics, new LaneBasedCFLCTacticalPlanner(
220                                 gtuFollowingModel, laneChangeModel, car), car);
221                 car.init(strategicalPlanner, carPositions, carSpeed);
222                 // leader = truck.headway(forwardMaxDistance);
223                 // TODO see how we can ask the vehicle to look 'forwardMaxDistance' ahead
224                 leader =
225                         truck.getTacticalPlanner().getPerception().getPerceptionCategory(DefaultSimplePerception.class)
226                                 .getForwardHeadway();
227                 double actualHeadway = leader.getDistance().si;
228                 double expectedHeadway =
229                         laneRank + carLanesCovered - 1 < truckFromLane || laneRank > truckUpToLane
230                                 || step - truckPosition.getSI() - truckLength.getSI() <= 0 ? Double.MAX_VALUE : step
231                                 - truckLength.getSI() - truckPosition.getSI();
232                 // System.out.println("carLanesCovered " + laneRank + ".." + (laneRank + carLanesCovered - 1)
233                 // + " truckLanesCovered " + truckFromLane + ".." + truckUpToLane + " car pos " + step
234                 // + " laneRank " + laneRank + " expected headway " + expectedHeadway);
235                 // The next assert found a subtle bug (">" instead of ">=")
236                 assertEquals("Forward headway should return " + expectedHeadway, expectedHeadway, actualHeadway, 0.1);
237                 String leaderGtuId = leader.getId();
238                 if (expectedHeadway == Double.MAX_VALUE)
239                 {
240                     assertEquals("Leader id should be null", null, leaderGtuId);
241                 }
242                 else
243                 {
244                     assertEquals("Leader id should be the car id", car, leaderGtuId);
245                 }
246                 // TODO follower = truck.headway(reverseMaxDistance);
247                 follower =
248                         truck.getTacticalPlanner().getPerception().getPerceptionCategory(DefaultSimplePerception.class)
249                                 .getBackwardHeadway();
250                 double actualReverseHeadway = follower.getDistance().si;
251                 double expectedReverseHeadway =
252                         laneRank + carLanesCovered - 1 < truckFromLane || laneRank > truckUpToLane
253                                 || step + carLength.getSI() >= truckPosition.getSI() ? Double.MAX_VALUE : truckPosition.getSI()
254                                 - carLength.getSI() - step;
255                 assertEquals("Reverse headway should return " + expectedReverseHeadway, expectedReverseHeadway,
256                         actualReverseHeadway, 0.1);
257                 String followerGtuId = follower.getId();
258                 if (expectedReverseHeadway == Double.MAX_VALUE)
259                 {
260                     assertEquals("Follower id should be null", null, followerGtuId);
261                 }
262                 else
263                 {
264                     assertEquals("Follower id should be the car id", car.getId(), followerGtuId);
265                 }
266                 for (int laneIndex = 0; laneIndex < laneCount; laneIndex++)
267                 {
268                     Lane l = null;
269                     double cumulativeDistance = 0;
270                     for (CrossSectionLink csl : links)
271                     {
272                         cumulativeDistance += csl.getLength().getSI();
273                         if (cumulativeDistance >= truckPosition.getSI())
274                         {
275                             l = getNthLane(csl, laneIndex);
276                             break;
277                         }
278                     }
279                     leader =
280                             truck.getTacticalPlanner().getPerception().getPerceptionCategory(DefaultSimplePerception.class)
281                                     .getForwardHeadway();
282                     actualHeadway = leader.getDistance().si;
283                     expectedHeadway =
284                             laneIndex < laneRank || laneIndex > laneRank + carLanesCovered - 1
285                                     || step - truckLength.getSI() - truckPosition.getSI() <= 0 ? Double.MAX_VALUE : step
286                                     - truckLength.getSI() - truckPosition.getSI();
287                     assertEquals("Headway on lane " + laneIndex + " should be " + expectedHeadway, expectedHeadway,
288                             actualHeadway, 0.001);
289                     leaderGtuId = leader.getId();
290                     if (laneIndex >= laneRank && laneIndex <= laneRank + carLanesCovered - 1
291                             && step - truckLength.getSI() - truckPosition.getSI() > 0)
292                     {
293                         assertEquals("Leader id should be the car id", car.getId(), leaderGtuId);
294                     }
295                     else
296                     {
297                         assertEquals("Leader id should be null", null, leaderGtuId);
298                     }
299                     follower =
300                             truck.getTacticalPlanner().getPerception().getPerceptionCategory(DefaultSimplePerception.class)
301                                     .getBackwardHeadway();
302                     actualReverseHeadway = follower.getDistance().si;
303                     expectedReverseHeadway =
304                             laneIndex < laneRank || laneIndex > laneRank + carLanesCovered - 1
305                                     || step + carLength.getSI() >= truckPosition.getSI() ? Double.MAX_VALUE : truckPosition
306                                     .getSI() - carLength.getSI() - step;
307                     assertEquals("Headway on lane " + laneIndex + " should be " + expectedReverseHeadway,
308                             expectedReverseHeadway, actualReverseHeadway, 0.001);
309                     followerGtuId = follower.getId();
310                     if (laneIndex >= laneRank && laneIndex <= laneRank + carLanesCovered - 1
311                             && step + carLength.getSI() < truckPosition.getSI())
312                     {
313                         assertEquals("Follower id should be the car id", car, followerGtuId);
314                     }
315                     else
316                     {
317                         assertEquals("Follower id should be null", null, followerGtuId);
318                     }
319                 }
320                 Collection<Headway> leftParallel =
321                         truck.getTacticalPlanner().getPerception().getPerceptionCategory(DefaultSimplePerception.class)
322                                 .getParallelHeadwaysLeft();
323                 int expectedLeftSize =
324                         laneRank + carLanesCovered - 1 < truckFromLane - 1 || laneRank >= truckUpToLane
325                                 || step + carLength.getSI() <= truckPosition.getSI()
326                                 || step > truckPosition.getSI() + truckLength.getSI() ? 0 : 1;
327                 // This one caught a complex bug
328                 assertEquals("Left parallel set size should be " + expectedLeftSize, expectedLeftSize, leftParallel.size());
329                 boolean foundCar = false;
330                 for (Headway hw : leftParallel)
331                 {
332                     if (car.getId().equals(hw.getId()))
333                     {
334                         foundCar = true;
335                         break;
336                     }
337                 }
338                 assertTrue("car was not found in rightParallel", foundCar);
339                 Collection<Headway> rightParallel =
340                         truck.getTacticalPlanner().getPerception().getPerceptionCategory(DefaultSimplePerception.class)
341                                 .getParallelHeadwaysRight();
342                 int expectedRightSize =
343                         laneRank + carLanesCovered - 1 <= truckFromLane || laneRank > truckUpToLane + 1
344                                 || step + carLength.getSI() < truckPosition.getSI()
345                                 || step > truckPosition.getSI() + truckLength.getSI() ? 0 : 1;
346                 assertEquals("Right parallel set size should be " + expectedRightSize, expectedRightSize, rightParallel.size());
347                 foundCar = false;
348                 for (Headway hw : rightParallel)
349                 {
350                     if (car.getId().equals(hw.getId()))
351                     {
352                         foundCar = true;
353                         break;
354                     }
355                 }
356                 assertTrue("car was not found in rightParallel", foundCar);
357                 for (DirectedLanePosition pos : carPositions)
358                 {
359                     pos.getLane().removeGTU(car, true);
360                 }
361             }
362         }
363     }
364 
365     /**
366      * Test the leader, follower and parallel methods.
367      * @throws Exception when something goes wrong (should not happen)
368      */
369     @Test
370     public void leaderFollowerAndParallelTest() throws Exception
371     {
372         // leaderFollowerParallel(2, 2, 1);
373         // leaderFollowerParallel(2, 3, 1);
374         // leaderFollowerParallel(2, 2, 2);
375         // leaderFollowerParallel(2, 3, 2);
376     }
377 
378     /**
379      * Test the deltaTimeForDistance and timeAtDistance methods.
380      * @throws Exception when something goes wrong (should not happen)
381      */
382     @Test
383     public void timeAtDistanceTest() throws Exception
384     {
385         for (int a = 1; a >= -1; a--)
386         {
387             this.network = new OTSNetwork("test"); // new network every time, otherwise nodes cannot be added again
388             // Create a car with constant acceleration
389             OTSModelInterface model = new Model();
390             SimpleSimulator simulator =
391                     new SimpleSimulator(new Time(0.0, SECOND), new Duration(0.0, SECOND), new Duration(3600.0, SECOND), model);
392             // Run the simulator clock to some non-zero value
393             simulator.runUpTo(new Time(60, SECOND));
394             while (simulator.isRunning())
395             {
396                 try
397                 {
398                     Thread.sleep(1);
399                 }
400                 catch (InterruptedException ie)
401                 {
402                     ie = null; // ignore
403                 }
404             }
405             GTUType carType = new GTUType("car");
406             Set<GTUType> compatibility = new HashSet<GTUType>();
407             compatibility.add(carType);
408             LaneType laneType = new LaneType("CarLane", compatibility);
409             OTSNode fromNode = new OTSNode(this.network, "Node A", new OTSPoint3D(0, 0, 0));
410             OTSNode toNode = new OTSNode(this.network, "Node B", new OTSPoint3D(1000, 0, 0));
411             String linkName = "AB";
412             Lane lane =
413                     LaneFactory.makeMultiLane(this.network, linkName, fromNode, toNode, null, 1, laneType, new Speed(200,
414                             KM_PER_HOUR), simulator, LongitudinalDirectionality.DIR_PLUS)[0];
415             Length carPosition = new Length(100, METER);
416             Set<DirectedLanePosition> carPositions = new LinkedHashSet<>(1);
417             carPositions.add(new DirectedLanePosition(lane, carPosition, GTUDirectionality.DIR_PLUS));
418             Speed carSpeed = new Speed(10, METER_PER_SECOND);
419             Acceleration acceleration = new Acceleration(a, METER_PER_SECOND_2);
420             FixedAccelerationModel fam = new FixedAccelerationModel(acceleration, new Duration(10, SECOND));
421             LaneChangeModel laneChangeModel = new FixedLaneChangeModel(null);
422             Speed maximumSpeed = new Speed(200, KM_PER_HOUR);
423             BehavioralCharacteristics behavioralCharacteristics = DefaultTestParameters.create();
424 
425             LaneBasedIndividualGTU car =
426                     new LaneBasedIndividualGTU("Car" + this.idGenerator.nextId(), carType, new Length(4, METER), new Length(
427                             1.8, METER), maximumSpeed, simulator, this.network);
428             LaneBasedStrategicalPlanner strategicalPlanner =
429                     new LaneBasedStrategicalRoutePlanner(behavioralCharacteristics, new LaneBasedCFLCTacticalPlanner(fam,
430                             laneChangeModel, car), car);
431             car.init(strategicalPlanner, carPositions, carSpeed);
432             // Let the simulator execute the move method of the car
433             simulator.runUpTo(new Time(61, SECOND));
434             while (simulator.isRunning())
435             {
436                 try
437                 {
438                     Thread.sleep(1);
439                 }
440                 catch (InterruptedException ie)
441                 {
442                     ie = null; // ignore
443                 }
444             }
445 
446             // System.out.println("acceleration is " + acceleration);
447             // Check the results
448             for (int timeStep = 1; timeStep < 100; timeStep++)
449             {
450                 double deltaTime = 0.1 * timeStep;
451                 double distanceAtTime = carSpeed.getSI() * deltaTime + 0.5 * acceleration.getSI() * deltaTime * deltaTime;
452                 // System.out.println(String.format("time %.1fs, distance %.3fm", 60 + deltaTime, carPosition.getSI()
453                 // + distanceAtTime));
454                 // System.out.println("Expected differential distance " + distanceAtTime);
455                 /*-
456                 assertEquals("It should take " + deltaTime + " seconds to cover distance " + distanceAtTime, deltaTime, car
457                         .deltaTimeForDistance(new Length(distanceAtTime, METER)).getSI(), 0.0001);
458                 assertEquals("Car should reach distance " + distanceAtTime + " at " + (deltaTime + 60), deltaTime + 60, car
459                         .timeAtDistance(new Length(distanceAtTime, METER)).getSI(), 0.0001);
460                  */
461             }
462         }
463     }
464 
465     /**
466      * Executed as scheduled event.
467      */
468     public final void autoPauseSimulator()
469     {
470         // do nothing
471     }
472 
473     /**
474      * Create the Map that records in which lane a GTU is registered.
475      * @param totalLongitudinalPosition Length; the front position of the GTU from the start of the chain of Links
476      * @param gtuLength Length; the length of the GTU
477      * @param links ArrayList&lt;CrossSectionLink&lt;?,?&gt;&gt;; the list of Links
478      * @param fromLaneRank int; lowest rank of lanes that the GTU must be registered on (0-based)
479      * @param uptoLaneRank int; highest rank of lanes that the GTU must be registered on (0-based)
480      * @return the Set of the LanePositions that the GTU is registered on
481      */
482     private Set<DirectedLanePosition> buildPositionsSet(Length totalLongitudinalPosition, Length gtuLength,
483             ArrayList<CrossSectionLink> links, int fromLaneRank, int uptoLaneRank)
484     {
485         Set<DirectedLanePosition> result = new LinkedHashSet<>(1);
486         double cumulativeLength = 0;
487         for (CrossSectionLink link : links)
488         {
489             double linkLength = link.getLength().getSI();
490             double frontPositionInLink = totalLongitudinalPosition.getSI() - cumulativeLength + gtuLength.getSI();
491             double rearPositionInLink = frontPositionInLink - gtuLength.getSI();
492             double linkEnd = cumulativeLength + linkLength;
493             // System.out.println("cumulativeLength: " + cumulativeLength + ", linkEnd: " + linkEnd + ", frontpos: "
494             // + frontPositionInLink + ", rearpos: " + rearPositionInLink);
495             if (rearPositionInLink < linkLength && frontPositionInLink >= 0)
496             {
497                 // Some part of the GTU is in this Link
498                 for (int laneRank = fromLaneRank; laneRank <= uptoLaneRank; laneRank++)
499                 {
500                     Lane lane = getNthLane(link, laneRank);
501                     if (null == lane)
502                     {
503                         fail("Error in test; canot find lane with rank " + laneRank);
504                     }
505                     try
506                     {
507                         result.add(new DirectedLanePosition(lane, new Length(rearPositionInLink, METER),
508                                 GTUDirectionality.DIR_PLUS));
509                     }
510                     catch (GTUException exception)
511                     {
512                         fail("Error in test; DirectedLanePosition for lane " + lane);
513                     }
514                 }
515             }
516             cumulativeLength += linkLength;
517         }
518         return result;
519     }
520 
521     /**
522      * Find the Nth Lane on a Link.
523      * @param link Link; the Link
524      * @param rank int; the zero-based rank of the Lane to return
525      * @return Lane
526      */
527     private Lane getNthLane(final CrossSectionLink link, int rank)
528     {
529         for (CrossSectionElement cse : link.getCrossSectionElementList())
530         {
531             if (cse instanceof Lane)
532             {
533                 if (0 == rank--)
534                 {
535                     return (Lane) cse;
536                 }
537             }
538         }
539         return null;
540     }
541 }
542 
543 /** */
544 class Model implements OTSModelInterface
545 {
546 
547     /** */
548     private static final long serialVersionUID = 20150127L;
549 
550     /** {@inheritDoc} */
551     @Override
552     public void constructModel(
553             SimulatorInterface<Time, Duration, OTSSimTimeDouble> simulator)
554             throws SimRuntimeException
555     {
556         // Dummy
557     }
558 
559     /** {@inheritDoc} */
560     @Override
561     public SimulatorInterface<Time, Duration, OTSSimTimeDouble> getSimulator()
562 
563     {
564         return null;
565     }
566 
567 }