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.simulators.DEVSSimulatorInterface;
34  
35  /**
36   * <p>
37   * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
38   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
39   * <p>
40   * @version $Revision$, $LastChangedDate$, by $Author$, initial version 11 dec. 2016 <br>
41   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
42   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
43   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
44   */
45  // TODO use z-coordinate for intersections of lines
46  public final class ConflictBuilder
47  {
48      /** number of merge onflicts. */
49      private static AtomicInteger numberMergeConflicts = new AtomicInteger(0);
50  
51      /** number of split onflicts. */
52      private static AtomicInteger numberSplitConflicts = new AtomicInteger(0);
53  
54      /** number of cross onflicts. */
55      private static AtomicInteger numberCrossConflicts = new AtomicInteger(0);
56  
57      /** Default width generator for conflicts which uses 80% of the lane width. */
58      public static final WidthGenerator DEFAULT_WIDTH_GENERATOR = new RelativeWidthGenerator(0.8);
59  
60      /**
61       * Empty constructor.
62       */
63      private ConflictBuilder()
64      {
65          //
66      }
67  
68      /**
69       * Build conflicts on network.
70       * @param network OTSRoadNetwork; network
71       * @param gtuType GTUType; gtu type
72       * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; simulator
73       * @param widthGenerator WidthGenerator; width generator
74       * @throws OTSGeometryException in case of geometry exception
75       */
76      public static void buildConflicts(final OTSRoadNetwork network, final GTUType gtuType,
77              final DEVSSimulatorInterface.TimeDoubleUnit simulator, final WidthGenerator widthGenerator)
78              throws OTSGeometryException
79      {
80          buildConflicts(network, gtuType, simulator, widthGenerator, new LaneCombinationListrk/lane/conflict/LaneCombinationList.html#LaneCombinationList">LaneCombinationList(), new LaneCombinationList());
81      }
82  
83      /**
84       * Build conflicts on network.
85       * @param network OTSRoadNetwork; network
86       * @param gtuType GTUType; gtu type
87       * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; simulator
88       * @param widthGenerator WidthGenerator; width generator
89       * @param ignoreList LaneCombinationList; lane combinations to ignore
90       * @param permittedList LaneCombinationList; lane combinations that are permitted by traffic control
91       * @throws OTSGeometryException in case of geometry exception
92       */
93      public static void buildConflicts(final OTSRoadNetwork network, final GTUType gtuType,
94              final DEVSSimulatorInterface.TimeDoubleUnit simulator, final WidthGenerator widthGenerator,
95              final LaneCombinationListflict/LaneCombinationList.html#LaneCombinationList">LaneCombinationList ignoreList, final LaneCombinationList permittedList) throws OTSGeometryException
96      {
97          // Create list of lanes
98          ImmutableMap<String, Link> links = network.getLinkMap();
99          List<Lane> lanes = new ArrayList<>();
100         for (String linkId : links.keySet())
101         {
102             Link link = links.get(linkId);
103             if (link instanceof CrossSectionLink)
104             {
105                 for (CrossSectionElement element : ((CrossSectionLink) link).getCrossSectionElementList())
106                 {
107                     if (element instanceof Lane)
108                     {
109                         lanes.add((Lane) element);
110                     }
111                 }
112             }
113         }
114         buildConflicts(lanes, gtuType, simulator, widthGenerator, ignoreList, permittedList);
115     }
116 
117     /**
118      * Build conflicts on list of lanes.
119      * @param lanes List&lt;Lane&gt;; lanes
120      * @param gtuType GTUType; gtu type
121      * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; simulator
122      * @param widthGenerator WidthGenerator; width generator
123      * @throws OTSGeometryException in case of geometry exception
124      */
125     public static void buildConflicts(final List<Lane> lanes, final GTUType gtuType,
126             final DEVSSimulatorInterface.TimeDoubleUnit simulator, final WidthGenerator widthGenerator)
127             throws OTSGeometryException
128     {
129         buildConflicts(lanes, gtuType, simulator, widthGenerator, new LaneCombinationListrk/lane/conflict/LaneCombinationList.html#LaneCombinationList">LaneCombinationList(), new LaneCombinationList());
130     }
131 
132     /**
133      * Build conflicts on list of lanes.
134      * @param lanes List&lt;Lane&gt;; list of Lanes
135      * @param gtuType GTUType; the GTU type
136      * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; the simulator
137      * @param widthGenerator WidthGenerator; the width generator
138      * @param ignoreList LaneCombinationList; lane combinations to ignore
139      * @param permittedList LaneCombinationList; lane combinations that are permitted by traffic control
140      * @throws OTSGeometryException in case of geometry exception
141      */
142     public static void buildConflicts(final List<Lane> lanes, final GTUType gtuType,
143             final DEVSSimulatorInterface.TimeDoubleUnit simulator, final WidthGenerator widthGenerator,
144             final LaneCombinationListflict/LaneCombinationList.html#LaneCombinationList">LaneCombinationList ignoreList, final LaneCombinationList permittedList) throws OTSGeometryException
145     {
146         // Loop Lane / GTUDirectionality combinations
147         long totalCombinations = ((long) lanes.size()) * ((long) lanes.size() - 1) / 2;
148         simulator.getLogger().always().debug("GENERATING CONFLICTS (NON-PARALLEL MODE). {} COMBINATIONS", totalCombinations);
149         long lastReported = 0;
150         Map<Lane, OTSLine3D> leftEdges = new LinkedHashMap<>();
151         Map<Lane, OTSLine3D> rightEdges = new LinkedHashMap<>();
152 
153         for (int i = 0; i < lanes.size(); i++)
154         {
155             long combinationsDone = totalCombinations - ((long) (lanes.size() - i)) * ((long) (lanes.size() - i)) / 2;
156             if (combinationsDone / 100000000 > lastReported)
157             {
158                 simulator.getLogger().always()
159                         .debug(String.format(
160                                 "generating conflicts at %.0f%% (generated %d merge conflicts, %d split "
161                                         + "conflicts, %d crossing conflicts)",
162                                 100.0 * combinationsDone / totalCombinations, numberMergeConflicts.get(),
163                                 numberSplitConflicts.get(), numberCrossConflicts.get()));
164                 lastReported = combinationsDone / 100000000;
165             }
166             Lane lane1 = lanes.get(i);
167             for (GTUDirectionality dir1 : lane1.getLaneType().getDirectionality(gtuType).getDirectionalities())
168             {
169                 ImmutableMap<Lane, GTUDirectionality> down1 = lane1.downstreamLanes(dir1, gtuType);
170                 ImmutableMap<Lane, GTUDirectionality> up1 = lane1.upstreamLanes(dir1, gtuType);
171 
172                 for (int j = i + 1; j < lanes.size(); j++)
173                 {
174                     Lane lane2 = lanes.get(j);
175                     if (ignoreList.contains(lane1, lane2))
176                     {
177                         continue;
178                     }
179                     boolean permitted = permittedList.contains(lane1, lane2);
180 
181                     for (GTUDirectionality dir2 : lane2.getLaneType().getDirectionality(gtuType).getDirectionalities())
182                     {
183                         ImmutableMap<Lane, GTUDirectionality> down2 = lane2.downstreamLanes(dir2, gtuType);
184                         ImmutableMap<Lane, GTUDirectionality> up2 = lane2.upstreamLanes(dir2, gtuType);
185                         // See if conflict needs to be build, and build if so
186                         try
187                         {
188                             buildConflicts(lane1, dir1, down1, up1, lane2, dir2, down2, up2, gtuType, permitted, simulator,
189                                     widthGenerator, leftEdges, rightEdges, true);
190                         }
191                         catch (NetworkException ne)
192                         {
193                             throw new RuntimeException("Conflict build with bad combination of types / rules.", ne);
194                         }
195                     }
196                 }
197             }
198         }
199         simulator.getLogger().always()
200                 .debug(String.format(
201                         "generating conflicts complete (generated %d merge conflicts, %d split "
202                                 + "conflicts, %d crossing conflicts)",
203                         numberMergeConflicts.get(), numberSplitConflicts.get(), numberCrossConflicts.get()));
204     }
205 
206     /**
207      * Build conflict on single lane pair. Connecting lanes are determined.
208      * @param lane1 Lane; lane 1
209      * @param dir1 GTUDirectionality; gtu direction 1
210      * @param lane2 Lane; lane 2
211      * @param dir2 GTUDirectionality; gtu direction 2
212      * @param gtuType GTUType; gtu type
213      * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; simulator
214      * @param widthGenerator WidthGenerator; width generator
215      * @throws OTSGeometryException in case of geometry exception
216      */
217     @SuppressWarnings("checkstyle:parameternumber")
218     public static void buildConflicts(final Lane lane1, Lane_keyword">final GTUDirectionality dir1, final Lane lane2,
219             final GTUDirectionality dir2, final GTUType gtuType, final DEVSSimulatorInterface.TimeDoubleUnit simulator,
220             final WidthGenerator widthGenerator) throws OTSGeometryException
221     {
222         buildConflicts(lane1, dir1, lane2, dir2, gtuType, simulator, widthGenerator, false);
223     }
224 
225     /**
226      * Build conflict on single lane pair. Connecting lanes are determined.
227      * @param lane1 Lane; lane 1
228      * @param dir1 GTUDirectionality; gtu direction 1
229      * @param lane2 Lane; lane 2
230      * @param dir2 GTUDirectionality; gtu direction 2
231      * @param gtuType GTUType; gtu type
232      * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; simulator
233      * @param widthGenerator WidthGenerator; width generator
234      * @param permitted boolean; conflict permitted by traffic control
235      * @throws OTSGeometryException in case of geometry exception
236      */
237     @SuppressWarnings("checkstyle:parameternumber")
238     public static void buildConflicts(final Lane lane1, Lane_keyword">final GTUDirectionality dir1, final Lane lane2,
239             final GTUDirectionality dir2, final GTUType gtuType, final DEVSSimulatorInterface.TimeDoubleUnit simulator,
240             final WidthGenerator widthGenerator, final boolean permitted) throws OTSGeometryException
241     {
242         ImmutableMap<Lane, GTUDirectionality> down1 = lane1.downstreamLanes(dir1, gtuType);
243         ImmutableMap<Lane, GTUDirectionality> up1 = lane1.upstreamLanes(dir1, gtuType);
244         ImmutableMap<Lane, GTUDirectionality> down2 = lane2.downstreamLanes(dir2, gtuType);
245         ImmutableMap<Lane, GTUDirectionality> up2 = lane2.upstreamLanes(dir2, gtuType);
246         try
247         {
248             buildConflicts(lane1, dir1, down1, up1, lane2, dir2, down2, up2, gtuType, permitted, simulator, widthGenerator,
249                     new LinkedHashMap<>(), new LinkedHashMap<>(), true);
250         }
251         catch (NetworkException ne)
252         {
253             throw new RuntimeException("Conflict build with bad combination of types / rules.", ne);
254         }
255     }
256 
257     /**
258      * Build conflicts on single lane pair.
259      * @param lane1 Lane; lane 1
260      * @param dir1 GTUDirectionality; gtu direction 1
261      * @param down1 Map&lt;Lane,GTUDirectionality&gt;; downstream lanes 1
262      * @param up1 Map&lt;Lane,GTUDirectionality&gt;; upstream lanes 1
263      * @param lane2 Lane; lane 2
264      * @param dir2 GTUDirectionality; gtu direction 2
265      * @param down2 Map&lt;Lane,GTUDirectionality&gt;; downstream lane 2
266      * @param up2 Map&lt;Lane,GTUDirectionality&gt;; upstream lanes 2
267      * @param gtuType GTUType; gtu type
268      * @param permitted boolean; conflict permitted by traffic control
269      * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; simulator
270      * @param widthGenerator WidthGenerator; width generator
271      * @param leftEdges Map<Lane, OTSLine3D>; cache of left edge lines
272      * @param rightEdges Map<Lane, OTSLine3D>; cache of right edge lines
273      * @param intersectionCheck indicate whether we have to do a countour intersection check still
274      * @throws OTSGeometryException in case of geometry exception
275      * @throws NetworkException if the combination of conflict type and both conflict rules is not correct
276      */
277     @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:methodlength"})
278     static void buildConflicts(final Lane lane1, final GTUDirectionality dir1,
279             final ImmutableMap<Lane, GTUDirectionality> down1, final ImmutableMap<Lane, GTUDirectionality> up1,
280             final Lane lane2, final GTUDirectionality dir2, final ImmutableMap<Lane, GTUDirectionality> down2,
281             final ImmutableMap<Lane, GTUDirectionality> up2, final GTUType gtuType, final boolean permitted,
282             final DEVSSimulatorInterface.TimeDoubleUnit simulator, final WidthGenerator widthGenerator,
283             final Map<Lane, OTSLine3D> leftEdges, final Map<Lane, OTSLine3D> rightEdges, final boolean intersectionCheck)
284             throws OTSGeometryException, NetworkException
285     {
286         // Quick contour check, skip if not overlapping -- Don't repeat if it has taken place
287         if (intersectionCheck)
288         {
289             if (!lane1.getContour().intersects(lane2.getContour()))
290             {
291                 return;
292             }
293         }
294 
295         // TODO: we cache, but the width generator may be different
296 
297         // Get left and right lines at specified width
298         OTSLine3D left1;
299         OTSLine3D right1;
300         synchronized (lane1)
301         {
302             left1 = leftEdges.get(lane1);
303             right1 = rightEdges.get(lane1);
304             OTSLine3D line1 = lane1.getCenterLine();
305             if (null == left1)
306             {
307                 left1 = line1.offsetLine(widthGenerator.getWidth(lane1, 0.0) / 2, widthGenerator.getWidth(lane1, 1.0) / 2);
308                 leftEdges.put(lane1, left1);
309             }
310             if (null == right1)
311             {
312                 right1 = line1.offsetLine(-widthGenerator.getWidth(lane1, 0.0) / 2, -widthGenerator.getWidth(lane1, 1.0) / 2);
313                 rightEdges.put(lane1, right1);
314             }
315         }
316 
317         OTSLine3D left2;
318         OTSLine3D right2;
319         synchronized (lane2)
320         {
321             left2 = leftEdges.get(lane2);
322             right2 = rightEdges.get(lane2);
323             OTSLine3D line2 = lane2.getCenterLine();
324             if (null == left2)
325             {
326                 left2 = line2.offsetLine(widthGenerator.getWidth(lane2, 0.0) / 2, widthGenerator.getWidth(lane2, 1.0) / 2);
327                 leftEdges.put(lane2, left2);
328             }
329             if (null == right2)
330             {
331                 right2 = line2.offsetLine(-widthGenerator.getWidth(lane2, 0.0) / 2, -widthGenerator.getWidth(lane2, 1.0) / 2);
332                 rightEdges.put(lane2, right2);
333             }
334         }
335 
336         // Get list of all intersection fractions
337         SortedSet<Intersection> intersections = Intersection.getIntersectionList(left1, left2, 0);
338         intersections.addAll(Intersection.getIntersectionList(left1, right2, 1));
339         intersections.addAll(Intersection.getIntersectionList(right1, left2, 2));
340         intersections.addAll(Intersection.getIntersectionList(right1, right2, 3));
341 
342         // Create merge
343         ImmutableIterator<ImmutableEntry<Lane, GTUDirectionality>> iterator1 = down1.entrySet().iterator();
344         ImmutableIterator<ImmutableEntry<Lane, GTUDirectionality>> iterator2 = down2.entrySet().iterator();
345         boolean merge = false;
346         while (iterator1.hasNext() && !merge)
347         {
348             ImmutableEntry<Lane, GTUDirectionality> next1 = iterator1.next();
349             while (iterator2.hasNext() && !merge)
350             {
351                 ImmutableEntry<Lane, GTUDirectionality> next2 = iterator2.next();
352                 if (next1.equals(next2))
353                 {
354                     // Same downstream lane, so a merge
355                     double fraction1 = Double.NaN;
356                     double fraction2 = Double.NaN;
357                     for (Intersection intersection : intersections)
358                     {
359                         // Only consider left/right and right/left intersections (others may or may not be at the end)
360                         if (intersection.getCombo() == 1 || intersection.getCombo() == 2)
361                         {
362                             fraction1 = intersection.getFraction1();
363                             fraction2 = intersection.getFraction2();
364                         }
365                     }
366                     // Remove all intersections beyond this point, these are the result of line starts/ends matching
367                     Iterator<Intersection> iterator = intersections.iterator();
368                     while (iterator.hasNext())
369                     {
370                         if (iterator.next().getFraction1() >= fraction1)
371                         {
372                             iterator.remove();
373                         }
374                     }
375                     if (Double.isNaN(fraction1))
376                     {
377                         simulator.getLogger().always().warn("Fixing fractions of merge conflict");
378                         fraction1 = 0;
379                         fraction2 = 0;
380                     }
381                     // Build conflict
382                     buildMergeConflict(lane1, dir1, fraction1, lane2, dir2, fraction2, gtuType, simulator, widthGenerator,
383                             permitted);
384                     // Skip loop for efficiency, and do not create multiple merges in case of multiple same downstream lanes
385                     merge = true;
386                 }
387             }
388         }
389 
390         // Create split
391         iterator1 = up1.entrySet().iterator();
392         iterator2 = up2.entrySet().iterator();
393         boolean split = false;
394         while (iterator1.hasNext() && !split)
395         {
396             ImmutableEntry<Lane, GTUDirectionality> prev1 = iterator1.next();
397             while (iterator2.hasNext() && !split)
398             {
399                 ImmutableEntry<Lane, GTUDirectionality> prev2 = iterator2.next();
400                 if (prev1.equals(prev2))
401                 {
402                     // Same upstream lane, so a split
403                     double fraction1 = Double.NaN;
404                     double fraction2 = Double.NaN;
405                     for (Intersection intersection : intersections)
406                     {
407                         // Only consider left/right and right/left intersections (others may or may not be at the start)
408                         if (intersection.getCombo() == 1 || intersection.getCombo() == 2)
409                         {
410                             fraction1 = intersection.getFraction1();
411                             fraction2 = intersection.getFraction2();
412                             break; // Split so first, not last
413                         }
414                     }
415                     // Remove all intersections up to this point, these are the result of line starts/ends matching
416                     Iterator<Intersection> iterator = intersections.iterator();
417                     while (iterator.hasNext())
418                     {
419                         if (iterator.next().getFraction1() <= fraction1)
420                         {
421                             iterator.remove();
422                         }
423                         else
424                         {
425                             // May skip further fraction
426                             break;
427                         }
428                     }
429                     if (Double.isNaN(fraction1))
430                     {
431                         simulator.getLogger().always().warn("Fixing fractions of split conflict");
432                         fraction1 = 1;
433                         fraction2 = 1;
434                     }
435                     // Build conflict
436                     buildSplitConflict(lane1, dir1, fraction1, lane2, dir2, fraction2, gtuType, simulator, widthGenerator);
437                     // Skip loop for efficiency, and do not create multiple splits in case of multiple same upstream lanes
438                     split = true;
439                 }
440             }
441         }
442 
443         // Create crossings
444         if (!lane1.getParentLink().equals(lane2.getParentLink())) // tight inner-curves with dedicated Bezier ignored
445         {
446             boolean[] crossed = new boolean[4];
447             Iterator<Intersection> iterator = intersections.iterator();
448             double f1Start = Double.NaN;
449             double f2Start = Double.NaN;
450             double f2End = Double.NaN;
451             while (iterator.hasNext())
452             {
453                 Intersection intersection = iterator.next();
454                 // First fraction found is start of conflict
455                 if (Double.isNaN(f1Start))
456                 {
457                     f1Start = intersection.getFraction1();
458                 }
459                 f2Start = Double.isNaN(f2Start) ? intersection.getFraction2() : Math.min(f2Start, intersection.getFraction2());
460                 f2End = Double.isNaN(f2End) ? intersection.getFraction2() : Math.max(f2End, intersection.getFraction2());
461                 // Flip crossed state of intersecting line combination
462                 crossed[intersection.getCombo()] = !crossed[intersection.getCombo()];
463                 // If all crossed or all not crossed, end of conflict
464                 if ((crossed[0] && crossed[1] && crossed[2] && crossed[3])
465                         || (!crossed[0] && !crossed[1] && !crossed[2] && !crossed[3]))
466                 {
467                     if (dir2.isMinus())
468                     {
469                         double f2Temp = f2Start;
470                         f2Start = f2End;
471                         f2End = f2Temp;
472                     }
473                     if (Double.isNaN(f1Start) || Double.isNaN(f2Start) || Double.isNaN(f2End))
474                     {
475                         simulator.getLogger().always().warn("NOT YET Fixing fractions of crossing conflict");
476                     }
477                     buildCrossingConflict(lane1, dir1, f1Start, intersection.getFraction1(), lane2, dir2, f2Start, f2End,
478                             gtuType, simulator, widthGenerator, permitted);
479                     f1Start = Double.NaN;
480                     f2Start = Double.NaN;
481                     f2End = Double.NaN;
482                 }
483             }
484         }
485 
486     }
487 
488     /**
489      * Build a merge conflict.
490      * @param lane1 Lane; lane 1
491      * @param dir1 GTUDirectionality; gtu direction 1
492      * @param f1start double; start fraction 1
493      * @param lane2 Lane; lane 2
494      * @param dir2 GTUDirectionality; gtu direction 2
495      * @param f2start double; start fraction 2
496      * @param gtuType GTUType; gtu type
497      * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; simulator
498      * @param widthGenerator WidthGenerator; width generator
499      * @param permitted boolean; conflict permitted by traffic control
500      * @throws NetworkException if the combination of conflict type and both conflict rules is not correct
501      * @throws OTSGeometryException in case of geometry exception
502      */
503     @SuppressWarnings("checkstyle:parameternumber")
504     private static void buildMergeConflict(final Lane lane1, final GTUDirectionality dir1, final double f1start,
505             final Lane lane2, final GTUDirectionality dir2, final double f2start, final GTUType gtuType,
506             final DEVSSimulatorInterface.TimeDoubleUnit simulator, final WidthGenerator widthGenerator, final boolean permitted)
507             throws NetworkException, OTSGeometryException
508     {
509 
510         // Determine lane end from direction
511         double f1end = dir1.isPlus() ? 1.0 : 0.0;
512         double f2end = dir2.isPlus() ? 1.0 : 0.0;
513 
514         // Get locations and length
515         Length longitudinalPosition1 = lane1.getLength().times(f1start);
516         Length longitudinalPosition2 = lane2.getLength().times(f2start);
517         Length length1 = lane1.getLength().times(Math.abs(f1end - f1start));
518         Length length2 = lane2.getLength().times(Math.abs(f2end - f2start));
519 
520         // Get geometries
521         OTSLine3D geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
522         OTSLine3D geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
523 
524         // Determine conflict rule
525         ConflictRule conflictRule;
526         if (lane1.getParentLink().getPriority().isBusStop() || lane2.getParentLink().getPriority().isBusStop())
527         {
528             Throw.when(lane1.getParentLink().getPriority().isBusStop() && lane2.getParentLink().getPriority().isBusStop(),
529                     IllegalArgumentException.class, "Merge conflict between two links with bus stop priority not supported.");
530             conflictRule = new BusStopConflictRule(simulator);
531         }
532         else
533         {
534             conflictRule = new DefaultConflictRule();
535         }
536 
537         // Make conflict
538         Conflict.generateConflictPair(ConflictType.MERGE, conflictRule, permitted, lane1, longitudinalPosition1, length1, dir1,
539                 geometry1, gtuType, lane2, longitudinalPosition2, length2, dir2, geometry2, gtuType, simulator);
540 
541         numberMergeConflicts.incrementAndGet();
542     }
543 
544     /**
545      * Build a split conflict.
546      * @param lane1 Lane; lane 1
547      * @param dir1 GTUDirectionality; gtu direction 1
548      * @param f1end double; end fraction 1
549      * @param lane2 Lane; lane 2
550      * @param dir2 GTUDirectionality; gtu direction 2
551      * @param f2end double; end fraction 2
552      * @param gtuType GTUType; gtu type
553      * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; simulator
554      * @param widthGenerator WidthGenerator; width generator
555      * @throws NetworkException if the combination of conflict type and both conflict rules is not correct
556      * @throws OTSGeometryException in case of geometry exception
557      */
558     @SuppressWarnings("checkstyle:parameternumber")
559     private static void buildSplitConflict(final Lane lane1, final GTUDirectionality dir1, finalLanetrong class="jxr_keyword">double f1end, final Lane lane2,
560             final GTUDirectionality dir2, final double f2end, final GTUType gtuType,
561             final DEVSSimulatorInterface.TimeDoubleUnit simulator, final WidthGenerator widthGenerator)
562             throws NetworkException, OTSGeometryException
563     {
564 
565         // Determine lane start from direction
566         double f1start = dir1.isPlus() ? 0.0 : 1.0;
567         double f2start = dir2.isPlus() ? 0.0 : 1.0;
568 
569         // Get locations and length
570         Length longitudinalPosition1 = lane1.getLength().times(f1start);
571         Length longitudinalPosition2 = lane2.getLength().times(f2start);
572         Length length1 = lane1.getLength().times(Math.abs(f1end - f1start));
573         Length length2 = lane2.getLength().times(Math.abs(f2end - f2start));
574 
575         // Get geometries
576         OTSLine3D geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
577         OTSLine3D geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
578 
579         // Make conflict
580         Conflict.generateConflictPair(ConflictType.SPLIT, new SplitConflictRule(), false, lane1, longitudinalPosition1, length1,
581                 dir1, geometry1, gtuType, lane2, longitudinalPosition2, length2, dir2, geometry2, gtuType, simulator);
582 
583         numberSplitConflicts.incrementAndGet();
584     }
585 
586     /**
587      * Build a crossing conflict.
588      * @param lane1 Lane; lane 1
589      * @param dir1 GTUDirectionality; gtu direction 1
590      * @param f1start double; start fraction 1
591      * @param f1end double; end fraction 1
592      * @param lane2 Lane; lane 2
593      * @param dir2 GTUDirectionality; gtu direction 2
594      * @param f2start double; start fraction 2
595      * @param f2end double; end fraction 2
596      * @param gtuType GTUType; gtu type
597      * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; simulator
598      * @param widthGenerator WidthGenerator; width generator
599      * @param permitted boolean; conflict permitted by traffic control
600      * @throws NetworkException if the combination of conflict type and both conflict rules is not correct
601      * @throws OTSGeometryException in case of geometry exception
602      */
603     @SuppressWarnings("checkstyle:parameternumber")
604     private static void buildCrossingConflict(final Lane lane1, final GTUDirectionality dir1, final double f1start,
605             final double f1end, final Lane lane2, final GTUDirectionality dir2, final double f2start, final double f2end,
606             final GTUType gtuType, final DEVSSimulatorInterface.TimeDoubleUnit simulator, final WidthGenerator widthGenerator,
607             final boolean permitted) throws NetworkException, OTSGeometryException
608     {
609 
610         // Fractions may be in opposite direction, for the start location this needs to be correct
611         // Note: for geometry (real order, not considering direction) and length (absolute value) this does not matter
612         double f1startDirected;
613         double f2startDirected;
614         if ((dir1.isPlus() && f1end < f1start) || (dir1.isMinus() && f1end > f1start))
615         {
616             f1startDirected = f1end;
617         }
618         else
619         {
620             f1startDirected = f1start;
621         }
622         if ((dir2.isPlus() && f2end < f2start) || (dir2.isMinus() && f2end > f2start))
623         {
624             f2startDirected = f2end;
625         }
626         else
627         {
628             f2startDirected = f2start;
629         }
630 
631         // Get locations and length
632         Length longitudinalPosition1 = lane1.getLength().times(f1startDirected);
633         Length longitudinalPosition2 = lane2.getLength().times(f2startDirected);
634         Length length1 = lane1.getLength().times(Math.abs(f1end - f1start));
635         Length length2 = lane2.getLength().times(Math.abs(f2end - f2start));
636 
637         // Get geometries
638         OTSLine3D geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
639         OTSLine3D geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
640 
641         // Determine conflict rule
642         ConflictRule conflictRule;
643         if (lane1.getParentLink().getPriority().isBusStop() || lane2.getParentLink().getPriority().isBusStop())
644         {
645             Throw.when(lane1.getParentLink().getPriority().isBusStop() && lane2.getParentLink().getPriority().isBusStop(),
646                     IllegalArgumentException.class, "Merge conflict between two links with bus stop priority not supported.");
647             conflictRule = new BusStopConflictRule(simulator);
648         }
649         else
650         {
651             conflictRule = new DefaultConflictRule();
652         }
653 
654         // Make conflict
655         Conflict.generateConflictPair(ConflictType.CROSSING, conflictRule, permitted, lane1, longitudinalPosition1, length1,
656                 dir1, geometry1, gtuType, lane2, longitudinalPosition2, length2, dir2, geometry2, gtuType, simulator);
657 
658         numberCrossConflicts.incrementAndGet();
659     }
660 
661     /**
662      * Creates geometry for conflict.
663      * @param lane Lane; lane
664      * @param fStart double; longitudinal fraction of start
665      * @param fEnd double; longitudinal fraction of end
666      * @param widthGenerator WidthGenerator; width generator
667      * @return geometry for conflict
668      * @throws OTSGeometryException in case of geometry exception
669      */
670     private static OTSLine3D getGeometry(final Lane lane, final double fStart, final double fEnd,
671             final WidthGenerator widthGenerator) throws OTSGeometryException
672     {
673         // extractFractional needs ordered fractions, irrespective of driving direction
674         double f1;
675         double f2;
676         if (fEnd > fStart)
677         {
678             f1 = fStart;
679             f2 = fEnd;
680         }
681         else
682         {
683             f1 = fEnd;
684             f2 = fStart;
685         }
686         if (f1 == f2)
687         {
688             lane.getParentLink().getSimulator().getLogger().always()
689                     .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                 simulator.getLogger().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         simulator.getLogger().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                 simulator.getLogger().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         simulator.getLogger().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             simulator.getLogger().setAllLogLevel(Level.WARNING);
1364             buildConflicts(lanes, gtuType, simulator, widthGenerator, new LaneCombinationListrk/lane/conflict/LaneCombinationList.html#LaneCombinationList">LaneCombinationList(), new LaneCombinationList());
1365             simulator.getLogger().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                             lane2.getParentLink().getSimulator().getLogger().always().error(ne,
1574                                     "Conflict build with bad combination of types / rules.");
1575                         }
1576                     }
1577                 }
1578 
1579             }
1580             catch (Exception e)
1581             {
1582                 e.printStackTrace();
1583             }
1584             this.nrOfJobs.decrementAndGet();
1585         }
1586     }
1587 
1588     /** */
1589     @SuppressWarnings("checkstyle:visibilitymodifier")
1590     static class ConflictBuilderRecordBig
1591     {
1592         /** */
1593         final int starti;
1594 
1595         /** */
1596         final List<Lane> lanes;
1597 
1598         /** */
1599         final LaneCombinationList ignoreList;
1600 
1601         /** */
1602         final LaneCombinationList permittedList;
1603 
1604         /** */
1605         final Lane lane1;
1606 
1607         /** */
1608         final GTUDirectionality dir1;
1609 
1610         /** */
1611         final ImmutableMap<Lane, GTUDirectionality> down1;
1612 
1613         /** */
1614         final ImmutableMap<Lane, GTUDirectionality> up1;
1615 
1616         /** */
1617         final GTUType gtuType;
1618 
1619         /** */
1620         final DEVSSimulatorInterface.TimeDoubleUnit simulator;
1621 
1622         /** */
1623         final WidthGenerator widthGenerator;
1624 
1625         /** */
1626         final Map<Lane, OTSLine3D> leftEdges;
1627 
1628         /** */
1629         final Map<Lane, OTSLine3D> rightEdges;
1630 
1631         /**
1632          * Stores conflicts about a single lane pair.
1633          * @param starti int; the start index
1634          * @param lanes List of lanes
1635          * @param ignoreList list of lane combinations to ignore
1636          * @param permittedList list of lane combinations to permit
1637          * @param lane1 Lane; lane 1
1638          * @param dir1 GTUDirectionality; gtu direction 1
1639          * @param down1 Map&lt;Lane,GTUDirectionality&gt;; downstream lanes 1
1640          * @param up1 Map&lt;Lane,GTUDirectionality&gt;; upstream lanes 1
1641          * @param gtuType GTUType; gtu type
1642          * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; simulator
1643          * @param widthGenerator WidthGenerator; width generator
1644          * @param leftEdges Map<Lane, OTSLine3D>; cache of left edge lines
1645          * @param rightEdges Map<Lane, OTSLine3D>; cache of right edge lines
1646          */
1647         @SuppressWarnings("checkstyle:parameternumber")
1648         ConflictBuilderRecordBig(final int starti, final List<Lane> lanes, final LaneCombinationList ignoreList,
1649                 final LaneCombinationList permittedList, final Lane lane1, final GTUDirectionality dir1,
1650                 final ImmutableMap<Lane, GTUDirectionality> down1, final ImmutableMap<Lane, GTUDirectionality> up1,
1651                 final GTUType gtuType, final DEVSSimulatorInterface.TimeDoubleUnit simulator,
1652                 final WidthGenerator widthGenerator, final Map<Lane, OTSLine3D> leftEdges,
1653                 final Map<Lane, OTSLine3D> rightEdges)
1654         {
1655             this.starti = starti;
1656             this.lanes = lanes;
1657             this.ignoreList = ignoreList;
1658             this.permittedList = permittedList;
1659             this.lane1 = lane1;
1660             this.dir1 = dir1;
1661             this.down1 = down1;
1662             this.up1 = up1;
1663             this.gtuType = gtuType;
1664             this.simulator = simulator;
1665             this.widthGenerator = widthGenerator;
1666             this.leftEdges = leftEdges;
1667             this.rightEdges = rightEdges;
1668         }
1669     }
1670 
1671 }