SplitFraction.java

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

  2. import java.util.Iterator;
  3. import java.util.LinkedHashMap;
  4. import java.util.Map;
  5. import java.util.TreeMap;

  6. import org.djunits.unit.DurationUnit;
  7. import org.djunits.value.ValueRuntimeException;
  8. import org.djunits.value.storage.StorageType;
  9. import org.djunits.value.vdouble.scalar.Duration;
  10. import org.djunits.value.vdouble.vector.DurationVector;
  11. import org.djunits.value.vdouble.vector.base.DoubleVector;
  12. import org.djutils.exceptions.Throw;
  13. import org.opentrafficsim.core.gtu.GTUType;
  14. import org.opentrafficsim.core.math.Draw;
  15. import org.opentrafficsim.core.network.Link;
  16. import org.opentrafficsim.core.network.Node;
  17. import org.opentrafficsim.road.network.lane.CrossSectionLink;

  18. import nl.tudelft.simulation.dsol.simulators.DEVSSimulatorInterface;
  19. import nl.tudelft.simulation.jstats.streams.StreamInterface;

  20. /**
  21.  * Split fraction at a node with fractions per link, optionally per gtu type.
  22.  * <p>
  23.  * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
  24.  * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
  25.  * <p>
  26.  * @version $Revision$, $LastChangedDate$, by $Author$, initial version 4 mei 2017 <br>
  27.  * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
  28.  * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
  29.  * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
  30.  */
  31. public class SplitFraction
  32. {

  33.     /** Node. */
  34.     private final Node node;

  35.     /** Interpolation. */
  36.     private final Interpolation interpolation;

  37.     /** Random stream. */
  38.     private final StreamInterface random;

  39.     /** Simulator. */
  40.     private final DEVSSimulatorInterface.TimeDoubleUnit simulator;

  41.     /** Map of fractions by GTUType and Link. */
  42.     private final Map<GTUType, Map<Link, Map<Duration, Double>>> fractions = new LinkedHashMap<>();

  43.     /**
  44.      * Constructor.
  45.      * @param node Node; node
  46.      * @param interpolation Interpolation; interpolation
  47.      * @param random StreamInterface; random stream
  48.      * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; simulator
  49.      */
  50.     public SplitFraction(final Node node, final Interpolation interpolation, final StreamInterface random,
  51.             final DEVSSimulatorInterface.TimeDoubleUnit simulator)
  52.     {
  53.         this.node = node;
  54.         this.interpolation = interpolation;
  55.         this.random = random;
  56.         this.simulator = simulator;
  57.     }

  58.     /**
  59.      * Add fraction to link, this will apply to {@code GTUType.ALL} and all time.
  60.      * @param link Link; link
  61.      * @param fraction double; fraction
  62.      */
  63.     public void addFraction(final Link link, final double fraction)
  64.     {
  65.         addFraction(link, link.getNetwork().getGtuType(GTUType.DEFAULTS.VEHICLE), fraction);
  66.     }

  67.     /**
  68.      * Add fraction to link for gtu type, this will apply to all time.
  69.      * @param link Link; link
  70.      * @param gtuType GTUType; gtu type
  71.      * @param fraction double; fraction
  72.      */
  73.     public void addFraction(final Link link, final GTUType gtuType, final double fraction)
  74.     {
  75.         double[] fracs = new double[2];
  76.         fracs[0] = fraction;
  77.         fracs[1] = fraction;
  78.         DurationVector time;
  79.         try
  80.         {
  81.             double[] t = new double[2];
  82.             t[1] = Double.MAX_VALUE;
  83.             time = DoubleVector.instantiate(t, DurationUnit.SI, StorageType.DENSE);
  84.         }
  85.         catch (ValueRuntimeException exception)
  86.         {
  87.             // should not happen, input is not null
  88.             throw new RuntimeException("Input null while creating duration vector.", exception);
  89.         }
  90.         addFraction(link, gtuType, time, fracs);
  91.     }

  92.     /**
  93.      * Add fraction to link over time, this will apply to {@code GTUType.ALL}.
  94.      * @param link Link; link
  95.      * @param time DurationVector; time
  96.      * @param fraction double[]; fraction
  97.      */
  98.     public void addFraction(final Link link, final DurationVector time, final double[] fraction)
  99.     {
  100.         addFraction(link, link.getNetwork().getGtuType(GTUType.DEFAULTS.VEHICLE), time, fraction);
  101.     }

  102.     /**
  103.      * Add fraction to link over time for gtu type.
  104.      * @param link Link; link
  105.      * @param gtuType GTUType; gtu type
  106.      * @param time DurationVector; time
  107.      * @param fraction double[]; fraction
  108.      */
  109.     public void addFraction(final Link link, final GTUType gtuType, final DurationVector time, final double[] fraction)
  110.     {
  111.         Throw.when(time.size() != fraction.length, IllegalArgumentException.class,
  112.                 "Time vector and fraction array require equal length.");
  113.         Throw.when(!this.node.getLinks().contains(link), IllegalArgumentException.class, "Link %s is not connected to node %s.",
  114.                 link, this.node);
  115.         for (double f : fraction)
  116.         {
  117.             Throw.when(f < 0.0, IllegalArgumentException.class, "Fraction should be larger than 0.0.");
  118.         }
  119.         if (this.fractions.containsKey(gtuType))
  120.         {
  121.             this.fractions.put(gtuType, new LinkedHashMap<>());
  122.         }
  123.         this.fractions.get(gtuType).put(link, new TreeMap<>());
  124.         for (int i = 0; i <= time.size(); i++)
  125.         {
  126.             try
  127.             {
  128.                 this.fractions.get(gtuType).get(link).put(time.get(i), fraction[i]);
  129.             }
  130.             catch (ValueRuntimeException exception)
  131.             {
  132.                 // should not happen, sizes are checked
  133.                 throw new RuntimeException("Index out of range.", exception);
  134.             }
  135.         }
  136.     }

  137.     /**
  138.      * Draw next link based on split fractions. If no fractions were defined, split fractions are determined based on the number
  139.      * of lanes per link.
  140.      * @param gtuType GTUType; gtuType
  141.      * @return next link
  142.      */
  143.     public Link draw(final GTUType gtuType)
  144.     {
  145.         for (GTUType gtu : this.fractions.keySet())
  146.         {
  147.             if (gtuType.isOfType(gtu))
  148.             {
  149.                 Map<Link, Double> currentFractions = new LinkedHashMap<>();
  150.                 double t = this.simulator.getSimulatorTime().si;
  151.                 for (Link link : this.fractions.get(gtu).keySet())
  152.                 {
  153.                     Iterator<Duration> iterator = this.fractions.get(gtu).get(link).keySet().iterator();
  154.                     Duration prev = iterator.next();
  155.                     while (iterator.hasNext())
  156.                     {
  157.                         Duration next = iterator.next();
  158.                         if (prev.si <= t && t < next.si)
  159.                         {
  160.                             // TODO let interpolation interpolate itself
  161.                             double f;
  162.                             if (this.interpolation.equals(Interpolation.STEPWISE))
  163.                             {
  164.                                 f = this.fractions.get(gtuType).get(link).get(prev);
  165.                             }
  166.                             else
  167.                             {
  168.                                 double r = (t - prev.si) / (next.si - prev.si);
  169.                                 f = (1 - r) * this.fractions.get(gtuType).get(link).get(prev)
  170.                                         + r * this.fractions.get(gtuType).get(link).get(next);
  171.                             }
  172.                             currentFractions.put(link, f);
  173.                             break;
  174.                         }
  175.                     }
  176.                 }
  177.                 return Draw.drawWeighted(currentFractions, this.random);
  178.             }
  179.         }
  180.         // GTU Type not defined, distribute by number of lanes (or weight = 1.0 if not a CrossSectionLink)
  181.         boolean fractionAdded = false;
  182.         for (Link link : this.node.getLinks())
  183.         {
  184.             if ((link.getStartNode().equals(this.node) && link.getDirectionality(gtuType).isForwardOrBoth())
  185.                     || (link.getEndNode().equals(this.node) && link.getDirectionality(gtuType).isBackwardOrBoth()))
  186.             {
  187.                 if (link instanceof CrossSectionLink)
  188.                 {
  189.                     int n = ((CrossSectionLink) link).getLanes().size();
  190.                     if (n > 0)
  191.                     {
  192.                         fractionAdded = true;
  193.                         addFraction(link, gtuType, n);
  194.                     }
  195.                 }
  196.                 else
  197.                 {
  198.                     fractionAdded = true;
  199.                     addFraction(link, gtuType, 1.0);
  200.                 }
  201.             }
  202.         }
  203.         Throw.when(!fractionAdded, UnsupportedOperationException.class,
  204.                 "Split fraction on node %s cannot be derived for gtuType %s as there are no outgoing links.", this.node,
  205.                 gtuType);
  206.         // redraw with the information that was just set
  207.         return draw(gtuType);
  208.     }

  209.     /** {@inheritDoc} */
  210.     @Override
  211.     public int hashCode()
  212.     {
  213.         final int prime = 31;
  214.         int result = 1;
  215.         result = prime * result + ((this.fractions == null) ? 0 : this.fractions.hashCode());
  216.         result = prime * result + ((this.node == null) ? 0 : this.node.hashCode());
  217.         return result;
  218.     }

  219.     /** {@inheritDoc} */
  220.     @Override
  221.     public boolean equals(final Object obj)
  222.     {
  223.         if (this == obj)
  224.         {
  225.             return true;
  226.         }
  227.         if (obj == null)
  228.         {
  229.             return false;
  230.         }
  231.         if (getClass() != obj.getClass())
  232.         {
  233.             return false;
  234.         }
  235.         SplitFraction other = (SplitFraction) obj;
  236.         if (this.fractions == null)
  237.         {
  238.             if (other.fractions != null)
  239.             {
  240.                 return false;
  241.             }
  242.         }
  243.         else if (!this.fractions.equals(other.fractions))
  244.         {
  245.             return false;
  246.         }
  247.         if (this.node == null)
  248.         {
  249.             if (other.node != null)
  250.             {
  251.                 return false;
  252.             }
  253.         }
  254.         else if (!this.node.equals(other.node))
  255.         {
  256.             return false;
  257.         }
  258.         return true;
  259.     }

  260.     /** {@inheritDoc} */
  261.     @Override
  262.     public String toString()
  263.     {
  264.         return "SplitFraction [node=" + this.node + ", fractions=" + this.fractions + "]";
  265.     }

  266. }