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