Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/router/RORouteDef.cpp
169665 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2002-2025 German Aerospace Center (DLR) and others.
4
// This program and the accompanying materials are made available under the
5
// terms of the Eclipse Public License 2.0 which is available at
6
// https://www.eclipse.org/legal/epl-2.0/
7
// This Source Code may also be made available under the following Secondary
8
// Licenses when the conditions for such availability set forth in the Eclipse
9
// Public License 2.0 are satisfied: GNU General Public License, version 2
10
// or later which is available at
11
// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13
/****************************************************************************/
14
/// @file RORouteDef.cpp
15
/// @author Daniel Krajzewicz
16
/// @author Michael Behrisch
17
/// @author Jakob Erdmann
18
/// @date Sept 2002
19
///
20
// Base class for a vehicle's route definition
21
/****************************************************************************/
22
#include <config.h>
23
24
#include <string>
25
#include <iterator>
26
#include <algorithm>
27
#include <utils/common/StringUtils.h>
28
#include <utils/common/ToString.h>
29
#include <utils/common/Named.h>
30
#include <utils/common/StringUtils.h>
31
#include <utils/common/MsgHandler.h>
32
#include <utils/common/RandHelper.h>
33
#include <utils/iodevices/OutputDevice.h>
34
#include <utils/options/OptionsCont.h>
35
#include "ROEdge.h"
36
#include "RORoute.h"
37
#include <utils/router/SUMOAbstractRouter.h>
38
#include <utils/router/RouteCostCalculator.h>
39
#include "RORouteDef.h"
40
#include "ROVehicle.h"
41
42
// ===========================================================================
43
// static members
44
// ===========================================================================
45
bool RORouteDef::myUsingJTRR(false);
46
47
// ===========================================================================
48
// method definitions
49
// ===========================================================================
50
RORouteDef::RORouteDef(const std::string& id, const int lastUsed,
51
const bool tryRepair, const bool mayBeDisconnected) :
52
Named(StringUtils::convertUmlaute(id)),
53
myPrecomputed(nullptr), myLastUsed(lastUsed), myTryRepair(tryRepair),
54
myMayBeDisconnected(mayBeDisconnected),
55
myDiscardSilent(false) {
56
}
57
58
59
RORouteDef::~RORouteDef() {
60
for (std::vector<RORoute*>::iterator i = myAlternatives.begin(); i != myAlternatives.end(); i++) {
61
if (myRouteRefs.count(*i) == 0) {
62
delete *i;
63
}
64
}
65
}
66
67
68
void
69
RORouteDef::addLoadedAlternative(RORoute* alt) {
70
myAlternatives.push_back(alt);
71
}
72
73
74
void
75
RORouteDef::addAlternativeDef(const RORouteDef* alt) {
76
std::copy(alt->myAlternatives.begin(), alt->myAlternatives.end(),
77
back_inserter(myAlternatives));
78
std::copy(alt->myAlternatives.begin(), alt->myAlternatives.end(),
79
std::inserter(myRouteRefs, myRouteRefs.end()));
80
}
81
82
83
RORoute*
84
RORouteDef::buildCurrentRoute(SUMOAbstractRouter<ROEdge, ROVehicle>& router,
85
SUMOTime begin, const ROVehicle& veh) const {
86
if (myPrecomputed == nullptr) {
87
preComputeCurrentRoute(router, begin, veh);
88
}
89
return myPrecomputed;
90
}
91
92
93
void
94
RORouteDef::preComputeCurrentRoute(SUMOAbstractRouter<ROEdge, ROVehicle>& router,
95
SUMOTime begin, const ROVehicle& veh) const {
96
myNewRoute = false;
97
const OptionsCont& oc = OptionsCont::getOptions();
98
const bool ignoreErrors = oc.getBool("ignore-errors");
99
assert(myAlternatives[0]->getEdgeVector().size() > 0);
100
MsgHandler* mh = ignoreErrors ? MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance();
101
if (myAlternatives[0]->getFirst()->prohibits(&veh) && (!oc.getBool("repair.from")
102
// do not try to reassign starting edge for trip input
103
|| myMayBeDisconnected || myAlternatives[0]->getEdgeVector().size() < 2)) {
104
mh->inform("Vehicle '" + veh.getID() + "' is not allowed to depart on edge '" +
105
myAlternatives[0]->getFirst()->getID() + "'.");
106
return;
107
} else if (myAlternatives[0]->getLast()->prohibits(&veh) && (!oc.getBool("repair.to")
108
// do not try to reassign destination edge for trip input
109
|| myMayBeDisconnected || myAlternatives[0]->getEdgeVector().size() < 2)) {
110
// this check is not strictly necessary unless myTryRepair is set.
111
// However, the error message is more helpful than "no connection found"
112
mh->inform("Vehicle '" + veh.getID() + "' is not allowed to arrive on edge '" +
113
myAlternatives[0]->getLast()->getID() + "'.");
114
return;
115
}
116
const bool skipTripRouting = (oc.exists("write-trips") && oc.getBool("write-trips")
117
&& RouteCostCalculator<RORoute, ROEdge, ROVehicle>::getCalculator().skipRouteCalculation());
118
if ((myTryRepair && !skipTripRouting) || myUsingJTRR) {
119
ConstROEdgeVector newEdges;
120
if (repairCurrentRoute(router, begin, veh, myAlternatives[0]->getEdgeVector(), newEdges)) {
121
if (myAlternatives[0]->getEdgeVector() != newEdges) {
122
if (!myMayBeDisconnected) {
123
WRITE_WARNINGF(TL("Repaired route of vehicle '%'."), veh.getID());
124
}
125
myNewRoute = true;
126
RGBColor* col = myAlternatives[0]->getColor() != nullptr ? new RGBColor(*myAlternatives[0]->getColor()) : nullptr;
127
myPrecomputed = new RORoute(myID, 0, myAlternatives[0]->getProbability(), newEdges, col, myAlternatives[0]->getStops());
128
} else {
129
myPrecomputed = myAlternatives[0];
130
}
131
}
132
return;
133
}
134
if ((RouteCostCalculator<RORoute, ROEdge, ROVehicle>::getCalculator().skipRouteCalculation()
135
|| OptionsCont::getOptions().getBool("remove-loops"))
136
&& (skipTripRouting || myAlternatives[myLastUsed]->isValid(veh, ignoreErrors))) {
137
myPrecomputed = myAlternatives[myLastUsed];
138
} else {
139
// build a new route to test whether it is better
140
ConstROEdgeVector oldEdges({getOrigin(), getDestination()});
141
ConstROEdgeVector edges;
142
if (repairCurrentRoute(router, begin, veh, oldEdges, edges, true)) {
143
if (edges.front()->isTazConnector()) {
144
edges.erase(edges.begin());
145
}
146
if (edges.back()->isTazConnector()) {
147
edges.pop_back();
148
}
149
// check whether the same route was already used
150
int existing = -1;
151
for (int i = 0; i < (int)myAlternatives.size(); i++) {
152
if (edges == myAlternatives[i]->getEdgeVector()) {
153
existing = i;
154
break;
155
}
156
}
157
if (existing >= 0) {
158
myPrecomputed = myAlternatives[existing];
159
} else {
160
RGBColor* col = myAlternatives[0]->getColor() != nullptr ? new RGBColor(*myAlternatives[0]->getColor()) : nullptr;
161
myPrecomputed = new RORoute(myID, 0, 1, edges, col, myAlternatives[0]->getStops());
162
myNewRoute = true;
163
}
164
}
165
}
166
}
167
168
169
bool
170
RORouteDef::repairCurrentRoute(SUMOAbstractRouter<ROEdge, ROVehicle>& router,
171
SUMOTime begin, const ROVehicle& veh,
172
ConstROEdgeVector oldEdges, ConstROEdgeVector& newEdges,
173
bool isTrip) const {
174
MsgHandler* mh = (OptionsCont::getOptions().getBool("ignore-errors") ?
175
MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance());
176
const int initialSize = (int)oldEdges.size();
177
if (initialSize == 1) {
178
if (myUsingJTRR) {
179
/// only ROJTRRouter is supposed to handle this type of input
180
bool ok = router.compute(oldEdges.front(), nullptr, &veh, begin, newEdges);
181
myDiscardSilent = ok && newEdges.size() == 0;
182
} else {
183
newEdges = oldEdges;
184
}
185
} else {
186
if (oldEdges.front()->prohibits(&veh)) {
187
// option repair.from is in effect
188
const std::string& frontID = oldEdges.front()->getID();
189
for (ConstROEdgeVector::iterator i = oldEdges.begin(); i != oldEdges.end();) {
190
if ((*i)->prohibits(&veh) || (*i)->isInternal()) {
191
i = oldEdges.erase(i);
192
} else {
193
WRITE_MESSAGE("Changing invalid starting edge '" + frontID
194
+ "' to '" + (*i)->getID() + "' for vehicle '" + veh.getID() + "'.");
195
break;
196
}
197
}
198
}
199
if (oldEdges.size() == 0) {
200
mh->inform("Could not find new starting edge for vehicle '" + veh.getID() + "'.");
201
return false;
202
}
203
if (oldEdges.back()->prohibits(&veh)) {
204
// option repair.to is in effect
205
const std::string& backID = oldEdges.back()->getID();
206
// oldEdges cannot get empty here, otherwise we would have left the stage when checking "from"
207
while (oldEdges.back()->prohibits(&veh) || oldEdges.back()->isInternal()) {
208
oldEdges.pop_back();
209
}
210
WRITE_MESSAGE("Changing invalid destination edge '" + backID
211
+ "' to edge '" + oldEdges.back()->getID() + "' for vehicle '" + veh.getID() + "'.");
212
}
213
ConstROEdgeVector mandatory = veh.getMandatoryEdges(oldEdges.front(), oldEdges.back());
214
std::set<ConstROEdgeVector::const_iterator> jumpStarts;
215
veh.collectJumps(mandatory, jumpStarts);
216
assert(mandatory.size() >= 2);
217
// removed prohibited
218
for (ConstROEdgeVector::iterator i = oldEdges.begin(); i != oldEdges.end();) {
219
if ((*i)->prohibits(&veh) || (*i)->isInternal()) {
220
// no need to check the mandatories here, this was done before
221
WRITE_MESSAGEF(TL("Removing invalid edge '%' for from route for vehicle '%'."), (*i)->getID(), veh.getID());
222
i = oldEdges.erase(i);
223
} else {
224
++i;
225
}
226
}
227
// reconnect remaining edges
228
if (mandatory.size() > oldEdges.size() && initialSize > 2) {
229
WRITE_MESSAGEF(TL("There are stop edges which were not part of the original route for vehicle '%'."), veh.getID());
230
}
231
const ConstROEdgeVector& targets = mandatory.size() > oldEdges.size() ? mandatory : oldEdges;
232
newEdges.push_back(targets.front());
233
ConstROEdgeVector::iterator nextMandatory = mandatory.begin() + 1;
234
int lastMandatory = 0;
235
for (ConstROEdgeVector::const_iterator i = targets.begin() + 1;
236
i != targets.end() && nextMandatory != mandatory.end(); ++i) {
237
const ROEdge* prev = *(i - 1);
238
const ROEdge* cur = *i;
239
if (prev->isConnectedTo(*cur, veh.getVClass()) && (!isRailway(veh.getVClass()) || prev->getBidiEdge() != cur)) {
240
newEdges.push_back(cur);
241
} else {
242
if (initialSize > 2) {
243
// only inform if the input is (probably) not a trip
244
WRITE_MESSAGEF(TL("Edge '%' not connected to edge '%' for vehicle '%'."), (*(i - 1))->getID(), (*i)->getID(), veh.getID());
245
}
246
const ROEdge* last = newEdges.back();
247
newEdges.pop_back();
248
if (last->isTazConnector() && newEdges.size() > 1) {
249
// assume this was a viaTaz
250
last = newEdges.back();
251
newEdges.pop_back();
252
}
253
if (veh.hasJumps() && jumpStarts.count(nextMandatory - 1) != 0) {
254
while (*i != *nextMandatory) {
255
++i;
256
}
257
newEdges.push_back(last);
258
newEdges.push_back(*i);
259
//std::cout << " skipJump mIndex=" << (nextMandatory - 1 - mandatory.begin()) << " last=" << last->getID() << " next=" << (*i)->getID() << " newEdges=" << toString(newEdges) << "\n";
260
} else {
261
262
int numEdgesBefore = (int)newEdges.size();
263
// router.setHint(targets.begin(), i, &veh, begin);
264
if (!router.compute(last, *i, &veh, begin, newEdges)) {
265
// backtrack: try to route from last mandatory edge to next mandatory edge
266
// XXX add option for backtracking in smaller increments
267
// (i.e. previous edge to edge after *i)
268
// we would then need to decide whether we have found a good
269
// tradeoff between faithfulness to the input data and detour-length
270
if (lastMandatory >= (int)newEdges.size() || last == newEdges[lastMandatory] || !backTrack(router, i, lastMandatory, nextMandatory, newEdges, veh, begin)) {
271
mh->inform("Mandatory edge '" + (*i)->getID() + "' not reachable by vehicle '" + veh.getID() + "'.");
272
return false;
273
}
274
} else if (!myMayBeDisconnected && !isTrip && last != (*i)) {
275
double airDist = last->getToJunction()->getPosition().distanceTo(
276
(*i)->getFromJunction()->getPosition());
277
double repairDist = 0;
278
for (auto it2 = (newEdges.begin() + numEdgesBefore + 1); it2 != newEdges.end() && it2 != newEdges.end() - 1; it2++) {
279
repairDist += (*it2)->getLength();
280
}
281
const double detourFactor = repairDist / MAX2(airDist, 1.0);
282
const double detour = MAX2(0.0, repairDist - airDist);
283
const double maxDetourFactor = OptionsCont::getOptions().getFloat("repair.max-detour-factor");
284
if (detourFactor > maxDetourFactor) {
285
WRITE_MESSAGEF(" Backtracking to avoid detour of %m for gap of %m)", detour, airDist);
286
backTrack(router, i, lastMandatory, nextMandatory, newEdges, veh, begin);
287
} else if (detourFactor > 1.1) {
288
WRITE_MESSAGEF(" Taking detour of %m to avoid gap of %m)", detour, airDist);
289
}
290
}
291
}
292
}
293
if (*i == *nextMandatory) {
294
nextMandatory++;
295
lastMandatory = (int)newEdges.size() - 1;
296
}
297
}
298
}
299
return true;
300
}
301
302
303
bool
304
RORouteDef::backTrack(SUMOAbstractRouter<ROEdge, ROVehicle>& router,
305
ConstROEdgeVector::const_iterator& i, int lastMandatory, ConstROEdgeVector::iterator nextMandatory,
306
ConstROEdgeVector& newEdges, const ROVehicle& veh, SUMOTime begin) {
307
ConstROEdgeVector edges;
308
bool ok = router.compute(newEdges[lastMandatory], *nextMandatory, &veh, begin, edges);
309
if (!ok) {
310
return false;
311
}
312
313
while (*i != *nextMandatory) {
314
++i;
315
}
316
newEdges.erase(newEdges.begin() + lastMandatory + 1, newEdges.end());
317
std::copy(edges.begin() + 1, edges.end(), back_inserter(newEdges));
318
return true;
319
}
320
321
322
void
323
RORouteDef::addAlternative(SUMOAbstractRouter<ROEdge, ROVehicle>& router,
324
const ROVehicle* const veh, RORoute* current, SUMOTime begin) {
325
if (myTryRepair || myUsingJTRR) {
326
if (myNewRoute) {
327
delete myAlternatives[0];
328
myAlternatives[0] = current;
329
}
330
if (!router.isValid(current->getEdgeVector(), veh)) {
331
throw ProcessError("Route '" + getID() + "' (vehicle '" + veh->getID() + "') is not valid.");
332
}
333
double costs = router.recomputeCosts(current->getEdgeVector(), veh, begin);
334
if (veh->hasJumps()) {
335
// @todo: jumpTime should be applied in recomputeCost to ensure the
336
// correctness of time-dependent traveltimes
337
costs += STEPS2TIME(veh->getJumpTime());
338
}
339
current->setCosts(costs);
340
return;
341
}
342
// add the route when it's new
343
if (myAlternatives.back()->getProbability() < 0) {
344
delete myAlternatives.back();
345
myAlternatives.pop_back();
346
}
347
if (myNewRoute) {
348
myAlternatives.push_back(current);
349
}
350
// recompute the costs and (when a new route was added) scale the probabilities
351
const double scale = double(myAlternatives.size() - 1) / double(myAlternatives.size());
352
for (RORoute* const alt : myAlternatives) {
353
if (!router.isValid(alt->getEdgeVector(), veh)) {
354
throw ProcessError("Route '" + current->getID() + "' (vehicle '" + veh->getID() + "') is not valid.");
355
}
356
// recompute the costs for all routes
357
const double newCosts = router.recomputeCosts(alt->getEdgeVector(), veh, begin);
358
assert(myAlternatives.size() != 0);
359
if (myNewRoute) {
360
if (alt == current) {
361
// set initial probability and costs
362
alt->setProbability(1. / (double) myAlternatives.size());
363
alt->setCosts(newCosts);
364
} else {
365
// rescale probs for all others
366
alt->setProbability(alt->getProbability() * scale);
367
}
368
}
369
RouteCostCalculator<RORoute, ROEdge, ROVehicle>::getCalculator().setCosts(alt, newCosts, alt == myAlternatives[myLastUsed]);
370
}
371
assert(myAlternatives.size() != 0);
372
RouteCostCalculator<RORoute, ROEdge, ROVehicle>::getCalculator().calculateProbabilities(myAlternatives, veh, veh->getDepartureTime());
373
const bool keepRoute = RouteCostCalculator<RORoute, ROEdge, ROVehicle>::getCalculator().keepRoute();
374
if (!RouteCostCalculator<RORoute, ROEdge, ROVehicle>::getCalculator().keepAllRoutes() && !keepRoute) {
375
// remove with probability of 0 (not mentioned in Gawron)
376
for (std::vector<RORoute*>::iterator i = myAlternatives.begin(); i != myAlternatives.end();) {
377
if ((*i)->getProbability() == 0) {
378
delete *i;
379
i = myAlternatives.erase(i);
380
} else {
381
i++;
382
}
383
}
384
}
385
int maxNumber = RouteCostCalculator<RORoute, ROEdge, ROVehicle>::getCalculator().getMaxRouteNumber();
386
if ((int)myAlternatives.size() > maxNumber) {
387
const RORoute* last = myAlternatives[myLastUsed];
388
// only keep the routes with highest probability
389
sort(myAlternatives.begin(), myAlternatives.end(), [](const RORoute * const a, const RORoute * const b) {
390
return a->getProbability() > b->getProbability();
391
});
392
if (keepRoute) {
393
for (int i = 0; i < (int)myAlternatives.size(); i++) {
394
if (myAlternatives[i] == last) {
395
myLastUsed = i;
396
break;
397
}
398
}
399
if (myLastUsed >= maxNumber) {
400
std::swap(myAlternatives[maxNumber - 1], myAlternatives[myLastUsed]);
401
myLastUsed = maxNumber - 1;
402
}
403
}
404
for (std::vector<RORoute*>::iterator i = myAlternatives.begin() + maxNumber; i != myAlternatives.end(); i++) {
405
delete *i;
406
}
407
myAlternatives.erase(myAlternatives.begin() + maxNumber, myAlternatives.end());
408
}
409
// rescale probabilities
410
double newSum = 0.;
411
for (const RORoute* const alt : myAlternatives) {
412
newSum += alt->getProbability();
413
}
414
assert(newSum > 0);
415
// @note newSum may be larger than 1 for numerical reasons
416
for (RORoute* const alt : myAlternatives) {
417
alt->setProbability(alt->getProbability() / newSum);
418
}
419
420
// find the route to use
421
if (!keepRoute) {
422
double chosen = RandHelper::rand();
423
myLastUsed = 0;
424
for (const RORoute* const alt : myAlternatives) {
425
chosen -= alt->getProbability();
426
if (chosen <= 0) {
427
return;
428
}
429
myLastUsed++;
430
}
431
}
432
}
433
434
const ROEdge*
435
RORouteDef::getOrigin() const {
436
return myAlternatives.back()->getFirst();
437
}
438
439
440
const ROEdge*
441
RORouteDef::getDestination() const {
442
return myAlternatives.back()->getLast();
443
}
444
445
446
OutputDevice&
447
RORouteDef::writeXMLDefinition(OutputDevice& dev, const ROVehicle* const veh,
448
bool asAlternatives, bool withExitTimes, bool withCost, bool withLength) const {
449
if (asAlternatives) {
450
dev.openTag(SUMO_TAG_ROUTE_DISTRIBUTION).writeAttr(SUMO_ATTR_LAST, myLastUsed);
451
for (int i = 0; i != (int)myAlternatives.size(); i++) {
452
myAlternatives[i]->writeXMLDefinition(dev, veh, true, true, withExitTimes, withLength);
453
}
454
dev.closeTag();
455
return dev;
456
} else {
457
return myAlternatives[myLastUsed]->writeXMLDefinition(dev, veh, withCost, false, withExitTimes, withLength);
458
}
459
}
460
461
462
RORouteDef*
463
RORouteDef::copy(const std::string& id, const SUMOTime stopOffset) const {
464
RORouteDef* result = new RORouteDef(id, 0, myTryRepair, myMayBeDisconnected);
465
for (const RORoute* const route : myAlternatives) {
466
RGBColor* col = route->getColor() != nullptr ? new RGBColor(*route->getColor()) : nullptr;
467
RORoute* newRoute = new RORoute(id, route->getCosts(), route->getProbability(), route->getEdgeVector(), col, route->getStops());
468
newRoute->addStopOffset(stopOffset);
469
result->addLoadedAlternative(newRoute);
470
}
471
return result;
472
}
473
474
475
double
476
RORouteDef::getOverallProb() const {
477
double sum = 0.;
478
for (std::vector<RORoute*>::const_iterator i = myAlternatives.begin(); i != myAlternatives.end(); i++) {
479
sum += (*i)->getProbability();
480
}
481
return sum;
482
}
483
484
485
/****************************************************************************/
486
487