View Javadoc
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   * Utility to create vehicle generators on a network from an OD.
62   * <p>
63   * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
64   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
65   * <p>
66   * @version $Revision$, $LastChangedDate$, by $Author$, initial version 30 nov. 2017 <br>
67   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
68   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
69   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
70   */
71  public final class ODApplier
72  {
73  
74      /**
75       * Utility class.
76       */
77      private ODApplier()
78      {
79          //
80      }
81  
82      /**
83       * Applies the OD to the network by creating vehicle generators. The map returned contains objects created for vehicle
84       * generation. These are bundled in a {@code GeneratorObjects} and mapped to the vehicle generator id. Vehicle generator id
85       * is equal to the origin node id. For lane-based generators the id's are appended with an ordered number (e.g. A1), where
86       * the ordering is first by link id, and then right to left concerning the lateral lane position at the start of the lane.
87       * For node "A" this would for example be:<br>
88       * <table summary="">
89       * <tr>
90       * <th>Generator id</th>
91       * <th>Link</th>
92       * <th>Lateral start offset</th>
93       * </tr>
94       * <tr>
95       * <th>A1</th>
96       * <th>AB</th>
97       * <th>-1.75m</th>
98       * </tr>
99       * <tr>
100      * <th>A2</th>
101      * <th>AB</th>
102      * <th>1.75m</th>
103      * </tr>
104      * <tr>
105      * <th>A3</th>
106      * <th>AC</th>
107      * <th>-3.5m</th>
108      * </tr>
109      * <tr>
110      * <th>A4</th>
111      * <th>AC</th>
112      * <th>0.0m</th>
113      * </tr>
114      * </table>
115      * @param network OTSNetwork; network
116      * @param od ODMatrix; OD matrix
117      * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; simulator
118      * @param odOptions ODOptions; options for vehicle generation
119      * @return Map&lt;String, GeneratorObjects&gt; map of generator id's and created generator objects mainly for testing
120      * @throws ParameterException if a parameter is missing
121      * @throws SimRuntimeException if this method is called after simulation time 0
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         // TODO sinks? white extension links?
134         final Categorization categorization = od.getCategorization();
135         final boolean laneBased = categorization.entails(Lane.class);
136         boolean markovian = od.getCategorization().entails(GTUType.class);
137 
138         // TODO clean up stream acquiring code after task OTS-315 has been completed
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             // Step 1: create DemandNode trees, starting with a root for each vehicle generator
159             DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>> rootNode = null; // root node for each generator
160             /**
161              * Depending on whether the categorization is lane based or not, we either have 1 root per origin, or we have 1 root
162              * per lane (i.e. 1 generator at an origin putting traffic on multiple lanes, or N generators per origin, each
163              * generating traffic on 1 lane). In order to know to which root node the sub nodes belong in a loop, we store root
164              * nodes by lane. Effectively, the map functions as an artificial branching of demand before the origin node, only
165              * used if the categorization contains lanes. For non-lane based demand, the root node and destination node created
166              * in the outer loop can simply be used.
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                             // obtain or create root and destination nodes
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); // 1 for each generator
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             // Step 2: gather DirectedLanePositions for each generator pertaining to each DemandNode<...>
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                             // count number of served links
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                                         // store weight under connected link, as this
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             // Step 3: create generator(s)
307             initialPositions = sortByValue(initialPositions); // sorts by lateral position at link start
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                 // id
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                 // functional generation elements
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                 // and finally, the generator
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                     // should not happen, we check that time is 0
365                     throw new RuntimeException(exception);
366                 }
367                 catch (ProbabilityException exception)
368                 {
369                     // should not happen, as we define probabilities in the headwayGenerator
370                     throw new RuntimeException(exception);
371                 }
372                 catch (NamingException | RemoteException exception)
373                 {
374                     // should not happen
375                     throw new RuntimeException(exception);
376                 }
377             }
378         }
379         return output;
380     }
381 
382     /**
383      * Returns the common ancestor {@code LinkType} of all links connected to the node, moving through connectors.
384      * @param node Node; origin node
385      * @return common ancestor {@code LinkType} of all links connected to the node, moving through connectors
386      */
387     private static LinkType getLinkTypeFromNode(final Node node)
388     {
389         return getLinkTypeFromNode0(node, false);
390     }
391 
392     /**
393      * Returns the common ancestor {@code LinkType} of all links connected to the node, moving through connectors.
394      * @param node Node; origin node
395      * @param ignoreConnectors boolean; ignore connectors
396      * @return common ancestor {@code LinkType} of all links connected to the node, moving through connectors
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                         // incompatible link types
421                         return null;
422                     }
423                 }
424             }
425         }
426         return linkType;
427     }
428 
429     /**
430      * Returns a sorted map.
431      * @param map Map; input map
432      * @param <K> key type (implemented for cleaner code only)
433      * @param <V> value type (implemented for cleaner code only)
434      * @return Map; sorted map
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      * Adds {@code DirectedLanePosition}s to the input set, for {@code Lane}s on the given link, starting at the given
463      * {@code Node}.
464      * @param link CrossSectionLink; link with lanes to add positions for
465      * @param node Node; node on the side where positions should be placed
466      * @param positionSet Set&lt;DirectedLanePosition&gt;; set to add position to
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      * Node for demand tree. Based on two constructors there are 2 types of nodes:<br>
488      * <ul>
489      * <li>Branch nodes; with an object and a stream for randomly drawing a child node.</li>
490      * <li>Leaf nodes; with an object and demand data (time, frequency, interpolation).</li>
491      * </ul>
492      * To accomplish a branching of Node (origin) &gt; Node (destination) &gt; Category, the following generics types can be
493      * used:<br>
494      * <br>
495      * {@code DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>>}
496      * <p>
497      * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
498      * <br>
499      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
500      * <p>
501      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 1 dec. 2017 <br>
502      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
503      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
504      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
505      * @param <T> type of contained object
506      * @param <K> type of child nodes
507      */
508     private static class DemandNode<T, K extends DemandNode<?, ?>> implements Arrivals
509     {
510 
511         /** Node object. */
512         private final T object;
513 
514         /** Random stream to draw child node. */
515         private final StreamInterface stream;
516 
517         /** Children. */
518         private final List<K> children = new ArrayList<>();
519 
520         /** Demand data. */
521         private final DemandPattern demandPattern;
522 
523         /** Unique GTU types of leaf nodes. */
524         private final List<GTUType> gtuTypes = new ArrayList<>();
525 
526         /** Number of leaf nodes for the unique GTU types. */
527         private final List<Integer> gtuTypeCounts = new ArrayList<>();
528 
529         /** GTU type of leaf nodes. */
530         private final Map<K, GTUType> gtuTypesPerChild = new LinkedHashMap<>();
531 
532         /** Markov chain for GTU type selection. */
533         private final MarkovChain markov;
534 
535         /**
536          * Constructor for branching node, with Markov selection.
537          * @param object T; node object
538          * @param stream StreamInterface; random stream to draw child node
539          * @param markov MarkovChain; Markov chain
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          * Constructor for leaf node, without Markov selection.
551          * @param object T; node object
552          * @param demandPattern DemandPattern; demand data
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          * Adds child to a branching node.
564          * @param child K; child node
565          */
566         public void addChild(final K child)
567         {
568             this.children.add(child);
569         }
570 
571         /**
572          * Adds child to a branching node.
573          * @param child K; child node
574          * @param gtuType GTUType; gtu type for Markov chain
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          * Randomly draws a child node.
596          * @param time Duration; simulation time
597          * @return K; randomly drawn child node
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                 // regular draw, loop children and collect their frequencies
606                 for (K child : this.children)
607                 {
608                     double f = child.getFrequency(time, true).si; // sliceStart = true is arbitrary
609                     weightMap.put(child, f);
610                 }
611             }
612             else
613             {
614                 // markov chain draw, the markov chain only selects a GTU type, not a child node
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<>(); // stored, saves us from calculating them twice
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); // sliceStart = true is arbitrary
625                     frequencies.put(child, f);
626                     steadyState[index] = steadyState[index].plus(f);
627                 }
628                 GTUType nextGtuType = this.markov.draw(gtuTypeArray, steadyState, this.stream);
629                 // select only child nodes registered to the next GTU type
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          * Returns the node object.
644          * @return T; node object
645          */
646         public T getObject()
647         {
648             return this.object;
649         }
650 
651         /**
652          * Returns the child that pertains to specified object or {@code null} if no such child is present.
653          * @param obj Object; child object
654          * @return child that pertains to specified object or {@code null} if no such child is present
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         /** {@inheritDoc} */
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         /** {@inheritDoc} */
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         /** {@inheritDoc} */
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      * Wrapper class around a {@code MarkovCorrelation}, including the last type. One of these should be used for each vehicle
714      * generator.
715      */
716     private static class MarkovChain
717     {
718         /** Markov correlation for GTU type selection. */
719         private final MarkovCorrelation<GTUType, Frequency> markov;
720 
721         /** Previously returned GTU type. */
722         private GTUType previousGtuType = null;
723 
724         /**
725          * Constructor.
726          * @param markov MarkovCorrelation&lt;GTUType, Frequency&gt;; Markov correlation for GTU type selection
727          */
728         MarkovChain(final MarkovCorrelation<GTUType, Frequency> markov)
729         {
730             this.markov = markov;
731         }
732 
733         /**
734          * Returns a next GTU type drawn using a Markov chain.
735          * @param gtuTypes GTUType[]; GTUTypes to consider
736          * @param intensities Frequency[]; frequency for each GTU type, i.e. the steady-state
737          * @param stream StreamInterface; stream for random numbers
738          * @return next GTU type drawn using a Markov chain
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      * Characteristics generation based on OD demand.
749      * <p>
750      * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
751      * <br>
752      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
753      * <p>
754      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 7 dec. 2017 <br>
755      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
756      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
757      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
758      */
759     private static class GTUCharacteristicsGeneratorODWrapper implements LaneBasedGTUCharacteristicsGenerator
760     {
761 
762         /** Root node with origin. */
763         private final DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>> root;
764 
765         /** Simulator. */
766         private final DEVSSimulatorInterface.TimeDoubleUnit simulator;
767 
768         /** Characteristics generator based on OD information. */
769         private final GTUCharacteristicsGeneratorOD charachteristicsGenerator;
770 
771         /** Stream for random numbers. */
772         private final StreamInterface randomStream;
773 
774         /**
775          * @param root DemandNode&lt;Node, DemandNode&lt;Node, DemandNode&lt;Category, ?&gt;&gt;&gt;; root node with origin
776          * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; simulator
777          * @param charachteristicsGenerator GTUCharacteristicsGeneratorOD; characteristics generator based on OD information
778          * @param randomStream StreamInterface; stream for random numbers
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         /** {@inheritDoc} */
791         @Override
792         public LaneBasedGTUCharacteristics draw() throws ProbabilityException, ParameterException, GTUException
793         {
794             // obtain node objects
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             // forward to lower-level generator
801             return this.charachteristicsGenerator.draw(origin, destination, category, this.randomStream);
802         }
803 
804         /** {@inheritDoc} */
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      * Class to contain created generator objects.
817      * <p>
818      * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
819      * <br>
820      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
821      * <p>
822      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 12 dec. 2017 <br>
823      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
824      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
825      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
826      */
827     public static class GeneratorObjects
828     {
829 
830         /** Main generator for GTU's. */
831         private final LaneBasedGTUGenerator generator;
832 
833         /** Generator of headways. */
834         private final Generator<Duration> headwayGenerator;
835 
836         /** Generator of GTU characteristics. */
837         private final LaneBasedGTUCharacteristicsGenerator charachteristicsGenerator;
838 
839         /**
840          * @param generator LaneBasedGTUGenerator; main generator for GTU's
841          * @param headwayGenerator Generator&lt;Duration&gt;; generator of headways
842          * @param charachteristicsGenerator LaneBasedGTUCharacteristicsGenerator; generator of GTU characteristics
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          * Returns the main generator for GTU's.
854          * @return LaneBasedGTUGenerator; main generator for GTU's
855          */
856         public LaneBasedGTUGenerator getGenerator()
857         {
858             return this.generator;
859         }
860 
861         /**
862          * Returns the generator of headways.
863          * @return Generator&lt;Duration&gt; generator of headways
864          */
865         public Generator<Duration> getHeadwayGenerator()
866         {
867             return this.headwayGenerator;
868         }
869 
870         /**
871          * Returns the generator of GTU characteristics.
872          * @return LaneBasedGTUCharacteristicsGenerator; generator of GTU characteristics
873          */
874         public LaneBasedGTUCharacteristicsGenerator getCharachteristicsGenerator()
875         {
876             return this.charachteristicsGenerator;
877         }
878 
879     }
880 
881 }