View Javadoc
1   package org.opentrafficsim.road.gtu.generator;
2   
3   import java.rmi.RemoteException;
4   
5   import org.djunits.unit.TimeUnit;
6   import org.djunits.value.ValueException;
7   import org.djunits.value.vdouble.scalar.Duration;
8   import org.djunits.value.vdouble.scalar.Frequency;
9   import org.djunits.value.vdouble.scalar.Time;
10  import org.djunits.value.vdouble.vector.FrequencyVector;
11  import org.djunits.value.vdouble.vector.TimeVector;
12  import org.opentrafficsim.core.distributions.Generator;
13  import org.opentrafficsim.core.distributions.ProbabilityException;
14  import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
15  import org.opentrafficsim.core.gtu.behavioralcharacteristics.ParameterException;
16  import org.opentrafficsim.road.gtu.strategical.od.Interpolation;
17  
18  import nl.tudelft.simulation.language.Throw;
19  
20  /**
21   * <p>
22   * Copyright (c) 2013-2017 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
23   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
24   * <p>
25   * @version $Revision$, $LastChangedDate$, by $Author$, initial version 17 nov. 2016 <br>
26   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
27   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
28   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
29   */
30  // TODO Link this class to an OD
31  public class HeadwayGeneratorDemand implements Generator<Duration>
32  {
33  
34      /** Interpolation of demand. */
35      private final Interpolation interpolation;
36  
37      /** Vector of time. */
38      private final TimeVector timeVector;
39  
40      /** Vector of flow values. */
41      private final FrequencyVector demandVector;
42  
43      /** Simulator. */
44      private final OTSSimulatorInterface simulator;
45  
46      /** Stream name of headway generation. */
47      private static final String HEADWAY_STREAM = "headwayGeneration";
48  
49      /**
50       * @param timeVector a time vector
51       * @param demandVector the corresponding demand vector
52       * @param simulator the simulator
53       */
54      public HeadwayGeneratorDemand(final TimeVector timeVector, final FrequencyVector demandVector,
55              final OTSSimulatorInterface simulator)
56      {
57          this(timeVector, demandVector, simulator, Interpolation.STEPWISE);
58      }
59  
60      /**
61       * @param timeVector a time vector
62       * @param demandVector the corresponding demand vector
63       * @param simulator the simulator
64       * @param interpolation interpolation type
65       */
66      public HeadwayGeneratorDemand(final TimeVector timeVector, final FrequencyVector demandVector,
67              final OTSSimulatorInterface simulator, final Interpolation interpolation)
68      {
69          Throw.whenNull(timeVector, "Time vector may not be null.");
70          Throw.whenNull(demandVector, "Demand vector may not be null.");
71          Throw.whenNull(simulator, "Simulator may not be null.");
72          Throw.whenNull(interpolation, "Interpolation may not be null.");
73          try
74          {
75              Throw.whenNull(simulator.getReplication().getStream(HEADWAY_STREAM),
76                      "Could not obtain random stream '" + HEADWAY_STREAM + "'.");
77          }
78          catch (RemoteException exception)
79          {
80              throw new RuntimeException("Could not obtain replication.", exception);
81          }
82          for (int i = 0; i < timeVector.size() - 1; i++)
83          {
84              try
85              {
86                  Throw.when(timeVector.get(i).ge(timeVector.get(i + 1)), IllegalArgumentException.class,
87                          "Time vector is not increasing.");
88              }
89              catch (ValueException exception)
90              {
91                  throw new RuntimeException(
92                          "Value out of range of time vector. Note that HeadwayGenerator does not create a safe copy.",
93                          exception);
94              }
95          }
96          Throw.when(timeVector.size() != demandVector.size(), IllegalArgumentException.class,
97                  "Time and flow vector should be of the same size.");
98          Throw.when(timeVector.size() < 2, IllegalArgumentException.class, "Time and flow vector should be at least of size 2.");
99          this.timeVector = timeVector;
100         this.demandVector = demandVector;
101         this.simulator = simulator;
102         this.interpolation = interpolation;
103     }
104 
105     /** {@inheritDoc} */
106     @Override
107     public Duration draw() throws ProbabilityException, ParameterException
108     {
109         Time time = this.simulator.getSimulatorTime().getTime();
110         try
111         {
112             Throw.when(time.lt(this.timeVector.get(0)), IllegalArgumentException.class,
113                     "Cannot return a headway at time before first time in vector.");
114 
115             // get time period of current time
116             int i = 0;
117             while (this.timeVector.get(i + 1).lt(time) && i < this.timeVector.size() - 1)
118             {
119                 i++;
120             }
121             try
122             {
123                 return nextArrival(i, time.minus(this.timeVector.get(i)), 1.0).minus(time);
124             }
125             catch (RemoteException exception)
126             {
127                 throw new RuntimeException("Could not obtain replication.", exception);
128             }
129         }
130         catch (ValueException exception)
131         {
132             throw new RuntimeException(
133                     "Value out of range of time or demand vector. Note that HeadwayGenerator does not create safe copies.",
134                     exception);
135         }
136     }
137 
138     /**
139      * Recursive determination of the next arrival time. Each recursion moves to the next time period. This occurs if a randomly
140      * determined arrival falls outside of a time period, or when demand in a time period is 0.
141      * @param i index of time period
142      * @param start reference time from start of period i, pertains to previous arrival, or zero during recursion
143      * @param fractionRemaining remaining fraction of headway to apply due to time in earlier time periods
144      * @return time of next arrival
145      * @throws ValueException in case of an illegal time vector
146      * @throws RemoteException in case of not being able to retrieve the replication
147      */
148     private Time nextArrival(final int i, final Duration start, final double fractionRemaining)
149             throws ValueException, RemoteException
150     {
151 
152         // escape if beyond specified time by infinite next arrival (= no traffic)
153         if (i == this.timeVector.size() - 1)
154         {
155             return new Time(Double.POSITIVE_INFINITY, TimeUnit.BASE);
156         }
157 
158         // skip zero-demand periods
159         if (this.demandVector.get(i).equals(Frequency.ZERO))
160         {
161             // after zero-demand, the next headway is a random fraction of a random headway as there is no previous arrival
162             return nextArrival(i + 1, Duration.ZERO, this.simulator.getReplication().getStream(HEADWAY_STREAM).nextDouble());
163         }
164 
165         // calculate headway from demand
166         Frequency demand;
167         if (this.interpolation.isStepWise())
168         {
169             demand = this.demandVector.get(i);
170         }
171         else
172         {
173             double f = start.si / (this.timeVector.get(i + 1).si - this.timeVector.get(i).si);
174             demand = Frequency.interpolate(this.demandVector.get(i), this.demandVector.get(i + 1), f);
175         }
176         double t = -Math.log(this.simulator.getReplication().getStream(HEADWAY_STREAM).nextDouble()) / demand.si;
177 
178         // calculate arrival
179         Time arrival = new Time(this.timeVector.get(i).si + start.si + t * fractionRemaining, TimeUnit.BASE);
180 
181         // go to next period if arrival is beyond current period
182         if (arrival.gt(this.timeVector.get(i + 1)))
183         {
184             double inStep = this.timeVector.get(i + 1).si - (this.timeVector.get(i).si + start.si);
185             return nextArrival(i + 1, Duration.ZERO, fractionRemaining - inStep / t);
186         }
187 
188         return arrival;
189 
190     }
191 
192 }