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