View Javadoc
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   * Utility to create vehicle generators on a network from an OD.
58   * <p>
59   * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
60   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
61   * <p>
62   * @version $Revision$, $LastChangedDate$, by $Author$, initial version 30 nov. 2017 <br>
63   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
64   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
65   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
66   */
67  public final class ODApplier
68  {
69  
70      /**
71       * Utility class.
72       */
73      private ODApplier()
74      {
75          //
76      }
77  
78      /**
79       * Applies the OD to the network by creating vehicle generators. The map returned contains objects created for vehicle
80       * generation. These are bundled in a {@code GeneratorObjects} and mapped to the vehicle generator id. Vehicle generator id
81       * is equal to the origin node id. For lane-based generators the id's are appended with an ordered number (e.g. A1), where
82       * the ordering is first by link id, and then right to left concerning the lateral lane position at the start of the lane.
83       * For node "A" this would for example be:<br>
84       * <table summary="">
85       * <tr>
86       * <th>Generator id</th>
87       * <th>Link</th>
88       * <th>Lateral start offset</th>
89       * </tr>
90       * <tr>
91       * <th>A1</th>
92       * <th>AB</th>
93       * <th>-1.75m</th>
94       * </tr>
95       * <tr>
96       * <th>A2</th>
97       * <th>AB</th>
98       * <th>1.75m</th>
99       * </tr>
100      * <tr>
101      * <th>A3</th>
102      * <th>AC</th>
103      * <th>-3.5m</th>
104      * </tr>
105      * <tr>
106      * <th>A4</th>
107      * <th>AC</th>
108      * <th>0.0m</th>
109      * </tr>
110      * </table>
111      * @param network OTSNetwork; network
112      * @param od ODMatrix; OD matrix
113      * @param simulator OTSSimulatorInterface; simulator
114      * @param odOptions ODOptions; options for vehicle generation
115      * @return Map&lt;String, GeneratorObjects&gt; map of generator id's and created generator objects mainly for testing
116      * @throws ParameterException if a parameter is missing
117      * @throws SimRuntimeException if this method is called after simulation time 0
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         // TODO sinks? white extension links?
130         final Categorization categorization = od.getCategorization();
131         final boolean laneBased = categorization.entails(Lane.class);
132         boolean markovian = od.getCategorization().entails(GTUType.class);
133 
134         // TODO clean up stream acquiring code after task OTS-315 has been completed
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             // Step 1: create DemandNode trees, starting with a root for each vehicle generator
155             DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>> rootNode = null; // root node for each generator
156             /**
157              * Depending on whether the categorization is lane based or not, we either have 1 root per origin, or we have 1 root
158              * per lane (i.e. 1 generator at an origin putting traffic on multiple lanes, or N generators per origin, each
159              * generating traffic on 1 lane). In order to know to which root node the sub nodes belong in a loop, we store root
160              * nodes by lane. Effectively, the map functions as an artificial branching of demand before the origin node, only
161              * used if the categorization contains lanes. For non-lane based demand, the root node and destination node created
162              * in the outer loop can simply be used.
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                             // obtain or create root and destination nodes
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); // 1 for each generator
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             // Step 2: gather DirectedLanePositions for each generator pertaining to each DemandNode<...>
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                             // count number of served links
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                                         // store weight under connected link, as this
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             // Step 3: create generator(s)
308             initialPositions = sortByValue(initialPositions); // sorts by lateral position at link start
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                 // id
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                 // functional generation elements
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                 // and finally, the generator
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                     // should not happen, we check that time is 0
360                     throw new RuntimeException(exception);
361                 }
362                 catch (ProbabilityException exception)
363                 {
364                     // should not happen, as we define probabilities in the headwayGenerator
365                     throw new RuntimeException(exception);
366                 }
367             }
368         }
369         return output;
370     }
371 
372     /**
373      * Returns the common ancestor {@code LinkType} of all links connected to the node, moving through connectors.
374      * @param node Node; origin node
375      * @return common ancestor {@code LinkType} of all links connected to the node, moving through connectors
376      */
377     private static LinkType getLinkTypeFromNode(final Node node)
378     {
379         return getLinkTypeFromNode0(node, false);
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      * @param ignoreConnectors boolean; ignore connectors
386      * @return common ancestor {@code LinkType} of all links connected to the node, moving through connectors
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                         // incompatible link types
411                         return null;
412                     }
413                 }
414             }
415         }
416         return linkType;
417     }
418 
419     /**
420      * Returns a sorted map.
421      * @param map Map&lt;K, V&gt;; input map
422      * @param <K> key type (implemented for cleaner code only)
423      * @param <V> value type (implemented for cleaner code only)
424      * @return Map; sorted map
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      * Adds {@code DirectedLanePosition}s to the input set, for {@code Lane}s on the given link, starting at the given
453      * {@code Node}.
454      * @param link CrossSectionLink; link with lanes to add positions for
455      * @param node Node; node on the side where positions should be placed
456      * @param positionSet Set&lt;DirectedLanePosition&gt;; set to add position to
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      * Node for demand tree. Based on two constructors there are 2 types of nodes:<br>
478      * <ul>
479      * <li>Branch nodes; with an object and a stream for randomly drawing a child node.</li>
480      * <li>Leaf nodes; with an object and demand data (time, frequency, interpolation).</li>
481      * </ul>
482      * To accomplish a branching of Node (origin) &gt; Node (destination) &gt; Category, the following generics types can be
483      * used:<br>
484      * <br>
485      * {@code DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>>}
486      * <p>
487      * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
488      * <br>
489      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
490      * <p>
491      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 1 dec. 2017 <br>
492      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
493      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
494      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
495      * @param <T> type of contained object
496      * @param <K> type of child nodes
497      */
498     private static class DemandNode<T, K extends DemandNode<?, ?>> implements Arrivals
499     {
500 
501         /** Node object. */
502         private final T object;
503 
504         /** Random stream to draw child node. */
505         private final StreamInterface stream;
506 
507         /** Children. */
508         private final List<K> children = new ArrayList<>();
509 
510         /** Demand data. */
511         private final DemandPattern demandPattern;
512 
513         /** Unique GTU types of leaf nodes. */
514         private final List<GTUType> gtuTypes = new ArrayList<>();
515 
516         /** Number of leaf nodes for the unique GTU types. */
517         private final List<Integer> gtuTypeCounts = new ArrayList<>();
518 
519         /** GTU type of leaf nodes. */
520         private final Map<K, GTUType> gtuTypesPerChild = new LinkedHashMap<>();
521 
522         /** Markov chain for GTU type selection. */
523         private final MarkovChain markov;
524 
525         /**
526          * Constructor for branching node, with Markov selection.
527          * @param object T; node object
528          * @param stream StreamInterface; random stream to draw child node
529          * @param markov MarkovChain; Markov chain
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          * Constructor for leaf node, without Markov selection.
541          * @param object T; node object
542          * @param demandPattern DemandPattern; demand data
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          * Adds child to a branching node.
554          * @param child K; child node
555          */
556         public void addChild(final K child)
557         {
558             this.children.add(child);
559         }
560 
561         /**
562          * Adds child to a branching node.
563          * @param child K; child node
564          * @param gtuType GTUType; gtu type for Markov chain
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          * Randomly draws a child node.
586          * @param time Time; simulation time
587          * @return K; randomly drawn child node
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                 // regular draw, loop children and collect their frequencies
596                 for (K child : this.children)
597                 {
598                     double f = child.getFrequency(time, true).si; // sliceStart = true is arbitrary
599                     weightMap.put(child, f);
600                 }
601             }
602             else
603             {
604                 // markov chain draw, the markov chain only selects a GTU type, not a child node
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<>(); // stored, saves us from calculating them twice
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); // sliceStart = true is arbitrary
615                     frequencies.put(child, f);
616                     steadyState[index] = steadyState[index].plus(f);
617                 }
618                 GTUType nextGtuType = this.markov.draw(gtuTypeArray, steadyState, this.stream);
619                 // select only child nodes registered to the next GTU type
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          * Returns the node object.
634          * @return T; node object
635          */
636         public T getObject()
637         {
638             return this.object;
639         }
640 
641         /**
642          * Returns the child that pertains to specified object or {@code null} if no such child is present.
643          * @param obj Object; child object
644          * @return child that pertains to specified object or {@code null} if no such child is present
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         /** {@inheritDoc} */
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         /** {@inheritDoc} */
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         /** {@inheritDoc} */
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      * Wrapper class around a {@code MarkovCorrelation}, including the last type. One of these should be used for each vehicle
704      * generator.
705      */
706     private static class MarkovChain
707     {
708         /** Markov correlation for GTU type selection. */
709         private final MarkovCorrelation<GTUType, Frequency> markov;
710 
711         /** Previously returned GTU type. */
712         private GTUType previousGtuType = null;
713 
714         /**
715          * Constructor.
716          * @param markov MarkovCorrelation&lt;GTUType, Frequency&gt;; Markov correlation for GTU type selection
717          */
718         MarkovChain(final MarkovCorrelation<GTUType, Frequency> markov)
719         {
720             this.markov = markov;
721         }
722 
723         /**
724          * Returns a next GTU type drawn using a Markov chain.
725          * @param gtuTypes GTUType[]; GTUTypes to consider
726          * @param intensities Frequency[]; frequency for each GTU type, i.e. the steady-state
727          * @param stream StreamInterface; stream for random numbers
728          * @return next GTU type drawn using a Markov chain
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      * Characteristics generation based on OD demand.
739      * <p>
740      * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
741      * <br>
742      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
743      * <p>
744      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 7 dec. 2017 <br>
745      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
746      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
747      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
748      */
749     private static class GTUCharacteristicsGeneratorODWrapper implements LaneBasedGTUCharacteristicsGenerator
750     {
751 
752         /** Root node with origin. */
753         private final DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>> root;
754 
755         /** Simulator. */
756         private final DEVSSimulatorInterface.TimeDoubleUnit simulator;
757 
758         /** Characteristics generator based on OD information. */
759         private final GTUCharacteristicsGeneratorOD charachteristicsGenerator;
760 
761         /** Stream for random numbers. */
762         private final StreamInterface randomStream;
763 
764         /**
765          * @param root DemandNode&lt;Node, DemandNode&lt;Node, DemandNode&lt;Category, ?&gt;&gt;&gt;; root node with origin
766          * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; simulator
767          * @param charachteristicsGenerator GTUCharacteristicsGeneratorOD; characteristics generator based on OD information
768          * @param randomStream StreamInterface; stream for random numbers
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         /** {@inheritDoc} */
781         @Override
782         public LaneBasedGTUCharacteristics draw() throws ProbabilityException, ParameterException, GTUException
783         {
784             // obtain node objects
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             // forward to lower-level generator
791             return this.charachteristicsGenerator.draw(origin, destination, category, this.randomStream);
792         }
793 
794         /** {@inheritDoc} */
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      * Class to contain created generator objects.
807      * <p>
808      * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
809      * <br>
810      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
811      * <p>
812      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 12 dec. 2017 <br>
813      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
814      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
815      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
816      */
817     public static class GeneratorObjects
818     {
819 
820         /** Main generator for GTU's. */
821         private final LaneBasedGTUGenerator generator;
822 
823         /** Generator of headways. */
824         private final Generator<Duration> headwayGenerator;
825 
826         /** Generator of GTU characteristics. */
827         private final LaneBasedGTUCharacteristicsGenerator charachteristicsGenerator;
828 
829         /**
830          * @param generator LaneBasedGTUGenerator; main generator for GTU's
831          * @param headwayGenerator Generator&lt;Duration&gt;; generator of headways
832          * @param charachteristicsGenerator LaneBasedGTUCharacteristicsGenerator; generator of GTU characteristics
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          * Returns the main generator for GTU's.
844          * @return LaneBasedGTUGenerator; main generator for GTU's
845          */
846         public LaneBasedGTUGenerator getGenerator()
847         {
848             return this.generator;
849         }
850 
851         /**
852          * Returns the generator of headways.
853          * @return Generator&lt;Duration&gt; generator of headways
854          */
855         public Generator<Duration> getHeadwayGenerator()
856         {
857             return this.headwayGenerator;
858         }
859 
860         /**
861          * Returns the generator of GTU characteristics.
862          * @return LaneBasedGTUCharacteristicsGenerator; generator of GTU characteristics
863          */
864         public LaneBasedGTUCharacteristicsGenerator getCharachteristicsGenerator()
865         {
866             return this.charachteristicsGenerator;
867         }
868 
869     }
870 
871 }