SplitFraction.java

  1. package org.opentrafficsim.road.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.dsol.OtsSimulatorInterface;
  14. import org.opentrafficsim.core.gtu.GtuType;
  15. import org.opentrafficsim.core.math.Draw;
  16. import org.opentrafficsim.core.network.Link;
  17. import org.opentrafficsim.core.network.Node;
  18. import org.opentrafficsim.road.network.lane.CrossSectionLink;

  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-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
  24.  * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  25.  * </p>
  26.  * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
  27.  * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
  28.  * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
  29.  */
  30. public class SplitFraction
  31. {

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

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

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

  38.     /** Simulator. */
  39.     private final OtsSimulatorInterface simulator;

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

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

  57.     /**
  58.      * Add fraction to link for gtu type, this will apply to all time.
  59.      * @param link Link; link
  60.      * @param gtuType GtuType; gtu type
  61.      * @param fraction double; fraction
  62.      */
  63.     public void addFraction(final Link link, final GtuType gtuType, final double fraction)
  64.     {
  65.         double[] fracs = new double[2];
  66.         fracs[0] = fraction;
  67.         fracs[1] = fraction;
  68.         DurationVector time;
  69.         try
  70.         {
  71.             double[] t = new double[2];
  72.             t[1] = Double.MAX_VALUE;
  73.             time = DoubleVector.instantiate(t, DurationUnit.SI, StorageType.DENSE);
  74.         }
  75.         catch (ValueRuntimeException exception)
  76.         {
  77.             // should not happen, input is not null
  78.             throw new RuntimeException("Input null while creating duration vector.", exception);
  79.         }
  80.         addFraction(link, gtuType, time, fracs);
  81.     }

  82.     /**
  83.      * Add fraction to link over time for gtu type.
  84.      * @param link Link; link
  85.      * @param gtuType GtuType; gtu type
  86.      * @param time DurationVector; time
  87.      * @param fraction double[]; fraction
  88.      */
  89.     public void addFraction(final Link link, final GtuType gtuType, final DurationVector time, final double[] fraction)
  90.     {
  91.         Throw.when(time.size() != fraction.length, IllegalArgumentException.class,
  92.                 "Time vector and fraction array require equal length.");
  93.         Throw.when(!this.node.getLinks().contains(link), IllegalArgumentException.class, "Link %s is not connected to node %s.",
  94.                 link, this.node);
  95.         for (double f : fraction)
  96.         {
  97.             Throw.when(f < 0.0, IllegalArgumentException.class, "Fraction should be larger than 0.0.");
  98.         }
  99.         if (this.fractions.containsKey(gtuType))
  100.         {
  101.             this.fractions.put(gtuType, new LinkedHashMap<>());
  102.         }
  103.         this.fractions.get(gtuType).put(link, new TreeMap<>());
  104.         for (int i = 0; i <= time.size(); i++)
  105.         {
  106.             try
  107.             {
  108.                 this.fractions.get(gtuType).get(link).put(time.get(i), fraction[i]);
  109.             }
  110.             catch (ValueRuntimeException exception)
  111.             {
  112.                 // should not happen, sizes are checked
  113.                 throw new RuntimeException("Index out of range.", exception);
  114.             }
  115.         }
  116.     }

  117.     /**
  118.      * Draw next link based on split fractions. If no fractions were defined, split fractions are determined based on the number
  119.      * of lanes per link.
  120.      * @param gtuType GtuType; gtuType
  121.      * @return next link
  122.      */
  123.     public Link draw(final GtuType gtuType)
  124.     {
  125.         for (GtuType gtu : this.fractions.keySet())
  126.         {
  127.             if (gtuType.isOfType(gtu))
  128.             {
  129.                 Map<Link, Double> currentFractions = new LinkedHashMap<>();
  130.                 double t = this.simulator.getSimulatorTime().si;
  131.                 for (Link link : this.fractions.get(gtu).keySet())
  132.                 {
  133.                     Iterator<Duration> iterator = this.fractions.get(gtu).get(link).keySet().iterator();
  134.                     Duration prev = iterator.next();
  135.                     while (iterator.hasNext())
  136.                     {
  137.                         Duration next = iterator.next();
  138.                         if (prev.si <= t && t < next.si)
  139.                         {
  140.                             // TODO let interpolation interpolate itself
  141.                             double f;
  142.                             if (this.interpolation.equals(Interpolation.STEPWISE))
  143.                             {
  144.                                 f = this.fractions.get(gtuType).get(link).get(prev);
  145.                             }
  146.                             else
  147.                             {
  148.                                 double r = (t - prev.si) / (next.si - prev.si);
  149.                                 f = (1 - r) * this.fractions.get(gtuType).get(link).get(prev)
  150.                                         + r * this.fractions.get(gtuType).get(link).get(next);
  151.                             }
  152.                             currentFractions.put(link, f);
  153.                             break;
  154.                         }
  155.                     }
  156.                 }
  157.                 return Draw.drawWeighted(currentFractions, this.random);
  158.             }
  159.         }
  160.         // GTU Type not defined, distribute by number of lanes (or weight = 1.0 if not a CrossSectionLink)
  161.         boolean fractionAdded = false;
  162.         for (Link link : this.node.getLinks())
  163.         {
  164.             if ((link.getStartNode().equals(this.node)))
  165.             {
  166.                 if (link instanceof CrossSectionLink)
  167.                 {
  168.                     int n = ((CrossSectionLink) link).getLanes().size();
  169.                     if (n > 0)
  170.                     {
  171.                         fractionAdded = true;
  172.                         addFraction(link, gtuType, n);
  173.                     }
  174.                 }
  175.                 else
  176.                 {
  177.                     fractionAdded = true;
  178.                     addFraction(link, gtuType, 1.0);
  179.                 }
  180.             }
  181.         }
  182.         Throw.when(!fractionAdded, UnsupportedOperationException.class,
  183.                 "Split fraction on node %s cannot be derived for gtuType %s as there are no outgoing links.", this.node,
  184.                 gtuType);
  185.         // redraw with the information that was just set
  186.         return draw(gtuType);
  187.     }

  188.     /** {@inheritDoc} */
  189.     @Override
  190.     public int hashCode()
  191.     {
  192.         final int prime = 31;
  193.         int result = 1;
  194.         result = prime * result + ((this.fractions == null) ? 0 : this.fractions.hashCode());
  195.         result = prime * result + ((this.node == null) ? 0 : this.node.hashCode());
  196.         return result;
  197.     }

  198.     /** {@inheritDoc} */
  199.     @Override
  200.     public boolean equals(final Object obj)
  201.     {
  202.         if (this == obj)
  203.         {
  204.             return true;
  205.         }
  206.         if (obj == null)
  207.         {
  208.             return false;
  209.         }
  210.         if (getClass() != obj.getClass())
  211.         {
  212.             return false;
  213.         }
  214.         SplitFraction other = (SplitFraction) obj;
  215.         if (this.fractions == null)
  216.         {
  217.             if (other.fractions != null)
  218.             {
  219.                 return false;
  220.             }
  221.         }
  222.         else if (!this.fractions.equals(other.fractions))
  223.         {
  224.             return false;
  225.         }
  226.         if (this.node == null)
  227.         {
  228.             if (other.node != null)
  229.             {
  230.                 return false;
  231.             }
  232.         }
  233.         else if (!this.node.equals(other.node))
  234.         {
  235.             return false;
  236.         }
  237.         return true;
  238.     }

  239.     /** {@inheritDoc} */
  240.     @Override
  241.     public String toString()
  242.     {
  243.         return "SplitFraction [node=" + this.node + ", fractions=" + this.fractions + "]";
  244.     }

  245. }