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.draw.line.Polygon2d;
17 import org.djutils.draw.point.Point2d;
18 import org.djutils.exceptions.Throw;
19 import org.djutils.immutablecollections.ImmutableMap;
20 import org.opentrafficsim.base.geometry.OtsLine2d;
21 import org.opentrafficsim.core.definitions.DefaultsNl;
22 import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
23 import org.opentrafficsim.core.network.Link;
24 import org.opentrafficsim.core.network.NetworkException;
25 import org.opentrafficsim.road.network.RoadNetwork;
26 import org.opentrafficsim.road.network.lane.CrossSectionElement;
27 import org.opentrafficsim.road.network.lane.CrossSectionLink;
28 import org.opentrafficsim.road.network.lane.Lane;
29 import org.opentrafficsim.road.network.lane.Shoulder;
30 import org.pmw.tinylog.Level;
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 public final class ConflictBuilder
51 {
52
53 private static AtomicInteger numberMergeConflicts = new AtomicInteger(0);
54
55
56 private static AtomicInteger numberSplitConflicts = new AtomicInteger(0);
57
58
59 private static AtomicInteger numberCrossConflicts = new AtomicInteger(0);
60
61
62 public static final WidthGenerator DEFAULT_WIDTH_GENERATOR = new RelativeWidthGenerator(0.8);
63
64
65
66
67 private ConflictBuilder()
68 {
69
70 }
71
72
73
74
75
76
77
78 public static void buildConflicts(final RoadNetwork network, final OtsSimulatorInterface simulator,
79 final WidthGenerator widthGenerator)
80 {
81 buildConflicts(network, simulator, widthGenerator, new LaneCombinationList(), new LaneCombinationList());
82 }
83
84
85
86
87
88
89
90
91
92 public static void buildConflicts(final RoadNetwork network, final OtsSimulatorInterface simulator,
93 final WidthGenerator widthGenerator, final LaneCombinationList ignoreList, final LaneCombinationList permittedList)
94 {
95 buildConflicts(getLanes(network), simulator, widthGenerator, ignoreList, permittedList, null);
96 }
97
98
99
100
101
102
103 private static List<Lane> getLanes(final RoadNetwork network)
104 {
105 ImmutableMap<String, Link> links = network.getLinkMap();
106 List<Lane> lanes = new ArrayList<>();
107 for (String linkId : links.keySet())
108 {
109 Link link = links.get(linkId);
110 if (link instanceof CrossSectionLink)
111 {
112 for (CrossSectionElement element : ((CrossSectionLink) link).getCrossSectionElementList())
113 {
114 if (element instanceof Lane lane && !(element instanceof Shoulder))
115 {
116 lanes.add((Lane) element);
117 }
118 }
119 }
120 }
121 return lanes;
122 }
123
124
125
126
127
128
129
130 public static void buildConflicts(final List<Lane> lanes, final OtsSimulatorInterface simulator,
131 final WidthGenerator widthGenerator)
132 {
133 buildConflicts(lanes, simulator, widthGenerator, new LaneCombinationList(), new LaneCombinationList(), null);
134 }
135
136
137
138
139
140
141
142
143
144
145 public static void buildConflicts(final List<Lane> lanes, final OtsSimulatorInterface simulator,
146 final WidthGenerator widthGenerator, final LaneCombinationList ignoreList, final LaneCombinationList permittedList,
147 final String conflictId)
148 {
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, OtsLine2d> leftEdges = new LinkedHashMap<>();
153 Map<Lane, OtsLine2d> 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 Set<Lane> down1 = lane1.nextLanes(null);
170 Set<Lane> up1 = lane1.prevLanes(null);
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 Set<Lane> down2 = lane2.nextLanes(null);
182 Set<Lane> up2 = lane2.prevLanes(null);
183
184 try
185 {
186 buildConflicts(lane1, down1, up1, lane2, down2, up2, permitted, simulator, widthGenerator, leftEdges,
187 rightEdges, true, conflictId);
188 }
189 catch (NetworkException ne)
190 {
191 throw new RuntimeException("Conflict build with bad combination of types / rules.", ne);
192 }
193 }
194 }
195 simulator.getLogger().always()
196 .trace(String.format(
197 "generating conflicts complete (generated %d merge conflicts, %d split "
198 + "conflicts, %d crossing conflicts)",
199 numberMergeConflicts.get(), numberSplitConflicts.get(), numberCrossConflicts.get()));
200 }
201
202
203
204
205
206
207
208
209 @SuppressWarnings("checkstyle:parameternumber")
210 public static void buildConflicts(final Lane lane1, final Lane lane2, final OtsSimulatorInterface simulator,
211 final WidthGenerator widthGenerator)
212 {
213 buildConflicts(lane1, lane2, simulator, widthGenerator, false);
214 }
215
216
217
218
219
220
221
222
223
224 @SuppressWarnings("checkstyle:parameternumber")
225 public static void buildConflicts(final Lane lane1, final Lane lane2, final OtsSimulatorInterface simulator,
226 final WidthGenerator widthGenerator, final boolean permitted)
227 {
228 Set<Lane> down1 = lane1.nextLanes(null);
229 Set<Lane> up1 = lane1.prevLanes(null);
230 Set<Lane> down2 = lane2.nextLanes(null);
231 Set<Lane> up2 = lane2.prevLanes(null);
232 try
233 {
234 buildConflicts(lane1, down1, up1, lane2, down2, up2, permitted, simulator, widthGenerator, new LinkedHashMap<>(),
235 new LinkedHashMap<>(), true, null);
236 }
237 catch (NetworkException ne)
238 {
239 throw new RuntimeException("Conflict build with bad combination of types / rules.", ne);
240 }
241 }
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260 @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:methodlength"})
261 static void buildConflicts(final Lane lane1, final Set<Lane> down1, final Set<Lane> up1, final Lane lane2,
262 final Set<Lane> down2, final Set<Lane> up2, final boolean permitted, final OtsSimulatorInterface simulator,
263 final WidthGenerator widthGenerator, final Map<Lane, OtsLine2d> leftEdges, final Map<Lane, OtsLine2d> rightEdges,
264 final boolean intersectionCheck, final String conflictId) throws NetworkException
265 {
266
267 if (intersectionCheck)
268 {
269 if (!lane1.getContour().intersects(lane2.getContour()))
270 {
271 return;
272 }
273 }
274
275
276
277 String paddedConflictId = null == conflictId ? "" : (" in conflict group " + conflictId);
278
279 OtsLine2d left1;
280 OtsLine2d right1;
281 synchronized (lane1)
282 {
283 left1 = leftEdges.get(lane1);
284 right1 = rightEdges.get(lane1);
285 OtsLine2d line1 = lane1.getCenterLine();
286 if (null == left1)
287 {
288 left1 = line1.offsetLine(widthGenerator.getWidth(lane1, 0.0) / 2, widthGenerator.getWidth(lane1, 1.0) / 2);
289 leftEdges.put(lane1, left1);
290 }
291 if (null == right1)
292 {
293 right1 = line1.offsetLine(-widthGenerator.getWidth(lane1, 0.0) / 2, -widthGenerator.getWidth(lane1, 1.0) / 2);
294 rightEdges.put(lane1, right1);
295 }
296 }
297
298 OtsLine2d left2;
299 OtsLine2d right2;
300 synchronized (lane2)
301 {
302 left2 = leftEdges.get(lane2);
303 right2 = rightEdges.get(lane2);
304 OtsLine2d line2 = lane2.getCenterLine();
305 if (null == left2)
306 {
307 left2 = line2.offsetLine(widthGenerator.getWidth(lane2, 0.0) / 2, widthGenerator.getWidth(lane2, 1.0) / 2);
308 leftEdges.put(lane2, left2);
309 }
310 if (null == right2)
311 {
312 right2 = line2.offsetLine(-widthGenerator.getWidth(lane2, 0.0) / 2, -widthGenerator.getWidth(lane2, 1.0) / 2);
313 rightEdges.put(lane2, right2);
314 }
315 }
316
317
318 SortedSet<Intersection> intersections = Intersection.getIntersectionList(left1, left2, 0);
319 intersections.addAll(Intersection.getIntersectionList(left1, right2, 1));
320 intersections.addAll(Intersection.getIntersectionList(right1, left2, 2));
321 intersections.addAll(Intersection.getIntersectionList(right1, right2, 3));
322
323
324 Iterator<Lane> iterator1 = down1.iterator();
325 Iterator<Lane> iterator2 = down2.iterator();
326 boolean merge = false;
327 while (iterator1.hasNext() && !merge)
328 {
329 Lane next1 = iterator1.next();
330 while (iterator2.hasNext() && !merge)
331 {
332 Lane next2 = iterator2.next();
333 if (next1.equals(next2))
334 {
335
336 double fraction1 = Double.NaN;
337 double fraction2 = Double.NaN;
338 for (Intersection intersection : intersections)
339 {
340
341 if (intersection.getCombo() == 1 || intersection.getCombo() == 2)
342 {
343 fraction1 = intersection.getFraction1();
344 fraction2 = intersection.getFraction2();
345 }
346 }
347
348 Iterator<Intersection> iterator = intersections.iterator();
349 while (iterator.hasNext())
350 {
351 if (iterator.next().getFraction1() >= fraction1)
352 {
353 iterator.remove();
354 }
355 }
356 if (Double.isNaN(fraction1))
357 {
358 simulator.getLogger().always().info("Fixing fractions of merge conflict{}", paddedConflictId);
359 fraction1 = 0;
360 fraction2 = 0;
361 }
362
363 buildMergeConflict(lane1, fraction1, lane2, fraction2, simulator, widthGenerator, permitted);
364
365 merge = true;
366 }
367 }
368 }
369
370
371 iterator1 = up1.iterator();
372 iterator2 = up2.iterator();
373 boolean split = false;
374 while (iterator1.hasNext() && !split)
375 {
376 Lane prev1 = iterator1.next();
377 while (iterator2.hasNext() && !split)
378 {
379 Lane prev2 = iterator2.next();
380 if (prev1.equals(prev2))
381 {
382
383 double fraction1 = Double.NaN;
384 double fraction2 = Double.NaN;
385 for (Intersection intersection : intersections)
386 {
387
388 if (intersection.getCombo() == 1 || intersection.getCombo() == 2)
389 {
390 fraction1 = intersection.getFraction1();
391 fraction2 = intersection.getFraction2();
392 break;
393 }
394 }
395
396 Iterator<Intersection> iterator = intersections.iterator();
397 while (iterator.hasNext())
398 {
399 if (iterator.next().getFraction1() <= fraction1)
400 {
401 iterator.remove();
402 }
403 else
404 {
405
406 break;
407 }
408 }
409 if (Double.isNaN(fraction1))
410 {
411 simulator.getLogger().always().info("Fixing fractions of split conflict{}", paddedConflictId);
412 fraction1 = 1;
413 fraction2 = 1;
414 }
415
416 buildSplitConflict(lane1, fraction1, lane2, fraction2, simulator, widthGenerator);
417
418 split = true;
419 }
420 }
421 }
422
423
424 if (!lane1.getLink().equals(lane2.getLink()))
425 {
426 boolean[] crossed = new boolean[4];
427 Iterator<Intersection> iterator = intersections.iterator();
428 double f1Start = Double.NaN;
429 double f2Start = Double.NaN;
430 double f2End = Double.NaN;
431 while (iterator.hasNext())
432 {
433 Intersection intersection = iterator.next();
434
435 if (Double.isNaN(f1Start))
436 {
437 f1Start = intersection.getFraction1();
438 }
439 f2Start = Double.isNaN(f2Start) ? intersection.getFraction2() : Math.min(f2Start, intersection.getFraction2());
440 f2End = Double.isNaN(f2End) ? intersection.getFraction2() : Math.max(f2End, intersection.getFraction2());
441
442 crossed[intersection.getCombo()] = !crossed[intersection.getCombo()];
443
444 if ((crossed[0] && crossed[1] && crossed[2] && crossed[3])
445 || (!crossed[0] && !crossed[1] && !crossed[2] && !crossed[3]))
446 {
447 if (Double.isNaN(f1Start) || Double.isNaN(f2Start) || Double.isNaN(f2End))
448 {
449 simulator.getLogger().always().warn("NOT YET Fixing fractions of crossing conflict{}",
450 paddedConflictId);
451 }
452 buildCrossingConflict(lane1, f1Start, intersection.getFraction1(), lane2, f2Start, f2End, simulator,
453 widthGenerator, permitted);
454 f1Start = Double.NaN;
455 f2Start = Double.NaN;
456 f2End = Double.NaN;
457 }
458 }
459 }
460
461 }
462
463
464
465
466
467
468
469
470
471
472
473
474 @SuppressWarnings("checkstyle:parameternumber")
475 private static void buildMergeConflict(final Lane lane1, final double f1start, final Lane lane2, final double f2start,
476 final OtsSimulatorInterface simulator, final WidthGenerator widthGenerator, final boolean permitted)
477 throws NetworkException
478 {
479
480
481 double f1end = 1.0;
482 double f2end = 1.0;
483
484
485 Length longitudinalPosition1 = lane1.getLength().times(f1start);
486 Length longitudinalPosition2 = lane2.getLength().times(f2start);
487 Length length1 = lane1.getLength().times(Math.abs(f1end - f1start));
488 Length length2 = lane2.getLength().times(Math.abs(f2end - f2start));
489
490
491 Polygon2d geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
492 Polygon2d geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
493
494
495 ConflictRule conflictRule;
496 if (lane1.getLink().getPriority().isBusStop() || lane2.getLink().getPriority().isBusStop())
497 {
498 Throw.when(lane1.getLink().getPriority().isBusStop() && lane2.getLink().getPriority().isBusStop(),
499 IllegalArgumentException.class, "Merge conflict between two links with bus stop priority not supported.");
500
501 conflictRule = new BusStopConflictRule(simulator, DefaultsNl.BUS);
502 }
503 else
504 {
505 conflictRule = new DefaultConflictRule();
506 }
507
508
509 Conflict.generateConflictPair(ConflictType.MERGE, conflictRule, permitted, lane1, longitudinalPosition1, length1,
510 geometry1, lane2, longitudinalPosition2, length2, geometry2, simulator);
511
512 numberMergeConflicts.incrementAndGet();
513 }
514
515
516
517
518
519
520
521
522
523
524
525 @SuppressWarnings("checkstyle:parameternumber")
526 private static void buildSplitConflict(final Lane lane1, final double f1end, final Lane lane2, final double f2end,
527 final OtsSimulatorInterface simulator, final WidthGenerator widthGenerator) throws NetworkException
528 {
529
530
531 double f1start = 0.0;
532 double f2start = 0.0;
533
534
535 Length longitudinalPosition1 = lane1.getLength().times(f1start);
536 Length longitudinalPosition2 = lane2.getLength().times(f2start);
537 Length length1 = lane1.getLength().times(Math.abs(f1end - f1start));
538 Length length2 = lane2.getLength().times(Math.abs(f2end - f2start));
539
540
541 Polygon2d geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
542 Polygon2d geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
543
544
545 Conflict.generateConflictPair(ConflictType.SPLIT, new SplitConflictRule(), false, lane1, longitudinalPosition1, length1,
546 geometry1, lane2, longitudinalPosition2, length2, geometry2, simulator);
547
548 numberSplitConflicts.incrementAndGet();
549 }
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564 @SuppressWarnings("checkstyle:parameternumber")
565 private static void buildCrossingConflict(final Lane lane1, final double f1start, final double f1end, final Lane lane2,
566 final double f2start, final double f2end, final OtsSimulatorInterface simulator,
567 final WidthGenerator widthGenerator, final boolean permitted) throws NetworkException
568 {
569
570
571
572 double f1startDirected;
573 double f2startDirected;
574 if (f1end < f1start)
575 {
576 f1startDirected = f1end;
577 }
578 else
579 {
580 f1startDirected = f1start;
581 }
582 if (f2end < f2start)
583 {
584 f2startDirected = f2end;
585 }
586 else
587 {
588 f2startDirected = f2start;
589 }
590
591
592 Length longitudinalPosition1 = lane1.getLength().times(f1startDirected);
593 Length longitudinalPosition2 = lane2.getLength().times(f2startDirected);
594 Length length1 = lane1.getLength().times(Math.abs(f1end - f1start));
595 Length length2 = lane2.getLength().times(Math.abs(f2end - f2start));
596
597
598 Polygon2d geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
599 Polygon2d geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
600
601
602 ConflictRule conflictRule;
603 if (lane1.getLink().getPriority().isBusStop() || lane2.getLink().getPriority().isBusStop())
604 {
605 Throw.when(lane1.getLink().getPriority().isBusStop() && lane2.getLink().getPriority().isBusStop(),
606 IllegalArgumentException.class, "Merge conflict between two links with bus stop priority not supported.");
607
608 conflictRule = new BusStopConflictRule(simulator, DefaultsNl.BUS);
609 }
610 else
611 {
612 conflictRule = new DefaultConflictRule();
613 }
614
615
616 Conflict.generateConflictPair(ConflictType.CROSSING, conflictRule, permitted, lane1, longitudinalPosition1, length1,
617 geometry1, lane2, longitudinalPosition2, length2, geometry2, simulator);
618
619 numberCrossConflicts.incrementAndGet();
620 }
621
622
623
624
625
626
627
628
629
630 private static Polygon2d getGeometry(final Lane lane, final double fStart, final double fEnd,
631 final WidthGenerator widthGenerator)
632 {
633
634 double f1;
635 double f2;
636 if (fEnd > fStart)
637 {
638 f1 = fStart;
639 f2 = fEnd;
640 }
641 else
642 {
643 f1 = fEnd;
644 f2 = fStart;
645 }
646 if (Math.abs(f1 - f2) < 1E-8)
647 {
648 lane.getLink().getSimulator().getLogger().always()
649 .debug("f1 (" + f1 + ") equals f2 (" + f2 + "); problematic lane is " + lane.toString());
650
651 if (f1 > 0)
652 {
653 f1 = f1 - f1 / 1000;
654 }
655 else
656 {
657 f2 = f2 + f2 / 1000;
658 }
659 }
660 OtsLine2d centerLine = lane.getCenterLine().extractFractional(f1, f2);
661 OtsLine2d left = centerLine.offsetLine(widthGenerator.getWidth(lane, f1) / 2, widthGenerator.getWidth(lane, f2) / 2);
662 OtsLine2d right =
663 centerLine.offsetLine(-widthGenerator.getWidth(lane, f1) / 2, -widthGenerator.getWidth(lane, f2) / 2).reverse();
664 List<Point2d> points = new ArrayList<>(left.size() + right.size());
665 points.addAll(left.getPointList());
666 points.addAll(right.getPointList());
667 return new Polygon2d(points);
668 }
669
670
671
672
673
674
675
676
677
678
679
680
681
682 private static class Intersection implements Comparable<Intersection>
683 {
684
685
686 private final double fraction1;
687
688
689 private final double fraction2;
690
691
692 private final int combo;
693
694
695
696
697
698
699 Intersection(final double fraction1, final double fraction2, final int combo)
700 {
701 this.fraction1 = fraction1;
702 this.fraction2 = fraction2;
703 this.combo = combo;
704 }
705
706
707
708
709 public final double getFraction1()
710 {
711 return this.fraction1;
712 }
713
714
715
716
717 public final double getFraction2()
718 {
719 return this.fraction2;
720 }
721
722
723
724
725 public final int getCombo()
726 {
727 return this.combo;
728 }
729
730 @Override
731 public int compareTo(final Intersection o)
732 {
733 int out = Double.compare(this.fraction1, o.fraction1);
734 if (out != 0)
735 {
736 return out;
737 }
738 out = Double.compare(this.fraction2, o.fraction2);
739 if (out != 0)
740 {
741 return out;
742 }
743 return Integer.compare(this.combo, o.combo);
744 }
745
746 @Override
747 public int hashCode()
748 {
749 final int prime = 31;
750 int result = 1;
751 result = prime * result + this.combo;
752 long temp;
753 temp = Double.doubleToLongBits(this.fraction1);
754 result = prime * result + (int) (temp ^ (temp >>> 32));
755 temp = Double.doubleToLongBits(this.fraction2);
756 result = prime * result + (int) (temp ^ (temp >>> 32));
757 return result;
758 }
759
760 @Override
761 public boolean equals(final Object obj)
762 {
763 if (this == obj)
764 {
765 return true;
766 }
767 if (obj == null)
768 {
769 return false;
770 }
771 if (getClass() != obj.getClass())
772 {
773 return false;
774 }
775 Intersection other = (Intersection) obj;
776 if (this.combo != other.combo)
777 {
778 return false;
779 }
780 if (Double.doubleToLongBits(this.fraction1) != Double.doubleToLongBits(other.fraction1))
781 {
782 return false;
783 }
784 if (Double.doubleToLongBits(this.fraction2) != Double.doubleToLongBits(other.fraction2))
785 {
786 return false;
787 }
788 return true;
789 }
790
791
792
793
794
795
796
797
798 public static SortedSet<Intersection> getIntersectionList(final OtsLine2d line1, final OtsLine2d line2, final int combo)
799 {
800 SortedSet<Intersection> out = new TreeSet<>();
801 double cumul1 = 0.0;
802 Point2d start1 = null;
803 Point2d end1 = line1.get(0);
804 for (int i = 0; i < line1.size() - 1; i++)
805 {
806 start1 = end1;
807 end1 = line1.get(i + 1);
808
809 double cumul2 = 0.0;
810 Point2d start2 = null;
811 Point2d end2 = line2.get(0);
812
813 for (int j = 0; j < line2.size() - 1; j++)
814 {
815 start2 = end2;
816 end2 = line2.get(j + 1);
817
818 Point2d p = Point2d.intersectionOfLineSegments(start1, end1, start2, end2);
819 if (p != null)
820 {
821
822 double dx = p.x - start1.x;
823 double dy = p.y - start1.y;
824 double length1 = cumul1 + Math.hypot(dx, dy);
825 dx = p.x - start2.x;
826 dy = p.y - start2.y;
827 double length2 = cumul2 + Math.hypot(dx, dy);
828 out.add(new Intersection(length1 / line1.getLength(), length2 / line2.getLength(), combo));
829 }
830
831 double dx = end2.x - start2.x;
832 double dy = end2.y - start2.y;
833 cumul2 += Math.hypot(dx, dy);
834 }
835
836 double dx = end1.x - start1.x;
837 double dy = end1.y - start1.y;
838 cumul1 += Math.hypot(dx, dy);
839 }
840
841 return out;
842 }
843
844 @Override
845 public String toString()
846 {
847 return "Intersection [fraction1=" + this.fraction1 + ", fraction2=" + this.fraction2 + ", combo=" + this.combo
848 + "]";
849 }
850
851 }
852
853
854
855
856
857
858
859
860
861
862
863
864 public interface WidthGenerator
865 {
866
867
868
869
870
871
872
873 double getWidth(Lane lane, double fraction);
874
875 }
876
877
878
879
880
881
882
883
884
885
886
887
888 public static class FixedWidthGenerator implements WidthGenerator
889 {
890
891
892 private final double width;
893
894
895
896
897
898 public FixedWidthGenerator(final Length width)
899 {
900 this.width = width.si;
901 }
902
903 @Override
904 public final double getWidth(final Lane lane, final double fraction)
905 {
906 return this.width;
907 }
908
909 @Override
910 public final String toString()
911 {
912 return "FixedWidthGenerator [width=" + this.width + "]";
913 }
914
915 }
916
917
918
919
920
921
922
923
924
925
926
927
928 public static class RelativeWidthGenerator implements WidthGenerator
929 {
930
931
932 private final double factor;
933
934
935
936
937
938 public RelativeWidthGenerator(final double factor)
939 {
940 this.factor = factor;
941 }
942
943 @Override
944 public final double getWidth(final Lane lane, final double fraction)
945 {
946 return lane.getWidth(fraction).si * this.factor;
947 }
948
949 @Override
950 public final String toString()
951 {
952 return "RelativeWidthGenerator [factor=" + this.factor + "]";
953 }
954
955 }
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971 public static void buildConflictsParallel(final RoadNetwork network, final OtsSimulatorInterface simulator,
972 final WidthGenerator widthGenerator)
973 {
974 buildConflictsParallel(network, simulator, widthGenerator, new LaneCombinationList(), new LaneCombinationList());
975 }
976
977
978
979
980
981
982
983
984
985 public static void buildConflictsParallel(final RoadNetwork network, final OtsSimulatorInterface simulator,
986 final WidthGenerator widthGenerator, final LaneCombinationList ignoreList, final LaneCombinationList permittedList)
987 {
988 buildConflictsParallelBig(getLanes(network), simulator, widthGenerator, ignoreList, permittedList);
989 }
990
991
992
993
994
995
996
997 public static void buildConflictsParallel(final List<Lane> lanes, final OtsSimulatorInterface simulator,
998 final WidthGenerator widthGenerator)
999 {
1000 buildConflictsParallelBig(lanes, simulator, widthGenerator, new LaneCombinationList(), new LaneCombinationList());
1001 }
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011 public static void buildConflictsParallelSmall(final List<Lane> lanes, final OtsSimulatorInterface simulator,
1012 final WidthGenerator widthGenerator, final LaneCombinationList ignoreList, final LaneCombinationList permittedList)
1013 {
1014 long totalCombinations = ((long) lanes.size()) * ((long) lanes.size() - 1) / 2;
1015 System.out.println("PARALLEL GENERATING OF CONFLICTS (SMALL JOBS). " + totalCombinations + " COMBINATIONS");
1016 long lastReported = 0;
1017 Map<Lane, OtsLine2d> leftEdges = new LinkedHashMap<>();
1018 Map<Lane, OtsLine2d> rightEdges = new LinkedHashMap<>();
1019
1020
1021 int cores = Runtime.getRuntime().availableProcessors();
1022 System.out.println("USING " + cores + " CORES");
1023 ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(cores);
1024 AtomicInteger numberOfJobs = new AtomicInteger(0);
1025 final int maxqueue = 2 * cores;
1026
1027 for (int i = 0; i < lanes.size(); i++)
1028 {
1029 long combinationsDone = totalCombinations - ((long) (lanes.size() - i)) * ((long) (lanes.size() - i - 1)) / 2;
1030 if (combinationsDone / 100000000 > lastReported)
1031 {
1032 simulator.getLogger().always()
1033 .debug(String.format(
1034 "generating conflicts at %.0f%% (generated %d merge conflicts, %d split "
1035 + "conflicts, %d crossing conflicts)",
1036 100.0 * combinationsDone / totalCombinations, numberMergeConflicts.get(),
1037 numberSplitConflicts.get(), numberCrossConflicts.get()));
1038 lastReported = combinationsDone / 100000000;
1039 }
1040 Lane lane1 = lanes.get(i);
1041 Set<Lane> down1 = lane1.nextLanes(null);
1042 Set<Lane> up1 = lane1.prevLanes(null);
1043
1044 for (int j = i + 1; j < lanes.size(); j++)
1045 {
1046 Lane lane2 = lanes.get(j);
1047 if (ignoreList.contains(lane1, lane2))
1048 {
1049 continue;
1050 }
1051
1052 try
1053 {
1054 if (!lane1.getContour().intersects(lane2.getContour()))
1055 {
1056 continue;
1057 }
1058 }
1059 catch (Exception e)
1060 {
1061 System.err.println("Contour problem - lane1 = [" + lane1.getFullId() + "], lane2 = [" + lane2.getFullId()
1062 + "]; skipped");
1063 continue;
1064 }
1065
1066 boolean permitted = permittedList.contains(lane1, lane2);
1067
1068 while (numberOfJobs.get() > maxqueue)
1069 {
1070 try
1071 {
1072 Thread.sleep(1);
1073 }
1074 catch (InterruptedException exception)
1075 {
1076
1077 }
1078 }
1079 numberOfJobs.incrementAndGet();
1080 Set<Lane> down2 = lane2.nextLanes(null);
1081 Set<Lane> up2 = lane2.prevLanes(null);
1082 ConflictBuilderRecordSmall cbr = new ConflictBuilderRecordSmall(lane1, down1, up1, lane2, down2, up2, permitted,
1083 simulator, widthGenerator, leftEdges, rightEdges);
1084 executor.execute(new CbrTaskSmall(numberOfJobs, cbr));
1085 }
1086 }
1087
1088 long time = System.currentTimeMillis();
1089
1090 while (numberOfJobs.get() > 0 && System.currentTimeMillis() - time < 60000)
1091 {
1092 try
1093 {
1094 Thread.sleep(10);
1095 }
1096 catch (InterruptedException exception)
1097 {
1098
1099 }
1100 }
1101
1102 executor.shutdown();
1103 while (!executor.isTerminated())
1104 {
1105 try
1106 {
1107 Thread.sleep(1);
1108 }
1109 catch (InterruptedException exception)
1110 {
1111
1112 }
1113 }
1114
1115 simulator.getLogger().always()
1116 .debug(String.format(
1117 "generating conflicts complete (generated %d merge conflicts, %d split "
1118 + "conflicts, %d crossing conflicts)",
1119 numberMergeConflicts.get(), numberSplitConflicts.get(), numberCrossConflicts.get()));
1120 }
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130 public static void buildConflictsParallelBig(final List<Lane> lanes, final OtsSimulatorInterface simulator,
1131 final WidthGenerator widthGenerator, final LaneCombinationList ignoreList, final LaneCombinationList permittedList)
1132 {
1133 long totalCombinations = ((long) lanes.size()) * ((long) lanes.size() - 1) / 2;
1134 System.out.println("PARALLEL GENERATING OF CONFLICTS (BIG JOBS). " + totalCombinations + " COMBINATIONS");
1135 long lastReported = 0;
1136 Map<Lane, OtsLine2d> leftEdges = new LinkedHashMap<>();
1137 Map<Lane, OtsLine2d> rightEdges = new LinkedHashMap<>();
1138
1139
1140 int cores = Runtime.getRuntime().availableProcessors();
1141 System.out.println("USING " + cores + " CORES");
1142 ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(cores);
1143 AtomicInteger numberOfJobs = new AtomicInteger(0);
1144 final int maxqueue = 200;
1145
1146 for (int i = 0; i < lanes.size(); i++)
1147 {
1148 long combinationsDone = totalCombinations - ((long) (lanes.size() - i)) * ((long) (lanes.size() - i - 1)) / 2;
1149 if (combinationsDone / 100000000 > lastReported)
1150 {
1151 simulator.getLogger().always()
1152 .debug(String.format(
1153 "generating conflicts at %.0f%% (generated %d merge conflicts, %d split "
1154 + "conflicts, %d crossing conflicts)",
1155 100.0 * combinationsDone / totalCombinations, numberMergeConflicts.get(),
1156 numberSplitConflicts.get(), numberCrossConflicts.get()));
1157 lastReported = combinationsDone / 100000000;
1158 }
1159
1160 while (numberOfJobs.get() > maxqueue)
1161 {
1162 try
1163 {
1164 Thread.sleep(0, 10);
1165 }
1166 catch (InterruptedException exception)
1167 {
1168
1169 }
1170 }
1171 numberOfJobs.incrementAndGet();
1172
1173 ConflictBuilderRecordBig cbr = new ConflictBuilderRecordBig(i, lanes, ignoreList, permittedList, simulator,
1174 widthGenerator, leftEdges, rightEdges);
1175 executor.execute(new CbrTaskBig(numberOfJobs, cbr));
1176
1177 }
1178
1179 long time = System.currentTimeMillis();
1180
1181 while (numberOfJobs.get() > 0 && System.currentTimeMillis() - time < 60000)
1182 {
1183 try
1184 {
1185 Thread.sleep(10);
1186 }
1187 catch (InterruptedException exception)
1188 {
1189
1190 }
1191 }
1192
1193 executor.shutdown();
1194 while (!executor.isTerminated())
1195 {
1196 try
1197 {
1198 Thread.sleep(1);
1199 }
1200 catch (InterruptedException exception)
1201 {
1202
1203 }
1204 }
1205
1206 simulator.getLogger().always()
1207 .debug(String.format(
1208 "generating conflicts complete (generated %d merge conflicts, %d split "
1209 + "conflicts, %d crossing conflicts)",
1210 numberMergeConflicts.get(), numberSplitConflicts.get(), numberCrossConflicts.get()));
1211 }
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221 public static void buildConflictsParallel(final RoadNetwork network, final Map<String, Set<Link>> conflictCandidateMap,
1222 final OtsSimulatorInterface simulator, final WidthGenerator widthGenerator)
1223 {
1224 for (String conflictId : conflictCandidateMap.keySet())
1225 {
1226
1227 List<Lane> lanes = new ArrayList<>();
1228 for (Link link : conflictCandidateMap.get(conflictId))
1229 {
1230 if (link instanceof CrossSectionLink)
1231 {
1232 for (CrossSectionElement element : ((CrossSectionLink) link).getCrossSectionElementList())
1233 {
1234 if (element instanceof Lane lane && !(element instanceof Shoulder))
1235 {
1236 lanes.add((Lane) element);
1237 }
1238 }
1239 }
1240 }
1241
1242
1243 buildConflicts(lanes, simulator, widthGenerator, new LaneCombinationList(), new LaneCombinationList(), conflictId);
1244 simulator.getLogger().setAllLogLevel(Level.DEBUG);
1245 }
1246 }
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259 static class CbrTaskSmall implements Runnable
1260 {
1261
1262 final ConflictBuilderRecordSmall cbr;
1263
1264
1265 final AtomicInteger nrOfJobs;
1266
1267
1268
1269
1270
1271
1272 CbrTaskSmall(final AtomicInteger nrOfJobs, final ConflictBuilderRecordSmall cbr)
1273 {
1274 this.nrOfJobs = nrOfJobs;
1275 this.cbr = cbr;
1276 }
1277
1278 @Override
1279 public void run()
1280 {
1281 try
1282 {
1283 buildConflicts(this.cbr.lane1, this.cbr.down1, this.cbr.up1, this.cbr.lane2, this.cbr.down2, this.cbr.up2,
1284 this.cbr.permitted, this.cbr.simulator, this.cbr.widthGenerator, this.cbr.leftEdges,
1285 this.cbr.rightEdges, false, null);
1286 }
1287 catch (NetworkException ne)
1288 {
1289 throw new RuntimeException("Conflict build with bad combination of types / rules.", ne);
1290 }
1291 this.nrOfJobs.decrementAndGet();
1292 }
1293 }
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317 @SuppressWarnings("checkstyle:visibilitymodifier")
1318 static record ConflictBuilderRecordSmall(Lane lane1, Set<Lane> down1, Set<Lane> up1, Lane lane2, Set<Lane> down2,
1319 Set<Lane> up2, boolean permitted, OtsSimulatorInterface simulator, WidthGenerator widthGenerator,
1320 Map<Lane, OtsLine2d> leftEdges, Map<Lane, OtsLine2d> rightEdges)
1321 {
1322 }
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336 static class CbrTaskBig implements Runnable
1337 {
1338
1339 final ConflictBuilderRecordBig cbr;
1340
1341
1342 final AtomicInteger nrOfJobs;
1343
1344
1345
1346
1347
1348
1349 CbrTaskBig(final AtomicInteger nrOfJobs, final ConflictBuilderRecordBig cbr)
1350 {
1351 this.nrOfJobs = nrOfJobs;
1352 this.cbr = cbr;
1353 }
1354
1355 @Override
1356 public void run()
1357 {
1358 try
1359 {
1360 Lane lane1 = this.cbr.lanes.get(this.cbr.starti);
1361 Set<Lane> up1 = lane1.prevLanes(null);
1362 Set<Lane> down1 = lane1.nextLanes(null);
1363 for (int j = this.cbr.starti + 1; j < this.cbr.lanes.size(); j++)
1364 {
1365 Lane lane2 = this.cbr.lanes.get(j);
1366 if (this.cbr.ignoreList.contains(lane1, lane2))
1367 {
1368 continue;
1369 }
1370
1371 try
1372 {
1373 if (!lane1.getContour().intersects(lane2.getContour()))
1374 {
1375 continue;
1376 }
1377 }
1378 catch (Exception e)
1379 {
1380 System.err.println("Contour problem - lane1 = [" + lane1.getFullId() + "], lane2 = ["
1381 + lane2.getFullId() + "]; skipped");
1382 continue;
1383 }
1384
1385 boolean permitted = this.cbr.permittedList.contains(lane1, lane2);
1386
1387 Set<Lane> down2 = lane2.nextLanes(null);
1388 Set<Lane> up2 = lane2.prevLanes(null);
1389
1390 try
1391 {
1392 buildConflicts(lane1, down1, up1, lane2, down2, up2, permitted, this.cbr.simulator,
1393 this.cbr.widthGenerator, this.cbr.leftEdges, this.cbr.rightEdges, false, null);
1394 }
1395 catch (NetworkException ne)
1396 {
1397 lane2.getLink().getSimulator().getLogger().always().error(ne,
1398 "Conflict build with bad combination of types / rules.");
1399 }
1400 }
1401
1402 }
1403 catch (Exception e)
1404 {
1405 e.printStackTrace();
1406 }
1407 this.nrOfJobs.decrementAndGet();
1408 }
1409 }
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431 @SuppressWarnings("checkstyle:visibilitymodifier")
1432 static record ConflictBuilderRecordBig(int starti, List<Lane> lanes, LaneCombinationList ignoreList,
1433 LaneCombinationList permittedList, OtsSimulatorInterface simulator, WidthGenerator widthGenerator,
1434 Map<Lane, OtsLine2d> leftEdges, Map<Lane, OtsLine2d> rightEdges)
1435 {
1436 }
1437
1438 }