1 package org.opentrafficsim.road.od;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.Comparator;
6 import java.util.LinkedHashMap;
7 import java.util.LinkedHashSet;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.Map.Entry;
11 import java.util.Optional;
12 import java.util.Set;
13 import java.util.function.Supplier;
14 import java.util.stream.Collectors;
15
16 import org.djunits.unit.FrequencyUnit;
17 import org.djunits.value.vdouble.scalar.Duration;
18 import org.djunits.value.vdouble.scalar.Frequency;
19 import org.djunits.value.vdouble.scalar.Length;
20 import org.djutils.exceptions.Throw;
21 import org.opentrafficsim.base.OtsRuntimeException;
22 import org.opentrafficsim.base.logger.Logger;
23 import org.opentrafficsim.base.parameters.ParameterException;
24 import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
25 import org.opentrafficsim.core.gtu.GtuException;
26 import org.opentrafficsim.core.gtu.GtuType;
27 import org.opentrafficsim.core.idgenerator.IdSupplier;
28 import org.opentrafficsim.core.math.Draw;
29 import org.opentrafficsim.core.network.Connector;
30 import org.opentrafficsim.core.network.Link;
31 import org.opentrafficsim.core.network.LinkType;
32 import org.opentrafficsim.core.network.NetworkException;
33 import org.opentrafficsim.core.network.Node;
34 import org.opentrafficsim.core.object.DetectorType;
35 import org.opentrafficsim.road.gtu.generator.GeneratorPositions;
36 import org.opentrafficsim.road.gtu.generator.GeneratorPositions.LaneBiases;
37 import org.opentrafficsim.road.gtu.generator.LaneBasedGtuGenerator;
38 import org.opentrafficsim.road.gtu.generator.LaneBasedGtuGenerator.RoomChecker;
39 import org.opentrafficsim.road.gtu.generator.MarkovCorrelation;
40 import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGtuCharacteristics;
41 import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGtuCharacteristicsGenerator;
42 import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGtuCharacteristicsGeneratorOd;
43 import org.opentrafficsim.road.gtu.generator.headway.Arrivals;
44 import org.opentrafficsim.road.gtu.generator.headway.ArrivalsHeadwayGenerator;
45 import org.opentrafficsim.road.gtu.generator.headway.ArrivalsHeadwayGenerator.HeadwayDistribution;
46 import org.opentrafficsim.road.gtu.generator.headway.DemandPattern;
47 import org.opentrafficsim.road.network.RoadNetwork;
48 import org.opentrafficsim.road.network.lane.CrossSectionLink;
49 import org.opentrafficsim.road.network.lane.Lane;
50 import org.opentrafficsim.road.network.lane.LanePosition;
51 import org.opentrafficsim.road.network.lane.object.detector.LaneDetector;
52 import org.opentrafficsim.road.network.lane.object.detector.SinkDetector;
53
54 import nl.tudelft.simulation.dsol.SimRuntimeException;
55 import nl.tudelft.simulation.jstats.streams.MersenneTwister;
56 import nl.tudelft.simulation.jstats.streams.StreamInterface;
57
58
59
60
61
62
63
64
65
66
67
68 public final class OdApplier
69 {
70
71
72
73
74 private OdApplier()
75 {
76
77 }
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127 @SuppressWarnings("checkstyle:methodlength")
128 public static Map<String, GeneratorObjects> applyOd(final RoadNetwork network, final OdMatrix od, final OdOptions odOptions,
129 final DetectorType detectorType) throws ParameterException, SimRuntimeException
130 {
131 Throw.whenNull(network, "Network may not be null.");
132 Throw.whenNull(od, "OD matrix may not be null.");
133 Throw.whenNull(odOptions, "OD options may not be null.");
134 OtsSimulatorInterface simulator = network.getSimulator();
135 Throw.when(!simulator.getSimulatorTime().eq0(), SimRuntimeException.class,
136 "Method OdApplier.applyOd() should be invoked at simulation time 0.");
137
138
139 for (Node destination : od.getDestinations())
140 {
141 createSensorsAtDestination(destination, detectorType);
142 }
143
144
145 StreamInterface stream = getStream(simulator);
146
147 boolean laneBased = od.getCategorization().entails(Lane.class);
148 Map<String, GeneratorObjects> output = new LinkedHashMap<>();
149 for (Node origin : od.getOrigins())
150 {
151 Map<Lane, DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>>> originNodePerLane = new LinkedHashMap<>();
152 DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>> originNodeZone =
153 buildDemandNodeTree(od, odOptions, stream, origin, originNodePerLane);
154 Map<DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>>, Set<LanePosition>> initialPositions =
155 new LinkedHashMap<>();
156 Map<CrossSectionLink, Double> linkWeights = new LinkedHashMap<>();
157 Map<CrossSectionLink, Node> viaNodes = new LinkedHashMap<>();
158 if (laneBased)
159 {
160 gatherPositionsLaneBased(originNodePerLane, initialPositions);
161 }
162 else
163 {
164 initialPositions.put(originNodeZone, gatherPositionsZone(origin, linkWeights, viaNodes));
165 }
166 if (linkWeights.isEmpty())
167 {
168 linkWeights = null;
169 viaNodes = null;
170 }
171 initialPositions = sortByValue(initialPositions);
172 createGenerators(network, odOptions, simulator, laneBased, stream, output, initialPositions, linkWeights, viaNodes);
173 }
174 return output;
175 }
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194 private static DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>> buildDemandNodeTree(final OdMatrix od,
195 final OdOptions odOptions, final StreamInterface stream, final Node origin,
196 final Map<Lane, DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>>> originNodePerLane)
197 {
198 boolean laneBased = od.getCategorization().entails(Lane.class);
199 boolean markovian = od.getCategorization().entails(GtuType.class);
200 DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>> demandNode = null;
201 MarkovChain markovChain = null;
202 if (!laneBased)
203 {
204 demandNode = new DemandNode<>(origin, stream, null);
205 LinkType linkType = getLinkTypeFromNode(origin);
206 if (markovian)
207 {
208 MarkovCorrelation<GtuType, Frequency> correlation = odOptions.get(OdOptions.MARKOV, null, origin, linkType);
209 if (correlation != null)
210 {
211 Throw.when(!od.getCategorization().entails(GtuType.class), IllegalArgumentException.class,
212 "Markov correlation can only be used on OD categorization entailing GTU type.");
213 markovChain = new MarkovChain(correlation);
214 }
215 }
216 }
217 for (Node destination : od.getDestinations())
218 {
219 Set<Category> categories = od.getCategories(origin, destination);
220 if (!categories.isEmpty())
221 {
222 DemandNode<Node, DemandNode<Category, ?>> destinationNode = null;
223 if (!laneBased)
224 {
225 destinationNode = new DemandNode<>(destination, stream, markovChain);
226 demandNode.addChild(destinationNode);
227 }
228 for (Category category : categories)
229 {
230 if (laneBased)
231 {
232
233 Lane lane = category.get(Lane.class);
234 demandNode = originNodePerLane.get(lane);
235 if (demandNode == null)
236 {
237 demandNode = new DemandNode<>(origin, stream, null);
238 originNodePerLane.put(lane, demandNode);
239 }
240 destinationNode = demandNode.getChild(destination).orElse(null);
241 if (destinationNode == null)
242 {
243 markovChain = null;
244 if (markovian)
245 {
246 MarkovCorrelation<GtuType, Frequency> correlation =
247 odOptions.get(OdOptions.MARKOV, lane, origin, lane.getLink().getType());
248 if (correlation != null)
249 {
250 Throw.when(!od.getCategorization().entails(GtuType.class), IllegalArgumentException.class,
251 "Markov correlation can only be used on OD categorization entailing GTU type.");
252 markovChain = new MarkovChain(correlation);
253 }
254 }
255 destinationNode = new DemandNode<>(destination, stream, markovChain);
256 demandNode.addChild(destinationNode);
257 }
258 }
259 Optional<DemandPattern> demandPattern = od.getDemandPattern(origin, destination, category);
260 if (demandPattern.isPresent())
261 {
262 DemandNode<Category, ?> categoryNode = new DemandNode<>(category, demandPattern.get());
263 if (markovian)
264 {
265 destinationNode.addLeaf(categoryNode, category.get(GtuType.class));
266 }
267 else
268 {
269 destinationNode.addChild(categoryNode);
270 }
271 }
272 }
273 }
274 }
275 return demandNode;
276 }
277
278
279
280
281
282
283
284
285 private static void gatherPositionsLaneBased(
286 final Map<Lane, DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>>> originNodePerLane,
287 final Map<DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>>, Set<LanePosition>> initialPositions)
288 {
289 for (Lane lane : originNodePerLane.keySet())
290 {
291 DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>> demandNode = originNodePerLane.get(lane);
292 Set<LanePosition> initialPosition = new LinkedHashSet<>();
293 initialPosition.add(lane.getLink().getStartNode().equals(demandNode.getObject())
294 ? new LanePosition(lane, Length.ZERO) : new LanePosition(lane, lane.getLength()));
295 initialPositions.put(demandNode, initialPosition);
296 }
297 }
298
299
300
301
302
303
304
305
306
307
308
309 private static Set<LanePosition> gatherPositionsZone(final Node origin, final Map<CrossSectionLink, Double> linkWeights,
310 final Map<CrossSectionLink, Node> viaNodes)
311 {
312 Set<LanePosition> positionSet = new LinkedHashSet<>();
313 for (Link link : origin.getLinks())
314 {
315 if (link instanceof Connector)
316 {
317 Connector connector = (Connector) link;
318 if (connector.getStartNode().equals(origin))
319 {
320 Node connectedNode = connector.getEndNode();
321
322 int served = 0;
323 for (Link connectedLink : connectedNode.getLinks())
324 {
325 if (connectedLink instanceof CrossSectionLink)
326 {
327 served++;
328 }
329 }
330 for (Link connectedLink : connectedNode.getLinks())
331 {
332 if (connectedLink instanceof CrossSectionLink)
333 {
334 if (connector.getDemandWeight() > 0.0)
335 {
336
337 linkWeights.put(((CrossSectionLink) connectedLink), connector.getDemandWeight() / served);
338 }
339 else
340 {
341
342 linkWeights.put(((CrossSectionLink) connectedLink), -1.0);
343 }
344 viaNodes.put((CrossSectionLink) connectedLink, connectedNode);
345 setLanePosition((CrossSectionLink) connectedLink, connectedNode, positionSet);
346 }
347 }
348 }
349 }
350 else if (link instanceof CrossSectionLink)
351 {
352 setLanePosition((CrossSectionLink) link, origin, positionSet);
353 }
354 }
355 return positionSet;
356 }
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376 @SuppressWarnings("checkstyle:parameternumber")
377 private static void createGenerators(final RoadNetwork network, final OdOptions odOptions,
378 final OtsSimulatorInterface simulator, final boolean laneBased, final StreamInterface stream,
379 final Map<String, GeneratorObjects> output,
380 final Map<DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>>, Set<LanePosition>> initialPositions,
381 final Map<CrossSectionLink, Double> linkWeights, final Map<CrossSectionLink, Node> viaNodes)
382 throws ParameterException
383 {
384 Map<Node, Integer> laneGeneratorCounterForUniqueId = new LinkedHashMap<>();
385 for (DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>> root : initialPositions.keySet())
386 {
387 Set<LanePosition> initialPosition = initialPositions.get(root);
388
389 Node o = root.getObject();
390 String id = o.getId();
391 if (laneBased)
392 {
393 Integer count = laneGeneratorCounterForUniqueId.get(o);
394 if (count == null)
395 {
396 count = 0;
397 }
398 count++;
399 id += count;
400 laneGeneratorCounterForUniqueId.put(o, count);
401 }
402
403 Lane lane;
404 LinkType linkType;
405 if (laneBased)
406 {
407 lane = initialPosition.iterator().next().lane();
408 linkType = lane.getLink().getType();
409 }
410 else
411 {
412 lane = null;
413 linkType = getLinkTypeFromNode(o);
414 }
415 HeadwayDistribution randomization = odOptions.get(OdOptions.HEADWAY_DIST, lane, o, linkType);
416 ArrivalsHeadwayGenerator headwayGenerator = new ArrivalsHeadwayGenerator(root, simulator, stream, randomization);
417 LaneBasedGtuCharacteristicsGeneratorOd characteristicsGeneratorOd =
418 odOptions.get(OdOptions.GTU_TYPE, lane, o, linkType);
419 LaneBasedGtuCharacteristicsGenerator characteristicsGenerator = new LaneBasedGtuCharacteristicsGenerator()
420 {
421 @Override
422 public LaneBasedGtuCharacteristics draw() throws ParameterException, GtuException
423 {
424 Duration time = simulator.getSimulatorTime();
425 Node origin = root.getObject();
426 DemandNode<Node, DemandNode<Category, ?>> destinationNode = root.draw(time);
427 Node destination = destinationNode.getObject();
428 Category category = destinationNode.draw(time).getObject();
429 return characteristicsGeneratorOd.draw(origin, destination, category, stream);
430 }
431 };
432
433 RoomChecker roomChecker = odOptions.get(OdOptions.ROOM_CHECKER, lane, o, linkType);
434 IdSupplier idGenerator = odOptions.get(OdOptions.GTU_ID, lane, o, linkType);
435 LaneBiases biases = odOptions.get(OdOptions.LANE_BIAS, lane, o, linkType);
436
437 try
438 {
439 LaneBasedGtuGenerator generator = new LaneBasedGtuGenerator(id, headwayGenerator, characteristicsGenerator,
440 GeneratorPositions.create(initialPosition, stream, biases, linkWeights, viaNodes), network, simulator,
441 roomChecker, idGenerator);
442 generator.setNoLaneChangeDistance(odOptions.get(OdOptions.NO_LC_DIST, lane, o, linkType));
443 generator.setBookkeeping(odOptions.get(OdOptions.BOOKKEEPING, lane, o, linkType));
444 generator.setErrorHandler(odOptions.get(OdOptions.ERROR_HANDLER, lane, o, linkType));
445 output.put(id, new GeneratorObjects(generator, headwayGenerator, characteristicsGenerator));
446 }
447 catch (SimRuntimeException exception)
448 {
449
450 Logger.ots().error(exception);
451 throw new OtsRuntimeException(exception);
452 }
453 catch (NetworkException exception)
454 {
455
456 Logger.ots().error(exception);
457 throw new OtsRuntimeException(exception);
458 }
459 }
460 }
461
462
463
464
465
466
467 private static StreamInterface getStream(final OtsSimulatorInterface simulator)
468 {
469 StreamInterface stream = simulator.getModel().getStream("generation");
470 if (stream == null)
471 {
472 stream = simulator.getModel().getStream("default");
473 if (stream == null)
474 {
475 Logger.ots()
476 .trace("Using locally created stream (not from the simulator) for vehicle generation, with seed 1.");
477 stream = new MersenneTwister(1L);
478 }
479 else
480 {
481 Logger.ots().trace("Using stream 'default' for vehicle generation.");
482 }
483 }
484 return stream;
485 }
486
487
488
489
490
491
492 private static void createSensorsAtDestination(final Node destination, final DetectorType detectorType)
493 {
494 for (Link link : destination.getLinks())
495 {
496 if (link.isConnector() && !link.getStartNode().equals(destination))
497 {
498 createSensorsAtDestinationNode(link.getStartNode(), detectorType);
499 }
500 else
501 {
502 createSensorsAtDestinationNode(destination, detectorType);
503 }
504 }
505 }
506
507
508
509
510
511
512 private static void createSensorsAtDestinationNode(final Node destination, final DetectorType detectorType)
513 {
514 for (Link link : destination.getLinks())
515 {
516 if (link instanceof CrossSectionLink)
517 {
518 for (Lane lane : ((CrossSectionLink) link).getLanes())
519 {
520 try
521 {
522
523 boolean destinationDetectorExists = false;
524 for (LaneDetector detector : lane.getDetectors())
525 {
526 if (detector instanceof SinkDetector)
527 {
528 destinationDetectorExists = true;
529 }
530 }
531 if (!destinationDetectorExists)
532 {
533 new SinkDetector(lane, lane.getLength(), detectorType, SinkDetector.DESTINATION);
534 }
535 }
536 catch (NetworkException exception)
537 {
538
539 Logger.ots().error(exception);
540 throw new OtsRuntimeException(exception);
541 }
542 }
543 }
544 }
545 }
546
547
548
549
550
551
552 private static LinkType getLinkTypeFromNode(final Node node)
553 {
554 return getLinkTypeFromNode0(node, false);
555 }
556
557
558
559
560
561
562
563 private static LinkType getLinkTypeFromNode0(final Node node, final boolean ignoreConnectors)
564 {
565 LinkType linkType = null;
566 for (Link link : node.getLinks())
567 {
568 LinkType next = link.getType();
569 if (!ignoreConnectors && link.isConnector())
570 {
571 Node otherNode = link.getStartNode().equals(node) ? link.getEndNode() : link.getStartNode();
572 next = getLinkTypeFromNode0(otherNode, true);
573 }
574 if (next != null && !link.isConnector())
575 {
576 if (linkType == null)
577 {
578 linkType = next;
579 }
580 else
581 {
582 linkType = linkType.commonAncestor(next).orElse(null);
583 if (linkType == null)
584 {
585
586 return null;
587 }
588 }
589 }
590 }
591 return linkType;
592 }
593
594
595
596
597
598
599
600
601 private static <K, V extends Set<LanePosition>> Map<K, V> sortByValue(final Map<K, V> map)
602 {
603 return map.entrySet().stream().sorted(new Comparator<Map.Entry<K, V>>()
604 {
605 @Override
606 public int compare(final Entry<K, V> o1, final Entry<K, V> o2)
607 {
608 LanePosition lanePos1 = o1.getValue().iterator().next();
609 String linkId1 = lanePos1.lane().getLink().getId();
610 LanePosition lanePos2 = o2.getValue().iterator().next();
611 String linkId2 = lanePos2.lane().getLink().getId();
612 int c = linkId1.compareToIgnoreCase(linkId2);
613 if (c == 0)
614 {
615 Length pos1 = Length.ZERO;
616 Length lat1 = lanePos1.lane().getLateralCenterPosition(pos1);
617 Length pos2 = Length.ZERO;
618 Length lat2 = lanePos2.lane().getLateralCenterPosition(pos2);
619 return lat1.compareTo(lat2);
620 }
621 return c;
622 }
623 }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
624 }
625
626
627
628
629
630
631
632 private static void setLanePosition(final CrossSectionLink link, final Node node, final Set<LanePosition> positionSet)
633 {
634 for (Lane lane : link.getLanes())
635 {
636
637 if (lane.getLink().getStartNode().equals(node))
638 {
639 positionSet.add(new LanePosition(lane, Length.ZERO));
640 }
641 }
642 }
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665 private static class DemandNode<T, K extends DemandNode<?, ?>> implements Arrivals
666 {
667
668
669 private final T object;
670
671
672 private final StreamInterface stream;
673
674
675 private final List<K> children = new ArrayList<>();
676
677
678 private final DemandPattern demandPattern;
679
680
681 private final List<GtuType> gtuTypes = new ArrayList<>();
682
683
684 private final List<Integer> gtuTypeCounts = new ArrayList<>();
685
686
687 private final Map<K, GtuType> gtuTypesPerChild = new LinkedHashMap<>();
688
689
690 private final MarkovChain markov;
691
692
693
694
695
696
697
698 DemandNode(final T object, final StreamInterface stream, final MarkovChain markov)
699 {
700 this.object = object;
701 this.stream = stream;
702 this.demandPattern = null;
703 this.markov = markov;
704 }
705
706
707
708
709
710
711 DemandNode(final T object, final DemandPattern demandPattern)
712 {
713 this.object = object;
714 this.stream = null;
715 this.demandPattern = demandPattern;
716 this.markov = null;
717 }
718
719
720
721
722
723 public void addChild(final K child)
724 {
725 this.children.add(child);
726 }
727
728
729
730
731
732
733 public void addLeaf(final K child, final GtuType gtuType)
734 {
735 Throw.when(this.gtuTypes == null, IllegalStateException.class,
736 "Adding leaf with GtuType in not possible on a non-Markov node.");
737 addChild(child);
738 this.gtuTypesPerChild.put(child, gtuType);
739 if (!this.gtuTypes.contains(gtuType))
740 {
741 this.gtuTypes.add(gtuType);
742 this.gtuTypeCounts.add(1);
743 }
744 else
745 {
746 int index = this.gtuTypes.indexOf(gtuType);
747 this.gtuTypeCounts.set(index, this.gtuTypeCounts.get(index) + 1);
748 }
749 }
750
751
752
753
754
755
756 public K draw(final Duration time)
757 {
758 Throw.when(this.children.isEmpty(), OtsRuntimeException.class, "Calling draw on a leaf node in the demand tree.");
759 Map<K, Double> weightMap = new LinkedHashMap<>();
760 if (this.markov == null)
761 {
762
763 for (K child : this.children)
764 {
765 double f = child.getFrequency(time, true).si;
766 weightMap.put(child, f);
767 }
768 }
769 else
770 {
771
772 GtuType[] gtuTypeArray = new GtuType[this.gtuTypes.size()];
773 gtuTypeArray = this.gtuTypes.toArray(gtuTypeArray);
774 Frequency[] steadyState = new Frequency[this.gtuTypes.size()];
775 Arrays.fill(steadyState, Frequency.ZERO);
776 Map<K, Frequency> frequencies = new LinkedHashMap<>();
777 for (K child : this.children)
778 {
779 GtuType gtuType = this.gtuTypesPerChild.get(child);
780 int index = this.gtuTypes.indexOf(gtuType);
781 Frequency f = child.getFrequency(time, true);
782 frequencies.put(child, f);
783 steadyState[index] = steadyState[index].plus(f);
784 }
785 GtuType nextGtuType = this.markov.draw(gtuTypeArray, steadyState, this.stream);
786
787 for (K child : this.children)
788 {
789 if (this.gtuTypesPerChild.get(child).equals(nextGtuType))
790 {
791 double f = frequencies.get(child).si;
792 weightMap.put(child, f);
793 }
794 }
795 }
796 return Draw.drawWeighted(weightMap, this.stream);
797 }
798
799
800
801
802
803 public T getObject()
804 {
805 return this.object;
806 }
807
808
809
810
811
812
813 public Optional<K> getChild(final Object obj)
814 {
815 for (K child : this.children)
816 {
817 if (child.getObject().equals(obj))
818 {
819 return Optional.of(child);
820 }
821 }
822 return Optional.empty();
823 }
824
825 @Override
826 public Frequency getFrequency(final Duration time, final boolean sliceStart)
827 {
828 if (this.demandPattern != null)
829 {
830 return this.demandPattern.getFrequency(time, sliceStart);
831 }
832 Frequency f = new Frequency(0.0, FrequencyUnit.PER_HOUR);
833 for (K child : this.children)
834 {
835 f = f.plus(child.getFrequency(time, sliceStart));
836 }
837 return f;
838 }
839
840 @Override
841 public Optional<Duration> nextTimeSlice(final Duration time)
842 {
843 if (this.demandPattern != null)
844 {
845 return this.demandPattern.nextTimeSlice(time);
846 }
847 Duration out = null;
848 for (K child : this.children)
849 {
850 Optional<Duration> childSlice = child.nextTimeSlice(time);
851 out = out == null || (childSlice.isEmpty() && childSlice.get().lt(out)) ? childSlice.orElse(null) : out;
852 }
853 return Optional.ofNullable(out);
854 }
855
856 @Override
857 public String toString()
858 {
859 return "DemandNode [object=" + this.object + ", stream=" + this.stream + ", children=" + this.children
860 + ", demandPattern=" + this.demandPattern + ", gtuTypes=" + this.gtuTypes + ", gtuTypeCounts="
861 + this.gtuTypeCounts + ", gtuTypesPerChild=" + this.gtuTypesPerChild + ", markov=" + this.markov + "]";
862 }
863
864 }
865
866
867
868
869
870 private static class MarkovChain
871 {
872
873 private final MarkovCorrelation<GtuType, Frequency> markov;
874
875
876 private GtuType previousGtuType = null;
877
878
879
880
881
882 MarkovChain(final MarkovCorrelation<GtuType, Frequency> markov)
883 {
884 this.markov = markov;
885 }
886
887
888
889
890
891
892
893
894 public GtuType draw(final GtuType[] gtuTypes, final Frequency[] intensities, final StreamInterface stream)
895 {
896 this.previousGtuType = this.markov.drawState(this.previousGtuType, gtuTypes, intensities, stream);
897 return this.previousGtuType;
898 }
899 }
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915 public record GeneratorObjects(LaneBasedGtuGenerator generator, Supplier<Duration> headwayGenerator,
916 LaneBasedGtuCharacteristicsGenerator characteristicsGenerator)
917 {
918 }
919
920 }