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