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) {
159 myInternalRouter->setBulkMode(mode);
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) {
215 myInternalRouter->setBulkMode(false);
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
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: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
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