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