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