1 package org.opentrafficsim.road.od;
2
3 import org.djunits.unit.DurationUnit;
4 import org.djunits.unit.FrequencyUnit;
5 import org.djunits.value.ValueRuntimeException;
6 import org.djunits.value.vdouble.scalar.Duration;
7 import org.djunits.value.vdouble.scalar.Frequency;
8 import org.djunits.value.vdouble.vector.DurationVector;
9 import org.djunits.value.vdouble.vector.FrequencyVector;
10 import org.opentrafficsim.base.OtsRuntimeException;
11
12 /**
13 * Interpolation of demand.
14 * <p>
15 * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
16 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
17 * </p>
18 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
19 * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
20 * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
21 */
22 public enum Interpolation
23 {
24
25 /** Stepwise interpolation of demand. */
26 STEPWISE
27 {
28 @Override
29 Frequency interpolate(final Frequency frequency0, final Duration time0, final Frequency frequency1,
30 final Duration time1, final Duration time)
31 {
32 return frequency0;
33 }
34
35 @Override
36 int integrate(final Frequency frequency0, final Duration time0, final Frequency frequency1, final Duration time1)
37 {
38 return (int) (frequency0.getInUnit(FrequencyUnit.PER_HOUR)
39 * (time1.getInUnit(DurationUnit.HOUR) - time0.getInUnit(DurationUnit.HOUR)));
40 }
41
42 @Override
43 public String toString()
44 {
45 return "STEPWISE";
46 }
47 },
48
49 /** Linear interpolation of demand. */
50 LINEAR
51 {
52 @Override
53 Frequency interpolate(final Frequency frequency0, final Duration time0, final Frequency frequency1,
54 final Duration time1, final Duration time)
55 {
56 return Frequency.interpolate(frequency0, frequency1, (time.si - time0.si) / (time1.si - time0.si));
57 }
58
59 @Override
60 int integrate(final Frequency frequency0, final Duration time0, final Frequency frequency1, final Duration time1)
61 {
62 return (int) (0.5 * (frequency0.getInUnit(FrequencyUnit.PER_HOUR) + frequency1.getInUnit(FrequencyUnit.PER_HOUR))
63 * (time1.getInUnit(DurationUnit.HOUR) - time0.getInUnit(DurationUnit.HOUR)));
64 }
65
66 @Override
67 public String toString()
68 {
69 return "LINEAR";
70 }
71 };
72
73 /**
74 * Interpolate between given frequencies.
75 * @param frequency0 frequency at {@code time0}
76 * @param time0 time of {@code frequency0} (≤ {@code time})
77 * @param frequency1 frequency at {@code time1}
78 * @param time1 time of {@code frequency1} (> {@code time})
79 * @param time {@code time0} ≤ {@code time} < {@code time1}
80 * @return interpolated frequency
81 */
82 abstract Frequency interpolate(Frequency frequency0, Duration time0, Frequency frequency1, Duration time1, Duration time);
83
84 /**
85 * Integrates to the number of trips in given period.
86 * @param frequency0 frequency at {@code time0}
87 * @param time0 time of {@code frequency0} (≤ {@code time})
88 * @param frequency1 frequency at {@code time1}
89 * @param time1 time of {@code frequency1} (> {@code time})
90 * @return number of trips in given period
91 */
92 abstract int integrate(Frequency frequency0, Duration time0, Frequency frequency1, Duration time1);
93
94 /**
95 * Returns whether this is step-wise interpolation.
96 * @return whether this is step-wise interpolation
97 */
98 public boolean isStepWise()
99 {
100 return this.equals(STEPWISE);
101 }
102
103 /**
104 * Returns whether this is linear interpolation.
105 * @return whether this is linear interpolation
106 */
107 public boolean isLinear()
108 {
109 return this.equals(LINEAR);
110 }
111
112 /**
113 * Returns interpolated value from array at given time. If time is outside of the vector range, 0 is returned.
114 * @param time time to determine the frequency at
115 * @param demandVector demand vector
116 * @param timeVector time vector
117 * @param sliceStart whether the time is at the start of an arbitrary time slice
118 * @return interpolated value from array at given time, or 0 when time is outside of range
119 */
120 public final Frequency interpolateVector(final Duration time, final FrequencyVector demandVector,
121 final DurationVector timeVector, final boolean sliceStart)
122 {
123 try
124 {
125 // empty data or before start or after end, return 0
126 // case 1: t < t(0)
127 // case 2: sliceEnd & t == t(0), i.e. end of no-demand time before time array
128 // case 3: sliceStart & t == t(end), i.e. start of no-demand time after time array
129 // case 4: t > t(end)
130 if (timeVector.size() == 0 || (sliceStart ? time.lt(timeVector.get(0)) : time.le(timeVector.get(0))) || (sliceStart
131 ? time.ge(timeVector.get(timeVector.size() - 1)) : time.gt(timeVector.get(timeVector.size() - 1))))
132 {
133 return new Frequency(0.0, FrequencyUnit.PER_HOUR); // Frequency.ZERO give "Hz" which is not nice for flow
134 }
135 // interpolate
136 for (int i = 0; i < timeVector.size() - 1; i++)
137 {
138 // cases where we can take the slice from i to i+1
139 // case 1: sliceStart & t(i+1) > t [edge case: t(i) = t]
140 // case 2: sliceEnd & t(i+1) >= t [edge case: t(i+1) = t]
141 if (sliceStart ? timeVector.get(i + 1).gt(time) : timeVector.get(i + 1).ge(time))
142 {
143 return interpolate(demandVector.get(i), timeVector.get(i), demandVector.get(i + 1), timeVector.get(i + 1),
144 time);
145 }
146 }
147 }
148 catch (ValueRuntimeException ve)
149 {
150 // should not happen, vector lengths are checked when given is input
151 throw new OtsRuntimeException("Index out of bounds.", ve);
152 }
153 // should not happen
154 throw new OtsRuntimeException("Demand interpolation failed.");
155 }
156
157 }