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