1 package org.opentrafficsim.road.gtu.strategical.od;
2
3 import java.util.List;
4
5 import org.djunits.unit.DurationUnit;
6 import org.djunits.unit.FrequencyUnit;
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-2017 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] = trips[i]
90 / (timeVector.get(i + 1).getInUnit(DurationUnit.HOUR) - timeVector.get(i).getInUnit(DurationUnit.HOUR));
91 }
92 // last value can remain zero as initialized
93 putDemandVector(origin, destination, category, new FrequencyVector(flow, FrequencyUnit.PER_HOUR, StorageType.DENSE),
94 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),
163 time.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, final int periodIndex,
187 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 = trips / (time.get(periodIndex + 1).getInUnit(DurationUnit.HOUR)
199 - time.get(periodIndex).getInUnit(DurationUnit.HOUR));
200 double[] dem = demand.getValuesInUnit(FrequencyUnit.PER_HOUR);
201 dem[periodIndex] += additionalDemand;
202 putDemandVector(origin, destination, category, new FrequencyVector(dem, FrequencyUnit.PER_HOUR, StorageType.DENSE),
203 time, Interpolation.STEPWISE);
204 }
205 catch (ValueException exception)
206 {
207 // should not happen as the index was checked
208 throw new RuntimeException("Could not get number of trips.", exception);
209 }
210 }
211
212 /**
213 * Calculates total number of trips over time for given origin.
214 * @param origin origin
215 * @return total number of trips over time for given origin
216 * @throws IllegalArgumentException if origin is not part of the OD matrix
217 * @throws NullPointerException if origin is null
218 */
219 public final int originTotal(final Node origin)
220 {
221 int sum = 0;
222 for (Node destination : getDestinations())
223 {
224 sum += originDestinationTotal(origin, destination);
225 }
226 return sum;
227 }
228
229 /**
230 * Calculates total number of trips over time for given destination.
231 * @param destination destination
232 * @return total number of trips over time for given destination
233 * @throws IllegalArgumentException if destination is not part of the OD matrix
234 * @throws NullPointerException if destination is null
235 */
236 public final int destinationTotal(final Node destination)
237 {
238 int sum = 0;
239 for (Node origin : getOrigins())
240 {
241 sum += originDestinationTotal(origin, destination);
242 }
243 return sum;
244 }
245
246 /**
247 * Calculates total number of trips over time for the complete matrix.
248 * @return total number of trips over time for the complete matrix
249 */
250 public final int matrixTotal()
251 {
252 int sum = 0;
253 for (Node origin : getOrigins())
254 {
255 for (Node destination : getDestinations())
256 {
257 sum += originDestinationTotal(origin, destination);
258 }
259 }
260 return sum;
261 }
262
263 /**
264 * Calculates total number of trips over time for given origin-destination combination.
265 * @param origin origin
266 * @param destination destination
267 * @return total number of trips over time for given origin-destination combination
268 * @throws IllegalArgumentException if origin or destination is not part of the OD matrix
269 * @throws NullPointerException if an input is null
270 */
271 public final int originDestinationTotal(final Node origin, final Node destination)
272 {
273 int sum = 0;
274 for (Category category : getCategories(origin, destination))
275 {
276 DurationVector time = getTimeVector(origin, destination, category);
277 FrequencyVector demand = getDemandVector(origin, destination, category);
278 Interpolation interpolation = getInterpolation(origin, destination, category);
279 for (int i = 0; i < time.size() - 1; i++)
280 {
281 try
282 {
283 sum += interpolation.integrate(demand.get(i), time.get(i), demand.get(i + 1), time.get(i + 1));
284 }
285 catch (ValueException exception)
286 {
287 // should not happen as we loop over the array length
288 throw new RuntimeException("Could not determine total trips over time.", exception);
289 }
290 }
291 }
292 return sum;
293 }
294
295 /** {@inheritDoc} */
296 public final String toString()
297 {
298 return "ODMatrixTrips [" + getOrigins().size() + " origins, " + getDestinations().size() + " destinations, "
299 + getCategorization() + " ]";
300 }
301
302 }