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