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