ODApplier.java

  1. package org.opentrafficsim.road.gtu.generator.od;

  2. import java.util.ArrayList;
  3. import java.util.Arrays;
  4. import java.util.Comparator;
  5. import java.util.LinkedHashMap;
  6. import java.util.LinkedHashSet;
  7. import java.util.List;
  8. import java.util.Map;
  9. import java.util.Map.Entry;
  10. import java.util.Set;
  11. import java.util.stream.Collectors;

  12. import org.djunits.unit.FrequencyUnit;
  13. import org.djunits.value.vdouble.scalar.Duration;
  14. import org.djunits.value.vdouble.scalar.Frequency;
  15. import org.djunits.value.vdouble.scalar.Length;
  16. import org.djunits.value.vdouble.scalar.Time;
  17. import org.djutils.exceptions.Throw;
  18. import org.opentrafficsim.base.parameters.ParameterException;
  19. import org.opentrafficsim.core.distributions.Generator;
  20. import org.opentrafficsim.core.distributions.ProbabilityException;
  21. import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
  22. import org.opentrafficsim.core.gtu.GTUDirectionality;
  23. import org.opentrafficsim.core.gtu.GTUException;
  24. import org.opentrafficsim.core.gtu.GTUType;
  25. import org.opentrafficsim.core.idgenerator.IdGenerator;
  26. import org.opentrafficsim.core.math.Draw;
  27. import org.opentrafficsim.core.network.Link;
  28. import org.opentrafficsim.core.network.LinkType;
  29. import org.opentrafficsim.core.network.NetworkException;
  30. import org.opentrafficsim.core.network.Node;
  31. import org.opentrafficsim.road.gtu.generator.GeneratorPositions;
  32. import org.opentrafficsim.road.gtu.generator.GeneratorPositions.LaneBiases;
  33. import org.opentrafficsim.road.gtu.generator.LaneBasedGTUGenerator;
  34. import org.opentrafficsim.road.gtu.generator.LaneBasedGTUGenerator.RoomChecker;
  35. import org.opentrafficsim.road.gtu.generator.MarkovCorrelation;
  36. import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGTUCharacteristics;
  37. import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGTUCharacteristicsGenerator;
  38. import org.opentrafficsim.road.gtu.generator.headway.Arrivals;
  39. import org.opentrafficsim.road.gtu.generator.headway.ArrivalsHeadwayGenerator;
  40. import org.opentrafficsim.road.gtu.generator.headway.ArrivalsHeadwayGenerator.HeadwayDistribution;
  41. import org.opentrafficsim.road.gtu.generator.headway.DemandPattern;
  42. import org.opentrafficsim.road.gtu.strategical.od.Categorization;
  43. import org.opentrafficsim.road.gtu.strategical.od.Category;
  44. import org.opentrafficsim.road.gtu.strategical.od.ODMatrix;
  45. import org.opentrafficsim.road.network.OTSRoadNetwork;
  46. import org.opentrafficsim.road.network.lane.CrossSectionLink;
  47. import org.opentrafficsim.road.network.lane.DirectedLanePosition;
  48. import org.opentrafficsim.road.network.lane.Lane;
  49. import org.opentrafficsim.road.network.lane.object.sensor.DestinationSensor;
  50. import org.opentrafficsim.road.network.lane.object.sensor.Sensor;

  51. import nl.tudelft.simulation.dsol.SimRuntimeException;
  52. import nl.tudelft.simulation.dsol.logger.SimLogger;
  53. import nl.tudelft.simulation.dsol.simulators.DEVSSimulatorInterface;
  54. import nl.tudelft.simulation.jstats.streams.MersenneTwister;
  55. import nl.tudelft.simulation.jstats.streams.StreamInterface;

  56. /**
  57.  * Utility to create vehicle generators on a network from an OD.
  58.  * <p>
  59.  * Copyright (c) 2013-2020 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.      * Utility class.
  71.      */
  72.     private ODApplier()
  73.     {
  74.         //
  75.     }

  76.     /**
  77.      * Applies the OD to the network by creating vehicle generators. The map returned contains objects created for vehicle
  78.      * generation. These are bundled in a {@code GeneratorObjects} and mapped to the vehicle generator id. Vehicle generator id
  79.      * is equal to the origin node id. For lane-based generators the id's are appended with an ordered number (e.g. A1), where
  80.      * the ordering is first by link id, and then right to left concerning the lateral lane position at the start of the lane.
  81.      * For node "A" this would for example be:<br>
  82.      * <table ><caption>&nbsp;</caption>
  83.      * <tr>
  84.      * <th>Generator id</th>
  85.      * <th>Link</th>
  86.      * <th>Lateral start offset</th>
  87.      * </tr>
  88.      * <tr>
  89.      * <th>A1</th>
  90.      * <th>AB</th>
  91.      * <th>-1.75m</th>
  92.      * </tr>
  93.      * <tr>
  94.      * <th>A2</th>
  95.      * <th>AB</th>
  96.      * <th>1.75m</th>
  97.      * </tr>
  98.      * <tr>
  99.      * <th>A3</th>
  100.      * <th>AC</th>
  101.      * <th>-3.5m</th>
  102.      * </tr>
  103.      * <tr>
  104.      * <th>A4</th>
  105.      * <th>AC</th>
  106.      * <th>0.0m</th>
  107.      * </tr>
  108.      * </table>
  109.      * @param network OTSRoadNetwork; network
  110.      * @param od ODMatrix; OD matrix
  111.      * @param simulator OTSSimulatorInterface; simulator
  112.      * @param odOptions ODOptions; options for vehicle generation
  113.      * @return Map&lt;String, GeneratorObjects&gt; map of generator id's and created generator objects mainly for testing
  114.      * @throws ParameterException if a parameter is missing
  115.      * @throws SimRuntimeException if this method is called after simulation time 0
  116.      */
  117.     @SuppressWarnings("checkstyle:methodlength")
  118.     public static Map<String, GeneratorObjects> applyOD(final OTSRoadNetwork network, final ODMatrix od,
  119.             final OTSSimulatorInterface simulator, final ODOptions odOptions) throws ParameterException, SimRuntimeException
  120.     {
  121.         Throw.whenNull(network, "Network may not be null.");
  122.         Throw.whenNull(od, "OD matrix may not be null.");
  123.         Throw.whenNull(simulator, "Simulator may not be null.");
  124.         Throw.whenNull(odOptions, "OD options may not be null.");
  125.         Throw.when(!simulator.getSimulatorTime().eq0(), SimRuntimeException.class,
  126.                 "Method ODApplier.applyOD() should be invoked at simulation time 0.");

  127.         // TODO sinks? white extension links?
  128.         for (Node destination : od.getDestinations())
  129.         {
  130.             createSensorsAtDestination(destination, simulator);
  131.         }

  132.         final Categorization categorization = od.getCategorization();
  133.         final boolean laneBased = categorization.entails(Lane.class);
  134.         boolean markovian = od.getCategorization().entails(GTUType.class);

  135.         // TODO clean up stream acquiring code after task OTS-315 has been completed
  136.         StreamInterface stream = simulator.getReplication().getStream("generation");
  137.         if (stream == null)
  138.         {
  139.             stream = simulator.getReplication().getStream("default");
  140.             if (stream == null)
  141.             {
  142.                 System.out
  143.                         .println("Using locally created stream (not from the simulator) for vehicle generation, with seed 1.");
  144.                 stream = new MersenneTwister(1L);
  145.             }
  146.             else
  147.             {
  148.                 System.out.println("Using stream 'default' for vehicle generation.");
  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.             // 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.             // Step 3: create generator(s)
  306.             initialPositions = sortByValue(initialPositions); // sorts by lateral position at link start
  307.             Map<Node, Integer> originGeneratorCounts = new LinkedHashMap<>();
  308.             for (DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>> root : initialPositions.keySet())
  309.             {
  310.                 Set<DirectedLanePosition> initialPosition = initialPositions.get(root);
  311.                 // id
  312.                 Node o = root.getObject();
  313.                 String id = o.getId();
  314.                 if (laneBased)
  315.                 {
  316.                     Integer count = originGeneratorCounts.get(o);
  317.                     if (count == null)
  318.                     {
  319.                         count = 0;
  320.                     }
  321.                     count++;
  322.                     id += count;
  323.                     originGeneratorCounts.put(o, count);
  324.                 }
  325.                 // functional generation elements
  326.                 Lane lane;
  327.                 LinkType linkType;
  328.                 if (laneBased)
  329.                 {
  330.                     lane = initialPosition.iterator().next().getLane();
  331.                     linkType = lane.getParentLink().getLinkType();
  332.                 }
  333.                 else
  334.                 {
  335.                     lane = null;
  336.                     linkType = getLinkTypeFromNode(o);
  337.                 }
  338.                 HeadwayDistribution randomization = odOptions.get(ODOptions.HEADWAY_DIST, lane, o, linkType);
  339.                 ArrivalsHeadwayGenerator headwayGenerator =
  340.                         new ArrivalsHeadwayGenerator(root, simulator, stream, randomization);
  341.                 GTUCharacteristicsGeneratorODWrapper characteristicsGenerator = new GTUCharacteristicsGeneratorODWrapper(root,
  342.                         simulator, odOptions.get(ODOptions.GTU_TYPE, lane, o, linkType), stream);
  343.                 RoomChecker roomChecker = odOptions.get(ODOptions.ROOM_CHECKER, lane, o, linkType);
  344.                 IdGenerator idGenerator = odOptions.get(ODOptions.GTU_ID, lane, o, linkType);
  345.                 LaneBiases biases = odOptions.get(ODOptions.getLaneBiasOption(network), lane, o, linkType);
  346.                 // and finally, the generator
  347.                 try
  348.                 {
  349.                     LaneBasedGTUGenerator generator = new LaneBasedGTUGenerator(id, headwayGenerator, characteristicsGenerator,
  350.                             GeneratorPositions.create(initialPosition, stream, biases, linkWeights), network, simulator,
  351.                             roomChecker, idGenerator);
  352.                     generator.setNoLaneChangeDistance(odOptions.get(ODOptions.NO_LC_DIST, lane, o, linkType));
  353.                     generator.setInstantaneousLaneChange(odOptions.get(ODOptions.INSTANT_LC, lane, o, linkType));
  354.                     generator.setErrorHandler(odOptions.get(ODOptions.ERROR_HANDLER, 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.                     simulator.getLogger().always().error(exception);
  361.                     throw new RuntimeException(exception);
  362.                 }
  363.                 catch (ProbabilityException exception)
  364.                 {
  365.                     // should not happen, as we define probabilities in the headwayGenerator
  366.                     simulator.getLogger().always().error(exception);
  367.                     throw new RuntimeException(exception);
  368.                 }
  369.             }
  370.         }
  371.         return output;
  372.     }

  373.     /**
  374.      * Create destination sensors at all lanes connected to a destination node. This method considers connectors too.
  375.      * @param destination Node; destination node
  376.      * @param simulator OTSSimulatorInterface; simulator
  377.      */
  378.     private static void createSensorsAtDestination(final Node destination, final OTSSimulatorInterface simulator)
  379.     {
  380.         for (Link link : destination.getLinks())
  381.         {
  382.             if (link.getLinkType().isConnector() && !link.getStartNode().equals(destination))
  383.             {
  384.                 createSensorsAtDestinationNode(link.getStartNode(), simulator);
  385.             }
  386.             else
  387.             {
  388.                 createSensorsAtDestinationNode(destination, simulator);
  389.             }
  390.         }
  391.     }
  392.    
  393.     /**
  394.      * Create sensors at all lanes connected to this node. This method does not handle connectors.
  395.      * @param destination Node; the destination node
  396.      * @param simulator OTSSimulatorInterface; simulator
  397.      */
  398.     private static void createSensorsAtDestinationNode(final Node destination, final OTSSimulatorInterface simulator)
  399.     {
  400.         for (Link link : destination.getLinks())
  401.         {
  402.             if (link instanceof CrossSectionLink)
  403.             {
  404.                 for (Lane lane : ((CrossSectionLink) link).getLanes())
  405.                 {
  406.                     try
  407.                     {
  408.                         // if the lane already contains a SinkSensor, skip creating a new one
  409.                         boolean destinationSensorExists = false;
  410.                         for (Sensor sensor : lane.getSensors())
  411.                         {
  412.                             if (sensor instanceof DestinationSensor)
  413.                             {
  414.                                 destinationSensorExists = true;
  415.                             }
  416.                         }
  417.                         if (!destinationSensorExists)
  418.                         {
  419.                             if (link.getEndNode().equals(destination))
  420.                             {
  421.                                 new DestinationSensor(lane, lane.getLength(), GTUDirectionality.DIR_PLUS, simulator);
  422.                             }
  423.                             else if (link.getStartNode().equals(destination))
  424.                             {
  425.                                 new DestinationSensor(lane, Length.ZERO, GTUDirectionality.DIR_MINUS, simulator);
  426.                             }
  427.                         }
  428.                     }
  429.                     catch (NetworkException exception)
  430.                     {
  431.                         // can not happen, we use Length.ZERO and lane.getLength()
  432.                         simulator.getLogger().always().error(exception);
  433.                         throw new RuntimeException(exception);
  434.                     }
  435.                 }
  436.             }
  437.         }
  438.     }
  439.    
  440.     /**
  441.      * Returns the common ancestor {@code LinkType} of all links connected to the node, moving through connectors.
  442.      * @param node Node; origin node
  443.      * @return common ancestor {@code LinkType} of all links connected to the node, moving through connectors
  444.      */
  445.     private static LinkType getLinkTypeFromNode(final Node node)
  446.     {
  447.         return getLinkTypeFromNode0(node, false);
  448.     }

  449.     /**
  450.      * Returns the common ancestor {@code LinkType} of all links connected to the node, moving through connectors.
  451.      * @param node Node; origin node
  452.      * @param ignoreConnectors boolean; ignore connectors
  453.      * @return common ancestor {@code LinkType} of all links connected to the node, moving through connectors
  454.      */
  455.     private static LinkType getLinkTypeFromNode0(final Node node, final boolean ignoreConnectors)
  456.     {
  457.         LinkType linkType = null;
  458.         for (Link link : node.getLinks())
  459.         {
  460.             LinkType next = link.getLinkType();
  461.             if (!ignoreConnectors && next.isConnector())
  462.             {
  463.                 Node otherNode = link.getStartNode().equals(node) ? link.getEndNode() : link.getStartNode();
  464.                 next = getLinkTypeFromNode0(otherNode, true);
  465.             }
  466.             if (next != null && !next.isConnector())
  467.             {
  468.                 if (linkType == null)
  469.                 {
  470.                     linkType = next;
  471.                 }
  472.                 else
  473.                 {
  474.                     linkType = linkType.commonAncestor(next);
  475.                     if (linkType == null)
  476.                     {
  477.                         // incompatible link types
  478.                         return null;
  479.                     }
  480.                 }
  481.             }
  482.         }
  483.         return linkType;
  484.     }

  485.     /**
  486.      * Returns a sorted map.
  487.      * @param map Map&lt;K, V&gt;; input map
  488.      * @param <K> key type (implemented for cleaner code only)
  489.      * @param <V> value type (implemented for cleaner code only)
  490.      * @return Map; sorted map
  491.      */
  492.     private static <K, V extends Set<DirectedLanePosition>> Map<K, V> sortByValue(final Map<K, V> map)
  493.     {
  494.         return map.entrySet().stream().sorted(new Comparator<Map.Entry<K, V>>()
  495.         {
  496.             @Override
  497.             public int compare(final Entry<K, V> o1, final Entry<K, V> o2)
  498.             {
  499.                 DirectedLanePosition lanePos1 = o1.getValue().iterator().next();
  500.                 String linkId1 = lanePos1.getLane().getParentLink().getId();
  501.                 DirectedLanePosition lanePos2 = o2.getValue().iterator().next();
  502.                 String linkId2 = lanePos2.getLane().getParentLink().getId();
  503.                 int c = linkId1.compareToIgnoreCase(linkId2);
  504.                 if (c == 0)
  505.                 {
  506.                     Length pos1 = lanePos1.getGtuDirection().isPlus() ? Length.ZERO : lanePos1.getLane().getLength();
  507.                     Length lat1 = lanePos1.getLane().getLateralCenterPosition(pos1);
  508.                     Length pos2 = lanePos2.getGtuDirection().isPlus() ? Length.ZERO : lanePos2.getLane().getLength();
  509.                     Length lat2 = lanePos2.getLane().getLateralCenterPosition(pos2);
  510.                     return lat1.compareTo(lat2);
  511.                 }
  512.                 return c;
  513.             }
  514.         }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
  515.     }

  516.     /**
  517.      * Adds {@code DirectedLanePosition}s to the input set, for {@code Lane}s on the given link, starting at the given
  518.      * {@code Node}.
  519.      * @param link CrossSectionLink; link with lanes to add positions for
  520.      * @param node Node; node on the side where positions should be placed
  521.      * @param positionSet Set&lt;DirectedLanePosition&gt;; set to add position to
  522.      */
  523.     private static void setDirectedLanePosition(final CrossSectionLink link, final Node node,
  524.             final Set<DirectedLanePosition> positionSet)
  525.     {
  526.         for (Lane lane : link.getLanes())
  527.         {
  528.             // TODO should be GTU type dependent.
  529.             if (!lane.getParentLink().getStartNode().equals(node))
  530.             {
  531.                 return;
  532.                 // TODO handle lanes that ARE drivable contrary to the design direction of the link
  533.             }
  534.             try
  535.             {
  536.                 positionSet.add(lane.getParentLink().getStartNode().equals(node)
  537.                         ? new DirectedLanePosition(lane, Length.ZERO, GTUDirectionality.DIR_PLUS)
  538.                         : new DirectedLanePosition(lane, lane.getLength(), GTUDirectionality.DIR_MINUS));
  539.             }
  540.             catch (GTUException ge)
  541.             {
  542.                 link.getSimulator().getLogger().always().error(ge);
  543.                 throw new RuntimeException(ge);
  544.             }
  545.         }
  546.     }

  547.     /**
  548.      * Node for demand tree. Based on two constructors there are 2 types of nodes:<br>
  549.      * <ul>
  550.      * <li>Branch nodes; with an object and a stream for randomly drawing a child node.</li>
  551.      * <li>Leaf nodes; with an object and demand data (time, frequency, interpolation).</li>
  552.      * </ul>
  553.      * To accomplish a branching of Node (origin) &gt; Node (destination) &gt; Category, the following generics types can be
  554.      * used:<br>
  555.      * <br>
  556.      * {@code DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>>}
  557.      * <p>
  558.      * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
  559.      * <br>
  560.      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
  561.      * <p>
  562.      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 1 dec. 2017 <br>
  563.      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
  564.      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
  565.      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
  566.      * @param <T> type of contained object
  567.      * @param <K> type of child nodes
  568.      */
  569.     private static class DemandNode<T, K extends DemandNode<?, ?>> implements Arrivals
  570.     {

  571.         /** Node object. */
  572.         private final T object;

  573.         /** Random stream to draw child node. */
  574.         private final StreamInterface stream;

  575.         /** Children. */
  576.         private final List<K> children = new ArrayList<>();

  577.         /** Demand data. */
  578.         private final DemandPattern demandPattern;

  579.         /** Unique GTU types of leaf nodes. */
  580.         private final List<GTUType> gtuTypes = new ArrayList<>();

  581.         /** Number of leaf nodes for the unique GTU types. */
  582.         private final List<Integer> gtuTypeCounts = new ArrayList<>();

  583.         /** GTU type of leaf nodes. */
  584.         private final Map<K, GTUType> gtuTypesPerChild = new LinkedHashMap<>();

  585.         /** Markov chain for GTU type selection. */
  586.         private final MarkovChain markov;

  587.         /**
  588.          * Constructor for branching node, with Markov selection.
  589.          * @param object T; node object
  590.          * @param stream StreamInterface; random stream to draw child node
  591.          * @param markov MarkovChain; Markov chain
  592.          */
  593.         DemandNode(final T object, final StreamInterface stream, final MarkovChain markov)
  594.         {
  595.             this.object = object;
  596.             this.stream = stream;
  597.             this.demandPattern = null;
  598.             this.markov = markov;
  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.          * Adds child to a branching node.
  614.          * @param child K; child node
  615.          */
  616.         public void addChild(final K child)
  617.         {
  618.             this.children.add(child);
  619.         }

  620.         /**
  621.          * Adds child to a branching node.
  622.          * @param child K; child node
  623.          * @param gtuType GTUType; gtu type for Markov chain
  624.          */
  625.         public void addLeaf(final K child, final GTUType gtuType)
  626.         {
  627.             Throw.when(this.gtuTypes == null, IllegalStateException.class,
  628.                     "Adding leaf with GTUType in not possible on a non-Markov node.");
  629.             addChild(child);
  630.             this.gtuTypesPerChild.put(child, gtuType);
  631.             if (!this.gtuTypes.contains(gtuType))
  632.             {
  633.                 this.gtuTypes.add(gtuType);
  634.                 this.gtuTypeCounts.add(1);
  635.             }
  636.             else
  637.             {
  638.                 int index = this.gtuTypes.indexOf(gtuType);
  639.                 this.gtuTypeCounts.set(index, this.gtuTypeCounts.get(index) + 1);
  640.             }
  641.         }

  642.         /**
  643.          * Randomly draws a child node.
  644.          * @param time Time; simulation time
  645.          * @return K; randomly drawn child node
  646.          */
  647.         public K draw(final Time time)
  648.         {
  649.             Throw.when(this.children.isEmpty(), RuntimeException.class, "Calling draw on a leaf node in the demand tree.");
  650.             Map<K, Double> weightMap = new LinkedHashMap<>();
  651.             if (this.markov == null)
  652.             {
  653.                 // regular draw, loop children and collect their frequencies
  654.                 for (K child : this.children)
  655.                 {
  656.                     double f = child.getFrequency(time, true).si; // sliceStart = true is arbitrary
  657.                     weightMap.put(child, f);
  658.                 }
  659.             }
  660.             else
  661.             {
  662.                 // markov chain draw, the markov chain only selects a GTU type, not a child node
  663.                 GTUType[] gtuTypeArray = new GTUType[this.gtuTypes.size()];
  664.                 gtuTypeArray = this.gtuTypes.toArray(gtuTypeArray);
  665.                 Frequency[] steadyState = new Frequency[this.gtuTypes.size()];
  666.                 Arrays.fill(steadyState, Frequency.ZERO);
  667.                 Map<K, Frequency> frequencies = new LinkedHashMap<>(); // stored, saves us from calculating them twice
  668.                 for (K child : this.children)
  669.                 {
  670.                     GTUType gtuType = this.gtuTypesPerChild.get(child);
  671.                     int index = this.gtuTypes.indexOf(gtuType);
  672.                     Frequency f = child.getFrequency(time, true); // sliceStart = true is arbitrary
  673.                     frequencies.put(child, f);
  674.                     steadyState[index] = steadyState[index].plus(f);
  675.                 }
  676.                 GTUType nextGtuType = this.markov.draw(gtuTypeArray, steadyState, this.stream);
  677.                 // select only child nodes registered to the next GTU type
  678.                 for (K child : this.children)
  679.                 {
  680.                     if (this.gtuTypesPerChild.get(child).equals(nextGtuType))
  681.                     {
  682.                         double f = frequencies.get(child).si;
  683.                         weightMap.put(child, f);
  684.                     }
  685.                 }
  686.             }
  687.             return Draw.drawWeighted(weightMap, this.stream);
  688.         }

  689.         /**
  690.          * Returns the node object.
  691.          * @return T; node object
  692.          */
  693.         public T getObject()
  694.         {
  695.             return this.object;
  696.         }

  697.         /**
  698.          * Returns the child that pertains to specified object or {@code null} if no such child is present.
  699.          * @param obj Object; child object
  700.          * @return child that pertains to specified object or {@code null} if no such child is present
  701.          */
  702.         public K getChild(final Object obj)
  703.         {
  704.             for (K child : this.children)
  705.             {
  706.                 if (child.getObject().equals(obj))
  707.                 {
  708.                     return child;
  709.                 }
  710.             }
  711.             return null;
  712.         }

  713.         /** {@inheritDoc} */
  714.         @Override
  715.         public Frequency getFrequency(final Time time, final boolean sliceStart)
  716.         {
  717.             if (this.demandPattern != null)
  718.             {
  719.                 return this.demandPattern.getFrequency(time, sliceStart);
  720.             }
  721.             Frequency f = new Frequency(0.0, FrequencyUnit.PER_HOUR);
  722.             for (K child : this.children)
  723.             {
  724.                 f = f.plus(child.getFrequency(time, sliceStart));
  725.             }
  726.             return f;
  727.         }

  728.         /** {@inheritDoc} */
  729.         @Override
  730.         public Time nextTimeSlice(final Time time)
  731.         {
  732.             if (this.demandPattern != null)
  733.             {
  734.                 return this.demandPattern.nextTimeSlice(time);
  735.             }
  736.             Time out = null;
  737.             for (K child : this.children)
  738.             {
  739.                 Time childSlice = child.nextTimeSlice(time);
  740.                 out = out == null || (childSlice != null && childSlice.lt(out)) ? childSlice : out;
  741.             }
  742.             return out;
  743.         }

  744.         /** {@inheritDoc} */
  745.         @Override
  746.         public String toString()
  747.         {
  748.             return "DemandNode [object=" + this.object + ", stream=" + this.stream + ", children=" + this.children
  749.                     + ", demandPattern=" + this.demandPattern + ", gtuTypes=" + this.gtuTypes + ", gtuTypeCounts="
  750.                     + this.gtuTypeCounts + ", gtuTypesPerChild=" + this.gtuTypesPerChild + ", markov=" + this.markov + "]";
  751.         }

  752.     }

  753.     /**
  754.      * Wrapper class around a {@code MarkovCorrelation}, including the last type. One of these should be used for each vehicle
  755.      * generator.
  756.      */
  757.     private static class MarkovChain
  758.     {
  759.         /** Markov correlation for GTU type selection. */
  760.         private final MarkovCorrelation<GTUType, Frequency> markov;

  761.         /** Previously returned GTU type. */
  762.         private GTUType previousGtuType = null;

  763.         /**
  764.          * Constructor.
  765.          * @param markov MarkovCorrelation&lt;GTUType, Frequency&gt;; Markov correlation for GTU type selection
  766.          */
  767.         MarkovChain(final MarkovCorrelation<GTUType, Frequency> markov)
  768.         {
  769.             this.markov = markov;
  770.         }

  771.         /**
  772.          * Returns a next GTU type drawn using a Markov chain.
  773.          * @param gtuTypes GTUType[]; GTUTypes to consider
  774.          * @param intensities Frequency[]; frequency for each GTU type, i.e. the steady-state
  775.          * @param stream StreamInterface; stream for random numbers
  776.          * @return next GTU type drawn using a Markov chain
  777.          */
  778.         public GTUType draw(final GTUType[] gtuTypes, final Frequency[] intensities, final StreamInterface stream)
  779.         {
  780.             this.previousGtuType = this.markov.drawState(this.previousGtuType, gtuTypes, intensities, stream);
  781.             return this.previousGtuType;
  782.         }
  783.     }

  784.     /**
  785.      * Characteristics generation based on OD demand.
  786.      * <p>
  787.      * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
  788.      * <br>
  789.      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
  790.      * <p>
  791.      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 7 dec. 2017 <br>
  792.      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
  793.      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
  794.      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
  795.      */
  796.     private static class GTUCharacteristicsGeneratorODWrapper implements LaneBasedGTUCharacteristicsGenerator
  797.     {

  798.         /** Root node with origin. */
  799.         private final DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>> root;

  800.         /** Simulator. */
  801.         private final DEVSSimulatorInterface.TimeDoubleUnit simulator;

  802.         /** Characteristics generator based on OD information. */
  803.         private final GTUCharacteristicsGeneratorOD characteristicsGenerator;

  804.         /** Stream for random numbers. */
  805.         private final StreamInterface randomStream;

  806.         /**
  807.          * @param root DemandNode&lt;Node, DemandNode&lt;Node, DemandNode&lt;Category, ?&gt;&gt;&gt;; root node with origin
  808.          * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; simulator
  809.          * @param characteristicsGenerator GTUCharacteristicsGeneratorOD; characteristics generator based on OD information
  810.          * @param randomStream StreamInterface; stream for random numbers
  811.          */
  812.         GTUCharacteristicsGeneratorODWrapper(final DemandNode<Node, DemandNode<Node, DemandNode<Category, ?>>> root,
  813.                 final DEVSSimulatorInterface.TimeDoubleUnit simulator,
  814.                 final GTUCharacteristicsGeneratorOD characteristicsGenerator, final StreamInterface randomStream)
  815.         {
  816.             this.root = root;
  817.             this.simulator = simulator;
  818.             this.characteristicsGenerator = characteristicsGenerator;
  819.             this.randomStream = randomStream;
  820.         }

  821.         /** {@inheritDoc} */
  822.         @Override
  823.         public LaneBasedGTUCharacteristics draw() throws ProbabilityException, ParameterException, GTUException
  824.         {
  825.             // obtain node objects
  826.             Time time = this.simulator.getSimulatorTime();
  827.             Node origin = this.root.getObject();
  828.             DemandNode<Node, DemandNode<Category, ?>> destinationNode = this.root.draw(time);
  829.             Node destination = destinationNode.getObject();
  830.             Category category = destinationNode.draw(time).getObject();
  831.             // forward to lower-level generator
  832.             // XXX typically calls DefaultGTUCharacteristicsGeneratorOD.draw(...)
  833.             return this.characteristicsGenerator.draw(origin, destination, category, this.randomStream);
  834.         }

  835.         /** {@inheritDoc} */
  836.         @Override
  837.         public String toString()
  838.         {
  839.             return "GTUCharacteristicsGeneratorODWrapper [root=" + this.root + ", simulator=" + this.simulator
  840.                     + ", characteristicsGenerator=" + this.characteristicsGenerator + ", randomStream=" + this.randomStream
  841.                     + "]";
  842.         }

  843.     }

  844.     /**
  845.      * Class to contain created generator objects.
  846.      * <p>
  847.      * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
  848.      * <br>
  849.      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
  850.      * <p>
  851.      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 12 dec. 2017 <br>
  852.      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
  853.      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
  854.      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
  855.      */
  856.     public static class GeneratorObjects
  857.     {

  858.         /** Main generator for GTU's. */
  859.         private final LaneBasedGTUGenerator generator;

  860.         /** Generator of headways. */
  861.         private final Generator<Duration> headwayGenerator;

  862.         /** Generator of GTU characteristics. */
  863.         private final LaneBasedGTUCharacteristicsGenerator characteristicsGenerator;

  864.         /**
  865.          * @param generator LaneBasedGTUGenerator; main generator for GTU's
  866.          * @param headwayGenerator Generator&lt;Duration&gt;; generator of headways
  867.          * @param characteristicsGenerator LaneBasedGTUCharacteristicsGenerator; generator of GTU characteristics
  868.          */
  869.         public GeneratorObjects(final LaneBasedGTUGenerator generator, final Generator<Duration> headwayGenerator,
  870.                 final LaneBasedGTUCharacteristicsGenerator characteristicsGenerator)
  871.         {
  872.             this.generator = generator;
  873.             this.headwayGenerator = headwayGenerator;
  874.             this.characteristicsGenerator = characteristicsGenerator;
  875.         }

  876.         /**
  877.          * Returns the main generator for GTU's.
  878.          * @return LaneBasedGTUGenerator; main generator for GTU's
  879.          */
  880.         public LaneBasedGTUGenerator getGenerator()
  881.         {
  882.             return this.generator;
  883.         }

  884.         /**
  885.          * Returns the generator of headways.
  886.          * @return Generator&lt;Duration&gt; generator of headways
  887.          */
  888.         public Generator<Duration> getHeadwayGenerator()
  889.         {
  890.             return this.headwayGenerator;
  891.         }

  892.         /**
  893.          * Returns the generator of GTU characteristics.
  894.          * @return LaneBasedGTUCharacteristicsGenerator; generator of GTU characteristics
  895.          */
  896.         public LaneBasedGTUCharacteristicsGenerator getCharachteristicsGenerator()
  897.         {
  898.             return this.characteristicsGenerator;
  899.         }

  900.     }

  901. }