Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2024 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 :
76 : public:
77 :
78 : /// Constructor
79 487 : RailwayRouter(const std::vector<E*>& edges, bool unbuildIsWarning, typename SUMOAbstractRouter<E, V>::Operation effortOperation,
80 : typename SUMOAbstractRouter<E, V>::Operation ttOperation = nullptr, bool silent = false,
81 : const bool havePermissions = false, const bool haveRestrictions = false, double maxTrainLength = 5000) :
82 : SUMOAbstractRouter<E, V>("RailwayRouter", unbuildIsWarning, effortOperation, ttOperation, havePermissions, haveRestrictions),
83 487 : myInternalRouter(nullptr), myOriginal(nullptr), mySilent(silent),
84 974 : myMaxTrainLength(maxTrainLength) {
85 487 : myStaticOperation = effortOperation;
86 143264 : for (const E* const edge : edges) {
87 142777 : myInitialEdges.push_back(edge->getRailwayRoutingEdge());
88 : }
89 487 : }
90 :
91 : /// Destructor
92 1600 : virtual ~RailwayRouter() {
93 574 : delete myInternalRouter;
94 2174 : }
95 :
96 313 : SUMOAbstractRouter<E, V>* clone() {
97 313 : return new RailwayRouter<E, V>(this);
98 : }
99 :
100 : /** @brief Builds the route between the given edges using the minimum effort at the given time
101 : The definition of the effort depends on the wished routing scheme */
102 28589 : bool compute(const E* from, const E* to, const V* const vehicle, SUMOTime msTime, std::vector<const E*>& into, bool silent = false) {
103 28589 : ensureInternalRouter();
104 28589 : if (vehicle->getLength() > myMaxTrainLength) {
105 15 : WRITE_WARNINGF("Vehicle '%' with length % exceeds configured value of --railway.max-train-length %",
106 : vehicle->getID(), toString(vehicle->getLength()), toString(myMaxTrainLength));
107 : }
108 28589 : return _compute(from, to, vehicle, msTime, into, silent, false);
109 : }
110 :
111 78 : void prohibit(const std::vector<E*>& toProhibit) {
112 78 : ensureInternalRouter();
113 : std::vector<_RailEdge*> railEdges;
114 90 : for (E* edge : toProhibit) {
115 12 : railEdges.push_back(edge->getRailwayRoutingEdge());
116 : }
117 78 : myInternalRouter->prohibit(railEdges);
118 78 : this->myProhibited = toProhibit;
119 : #ifdef RailwayRouter_DEBUG_ROUTES
120 : std::cout << "RailRouter prohibit=" << toString(toProhibit) << "\n";
121 : #endif
122 78 : }
123 :
124 :
125 20268 : double recomputeCosts(const std::vector<const E*>& edges, const V* const v, SUMOTime msTime, double* lengthp = nullptr) const {
126 20268 : double effort = SUMOAbstractRouter<E, V>::recomputeCosts(edges, v, msTime, lengthp);
127 : const E* prev = nullptr;
128 : // reversal corrections
129 20268 : double timeCorrection = STEPS2TIME(msTime);
130 733583 : for (const E* const e : edges) {
131 713315 : if (prev != nullptr && e->getBidiEdge() == prev) {
132 15069 : if (e->getLength() > v->getLength()) {
133 : // vehicle doesn't need to drive to the end of the edge
134 : // @note exact timing correction for time-dependent effort is not done
135 15027 : const double savingsFactor = 1 - v->getLength() / e->getLength();
136 : double effortCorrection = 0;
137 15027 : double lengthCorrection = 0.;
138 15027 : effortCorrection += this->getEffort(prev, v, timeCorrection);
139 15027 : this->updateViaCost(prev, e, v, timeCorrection, effortCorrection, lengthCorrection);
140 15027 : effort -= savingsFactor * effortCorrection;
141 15027 : if (lengthp != nullptr) {
142 0 : *lengthp -= savingsFactor * lengthCorrection;
143 : }
144 : }
145 : }
146 : prev = e;
147 : }
148 20268 : return effort;
149 : }
150 :
151 :
152 : private:
153 313 : RailwayRouter(RailwayRouter* other) :
154 : SUMOAbstractRouter<E, V>(other),
155 313 : myInternalRouter(nullptr),
156 313 : myOriginal(other),
157 313 : mySilent(other->mySilent),
158 313 : myMaxTrainLength(other->myMaxTrainLength)
159 313 : {}
160 :
161 28667 : void ensureInternalRouter() {
162 28667 : if (myInternalRouter == nullptr) {
163 1148 : myInternalRouter = new _InternalDijkstra(getRailEdges(), this->myErrorMsgHandler == MsgHandler::getWarningInstance(), &getTravelTimeStatic,
164 574 : nullptr, mySilent, nullptr, this->myHavePermissions, this->myHaveRestrictions);
165 : }
166 28667 : }
167 :
168 28612 : bool _compute(const E* from, const E* to, const V* const vehicle, SUMOTime msTime, std::vector<const E*>& into, bool silent, bool avoidUnsafeBackTracking) {
169 : // 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)
170 : std::vector<double> backLengths;
171 28612 : double backDist = vehicle->getLength() - from->getLength();
172 : const E* start = from;
173 29279 : while (backDist > 0) {
174 1071 : const E* prev = getStraightPredecessor(start, into, (int)backLengths.size());
175 1071 : if (prev == nullptr) {
176 : #ifdef RailwayRouter_DEBUG_ROUTES
177 : std::cout << " Could not determine back edge for vehicle '" << vehicle->getID() << "' when routing from edge '" << from->getID() << "' at time=" << time2string(msTime) << "\n";
178 : #endif
179 : //WRITE_WARNING("Could not determine back edge for vehicle '" + vehicle->getID() + "' when routing from edge '" + from->getID() + "' at time=" + time2string(msTime));
180 : break;
181 : }
182 690 : backDist -= prev->getLength();
183 690 : if (avoidUnsafeBackTracking && prev->getSuccessors().size() > 1) {
184 : bool foundSwitch = false;
185 41 : for (const E* succ : prev->getSuccessors()) {
186 41 : if (succ != start && succ != prev->getBidiEdge()) {
187 : foundSwitch = true;
188 : break;
189 : }
190 : }
191 23 : if (foundSwitch) {
192 : break;
193 : }
194 : }
195 667 : backLengths.push_back(prev->getLength() + (backLengths.empty()
196 667 : ? MIN2(vehicle->getLength(), from->getLength())
197 : : backLengths.back()));
198 : start = prev;
199 : }
200 :
201 : std::vector<const _RailEdge*> intoTmp;
202 28612 : bool success = myInternalRouter->compute(start->getRailwayRoutingEdge(), to->getRailwayRoutingEdge(), vehicle, msTime, intoTmp, silent);
203 : #ifdef RailwayRouter_DEBUG_ROUTES
204 : std::cout << "RailRouter veh=" << vehicle->getID() << " from=" << from->getID() << " to=" << to->getID() << " t=" << time2string(msTime)
205 : << " safe=" << avoidUnsafeBackTracking << " success=" << success << " into=" << toString(into) << "\n";
206 : #endif
207 28612 : if (success) {
208 : const size_t intoSize = into.size();
209 28571 : int backIndex = (int)backLengths.size();
210 418939 : for (const _RailEdge* railEdge : intoTmp) {
211 390368 : if (railEdge->getOriginal() != nullptr) {
212 382690 : backIndex--;
213 : }
214 : // prevent premature reversal on back edge (extend train length)
215 390368 : const double length = backIndex >= 0 ? backLengths[backIndex] : vehicle->getLength();
216 390368 : railEdge->insertOriginalEdges(length, into);
217 : }
218 : #ifdef RailwayRouter_DEBUG_ROUTES
219 : std::cout << "RailRouter: internal result=" << toString(intoTmp) << "\n";
220 : std::cout << "RailRouter: expanded result=" << toString(into) << "\n";
221 : #endif
222 28571 : if (backLengths.size() > 0) {
223 : // skip the virtual back-edges
224 : into.erase(into.begin() + intoSize, into.begin() + intoSize + backLengths.size());
225 : #ifdef RailwayRouter_DEBUG_ROUTES
226 : std::cout << "RailRouter: backLengths=" << toString(backLengths) << " intoSize=" << intoSize << " final result=" << toString(into) << "\n";
227 : #endif
228 541 : if (*(into.begin() + intoSize) != from) {
229 23 : if (!avoidUnsafeBackTracking) {
230 : // try again, this time with more safety (but unable to
231 : // make use of turn-arounds on short edge)
232 : into.erase(into.begin() + intoSize, into.end());
233 23 : return _compute(from, to, vehicle, msTime, into, silent, true);
234 : } else {
235 0 : WRITE_WARNING("Railway routing failure due to turn-around on short edge '" + from->getID()
236 : + "' for vehicle '" + vehicle->getID() + "' time=" + time2string(msTime) + ".");
237 : }
238 : }
239 : }
240 28548 : if (this->myProhibited.size() > 0) {
241 : // make sure that turnarounds don't use prohibited edges
242 24 : for (const E* e : into) {
243 21 : if (std::find(this->myProhibited.begin(), this->myProhibited.end(), e) != this->myProhibited.end()) {
244 : into.clear();
245 : success = false;
246 : break;
247 : }
248 : }
249 : }
250 : }
251 : return success;
252 :
253 28612 : }
254 :
255 679 : const std::vector<_RailEdge*>& getRailEdges() {
256 679 : if (myOriginal != nullptr) {
257 105 : return myOriginal->getRailEdges();
258 : }
259 : #ifdef HAVE_FOX
260 574 : FXMutexLock locker(myLock);
261 : #endif
262 574 : if (myRailEdges.empty()) {
263 469 : myRailEdges = myInitialEdges;
264 469 : int numericalID = myInitialEdges.back()->getNumericalID() + 1;
265 34992 : for (_RailEdge* railEdge : myInitialEdges) {
266 34523 : railEdge->init(myRailEdges, numericalID, myMaxTrainLength);
267 : }
268 : }
269 574 : return myRailEdges;
270 : }
271 :
272 1248726 : static inline double getTravelTimeStatic(const RailEdge<E, V>* const edge, const V* const veh, double time) {
273 1248726 : if (edge->getOriginal() != nullptr) {
274 1172714 : return (*myStaticOperation)(edge->getOriginal(), veh, time);
275 : } else {
276 : // turnaround edge
277 76012 : if (edge->isVirtual()) {
278 : // add up time for replacement edges
279 : std::vector<const E*> repl;
280 56061 : edge->insertOriginalEdges(veh->getLength(), repl);
281 : assert(repl.size() > 0);
282 : double seenDist = 0;
283 : double result = 0;
284 : repl.pop_back(); // last edge must not be used fully
285 215168 : for (const E* e : repl) {
286 159107 : result += (*myStaticOperation)(e, veh, time + result);
287 159107 : seenDist += e->getLength();
288 : }
289 56061 : const double lengthOnLastEdge = MAX2(0.0, veh->getLength() - seenDist);
290 56061 : return result + myReversalPenalty + lengthOnLastEdge * myReversalPenaltyFactor;
291 56061 : } else {
292 : // if the edge from which this turnaround starts is longer
293 : // than the vehicle then we are overstimating the cost
294 : // (because the turnaround may happen before driving to the end)
295 : // However, unless the vehicle starts on this edge, we should be taking a
296 : // virtual turnaround at the end of the previous edge. Otherwise, the exaggerated cost doesn't
297 19951 : return myReversalPenalty;
298 : }
299 : }
300 : }
301 :
302 :
303 1071 : static const E* getStraightPredecessor(const E* edge, std::vector<const E*>& prevRoute, int backIndex) {
304 : const E* result = nullptr;
305 : #ifdef RailwayRouter_DEBUG_ROUTES
306 : std::cout << " getStraightPredecessor edge=" << edge->getID() << " prevRouteSize=" << prevRoute.size() << " backIndex=" << backIndex << "\n";
307 : #endif
308 1071 : if ((int)prevRoute.size() > backIndex) {
309 124 : return prevRoute[(int)prevRoute.size() - 1 - backIndex];
310 : }
311 2510 : for (const E* cand : edge->getPredecessors()) {
312 1704 : if (!cand->isInternal() && cand->getBidiEdge() != edge) {
313 : //std::cout << " cand=" << cand->getID() << "\n";
314 848 : if (result == nullptr) {
315 : result = cand;
316 : } else {
317 : // predecessor not unique. Better abort with a warning
318 : return nullptr;
319 : }
320 : }
321 : }
322 : return result;
323 : }
324 :
325 : private:
326 : _InternalRouter* myInternalRouter;
327 : RailwayRouter<E, V>* const myOriginal;
328 : /// @brief a RailEdge for every existing edge, filled on construction (but not in clones)
329 : std::vector<_RailEdge*> myInitialEdges;
330 : /// @brief complete rail network filled on demand (but not in clones)
331 : std::vector<_RailEdge*> myRailEdges;
332 :
333 : /// @brief whether to suppress warning/error if no route was found
334 : const bool mySilent;
335 :
336 : const double myMaxTrainLength;
337 :
338 : #ifdef HAVE_FOX
339 : /// The mutex used to avoid concurrent updates of myRailEdges
340 : mutable FXMutex myLock;
341 : #endif
342 :
343 : /// @brief The object's operation to perform. (hack)
344 : static typename SUMOAbstractRouter<E, V>::Operation myStaticOperation;
345 :
346 : static double myReversalPenalty;
347 : static double myReversalPenaltyFactor;
348 :
349 : private:
350 : /// @brief Invalidated assignment operator
351 : RailwayRouter& operator=(const RailwayRouter& s);
352 :
353 : };
354 :
355 : template<class E, class V>
356 : typename SUMOAbstractRouter<E, V>::Operation RailwayRouter<E, V>::myStaticOperation(nullptr);
357 : template<class E, class V>
358 : double RailwayRouter<E, V>::myReversalPenalty(60);
359 : template<class E, class V>
360 : double RailwayRouter<E, V>::myReversalPenaltyFactor(0.2); // 1/v
|