View Javadoc
1   package strategies;
2   
3   import java.awt.Color;
4   import java.awt.Component;
5   import java.awt.Dimension;
6   import java.rmi.RemoteException;
7   import java.util.ArrayList;
8   import java.util.Hashtable;
9   import java.util.Iterator;
10  import java.util.LinkedHashMap;
11  import java.util.LinkedHashSet;
12  import java.util.List;
13  import java.util.Map;
14  import java.util.Set;
15  
16  import javax.naming.NamingException;
17  import javax.swing.Box;
18  import javax.swing.JLabel;
19  import javax.swing.JSlider;
20  import javax.swing.SwingConstants;
21  import javax.swing.event.ChangeEvent;
22  import javax.swing.event.ChangeListener;
23  
24  import org.djunits.unit.DirectionUnit;
25  import org.djunits.unit.SpeedUnit;
26  import org.djunits.value.vdouble.scalar.Acceleration;
27  import org.djunits.value.vdouble.scalar.Direction;
28  import org.djunits.value.vdouble.scalar.Duration;
29  import org.djunits.value.vdouble.scalar.Length;
30  import org.djunits.value.vdouble.scalar.Speed;
31  import org.djutils.cli.CliException;
32  import org.djutils.cli.CliUtil;
33  import org.djutils.event.EventInterface;
34  import org.djutils.event.EventListenerInterface;
35  import org.djutils.exceptions.Try;
36  import org.opentrafficsim.base.parameters.ParameterException;
37  import org.opentrafficsim.base.parameters.ParameterSet;
38  import org.opentrafficsim.base.parameters.ParameterTypes;
39  import org.opentrafficsim.base.parameters.Parameters;
40  import org.opentrafficsim.core.animation.gtu.colorer.AccelerationGTUColorer;
41  import org.opentrafficsim.core.animation.gtu.colorer.SpeedGTUColorer;
42  import org.opentrafficsim.core.animation.gtu.colorer.SwitchableGTUColorer;
43  import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
44  import org.opentrafficsim.core.geometry.OTSGeometryException;
45  import org.opentrafficsim.core.geometry.OTSLine3D;
46  import org.opentrafficsim.core.geometry.OTSPoint3D;
47  import org.opentrafficsim.core.gtu.GTU;
48  import org.opentrafficsim.core.gtu.GTUCharacteristics;
49  import org.opentrafficsim.core.gtu.GTUDirectionality;
50  import org.opentrafficsim.core.gtu.GTUException;
51  import org.opentrafficsim.core.gtu.GTUType;
52  import org.opentrafficsim.core.gtu.perception.DirectEgoPerception;
53  import org.opentrafficsim.core.network.Link;
54  import org.opentrafficsim.core.network.LinkType;
55  import org.opentrafficsim.core.network.NetworkException;
56  import org.opentrafficsim.core.parameters.ParameterFactoryByType;
57  import org.opentrafficsim.road.gtu.colorer.DesiredHeadwayColorer;
58  import org.opentrafficsim.road.gtu.colorer.FixedColor;
59  import org.opentrafficsim.road.gtu.colorer.IncentiveColorer;
60  import org.opentrafficsim.road.gtu.colorer.SocialPressureColorer;
61  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
62  import org.opentrafficsim.road.gtu.lane.LaneBasedIndividualGTU;
63  import org.opentrafficsim.road.gtu.lane.perception.CategoricalLanePerception;
64  import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
65  import org.opentrafficsim.road.gtu.lane.perception.PerceptionFactory;
66  import org.opentrafficsim.road.gtu.lane.perception.categories.AnticipationTrafficPerception;
67  import org.opentrafficsim.road.gtu.lane.perception.categories.DirectInfrastructurePerception;
68  import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.DirectNeighborsPerception;
69  import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.HeadwayGtuType;
70  import org.opentrafficsim.road.gtu.lane.tactical.following.AbstractIDM;
71  import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModelFactory;
72  import org.opentrafficsim.road.gtu.lane.tactical.following.IDMPlus;
73  import org.opentrafficsim.road.gtu.lane.tactical.following.IDMPlusFactory;
74  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.AccelerationIncentive;
75  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.IncentiveKeep;
76  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.IncentiveRoute;
77  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.IncentiveSocioSpeed;
78  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.IncentiveSpeedWithCourtesy;
79  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.IncentiveStayRight;
80  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.LMRSFactory;
81  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.SocioDesiredSpeed;
82  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.Cooperation;
83  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.GapAcceptance;
84  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.LmrsParameters;
85  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.MandatoryIncentive;
86  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.Synchronization;
87  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.Tailgating;
88  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.VoluntaryIncentive;
89  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlanner;
90  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlannerFactory;
91  import org.opentrafficsim.road.gtu.strategical.route.LaneBasedStrategicalRoutePlannerFactory;
92  import org.opentrafficsim.road.network.OTSRoadNetwork;
93  import org.opentrafficsim.road.network.factory.LaneFactory;
94  import org.opentrafficsim.road.network.lane.CrossSectionLink;
95  import org.opentrafficsim.road.network.lane.DirectedLanePosition;
96  import org.opentrafficsim.road.network.lane.Lane;
97  import org.opentrafficsim.road.network.lane.LaneType;
98  import org.opentrafficsim.road.network.lane.OTSRoadNode;
99  import org.opentrafficsim.road.network.lane.Stripe.Permeable;
100 import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy;
101 import org.opentrafficsim.swing.gui.OTSAnimationPanel;
102 import org.opentrafficsim.swing.script.AbstractSimulationScript;
103 
104 import nl.tudelft.simulation.dsol.SimRuntimeException;
105 import nl.tudelft.simulation.jstats.distributions.DistNormal;
106 import nl.tudelft.simulation.jstats.streams.MersenneTwister;
107 import nl.tudelft.simulation.jstats.streams.StreamInterface;
108 import picocli.CommandLine.Option;
109 
110 /**
111  * Demo of lane change strategies.
112  * <p>
113  * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
114  * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
115  * <p>
116  * @version $Revision$, $LastChangedDate$, by $Author$, initial version 4 jun. 2018 <br>
117  * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
118  */
119 public class StrategiesDemo extends AbstractSimulationScript
120 {
121     /** Factories. */
122     private final Map<GTUType, LaneBasedStrategicalPlannerFactory<?>> factories = new LinkedHashMap<>();
123 
124     /** GTU id number. */
125     private int gtuIdNum = 0;
126 
127     /** Number of GTUs. */
128     private int gtuNum = 60;
129 
130     /** Random stream. */
131     private final StreamInterface stream = new MersenneTwister(1L);
132 
133     /** Queue of cumulative odometers. */
134     private final List<Double> queue = new ArrayList<>();
135 
136     /** Lane change listener. */
137     private KmplcListener kmplcListener;
138 
139     /** Truck fraction. */
140     private double truckFraction = 0.1;
141 
142     /** Next GTU type. */
143     private GTUType nextGtuType;
144 
145     /** Truck length. */
146     private Length truckLength;
147 
148     /** Truck mid. */
149     private Length truckMid;
150 
151     /** Car length. */
152     @Option(names = "--length", description = "Length", defaultValue = "2m")
153     private Length carLength;
154 
155     /** Car mid. */
156     private Length carMid;
157 
158     /**
159      * Constructor.
160      */
161     protected StrategiesDemo()
162     {
163         super("Strategies demo", "Demo of driving strategies in LMRS.");
164         setGtuColorer(SwitchableGTUColorer.builder().addColorer(new FixedColor(Color.BLUE, "Blue"))
165                 .addColorer(new SpeedGTUColorer(new Speed(150, SpeedUnit.KM_PER_HOUR)))
166                 .addColorer(new AccelerationGTUColorer(Acceleration.instantiateSI(-6.0), Acceleration.instantiateSI(2)))
167                 .addActiveColorer(new SocialPressureColorer())
168                 .addColorer(new DesiredHeadwayColorer(Duration.instantiateSI(0.5), Duration.instantiateSI(1.6)))
169                 .addColorer(new IncentiveColorer(IncentiveSocioSpeed.class)).build());
170         try
171         {
172             CliUtil.changeOptionDefault(this, "simulationTime", "3600000s");
173         }
174         catch (NoSuchFieldException | IllegalStateException | IllegalArgumentException | CliException exception)
175         {
176             throw new RuntimeException(exception);
177         }
178     }
179 
180     /**
181      * Main method.
182      * @param args String[]; arguments
183      */
184     public static void main(final String[] args)
185     {
186         StrategiesDemomo">StrategiesDemo demo = new StrategiesDemo();
187         CliUtil.execute(demo, args);
188         try
189         {
190             demo.start();
191         }
192         catch (Exception ex)
193         {
194             ex.printStackTrace();
195         }
196     }
197 
198     /** {@inheritDoc} */
199     @Override
200     protected void setupDemo(final OTSAnimationPanel animation, final OTSRoadNetwork network)
201     {
202         // text
203         JLabel textLabel = new JLabel("<html><p align=\"justify\">"
204                 + "Adjust the sliders below to change the ego-speed sensitivity and socio-speed sensitivity of the drivers, "
205                 + "and observe how traffic is affected. Detailed instructions are in the attached read-me." + "</html>");
206         textLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
207         animation.getDemoPanel().add(textLabel);
208 
209         // spacer
210         animation.getDemoPanel().add(Box.createVerticalStrut(20));
211 
212         // number of vehicles
213         JLabel gtuLabel = new JLabel("<html>Number of vehicles</html>");
214         gtuLabel.setHorizontalAlignment(SwingConstants.CENTER);
215         gtuLabel.setPreferredSize(new Dimension(200, 0));
216         gtuLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
217         animation.getDemoPanel().add(gtuLabel);
218         JSlider gtuSlider = new JSlider(0, 120, this.gtuNum);
219         gtuSlider.setMinorTickSpacing(10);
220         gtuSlider.setMajorTickSpacing(30);
221         gtuSlider.setPaintTicks(true);
222         gtuSlider.setPaintLabels(true);
223         gtuSlider.setToolTipText("<html>Number of vehicles</html>");
224         gtuSlider.addChangeListener(new ChangeListener()
225         {
226             @SuppressWarnings("synthetic-access")
227             @Override
228             public void stateChanged(final ChangeEvent e)
229             {
230                 StrategiesDemo.this.gtuNum = ((JSlider) e.getSource()).getValue();
231                 if (!StrategiesDemo.this.getSimulator().isRunning())
232                 {
233                     // StrategiesDemo.this.checkVehicleNumber();
234                     animation.getDemoPanel().getParent().repaint();
235                 }
236             }
237         });
238         animation.getDemoPanel().add(gtuSlider);
239 
240         // spacer
241         animation.getDemoPanel().add(Box.createVerticalStrut(20));
242 
243         // ego
244         JLabel egoLabel = new JLabel("<html>Ego-speed sensitivity<sup>-1</sup> [km/h]</html>");
245         egoLabel.setHorizontalAlignment(SwingConstants.CENTER);
246         egoLabel.setPreferredSize(new Dimension(200, 0));
247         egoLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
248         animation.getDemoPanel().add(egoLabel);
249         int egoSteps = 70;
250         JSlider egoSlider = new JSlider(0, egoSteps, egoSteps / 2);
251         egoSlider.setMinorTickSpacing(2);
252         egoSlider.setMajorTickSpacing(egoSteps / 5);
253         egoSlider.setPaintTicks(true);
254         egoSlider.setPaintLabels(true);
255         Hashtable<Integer, JLabel> egoTable = new Hashtable<>();
256         for (int i = 0; i <= egoSteps; i += egoSteps / 5)
257         {
258             egoTable.put(i, new JLabel(String.format("%d", i)));
259         }
260         egoSlider.setLabelTable(egoTable);
261         egoSlider.setToolTipText("<html>Ego-speed sensitivity as 1/<i>v<sub>gain</sub></i></html>");
262         egoSlider.addChangeListener(new ChangeListener()
263         {
264             @Override
265             public void stateChanged(final ChangeEvent e)
266             {
267                 double v = Math.max(((JSlider) e.getSource()).getValue(), 0.01);
268                 Speed vGain = new Speed(v, SpeedUnit.KM_PER_HOUR);
269                 for (GTU gtu : getNetwork().getGTUs())
270                 {
271                     if (gtu.getGTUType().isOfType(GTUType.DEFAULTS.CAR))
272                     {
273                         Try.execute(() -> gtu.getParameters().setParameter(LmrsParameters.VGAIN, vGain),
274                                 "Exception while setting vGain");
275                     }
276                 }
277             }
278         });
279         animation.getDemoPanel().add(egoSlider);
280 
281         // spacer
282         animation.getDemoPanel().add(Box.createVerticalStrut(20));
283 
284         // socio
285         JLabel socioLabel = new JLabel("Socio-speed sensitivity [-]");
286         socioLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
287         animation.getDemoPanel().add(socioLabel);
288         int socioSteps = 20;
289         JSlider socioSlider = new JSlider(0, socioSteps, socioSteps / 2);
290         socioSlider.setMinorTickSpacing(1);
291         socioSlider.setMajorTickSpacing(socioSteps / 5);
292         socioSlider.setPaintTicks(true);
293         socioSlider.setPaintLabels(true);
294         Hashtable<Integer, JLabel> socioTable = new Hashtable<>();
295         for (int i = 0; i <= socioSteps; i += socioSteps / 5)
296         {
297             double val = (double) i / socioSteps;
298             socioTable.put(i, new JLabel(String.format("%.2f", val)));
299         }
300         socioSlider.setLabelTable(socioTable);
301         socioSlider.setToolTipText("Socio-speed sensitivity between 0 and 1");
302         socioSlider.addChangeListener(new ChangeListener()
303         {
304             @Override
305             public void stateChanged(final ChangeEvent e)
306             {
307                 JSlider slider = (JSlider) e.getSource();
308                 double sigma = ((double) slider.getValue()) / slider.getMaximum();
309                 for (GTU gtu : getNetwork().getGTUs())
310                 {
311                     if (gtu.getGTUType().isOfType(GTUType.DEFAULTS.CAR))
312                     {
313                         Try.execute(() -> gtu.getParameters().setParameter(LmrsParameters.SOCIO, sigma),
314                                 "Exception while setting vGain");
315                     }
316                 }
317             }
318         });
319         animation.getDemoPanel().add(socioSlider);
320 
321         // spacer
322         animation.getDemoPanel().add(Box.createVerticalStrut(20));
323 
324         // km/lc
325         JLabel kmplcLabel = new JLabel("Km between lane changes (last 0): -");
326         this.kmplcListener = new KmplcListener(kmplcLabel, network);
327         for (GTU gtu : network.getGTUs())
328         {
329             Try.execute(() -> gtu.addListener(this.kmplcListener, LaneBasedGTU.LANE_CHANGE_EVENT),
330                     "Exception while adding lane change listener");
331         }
332         kmplcLabel.setHorizontalAlignment(SwingConstants.LEFT);
333         kmplcLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
334         animation.getDemoPanel().add(kmplcLabel);
335     }
336 
337     /**
338      * Create or destroy vehicles.
339      */
340     @SuppressWarnings("unused")
341     private void checkVehicleNumber()
342     {
343         while (getNetwork().getGTUs().size() > this.gtuNum)
344         {
345             int i = StrategiesDemo.this.stream.nextInt(0, getNetwork().getGTUs().size());
346             Iterator<GTU> it = getNetwork().getGTUs().iterator();
347             GTU gtu = it.next();
348             for (int j = 0; j < i; j++)
349             {
350                 gtu = it.next();
351             }
352             gtu.destroy();
353             this.queue.clear();
354         }
355         // if: add one vehicle per cycle, so we limit the disturbance
356         if (getNetwork().getGTUs().size() < this.gtuNum)
357         {
358             Lane lane = null;
359             Length pos = null;
360             Speed initialSpeed = null;
361             Length gap = Length.ZERO;
362             for (Link link : getNetwork().getLinkMap().values())
363             {
364                 for (Lane l : ((CrossSectionLink) link).getLanes())
365                 {
366                     if (l.numberOfGtus() == 0)
367                     {
368                         lane = l;
369                         pos = lane.getLength().times(0.5);
370                         gap = Length.POSITIVE_INFINITY;
371                         initialSpeed = Speed.ZERO;
372                     }
373                     for (int i = 0; i < l.numberOfGtus(); i++)
374                     {
375                         LaneBasedGTU gtu1 = l.getGtu(i);
376                         Length up = Try.assign(() -> gtu1.position(l, gtu1.getFront()), "");
377                         LaneBasedGTU gtu2;
378                         Length down;
379                         if (i < l.numberOfGtus() - 1)
380                         {
381                             gtu2 = l.getGtu(i + 1);
382                             down = Try.assign(() -> gtu2.position(l, gtu2.getRear()), "");
383                         }
384                         else
385                         {
386                             Lane nextLane =
387                                     l.nextLanes(getNetwork().getGtuType(GTUType.DEFAULTS.VEHICLE)).keySet().iterator().next();
388                             if (nextLane.numberOfGtus() == 0)
389                             {
390                                 continue;
391                             }
392                             gtu2 = nextLane.getGtu(0);
393                             down = l.getLength().plus(Try.assign(() -> gtu2.position(nextLane, gtu2.getRear()), ""));
394                         }
395                         Length tentativeGap =
396                                 down.minus(up).minus(this.nextGtuType.isOfType(getNetwork().getGtuType(GTUType.DEFAULTS.TRUCK))
397                                         ? this.truckLength : this.carLength);
398                         if (tentativeGap.gt(gap))
399                         {
400                             // check reasonable gap (0.3s)
401                             Speed maxSpeed = Speed.max(gtu1.getSpeed(), gtu2.getSpeed());
402                             if (maxSpeed.eq0() || tentativeGap.divide(maxSpeed).si * .5 > .3)
403                             {
404                                 gap = tentativeGap;
405                                 initialSpeed = Speed.interpolate(gtu1.getSpeed(), gtu2.getSpeed(), 0.5);
406                                 pos = up.plus(tentativeGap.times(0.5))
407                                         .minus(this.nextGtuType.isOfType(getNetwork().getGtuType(GTUType.DEFAULTS.TRUCK))
408                                                 ? this.truckMid : this.carMid);
409                                 if (pos.gt(l.getLength()))
410                                 {
411                                     pos = pos.minus(l.getLength());
412                                     lane = l.nextLanes(getNetwork().getGtuType(GTUType.DEFAULTS.VEHICLE)).keySet().iterator()
413                                             .next();
414                                 }
415                                 else
416                                 {
417                                     lane = l;
418                                 }
419                             }
420                         }
421                     }
422                 }
423             }
424             if (lane != null)
425             {
426                 try
427                 {
428                     createGtu(lane, pos, this.nextGtuType, initialSpeed, getNetwork());
429                 }
430                 catch (NamingException | GTUException | NetworkException | SimRuntimeException | OTSGeometryException exception)
431                 {
432                     throw new RuntimeException(exception);
433                 }
434                 this.nextGtuType = this.stream.nextDouble() < this.truckFraction
435                         ? getNetwork().getGtuType(GTUType.DEFAULTS.TRUCK) : getNetwork().getGtuType(GTUType.DEFAULTS.CAR);
436                 this.queue.clear();
437             }
438         }
439         Try.execute(() -> getSimulator().scheduleEventRel(Duration.instantiateSI(0.5), this, this, "checkVehicleNumber",
440                 new Object[] {}), "");
441     }
442 
443     /** Lane change listener. */
444     private class KmplcListener implements EventListenerInterface
445     {
446         /** Label to show statistic. */
447         private final JLabel label;
448 
449         /** Network. */
450         private final OTSRoadNetwork network;
451 
452         /**
453          * Constructor.
454          * @param label JLabel; label
455          * @param network OTSRoadNetwork; network
456          */
457         @SuppressWarnings("synthetic-access")
458         KmplcListener(final JLabel label, final OTSRoadNetwork network)
459         {
460             this.label = label;
461             this.network = network;
462             StrategiesDemo.this.queue.add(0.0);
463         }
464 
465         /** {@inheritDoc} */
466         @SuppressWarnings("synthetic-access")
467         @Override
468         public void notify(final EventInterface event) throws RemoteException
469         {
470             if (event.getType().equals(LaneBasedGTU.LANE_CHANGE_EVENT))
471             {
472                 double cumul = 0.0;
473                 for (GTU gtu : this.network.getGTUs())
474                 {
475                     cumul += gtu.getOdometer().si;
476                 }
477                 cumul /= 1000;
478                 StrategiesDemo.this.queue.add(cumul);
479                 while (StrategiesDemo.this.queue.size() > 51)
480                 {
481                     StrategiesDemo.this.queue.remove(0);
482                 }
483                 double val =
484                         (StrategiesDemo.this.queue.get(StrategiesDemo.this.queue.size() - 1) - StrategiesDemo.this.queue.get(0))
485                                 / (StrategiesDemo.this.queue.size() - 1.0);
486                 this.label.setText(
487                         String.format("Lane change rate (last %d): %.1f km/lc", StrategiesDemo.this.queue.size() - 1, val));
488             }
489         }
490     }
491 
492     /** {@inheritDoc} */
493     @Override
494     protected OTSRoadNetwork setupSimulation(final OTSSimulatorInterface sim) throws Exception
495     {
496         OTSRoadNetwork network = new OTSRoadNetwork("Strategies demo", true);
497 
498         GTUCharacteristics truck =
499                 GTUType.defaultCharacteristics(network.getGtuType(GTUType.DEFAULTS.TRUCK), network, this.stream);
500         GTUCharacteristics car = GTUType.defaultCharacteristics(network.getGtuType(GTUType.DEFAULTS.CAR), network, this.stream);
501         this.truckLength = truck.getLength();
502         this.truckMid = truck.getLength().times(0.5).minus(truck.getFront());
503         this.carLength = car.getLength();
504         this.carMid = car.getLength().times(0.5).minus(car.getFront());
505 
506         double radius = 150;
507         Speed speedLimit = new Speed(120.0, SpeedUnit.KM_PER_HOUR);
508         OTSRoadNode nodeA = new OTSRoadNode(network, "A", new OTSPoint3D(-radius, 0, 0), 
509                 new Direction(270, DirectionUnit.EAST_DEGREE));
510         OTSRoadNode nodeB = new OTSRoadNode(network, "B", new OTSPoint3D(radius, 0, 0), 
511                 new Direction(90, DirectionUnit.EAST_DEGREE));
512 
513         OTSPoint3D[] coordsHalf1 = new OTSPoint3D[127];
514         for (int i = 0; i < coordsHalf1.length; i++)
515         {
516             double angle = Math.PI * (i) / (coordsHalf1.length - 1);
517             coordsHalf1[i] = new OTSPoint3D(radius * Math.cos(angle), radius * Math.sin(angle), 0);
518         }
519         List<Lane> lanes1 = new LaneFactory(network, nodeB, nodeA, network.getLinkType(LinkType.DEFAULTS.FREEWAY), sim,
520                 LaneKeepingPolicy.KEEPLEFT, new OTSLine3D(coordsHalf1))
521                         .leftToRight(0.0, Length.instantiateSI(3.5), network.getLaneType(LaneType.DEFAULTS.FREEWAY), speedLimit)
522                         .addLanes(Permeable.BOTH).getLanes();
523         OTSPoint3D[] coordsHalf2 = new OTSPoint3D[127];
524         for (int i = 0; i < coordsHalf2.length; i++)
525         {
526             double angle = Math.PI + Math.PI * (i) / (coordsHalf2.length - 1);
527             coordsHalf2[i] = new OTSPoint3D(radius * Math.cos(angle), radius * Math.sin(angle), 0);
528         }
529         List<Lane> lanes2 = new LaneFactory(network, nodeA, nodeB, network.getLinkType(LinkType.DEFAULTS.FREEWAY), sim,
530                 LaneKeepingPolicy.KEEPLEFT, new OTSLine3D(coordsHalf2))
531                         .leftToRight(0.0, Length.instantiateSI(3.5), network.getLaneType(LaneType.DEFAULTS.FREEWAY), speedLimit)
532                         .addLanes(Permeable.BOTH).getLanes();
533 
534         // Strategical factories
535         PerceptionFactory perceptionFactory = new LmrsStrategiesPerceptionFactory();
536         // random parameters
537         ParameterFactoryByType parameterFactory = new ParameterFactoryByType();
538         parameterFactory.addParameter(Tailgating.RHO, 0.0);
539         parameterFactory.addParameter(network.getGtuType(GTUType.DEFAULTS.CAR), LmrsParameters.SOCIO, 0.5);
540         parameterFactory.addParameter(network.getGtuType(GTUType.DEFAULTS.TRUCK), LmrsParameters.SOCIO, 1.0);
541         parameterFactory.addParameter(network.getGtuType(GTUType.DEFAULTS.CAR), LmrsParameters.VGAIN,
542                 new Speed(35.0, SpeedUnit.KM_PER_HOUR));
543         parameterFactory.addParameter(network.getGtuType(GTUType.DEFAULTS.TRUCK), LmrsParameters.VGAIN,
544                 new Speed(50.0, SpeedUnit.KM_PER_HOUR));
545         parameterFactory.addParameter(ParameterTypes.TMAX, Duration.instantiateSI(1.6));
546         parameterFactory.addParameter(network.getGtuType(GTUType.DEFAULTS.CAR), ParameterTypes.FSPEED,
547                 new DistNormal(this.stream, 123.7 / 120.0, 12.0 / 120.0));
548         parameterFactory.addParameter(network.getGtuType(GTUType.DEFAULTS.TRUCK), ParameterTypes.A, Acceleration.instantiateSI(0.4));
549         parameterFactory.addParameter(network.getGtuType(GTUType.DEFAULTS.TRUCK), ParameterTypes.FSPEED, 1.0);
550         for (GTUType gtuType : new GTUType[] {network.getGtuType(GTUType.DEFAULTS.CAR),
551                 network.getGtuType(GTUType.DEFAULTS.TRUCK)})
552         {
553             // incentives
554             Set<MandatoryIncentive> mandatoryIncentives = new LinkedHashSet<>();
555             Set<VoluntaryIncentive> voluntaryIncentives = new LinkedHashSet<>();
556             Set<AccelerationIncentive> accelerationIncentives = new LinkedHashSet<>();
557             mandatoryIncentives.add(new IncentiveRoute());
558             voluntaryIncentives.add(new IncentiveSpeedWithCourtesy());
559             voluntaryIncentives.add(new IncentiveKeep());
560             voluntaryIncentives.add(new IncentiveSocioSpeed());
561             if (gtuType.equals(network.getGtuType(GTUType.DEFAULTS.TRUCK)))
562             {
563                 voluntaryIncentives.add(new IncentiveStayRight());
564             }
565             // car-following factory
566             CarFollowingModelFactory<?> cfFactory = // trucks don't change their desired speed
567                     gtuType.equals(network.getGtuType(GTUType.DEFAULTS.CAR)) ? new SocioIDMFactory()
568                             : new IDMPlusFactory(this.stream);
569             // tailgating
570             Tailgating tlgt = Tailgating.PRESSURE;
571             // strategical and tactical factory
572             LaneBasedStrategicalPlannerFactory<?> laneBasedStrategicalPlannerFactory =
573                     new LaneBasedStrategicalRoutePlannerFactory(new LMRSFactory(cfFactory, perceptionFactory,
574                             Synchronization.PASSIVE, Cooperation.PASSIVE, GapAcceptance.INFORMED, tlgt, mandatoryIncentives,
575                             voluntaryIncentives, accelerationIncentives), parameterFactory);
576             this.factories.put(gtuType, laneBasedStrategicalPlannerFactory);
577         }
578         for (int i = 0; i < lanes1.size(); i++)
579         {
580             Length pos = Length.instantiateSI(10.0);
581             Length gap = lanes1.get(i).getLength().plus(lanes2.get(i).getLength()).divide(this.gtuNum / 2);
582             for (int j = 0; j < 2; j++)
583             {
584                 Lane lane = j == 0 ? lanes1.get(i) : lanes2.get(i);
585                 while (true)
586                 {
587                     GTUType gtuType;
588                     if (i == 0)
589                     {
590                         gtuType = network.getGtuType(GTUType.DEFAULTS.CAR);
591                     }
592                     else
593                     {
594                         gtuType = this.stream.nextDouble() < 2 * this.truckFraction ? network.getGtuType(GTUType.DEFAULTS.TRUCK)
595                                 : network.getGtuType(GTUType.DEFAULTS.CAR);
596                     }
597 
598                     Speed initialSpeed = Speed.ZERO;
599 
600                     createGtu(lane, pos, gtuType, initialSpeed, network);
601 
602                     pos = pos.plus(gap);
603                     if (pos.si > lane.getLength().si)
604                     {
605                         pos = pos.minus(lane.getLength());
606                         break;
607                     }
608                 }
609             }
610         }
611 
612         this.nextGtuType = this.stream.nextDouble() < this.truckFraction ? network.getGtuType(GTUType.DEFAULTS.TRUCK)
613                 : network.getGtuType(GTUType.DEFAULTS.CAR);
614         sim.scheduleEventNow(this, this, "checkVehicleNumber", new Object[] {});
615 
616         return network;
617     }
618 
619     /**
620      * Creates a GTU.
621      * @param lane Lane; lane
622      * @param pos Length; position
623      * @param gtuType GTUType; GTU type
624      * @param initialSpeed Speed; initial speed
625      * @param net OTSRoadNetwork; network
626      * @throws NamingException on exception
627      * @throws GTUException on exception
628      * @throws NetworkException on exception
629      * @throws SimRuntimeException on exception
630      * @throws OTSGeometryException on exception
631      */
632     public void createGtu(final Lane lane, final Length pos, final GTUType gtuType, final Speed initialSpeed,
633             final OTSRoadNetwork net)
634             throws NamingException, GTUException, NetworkException, SimRuntimeException, OTSGeometryException
635     {
636         GTUCharacteristics gtuCharacteristics = Try.assign(() -> GTUType.defaultCharacteristics(gtuType, net, this.stream),
637                 "Exception while applying default GTU characteristics.");
638 
639         LaneBasedIndividualGTU gtu = new LaneBasedIndividualGTU("" + (++this.gtuIdNum), gtuType, gtuCharacteristics.getLength(),
640                 gtuCharacteristics.getWidth(), gtuCharacteristics.getMaximumSpeed(), gtuCharacteristics.getFront(),
641                 getSimulator(), net);
642         gtu.setMaximumAcceleration(gtuCharacteristics.getMaximumAcceleration());
643         gtu.setMaximumDeceleration(gtuCharacteristics.getMaximumDeceleration());
644         gtu.setNoLaneChangeDistance(Length.instantiateSI(50));
645         gtu.setInstantaneousLaneChange(true);
646 
647         // strategical planner
648         LaneBasedStrategicalPlanner strategicalPlanner = this.factories.get(gtuType).create(gtu, null, null, null);
649 
650         // init
651         Set<DirectedLanePosition> initialPositions = new LinkedHashSet<>(1);
652         initialPositions.add(new DirectedLanePosition(lane, pos, GTUDirectionality.DIR_PLUS));
653         if (pos.plus(gtu.getFront().getDx()).gt(lane.getLength()))
654         {
655             Lane nextLane = lane.nextLanes(gtuType).keySet().iterator().next();
656             Length nextPos = pos.minus(lane.getLength());
657             initialPositions.add(new DirectedLanePosition(nextLane, nextPos, GTUDirectionality.DIR_PLUS));
658         }
659         if (pos.plus(gtu.getRear().getDx()).lt0())
660         {
661             Lane prevLane = lane.prevLanes(gtuType).keySet().iterator().next();
662             Length prevPos = prevLane.getLength().plus(pos.plus(gtu.getRear().getDx()));
663             initialPositions.add(new DirectedLanePosition(prevLane, prevPos, GTUDirectionality.DIR_PLUS));
664         }
665         gtu.init(strategicalPlanner, initialPositions, initialSpeed);
666 
667         Try.execute(() -> gtu.addListener(this.kmplcListener, LaneBasedGTU.LANE_CHANGE_EVENT),
668                 "Exception while adding lane change listener");
669     }
670 
671     /** IDM factory with socio speed. */
672     class SocioIDMFactory implements CarFollowingModelFactory<IDMPlus>
673     {
674         /** {@inheritDoc} */
675         @Override
676         public Parameters getParameters() throws ParameterException
677         {
678             ParameterSet parameters = new ParameterSet();
679             parameters.setDefaultParameters(AbstractIDM.class);
680             return parameters;
681         }
682 
683         /** {@inheritDoc} */
684         @Override
685         public IDMPlus generateCarFollowingModel()
686         {
687             return new IDMPlus(AbstractIDM.HEADWAY, new SocioDesiredSpeed(AbstractIDM.DESIRED_SPEED));
688         }
689     }
690 
691     /** Perception factory. */
692     class LmrsStrategiesPerceptionFactory implements PerceptionFactory
693     {
694         /** {@inheritDoc} */
695         @Override
696         public LanePerception generatePerception(final LaneBasedGTU gtu)
697         {
698             LanePerception perception = new CategoricalLanePerception(gtu);
699             perception.addPerceptionCategory(new DirectEgoPerception<>(perception));
700             perception.addPerceptionCategory(new DirectInfrastructurePerception(perception));
701             perception.addPerceptionCategory(new DirectNeighborsPerception(perception, HeadwayGtuType.WRAP));
702             perception.addPerceptionCategory(new AnticipationTrafficPerception(perception));
703             return perception;
704         }
705 
706         /** {@inheritDoc} */
707         @Override
708         public Parameters getParameters() throws ParameterException
709         {
710             return new ParameterSet().setDefaultParameter(ParameterTypes.LOOKAHEAD)
711                     .setDefaultParameter(ParameterTypes.LOOKBACKOLD).setDefaultParameter(ParameterTypes.PERCEPTION)
712                     .setDefaultParameter(ParameterTypes.LOOKBACK);
713         }
714     }
715 
716 }