View Javadoc
1   package org.opentrafficsim.road.gtu.strategical.od;
2   
3   import java.util.HashMap;
4   import java.util.Iterator;
5   import java.util.LinkedHashMap;
6   import java.util.Map;
7   import java.util.TreeMap;
8   
9   import org.djunits.unit.DurationUnit;
10  import org.djunits.value.StorageType;
11  import org.djunits.value.ValueException;
12  import org.djunits.value.vdouble.scalar.Duration;
13  import org.djunits.value.vdouble.vector.DurationVector;
14  import org.djutils.exceptions.Throw;
15  import org.opentrafficsim.core.gtu.GTUType;
16  import org.opentrafficsim.core.math.Draw;
17  import org.opentrafficsim.core.network.Link;
18  import org.opentrafficsim.core.network.Node;
19  import org.opentrafficsim.road.network.lane.CrossSectionLink;
20  
21  import nl.tudelft.simulation.dsol.simulators.DEVSSimulatorInterface;
22  import nl.tudelft.simulation.jstats.streams.StreamInterface;
23  
24  /**
25   * Split fraction at a node with fractions per link, optionally per gtu type.
26   * <p>
27   * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
28   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
29   * <p>
30   * @version $Revision$, $LastChangedDate$, by $Author$, initial version 4 mei 2017 <br>
31   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
32   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
33   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
34   */
35  public class SplitFraction
36  {
37  
38      /** Node. */
39      private final Node node;
40  
41      /** Interpolation. */
42      private final Interpolation interpolation;
43  
44      /** Random stream. */
45      private final StreamInterface random;
46  
47      /** Simulator. */
48      private final DEVSSimulatorInterface.TimeDoubleUnit simulator;
49  
50      /** Map of fractions by GTUType and Link. */
51      private final Map<GTUType, Map<Link, Map<Duration, Double>>> fractions = new HashMap<>();
52  
53      /**
54       * Constructor.
55       * @param node Node; node
56       * @param interpolation Interpolation; interpolation
57       * @param random StreamInterface; random stream
58       * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; simulator
59       */
60      public SplitFraction(final Node node, final Interpolation interpolation, final StreamInterface random,
61              final DEVSSimulatorInterface.TimeDoubleUnit simulator)
62      {
63          this.node = node;
64          this.interpolation = interpolation;
65          this.random = random;
66          this.simulator = simulator;
67      }
68  
69      /**
70       * Add fraction to link, this will apply to {@code GTUType.ALL} and all time.
71       * @param link Link; link
72       * @param fraction double; fraction
73       */
74      public void addFraction(final Link link, final double fraction)
75      {
76          addFraction(link, link.getNetwork().getGtuType(GTUType.DEFAULTS.VEHICLE), fraction);
77      }
78  
79      /**
80       * Add fraction to link for gtu type, this will apply to all time.
81       * @param link Link; link
82       * @param gtuType GTUType; gtu type
83       * @param fraction double; fraction
84       */
85      public void addFraction(final Link link, final GTUType gtuType, final double fraction)
86      {
87          double[] fracs = new double[2];
88          fracs[0] = fraction;
89          fracs[1] = fraction;
90          DurationVector time;
91          try
92          {
93              double[] t = new double[2];
94              t[1] = Double.MAX_VALUE;
95              time = new DurationVector(t, DurationUnit.SI, StorageType.DENSE);
96          }
97          catch (ValueException exception)
98          {
99              // should not happen, input is not null
100             throw new RuntimeException("Input null while creating duration vector.", exception);
101         }
102         addFraction(link, gtuType, time, fracs);
103     }
104 
105     /**
106      * Add fraction to link over time, this will apply to {@code GTUType.ALL}.
107      * @param link Link; link
108      * @param time DurationVector; time
109      * @param fraction double[]; fraction
110      */
111     public void addFraction(final Link link, final DurationVector time, final double[] fraction)
112     {
113         addFraction(link, link.getNetwork().getGtuType(GTUType.DEFAULTS.VEHICLE), time, fraction);
114     }
115 
116     /**
117      * Add fraction to link over time for gtu type.
118      * @param link Link; link
119      * @param gtuType GTUType; gtu type
120      * @param time DurationVector; time
121      * @param fraction double[]; fraction
122      */
123     public void addFraction(final Link link, final GTUType gtuType, final DurationVector time, final double[] fraction)
124     {
125         Throw.when(time.size() != fraction.length, IllegalArgumentException.class,
126                 "Time vector and fraction array require equal length.");
127         Throw.when(!this.node.getLinks().contains(link), IllegalArgumentException.class, "Link %s is not connected to node %s.",
128                 link, this.node);
129         for (double f : fraction)
130         {
131             Throw.when(f < 0.0, IllegalArgumentException.class, "Fraction should be larger than 0.0.");
132         }
133         if (this.fractions.containsKey(gtuType))
134         {
135             this.fractions.put(gtuType, new HashMap<>());
136         }
137         this.fractions.get(gtuType).put(link, new TreeMap<>());
138         for (int i = 0; i <= time.size(); i++)
139         {
140             try
141             {
142                 this.fractions.get(gtuType).get(link).put(time.get(i), fraction[i]);
143             }
144             catch (ValueException exception)
145             {
146                 // should not happen, sizes are checked
147                 throw new RuntimeException("Index out of range.", exception);
148             }
149         }
150     }
151 
152     /**
153      * Draw next link based on split fractions. If no fractions were defined, split fractions are determined based on the number
154      * of lanes per link.
155      * @param gtuType GTUType; gtuType
156      * @return next link
157      */
158     public Link draw(final GTUType gtuType)
159     {
160         for (GTUType gtu : this.fractions.keySet())
161         {
162             if (gtuType.isOfType(gtu))
163             {
164                 Map<Link, Double> currentFractions = new LinkedHashMap<>();
165                 double t = this.simulator.getSimulatorTime().si;
166                 for (Link link : this.fractions.get(gtu).keySet())
167                 {
168                     Iterator<Duration> iterator = this.fractions.get(gtu).get(link).keySet().iterator();
169                     Duration prev = iterator.next();
170                     while (iterator.hasNext())
171                     {
172                         Duration next = iterator.next();
173                         if (prev.si <= t && t < next.si)
174                         {
175                             // TODO let interpolation interpolate itself
176                             double f;
177                             if (this.interpolation.equals(Interpolation.STEPWISE))
178                             {
179                                 f = this.fractions.get(gtuType).get(link).get(prev);
180                             }
181                             else
182                             {
183                                 double r = (t - prev.si) / (next.si - prev.si);
184                                 f = (1 - r) * this.fractions.get(gtuType).get(link).get(prev)
185                                         + r * this.fractions.get(gtuType).get(link).get(next);
186                             }
187                             currentFractions.put(link, f);
188                             break;
189                         }
190                     }
191                 }
192                 return Draw.drawWeighted(currentFractions, this.random);
193             }
194         }
195         // GTU Type not defined, distribute by number of lanes (or weight = 1.0 if not a CrossSectionLink)
196         boolean fractionAdded = false;
197         for (Link link : this.node.getLinks())
198         {
199             if ((link.getStartNode().equals(this.node) && link.getDirectionality(gtuType).isForwardOrBoth())
200                     || (link.getEndNode().equals(this.node) && link.getDirectionality(gtuType).isBackwardOrBoth()))
201             {
202                 if (link instanceof CrossSectionLink)
203                 {
204                     int n = ((CrossSectionLink) link).getLanes().size();
205                     if (n > 0)
206                     {
207                         fractionAdded = true;
208                         addFraction(link, gtuType, n);
209                     }
210                 }
211                 else
212                 {
213                     fractionAdded = true;
214                     addFraction(link, gtuType, 1.0);
215                 }
216             }
217         }
218         Throw.when(!fractionAdded, UnsupportedOperationException.class,
219                 "Split fraction on node %s cannot be derived for gtuType %s as there are no outgoing links.", this.node,
220                 gtuType);
221         // redraw with the information that was just set
222         return draw(gtuType);
223     }
224 
225     /** {@inheritDoc} */
226     @Override
227     public int hashCode()
228     {
229         final int prime = 31;
230         int result = 1;
231         result = prime * result + ((this.fractions == null) ? 0 : this.fractions.hashCode());
232         result = prime * result + ((this.node == null) ? 0 : this.node.hashCode());
233         return result;
234     }
235 
236     /** {@inheritDoc} */
237     @Override
238     public boolean equals(final Object obj)
239     {
240         if (this == obj)
241         {
242             return true;
243         }
244         if (obj == null)
245         {
246             return false;
247         }
248         if (getClass() != obj.getClass())
249         {
250             return false;
251         }
252         SplitFraction other = (SplitFraction) obj;
253         if (this.fractions == null)
254         {
255             if (other.fractions != null)
256             {
257                 return false;
258             }
259         }
260         else if (!this.fractions.equals(other.fractions))
261         {
262             return false;
263         }
264         if (this.node == null)
265         {
266             if (other.node != null)
267             {
268                 return false;
269             }
270         }
271         else if (!this.node.equals(other.node))
272         {
273             return false;
274         }
275         return true;
276     }
277 
278     /** {@inheritDoc} */
279     @Override
280     public String toString()
281     {
282         return "SplitFraction [node=" + this.node + ", fractions=" + this.fractions + "]";
283     }
284 
285 }