View Javadoc
1   package org.opentrafficsim.road.gtu.generator;
2   
3   import java.util.ArrayList;
4   import java.util.Collections;
5   import java.util.Comparator;
6   import java.util.LinkedHashMap;
7   import java.util.LinkedHashSet;
8   import java.util.List;
9   import java.util.Map;
10  import java.util.Objects;
11  import java.util.Set;
12  
13  import org.djunits.unit.SpeedUnit;
14  import org.djunits.value.vdouble.scalar.Length;
15  import org.djunits.value.vdouble.scalar.Speed;
16  import org.djutils.exceptions.Throw;
17  import org.opentrafficsim.core.gtu.GtuException;
18  import org.opentrafficsim.core.gtu.GtuType;
19  import org.opentrafficsim.core.math.Draw;
20  import org.opentrafficsim.core.network.Link;
21  import org.opentrafficsim.core.network.NetworkException;
22  import org.opentrafficsim.core.network.Node;
23  import org.opentrafficsim.core.network.route.Route;
24  import org.opentrafficsim.road.gtu.generator.GeneratorPositions.RoadPosition.BySpeed;
25  import org.opentrafficsim.road.gtu.generator.GeneratorPositions.RoadPosition.ByValue;
26  import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGtuCharacteristics;
27  import org.opentrafficsim.road.network.lane.CrossSectionLink;
28  import org.opentrafficsim.road.network.lane.Lane;
29  import org.opentrafficsim.road.network.lane.LanePosition;
30  
31  import nl.tudelft.simulation.jstats.streams.StreamInterface;
32  
33  /**
34   * Helper class for vehicle generation which can draw the next GTU position to try to place a GTU. If the GTU can not be placed,
35   * it should be included in a queue. This class requires the number of unplaced GTU's per lane, in order to appropriately divide
36   * traffic over the lanes.
37   * <p>
38   * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
39   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
40   * </p>
41   * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
42   * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
43   * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
44   */
45  public interface GeneratorPositions
46  {
47  
48      /**
49       * Draw a new position to generate a GTU.
50       * @param gtuType GtuType; GTU type.
51       * @param characteristics LaneBasedGtuCharacteristics; characteristics of the generated GTU.
52       * @param unplaced Map&lt;CrossSectionLink, Map&lt;Integer, Integer&gt;&gt;; number of unplaced GTUs per lane, counting from
53       *            the right and starting at 1.
54       * @return GeneratorLanePosition; new position to generate a GTU.
55       * @throws GtuException when the underlying structure is inconsistent for drawing
56       */
57      GeneratorLanePosition draw(GtuType gtuType, LaneBasedGtuCharacteristics characteristics,
58              Map<CrossSectionLink, Map<Integer, Integer>> unplaced) throws GtuException;
59  
60      /**
61       * Returns all underlying positions.
62       * @return all underlying positions.
63       */
64      Set<GeneratorLanePosition> getAllPositions();
65  
66      /**
67       * Create a GeneratorPositions object to draw positions from. The given positions are grouped per link. Lanes are drawn
68       * without bias. Each link receives a weight equal to the number of lanes.
69       * @param positions Set&lt;LanePosition&gt;; all considered positions, each lane is considered separately
70       * @param stream StreamInterface; stream for random numbers
71       * @return GeneratorPositions; object to draw positions from
72       */
73      static GeneratorPositions create(final Set<LanePosition> positions, final StreamInterface stream)
74      {
75          return create(positions, stream, null, null, null);
76      }
77  
78      /**
79       * Create a GeneratorPositions object to draw positions from. The given positions are grouped per link. Each link receives a
80       * weight equal to the number of lanes.
81       * @param positions Set&lt;LanePosition&gt;; all considered positions, each lane is considered separately
82       * @param stream StreamInterface; stream for random numbers
83       * @param biases LaneBiases; lane biases for GTU types
84       * @return GeneratorPositions; object to draw positions from
85       */
86      static GeneratorPositions create(final Set<LanePosition> positions, final StreamInterface stream, final LaneBiases biases)
87      {
88          return create(positions, stream, biases, null, null);
89      }
90  
91      /**
92       * Create a GeneratorPositions object to draw positions from. The given positions are grouped per link. Lanes are drawn
93       * without bias.
94       * @param positions Set&lt;LanePosition&gt;; all considered positions, each lane is considered separately
95       * @param stream StreamInterface; stream for random numbers
96       * @param linkWeights Map&lt;CrossSectionLink, Double&gt;; weight per link direction
97       * @param viaNodes Map&lt;CrossSectionLink, Node&gt;; nodes connectors feed to for each link where GTU's will be generated
98       * @return GeneratorPositions; object to draw positions from
99       */
100     static GeneratorPositions create(final Set<LanePosition> positions, final StreamInterface stream,
101             final Map<CrossSectionLink, Double> linkWeights, final Map<CrossSectionLink, Node> viaNodes)
102     {
103         return create(positions, stream, null, linkWeights, viaNodes);
104     }
105 
106     /**
107      * Create a GeneratorPositions object to draw positions from. The given positions are grouped per link.
108      * @param positions Set&lt;LanePosition&gt;; all considered positions, each lane is considered separately
109      * @param stream StreamInterface; stream for random numbers
110      * @param laneBiases LaneBiases; lane biases for GTU types
111      * @param linkWeights Map&lt;CrossSectionLink, Double&gt;; weight per link
112      * @param viaNodes Map&lt;CrossSectionLink, Node&gt;; nodes connectors feed to for each link where GTU's will be generated
113      * @return GeneratorPositions; object to draw positions from
114      */
115     static GeneratorPositions create(final Set<LanePosition> positions, final StreamInterface stream,
116             final LaneBiases laneBiases, final Map<CrossSectionLink, Double> linkWeights,
117             final Map<CrossSectionLink, Node> viaNodes)
118     {
119 
120         // group directions per link
121         Map<Link, Set<LanePosition>> linkSplit = new LinkedHashMap<>();
122         for (LanePosition position : positions)
123         {
124             linkSplit.computeIfAbsent(position.getLane().getParentLink(), (link) -> new LinkedHashSet<>()).add(position);
125         }
126 
127         // create list of GeneratorLinkPositions
128         List<GeneratorLinkPosition> linkPositions = new ArrayList<>();
129         Set<GeneratorLanePosition> allLanePositions = new LinkedHashSet<>();
130         for (Link splitLink : linkSplit.keySet())
131         {
132             List<Lane> lanes = ((CrossSectionLink) splitLink).getLanes();
133             // let's sort the lanes by lateral position
134             Collections.sort(lanes, new Comparator<Lane>()
135             {
136                 /** {@inheritDoc} */
137                 @Override
138                 public int compare(final Lane lane1, final Lane lane2)
139                 {
140                     Length lat1 = lane1.getDesignLineOffsetAtBegin();
141                     Length lat2 = lane2.getDesignLineOffsetAtBegin();
142                     return lat1.compareTo(lat2);
143                 }
144             });
145             // create list of GeneratorLanePositions
146             List<GeneratorLanePosition> lanePositions = new ArrayList<>();
147             for (LanePosition lanePosition : linkSplit.get(splitLink))
148             {
149                 Set<LanePosition> set = new LinkedHashSet<>();
150                 set.add(lanePosition);
151                 lanePositions.add(new GeneratorLanePosition(lanes.indexOf(lanePosition.getLane()) + 1, set,
152                         (CrossSectionLink) splitLink));
153             }
154             allLanePositions.addAll(lanePositions);
155             // create the GeneratorLinkPosition
156             if (linkWeights == null)
157             {
158                 linkPositions.add(new GeneratorLinkPosition(lanePositions, splitLink, stream, laneBiases));
159             }
160             else
161             {
162                 Double weight = linkWeights.get(splitLink);
163                 Throw.whenNull(weight, "Using link weights for GTU generation, but no weight for link %s is defined.",
164                         splitLink);
165                 linkPositions.add(new GeneratorLinkPosition(lanePositions, splitLink, stream, laneBiases, weight,
166                         viaNodes.get(splitLink)));
167             }
168         }
169 
170         // create the GeneratorZonePosition
171         GeneratorZonePosition position = new GeneratorZonePosition(linkPositions);
172         return new GeneratorPositions()
173         {
174             /** {@inheritDoc} */
175             @Override
176             public GeneratorLanePosition draw(final GtuType gtuType, final LaneBasedGtuCharacteristics characteristics,
177                     final Map<CrossSectionLink, Map<Integer, Integer>> unplaced) throws GtuException
178             {
179                 GeneratorLinkPosition linkPosition =
180                         position.draw(gtuType, stream, characteristics.getDestination(), characteristics.getRoute());
181                 Speed desiredSpeed = characteristics.getStrategicalPlannerFactory().peekDesiredSpeed(gtuType,
182                         linkPosition.speedLimit(gtuType), characteristics.getMaximumSpeed());
183                 return linkPosition.draw(gtuType, unplaced.get(linkPosition.getLink()), desiredSpeed);
184             }
185 
186             /** {@inheritDoc} */
187             @Override
188             public Set<GeneratorLanePosition> getAllPositions()
189             {
190                 return allLanePositions;
191             }
192         };
193     }
194 
195     /**
196      * Class representing a vehicle generation lane, providing elementary information for randomly drawing links and lanes.
197      * <p>
198      * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
199      * <br>
200      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
201      * </p>
202      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
203      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
204      * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
205      */
206     final class GeneratorLanePosition
207     {
208 
209         /** Lane number, where 1 is the right-most lane. */
210         private final int laneNumber;
211 
212         /** Position set, representing a single GTU position on the network. */
213         private final Set<LanePosition> position;
214 
215         /** Link. */
216         private final CrossSectionLink link;
217 
218         /**
219          * Constructor.
220          * @param laneNumber int; lane number, where 1 is the right-most lane
221          * @param position Set&lt;LanePosition&gt;; position set, representing a single GTU position on the network
222          * @param link CrossSectionLink; link
223          */
224         GeneratorLanePosition(final int laneNumber, final Set<LanePosition> position, final CrossSectionLink link)
225         {
226             this.laneNumber = laneNumber;
227             this.position = position;
228             this.link = link;
229         }
230 
231         /**
232          * Returns the lane number, where 1 is the right-most lane.
233          * @return lane number, where 1 is the right-most lane
234          */
235         int getLaneNumber()
236         {
237             return this.laneNumber;
238         }
239 
240         /**
241          * Returns whether this lane is accessible to the GTU type.
242          * @param gtuType GtuType; gtu type
243          * @return boolean; whether this lane is accessible to the GTU type
244          */
245         boolean allows(final GtuType gtuType)
246         {
247             for (LanePosition pos : this.position)
248             {
249                 if (pos.getLane().getType().isCompatible(gtuType))
250                 {
251                     return true;
252                 }
253             }
254             return false;
255         }
256 
257         /**
258          * Returns the contained position set, representing a single GTU position on the network.
259          * @return Set&lt;LanePosition&gt;; contained position set, representing a single GTU position on the network
260          */
261         Set<LanePosition> getPosition()
262         {
263             return this.position;
264         }
265 
266         /**
267          * Returns the link.
268          * @return CrossSectionLink; link
269          */
270         CrossSectionLink getLink()
271         {
272             return this.link;
273         }
274 
275         /** {@inheritDoc} */
276         @Override
277         public int hashCode()
278         {
279             return Objects.hash(this.laneNumber, this.link, this.position);
280         }
281 
282         /** {@inheritDoc} */
283         @Override
284         public boolean equals(final Object obj)
285         {
286             if (this == obj)
287             {
288                 return true;
289             }
290             if (obj == null)
291             {
292                 return false;
293             }
294             if (getClass() != obj.getClass())
295             {
296                 return false;
297             }
298             GeneratorLanePosition other = (GeneratorLanePosition) obj;
299             return this.laneNumber == other.laneNumber && Objects.equals(this.link, other.link)
300                     && Objects.equals(this.position, other.position);
301         }
302 
303         /** {@inheritDoc} */
304         @Override
305         public String toString()
306         {
307             return "GeneratorLanePosition [laneNumber=" + this.laneNumber + ", position=" + this.position + ", link="
308                     + this.link + "]";
309         }
310 
311     }
312 
313     /**
314      * Class representing a vehicle generation link to provide individual generation positions.
315      * <p>
316      * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
317      * <br>
318      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
319      * </p>
320      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
321      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
322      * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
323      */
324     final class GeneratorLinkPosition
325     {
326 
327         /** Contained lanes. */
328         private final List<GeneratorLanePosition> positions;
329 
330         /** The link. */
331         private final Link link;
332 
333         /** Random stream. */
334         private final StreamInterface stream;
335 
336         /** Lane bias. */
337         private final LaneBiases laneBiases;
338 
339         /** Weight for drawing this link. */
340         private final double weight;
341 
342         /** Node by which a connector connects, may be {@code null}. */
343         private final Node viaNode;
344 
345         /**
346          * Constructor.
347          * @param positions List&lt;GeneratorLanePosition&gt;; contained lanes
348          * @param link Link; the link
349          * @param stream StreamInterface; stream
350          * @param laneBiases LaneBiases; lane biases
351          */
352         GeneratorLinkPosition(final List<GeneratorLanePosition> positions, final Link link, final StreamInterface stream,
353                 final LaneBiases laneBiases)
354         {
355             this.positions = positions;
356             this.link = link;
357             this.stream = stream;
358             this.laneBiases = laneBiases;
359             this.weight = -1;
360             this.viaNode = null;
361         }
362 
363         /**
364          * Constructor.
365          * @param positions List&lt;GeneratorLanePosition&gt;; contained lanes
366          * @param link Link; the link
367          * @param stream StreamInterface; stream
368          * @param laneBiases LaneBiases; lane biases
369          * @param weight double; weight for drawing this link
370          * @param viaNode Node; node by which a connector connects
371          */
372         GeneratorLinkPosition(final List<GeneratorLanePosition> positions, final Link link, final StreamInterface stream,
373                 final LaneBiases laneBiases, final double weight, final Node viaNode)
374         {
375             this.positions = positions;
376             this.link = link;
377             this.stream = stream;
378             this.laneBiases = laneBiases;
379             this.weight = weight;
380             this.viaNode = viaNode;
381         }
382 
383         /**
384          * Return the link.
385          * @return CrossSectionLink; link
386          */
387         Link getLink()
388         {
389             return this.link;
390         }
391 
392         /**
393          * Returns the weight for this link. This is either a predefined weight, or the number of lanes for the GTU type.
394          * @param gtuType GtuType; GTU type
395          * @return double; weight for this link
396          */
397         double getWeight(final GtuType gtuType)
398         {
399             if (this.weight < 0.0)
400             {
401                 return getNumberOfLanes(gtuType);
402             }
403             return this.weight;
404         }
405 
406         /**
407          * Returns the node by which a connector connects.
408          * @return the node by which a connector connects
409          */
410         Node getViaNode()
411         {
412             return this.viaNode;
413         }
414 
415         /**
416          * Returns the number of accessible lanes for the GTU type.
417          * @param gtuType GtuType; GTU type
418          * @return int; number of accessible lanes for the GTU type
419          */
420         int getNumberOfLanes(final GtuType gtuType)
421         {
422             int numberOfLanes = 0;
423             for (GeneratorLanePosition lanePosition : this.positions)
424             {
425                 if (lanePosition.allows(gtuType))
426                 {
427                     numberOfLanes++;
428                 }
429             }
430             return numberOfLanes;
431         }
432 
433         /**
434          * Draws a specific GeneratorLanePosition utilizing lane biases of GTU types.
435          * @param gtuType GtuType; GTU type
436          * @param unplaced Map&lt;Integer, Integer&gt;; number of unplaced GTUs per lane. The lane number should match with
437          *            {@code GeneratorLanePosition.getLaneNumber()}, where 1 is the right-most lane. Missing lanes are assumed
438          *            to have no queue.
439          * @param desiredSpeed Speed; desired speed, possibly used to determine the biased road position
440          * @return GeneratorLanePosition; specific GeneratorLanePosition utilizing lane biases of GTU types
441          */
442         GeneratorLanePosition draw(final GtuType gtuType, final Map<Integer, Integer> unplaced, final Speed desiredSpeed)
443         {
444             Map<GeneratorLanePosition, Double> map = new LinkedHashMap<>();
445             for (int i = 0; i < this.positions.size(); i++)
446             {
447                 GeneratorLanePosition lanePosition = this.positions.get(i);
448                 if (lanePosition.allows(gtuType))
449                 {
450                     GtuType type = gtuType;
451                     boolean found = false;
452                     while (this.laneBiases != null && !found && type != null)
453                     {
454                         if (this.laneBiases.contains(type))
455                         {
456                             found = true;
457                             int laneNum = lanePosition.getLaneNumber();
458                             int unplacedTemplates = unplaced == null ? 0 : unplaced.getOrDefault(laneNum, 0);
459                             double w = this.laneBiases.getBias(type).calculateWeight(laneNum, getNumberOfLanes(gtuType),
460                                     unplacedTemplates, desiredSpeed);
461                             map.put(lanePosition, w);
462                         }
463                         type = type.getParent();
464                     }
465                     if (!found)
466                     {
467                         map.put(lanePosition, 1.0);
468                     }
469                 }
470             }
471             if (0 == map.size())
472             {
473                 System.err.println("This really, really can't work...");
474             }
475             return Draw.drawWeighted(map, this.stream);
476         }
477 
478         /** {@inheritDoc} */
479         @Override
480         public String toString()
481         {
482             return "GeneratorLinkPosition [positions=" + this.positions + "]";
483         }
484 
485         /**
486          * @param gtuType GtuType; GTU type
487          * @return Speed; speed limit
488          */
489         public Speed speedLimit(final GtuType gtuType)
490         {
491             Speed speedLimit = null;
492             for (GeneratorLanePosition pos : this.positions)
493             {
494                 for (LanePosition lane : pos.getPosition())
495                 {
496                     try
497                     {
498                         Speed limit = lane.getLane().getSpeedLimit(gtuType);
499                         if (speedLimit == null || limit.lt(speedLimit))
500                         {
501                             speedLimit = limit;
502                         }
503                     }
504                     catch (NetworkException exception)
505                     {
506                         // ignore
507                     }
508                 }
509             }
510             Throw.when(speedLimit == null, IllegalStateException.class, "No speed limit could be determined for GtuType %s.",
511                     gtuType);
512             return speedLimit;
513         }
514 
515     }
516 
517     /**
518      * Class representing a vehicle generation zone to provide individual generation positions.
519      * <p>
520      * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
521      * <br>
522      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
523      * </p>
524      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
525      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
526      * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
527      */
528     final class GeneratorZonePosition
529     {
530 
531         /** Contained links. */
532         private final List<GeneratorLinkPosition> positions;
533 
534         /**
535          * Constructor.
536          * @param positions List&lt;GeneratorLinkPosition&gt;; contained links
537          */
538         GeneratorZonePosition(final List<GeneratorLinkPosition> positions)
539         {
540             this.positions = positions;
541         }
542 
543         /**
544          * Draws a GeneratorLinkPosition using number of accessible lanes for the GtuType as weight, and a GeneratorLanePosition
545          * from that.
546          * @param gtuType GtuType; GTU type
547          * @param stream StreamInterface; stream for random numbers
548          * @param destination Node; destination node
549          * @param route Route; route, may be {@code null}
550          * @return GeneratorLanePosition; draws a LinkPosition using number of accessible lanes for the GtuType as weight, and a
551          *         GeneratorLanePosition from that
552          */
553         GeneratorLinkPosition draw(final GtuType gtuType, final StreamInterface stream, final Node destination,
554                 final Route route)
555         {
556             Map<GeneratorLinkPosition, Double> map = new LinkedHashMap<>();
557             for (int i = 0; i < this.positions.size(); i++)
558             {
559                 GeneratorLinkPosition glp = this.positions.get(i);
560                 Link link = glp.getLink();
561                 if (route != null)
562                 {
563                     int from = route.indexOf(link.getStartNode());
564                     int to = route.indexOf(link.getEndNode());
565                     if (from > -1 && to > -1 && to - from == 1)
566                     {
567                         map.put(glp, glp.getWeight(gtuType));
568                     }
569                 }
570                 else
571                 {
572                     // let's check whether any route is possible over this link
573                     if (glp.getViaNode() != null)
574                     {
575                         Route r;
576                         try
577                         {
578                             r = glp.getViaNode().getNetwork().getShortestRouteBetween(gtuType, glp.getViaNode(), destination);
579                         }
580                         catch (NetworkException exception)
581                         {
582                             r = null;
583                         }
584                         if (r != null)
585                         {
586                             map.put(glp, glp.getWeight(gtuType));
587                         }
588                     }
589                     else
590                     {
591                         map.put(glp, glp.getWeight(gtuType));
592                     }
593                 }
594             }
595             return Draw.drawWeighted(map, stream);
596         }
597 
598         /** {@inheritDoc} */
599         @Override
600         public String toString()
601         {
602             return "GeneratorZonePosition [positions=" + this.positions + "]";
603         }
604 
605     }
606 
607     /**
608      * Set of lane biases per GTU type.
609      * <p>
610      * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
611      * <br>
612      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
613      * </p>
614      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
615      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
616      * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
617      */
618     final class LaneBiases
619     {
620 
621         /** Biases per GTU type. */
622         private final Map<GtuType, LaneBias> biases = new LinkedHashMap<>();
623 
624         /**
625          * Adds a GTU bias for randomly drawing a lane.
626          * @param gtuType GtuType; gtu type
627          * @param bias LaneBias; bias
628          * @return LaneBiases; lane biases for method chaining
629          */
630         public LaneBiases addBias(final GtuType gtuType, final LaneBias bias)
631         {
632             Throw.whenNull(gtuType, "GTU type may not be null.");
633             Throw.whenNull(bias, "Bias may not be null.");
634             this.biases.put(gtuType, bias);
635             return this;
636         }
637 
638         /**
639          * Whether a bias is defined for the given type.
640          * @param gtuType GtuType; GTU type
641          * @return whether a bias is defined for the given type
642          */
643         public boolean contains(final GtuType gtuType)
644         {
645             return this.biases.containsKey(gtuType);
646         }
647 
648         /**
649          * Returns the bias of given GTU type, or {@code Bias.None} if none defined for the GTU type.
650          * @param gtuType GtuType; GTU type
651          * @return Bias; bias of the GTU type
652          */
653         public LaneBias getBias(final GtuType gtuType)
654         {
655             return this.biases.getOrDefault(gtuType, LaneBias.NONE);
656         }
657 
658         /** {@inheritDoc} */
659         @Override
660         public String toString()
661         {
662             return "LaneBiases [" + this.biases + "]";
663         }
664 
665     }
666 
667     /**
668      * Vehicle generation lateral bias. Includes a lane maximum, e.g. trucks only on 2 right-hand lanes.
669      * <p>
670      * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
671      * <br>
672      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
673      * </p>
674      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
675      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
676      * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
677      */
678     final class LaneBias
679     {
680 
681         /** No bias. */
682         public static final LaneBias NONE = new LaneBias(new ByValue(0.0), 0.0, Integer.MAX_VALUE);
683 
684         /** Weak left-hand bias, 2nd left lane contains 50% relative to left most lane, in free traffic. */
685         public static final LaneBias WEAK_LEFT = new LaneBias(new ByValue(1.0), 1.0, Integer.MAX_VALUE);
686 
687         /** Left-hand bias, 2nd left lane contains 25% relative to left most lane, in free traffic. */
688         public static final LaneBias LEFT = new LaneBias(new ByValue(1.0), 2.0, Integer.MAX_VALUE);
689 
690         /** Strong left-hand bias, 2nd left lane contains 3.125% relative to left most lane, in free traffic. */
691         public static final LaneBias STRONG_LEFT = new LaneBias(new ByValue(1.0), 5.0, Integer.MAX_VALUE);
692 
693         /** Weak middle bias, 2nd left lane contains 50% relative to left most lane, in free traffic. */
694         public static final LaneBias WEAK_MIDDLE = new LaneBias(new ByValue(0.5), 1.0, Integer.MAX_VALUE);
695 
696         /** Middle bias, 2nd left lane contains 25% relative to left most lane, in free traffic. */
697         public static final LaneBias MIDDLE = new LaneBias(new ByValue(0.5), 2.0, Integer.MAX_VALUE);
698 
699         /** Strong middle bias, 2nd left lane contains 3.125% relative to left most lane, in free traffic. */
700         public static final LaneBias STRONG_MIDDLE = new LaneBias(new ByValue(0.5), 5.0, Integer.MAX_VALUE);
701 
702         /** Weak right-hand bias, 2nd right lane contains 50% relative to right most lane, in free traffic. */
703         public static final LaneBias WEAK_RIGHT = new LaneBias(new ByValue(0.0), 1.0, Integer.MAX_VALUE);
704 
705         /** Right-hand bias, 2nd right lane contains 25% relative to right most lane, in free traffic. */
706         public static final LaneBias RIGHT = new LaneBias(new ByValue(0.0), 2.0, Integer.MAX_VALUE);
707 
708         /** Strong right-hand bias, 2nd right lane contains 3.125% relative to right most lane, in free traffic. */
709         public static final LaneBias STRONG_RIGHT = new LaneBias(new ByValue(0.0), 5.0, Integer.MAX_VALUE);
710 
711         /** Strong right-hand bias, limited to a maximum of 2 lanes. */
712         public static final LaneBias TRUCK_RIGHT = new LaneBias(new ByValue(0.0), 5.0, 2);
713 
714         /**
715          * Returns a bias by speed with normal extent.
716          * @param leftSpeed Speed; desired speed for full left bias
717          * @param rightSpeed Speed; desired speed for full right bias
718          * @return bias by speed with normal extent
719          */
720         public static LaneBias bySpeed(final Speed leftSpeed, final Speed rightSpeed)
721         {
722             return new LaneBias(new BySpeed(leftSpeed, rightSpeed), 2.0, Integer.MAX_VALUE);
723         }
724 
725         /**
726          * Returns a bias by speed with normal extent. Convenience km/h input.
727          * @param leftSpeedKm double; desired speed for full left bias
728          * @param rightSpeedKm double; desired speed for full right bias
729          * @return bias by speed with normal extent
730          */
731         public static LaneBias bySpeed(final double leftSpeedKm, final double rightSpeedKm)
732         {
733             return bySpeed(new Speed(leftSpeedKm, SpeedUnit.KM_PER_HOUR), new Speed(rightSpeedKm, SpeedUnit.KM_PER_HOUR));
734         }
735 
736         /** Provider of position on the road (0 = full left, 1 = full right). */
737         private final RoadPosition roadPosition;
738 
739         /** Bias extent. */
740         private final double bias;
741 
742         /** Number of lanes to consider in either direction, including the preferred lane. */
743         private final double stickyLanes;
744 
745         /**
746          * Constructor.
747          * @param roadPosition RoadPosition; lateral position on the road (0 = right, 0.5 = middle, 1 = left)
748          * @param bias double; bias extent, lower values create more spread traffic, 0.0 causes no lane preference
749          * @param stickyLanes double; number of lanes to consider in either direction, including the preferred lane
750          */
751         public LaneBias(final RoadPosition roadPosition, final double bias, final double stickyLanes)
752         {
753             Throw.when(bias < 0.0, IllegalArgumentException.class, "Bias should be positive or 0.");
754             Throw.when(stickyLanes < 1.0, IllegalArgumentException.class, "Sticky lanes should be 1.0 or larger.");
755             this.roadPosition = roadPosition;
756             this.bias = bias;
757             this.stickyLanes = stickyLanes;
758         }
759 
760         /**
761          * Returns a random draw weight for given lane. The weight is calculated as:
762          * 
763          * <pre>
764          * weight = { 0,                               d &gt;= number of sticky lanes
765          *          { 1 / ((d + 1)^bias * (m + 1)),    otherwise
766          * 
767          * where,
768          *      d:      lane deviation from lateral bias position
769          *      bias:   bias extent
770          *      m:      number of unplaced GTU's
771          * </pre>
772          * 
773          * The formula makes sure that all lanes have equal weight for <i>bias</i> &#61; 0, given an equal number of unplaced
774          * GTU's <i>m</i>. The bias can be seen to result in this: for each GTU on the 2nd lane, there are 2^(<i>bias</i> - 1)
775          * GTU's on the 1st lane. In numbers: 1 vs. 1 for <i>bias</i> &#61; 0, 1 vs. 2 for <i>bias</i> &#61; 1, 1 vs. 4 for
776          * <i>bias</i> &#61; 2, 1 vs. 8 for <i>bias</i> &#61; 3, etc.<br>
777          * <br>
778          * Division by <i>m</i> + 1 makes sure traffic distributes over the lanes in case of spillback, or otherwise too high
779          * demand on a particular lane. The weight for lanes with more unplaced GTU's simply reduces. This effect balances out
780          * with the bias, meaning that for a strong bias, GTU's are still likely to be generated on the biased lanes. Given a
781          * relatively strong bias of <i>bias</i> &#61; 5, the weight for the 1st and 2nd lane becomes equal if the 2nd lane has
782          * no unplaced GTU's, while the 1st lane has 31 unplaced GTU's.<br>
783          * <br>
784          * Lane deviation <i>d</i> is calculated as <i>d</i> &#61; abs(<i>latBiasLane</i> - <i>laneNumFromRight</i>). Here,
785          * <i>latBiasLane</i> &#61; 1 + <i>roadPosition</i>*(<i>numberOfLanes</i> - 1), i.e. ranging from 1 to 4 on a 4-lane
786          * road. For lanes that are beyond the number of sticky lanes, the weight is always 0.<br>
787          * <br>
788          * @param laneNumFromRight int; number of lane counted from right to left
789          * @param numberOfLanes int; total number of lanes
790          * @param numberOfUnplacedGTUs int; number of GTU's in the generation queue
791          * @param desiredSpeed Speed; desired speed, possibly used to determine the biased road position
792          * @return double; random draw weight for given lane
793          */
794         public double calculateWeight(final int laneNumFromRight, final int numberOfLanes, final int numberOfUnplacedGTUs,
795                 final Speed desiredSpeed)
796         {
797             double d = Math.abs((1.0 + this.roadPosition.getValue(desiredSpeed) * (numberOfLanes - 1.0)) - laneNumFromRight);
798             if (d >= this.stickyLanes)
799             {
800                 return 0.0;
801             }
802             return 1.0 / (Math.pow(d + 1.0, this.bias) * (numberOfUnplacedGTUs + 1.0));
803         }
804 
805         /** {@inheritDoc} */
806         @Override
807         public int hashCode()
808         {
809             final int prime = 31;
810             int result = 1;
811             long temp;
812             temp = Double.doubleToLongBits(this.bias);
813             result = prime * result + (int) (temp ^ (temp >>> 32));
814             result = prime * result + ((this.roadPosition == null) ? 0 : this.roadPosition.hashCode());
815             temp = Double.doubleToLongBits(this.stickyLanes);
816             result = prime * result + (int) (temp ^ (temp >>> 32));
817             return result;
818         }
819 
820         /** {@inheritDoc} */
821         @Override
822         public boolean equals(final Object obj)
823         {
824             if (this == obj)
825             {
826                 return true;
827             }
828             if (obj == null)
829             {
830                 return false;
831             }
832             if (getClass() != obj.getClass())
833             {
834                 return false;
835             }
836             LaneBias other = (LaneBias) obj;
837             if (Double.doubleToLongBits(this.bias) != Double.doubleToLongBits(other.bias))
838             {
839                 return false;
840             }
841             if (this.roadPosition == null)
842             {
843                 if (other.roadPosition != null)
844                 {
845                     return false;
846                 }
847             }
848             else if (!this.roadPosition.equals(other.roadPosition))
849             {
850                 return false;
851             }
852             if (Double.doubleToLongBits(this.stickyLanes) != Double.doubleToLongBits(other.stickyLanes))
853             {
854                 return false;
855             }
856             return true;
857         }
858 
859         /** {@inheritDoc} */
860         @Override
861         public String toString()
862         {
863             return "Bias [roadPosition=" + this.roadPosition + ", bias=" + this.bias + ", stickyLanes=" + this.stickyLanes
864                     + "]";
865         }
866 
867     }
868 
869     /**
870      * Interface for preferred road position for a lane bias.
871      * <p>
872      * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
873      * <br>
874      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
875      * </p>
876      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
877      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
878      * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
879      */
880     public interface RoadPosition
881     {
882 
883         /**
884          * Returns the road position (0.0 = right, 1.0 = left).
885          * @param desiredSpeed Speed; desired speed at the generator
886          * @return road position (0.0 = right, 1.0 = left)
887          */
888         double getValue(Speed desiredSpeed);
889 
890         /**
891          * Fixed road position.
892          * <p>
893          * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights
894          * reserved. <br>
895          * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
896          * <p>
897          * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
898          * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
899          * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
900          */
901         class ByValue implements RoadPosition
902         {
903 
904             /** Road position. */
905             private double value;
906 
907             /**
908              * Constructor.
909              * @param value double; road position
910              */
911             public ByValue(final double value)
912             {
913                 Throw.when(value < 0.0 || value > 1.0, IllegalArgumentException.class,
914                         "Road position value should be in the range [0...1].");
915                 this.value = value;
916             }
917 
918             /** {@inheritDoc} */
919             @Override
920             public double getValue(final Speed desiredSpeed)
921             {
922                 return this.value;
923             }
924 
925             /** {@inheritDoc} */
926             @Override
927             public int hashCode()
928             {
929                 final int prime = 31;
930                 int result = 1;
931                 long temp;
932                 temp = Double.doubleToLongBits(this.value);
933                 result = prime * result + (int) (temp ^ (temp >>> 32));
934                 return result;
935             }
936 
937             /** {@inheritDoc} */
938             @Override
939             public boolean equals(final Object obj)
940             {
941                 if (this == obj)
942                 {
943                     return true;
944                 }
945                 if (obj == null)
946                 {
947                     return false;
948                 }
949                 if (getClass() != obj.getClass())
950                 {
951                     return false;
952                 }
953                 ByValue other = (ByValue) obj;
954                 if (Double.doubleToLongBits(this.value) != Double.doubleToLongBits(other.value))
955                 {
956                     return false;
957                 }
958                 return true;
959             }
960 
961         }
962 
963         /**
964          * Road position based on desired speed.
965          * <p>
966          * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights
967          * reserved. <br>
968          * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
969          * <p>
970          * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
971          * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
972          * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
973          */
974         class BySpeed implements RoadPosition
975         {
976 
977             /** Desired speed at left side of the road. */
978             private Speed leftSpeed;
979 
980             /** Desired speed at the right side of the road. */
981             private Speed rightSpeed;
982 
983             /**
984              * Constructor.
985              * @param leftSpeed Speed; desired speed at left side of the road
986              * @param rightSpeed Speed; desired speed at right side of the road
987              */
988             public BySpeed(final Speed leftSpeed, final Speed rightSpeed)
989             {
990                 Throw.when(leftSpeed.eq(rightSpeed), IllegalArgumentException.class,
991                         "Left speed and right speed may not be equal. Use LaneBias.NONE.");
992                 this.leftSpeed = leftSpeed;
993                 this.rightSpeed = rightSpeed;
994             }
995 
996             /** {@inheritDoc} */
997             @Override
998             public double getValue(final Speed desiredSpeed)
999             {
1000                 Throw.whenNull(desiredSpeed, "Peeked desired speed from a strategical planner factory is null, "
1001                         + "while a lane bias depends on desired speed.");
1002                 double value = (desiredSpeed.si - this.rightSpeed.si) / (this.leftSpeed.si - this.rightSpeed.si);
1003                 return value < 0.0 ? 0.0 : (value > 1.0 ? 1.0 : value);
1004             }
1005 
1006             /** {@inheritDoc} */
1007             @Override
1008             public int hashCode()
1009             {
1010                 final int prime = 31;
1011                 int result = 1;
1012                 result = prime * result + ((this.leftSpeed == null) ? 0 : this.leftSpeed.hashCode());
1013                 result = prime * result + ((this.rightSpeed == null) ? 0 : this.rightSpeed.hashCode());
1014                 return result;
1015             }
1016 
1017             /** {@inheritDoc} */
1018             @Override
1019             public boolean equals(final Object obj)
1020             {
1021                 if (this == obj)
1022                 {
1023                     return true;
1024                 }
1025                 if (obj == null)
1026                 {
1027                     return false;
1028                 }
1029                 if (getClass() != obj.getClass())
1030                 {
1031                     return false;
1032                 }
1033                 BySpeed other = (BySpeed) obj;
1034                 if (this.leftSpeed == null)
1035                 {
1036                     if (other.leftSpeed != null)
1037                     {
1038                         return false;
1039                     }
1040                 }
1041                 else if (!this.leftSpeed.equals(other.leftSpeed))
1042                 {
1043                     return false;
1044                 }
1045                 if (this.rightSpeed == null)
1046                 {
1047                     if (other.rightSpeed != null)
1048                     {
1049                         return false;
1050                     }
1051                 }
1052                 else if (!this.rightSpeed.equals(other.rightSpeed))
1053                 {
1054                     return false;
1055                 }
1056                 return true;
1057             }
1058 
1059         }
1060 
1061     }
1062 
1063 }