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-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/****************************************************************************/
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*, RouterProhibition> 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 myReversalPenalty = reversalPenalty;
89 myStaticOperation = effortOperation;
90 for (const E* const edge : edges) {
91 myInitialEdges.push_back(edge->getRailwayRoutingEdge());
92 }
93 }
94
96 virtual ~RailwayRouter() {
97 delete myInternalRouter;
98 }
99
101 return new RailwayRouter<E, V>(this);
102 }
103
106 bool compute(const E* from, const E* to, const V* const vehicle, SUMOTime msTime, std::vector<const E*>& into, bool silent = false) {
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) {
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) {
159 }
160 }
161
162private:
164 SUMOAbstractRouter<E, V>(other),
165 myInternalRouter(nullptr),
166 myOriginal(other),
167 mySilent(other->mySilent),
169 {}
170
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) {
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()) {
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
340private:
344 std::vector<_RailEdge*> myInitialEdges;
346 std::vector<_RailEdge*> myRailEdges;
347
349 const bool mySilent;
350
351 const double myMaxTrainLength;
352
354 const E* myLastFrom;
355
356#ifdef HAVE_FOX
358 mutable FXMutex myLock;
359#endif
360
363
364 static double myReversalPenalty;
366
367private:
370
371};
372
373template<class E, class V>
375template<class E, class V>
377template<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)
SUMOAbstractRouter< E, V > * clone()
std::map< const E *, RouterProhibition > Prohibitions
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.
const bool myHavePermissions
whether edge permissions need to be considered
virtual void setBulkMode(const bool mode)
std::map< const E *, RouterProhibition > Prohibitions
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
MsgHandler * myErrorMsgHandler
the handler for routing errors