Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/utils/router/RailwayRouter.h
193904 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2001-2026 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 RailwayRouter.h
15
/// @author Jakob Erdmann
16
/// @date Tue, 25 Feb 2020
17
///
18
// The RailwayRouter builds a special network for railway routing to handle train reversal restrictions (delegates to a SUMOAbstractRouter)
19
/****************************************************************************/
20
#pragma once
21
#include <config.h>
22
23
#include <string>
24
#include <vector>
25
#include <algorithm>
26
#include <assert.h>
27
#ifdef HAVE_FOX
28
#include <utils/foxtools/fxheader.h>
29
#endif
30
#include <utils/common/MsgHandler.h>
31
#include <utils/common/SUMOTime.h>
32
#include <utils/common/ToString.h>
33
#include <utils/iodevices/OutputDevice.h>
34
#include "SUMOAbstractRouter.h"
35
#include "DijkstraRouter.h"
36
#include "RailEdge.h"
37
38
39
//#define RailwayRouter_DEBUG_ROUTES
40
41
// ===========================================================================
42
// class definitions
43
// ===========================================================================
44
/**
45
* @class RailwayRouter
46
* The router for rail vehicles for track networks where some sections may be used in both directions
47
* and trains may reverse depending on location and train length
48
*
49
* Example (assume each track section is 100m long) running from left to right and a negative sign indicates reverse direction
50
*
51
* A C D
52
* .______.______.______.
53
* ._____/
54
* B
55
*
56
* Consider a train of 200m length that enters on B and must move to A (with a reversal):
57
* The necessary route is B C D -D -C -A were D,-D are needed for the train to fully pass the switch
58
*
59
* We shadow the normal edge graph with a railEdge graph to include virtual
60
* turnarounds that look at the train length.
61
* The graph extension takes place via RailEdge::init()
62
* For edge C we create a virtual turnaround (as successor of C) that connectes C with -C and is then expanded to C D -D -C for trains longer than 100m.
63
* The expension takes place via RailEdge::insertOriginalEdges()
64
*
65
*/
66
template<class E, class V>
67
class RailwayRouter : public SUMOAbstractRouter<E, V> {
68
69
private:
70
71
72
typedef RailEdge<E, V> _RailEdge;
73
typedef SUMOAbstractRouter<_RailEdge, V> _InternalRouter;
74
typedef DijkstraRouter<_RailEdge, V> _InternalDijkstra;
75
typedef std::map<const E*, RouterProhibition> Prohibitions;
76
77
public:
78
79
/// Constructor
80
RailwayRouter(const std::vector<E*>& edges, bool unbuildIsWarning, typename SUMOAbstractRouter<E, V>::Operation effortOperation,
81
typename SUMOAbstractRouter<E, V>::Operation ttOperation = nullptr, bool silent = false,
82
bool havePermissions = false, const bool haveRestrictions = false, double maxTrainLength = 5000,
83
double reversalPenalty = 60) :
84
SUMOAbstractRouter<E, V>("RailwayRouter", unbuildIsWarning, effortOperation, ttOperation, havePermissions, haveRestrictions),
85
myInternalRouter(nullptr), myOriginal(nullptr), mySilent(silent),
86
myMaxTrainLength(maxTrainLength),
87
myLastFrom(nullptr) {
88
myReversalPenalty = reversalPenalty;
89
myStaticOperation = effortOperation;
90
for (const E* const edge : edges) {
91
myInitialEdges.push_back(edge->getRailwayRoutingEdge());
92
}
93
}
94
95
/// Destructor
96
virtual ~RailwayRouter() {
97
delete myInternalRouter;
98
}
99
100
SUMOAbstractRouter<E, V>* clone() {
101
return new RailwayRouter<E, V>(this);
102
}
103
104
/** @brief Builds the route between the given edges using the minimum effort at the given time
105
The definition of the effort depends on the wished routing scheme */
106
bool compute(const E* from, const E* to, const V* const vehicle, SUMOTime msTime, std::vector<const E*>& into, bool silent = false) {
107
ensureInternalRouter();
108
if (vehicle->getLength() > myMaxTrainLength) {
109
WRITE_WARNINGF("Vehicle '%' with length % exceeds configured value of --railway.max-train-length %",
110
vehicle->getID(), toString(vehicle->getLength()), toString(myMaxTrainLength));
111
}
112
return _compute(from, to, vehicle, msTime, into, silent, false);
113
}
114
115
void prohibit(const Prohibitions& toProhibit) {
116
ensureInternalRouter();
117
typename _InternalRouter::Prohibitions _toProhibit;
118
for (auto item : toProhibit) {
119
_toProhibit[item.first->getRailwayRoutingEdge()] = item.second;
120
}
121
myInternalRouter->prohibit(_toProhibit);
122
this->myProhibited = toProhibit;
123
#ifdef RailwayRouter_DEBUG_ROUTES
124
std::cout << "RailRouter numProhibitions=" << toProhibit.size() << "\n";
125
#endif
126
}
127
128
129
double recomputeCosts(const std::vector<const E*>& edges, const V* const v, SUMOTime msTime, double* lengthp = nullptr) const {
130
double effort = SUMOAbstractRouter<E, V>::recomputeCosts(edges, v, msTime, lengthp);
131
const E* prev = nullptr;
132
// reversal corrections
133
double timeCorrection = STEPS2TIME(msTime);
134
for (const E* const e : edges) {
135
if (prev != nullptr && e->getBidiEdge() == prev) {
136
if (e->getLength() > v->getLength()) {
137
// vehicle doesn't need to drive to the end of the edge
138
// @note exact timing correction for time-dependent effort is not done
139
const double savingsFactor = 1 - v->getLength() / e->getLength();
140
double effortCorrection = 0;
141
double lengthCorrection = 0.;
142
effortCorrection += this->getEffort(prev, v, timeCorrection);
143
this->updateViaCost(prev, e, v, timeCorrection, effortCorrection, lengthCorrection);
144
effort -= savingsFactor * effortCorrection;
145
if (lengthp != nullptr) {
146
*lengthp -= savingsFactor * lengthCorrection;
147
}
148
}
149
effort += myReversalPenalty;
150
}
151
prev = e;
152
}
153
return effort;
154
}
155
156
inline void setBulkMode(const bool mode) {
157
if (myInternalRouter != nullptr) {
158
myInternalRouter->setBulkMode(mode);
159
}
160
}
161
162
private:
163
RailwayRouter(RailwayRouter* other) :
164
SUMOAbstractRouter<E, V>(other),
165
myInternalRouter(nullptr),
166
myOriginal(other),
167
mySilent(other->mySilent),
168
myMaxTrainLength(other->myMaxTrainLength)
169
{}
170
171
void ensureInternalRouter() {
172
if (myInternalRouter == nullptr) {
173
myInternalRouter = new _InternalDijkstra(getRailEdges(), this->myErrorMsgHandler == MsgHandler::getWarningInstance(), &getTravelTimeStatic,
174
nullptr, mySilent, nullptr, this->myHavePermissions, this->myHaveRestrictions);
175
}
176
}
177
178
bool _compute(const E* from, const E* to, const V* const vehicle, SUMOTime msTime, std::vector<const E*>& into, bool silent, bool avoidUnsafeBackTracking) {
179
// make sure that the vehicle can turn-around when starting on a short edge (the virtual turn-around for this lies backwards along the route / track)
180
// if reversals are forbidden (negative penalty), we don't need to check this
181
std::vector<double> backLengths;
182
double backDist = myReversalPenalty >= 0 ? vehicle->getLength() - from->getLength() : 0;
183
const E* start = from;
184
while (backDist > 0) {
185
const E* prev = getStraightPredecessor(start, into, (int)backLengths.size());
186
if (prev == nullptr) {
187
#ifdef RailwayRouter_DEBUG_ROUTES
188
std::cout << " Could not determine back edge for vehicle '" << vehicle->getID() << "' when routing from edge '" << from->getID() << "' at time=" << time2string(msTime) << "\n";
189
#endif
190
//WRITE_WARNING("Could not determine back edge for vehicle '" + vehicle->getID() + "' when routing from edge '" + from->getID() + "' at time=" + time2string(msTime));
191
break;
192
}
193
backDist -= prev->getLength();
194
if (avoidUnsafeBackTracking && prev->getSuccessors().size() > 1) {
195
bool foundSwitch = false;
196
for (const E* succ : prev->getSuccessors()) {
197
if (succ != start && succ != prev->getBidiEdge()) {
198
foundSwitch = true;
199
break;
200
}
201
}
202
if (foundSwitch) {
203
break;
204
}
205
}
206
backLengths.push_back(prev->getLength() + (backLengths.empty()
207
? MIN2(vehicle->getLength(), from->getLength())
208
: backLengths.back()));
209
start = prev;
210
}
211
212
std::vector<const _RailEdge*> intoTmp;
213
if (myLastFrom != start) {
214
myInternalRouter->setBulkMode(false);
215
}
216
bool success = myInternalRouter->compute(start->getRailwayRoutingEdge(), to->getRailwayRoutingEdge(), vehicle, msTime, intoTmp, silent);
217
myLastFrom = start;
218
#ifdef RailwayRouter_DEBUG_ROUTES
219
std::cout << "RailRouter veh=" << vehicle->getID() << " from=" << from->getID() << " to=" << to->getID() << " t=" << time2string(msTime)
220
<< " safe=" << avoidUnsafeBackTracking << " success=" << success << " into=" << toString(into) << "\n";
221
#endif
222
if (success) {
223
const size_t intoSize = into.size();
224
int backIndex = (int)backLengths.size();
225
for (const _RailEdge* railEdge : intoTmp) {
226
if (railEdge->getOriginal() != nullptr) {
227
backIndex--;
228
}
229
// prevent premature reversal on back edge (extend train length)
230
const double length = backIndex >= 0 ? backLengths[backIndex] : vehicle->getLength();
231
railEdge->insertOriginalEdges(length, into);
232
}
233
#ifdef RailwayRouter_DEBUG_ROUTES
234
std::cout << "RailRouter: internal result=" << toString(intoTmp) << "\n";
235
std::cout << "RailRouter: expanded result=" << toString(into) << "\n";
236
std::cout << "RailRouter: backLengths=" << toString(backLengths) << " bls=" << backLengths.size() << " intoSize=" << intoSize << " final result=" << toString(into) << "\n";
237
#endif
238
if (backLengths.size() > 0) {
239
// skip the virtual back-edges
240
into.erase(into.begin() + intoSize, into.begin() + intoSize + backLengths.size());
241
if (*(into.begin() + intoSize) != from) {
242
if (!avoidUnsafeBackTracking) {
243
// try again, this time with more safety (but unable to
244
// make use of turn-arounds on short edge)
245
into.erase(into.begin() + intoSize, into.end());
246
// we are starting from a different edge and are thus violating the assumptions of bulk mode
247
success = _compute(from, to, vehicle, msTime, into, silent, true);
248
return success;
249
} else {
250
WRITE_WARNING("Railway routing failure due to turn-around on short edge '" + from->getID()
251
+ "' for vehicle '" + vehicle->getID() + "' time=" + time2string(msTime) + ".");
252
}
253
}
254
}
255
if (this->myProhibited.size() > 0) {
256
// make sure that turnarounds don't use prohibited edges
257
for (const E* e : into) {
258
if (this->myProhibited.find(e) != this->myProhibited.end()) {
259
into.clear();
260
success = false;
261
break;
262
}
263
}
264
}
265
}
266
return success;
267
268
}
269
270
const std::vector<_RailEdge*>& getRailEdges() {
271
if (myOriginal != nullptr) {
272
return myOriginal->getRailEdges();
273
}
274
#ifdef HAVE_FOX
275
FXMutexLock locker(myLock);
276
#endif
277
if (myRailEdges.empty()) {
278
myRailEdges = myInitialEdges;
279
int numericalID = myInitialEdges.back()->getNumericalID() + 1;
280
for (_RailEdge* railEdge : myInitialEdges) {
281
railEdge->init(myRailEdges, numericalID, myMaxTrainLength, myReversalPenalty >= 0);
282
}
283
}
284
return myRailEdges;
285
}
286
287
static inline double getTravelTimeStatic(const RailEdge<E, V>* const edge, const V* const veh, double time) {
288
if (edge->getOriginal() != nullptr) {
289
return (*myStaticOperation)(edge->getOriginal(), veh, time);
290
} else {
291
// turnaround edge
292
if (edge->isVirtual()) {
293
// add up time for replacement edges
294
std::vector<const E*> repl;
295
edge->insertOriginalEdges(veh->getLength(), repl);
296
assert(repl.size() > 0);
297
double seenDist = 0;
298
double result = 0;
299
repl.pop_back(); // last edge must not be used fully
300
for (const E* e : repl) {
301
result += (*myStaticOperation)(e, veh, time + result);
302
seenDist += e->getLength();
303
}
304
const double lengthOnLastEdge = MAX2(0.0, veh->getLength() - seenDist);
305
return result + myReversalPenalty + lengthOnLastEdge * myReversalPenaltyFactor;
306
} else {
307
// if the edge from which this turnaround starts is longer
308
// than the vehicle then we are overstimating the cost
309
// (because the turnaround may happen before driving to the end)
310
// However, unless the vehicle starts on this edge, we should be taking a
311
// virtual turnaround at the end of the previous edge. Otherwise, the exaggerated cost doesn't
312
return myReversalPenalty;
313
}
314
}
315
}
316
317
318
static const E* getStraightPredecessor(const E* edge, std::vector<const E*>& prevRoute, int backIndex) {
319
const E* result = nullptr;
320
#ifdef RailwayRouter_DEBUG_ROUTES
321
std::cout << " getStraightPredecessor edge=" << edge->getID() << " prevRouteSize=" << prevRoute.size() << " backIndex=" << backIndex << "\n";
322
#endif
323
if ((int)prevRoute.size() > backIndex) {
324
return prevRoute[(int)prevRoute.size() - 1 - backIndex];
325
}
326
for (const E* cand : edge->getPredecessors()) {
327
if (!cand->isInternal() && cand->getBidiEdge() != edge) {
328
//std::cout << " cand=" << cand->getID() << "\n";
329
if (result == nullptr) {
330
result = cand;
331
} else {
332
// predecessor not unique. Better abort with a warning
333
return nullptr;
334
}
335
}
336
}
337
return result;
338
}
339
340
private:
341
_InternalRouter* myInternalRouter;
342
RailwayRouter<E, V>* const myOriginal;
343
/// @brief a RailEdge for every existing edge, filled on construction (but not in clones)
344
std::vector<_RailEdge*> myInitialEdges;
345
/// @brief complete rail network filled on demand (but not in clones)
346
std::vector<_RailEdge*> myRailEdges;
347
348
/// @brief whether to suppress warning/error if no route was found
349
const bool mySilent;
350
351
const double myMaxTrainLength;
352
353
/// @brief track previous edge for correct bulk routing
354
const E* myLastFrom;
355
356
#ifdef HAVE_FOX
357
/// The mutex used to avoid concurrent updates of myRailEdges
358
mutable FXMutex myLock;
359
#endif
360
361
/// @brief The object's operation to perform. (hack)
362
static typename SUMOAbstractRouter<E, V>::Operation myStaticOperation;
363
364
static double myReversalPenalty;
365
static double myReversalPenaltyFactor;
366
367
private:
368
/// @brief Invalidated assignment operator
369
RailwayRouter& operator=(const RailwayRouter& s);
370
371
};
372
373
template<class E, class V>
374
typename SUMOAbstractRouter<E, V>::Operation RailwayRouter<E, V>::myStaticOperation(nullptr);
375
template<class E, class V>
376
double RailwayRouter<E, V>::myReversalPenalty(60);
377
template<class E, class V>
378
double RailwayRouter<E, V>::myReversalPenaltyFactor(0.2); // 1/v
379
380