1 package org.opentrafficsim.road.gtu.strategical.od;
2
3 import java.util.List;
4
5 import org.djunits.unit.FrequencyUnit;
6 import org.djunits.unit.TimeUnit;
7 import org.djunits.value.StorageType;
8 import org.djunits.value.ValueException;
9 import org.djunits.value.vdouble.vector.DurationVector;
10 import org.djunits.value.vdouble.vector.FrequencyVector;
11 import org.opentrafficsim.core.network.Node;
12
13 import nl.tudelft.simulation.language.Throw;
14
15 /**
16 * Extension of ODMatrix where all input and output can be given in number of trips. All data that is defined in number of trips
17 * has Interpolation.STEPWISE.
18 * <p>
19 * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
20 * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
21 * <p>
22 * @version $Revision$, $LastChangedDate$, by $Author$, initial version Sep 28, 2016 <br>
23 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
24 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
25 * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
26 */
27 // TODO pce (i.e. passenger car equivalent) instead of veh's
28 public class ODMatrixTrips extends ODMatrix
29 {
30
31 /** */
32 private static final long serialVersionUID = 20160928L;
33
34 /**
35 * Constructs an OD matrix based on trips.
36 * @param id id
37 * @param origins origin nodes
38 * @param destinations destination nodes
39 * @param categorization categorization of data
40 * @param globalTimeVector default time
41 * @param globalInterpolation interpolation of demand data
42 * @throws NullPointerException if any input is null
43 */
44 public ODMatrixTrips(final String id, final List<Node> origins, final List<Node> destinations,
45 final Categorization categorization, final DurationVector globalTimeVector, final Interpolation globalInterpolation)
46 {
47 super(id, origins, destinations, categorization, globalTimeVector, globalInterpolation);
48 }
49
50 /**
51 * @param origin origin
52 * @param destination destination
53 * @param category category
54 * @param trips trip data, length has to be equal to the global time vector - 1
55 * @throws IllegalArgumentException if origin or destination is not part of the OD matrix
56 * @throws IllegalArgumentException if the category does not belong to the categorization
57 * @throws NullPointerException if an input is null
58 */
59 public final void putTripsVector(final Node origin, final Node destination, final Category category, final int[] trips)
60 {
61 putTripsVector(origin, destination, category, trips, getGlobalTimeVector());
62 }
63
64 /**
65 * Sets demand data by number of trips. Interpolation over time is stepwise.
66 * @param origin origin
67 * @param destination destination
68 * @param category category
69 * @param trips trip data, length has to be equal to the time vector - 1
70 * @param timeVector time vector
71 * @throws IllegalArgumentException if origin or destination is not part of the OD matrix
72 * @throws IllegalArgumentException if the category does not belong to the categorization
73 * @throws NullPointerException if an input is null
74 */
75 public final void putTripsVector(final Node origin, final Node destination, final Category category, final int[] trips,
76 final DurationVector timeVector)
77 {
78 // this is what we need here, other checks in putDemandVector
79 Throw.whenNull(trips, "Demand data may not be null.");
80 Throw.whenNull(timeVector, "Time vector may not be null.");
81 Throw.when(trips.length != timeVector.size() - 1, IllegalArgumentException.class,
82 "Trip data and time data have wrong lengths. Trip data should be 1 shorter than time data.");
83 // convert to flow
84 double[] flow = new double[timeVector.size()];
85 try
86 {
87 for (int i = 0; i < trips.length; i++)
88 {
89 flow[i] =
90 trips[i] / (timeVector.get(i + 1).getInUnit(TimeUnit.HOUR) - timeVector.get(i).getInUnit(TimeUnit.HOUR));
91 }
92 // last value can remain zero as initialized
93 putDemandVector(origin, destination, category, new FrequencyVector(flow, FrequencyUnit.PER_HOUR,
94 StorageType.DENSE), timeVector, Interpolation.STEPWISE);
95 }
96 catch (ValueException exception)
97 {
98 // should not happen as we check and then loop over the array length
99 throw new RuntimeException("Could not translate trip vector into demand vector.", exception);
100 }
101 }
102
103 /**
104 * @param origin origin
105 * @param destination destination
106 * @param category category
107 * @return trip data for given origin, destination and categorization, {@code null} if no data is given
108 * @throws IllegalArgumentException if origin or destination is not part of the OD matrix
109 * @throws IllegalArgumentException if the category does not belong to the categorization
110 * @throws NullPointerException if an input is null
111 */
112 public final int[] getTripsVector(final Node origin, final Node destination, final Category category)
113 {
114 FrequencyVector demand = getDemandVector(origin, destination, category);
115 if (demand == null)
116 {
117 return null;
118 }
119 int[] trips = new int[demand.size() - 1];
120 DurationVector time = getTimeVector(origin, destination, category);
121 Interpolation interpolation = getInterpolation(origin, destination, category);
122 for (int i = 0; i < trips.length; i++)
123 {
124 try
125 {
126 trips[i] = interpolation.integrate(demand.get(i), time.get(i), demand.get(i + 1), time.get(i + 1));
127 }
128 catch (ValueException exception)
129 {
130 // should not happen as we loop over the array length
131 throw new RuntimeException("Could not translate demand vector into trip vector.", exception);
132 }
133 }
134 return trips;
135 }
136
137 /**
138 * Returns the number of trips in the given time period.
139 * @param origin origin
140 * @param destination destination
141 * @param category category
142 * @param periodIndex index of time period
143 * @return demand for given origin, destination and categorization, at given time
144 * @throws IllegalArgumentException if origin or destination is not part of the OD matrix
145 * @throws IllegalArgumentException if the category does not belong to the categorization
146 * @throws IllegalArgumentException if the period is outside of the specified range
147 * @throws NullPointerException if an input is null
148 */
149 public final int getTrips(final Node origin, final Node destination, final Category category, final int periodIndex)
150 {
151 DurationVector time = getTimeVector(origin, destination, category);
152 if (time == null)
153 {
154 return 0;
155 }
156 Throw.when(periodIndex < 0 || periodIndex >= time.size() - 1, IllegalArgumentException.class,
157 "Period index out of range.");
158 FrequencyVector demand = getDemandVector(origin, destination, category);
159 Interpolation interpolation = getInterpolation(origin, destination, category);
160 try
161 {
162 return interpolation.integrate(demand.get(periodIndex), time.get(periodIndex), demand.get(periodIndex + 1), time
163 .get(periodIndex + 1));
164 }
165 catch (ValueException exception)
166 {
167 // should not happen as the index was checked
168 throw new RuntimeException("Could not get number of trips.", exception);
169 }
170 }
171
172 /**
173 * Adds a number of trips to given origin-destination combination, category and time period. This can only be done for data
174 * with stepwise interpolation.
175 * @param origin origin
176 * @param destination destination
177 * @param category category
178 * @param periodIndex index of time period
179 * @param trips trips to add (may be negative)
180 * @throws IllegalArgumentException if origin or destination is not part of the OD matrix
181 * @throws IllegalArgumentException if the category does not belong to the categorization
182 * @throws IllegalArgumentException if the period is outside of the specified range
183 * @throws UnsupportedOperationException if the interpolation of the data is not stepwise
184 * @throws NullPointerException if an input is null
185 */
186 public final void increaseTrips(final Node origin, final Node destination, final Category category,
187 final int periodIndex, final int trips)
188 {
189 Interpolation interpolation = getInterpolation(origin, destination, category);
190 Throw.when(!interpolation.equals(Interpolation.STEPWISE), UnsupportedOperationException.class,
191 "Can only increase the number of trips for data with stepwise interpolation.");
192 DurationVector time = getTimeVector(origin, destination, category);
193 Throw.when(periodIndex < 0 || periodIndex >= time.size() - 1, IllegalArgumentException.class,
194 "Period index out of range.");
195 FrequencyVector demand = getDemandVector(origin, destination, category);
196 try
197 {
198 double additionalDemand =
199 trips
200 / (time.get(periodIndex + 1).getInUnit(TimeUnit.HOUR) - time.get(periodIndex).getInUnit(TimeUnit.HOUR));
201 double[] dem = demand.getValuesInUnit(FrequencyUnit.PER_HOUR);
202 dem[periodIndex] += additionalDemand;
203 putDemandVector(origin, destination, category, new FrequencyVector(dem, FrequencyUnit.PER_HOUR,
204 StorageType.DENSE), time, Interpolation.STEPWISE);
205 }
206 catch (ValueException exception)
207 {
208 // should not happen as the index was checked
209 throw new RuntimeException("Could not get number of trips.", exception);
210 }
211 }
212
213 /**
214 * Calculates total number of trips over time for given origin.
215 * @param origin origin
216 * @return total number of trips over time for given origin
217 * @throws IllegalArgumentException if origin is not part of the OD matrix
218 * @throws NullPointerException if origin is null
219 */
220 public final int originTotal(final Node origin)
221 {
222 int sum = 0;
223 for (Node destination : getDestinations())
224 {
225 sum += originDestinationTotal(origin, destination);
226 }
227 return sum;
228 }
229
230 /**
231 * Calculates total number of trips over time for given destination.
232 * @param destination destination
233 * @return total number of trips over time for given destination
234 * @throws IllegalArgumentException if destination is not part of the OD matrix
235 * @throws NullPointerException if destination is null
236 */
237 public final int destinationTotal(final Node destination)
238 {
239 int sum = 0;
240 for (Node origin : getOrigins())
241 {
242 sum += originDestinationTotal(origin, destination);
243 }
244 return sum;
245 }
246
247 /**
248 * Calculates total number of trips over time for the complete matrix.
249 * @return total number of trips over time for the complete matrix
250 */
251 public final int matrixTotal()
252 {
253 int sum = 0;
254 for (Node origin : getOrigins())
255 {
256 for (Node destination : getDestinations())
257 {
258 sum += originDestinationTotal(origin, destination);
259 }
260 }
261 return sum;
262 }
263
264 /**
265 * Calculates total number of trips over time for given origin-destination combination.
266 * @param origin origin
267 * @param destination destination
268 * @return total number of trips over time for given origin-destination combination
269 * @throws IllegalArgumentException if origin or destination is not part of the OD matrix
270 * @throws NullPointerException if an input is null
271 */
272 public final int originDestinationTotal(final Node origin, final Node destination)
273 {
274 int sum = 0;
275 for (Category category : getCategories(origin, destination))
276 {
277 DurationVector time = getTimeVector(origin, destination, category);
278 FrequencyVector demand = getDemandVector(origin, destination, category);
279 Interpolation interpolation = getInterpolation(origin, destination, category);
280 for (int i = 0; i < time.size() - 1; i++)
281 {
282 try
283 {
284 sum += interpolation.integrate(demand.get(i), time.get(i), demand.get(i + 1), time.get(i + 1));
285 }
286 catch (ValueException exception)
287 {
288 // should not happen as we loop over the array length
289 throw new RuntimeException("Could not determine total trips over time.", exception);
290 }
291 }
292 }
293 return sum;
294 }
295
296 /** {@inheritDoc} */
297 public final String toString()
298 {
299 return "ODMatrixTrips [" + getOrigins().size() + " origins, " + getDestinations().size() + " destinations, "
300 + getCategorization() + " ]";
301 }
302
303 }