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) {
158 myInternalRouter->setBulkMode(mode);
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) {
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()) {
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
template FXMutex GLObjectValuePassConnector< double >::myLock
Definition GUINet.cpp:74
#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:58
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:49
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
std::map< const _RailEdge *, RouterProhibition > Prohibitions
double(* Operation)(const E *const, const V *const, double)
Type of the function that is used to retrieve the edge effort.
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
SUMOAbstractRouter(const std::string &type, bool unbuildIsWarning, Operation operation, Operation ttOperation, const bool havePermissions, const bool haveRestrictions)
Constructor.
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 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