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