Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
RailwayRouter.h
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3// Copyright (C) 2001-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/****************************************************************************/
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
29#endif
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// ===========================================================================
66template<class E, class V>
67class RailwayRouter : public SUMOAbstractRouter<E, V> {
68
69private:
70
71
75 typedef std::map<const E*, double> Prohibitions;
76
77public:
78
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 {
89 myReversalPenalty = reversalPenalty;
90 myStaticOperation = effortOperation;
91 for (const E* const edge : edges) {
92 myInitialEdges.push_back(edge->getRailwayRoutingEdge());
93 }
94 }
95
97 virtual ~RailwayRouter() {
98 delete myInternalRouter;
99 }
100
102 return new RailwayRouter<E, V>(this);
103 }
104
107 bool compute(const E* from, const E* to, const V* const vehicle, SUMOTime msTime, std::vector<const E*>& into, bool silent = false) {
109 if (vehicle->getLength() > myMaxTrainLength) {
110 WRITE_WARNINGF("Vehicle '%' with length % exceeds configured value of --railway.max-train-length %",
111 vehicle->getID(), toString(vehicle->getLength()), toString(myMaxTrainLength));
112 }
113 return _compute(from, to, vehicle, msTime, into, silent, false);
114 }
115
116 void prohibit(const Prohibitions& toProhibit) {
118 std::map<const _RailEdge*, double> railEdges;
119 for (auto item : toProhibit) {
120 railEdges[item.first->getRailwayRoutingEdge()] = item.second;
121 }
122 myInternalRouter->prohibit(railEdges);
123 this->myProhibited = toProhibit;
124#ifdef RailwayRouter_DEBUG_ROUTES
125 std::cout << "RailRouter numProhibitions=" << toProhibit.size() << "\n";
126#endif
127 }
128
129
130 double recomputeCosts(const std::vector<const E*>& edges, const V* const v, SUMOTime msTime, double* lengthp = nullptr) const {
131 double effort = SUMOAbstractRouter<E, V>::recomputeCosts(edges, v, msTime, lengthp);
132 const E* prev = nullptr;
133 // reversal corrections
134 double timeCorrection = STEPS2TIME(msTime);
135 for (const E* const e : edges) {
136 if (prev != nullptr && e->getBidiEdge() == prev) {
137 if (e->getLength() > v->getLength()) {
138 // vehicle doesn't need to drive to the end of the edge
139 // @note exact timing correction for time-dependent effort is not done
140 const double savingsFactor = 1 - v->getLength() / e->getLength();
141 double effortCorrection = 0;
142 double lengthCorrection = 0.;
143 effortCorrection += this->getEffort(prev, v, timeCorrection);
144 this->updateViaCost(prev, e, v, timeCorrection, effortCorrection, lengthCorrection);
145 effort -= savingsFactor * effortCorrection;
146 if (lengthp != nullptr) {
147 *lengthp -= savingsFactor * lengthCorrection;
148 }
149 }
150 effort += myReversalPenalty;
151 }
152 prev = e;
153 }
154 return effort;
155 }
156
157 inline void setBulkMode(const bool mode) {
158 if (myInternalRouter != nullptr) {
160 }
161 }
162
163private:
165 SUMOAbstractRouter<E, V>(other),
166 myInternalRouter(nullptr),
167 myOriginal(other),
168 mySilent(other->mySilent),
170 {}
171
178
179 bool _compute(const E* from, const E* to, const V* const vehicle, SUMOTime msTime, std::vector<const E*>& into, bool silent, bool avoidUnsafeBackTracking) {
180 // 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)
181 // if reversals are forbidden (negative penalty), we don't need to check this
182 std::vector<double> backLengths;
183 double backDist = myReversalPenalty >= 0 ? vehicle->getLength() - from->getLength() : 0;
184 const E* start = from;
185 while (backDist > 0) {
186 const E* prev = getStraightPredecessor(start, into, (int)backLengths.size());
187 if (prev == nullptr) {
188#ifdef RailwayRouter_DEBUG_ROUTES
189 std::cout << " Could not determine back edge for vehicle '" << vehicle->getID() << "' when routing from edge '" << from->getID() << "' at time=" << time2string(msTime) << "\n";
190#endif
191 //WRITE_WARNING("Could not determine back edge for vehicle '" + vehicle->getID() + "' when routing from edge '" + from->getID() + "' at time=" + time2string(msTime));
192 break;
193 }
194 backDist -= prev->getLength();
195 if (avoidUnsafeBackTracking && prev->getSuccessors().size() > 1) {
196 bool foundSwitch = false;
197 for (const E* succ : prev->getSuccessors()) {
198 if (succ != start && succ != prev->getBidiEdge()) {
199 foundSwitch = true;
200 break;
201 }
202 }
203 if (foundSwitch) {
204 break;
205 }
206 }
207 backLengths.push_back(prev->getLength() + (backLengths.empty()
208 ? MIN2(vehicle->getLength(), from->getLength())
209 : backLengths.back()));
210 start = prev;
211 }
212
213 std::vector<const _RailEdge*> intoTmp;
214 if (myLastFrom != start) {
216 }
217 bool success = myInternalRouter->compute(start->getRailwayRoutingEdge(), to->getRailwayRoutingEdge(), vehicle, msTime, intoTmp, silent);
218 myLastFrom = start;
219#ifdef RailwayRouter_DEBUG_ROUTES
220 std::cout << "RailRouter veh=" << vehicle->getID() << " from=" << from->getID() << " to=" << to->getID() << " t=" << time2string(msTime)
221 << " safe=" << avoidUnsafeBackTracking << " success=" << success << " into=" << toString(into) << "\n";
222#endif
223 if (success) {
224 const size_t intoSize = into.size();
225 int backIndex = (int)backLengths.size();
226 for (const _RailEdge* railEdge : intoTmp) {
227 if (railEdge->getOriginal() != nullptr) {
228 backIndex--;
229 }
230 // prevent premature reversal on back edge (extend train length)
231 const double length = backIndex >= 0 ? backLengths[backIndex] : vehicle->getLength();
232 railEdge->insertOriginalEdges(length, into);
233 }
234#ifdef RailwayRouter_DEBUG_ROUTES
235 std::cout << "RailRouter: internal result=" << toString(intoTmp) << "\n";
236 std::cout << "RailRouter: expanded result=" << toString(into) << "\n";
237 std::cout << "RailRouter: backLengths=" << toString(backLengths) << " bls=" << backLengths.size() << " intoSize=" << intoSize << " final result=" << toString(into) << "\n";
238#endif
239 if (backLengths.size() > 0) {
240 // skip the virtual back-edges
241 into.erase(into.begin() + intoSize, into.begin() + intoSize + backLengths.size());
242 if (*(into.begin() + intoSize) != from) {
243 if (!avoidUnsafeBackTracking) {
244 // try again, this time with more safety (but unable to
245 // make use of turn-arounds on short edge)
246 into.erase(into.begin() + intoSize, into.end());
247 // we are starting from a different edge and are thus violating the assumptions of bulk mode
248 success = _compute(from, to, vehicle, msTime, into, silent, true);
249 return success;
250 } else {
251 WRITE_WARNING("Railway routing failure due to turn-around on short edge '" + from->getID()
252 + "' for vehicle '" + vehicle->getID() + "' time=" + time2string(msTime) + ".");
253 }
254 }
255 }
256 if (this->myProhibited.size() > 0) {
257 // make sure that turnarounds don't use prohibited edges
258 for (const E* e : into) {
259 if (this->myProhibited.find(e) != this->myProhibited.end()) {
260 into.clear();
261 success = false;
262 break;
263 }
264 }
265 }
266 }
267 return success;
268
269 }
270
271 const std::vector<_RailEdge*>& getRailEdges() {
272 if (myOriginal != nullptr) {
273 return myOriginal->getRailEdges();
274 }
275#ifdef HAVE_FOX
276 FXMutexLock locker(myLock);
277#endif
278 if (myRailEdges.empty()) {
280 int numericalID = myInitialEdges.back()->getNumericalID() + 1;
281 for (_RailEdge* railEdge : myInitialEdges) {
282 railEdge->init(myRailEdges, numericalID, myMaxTrainLength, myReversalPenalty >= 0);
283 }
284 }
285 return myRailEdges;
286 }
287
288 static inline double getTravelTimeStatic(const RailEdge<E, V>* const edge, const V* const veh, double time) {
289 if (edge->getOriginal() != nullptr) {
290 return (*myStaticOperation)(edge->getOriginal(), veh, time);
291 } else {
292 // turnaround edge
293 if (edge->isVirtual()) {
294 // add up time for replacement edges
295 std::vector<const E*> repl;
296 edge->insertOriginalEdges(veh->getLength(), repl);
297 assert(repl.size() > 0);
298 double seenDist = 0;
299 double result = 0;
300 repl.pop_back(); // last edge must not be used fully
301 for (const E* e : repl) {
302 result += (*myStaticOperation)(e, veh, time + result);
303 seenDist += e->getLength();
304 }
305 const double lengthOnLastEdge = MAX2(0.0, veh->getLength() - seenDist);
306 return result + myReversalPenalty + lengthOnLastEdge * myReversalPenaltyFactor;
307 } else {
308 // if the edge from which this turnaround starts is longer
309 // than the vehicle then we are overstimating the cost
310 // (because the turnaround may happen before driving to the end)
311 // However, unless the vehicle starts on this edge, we should be taking a
312 // virtual turnaround at the end of the previous edge. Otherwise, the exaggerated cost doesn't
313 return myReversalPenalty;
314 }
315 }
316 }
317
318
319 static const E* getStraightPredecessor(const E* edge, std::vector<const E*>& prevRoute, int backIndex) {
320 const E* result = nullptr;
321#ifdef RailwayRouter_DEBUG_ROUTES
322 std::cout << " getStraightPredecessor edge=" << edge->getID() << " prevRouteSize=" << prevRoute.size() << " backIndex=" << backIndex << "\n";
323#endif
324 if ((int)prevRoute.size() > backIndex) {
325 return prevRoute[(int)prevRoute.size() - 1 - backIndex];
326 }
327 for (const E* cand : edge->getPredecessors()) {
328 if (!cand->isInternal() && cand->getBidiEdge() != edge) {
329 //std::cout << " cand=" << cand->getID() << "\n";
330 if (result == nullptr) {
331 result = cand;
332 } else {
333 // predecessor not unique. Better abort with a warning
334 return nullptr;
335 }
336 }
337 }
338 return result;
339 }
340
341private:
345 std::vector<_RailEdge*> myInitialEdges;
347 std::vector<_RailEdge*> myRailEdges;
348
350 const bool mySilent;
351
352 const double myMaxTrainLength;
353
355 const E* myLastFrom;
356
357#ifdef HAVE_FOX
359 mutable FXMutex myLock;
360#endif
361
364
365 static double myReversalPenalty;
367
368private:
371
372};
373
374template<class E, class V>
376template<class E, class V>
378template<class E, class V>
long long int SUMOTime
Definition GUI.h:36
#define WRITE_WARNINGF(...)
Definition MsgHandler.h:287
#define WRITE_WARNING(msg)
Definition MsgHandler.h:286
std::string time2string(SUMOTime t, bool humanReadable)
convert SUMOTime to string (independently of global format setting)
Definition SUMOTime.cpp:91
#define STEPS2TIME(x)
Definition SUMOTime.h:55
T MIN2(T a, T b)
Definition StdDefs.h:80
T MAX2(T a, T b)
Definition StdDefs.h:86
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition ToString.h:46
Computes the shortest path through a network using the Dijkstra algorithm.
static MsgHandler * getWarningInstance()
Returns the instance to add warnings to.
the edge type representing backward edges
Definition RailEdge.h:38
void insertOriginalEdges(double length, std::vector< const E * > &into) const
Definition RailEdge.h:184
bool isVirtual() const
Definition RailEdge.h:264
const E * getOriginal() const
Returns the original edge.
Definition RailEdge.h:173
SUMOAbstractRouter< _RailEdge, V > _InternalRouter
static double myReversalPenaltyFactor
RailwayRouter< E, V > *const myOriginal
RailwayRouter(RailwayRouter *other)
DijkstraRouter< _RailEdge, V > _InternalDijkstra
std::vector< _RailEdge * > myInitialEdges
a RailEdge for every existing edge, filled on construction (but not in clones)
RailwayRouter & operator=(const RailwayRouter &s)
Invalidated assignment operator.
std::vector< _RailEdge * > myRailEdges
complete rail network filled on demand (but not in clones)
void prohibit(const Prohibitions &toProhibit)
void setBulkMode(const bool mode)
double recomputeCosts(const std::vector< const E * > &edges, const V *const v, SUMOTime msTime, double *lengthp=nullptr) const
const bool mySilent
whether to suppress warning/error if no route was found
bool compute(const E *from, const E *to, const V *const vehicle, SUMOTime msTime, std::vector< const E * > &into, bool silent=false)
Builds the route between the given edges using the minimum effort at the given time The definition of...
_InternalRouter * myInternalRouter
static SUMOAbstractRouter< E, V >::Operation myStaticOperation
The object's operation to perform. (hack)
const double myMaxTrainLength
bool _compute(const E *from, const E *to, const V *const vehicle, SUMOTime msTime, std::vector< const E * > &into, bool silent, bool avoidUnsafeBackTracking)
void ensureInternalRouter()
const std::vector< _RailEdge * > & getRailEdges()
RailEdge< E, V > _RailEdge
static double myReversalPenalty
static const E * getStraightPredecessor(const E *edge, std::vector< const E * > &prevRoute, int backIndex)
static double getTravelTimeStatic(const RailEdge< E, V > *const edge, const V *const veh, double time)
std::map< const E *, double > Prohibitions
SUMOAbstractRouter< E, V > * clone()
const E * myLastFrom
track previous edge for correct bulk routing
virtual ~RailwayRouter()
Destructor.
RailwayRouter(const std::vector< E * > &edges, bool unbuildIsWarning, typename SUMOAbstractRouter< E, V >::Operation effortOperation, typename SUMOAbstractRouter< E, V >::Operation ttOperation=nullptr, bool silent=false, bool havePermissions=false, const bool haveRestrictions=false, double maxTrainLength=5000, double reversalPenalty=60)
Constructor.
MsgHandler *const myErrorMsgHandler
the handler for routing errors
const bool myHavePermissions
whether edge permissions need to be considered
virtual void setBulkMode(const bool mode)
void updateViaCost(const E *const prev, const E *const e, const V *const v, double &time, double &effort, double &length) const
double getEffort(const E *const e, const V *const v, double t) const
virtual void prohibit(const Prohibitions &toProhibit)
Prohibitions myProhibited
The list of explicitly prohibited edges and estimated end time of prohibition.
const bool myHaveRestrictions
whether edge restrictions need to be considered
virtual bool compute(const E *from, const E *to, const V *const vehicle, SUMOTime msTime, std::vector< const E * > &into, bool silent=false)=0
Builds the route between the given edges using the minimum effort at the given time The definition of...
virtual double recomputeCosts(const std::vector< const E * > &edges, const V *const v, SUMOTime msTime, double *lengthp=nullptr) const