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.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.Sensor;
52  import org.opentrafficsim.road.network.lane.object.sensor.SinkSensor;
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  
59  /**
60   * Utility to create vehicle generators on a network from an OD.
61   * <p>
62   * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
63   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
64   * <p>
65   * @version $Revision$, $LastChangedDate$, by $Author$, initial version 30 nov. 2017 <br>
66   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
67   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
68   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
69   */
70  public final class ODApplier
71  {
72  
73      /**
74       * Utility class.
75       */
76      private ODApplier()
77      {
78          //
79      }
80  
81      /**
82       * Applies the OD to the network by creating vehicle generators. The map returned contains objects created for vehicle
83       * generation. These are bundled in a {@code GeneratorObjects} and mapped to the vehicle generator id. Vehicle generator id
84       * is equal to the origin node id. For lane-based generators the id's are appended with an ordered number (e.g. A1), where
85       * the ordering is first by link id, and then right to left concerning the lateral lane position at the start of the lane.
86       * For node "A" this would for example be:<br>
87       * <table ><caption>&nbsp;</caption>
88       * <tr>
89       * <th>Generator id</th>
90       * <th>Link</th>
91       * <th>Lateral start offset</th>
92       * </tr>
93       * <tr>
94       * <th>A1</th>
95       * <th>AB</th>
96       * <th>-1.75m</th>
97       * </tr>
98       * <tr>
99       * <th>A2</th>
100      * <th>AB</th>
101      * <th>1.75m</th>
102      * </tr>
103      * <tr>
104      * <th>A3</th>
105      * <th>AC</th>
106      * <th>-3.5m</th>
107      * </tr>
108      * <tr>
109      * <th>A4</th>
110      * <th>AC</th>
111      * <th>0.0m</th>
112      * </tr>
113      * </table>
114      * @param network OTSRoadNetwork; network
115      * @param od ODMatrix; OD matrix
116      * @param simulator OTSSimulatorInterface; simulator
117      * @param odOptions ODOptions; options for vehicle generation
118      * @return Map&lt;String, GeneratorObjects&gt; map of generator id's and created generator objects mainly for testing
119      * @throws ParameterException if a parameter is missing
120      * @throws SimRuntimeException if this method is called after simulation time 0
121      */
122     @SuppressWarnings("checkstyle:methodlength")
123     public static Map<String, GeneratorObjects> applyOD(final OTSRoadNetwork network, final ODMatrix od,
124             final OTSSimulatorInterface 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         for (Node destination : od.getDestinations())
135         {
136             createSinksAtDestination(destination, simulator);
137         }
138 
139         final Categorization categorization = od.getCategorization();
140         final boolean laneBased = categorization.entails(Lane.class);
141         boolean markovian = od.getCategorization().entails(GTUType.class);
142 
143         // TODO clean up stream acquiring code after task OTS-315 has been completed
144         StreamInterface stream = simulator.getReplication().getStream("generation");
145         if (stream == null)
146         {
147             stream = simulator.getReplication().getStream("default");
148             if (stream == null)
149             {
150                 System.out
151                         .println("Using locally created stream (not from the simulator) for vehicle generation, with seed 1.");
152                 stream = new MersenneTwister(1L);
153             }
154             else
155             {
156                 System.out.println("Using stream 'default' for vehicle generation.");
157             }
158         }
159 
160         Map<String, GeneratorObjects> output = new LinkedHashMap<>();
161         for (Node origin : od.getOrigins())
162         {
163             // Step 1: create DemandNode trees, starting with a root for each vehicle generator
164             DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>> rootNode = null; // root node for each generator
165             /**
166              * Depending on whether the categorization is lane based or not, we either have 1 root per origin, or we have 1 root
167              * per lane (i.e. 1 generator at an origin putting traffic on multiple lanes, or N generators per origin, each
168              * generating traffic on 1 lane). In order to know to which root node the sub nodes belong in a loop, we store root
169              * nodes by lane. Effectively, the map functions as an artificial branching of demand before the origin node, only
170              * used if the categorization contains lanes. For non-lane based demand, the root node and destination node created
171              * in the outer loop can simply be used.
172              */
173             Map<Lane, DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>>> originNodePerLane = new LinkedHashMap<>();
174             MarkovChain markovChain = null;
175             if (!laneBased)
176             {
177                 rootNode = new DemandNode<>(origin, stream, null);
178                 LinkType linkType = getLinkTypeFromNode(origin);
179                 if (markovian)
180                 {
181                     MarkovCorrelation<GTUType, Frequency> correlation = odOptions.get(ODOptions.MARKOV, null, origin, linkType);
182                     if (correlation != null)
183                     {
184                         Throw.when(!od.getCategorization().entails(GTUType.class), IllegalArgumentException.class,
185                                 "Markov correlation can only be used on OD categorization entailing GTU type.");
186                         markovChain = new MarkovChain(correlation);
187                     }
188                 }
189             }
190             for (Node destination : od.getDestinations())
191             {
192                 Set<Category> categories = od.getCategories(origin, destination);
193                 if (!categories.isEmpty())
194                 {
195                     DemandNode<Node, DemandNode<Category, ?>> destinationNode = null;
196                     if (!laneBased)
197                     {
198                         destinationNode = new DemandNode<>(destination, stream, markovChain);
199                         rootNode.addChild(destinationNode);
200                     }
201                     for (Category category : categories)
202                     {
203                         if (laneBased)
204                         {
205                             // obtain or create root and destination nodes
206                             Lane lane = category.get(Lane.class);
207                             rootNode = originNodePerLane.get(lane);
208                             if (rootNode == null)
209                             {
210                                 rootNode = new DemandNode<>(origin, stream, null);
211                                 originNodePerLane.put(lane, rootNode);
212                             }
213                             destinationNode = rootNode.getChild(destination);
214                             if (destinationNode == null)
215                             {
216                                 markovChain = null;
217                                 if (markovian)
218                                 {
219                                     MarkovCorrelation<GTUType, Frequency> correlation =
220                                             odOptions.get(ODOptions.MARKOV, lane, origin, lane.getParentLink().getLinkType());
221                                     if (correlation != null)
222                                     {
223                                         Throw.when(!od.getCategorization().entails(GTUType.class),
224                                                 IllegalArgumentException.class,
225                                                 "Markov correlation can only be used on OD categorization entailing GTU type.");
226                                         markovChain = new MarkovChain(correlation); // 1 for each generator
227                                     }
228                                 }
229                                 destinationNode = new DemandNode<>(destination, stream, markovChain);
230                                 rootNode.addChild(destinationNode);
231                             }
232                         }
233                         DemandNode<Category, ?> categoryNode =
234                                 new DemandNode<>(category, od.getDemandPattern(origin, destination, category));
235                         if (markovian)
236                         {
237                             destinationNode.addLeaf(categoryNode, category.get(GTUType.class));
238                         }
239                         else
240                         {
241                             destinationNode.addChild(categoryNode);
242                         }
243                     }
244                 }
245             }
246 
247             // Step 2: gather DirectedLanePositions for each generator pertaining to each DemandNode<...>
248             Map<DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>>, Set<DirectedLanePosition>> initialPositions =
249                     new LinkedHashMap<>();
250             Map<CrossSectionLink, Double> linkWeights = null;
251             if (laneBased)
252             {
253                 for (Lane lane : originNodePerLane.keySet())
254                 {
255                     DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>> demandNode = originNodePerLane.get(lane);
256                     Set<DirectedLanePosition> initialPosition = new LinkedHashSet<>();
257                     try
258                     {
259                         initialPosition.add(lane.getParentLink().getStartNode().equals(demandNode.getObject())
260                                 ? new DirectedLanePosition(lane, Length.ZERO, GTUDirectionality.DIR_PLUS)
261                                 : new DirectedLanePosition(lane, lane.getLength(), GTUDirectionality.DIR_MINUS));
262                     }
263                     catch (GTUException ge)
264                     {
265                         throw new RuntimeException(ge);
266                     }
267                     initialPositions.put(demandNode, initialPosition);
268                 }
269             }
270             else
271             {
272                 Set<DirectedLanePosition> positionSet = new LinkedHashSet<>();
273                 for (Link link : origin.getLinks())
274                 {
275                     if (link.getLinkType().isConnector())
276                     {
277                         if (link.getStartNode().equals(origin))
278                         {
279                             Node connectedNode = link.getEndNode();
280                             // count number of served links
281                             int served = 0;
282                             for (Link connectedLink : connectedNode.getLinks())
283                             {
284                                 if (connectedLink instanceof CrossSectionLink && !connectedLink.getLinkType().isConnector())
285                                 {
286                                     served++;
287                                 }
288                             }
289                             for (Link connectedLink : connectedNode.getLinks())
290                             {
291                                 if (connectedLink instanceof CrossSectionLink)
292                                 {
293                                     if (link instanceof CrossSectionLink./../org/opentrafficsim/road/network/lane/CrossSectionLink.html#CrossSectionLink">CrossSectionLink && ((CrossSectionLink) link).getDemandWeight() != null)
294                                     {
295                                         if (linkWeights == null)
296                                         {
297                                             linkWeights = new LinkedHashMap<>();
298                                         }
299                                         // store weight under connected link, as this
300                                         linkWeights.put(((CrossSectionLink) connectedLink),
301                                                 ((CrossSectionLink) link).getDemandWeight() / served);
302                                     }
303                                     setDirectedLanePosition((CrossSectionLink) connectedLink, connectedNode, positionSet);
304                                 }
305                             }
306                         }
307                     }
308                     else if (link instanceof CrossSectionLink)
309                     {
310                         setDirectedLanePosition((CrossSectionLink) link, origin, positionSet);
311                     }
312                 }
313                 initialPositions.put(rootNode, positionSet);
314             }
315 
316             // Step 3: create generator(s)
317             initialPositions = sortByValue(initialPositions); // sorts by lateral position at link start
318             Map<Node, Integer> originGeneratorCounts = new LinkedHashMap<>();
319             for (DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>> root : initialPositions.keySet())
320             {
321                 Set<DirectedLanePosition> initialPosition = initialPositions.get(root);
322                 // id
323                 Node o = root.getObject();
324                 String id = o.getId();
325                 if (laneBased)
326                 {
327                     Integer count = originGeneratorCounts.get(o);
328                     if (count == null)
329                     {
330                         count = 0;
331                     }
332                     count++;
333                     id += count;
334                     originGeneratorCounts.put(o, count);
335                 }
336                 // functional generation elements
337                 Lane lane;
338                 LinkType linkType;
339                 if (laneBased)
340                 {
341                     lane = initialPosition.iterator().next().getLane();
342                     linkType = lane.getParentLink().getLinkType();
343                 }
344                 else
345                 {
346                     lane = null;
347                     linkType = getLinkTypeFromNode(o);
348                 }
349                 HeadwayDistribution randomization = odOptions.get(ODOptions.HEADWAY_DIST, lane, o, linkType);
350                 ArrivalsHeadwayGenerator headwayGenerator =
351                         new ArrivalsHeadwayGenerator(root, simulator, stream, randomization);
352                 GTUCharacteristicsGeneratorODWrapper characteristicsGenerator = new GTUCharacteristicsGeneratorODWrapper(root,
353                         simulator, odOptions.get(ODOptions.GTU_TYPE, lane, o, linkType), stream);
354                 RoomChecker roomChecker = odOptions.get(ODOptions.ROOM_CHECKER, lane, o, linkType);
355                 IdGenerator idGenerator = odOptions.get(ODOptions.GTU_ID, lane, o, linkType);
356                 LaneBiases biases = odOptions.get(ODOptions.getLaneBiasOption(network), lane, o, linkType);
357                 // and finally, the generator
358                 try
359                 {
360                     LaneBasedGTUGeneratorneBasedGTUGenerator.html#LaneBasedGTUGenerator">LaneBasedGTUGenerator generator = new LaneBasedGTUGenerator(id, headwayGenerator, characteristicsGenerator,
361                             GeneratorPositions.create(initialPosition, stream, biases, linkWeights), network, simulator,
362                             roomChecker, idGenerator);
363                     generator.setNoLaneChangeDistance(odOptions.get(ODOptions.NO_LC_DIST, lane, o, linkType));
364                     generator.setInstantaneousLaneChange(odOptions.get(ODOptions.INSTANT_LC, lane, o, linkType));
365                     generator.setErrorHandler(odOptions.get(ODOptions.ERROR_HANDLER, lane, o, linkType));
366                     output.put(id, new GeneratorObjects(generator, headwayGenerator, characteristicsGenerator));
367                 }
368                 catch (SimRuntimeException exception)
369                 {
370                     // should not happen, we check that time is 0
371                     throw new RuntimeException(exception);
372                 }
373                 catch (ProbabilityException exception)
374                 {
375                     // should not happen, as we define probabilities in the headwayGenerator
376                     throw new RuntimeException(exception);
377                 }
378             }
379         }
380         return output;
381     }
382 
383     /**
384      * Create sinks at all lanes connected to a destination node. This method considers connectors too.
385      * @param destination Node; destination node
386      * @param simulator OTSSimulatorInterface; simulator
387      */
388     private static void createSinksAtDestination(final Node destination, final OTSSimulatorInterface simulator)
389     {
390         for (Link link : destination.getLinks())
391         {
392             if (link.getLinkType().isConnector() && !link.getStartNode().equals(destination))
393             {
394                 createSinksAtDestination(link.getStartNode(), simulator);
395             }
396             if (link instanceof CrossSectionLink)
397             {
398                 for (Lane lane : ((CrossSectionLink) link).getLanes())
399                 {
400                     try
401                     {
402                         // if the lane already contains a SinkSensor, skip creating a new one
403                         boolean sinkSensorExists = false;
404                         for (Sensor sensor : lane.getSensors())
405                         {
406                             if (sensor instanceof SinkSensor)
407                             {
408                                 sinkSensorExists = true;
409                             }
410                         }
411                         if (!sinkSensorExists)
412                         {
413                             if (link.getEndNode().equals(destination))
414                             {
415                                 new SinkSensor(lane, lane.getLength(), GTUDirectionality.DIR_PLUS, simulator);
416                             }
417                             else if (link.getStartNode().equals(destination))
418                             {
419                                 new SinkSensor(lane, Length.ZERO, GTUDirectionality.DIR_MINUS, simulator);
420                             }
421                         }
422                     }
423                     catch (NetworkException exception)
424                     {
425                         // can not happen, we use Length.ZERO and lane.getLength()
426                         throw new RuntimeException(exception);
427                     }
428                 }
429             }
430         }
431     }
432 
433     /**
434      * Returns the common ancestor {@code LinkType} of all links connected to the node, moving through connectors.
435      * @param node Node; origin node
436      * @return common ancestor {@code LinkType} of all links connected to the node, moving through connectors
437      */
438     private static LinkType getLinkTypeFromNode(final Node node)
439     {
440         return getLinkTypeFromNode0(node, false);
441     }
442 
443     /**
444      * Returns the common ancestor {@code LinkType} of all links connected to the node, moving through connectors.
445      * @param node Node; origin node
446      * @param ignoreConnectors boolean; ignore connectors
447      * @return common ancestor {@code LinkType} of all links connected to the node, moving through connectors
448      */
449     private static LinkType getLinkTypeFromNode0(final Node node, final boolean ignoreConnectors)
450     {
451         LinkType linkType = null;
452         for (Link link : node.getLinks())
453         {
454             LinkType next = link.getLinkType();
455             if (!ignoreConnectors && next.isConnector())
456             {
457                 Node otherNode = link.getStartNode().equals(node) ? link.getEndNode() : link.getStartNode();
458                 next = getLinkTypeFromNode0(otherNode, true);
459             }
460             if (next != null && !next.isConnector())
461             {
462                 if (linkType == null)
463                 {
464                     linkType = next;
465                 }
466                 else
467                 {
468                     linkType = linkType.commonAncestor(next);
469                     if (linkType == null)
470                     {
471                         // incompatible link types
472                         return null;
473                     }
474                 }
475             }
476         }
477         return linkType;
478     }
479 
480     /**
481      * Returns a sorted map.
482      * @param map Map&lt;K, V&gt;; input map
483      * @param <K> key type (implemented for cleaner code only)
484      * @param <V> value type (implemented for cleaner code only)
485      * @return Map; sorted map
486      */
487     private static <K, V extends Set<DirectedLanePosition>> Map<K, V> sortByValue(final Map<K, V> map)
488     {
489         return map.entrySet().stream().sorted(new Comparator<Map.Entry<K, V>>()
490         {
491             @Override
492             public int compare(final Entry<K, V> o1, final Entry<K, V> o2)
493             {
494                 DirectedLanePosition lanePos1 = o1.getValue().iterator().next();
495                 String linkId1 = lanePos1.getLane().getParentLink().getId();
496                 DirectedLanePosition lanePos2 = o2.getValue().iterator().next();
497                 String linkId2 = lanePos2.getLane().getParentLink().getId();
498                 int c = linkId1.compareToIgnoreCase(linkId2);
499                 if (c == 0)
500                 {
501                     Length pos1 = lanePos1.getGtuDirection().isPlus() ? Length.ZERO : lanePos1.getLane().getLength();
502                     Length lat1 = lanePos1.getLane().getLateralCenterPosition(pos1);
503                     Length pos2 = lanePos2.getGtuDirection().isPlus() ? Length.ZERO : lanePos2.getLane().getLength();
504                     Length lat2 = lanePos2.getLane().getLateralCenterPosition(pos2);
505                     return lat1.compareTo(lat2);
506                 }
507                 return c;
508             }
509         }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
510     }
511 
512     /**
513      * Adds {@code DirectedLanePosition}s to the input set, for {@code Lane}s on the given link, starting at the given
514      * {@code Node}.
515      * @param link CrossSectionLink; link with lanes to add positions for
516      * @param node Node; node on the side where positions should be placed
517      * @param positionSet Set&lt;DirectedLanePosition&gt;; set to add position to
518      */
519     private static void setDirectedLanePosition(final CrossSectionLink link, final Node node,
520             final Set<DirectedLanePosition> positionSet)
521     {
522         for (Lane lane : link.getLanes())
523         {
524             try
525             {
526                 positionSet.add(lane.getParentLink().getStartNode().equals(node)
527                         ? new DirectedLanePosition(lane, Length.ZERO, GTUDirectionality.DIR_PLUS)
528                         : new DirectedLanePosition(lane, lane.getLength(), GTUDirectionality.DIR_MINUS));
529             }
530             catch (GTUException ge)
531             {
532                 throw new RuntimeException(ge);
533             }
534         }
535     }
536 
537     /**
538      * Node for demand tree. Based on two constructors there are 2 types of nodes:<br>
539      * <ul>
540      * <li>Branch nodes; with an object and a stream for randomly drawing a child node.</li>
541      * <li>Leaf nodes; with an object and demand data (time, frequency, interpolation).</li>
542      * </ul>
543      * To accomplish a branching of Node (origin) &gt; Node (destination) &gt; Category, the following generics types can be
544      * used:<br>
545      * <br>
546      * {@code DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>>}
547      * <p>
548      * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
549      * <br>
550      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
551      * <p>
552      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 1 dec. 2017 <br>
553      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
554      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
555      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
556      * @param <T> type of contained object
557      * @param <K> type of child nodes
558      */
559     private static class DemandNode<T, K extends DemandNode<?, ?>> implements Arrivals
560     {
561 
562         /** Node object. */
563         private final T object;
564 
565         /** Random stream to draw child node. */
566         private final StreamInterface stream;
567 
568         /** Children. */
569         private final List<K> children = new ArrayList<>();
570 
571         /** Demand data. */
572         private final DemandPattern demandPattern;
573 
574         /** Unique GTU types of leaf nodes. */
575         private final List<GTUType> gtuTypes = new ArrayList<>();
576 
577         /** Number of leaf nodes for the unique GTU types. */
578         private final List<Integer> gtuTypeCounts = new ArrayList<>();
579 
580         /** GTU type of leaf nodes. */
581         private final Map<K, GTUType> gtuTypesPerChild = new LinkedHashMap<>();
582 
583         /** Markov chain for GTU type selection. */
584         private final MarkovChain markov;
585 
586         /**
587          * Constructor for branching node, with Markov selection.
588          * @param object T; node object
589          * @param stream StreamInterface; random stream to draw child node
590          * @param markov MarkovChain; Markov chain
591          */
592         DemandNode(final T object, final StreamInterface stream, final MarkovChain markov)
593         {
594             this.object = object;
595             this.stream = stream;
596             this.demandPattern = null;
597             this.markov = markov;
598         }
599 
600         /**
601          * Constructor for leaf node, without Markov selection.
602          * @param object T; node object
603          * @param demandPattern DemandPattern; demand data
604          */
605         DemandNode(final T object, final DemandPattern demandPattern)
606         {
607             this.object = object;
608             this.stream = null;
609             this.demandPattern = demandPattern;
610             this.markov = null;
611         }
612 
613         /**
614          * Adds child to a branching node.
615          * @param child K; child node
616          */
617         public void addChild(final K child)
618         {
619             this.children.add(child);
620         }
621 
622         /**
623          * Adds child to a branching node.
624          * @param child K; child node
625          * @param gtuType GTUType; gtu type for Markov chain
626          */
627         public void addLeaf(final K child, final GTUType gtuType)
628         {
629             Throw.when(this.gtuTypes == null, IllegalStateException.class,
630                     "Adding leaf with GTUType in not possible on a non-Markov node.");
631             addChild(child);
632             this.gtuTypesPerChild.put(child, gtuType);
633             if (!this.gtuTypes.contains(gtuType))
634             {
635                 this.gtuTypes.add(gtuType);
636                 this.gtuTypeCounts.add(1);
637             }
638             else
639             {
640                 int index = this.gtuTypes.indexOf(gtuType);
641                 this.gtuTypeCounts.set(index, this.gtuTypeCounts.get(index) + 1);
642             }
643         }
644 
645         /**
646          * Randomly draws a child node.
647          * @param time Time; simulation time
648          * @return K; randomly drawn child node
649          */
650         public K draw(final Time time)
651         {
652             Throw.when(this.children.isEmpty(), RuntimeException.class, "Calling draw on a leaf node in the demand tree.");
653             Map<K, Double> weightMap = new LinkedHashMap<>();
654             if (this.markov == null)
655             {
656                 // regular draw, loop children and collect their frequencies
657                 for (K child : this.children)
658                 {
659                     double f = child.getFrequency(time, true).si; // sliceStart = true is arbitrary
660                     weightMap.put(child, f);
661                 }
662             }
663             else
664             {
665                 // markov chain draw, the markov chain only selects a GTU type, not a child node
666                 GTUType[] gtuTypeArray = new GTUType[this.gtuTypes.size()];
667                 gtuTypeArray = this.gtuTypes.toArray(gtuTypeArray);
668                 Frequency[] steadyState = new Frequency[this.gtuTypes.size()];
669                 Arrays.fill(steadyState, Frequency.ZERO);
670                 Map<K, Frequency> frequencies = new LinkedHashMap<>(); // stored, saves us from calculating them twice
671                 for (K child : this.children)
672                 {
673                     GTUType gtuType = this.gtuTypesPerChild.get(child);
674                     int index = this.gtuTypes.indexOf(gtuType);
675                     Frequency f = child.getFrequency(time, true); // sliceStart = true is arbitrary
676                     frequencies.put(child, f);
677                     steadyState[index] = steadyState[index].plus(f);
678                 }
679                 GTUType nextGtuType = this.markov.draw(gtuTypeArray, steadyState, this.stream);
680                 // select only child nodes registered to the next GTU type
681                 for (K child : this.children)
682                 {
683                     if (this.gtuTypesPerChild.get(child).equals(nextGtuType))
684                     {
685                         double f = frequencies.get(child).si;
686                         weightMap.put(child, f);
687                     }
688                 }
689             }
690             return Draw.drawWeighted(weightMap, this.stream);
691         }
692 
693         /**
694          * Returns the node object.
695          * @return T; node object
696          */
697         public T getObject()
698         {
699             return this.object;
700         }
701 
702         /**
703          * Returns the child that pertains to specified object or {@code null} if no such child is present.
704          * @param obj Object; child object
705          * @return child that pertains to specified object or {@code null} if no such child is present
706          */
707         public K getChild(final Object obj)
708         {
709             for (K child : this.children)
710             {
711                 if (child.getObject().equals(obj))
712                 {
713                     return child;
714                 }
715             }
716             return null;
717         }
718 
719         /** {@inheritDoc} */
720         @Override
721         public Frequency getFrequency(final Time time, final boolean sliceStart)
722         {
723             if (this.demandPattern != null)
724             {
725                 return this.demandPattern.getFrequency(time, sliceStart);
726             }
727             Frequency f = new Frequency(0.0, FrequencyUnit.PER_HOUR);
728             for (K child : this.children)
729             {
730                 f = f.plus(child.getFrequency(time, sliceStart));
731             }
732             return f;
733         }
734 
735         /** {@inheritDoc} */
736         @Override
737         public Time nextTimeSlice(final Time time)
738         {
739             if (this.demandPattern != null)
740             {
741                 return this.demandPattern.nextTimeSlice(time);
742             }
743             Time out = null;
744             for (K child : this.children)
745             {
746                 Time childSlice = child.nextTimeSlice(time);
747                 out = out == null || (childSlice != null && childSlice.lt(out)) ? childSlice : out;
748             }
749             return out;
750         }
751 
752         /** {@inheritDoc} */
753         @Override
754         public String toString()
755         {
756             return "DemandNode [object=" + this.object + ", stream=" + this.stream + ", children=" + this.children
757                     + ", demandPattern=" + this.demandPattern + ", gtuTypes=" + this.gtuTypes + ", gtuTypeCounts="
758                     + this.gtuTypeCounts + ", gtuTypesPerChild=" + this.gtuTypesPerChild + ", markov=" + this.markov + "]";
759         }
760 
761     }
762 
763     /**
764      * Wrapper class around a {@code MarkovCorrelation}, including the last type. One of these should be used for each vehicle
765      * generator.
766      */
767     private static class MarkovChain
768     {
769         /** Markov correlation for GTU type selection. */
770         private final MarkovCorrelation<GTUType, Frequency> markov;
771 
772         /** Previously returned GTU type. */
773         private GTUType previousGtuType = null;
774 
775         /**
776          * Constructor.
777          * @param markov MarkovCorrelation&lt;GTUType, Frequency&gt;; Markov correlation for GTU type selection
778          */
779         MarkovChain(final MarkovCorrelation<GTUType, Frequency> markov)
780         {
781             this.markov = markov;
782         }
783 
784         /**
785          * Returns a next GTU type drawn using a Markov chain.
786          * @param gtuTypes GTUType[]; GTUTypes to consider
787          * @param intensities Frequency[]; frequency for each GTU type, i.e. the steady-state
788          * @param stream StreamInterface; stream for random numbers
789          * @return next GTU type drawn using a Markov chain
790          */
791         public GTUType draw(final GTUType[] gtuTypes, final Frequency[] intensities, final StreamInterface stream)
792         {
793             this.previousGtuType = this.markov.drawState(this.previousGtuType, gtuTypes, intensities, stream);
794             return this.previousGtuType;
795         }
796     }
797 
798     /**
799      * Characteristics generation based on OD demand.
800      * <p>
801      * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
802      * <br>
803      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
804      * <p>
805      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 7 dec. 2017 <br>
806      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
807      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
808      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
809      */
810     private static class GTUCharacteristicsGeneratorODWrapper implements LaneBasedGTUCharacteristicsGenerator
811     {
812 
813         /** Root node with origin. */
814         private final DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>> root;
815 
816         /** Simulator. */
817         private final DEVSSimulatorInterface.TimeDoubleUnit simulator;
818 
819         /** Characteristics generator based on OD information. */
820         private final GTUCharacteristicsGeneratorOD charachteristicsGenerator;
821 
822         /** Stream for random numbers. */
823         private final StreamInterface randomStream;
824 
825         /**
826          * @param root DemandNode&lt;Node, DemandNode&lt;Node, DemandNode&lt;Category, ?&gt;&gt;&gt;; root node with origin
827          * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; simulator
828          * @param charachteristicsGenerator GTUCharacteristicsGeneratorOD; characteristics generator based on OD information
829          * @param randomStream StreamInterface; stream for random numbers
830          */
831         GTUCharacteristicsGeneratorODWrapper(final DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>> root,
832                 final DEVSSimulatorInterface.TimeDoubleUnit simulator,
833                 final GTUCharacteristicsGeneratorOD charachteristicsGenerator, final StreamInterface randomStream)
834         {
835             this.root = root;
836             this.simulator = simulator;
837             this.charachteristicsGenerator = charachteristicsGenerator;
838             this.randomStream = randomStream;
839         }
840 
841         /** {@inheritDoc} */
842         @Override
843         public LaneBasedGTUCharacteristics draw() throws ProbabilityException, ParameterException, GTUException
844         {
845             // obtain node objects
846             Time time = this.simulator.getSimulatorTime();
847             Node origin = this.root.getObject();
848             DemandNode<Node, DemandNode<Category, ?>> destinationNode = this.root.draw(time);
849             Node destination = destinationNode.getObject();
850             Category category = destinationNode.draw(time).getObject();
851             // forward to lower-level generator
852             return this.charachteristicsGenerator.draw(origin, destination, category, this.randomStream);
853         }
854 
855         /** {@inheritDoc} */
856         @Override
857         public String toString()
858         {
859             return "GTUCharacteristicsGeneratorODWrapper [root=" + this.root + ", simulator=" + this.simulator
860                     + ", charachteristicsGenerator=" + this.charachteristicsGenerator + ", randomStream=" + this.randomStream
861                     + "]";
862         }
863 
864     }
865 
866     /**
867      * Class to contain created generator objects.
868      * <p>
869      * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
870      * <br>
871      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
872      * <p>
873      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 12 dec. 2017 <br>
874      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
875      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
876      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
877      */
878     public static class GeneratorObjects
879     {
880 
881         /** Main generator for GTU's. */
882         private final LaneBasedGTUGenerator generator;
883 
884         /** Generator of headways. */
885         private final Generator<Duration> headwayGenerator;
886 
887         /** Generator of GTU characteristics. */
888         private final LaneBasedGTUCharacteristicsGenerator charachteristicsGenerator;
889 
890         /**
891          * @param generator LaneBasedGTUGenerator; main generator for GTU's
892          * @param headwayGenerator Generator&lt;Duration&gt;; generator of headways
893          * @param charachteristicsGenerator LaneBasedGTUCharacteristicsGenerator; generator of GTU characteristics
894          */
895         public GeneratorObjects(final LaneBasedGTUGenerator generator, final Generator<Duration> headwayGenerator,
896                 final LaneBasedGTUCharacteristicsGenerator charachteristicsGenerator)
897         {
898             this.generator = generator;
899             this.headwayGenerator = headwayGenerator;
900             this.charachteristicsGenerator = charachteristicsGenerator;
901         }
902 
903         /**
904          * Returns the main generator for GTU's.
905          * @return LaneBasedGTUGenerator; main generator for GTU's
906          */
907         public LaneBasedGTUGenerator getGenerator()
908         {
909             return this.generator;
910         }
911 
912         /**
913          * Returns the generator of headways.
914          * @return Generator&lt;Duration&gt; generator of headways
915          */
916         public Generator<Duration> getHeadwayGenerator()
917         {
918             return this.headwayGenerator;
919         }
920 
921         /**
922          * Returns the generator of GTU characteristics.
923          * @return LaneBasedGTUCharacteristicsGenerator; generator of GTU characteristics
924          */
925         public LaneBasedGTUCharacteristicsGenerator getCharachteristicsGenerator()
926         {
927             return this.charachteristicsGenerator;
928         }
929 
930     }
931 
932 }