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