1 package org.opentrafficsim.road.gtu.generator;
2
3 import java.util.LinkedHashMap;
4 import java.util.Map;
5 import java.util.PriorityQueue;
6 import java.util.Queue;
7 import java.util.Set;
8 import java.util.SortedMap;
9 import java.util.TreeMap;
10
11 import javax.naming.NamingException;
12
13 import org.djunits.value.ValueRuntimeException;
14 import org.djunits.value.storage.StorageType;
15 import org.djunits.value.vdouble.scalar.Frequency;
16 import org.djunits.value.vdouble.scalar.Time;
17 import org.djunits.value.vdouble.vector.FrequencyVector;
18 import org.djunits.value.vdouble.vector.TimeVector;
19 import org.djunits.value.vdouble.vector.base.DoubleVector;
20 import org.djutils.exceptions.Throw;
21 import org.opentrafficsim.base.WeightedMeanAndSum;
22 import org.opentrafficsim.base.parameters.ParameterException;
23 import org.opentrafficsim.core.distributions.ProbabilityException;
24 import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
25 import org.opentrafficsim.core.geometry.OtsGeometryException;
26 import org.opentrafficsim.core.gtu.GtuException;
27 import org.opentrafficsim.core.gtu.GtuType;
28 import org.opentrafficsim.core.network.NetworkException;
29 import org.opentrafficsim.core.network.Node;
30 import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGtuCharacteristicsGenerator;
31 import org.opentrafficsim.road.gtu.generator.characteristics.LaneBasedGtuCharacteristicsGeneratorOd;
32 import org.opentrafficsim.road.network.lane.Lane;
33 import org.opentrafficsim.road.od.Category;
34 import org.opentrafficsim.road.od.Interpolation;
35
36 import nl.tudelft.simulation.dsol.SimRuntimeException;
37 import nl.tudelft.simulation.jstats.streams.StreamInterface;
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55 public abstract class Platoons<T>
56 {
57
58
59 private LaneBasedGtuGenerator gen;
60
61
62 private final OtsSimulatorInterface simulator;
63
64
65 private final Set<Lane> position;
66
67
68 private final Queue<PlatoonGtu<T>> queue = new PriorityQueue<>();
69
70
71 private final SortedMap<Time, Time> periods = new TreeMap<>();
72
73
74 private Time startTime;
75
76
77 private Time endTime;
78
79
80 private Node fixedOrigin;
81
82
83 private Node fixedDestination;
84
85
86 private T fixedCategory;
87
88
89 private Map<T, Integer> numberOfGtus = new LinkedHashMap<>();
90
91
92 private boolean started = false;
93
94
95
96
97
98
99 private Platoons(final OtsSimulatorInterface simulator, final Set<Lane> position)
100 {
101 this.simulator = simulator;
102 this.position = position;
103 }
104
105
106
107
108
109
110
111
112
113 public static Platoons<Category> ofCategory(final LaneBasedGtuCharacteristicsGeneratorOd characteristics,
114 final OtsSimulatorInterface simulator, final StreamInterface stream, final Set<Lane> position)
115 {
116 return new Platoons<Category>(simulator, position)
117 {
118
119 private final LaneBasedGtuCharacteristicsGeneratorOd characteristicsOD = characteristics;
120
121
122 private final StreamInterface strm = stream;
123
124
125 @Override
126 protected void placeGtu(final PlatoonGtu<Category> platoonGtu) throws SimRuntimeException, NamingException,
127 GtuException, NetworkException, OtsGeometryException, ProbabilityException, ParameterException
128 {
129 getGenerator().queueGtu(this.characteristicsOD.draw(platoonGtu.getOrigin(), platoonGtu.getDestination(),
130 platoonGtu.getCategory(), this.strm), getPosition());
131 start();
132 }
133 };
134 }
135
136
137
138
139
140
141
142
143
144 @SuppressWarnings("synthetic-access")
145 public static Platoons<GtuType> ofGtuType(final LaneBasedGtuCharacteristicsGenerator characteristics,
146 final OtsSimulatorInterface simulator, final StreamInterface stream, final Set<Lane> position)
147 {
148 return new Platoons<GtuType>(simulator, position)
149 {
150
151 private final LaneBasedGtuCharacteristicsGenerator chrctrstcs = characteristics;
152
153
154 @Override
155 protected void placeGtu(final PlatoonGtu<GtuType> platoonGtu) throws SimRuntimeException, NamingException,
156 GtuException, NetworkException, OtsGeometryException, ProbabilityException, ParameterException
157 {
158
159 getGenerator().queueGtu(this.chrctrstcs.draw(), getPosition());
160 start();
161 }
162 };
163 }
164
165
166
167
168
169
170
171
172
173 public Platoons<T> addPlatoon(final Time start, final Time end) throws SimRuntimeException
174 {
175 Throw.when(this.started, IllegalStateException.class, "Cannot add a platoon after the Platoons was started.");
176 Throw.whenNull(start, "Start may not be null.");
177 Throw.whenNull(end, "End may not be null.");
178 this.startTime = start;
179 this.endTime = end;
180 this.periods.put(start, end);
181 return this;
182 }
183
184
185
186
187
188
189
190
191 public Platoons<T> fixInfo(final Node origin, final Node destination, final T category)
192 {
193 this.fixedOrigin = origin;
194 this.fixedDestination = destination;
195 this.fixedCategory = category;
196 return this;
197 }
198
199
200
201
202
203
204
205
206 public Platoons<T> addGtu(final Time time)
207 {
208 Throw.when(this.fixedOrigin == null || this.fixedDestination == null || this.fixedCategory == null,
209 IllegalStateException.class, "When using addGtu(Time), used fixInfo(...) before to set other info.");
210 return addGtu(time, this.fixedOrigin, this.fixedDestination, this.fixedCategory);
211 }
212
213
214
215
216
217
218
219
220
221
222 public Platoons<T> addGtu(final Time time, final Node origin, final Node destination, final T category)
223 {
224 Throw.when(this.started, IllegalStateException.class, "Cannot add a GTU after the Platoons was started.");
225 Throw.when(this.startTime == null || this.endTime == null, IllegalStateException.class,
226 "First call addPlatoon() before calling addGtu()");
227 Throw.when(time.gt(this.endTime) || time.lt(this.startTime), IllegalArgumentException.class,
228 "Time %s is not between %s and %s", time, this.startTime, this.endTime);
229 this.queue.add(new PlatoonGtu<>(time, origin, destination, category));
230 this.numberOfGtus.put(category, this.numberOfGtus.getOrDefault(category, 0) + 1);
231 return this;
232 }
233
234
235
236
237
238
239 public void start(final LaneBasedGtuGenerator generator) throws SimRuntimeException
240 {
241 Throw.when(this.started, IllegalStateException.class, "Cannot start the Platoons, it was already started.");
242 this.gen = generator;
243
244 Time prevEnd = null;
245 for (Map.Entry<Time, Time> entry : this.periods.entrySet())
246 {
247 Time start = entry.getKey();
248 Throw.when(prevEnd != null && start.le(prevEnd), IllegalStateException.class, "Platoons are overlapping.");
249 prevEnd = entry.getValue();
250 this.gen.disable(start, prevEnd, this.position);
251 }
252 this.started = true;
253 start();
254 }
255
256
257
258
259
260 protected LaneBasedGtuGenerator getGenerator()
261 {
262 return this.gen;
263 }
264
265
266
267
268
269 protected Set<Lane> getPosition()
270 {
271 return this.position;
272 }
273
274
275
276
277
278 protected void start() throws SimRuntimeException
279 {
280 if (!this.queue.isEmpty())
281 {
282 this.simulator.scheduleEventAbsTime(this.queue.peek().getTime(), this, "placeGtu", new Object[] {this.queue.poll()});
283 }
284 }
285
286
287
288
289
290
291
292
293
294
295 public FrequencyVector compensate(final T category, final FrequencyVector demand, final TimeVector time,
296 final Interpolation interpolation)
297 {
298 Throw.whenNull(category, "Category may not be null.");
299 Throw.whenNull(demand, "Demand may not be null.");
300 Throw.whenNull(time, "Time may not be null.");
301 Throw.whenNull(interpolation, "Interpolation may not be null.");
302 Throw.when(demand.size() != time.size(), IllegalArgumentException.class, "Demand and time have unequal length.");
303 try
304 {
305 WeightedMeanAndSum<Double, Double> weightedSumLost = new WeightedMeanAndSum<>();
306 for (Map.Entry<Time, Time> entry : this.periods.entrySet())
307 {
308 Time start = entry.getKey();
309 Time end = entry.getValue();
310 for (int i = 0; i < demand.size() - 1; i++)
311 {
312 Time s = Time.max(start, time.get(i));
313 Time e = Time.min(end, time.get(i + 1));
314 if (s.lt(e))
315 {
316 Frequency fStart = interpolation.interpolateVector(s, demand, time, true);
317
318 Frequency fEnd = interpolation.interpolateVector(e, demand, time, false);
319 weightedSumLost.add((fStart.si + fEnd.si) / 2, e.si - s.si);
320 }
321 }
322 }
323 WeightedMeanAndSum<Double, Double> weightedSumTotal = new WeightedMeanAndSum<>();
324 for (int i = 0; i < demand.size() - 1; i++)
325 {
326 Frequency fStart = interpolation.interpolateVector(time.get(i), demand, time, true);
327 Frequency fEnd = interpolation.interpolateVector(time.get(i + 1), demand, time, false);
328 weightedSumTotal.add((fStart.si + fEnd.si) / 2, time.getSI(i + 1) - time.getSI(i));
329
330 }
331
332 double lost = weightedSumLost.getSum();
333 double total = weightedSumTotal.getSum();
334 int platooning = this.numberOfGtus.getOrDefault(category, 0);
335 double factor = (total - platooning) / (total - lost);
336 if (factor < 0.0)
337 {
338 this.simulator.getLogger().always().warn("Reducing demand of {} by {}, demand is set to 0.", total,
339 total - factor * total);
340 factor = 0.0;
341 }
342
343 double[] array = new double[demand.size()];
344 for (int i = 0; i < array.length - 1; i++)
345 {
346 array[i] = demand.getInUnit(i) * factor;
347 }
348 return DoubleVector.instantiate(array, demand.getDisplayUnit(), StorageType.DENSE);
349 }
350 catch (ValueRuntimeException exception)
351 {
352 throw new RuntimeException("Unexpected exception while looping vector.", exception);
353 }
354 }
355
356
357
358
359
360
361
362
363
364
365
366
367 protected abstract void placeGtu(PlatoonGtu<T> platoonGtu) throws SimRuntimeException, NamingException, GtuException,
368 NetworkException, OtsGeometryException, ProbabilityException, ParameterException;
369
370
371
372
373
374
375
376
377
378
379
380
381
382 private static class PlatoonGtu<K> implements Comparable<PlatoonGtu<K>>
383 {
384
385
386 private final Time time;
387
388
389 private final Node origin;
390
391
392 private final Node destination;
393
394
395 private final K category;
396
397
398
399
400
401
402
403
404 PlatoonGtu(final Time time, final Node origin, final Node destination, final K category)
405 {
406 this.time = time;
407 this.origin = origin;
408 this.destination = destination;
409 this.category = category;
410 }
411
412
413 @Override
414 public int compareTo(final PlatoonGtu<K> o)
415 {
416 if (o == null)
417 {
418 return 1;
419 }
420 return this.time.compareTo(o.time);
421 }
422
423
424
425
426 protected Time getTime()
427 {
428 return this.time;
429 }
430
431
432
433
434 protected Node getOrigin()
435 {
436 return this.origin;
437 }
438
439
440
441
442 protected Node getDestination()
443 {
444 return this.destination;
445 }
446
447
448
449
450 protected K getCategory()
451 {
452 return this.category;
453 }
454
455 }
456 }