View Javadoc
1   package org.opentrafficsim.road.network.lane.conflict;
2   
3   import java.util.ArrayList;
4   import java.util.Iterator;
5   import java.util.LinkedHashMap;
6   import java.util.List;
7   import java.util.Map;
8   import java.util.Set;
9   import java.util.SortedSet;
10  import java.util.TreeSet;
11  import java.util.concurrent.Executors;
12  import java.util.concurrent.ThreadPoolExecutor;
13  import java.util.concurrent.atomic.AtomicInteger;
14  
15  import org.djunits.value.vdouble.scalar.Length;
16  import org.djutils.exceptions.Throw;
17  import org.djutils.immutablecollections.ImmutableIterator;
18  import org.djutils.immutablecollections.ImmutableMap;
19  import org.djutils.immutablecollections.ImmutableMap.ImmutableEntry;
20  import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
21  import org.opentrafficsim.core.geometry.OTSGeometryException;
22  import org.opentrafficsim.core.geometry.OTSLine3D;
23  import org.opentrafficsim.core.geometry.OTSPoint3D;
24  import org.opentrafficsim.core.gtu.GTUDirectionality;
25  import org.opentrafficsim.core.gtu.GTUType;
26  import org.opentrafficsim.core.network.Link;
27  import org.opentrafficsim.core.network.NetworkException;
28  import org.opentrafficsim.road.network.OTSRoadNetwork;
29  import org.opentrafficsim.road.network.lane.CrossSectionElement;
30  import org.opentrafficsim.road.network.lane.CrossSectionLink;
31  import org.opentrafficsim.road.network.lane.Lane;
32  import org.pmw.tinylog.Level;
33  
34  /**
35   * <p>
36   * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
37   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
38   * <p>
39   * @version $Revision$, $LastChangedDate$, by $Author$, initial version 11 dec. 2016 <br>
40   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
41   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
42   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
43   */
44  // TODO use z-coordinate for intersections of lines
45  public final class ConflictBuilder
46  {
47      /** number of merge onflicts. */
48      private static AtomicInteger numberMergeConflicts = new AtomicInteger(0);
49  
50      /** number of split onflicts. */
51      private static AtomicInteger numberSplitConflicts = new AtomicInteger(0);
52  
53      /** number of cross onflicts. */
54      private static AtomicInteger numberCrossConflicts = new AtomicInteger(0);
55  
56      /** Default width generator for conflicts which uses 80% of the lane width. */
57      public static final WidthGenerator DEFAULT_WIDTH_GENERATOR = new RelativeWidthGenerator(0.8);
58  
59      /**
60       * Empty constructor.
61       */
62      private ConflictBuilder()
63      {
64          //
65      }
66  
67      /**
68       * Build conflicts on network.
69       * @param network OTSRoadNetwork; network
70       * @param gtuType GTUType; gtu type
71       * @param simulator OTSSimulatorInterface; simulator
72       * @param widthGenerator WidthGenerator; width generator
73       * @throws OTSGeometryException in case of geometry exception
74       */
75      public static void buildConflicts(final OTSRoadNetwork network, final GTUType gtuType,
76              final OTSSimulatorInterface simulator, final WidthGenerator widthGenerator)
77              throws OTSGeometryException
78      {
79          buildConflicts(network, gtuType, simulator, widthGenerator, new LaneCombinationList(), new LaneCombinationList());
80      }
81  
82      /**
83       * Build conflicts on network.
84       * @param network OTSRoadNetwork; network
85       * @param gtuType GTUType; gtu type
86       * @param simulator OTSSimulatorInterface; simulator
87       * @param widthGenerator WidthGenerator; width generator
88       * @param ignoreList LaneCombinationList; lane combinations to ignore
89       * @param permittedList LaneCombinationList; lane combinations that are permitted by traffic control
90       * @throws OTSGeometryException in case of geometry exception
91       */
92      public static void buildConflicts(final OTSRoadNetwork network, final GTUType gtuType,
93              final OTSSimulatorInterface simulator, final WidthGenerator widthGenerator,
94              final LaneCombinationList ignoreList, final LaneCombinationList permittedList) throws OTSGeometryException
95      {
96          // Create list of lanes
97          ImmutableMap<String, Link> links = network.getLinkMap();
98          List<Lane> lanes = new ArrayList<>();
99          for (String linkId : links.keySet())
100         {
101             Link link = links.get(linkId);
102             if (link instanceof CrossSectionLink)
103             {
104                 for (CrossSectionElement element : ((CrossSectionLink) link).getCrossSectionElementList())
105                 {
106                     if (element instanceof Lane)
107                     {
108                         lanes.add((Lane) element);
109                     }
110                 }
111             }
112         }
113         buildConflicts(lanes, gtuType, simulator, widthGenerator, ignoreList, permittedList, null);
114     }
115 
116     /**
117      * Build conflicts on list of lanes.
118      * @param lanes List&lt;Lane&gt;; lanes
119      * @param gtuType GTUType; gtu type
120      * @param simulator OTSSimulatorInterface; simulator
121      * @param widthGenerator WidthGenerator; width generator
122      * @throws OTSGeometryException in case of geometry exception
123      */
124     public static void buildConflicts(final List<Lane> lanes, final GTUType gtuType,
125             final OTSSimulatorInterface simulator, final WidthGenerator widthGenerator)
126             throws OTSGeometryException
127     {
128         buildConflicts(lanes, gtuType, simulator, widthGenerator, new LaneCombinationList(), new LaneCombinationList(), null);
129     }
130 
131     /**
132      * Build conflicts on list of lanes.
133      * @param lanes List&lt;Lane&gt;; list of Lanes
134      * @param gtuType GTUType; the GTU type
135      * @param simulator OTSSimulatorInterface; the simulator
136      * @param widthGenerator WidthGenerator; the width generator
137      * @param ignoreList LaneCombinationList; lane combinations to ignore
138      * @param permittedList LaneCombinationList; lane combinations that are permitted by traffic control
139      * @param conflictId String; identification of the conflict (null value permitted)
140      * @throws OTSGeometryException in case of geometry exception
141      */
142     public static void buildConflicts(final List<Lane> lanes, final GTUType gtuType,
143             final OTSSimulatorInterface simulator, final WidthGenerator widthGenerator,
144             final LaneCombinationList ignoreList, final LaneCombinationList permittedList, final String conflictId)
145             throws OTSGeometryException
146     {
147         // Loop Lane / GTUDirectionality combinations
148         long totalCombinations = ((long) lanes.size()) * ((long) lanes.size() - 1) / 2;
149         simulator.getLogger().always().trace("GENERATING CONFLICTS (NON-PARALLEL MODE). {} COMBINATIONS", totalCombinations);
150         long lastReported = 0;
151         Map<Lane, OTSLine3D> leftEdges = new LinkedHashMap<>();
152         Map<Lane, OTSLine3D> rightEdges = new LinkedHashMap<>();
153 
154         for (int i = 0; i < lanes.size(); i++)
155         {
156             long combinationsDone = totalCombinations - ((long) (lanes.size() - i)) * ((long) (lanes.size() - i)) / 2;
157             if (combinationsDone / 100000000 > lastReported)
158             {
159                 simulator.getLogger().always()
160                         .debug(String.format(
161                                 "generating conflicts at %.0f%% (generated %d merge conflicts, %d split "
162                                         + "conflicts, %d crossing conflicts)",
163                                 100.0 * combinationsDone / totalCombinations, numberMergeConflicts.get(),
164                                 numberSplitConflicts.get(), numberCrossConflicts.get()));
165                 lastReported = combinationsDone / 100000000;
166             }
167             Lane lane1 = lanes.get(i);
168             for (GTUDirectionality dir1 : lane1.getLaneType().getDirectionality(gtuType).getDirectionalities())
169             {
170                 ImmutableMap<Lane, GTUDirectionality> down1 = lane1.downstreamLanes(dir1, gtuType);
171                 ImmutableMap<Lane, GTUDirectionality> up1 = lane1.upstreamLanes(dir1, gtuType);
172 
173                 for (int j = i + 1; j < lanes.size(); j++)
174                 {
175                     Lane lane2 = lanes.get(j);
176                     if (ignoreList.contains(lane1, lane2))
177                     {
178                         continue;
179                     }
180                     boolean permitted = permittedList.contains(lane1, lane2);
181 
182                     for (GTUDirectionality dir2 : lane2.getLaneType().getDirectionality(gtuType).getDirectionalities())
183                     {
184                         ImmutableMap<Lane, GTUDirectionality> down2 = lane2.downstreamLanes(dir2, gtuType);
185                         ImmutableMap<Lane, GTUDirectionality> up2 = lane2.upstreamLanes(dir2, gtuType);
186                         // See if conflict needs to be build, and build if so
187                         try
188                         {
189                             buildConflicts(lane1, dir1, down1, up1, lane2, dir2, down2, up2, gtuType, permitted, simulator,
190                                     widthGenerator, leftEdges, rightEdges, true, conflictId);
191                         }
192                         catch (NetworkException ne)
193                         {
194                             throw new RuntimeException("Conflict build with bad combination of types / rules.", ne);
195                         }
196                     }
197                 }
198             }
199         }
200         simulator.getLogger().always()
201                 .trace(String.format(
202                         "generating conflicts complete (generated %d merge conflicts, %d split "
203                                 + "conflicts, %d crossing conflicts)",
204                         numberMergeConflicts.get(), numberSplitConflicts.get(), numberCrossConflicts.get()));
205     }
206 
207     /**
208      * Build conflict on single lane pair. Connecting lanes are determined.
209      * @param lane1 Lane; lane 1
210      * @param dir1 GTUDirectionality; gtu direction 1
211      * @param lane2 Lane; lane 2
212      * @param dir2 GTUDirectionality; gtu direction 2
213      * @param gtuType GTUType; gtu type
214      * @param simulator OTSSimulatorInterface; simulator
215      * @param widthGenerator WidthGenerator; width generator
216      * @throws OTSGeometryException in case of geometry exception
217      */
218     @SuppressWarnings("checkstyle:parameternumber")
219     public static void buildConflicts(final Lane lane1, final GTUDirectionality dir1, final Lane lane2,
220             final GTUDirectionality dir2, final GTUType gtuType, final OTSSimulatorInterface simulator,
221             final WidthGenerator widthGenerator) throws OTSGeometryException
222     {
223         buildConflicts(lane1, dir1, lane2, dir2, gtuType, simulator, widthGenerator, false);
224     }
225 
226     /**
227      * Build conflict on single lane pair. Connecting lanes are determined.
228      * @param lane1 Lane; lane 1
229      * @param dir1 GTUDirectionality; gtu direction 1
230      * @param lane2 Lane; lane 2
231      * @param dir2 GTUDirectionality; gtu direction 2
232      * @param gtuType GTUType; gtu type
233      * @param simulator OTSSimulatorInterface; simulator
234      * @param widthGenerator WidthGenerator; width generator
235      * @param permitted boolean; conflict permitted by traffic control
236      * @throws OTSGeometryException in case of geometry exception
237      */
238     @SuppressWarnings("checkstyle:parameternumber")
239     public static void buildConflicts(final Lane lane1, final GTUDirectionality dir1, final Lane lane2,
240             final GTUDirectionality dir2, final GTUType gtuType, final OTSSimulatorInterface simulator,
241             final WidthGenerator widthGenerator, final boolean permitted) throws OTSGeometryException
242     {
243         ImmutableMap<Lane, GTUDirectionality> down1 = lane1.downstreamLanes(dir1, gtuType);
244         ImmutableMap<Lane, GTUDirectionality> up1 = lane1.upstreamLanes(dir1, gtuType);
245         ImmutableMap<Lane, GTUDirectionality> down2 = lane2.downstreamLanes(dir2, gtuType);
246         ImmutableMap<Lane, GTUDirectionality> up2 = lane2.upstreamLanes(dir2, gtuType);
247         try
248         {
249             buildConflicts(lane1, dir1, down1, up1, lane2, dir2, down2, up2, gtuType, permitted, simulator, widthGenerator,
250                     new LinkedHashMap<>(), new LinkedHashMap<>(), true, null);
251         }
252         catch (NetworkException ne)
253         {
254             throw new RuntimeException("Conflict build with bad combination of types / rules.", ne);
255         }
256     }
257 
258     /**
259      * Build conflicts on single lane pair.
260      * @param lane1 Lane; lane 1
261      * @param dir1 GTUDirectionality; gtu direction 1
262      * @param down1 Map&lt;Lane,GTUDirectionality&gt;; downstream lanes 1
263      * @param up1 Map&lt;Lane,GTUDirectionality&gt;; upstream lanes 1
264      * @param lane2 Lane; lane 2
265      * @param dir2 GTUDirectionality; gtu direction 2
266      * @param down2 Map&lt;Lane,GTUDirectionality&gt;; downstream lane 2
267      * @param up2 Map&lt;Lane,GTUDirectionality&gt;; upstream lanes 2
268      * @param gtuType GTUType; gtu type
269      * @param permitted boolean; conflict permitted by traffic control
270      * @param simulator OTSSimulatorInterface; simulator
271      * @param widthGenerator WidthGenerator; width generator
272      * @param leftEdges Map&lt;Lane, OTSLine3D&gt;; cache of left edge lines
273      * @param rightEdges Map&lt;Lane, OTSLine3D&gt;; cache of right edge lines
274      * @param intersectionCheck indicate whether we have to do a contour intersection check still
275      * @param conflictId String; identification of the conflict (may be null)
276      * @throws OTSGeometryException in case of geometry exception
277      * @throws NetworkException if the combination of conflict type and both conflict rules is not correct
278      */
279     @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:methodlength"})
280     static void buildConflicts(final Lane lane1, final GTUDirectionality dir1,
281             final ImmutableMap<Lane, GTUDirectionality> down1, final ImmutableMap<Lane, GTUDirectionality> up1,
282             final Lane lane2, final GTUDirectionality dir2, final ImmutableMap<Lane, GTUDirectionality> down2,
283             final ImmutableMap<Lane, GTUDirectionality> up2, final GTUType gtuType, final boolean permitted,
284             final OTSSimulatorInterface simulator, final WidthGenerator widthGenerator,
285             final Map<Lane, OTSLine3D> leftEdges, final Map<Lane, OTSLine3D> rightEdges, final boolean intersectionCheck,
286             final String conflictId) throws OTSGeometryException, NetworkException
287     {
288         // Quick contour check, skip if not overlapping -- Don't repeat if it has taken place
289         if (intersectionCheck)
290         {
291             if (!lane1.getContour().intersects(lane2.getContour()))
292             {
293                 return;
294             }
295         }
296 
297         // TODO: we cache, but the width generator may be different
298 
299         String paddedConflictId = null == conflictId ? "" : (" in conflict group " + conflictId);
300         // Get left and right lines at specified width
301         OTSLine3D left1;
302         OTSLine3D right1;
303         synchronized (lane1)
304         {
305             left1 = leftEdges.get(lane1);
306             right1 = rightEdges.get(lane1);
307             OTSLine3D line1 = lane1.getCenterLine();
308             if (null == left1)
309             {
310                 left1 = line1.offsetLine(widthGenerator.getWidth(lane1, 0.0) / 2, widthGenerator.getWidth(lane1, 1.0) / 2);
311                 leftEdges.put(lane1, left1);
312             }
313             if (null == right1)
314             {
315                 right1 = line1.offsetLine(-widthGenerator.getWidth(lane1, 0.0) / 2, -widthGenerator.getWidth(lane1, 1.0) / 2);
316                 rightEdges.put(lane1, right1);
317             }
318         }
319 
320         OTSLine3D left2;
321         OTSLine3D right2;
322         synchronized (lane2)
323         {
324             left2 = leftEdges.get(lane2);
325             right2 = rightEdges.get(lane2);
326             OTSLine3D line2 = lane2.getCenterLine();
327             if (null == left2)
328             {
329                 left2 = line2.offsetLine(widthGenerator.getWidth(lane2, 0.0) / 2, widthGenerator.getWidth(lane2, 1.0) / 2);
330                 leftEdges.put(lane2, left2);
331             }
332             if (null == right2)
333             {
334                 right2 = line2.offsetLine(-widthGenerator.getWidth(lane2, 0.0) / 2, -widthGenerator.getWidth(lane2, 1.0) / 2);
335                 rightEdges.put(lane2, right2);
336             }
337         }
338 
339         // Get list of all intersection fractions
340         SortedSet<Intersection> intersections = Intersection.getIntersectionList(left1, left2, 0);
341         intersections.addAll(Intersection.getIntersectionList(left1, right2, 1));
342         intersections.addAll(Intersection.getIntersectionList(right1, left2, 2));
343         intersections.addAll(Intersection.getIntersectionList(right1, right2, 3));
344 
345         // Create merge
346         ImmutableIterator<ImmutableEntry<Lane, GTUDirectionality>> iterator1 = down1.entrySet().iterator();
347         ImmutableIterator<ImmutableEntry<Lane, GTUDirectionality>> iterator2 = down2.entrySet().iterator();
348         boolean merge = false;
349         while (iterator1.hasNext() && !merge)
350         {
351             ImmutableEntry<Lane, GTUDirectionality> next1 = iterator1.next();
352             while (iterator2.hasNext() && !merge)
353             {
354                 ImmutableEntry<Lane, GTUDirectionality> next2 = iterator2.next();
355                 if (next1.equals(next2))
356                 {
357                     // Same downstream lane, so a merge
358                     double fraction1 = Double.NaN;
359                     double fraction2 = Double.NaN;
360                     for (Intersection intersection : intersections)
361                     {
362                         // Only consider left/right and right/left intersections (others may or may not be at the end)
363                         if (intersection.getCombo() == 1 || intersection.getCombo() == 2)
364                         {
365                             fraction1 = intersection.getFraction1();
366                             fraction2 = intersection.getFraction2();
367                         }
368                     }
369                     // Remove all intersections beyond this point, these are the result of line starts/ends matching
370                     Iterator<Intersection> iterator = intersections.iterator();
371                     while (iterator.hasNext())
372                     {
373                         if (iterator.next().getFraction1() >= fraction1)
374                         {
375                             iterator.remove();
376                         }
377                     }
378                     if (Double.isNaN(fraction1))
379                     {
380                         simulator.getLogger().always().info("Fixing fractions of merge conflict{}", paddedConflictId);
381                         fraction1 = 0;
382                         fraction2 = 0;
383                     }
384                     // Build conflict
385                     buildMergeConflict(lane1, dir1, fraction1, lane2, dir2, fraction2, gtuType, simulator, widthGenerator,
386                             permitted);
387                     // Skip loop for efficiency, and do not create multiple merges in case of multiple same downstream lanes
388                     merge = true;
389                 }
390             }
391         }
392 
393         // Create split
394         iterator1 = up1.entrySet().iterator();
395         iterator2 = up2.entrySet().iterator();
396         boolean split = false;
397         while (iterator1.hasNext() && !split)
398         {
399             ImmutableEntry<Lane, GTUDirectionality> prev1 = iterator1.next();
400             while (iterator2.hasNext() && !split)
401             {
402                 ImmutableEntry<Lane, GTUDirectionality> prev2 = iterator2.next();
403                 if (prev1.equals(prev2))
404                 {
405                     // Same upstream lane, so a split
406                     double fraction1 = Double.NaN;
407                     double fraction2 = Double.NaN;
408                     for (Intersection intersection : intersections)
409                     {
410                         // Only consider left/right and right/left intersections (others may or may not be at the start)
411                         if (intersection.getCombo() == 1 || intersection.getCombo() == 2)
412                         {
413                             fraction1 = intersection.getFraction1();
414                             fraction2 = intersection.getFraction2();
415                             break; // Split so first, not last
416                         }
417                     }
418                     // Remove all intersections up to this point, these are the result of line starts/ends matching
419                     Iterator<Intersection> iterator = intersections.iterator();
420                     while (iterator.hasNext())
421                     {
422                         if (iterator.next().getFraction1() <= fraction1)
423                         {
424                             iterator.remove();
425                         }
426                         else
427                         {
428                             // May skip further fraction
429                             break;
430                         }
431                     }
432                     if (Double.isNaN(fraction1))
433                     {
434                         simulator.getLogger().always().info("Fixing fractions of split conflict{}", paddedConflictId);
435                         fraction1 = 1;
436                         fraction2 = 1;
437                     }
438                     // Build conflict
439                     buildSplitConflict(lane1, dir1, fraction1, lane2, dir2, fraction2, gtuType, simulator, widthGenerator);
440                     // Skip loop for efficiency, and do not create multiple splits in case of multiple same upstream lanes
441                     split = true;
442                 }
443             }
444         }
445 
446         // Create crossings
447         if (!lane1.getParentLink().equals(lane2.getParentLink())) // tight inner-curves with dedicated Bezier ignored
448         {
449             boolean[] crossed = new boolean[4];
450             Iterator<Intersection> iterator = intersections.iterator();
451             double f1Start = Double.NaN;
452             double f2Start = Double.NaN;
453             double f2End = Double.NaN;
454             while (iterator.hasNext())
455             {
456                 Intersection intersection = iterator.next();
457                 // First fraction found is start of conflict
458                 if (Double.isNaN(f1Start))
459                 {
460                     f1Start = intersection.getFraction1();
461                 }
462                 f2Start = Double.isNaN(f2Start) ? intersection.getFraction2() : Math.min(f2Start, intersection.getFraction2());
463                 f2End = Double.isNaN(f2End) ? intersection.getFraction2() : Math.max(f2End, intersection.getFraction2());
464                 // Flip crossed state of intersecting line combination
465                 crossed[intersection.getCombo()] = !crossed[intersection.getCombo()];
466                 // If all crossed or all not crossed, end of conflict
467                 if ((crossed[0] && crossed[1] && crossed[2] && crossed[3])
468                         || (!crossed[0] && !crossed[1] && !crossed[2] && !crossed[3]))
469                 {
470                     if (dir2.isMinus())
471                     {
472                         double f2Temp = f2Start;
473                         f2Start = f2End;
474                         f2End = f2Temp;
475                     }
476                     if (Double.isNaN(f1Start) || Double.isNaN(f2Start) || Double.isNaN(f2End))
477                     {
478                         simulator.getLogger().always().warn("NOT YET Fixing fractions of crossing conflict{}",
479                                 paddedConflictId);
480                     }
481                     buildCrossingConflict(lane1, dir1, f1Start, intersection.getFraction1(), lane2, dir2, f2Start, f2End,
482                             gtuType, simulator, widthGenerator, permitted);
483                     f1Start = Double.NaN;
484                     f2Start = Double.NaN;
485                     f2End = Double.NaN;
486                 }
487             }
488         }
489 
490     }
491 
492     /**
493      * Build a merge conflict.
494      * @param lane1 Lane; lane 1
495      * @param dir1 GTUDirectionality; gtu direction 1
496      * @param f1start double; start fraction 1
497      * @param lane2 Lane; lane 2
498      * @param dir2 GTUDirectionality; gtu direction 2
499      * @param f2start double; start fraction 2
500      * @param gtuType GTUType; gtu type
501      * @param simulator OTSSimulatorInterface; simulator
502      * @param widthGenerator WidthGenerator; width generator
503      * @param permitted boolean; conflict permitted by traffic control
504      * @throws NetworkException if the combination of conflict type and both conflict rules is not correct
505      * @throws OTSGeometryException in case of geometry exception
506      */
507     @SuppressWarnings("checkstyle:parameternumber")
508     private static void buildMergeConflict(final Lane lane1, final GTUDirectionality dir1, final double f1start,
509             final Lane lane2, final GTUDirectionality dir2, final double f2start, final GTUType gtuType,
510             final OTSSimulatorInterface simulator, final WidthGenerator widthGenerator, final boolean permitted)
511             throws NetworkException, OTSGeometryException
512     {
513 
514         // Determine lane end from direction
515         double f1end = dir1.isPlus() ? 1.0 : 0.0;
516         double f2end = dir2.isPlus() ? 1.0 : 0.0;
517 
518         // Get locations and length
519         Length longitudinalPosition1 = lane1.getLength().times(f1start);
520         Length longitudinalPosition2 = lane2.getLength().times(f2start);
521         Length length1 = lane1.getLength().times(Math.abs(f1end - f1start));
522         Length length2 = lane2.getLength().times(Math.abs(f2end - f2start));
523 
524         // Get geometries
525         OTSLine3D geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
526         OTSLine3D geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
527 
528         // Determine conflict rule
529         ConflictRule conflictRule;
530         if (lane1.getParentLink().getPriority().isBusStop() || lane2.getParentLink().getPriority().isBusStop())
531         {
532             Throw.when(lane1.getParentLink().getPriority().isBusStop() && lane2.getParentLink().getPriority().isBusStop(),
533                     IllegalArgumentException.class, "Merge conflict between two links with bus stop priority not supported.");
534             conflictRule = new BusStopConflictRule(simulator);
535         }
536         else
537         {
538             conflictRule = new DefaultConflictRule();
539         }
540 
541         // Make conflict
542         Conflict.generateConflictPair(ConflictType.MERGE, conflictRule, permitted, lane1, longitudinalPosition1, length1, dir1,
543                 geometry1, gtuType, lane2, longitudinalPosition2, length2, dir2, geometry2, gtuType, simulator);
544 
545         numberMergeConflicts.incrementAndGet();
546     }
547 
548     /**
549      * Build a split conflict.
550      * @param lane1 Lane; lane 1
551      * @param dir1 GTUDirectionality; gtu direction 1
552      * @param f1end double; end fraction 1
553      * @param lane2 Lane; lane 2
554      * @param dir2 GTUDirectionality; gtu direction 2
555      * @param f2end double; end fraction 2
556      * @param gtuType GTUType; gtu type
557      * @param simulator OTSSimulatorInterface; simulator
558      * @param widthGenerator WidthGenerator; width generator
559      * @throws NetworkException if the combination of conflict type and both conflict rules is not correct
560      * @throws OTSGeometryException in case of geometry exception
561      */
562     @SuppressWarnings("checkstyle:parameternumber")
563     private static void buildSplitConflict(final Lane lane1, final GTUDirectionality dir1, final double f1end, final Lane lane2,
564             final GTUDirectionality dir2, final double f2end, final GTUType gtuType,
565             final OTSSimulatorInterface simulator, final WidthGenerator widthGenerator)
566             throws NetworkException, OTSGeometryException
567     {
568 
569         // Determine lane start from direction
570         double f1start = dir1.isPlus() ? 0.0 : 1.0;
571         double f2start = dir2.isPlus() ? 0.0 : 1.0;
572 
573         // Get locations and length
574         Length longitudinalPosition1 = lane1.getLength().times(f1start);
575         Length longitudinalPosition2 = lane2.getLength().times(f2start);
576         Length length1 = lane1.getLength().times(Math.abs(f1end - f1start));
577         Length length2 = lane2.getLength().times(Math.abs(f2end - f2start));
578 
579         // Get geometries
580         OTSLine3D geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
581         OTSLine3D geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
582 
583         // Make conflict
584         Conflict.generateConflictPair(ConflictType.SPLIT, new SplitConflictRule(), false, lane1, longitudinalPosition1, length1,
585                 dir1, geometry1, gtuType, lane2, longitudinalPosition2, length2, dir2, geometry2, gtuType, simulator);
586 
587         numberSplitConflicts.incrementAndGet();
588     }
589 
590     /**
591      * Build a crossing conflict.
592      * @param lane1 Lane; lane 1
593      * @param dir1 GTUDirectionality; gtu direction 1
594      * @param f1start double; start fraction 1
595      * @param f1end double; end fraction 1
596      * @param lane2 Lane; lane 2
597      * @param dir2 GTUDirectionality; gtu direction 2
598      * @param f2start double; start fraction 2
599      * @param f2end double; end fraction 2
600      * @param gtuType GTUType; gtu type
601      * @param simulator OTSSimulatorInterface; simulator
602      * @param widthGenerator WidthGenerator; width generator
603      * @param permitted boolean; conflict permitted by traffic control
604      * @throws NetworkException if the combination of conflict type and both conflict rules is not correct
605      * @throws OTSGeometryException in case of geometry exception
606      */
607     @SuppressWarnings("checkstyle:parameternumber")
608     private static void buildCrossingConflict(final Lane lane1, final GTUDirectionality dir1, final double f1start,
609             final double f1end, final Lane lane2, final GTUDirectionality dir2, final double f2start, final double f2end,
610             final GTUType gtuType, final OTSSimulatorInterface simulator, final WidthGenerator widthGenerator,
611             final boolean permitted) throws NetworkException, OTSGeometryException
612     {
613 
614         // Fractions may be in opposite direction, for the start location this needs to be correct
615         // Note: for geometry (real order, not considering direction) and length (absolute value) this does not matter
616         double f1startDirected;
617         double f2startDirected;
618         if ((dir1.isPlus() && f1end < f1start) || (dir1.isMinus() && f1end > f1start))
619         {
620             f1startDirected = f1end;
621         }
622         else
623         {
624             f1startDirected = f1start;
625         }
626         if ((dir2.isPlus() && f2end < f2start) || (dir2.isMinus() && f2end > f2start))
627         {
628             f2startDirected = f2end;
629         }
630         else
631         {
632             f2startDirected = f2start;
633         }
634 
635         // Get locations and length
636         Length longitudinalPosition1 = lane1.getLength().times(f1startDirected);
637         Length longitudinalPosition2 = lane2.getLength().times(f2startDirected);
638         Length length1 = lane1.getLength().times(Math.abs(f1end - f1start));
639         Length length2 = lane2.getLength().times(Math.abs(f2end - f2start));
640 
641         // Get geometries
642         OTSLine3D geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
643         OTSLine3D geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
644 
645         // Determine conflict rule
646         ConflictRule conflictRule;
647         if (lane1.getParentLink().getPriority().isBusStop() || lane2.getParentLink().getPriority().isBusStop())
648         {
649             Throw.when(lane1.getParentLink().getPriority().isBusStop() && lane2.getParentLink().getPriority().isBusStop(),
650                     IllegalArgumentException.class, "Merge conflict between two links with bus stop priority not supported.");
651             conflictRule = new BusStopConflictRule(simulator);
652         }
653         else
654         {
655             conflictRule = new DefaultConflictRule();
656         }
657 
658         // Make conflict
659         Conflict.generateConflictPair(ConflictType.CROSSING, conflictRule, permitted, lane1, longitudinalPosition1, length1,
660                 dir1, geometry1, gtuType, lane2, longitudinalPosition2, length2, dir2, geometry2, gtuType, simulator);
661 
662         numberCrossConflicts.incrementAndGet();
663     }
664 
665     /**
666      * Creates geometry for conflict.
667      * @param lane Lane; lane
668      * @param fStart double; longitudinal fraction of start
669      * @param fEnd double; longitudinal fraction of end
670      * @param widthGenerator WidthGenerator; width generator
671      * @return geometry for conflict
672      * @throws OTSGeometryException in case of geometry exception
673      */
674     private static OTSLine3D getGeometry(final Lane lane, final double fStart, final double fEnd,
675             final WidthGenerator widthGenerator) throws OTSGeometryException
676     {
677         // extractFractional needs ordered fractions, irrespective of driving direction
678         double f1;
679         double f2;
680         if (fEnd > fStart)
681         {
682             f1 = fStart;
683             f2 = fEnd;
684         }
685         else
686         {
687             f1 = fEnd;
688             f2 = fStart;
689         }
690         if (f1 == f2)
691         {
692             lane.getParentLink().getSimulator().getLogger().always()
693                     .debug("f1 (" + f1 + ") equals f2 (" + f2 + "); problematic lane is " + lane.toString());
694             // Fix up
695             if (f1 > 0)
696             {
697                 f1 = f1 - f1 / 1000;
698             }
699             else
700             {
701                 f2 = f2 + f2 / 1000;
702             }
703         }
704         OTSLine3D centerLine = lane.getCenterLine().extractFractional(f1, f2);
705         OTSLine3D left = centerLine.offsetLine(widthGenerator.getWidth(lane, f1) / 2, widthGenerator.getWidth(lane, f2) / 2);
706         OTSLine3D right =
707                 centerLine.offsetLine(-widthGenerator.getWidth(lane, f1) / 2, -widthGenerator.getWidth(lane, f2) / 2).reverse();
708         OTSPoint3D[] points = new OTSPoint3D[left.size() + right.size()];
709         System.arraycopy(left.getPoints(), 0, points, 0, left.size());
710         System.arraycopy(right.getPoints(), 0, points, left.size(), right.size());
711         return new OTSLine3D(points);
712     }
713 
714     /**
715      * Intersection holds two fractions where two lines have crossed. There is also a combo to identify which lines have been
716      * used to find the intersection.
717      * <p>
718      * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
719      * <br>
720      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
721      * <p>
722      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 21 dec. 2016 <br>
723      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
724      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
725      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
726      */
727     private static class Intersection implements Comparable<Intersection>
728     {
729 
730         /** Fraction on lane 1. */
731         private final double fraction1;
732 
733         /** Fraction on lane 2. */
734         private final double fraction2;
735 
736         /** Edge combination number. */
737         private final int combo;
738 
739         /**
740          * @param fraction1 double; fraction on lane 1
741          * @param fraction2 double; fraction on lane 1
742          * @param combo int; edge combination number
743          */
744         Intersection(final double fraction1, final double fraction2, final int combo)
745         {
746             this.fraction1 = fraction1;
747             this.fraction2 = fraction2;
748             this.combo = combo;
749         }
750 
751         /**
752          * @return fraction1.
753          */
754         public final double getFraction1()
755         {
756             return this.fraction1;
757         }
758 
759         /**
760          * @return fraction2.
761          */
762         public final double getFraction2()
763         {
764             return this.fraction2;
765         }
766 
767         /**
768          * @return combo.
769          */
770         public final int getCombo()
771         {
772             return this.combo;
773         }
774 
775         /** {@inheritDoc} */
776         @Override
777         public int compareTo(final Intersection o)
778         {
779             int out = Double.compare(this.fraction1, o.fraction1);
780             if (out != 0)
781             {
782                 return out;
783             }
784             out = Double.compare(this.fraction2, o.fraction2);
785             if (out != 0)
786             {
787                 return out;
788             }
789             return Integer.compare(this.combo, o.combo);
790         }
791 
792         /** {@inheritDoc} */
793         @Override
794         public int hashCode()
795         {
796             final int prime = 31;
797             int result = 1;
798             result = prime * result + this.combo;
799             long temp;
800             temp = Double.doubleToLongBits(this.fraction1);
801             result = prime * result + (int) (temp ^ (temp >>> 32));
802             temp = Double.doubleToLongBits(this.fraction2);
803             result = prime * result + (int) (temp ^ (temp >>> 32));
804             return result;
805         }
806 
807         /** {@inheritDoc} */
808         @Override
809         public boolean equals(final Object obj)
810         {
811             if (this == obj)
812             {
813                 return true;
814             }
815             if (obj == null)
816             {
817                 return false;
818             }
819             if (getClass() != obj.getClass())
820             {
821                 return false;
822             }
823             Intersection other = (Intersection) obj;
824             if (this.combo != other.combo)
825             {
826                 return false;
827             }
828             if (Double.doubleToLongBits(this.fraction1) != Double.doubleToLongBits(other.fraction1))
829             {
830                 return false;
831             }
832             if (Double.doubleToLongBits(this.fraction2) != Double.doubleToLongBits(other.fraction2))
833             {
834                 return false;
835             }
836             return true;
837         }
838 
839         /**
840          * Returns a set of intersections, sorted by the fraction on line 1.
841          * @param line1 OTSLine3D; line 1
842          * @param line2 OTSLine3D; line 2
843          * @param combo int; edge combination number
844          * @return set of intersections, sorted by the fraction on line 1
845          * @throws OTSGeometryException in case of geometry exception
846          */
847         public static SortedSet<Intersection> getIntersectionList(final OTSLine3D line1, final OTSLine3D line2, final int combo)
848                 throws OTSGeometryException
849         {
850             SortedSet<Intersection> out = new TreeSet<>();
851             // if (!line1.getBounds().intersect(line2.getBounds()))
852             // {
853             // return out;
854             // }
855             double cumul1 = 0.0;
856             OTSPoint3D start1 = null;
857             OTSPoint3D end1 = line1.get(0);
858             for (int i = 0; i < line1.size() - 1; i++)
859             {
860                 start1 = end1;
861                 end1 = line1.get(i + 1);
862 
863                 double cumul2 = 0.0;
864                 OTSPoint3D start2 = null;
865                 OTSPoint3D end2 = line2.get(0);
866 
867                 for (int j = 0; j < line2.size() - 1; j++)
868                 {
869                     start2 = end2;
870                     end2 = line2.get(j + 1);
871 
872                     OTSPoint3D p = OTSPoint3D.intersectionOfLineSegments(start1, end1, start2, end2);
873                     if (p != null)
874                     {
875                         // Segments intersect
876                         double dx = p.x - start1.x;
877                         double dy = p.y - start1.y;
878                         double length1 = cumul1 + Math.sqrt(dx * dx + dy * dy);
879                         dx = p.x - start2.x;
880                         dy = p.y - start2.y;
881                         double length2 = cumul2 + Math.sqrt(dx * dx + dy * dy);
882                         out.add(new Intersection(length1 / line1.getLengthSI(), length2 / line2.getLengthSI(), combo));
883                     }
884 
885                     double dx = end2.x - start2.x;
886                     double dy = end2.y - start2.y;
887                     cumul2 += Math.sqrt(dx * dx + dy * dy);
888                 }
889 
890                 double dx = end1.x - start1.x;
891                 double dy = end1.y - start1.y;
892                 cumul1 += Math.sqrt(dx * dx + dy * dy);
893             }
894 
895             return out;
896         }
897 
898         /** {@inheritDoc} */
899         @Override
900         public String toString()
901         {
902             return "Intersection [fraction1=" + this.fraction1 + ", fraction2=" + this.fraction2 + ", combo=" + this.combo
903                     + "]";
904         }
905 
906     }
907 
908     /**
909      * Generator for width.
910      * <p>
911      * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
912      * <br>
913      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
914      * <p>
915      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 16 dec. 2016 <br>
916      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
917      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
918      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
919      */
920     public interface WidthGenerator
921     {
922 
923         /**
924          * Returns the begin width of this lane.
925          * @param lane Lane; lane
926          * @param fraction double; fraction
927          * @return begin width of this lane
928          */
929         double getWidth(Lane lane, double fraction);
930 
931     }
932 
933     /**
934      * Generator with fixed width.
935      * <p>
936      * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
937      * <br>
938      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
939      * <p>
940      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 16 dec. 2016 <br>
941      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
942      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
943      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
944      */
945     public static class FixedWidthGenerator implements WidthGenerator
946     {
947 
948         /** Fixed width. */
949         private final double width;
950 
951         /**
952          * Constructor with width.
953          * @param width Length; width
954          */
955         public FixedWidthGenerator(final Length width)
956         {
957             this.width = width.si;
958         }
959 
960         /** {@inheritDoc} */
961         @Override
962         public final double getWidth(final Lane lane, final double fraction)
963         {
964             return this.width;
965         }
966 
967         /** {@inheritDoc} */
968         @Override
969         public final String toString()
970         {
971             return "FixedWidthGenerator [width=" + this.width + "]";
972         }
973 
974     }
975 
976     /**
977      * Generator with width factor on actual lane width.
978      * <p>
979      * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
980      * <br>
981      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
982      * <p>
983      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 16 dec. 2016 <br>
984      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
985      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
986      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
987      */
988     public static class RelativeWidthGenerator implements WidthGenerator
989     {
990 
991         /** Width factor. */
992         private final double factor;
993 
994         /**
995          * Constructor with width factor.
996          * @param factor double; width factor
997          */
998         public RelativeWidthGenerator(final double factor)
999         {
1000             this.factor = factor;
1001         }
1002 
1003         /** {@inheritDoc} */
1004         @Override
1005         public final double getWidth(final Lane lane, final double fraction)
1006         {
1007             return lane.getWidth(fraction).si * this.factor;
1008         }
1009 
1010         /** {@inheritDoc} */
1011         @Override
1012         public final String toString()
1013         {
1014             return "RelativeWidthGenerator [factor=" + this.factor + "]";
1015         }
1016 
1017     }
1018 
1019     /* ******************************************************************************************************************** */
1020     /* ******************************************************************************************************************** */
1021     /* ******************************************************************************************************************** */
1022     /* ********************************************* PARALLEL IMPLEMENTATION ********************************************** */
1023     /* ******************************************************************************************************************** */
1024     /* ******************************************************************************************************************** */
1025     /* ******************************************************************************************************************** */
1026 
1027     /**
1028      * Build conflicts on network; parallel implementation.
1029      * @param network OTSRoadNetwork; network
1030      * @param gtuType GTUType; gtu type
1031      * @param simulator OTSSimulatorInterface; simulator
1032      * @param widthGenerator WidthGenerator; width generator
1033      * @throws OTSGeometryException in case of geometry exception
1034      */
1035     public static void buildConflictsParallel(final OTSRoadNetwork network, final GTUType gtuType,
1036             final OTSSimulatorInterface simulator, final WidthGenerator widthGenerator)
1037             throws OTSGeometryException
1038     {
1039         buildConflictsParallel(network, gtuType, simulator, widthGenerator, new LaneCombinationList(),
1040                 new LaneCombinationList());
1041     }
1042 
1043     /**
1044      * Build conflicts on network; parallel implementation.
1045      * @param network OTSRoadNetwork; network
1046      * @param gtuType GTUType; gtu type
1047      * @param simulator OTSSimulatorInterface; simulator
1048      * @param widthGenerator WidthGenerator; width generator
1049      * @param ignoreList LaneCombinationList; lane combinations to ignore
1050      * @param permittedList LaneCombinationList; lane combinations that are permitted by traffic control
1051      * @throws OTSGeometryException in case of geometry exception
1052      */
1053     public static void buildConflictsParallel(final OTSRoadNetwork network, final GTUType gtuType,
1054             final OTSSimulatorInterface simulator, final WidthGenerator widthGenerator,
1055             final LaneCombinationList ignoreList, final LaneCombinationList permittedList) throws OTSGeometryException
1056     {
1057         // Create list of lanes
1058         ImmutableMap<String, Link> links = network.getLinkMap();
1059         List<Lane> lanes = new ArrayList<>();
1060         for (String linkId : links.keySet())
1061         {
1062             Link link = links.get(linkId);
1063             if (link instanceof CrossSectionLink)
1064             {
1065                 for (CrossSectionElement element : ((CrossSectionLink) link).getCrossSectionElementList())
1066                 {
1067                     if (element instanceof Lane)
1068                     {
1069                         lanes.add((Lane) element);
1070                     }
1071                 }
1072             }
1073         }
1074         buildConflictsParallelBig(lanes, gtuType, simulator, widthGenerator, ignoreList, permittedList);
1075     }
1076 
1077     /**
1078      * Build conflicts on list of lanes; parallel implementation.
1079      * @param lanes List&lt;Lane&gt;; lanes
1080      * @param gtuType GTUType; gtu type
1081      * @param simulator OTSSimulatorInterface; simulator
1082      * @param widthGenerator WidthGenerator; width generator
1083      * @throws OTSGeometryException in case of geometry exception
1084      */
1085     public static void buildConflictsParallel(final List<Lane> lanes, final GTUType gtuType,
1086             final OTSSimulatorInterface simulator, final WidthGenerator widthGenerator)
1087             throws OTSGeometryException
1088     {
1089         buildConflictsParallelBig(lanes, gtuType, simulator, widthGenerator, new LaneCombinationList(),
1090                 new LaneCombinationList());
1091     }
1092 
1093     /**
1094      * Build conflicts on list of lanes; parallel implementation. Small jobs.
1095      * @param lanes List&lt;Lane&gt;; list of Lanes
1096      * @param gtuType GTUType; the GTU type
1097      * @param simulator OTSSimulatorInterface; the simulator
1098      * @param widthGenerator WidthGenerator; the width generator
1099      * @param ignoreList LaneCombinationList; lane combinations to ignore
1100      * @param permittedList LaneCombinationList; lane combinations that are permitted by traffic control
1101      * @throws OTSGeometryException in case of geometry exception
1102      */
1103     public static void buildConflictsParallelSmall(final List<Lane> lanes, final GTUType gtuType,
1104             final OTSSimulatorInterface simulator, final WidthGenerator widthGenerator,
1105             final LaneCombinationList ignoreList, final LaneCombinationList permittedList) throws OTSGeometryException
1106     {
1107         // Loop Lane / GTUDirectionality combinations
1108         long totalCombinations = ((long) lanes.size()) * ((long) lanes.size() - 1) / 2;
1109         System.out.println("PARALLEL GENERATING OF CONFLICTS (SMALL JOBS). " + totalCombinations + " COMBINATIONS");
1110         long lastReported = 0;
1111         Map<Lane, OTSLine3D> leftEdges = new LinkedHashMap<>();
1112         Map<Lane, OTSLine3D> rightEdges = new LinkedHashMap<>();
1113 
1114         // force the envelopes to be built first
1115         for (Lane lane : lanes)
1116         {
1117             lane.getContour().getEnvelope();
1118         }
1119 
1120         // make a threadpool and execute buildConflicts for all records
1121         int cores = Runtime.getRuntime().availableProcessors();
1122         System.out.println("USING " + cores + " CORES");
1123         ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(cores);
1124         AtomicInteger numberOfJobs = new AtomicInteger(0);
1125         final int maxqueue = 2 * cores;
1126 
1127         for (int i = 0; i < lanes.size(); i++)
1128         {
1129             long combinationsDone = totalCombinations - ((long) (lanes.size() - i)) * ((long) (lanes.size() - i)) / 2;
1130             if (combinationsDone / 100000000 > lastReported)
1131             {
1132                 simulator.getLogger().always()
1133                         .debug(String.format(
1134                                 "generating conflicts at %.0f%% (generated %d merge conflicts, %d split "
1135                                         + "conflicts, %d crossing conflicts)",
1136                                 100.0 * combinationsDone / totalCombinations, numberMergeConflicts.get(),
1137                                 numberSplitConflicts.get(), numberCrossConflicts.get()));
1138                 lastReported = combinationsDone / 100000000;
1139             }
1140             Lane lane1 = lanes.get(i);
1141             for (GTUDirectionality dir1 : lane1.getLaneType().getDirectionality(gtuType).getDirectionalities())
1142             {
1143                 ImmutableMap<Lane, GTUDirectionality> down1 = lane1.downstreamLanes(dir1, gtuType);
1144                 ImmutableMap<Lane, GTUDirectionality> up1 = lane1.upstreamLanes(dir1, gtuType);
1145 
1146                 for (int j = i + 1; j < lanes.size(); j++)
1147                 {
1148                     Lane lane2 = lanes.get(j);
1149                     if (ignoreList.contains(lane1, lane2))
1150                     {
1151                         continue;
1152                     }
1153                     // Quick contour check, skip if non-overlapping envelopes
1154                     try
1155                     {
1156                         if (!lane1.getContour().intersects(lane2.getContour()))
1157                         {
1158                             continue;
1159                         }
1160                     }
1161                     catch (Exception e)
1162                     {
1163                         System.err.println("Contour problem - lane1 = [" + lane1.getFullId() + "], lane2 = ["
1164                                 + lane2.getFullId() + "]; skipped");
1165                         continue;
1166                     }
1167 
1168                     boolean permitted = permittedList.contains(lane1, lane2);
1169 
1170                     for (GTUDirectionality dir2 : lane2.getLaneType().getDirectionality(gtuType).getDirectionalities())
1171                     {
1172                         while (numberOfJobs.get() > maxqueue) // keep max maxqueue jobs in the pool
1173                         {
1174                             try
1175                             {
1176                                 Thread.sleep(1);
1177                             }
1178                             catch (InterruptedException exception)
1179                             {
1180                                 // ignore
1181                             }
1182                         }
1183                         numberOfJobs.incrementAndGet();
1184                         ImmutableMap<Lane, GTUDirectionality> down2 = lane2.downstreamLanes(dir2, gtuType);
1185                         ImmutableMap<Lane, GTUDirectionality> up2 = lane2.upstreamLanes(dir2, gtuType);
1186                         ConflictBuilderRecordSmall cbr = new ConflictBuilderRecordSmall(lane1, dir1, down1, up1, lane2, dir2,
1187                                 down2, up2, gtuType, permitted, simulator, widthGenerator, leftEdges, rightEdges);
1188                         executor.execute(new CbrTaskSmall(numberOfJobs, cbr));
1189                     }
1190                 }
1191             }
1192         }
1193 
1194         long time = System.currentTimeMillis();
1195         // wait max 60 sec for last maxqueue jobs
1196         while (numberOfJobs.get() > 0 && System.currentTimeMillis() - time < 60000)
1197         {
1198             try
1199             {
1200                 Thread.sleep(10);
1201             }
1202             catch (InterruptedException exception)
1203             {
1204                 // ignore
1205             }
1206         }
1207 
1208         executor.shutdown();
1209         while (!executor.isTerminated())
1210         {
1211             try
1212             {
1213                 Thread.sleep(1);
1214             }
1215             catch (InterruptedException exception)
1216             {
1217                 // ignore
1218             }
1219         }
1220 
1221         simulator.getLogger().always()
1222                 .debug(String.format(
1223                         "generating conflicts complete (generated %d merge conflicts, %d split "
1224                                 + "conflicts, %d crossing conflicts)",
1225                         numberMergeConflicts.get(), numberSplitConflicts.get(), numberCrossConflicts.get()));
1226     }
1227 
1228     /**
1229      * Build conflicts on list of lanes; parallel implementation. Big jobs.
1230      * @param lanes List&lt;Lane&gt;; list of Lanes
1231      * @param gtuType GTUType; the GTU type
1232      * @param simulator OTSSimulatorInterface; the simulator
1233      * @param widthGenerator WidthGenerator; the width generator
1234      * @param ignoreList LaneCombinationList; lane combinations to ignore
1235      * @param permittedList LaneCombinationList; lane combinations that are permitted by traffic control
1236      * @throws OTSGeometryException in case of geometry exception
1237      */
1238     public static void buildConflictsParallelBig(final List<Lane> lanes, final GTUType gtuType,
1239             final OTSSimulatorInterface simulator, final WidthGenerator widthGenerator,
1240             final LaneCombinationList ignoreList, final LaneCombinationList permittedList) throws OTSGeometryException
1241     {
1242         // Loop Lane / GTUDirectionality combinations
1243         long totalCombinations = ((long) lanes.size()) * ((long) lanes.size() - 1) / 2;
1244         System.out.println("PARALLEL GENERATING OF CONFLICTS (BIG JOBS). " + totalCombinations + " COMBINATIONS");
1245         long lastReported = 0;
1246         Map<Lane, OTSLine3D> leftEdges = new LinkedHashMap<>();
1247         Map<Lane, OTSLine3D> rightEdges = new LinkedHashMap<>();
1248 
1249         // force the envelopes to be built first
1250         for (Lane lane : lanes)
1251         {
1252             lane.getContour().getEnvelope();
1253         }
1254 
1255         // make a threadpool and execute buildConflicts for all records
1256         int cores = Runtime.getRuntime().availableProcessors();
1257         System.out.println("USING " + cores + " CORES");
1258         ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(cores);
1259         AtomicInteger numberOfJobs = new AtomicInteger(0);
1260         final int maxqueue = 200;
1261 
1262         for (int i = 0; i < lanes.size(); i++)
1263         {
1264             long combinationsDone = totalCombinations - ((long) (lanes.size() - i)) * ((long) (lanes.size() - i)) / 2;
1265             if (combinationsDone / 100000000 > lastReported)
1266             {
1267                 simulator.getLogger().always()
1268                         .debug(String.format(
1269                                 "generating conflicts at %.0f%% (generated %d merge conflicts, %d split "
1270                                         + "conflicts, %d crossing conflicts)",
1271                                 100.0 * combinationsDone / totalCombinations, numberMergeConflicts.get(),
1272                                 numberSplitConflicts.get(), numberCrossConflicts.get()));
1273                 lastReported = combinationsDone / 100000000;
1274             }
1275             Lane lane1 = lanes.get(i);
1276             for (GTUDirectionality dir1 : lane1.getLaneType().getDirectionality(gtuType).getDirectionalities())
1277             {
1278                 ImmutableMap<Lane, GTUDirectionality> down1 = lane1.downstreamLanes(dir1, gtuType);
1279                 ImmutableMap<Lane, GTUDirectionality> up1 = lane1.upstreamLanes(dir1, gtuType);
1280 
1281                 while (numberOfJobs.get() > maxqueue) // keep max maxqueue jobs in the pool
1282                 {
1283                     try
1284                     {
1285                         Thread.sleep(0, 10);
1286                     }
1287                     catch (InterruptedException exception)
1288                     {
1289                         // ignore
1290                     }
1291                 }
1292                 numberOfJobs.incrementAndGet();
1293 
1294                 ConflictBuilderRecordBig cbr = new ConflictBuilderRecordBig(i, lanes, ignoreList, permittedList, lane1, dir1,
1295                         down1, up1, gtuType, simulator, widthGenerator, leftEdges, rightEdges);
1296                 executor.execute(new CbrTaskBig(numberOfJobs, cbr));
1297 
1298             }
1299         }
1300 
1301         long time = System.currentTimeMillis();
1302         // wait max 60 sec for last maxqueue jobs
1303         while (numberOfJobs.get() > 0 && System.currentTimeMillis() - time < 60000)
1304         {
1305             try
1306             {
1307                 Thread.sleep(10);
1308             }
1309             catch (InterruptedException exception)
1310             {
1311                 // ignore
1312             }
1313         }
1314 
1315         executor.shutdown();
1316         while (!executor.isTerminated())
1317         {
1318             try
1319             {
1320                 Thread.sleep(1);
1321             }
1322             catch (InterruptedException exception)
1323             {
1324                 // ignore
1325             }
1326         }
1327 
1328         simulator.getLogger().always()
1329                 .debug(String.format(
1330                         "generating conflicts complete (generated %d merge conflicts, %d split "
1331                                 + "conflicts, %d crossing conflicts)",
1332                         numberMergeConflicts.get(), numberSplitConflicts.get(), numberCrossConflicts.get()));
1333     }
1334 
1335     /**
1336      * Build conflicts on network using only the groups of links that have been identified as candidates with conflicts;
1337      * parallel implementation.
1338      * @param network OTSRoadNetwork; network
1339      * @param conflictCandidateMap Map&lt;String, Set&lt;Link&gt;&gt;; the map of the conflicting links to implement
1340      * @param gtuType GTUType; gtu type
1341      * @param simulator OTSSimulatorInterface; simulator
1342      * @param widthGenerator WidthGenerator; width generator
1343      * @throws OTSGeometryException in case of geometry exception
1344      */
1345     public static void buildConflictsParallel(final OTSRoadNetwork network, final Map<String, Set<Link>> conflictCandidateMap,
1346             final GTUType gtuType, final OTSSimulatorInterface simulator, final WidthGenerator widthGenerator)
1347             throws OTSGeometryException
1348     {
1349         for (String conflictId : conflictCandidateMap.keySet())
1350         {
1351             // System.out.println(conflictId);
1352             List<Lane> lanes = new ArrayList<>();
1353             for (Link link : conflictCandidateMap.get(conflictId))
1354             {
1355                 if (link instanceof CrossSectionLink)
1356                 {
1357                     for (CrossSectionElement element : ((CrossSectionLink) link).getCrossSectionElementList())
1358                     {
1359                         if (element instanceof Lane)
1360                         {
1361                             lanes.add((Lane) element);
1362                         }
1363                     }
1364                 }
1365             }
1366             // TODO: make parallel
1367             //simulator.getLogger().setAllLogLevel(Level.WARNING);
1368             buildConflicts(lanes, gtuType, simulator, widthGenerator, new LaneCombinationList(), new LaneCombinationList(),
1369                     conflictId);
1370             simulator.getLogger().setAllLogLevel(Level.DEBUG);
1371         }
1372     }
1373 
1374     /** */
1375     static class CbrTaskSmall implements Runnable
1376     {
1377         /** */
1378         final ConflictBuilderRecordSmall cbr;
1379 
1380         /** */
1381         final AtomicInteger nrOfJobs;
1382 
1383         /**
1384          * @param nrOfJobs nr
1385          * @param cbr the record to execute
1386          */
1387         CbrTaskSmall(final AtomicInteger nrOfJobs, final ConflictBuilderRecordSmall cbr)
1388         {
1389             this.nrOfJobs = nrOfJobs;
1390             this.cbr = cbr;
1391         }
1392 
1393         @Override
1394         public void run()
1395         {
1396             // System.err.println("conflict #" + this.nr);
1397             try
1398             {
1399                 buildConflictsSmall(this.cbr);
1400             }
1401             catch (Exception e)
1402             {
1403                 e.printStackTrace();
1404             }
1405             this.nrOfJobs.decrementAndGet();
1406         }
1407 
1408     }
1409 
1410     /**
1411      * Build conflicts for one record.
1412      * @param cbr the lane record
1413      */
1414     static void buildConflictsSmall(final ConflictBuilderRecordSmall cbr)
1415     {
1416         // See if conflict needs to be build, and build if so
1417         try
1418         {
1419             buildConflicts(cbr.lane1, cbr.dir1, cbr.down1, cbr.up1, cbr.lane2, cbr.dir2, cbr.down2, cbr.up2, cbr.gtuType,
1420                     cbr.permitted, cbr.simulator, cbr.widthGenerator, cbr.leftEdges, cbr.rightEdges, false, null);
1421         }
1422         catch (NetworkException | OTSGeometryException ne)
1423         {
1424             throw new RuntimeException("Conflict build with bad combination of types / rules.", ne);
1425         }
1426     }
1427 
1428     /** */
1429     @SuppressWarnings("checkstyle:visibilitymodifier")
1430     static class ConflictBuilderRecordSmall
1431     {
1432         /** */
1433         final Lane lane1;
1434 
1435         /** */
1436         final GTUDirectionality dir1;
1437 
1438         /** */
1439         final ImmutableMap<Lane, GTUDirectionality> down1;
1440 
1441         /** */
1442         final ImmutableMap<Lane, GTUDirectionality> up1;
1443 
1444         /** */
1445         final Lane lane2;
1446 
1447         /** */
1448         final GTUDirectionality dir2;
1449 
1450         /** */
1451         final ImmutableMap<Lane, GTUDirectionality> down2;
1452 
1453         /** */
1454         final ImmutableMap<Lane, GTUDirectionality> up2;
1455 
1456         /** */
1457         final GTUType gtuType;
1458 
1459         /** */
1460         final boolean permitted;
1461 
1462         /** */
1463         final OTSSimulatorInterface simulator;
1464 
1465         /** */
1466         final WidthGenerator widthGenerator;
1467 
1468         /** */
1469         final Map<Lane, OTSLine3D> leftEdges;
1470 
1471         /** */
1472         final Map<Lane, OTSLine3D> rightEdges;
1473 
1474         /**
1475          * Stores conflicts about a single lane pair.
1476          * @param lane1 Lane; lane 1
1477          * @param dir1 GTUDirectionality; gtu direction 1
1478          * @param down1 Map&lt;Lane,GTUDirectionality&gt;; downstream lanes 1
1479          * @param up1 Map&lt;Lane,GTUDirectionality&gt;; upstream lanes 1
1480          * @param lane2 Lane; lane 2
1481          * @param dir2 GTUDirectionality; gtu direction 2
1482          * @param down2 Map&lt;Lane,GTUDirectionality&gt;; downstream lane 2
1483          * @param up2 Map&lt;Lane,GTUDirectionality&gt;; upstream lanes 2
1484          * @param gtuType GTUType; gtu type
1485          * @param permitted boolean; conflict permitted by traffic control
1486          * @param simulator OTSSimulatorInterface; simulator
1487          * @param widthGenerator WidthGenerator; width generator
1488          * @param leftEdges Map&lt;Lane, OTSLine3D&gt;; cache of left edge lines
1489          * @param rightEdges Map&lt;Lane, OTSLine3D&gt;; cache of right edge lines
1490          */
1491         @SuppressWarnings("checkstyle:parameternumber")
1492         ConflictBuilderRecordSmall(final Lane lane1, final GTUDirectionality dir1,
1493                 final ImmutableMap<Lane, GTUDirectionality> down1, final ImmutableMap<Lane, GTUDirectionality> up1,
1494                 final Lane lane2, final GTUDirectionality dir2, final ImmutableMap<Lane, GTUDirectionality> down2,
1495                 final ImmutableMap<Lane, GTUDirectionality> up2, final GTUType gtuType, final boolean permitted,
1496                 final OTSSimulatorInterface simulator, final WidthGenerator widthGenerator,
1497                 final Map<Lane, OTSLine3D> leftEdges, final Map<Lane, OTSLine3D> rightEdges)
1498         {
1499             this.lane1 = lane1;
1500             this.dir1 = dir1;
1501             this.down1 = down1;
1502             this.up1 = up1;
1503             this.lane2 = lane2;
1504             this.dir2 = dir2;
1505             this.down2 = down2;
1506             this.up2 = up2;
1507             this.gtuType = gtuType;
1508             this.permitted = permitted;
1509             this.simulator = simulator;
1510             this.widthGenerator = widthGenerator;
1511             this.leftEdges = leftEdges;
1512             this.rightEdges = rightEdges;
1513         }
1514     }
1515 
1516     /** */
1517     static class CbrTaskBig implements Runnable
1518     {
1519         /** */
1520         final ConflictBuilderRecordBig cbr;
1521 
1522         /** */
1523         final AtomicInteger nrOfJobs;
1524 
1525         /**
1526          * @param nrOfJobs nr
1527          * @param cbr the record to execute
1528          */
1529         CbrTaskBig(final AtomicInteger nrOfJobs, final ConflictBuilderRecordBig cbr)
1530         {
1531             this.nrOfJobs = nrOfJobs;
1532             this.cbr = cbr;
1533         }
1534 
1535         @Override
1536         public void run()
1537         {
1538             // System.err.println("conflict #" + this.nr);
1539             try
1540             {
1541                 for (int j = this.cbr.starti + 1; j < this.cbr.lanes.size(); j++)
1542                 {
1543                     Lane lane2 = this.cbr.lanes.get(j);
1544                     if (this.cbr.ignoreList.contains(this.cbr.lane1, lane2))
1545                     {
1546                         continue;
1547                     }
1548                     // Quick contour check, skip if non-overlapping envelopes
1549                     try
1550                     {
1551                         if (!this.cbr.lane1.getContour().intersects(lane2.getContour()))
1552                         {
1553                             continue;
1554                         }
1555                     }
1556                     catch (Exception e)
1557                     {
1558                         System.err.println("Contour problem - lane1 = [" + this.cbr.lane1.getFullId() + "], lane2 = ["
1559                                 + lane2.getFullId() + "]; skipped");
1560                         continue;
1561                     }
1562 
1563                     boolean permitted = this.cbr.permittedList.contains(this.cbr.lane1, lane2);
1564 
1565                     for (GTUDirectionality dir2 : lane2.getLaneType().getDirectionality(this.cbr.gtuType).getDirectionalities())
1566                     {
1567                         ImmutableMap<Lane, GTUDirectionality> down2 = lane2.downstreamLanes(dir2, this.cbr.gtuType);
1568                         ImmutableMap<Lane, GTUDirectionality> up2 = lane2.upstreamLanes(dir2, this.cbr.gtuType);
1569 
1570                         try
1571                         {
1572                             buildConflicts(this.cbr.lane1, this.cbr.dir1, this.cbr.down1, this.cbr.up1, lane2, dir2, down2, up2,
1573                                     this.cbr.gtuType, permitted, this.cbr.simulator, this.cbr.widthGenerator,
1574                                     this.cbr.leftEdges, this.cbr.rightEdges, false, null);
1575                         }
1576                         catch (NetworkException | OTSGeometryException ne)
1577                         {
1578                             lane2.getParentLink().getSimulator().getLogger().always().error(ne,
1579                                     "Conflict build with bad combination of types / rules.");
1580                         }
1581                     }
1582                 }
1583 
1584             }
1585             catch (Exception e)
1586             {
1587                 e.printStackTrace();
1588             }
1589             this.nrOfJobs.decrementAndGet();
1590         }
1591     }
1592 
1593     /** */
1594     @SuppressWarnings("checkstyle:visibilitymodifier")
1595     static class ConflictBuilderRecordBig
1596     {
1597         /** */
1598         final int starti;
1599 
1600         /** */
1601         final List<Lane> lanes;
1602 
1603         /** */
1604         final LaneCombinationList ignoreList;
1605 
1606         /** */
1607         final LaneCombinationList permittedList;
1608 
1609         /** */
1610         final Lane lane1;
1611 
1612         /** */
1613         final GTUDirectionality dir1;
1614 
1615         /** */
1616         final ImmutableMap<Lane, GTUDirectionality> down1;
1617 
1618         /** */
1619         final ImmutableMap<Lane, GTUDirectionality> up1;
1620 
1621         /** */
1622         final GTUType gtuType;
1623 
1624         /** */
1625         final OTSSimulatorInterface simulator;
1626 
1627         /** */
1628         final WidthGenerator widthGenerator;
1629 
1630         /** */
1631         final Map<Lane, OTSLine3D> leftEdges;
1632 
1633         /** */
1634         final Map<Lane, OTSLine3D> rightEdges;
1635 
1636         /**
1637          * Stores conflicts about a single lane pair.
1638          * @param starti int; the start index
1639          * @param lanes List of lanes
1640          * @param ignoreList list of lane combinations to ignore
1641          * @param permittedList list of lane combinations to permit
1642          * @param lane1 Lane; lane 1
1643          * @param dir1 GTUDirectionality; gtu direction 1
1644          * @param down1 Map&lt;Lane,GTUDirectionality&gt;; downstream lanes 1
1645          * @param up1 Map&lt;Lane,GTUDirectionality&gt;; upstream lanes 1
1646          * @param gtuType GTUType; gtu type
1647          * @param simulator OTSSimulatorInterface; simulator
1648          * @param widthGenerator WidthGenerator; width generator
1649          * @param leftEdges Map&lt;Lane, OTSLine3D&gt;; cache of left edge lines
1650          * @param rightEdges Map&lt;Lane, OTSLine3D&gt;; cache of right edge lines
1651          */
1652         @SuppressWarnings("checkstyle:parameternumber")
1653         ConflictBuilderRecordBig(final int starti, final List<Lane> lanes, final LaneCombinationList ignoreList,
1654                 final LaneCombinationList permittedList, final Lane lane1, final GTUDirectionality dir1,
1655                 final ImmutableMap<Lane, GTUDirectionality> down1, final ImmutableMap<Lane, GTUDirectionality> up1,
1656                 final GTUType gtuType, final OTSSimulatorInterface simulator,
1657                 final WidthGenerator widthGenerator, final Map<Lane, OTSLine3D> leftEdges,
1658                 final Map<Lane, OTSLine3D> rightEdges)
1659         {
1660             this.starti = starti;
1661             this.lanes = lanes;
1662             this.ignoreList = ignoreList;
1663             this.permittedList = permittedList;
1664             this.lane1 = lane1;
1665             this.dir1 = dir1;
1666             this.down1 = down1;
1667             this.up1 = up1;
1668             this.gtuType = gtuType;
1669             this.simulator = simulator;
1670             this.widthGenerator = widthGenerator;
1671             this.leftEdges = leftEdges;
1672             this.rightEdges = rightEdges;
1673         }
1674     }
1675 
1676 }