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