1 package org.opentrafficsim.road.gtu.generator.headway;
2
3 import org.djunits.value.vdouble.scalar.Duration;
4 import org.djunits.value.vdouble.scalar.Time;
5 import org.opentrafficsim.core.distributions.Generator;
6 import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
7
8 import nl.tudelft.simulation.jstats.distributions.DistNormal;
9 import nl.tudelft.simulation.jstats.streams.StreamInterface;
10
11
12
13
14
15
16
17
18
19
20
21 public class ArrivalsHeadwayGenerator implements Generator<Duration>
22 {
23
24
25 private final Arrivals arrivals;
26
27
28 private final OtsSimulatorInterface simulator;
29
30
31 private final StreamInterface stream;
32
33
34 private final HeadwayDistribution distribution;
35
36
37 private boolean first = true;
38
39
40
41
42
43
44
45 public ArrivalsHeadwayGenerator(final Arrivals arrivals, final OtsSimulatorInterface simulator,
46 final StreamInterface stream, final HeadwayDistribution distribution)
47 {
48 this.arrivals = arrivals;
49 this.simulator = simulator;
50 this.stream = stream;
51 this.distribution = distribution;
52 }
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85 @Override
86 public Duration draw()
87 {
88 Time now = this.simulator.getSimulatorAbsTime();
89
90 Time t1 = now;
91 double f1 = this.arrivals.getFrequency(t1, true).si;
92 Time t2 = this.arrivals.nextTimeSlice(t1);
93 if (t2 == null)
94 {
95 return null;
96 }
97 double f2 = this.arrivals.getFrequency(t2, false).si;
98
99 double rem = this.distribution.draw(this.stream);
100 if (this.first)
101 {
102
103 rem *= this.stream.nextDouble();
104 this.first = false;
105 }
106
107 while (rem > 0.0)
108 {
109
110 double dt = t2.si - t1.si;
111 double t;
112 double slope = (f2 - f1) / dt;
113 if (Math.abs(slope) < 1e-12)
114 {
115 if (f1 > 0.0)
116 {
117 t = rem / f1;
118 }
119 else
120 {
121 t = Double.POSITIVE_INFINITY;
122 }
123 }
124 else
125 {
126
127 double sqrt = 2 * slope * rem + f1 * f1;
128 if (sqrt >= 0.0)
129 {
130 t = (-f1 + Math.sqrt(sqrt)) / slope;
131 }
132 else
133 {
134 t = Double.POSITIVE_INFINITY;
135 }
136 }
137 if (t > dt)
138 {
139
140 rem -= dt * (f1 + f2) / 2;
141 t1 = t2;
142 t2 = this.arrivals.nextTimeSlice(t1);
143 if (t2 == null)
144 {
145 return null;
146 }
147 f1 = this.arrivals.getFrequency(t1, true).si;
148 f2 = this.arrivals.getFrequency(t2, false).si;
149 }
150 else
151 {
152
153 return Duration.instantiateSI(t1.si + t - now.si);
154 }
155 }
156 throw new RuntimeException("Exception while determining headway from Arrivals.");
157 }
158
159 @Override
160 public String toString()
161 {
162 return "ArrivalsHeadwayGenerator [arrivals=" + this.arrivals + ", simulator=" + this.simulator + ", stream="
163 + this.stream + ", distribution=" + this.distribution + ", first=" + this.first + "]";
164 }
165
166
167
168
169
170
171
172
173
174
175
176
177 public interface HeadwayDistribution
178 {
179
180
181 HeadwayDistribution CONSTANT = new HeadwayDistribution()
182 {
183 @Override
184 public double draw(final StreamInterface randomStream)
185 {
186 return 1.0;
187 }
188
189 @Override
190 public String getName()
191 {
192 return "CONSTANT";
193 }
194 };
195
196
197 HeadwayDistribution EXPONENTIAL = new HeadwayDistribution()
198 {
199 @Override
200 public double draw(final StreamInterface randomStream)
201 {
202 return -Math.log(randomStream.nextDouble());
203 }
204
205 @Override
206 public String getName()
207 {
208 return "EXPONENTIAL";
209 }
210 };
211
212
213 HeadwayDistribution UNIFORM = new HeadwayDistribution()
214 {
215 @Override
216 public double draw(final StreamInterface randomStream)
217 {
218 return 2.0 * randomStream.nextDouble();
219 }
220
221 @Override
222 public String getName()
223 {
224 return "UNIFORM";
225 }
226 };
227
228
229 HeadwayDistribution TRIANGULAR = new HeadwayDistribution()
230 {
231 @Override
232 public double draw(final StreamInterface randomStream)
233 {
234 double r = randomStream.nextDouble();
235 if (r < .5)
236 {
237 return Math.sqrt(r * 2.0);
238 }
239 return 2.0 - Math.sqrt((1.0 - r) * 2.0);
240 }
241
242 @Override
243 public String getName()
244 {
245 return "TRIANGULAR";
246 }
247 };
248
249
250 HeadwayDistribution TRI_EXP = new HeadwayDistribution()
251 {
252 @Override
253 public double draw(final StreamInterface randomStream)
254 {
255 double r = randomStream.nextDouble();
256 if (r < .5)
257 {
258 return Math.sqrt(r * 2.0);
259 }
260 return 1.0 - Math.log((1.0 - r) * 2.0) / 3.0;
261
262 }
263
264 @Override
265 public String getName()
266 {
267 return "TRI_EXP";
268 }
269 };
270
271
272 HeadwayDistribution LOGNORMAL = new HeadwayDistribution()
273 {
274
275 private final double mu = Math.log(1.0 / Math.sqrt(2.0));
276
277
278 private final double sigma = Math.sqrt(Math.log(2.0));
279
280 @Override
281 public double draw(final StreamInterface randomStream)
282 {
283 return Math.exp(new DistNormal(randomStream, this.mu, this.sigma).draw());
284 }
285
286 @Override
287 public String getName()
288 {
289 return "LOGNORMAL";
290 }
291 };
292
293
294
295
296
297
298
299 double draw(StreamInterface randomStream);
300
301
302
303
304
305 String getName();
306
307 }
308
309 }