Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
CCHRouter.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// A SUMOAbstractRouter that answers queries via a RoutingKit Customizable
19// Contraction Hierarchy over the edge graph. The metric is re-customized
20// out-of-band (MSRoutingEngine::adaptEdgeEfforts) and published as a
21// shared_ptr snapshot; each query snapshots the current metric, so a
22// publish that lands mid-query is safe (double buffer).
23//
24// Following the AStarRouter<E, V, M> lookup-table pattern, the graph-mapping
25// class is the GRAPH template parameter (the simulation supplies its concrete
26// mapper, e.g. microsim's CCHGraph), so this header carries no simulation
27// dependencies. GRAPH must provide:
28// unsigned nodeOf(const E*) (INVALID_NODE if not a node)
29// const E* edgeOf(unsigned node)
30// const std::vector<unsigned>& tazSources(const E*) (entry-edge node sets)
31// const std::vector<unsigned>& tazSinks(const E*) (exit-edge node sets)
32// void expandNodePath(const std::vector<unsigned>&, std::vector<const E*>&)
33// static const unsigned INVALID_NODE
34//
35// Every vehicle class routes on CCH via its own published metric. Closures are
36// permission changes the per-class metric encodes as inf_weight once they have
37// been customized in; the simulation's metric provider hands out nullptr while
38// a permission flip is still pending, so those queries route exactly. The
39// embedded A* fallback is used when a class has no (fresh) metric, when the
40// vehicle routes on the ORIGINAL permissions (ignoreTransientPermissions --
41// the shared metric bakes in the live ones), or when a prohibition is NOT
42// expressible as a live permission closure (an arbitrary per-query
43// prohibition, which no shared CCH metric can represent) -- see compute() /
44// prohibitionsCoveredByMetric().
45/****************************************************************************/
46#pragma once
47#include <config.h>
48
49
50#include <memory>
51#include <vector>
52#include <cmath>
53#include <utils/common/Named.h>
58#ifdef __GNUC__
59#pragma GCC diagnostic push
60#pragma GCC diagnostic ignored "-Wunused-parameter"
61#endif
62#ifdef _MSC_VER
63#pragma warning(push)
64#pragma warning(disable: 4267 4458)
65#endif
67#ifdef _MSC_VER
68#pragma warning(pop)
69#endif
70#ifdef __GNUC__
71#pragma GCC diagnostic pop
72#endif
73
74
75// ===========================================================================
76// class definitions
77// ===========================================================================
82template<class E, class V, class GRAPH>
83class CCHRouter : public SUMOAbstractRouter<E, V> {
84public:
100 typedef void (*ResetHook)(const V*);
103
112 CCHRouter(const GRAPH* graph, MetricProvider provider,
113 Operation operation, const bool unbuildIsWarning,
114 SUMOAbstractRouter<E, V>* fallback,
115 PeriodEnd periodEnd = nullptr, ResetHook onReset = nullptr) :
116 SUMOAbstractRouter<E, V>("CCHRouter", unbuildIsWarning, operation, nullptr, false, false),
117 myGraph(graph), myMetricProvider(provider), myFallback(fallback),
118 myPeriodEnd(periodEnd), myOnReset(onReset), myProhibitionActive(false) {
119 }
120
129
130 virtual ~CCHRouter() {
131 delete myFallback;
132 }
133
135 return new CCHRouter<E, V, GRAPH>(this);
136 }
137
142 virtual void reset(const V* const vehicle) {
143 if (myOnReset != nullptr) {
144 myOnReset(vehicle);
145 }
146 myFallback->reset(vehicle);
147 }
148
149 bool compute(const E* from, const E* to, const V* const vehicle,
150 SUMOTime msTime, std::vector<const E*>& into, bool silent = false) {
151 // A CCH query can only avoid an edge the metric already encodes as inf:
152 // RoutingKit's query has no per-arc blacklist, and a query-time skip is
153 // unsound because shortcut arcs bake in shortest paths THROUGH the edge
154 // (SUMO's own CHRouter refuses prohibitions for the same reason). So we
155 // run CCH when the metric can express the request and fall back to the
156 // exact A* router otherwise: (a) no metric for this class, or (b) an
157 // active prohibition that is NOT already a live permission-closure this
158 // class's metric encodes (prohibitionsCoveredByMetric).
159 const SUMOVehicleClass vClass = vehicle == nullptr ? SVC_PASSENGER : vehicle->getVClass();
160 // A vehicle that routes on the ORIGINAL permissions (routing mode bit
161 // ROUTING_MODE_IGNORE_TRANSIENT_PERMISSIONS) cannot use the shared
162 // metric, which bakes in the LIVE permissions, transient closures
163 // included -- route it exactly, like Dijkstra/A* do natively.
164 if (vehicle != nullptr && vehicle->ignoreTransientPermissions()) {
165 return myFallback->compute(from, to, vehicle, msTime, into, silent);
166 }
167 MetricPtr metric = myMetricProvider(vClass, msTime, vehicle);
168 if (metric == nullptr || (myProhibitionActive && !prohibitionsCoveredByMetric(vClass))) {
169 return myFallback->compute(from, to, vehicle, msTime, into, silent);
170 }
171 // Endpoints -> node SETS: a district star connector expands to its
172 // member edges (phantom-node seeding); everything that is a graph
173 // node -- including legacy function="connector" net edges -- routes
174 // as a single node. TAZ member edges are the class-union set, so
175 // filter to edges this class may enter.
176 const bool fromTaz = from->isTazConnector() && myGraph->nodeOf(from) == GRAPH::INVALID_NODE;
177 const bool toTaz = to->isTazConnector() && myGraph->nodeOf(to) == GRAPH::INVALID_NODE;
178 // Mirror the exact routers' endpoint rejection (AStarRouter::compute):
179 // an endpoint the vehicle may not use fails loudly instead of quietly
180 // seeding a query on it. The metric masks only arcs BETWEEN nodes --
181 // the source edge's own closure never enters it (its effort is added
182 // as a plain source seed), so without this check a query from a
183 // hard-closed edge would "succeed". TAZ members are filtered below.
184 if (vehicle != nullptr) {
185 if (!fromTaz && from->prohibits(vehicle)) {
186 if (!silent && this->myErrorMsgHandler != nullptr) {
187 this->myErrorMsgHandler->inform("Vehicle '" + Named::getIDSecure(vehicle) + "' is not allowed on source edge '" + from->getID() + "'.");
188 }
189 return false;
190 }
191 if (!toTaz && to->prohibits(vehicle)) {
192 if (!silent && this->myErrorMsgHandler != nullptr) {
193 this->myErrorMsgHandler->inform("Vehicle '" + Named::getIDSecure(vehicle) + "' is not allowed on destination edge '" + to->getID() + "'.");
194 }
195 return false;
196 }
197 }
198 std::vector<unsigned> srcBuf, tgtBuf;
199 const std::vector<unsigned>* sources;
200 const std::vector<unsigned>* targets;
201 if (fromTaz) {
202 for (const unsigned m : myGraph->tazSources(from)) {
203 if ((myGraph->edgeOf(m)->getPermissions() & vClass) != 0) {
204 srcBuf.push_back(m);
205 }
206 }
207 sources = &srcBuf;
208 } else {
209 const unsigned s = myGraph->nodeOf(from);
210 if (s != GRAPH::INVALID_NODE) {
211 srcBuf.push_back(s);
212 }
213 sources = &srcBuf;
214 }
215 if (toTaz) {
216 for (const unsigned m : myGraph->tazSinks(to)) {
217 if ((myGraph->edgeOf(m)->getPermissions() & vClass) != 0) {
218 tgtBuf.push_back(m);
219 }
220 }
221 targets = &tgtBuf;
222 } else {
223 const unsigned t = myGraph->nodeOf(to);
224 if (t != GRAPH::INVALID_NODE) {
225 tgtBuf.push_back(t);
226 }
227 targets = &tgtBuf;
228 }
229 if (sources->empty() || targets->empty()) {
230 // unmappable endpoint (e.g. internal edge, or a TAZ with no member
231 // in this class's graph) -> exact fallback.
232 return myFallback->compute(from, to, vehicle, msTime, into, silent);
233 }
234
235 this->startQuery();
236 const double t = STEPS2TIME(msTime);
237 if (!runQuery(metric, *sources, *targets, vehicle, t)) {
238 this->endQuery(0);
239 if (!silent && this->myErrorMsgHandler != nullptr) {
240 this->myErrorMsgHandler->informf(TL("No connection between edge '%' and edge '%' found."),
241 from->getID(), to->getID());
242 }
243 return false;
244 }
245 std::vector<const E*> path;
246 buildPath(from, to, fromTaz, toTaz, path);
247 int visited = (int)path.size();
248 // Weight-period boundary: the metric of the depart period is exact as
249 // long as the trip finishes inside that period (efforts are constant
250 // within it, and no time-propagated optimum can cross the boundary if
251 // this trip does not: any path entering the next period costs at
252 // least the remaining period, which already exceeds this result).
253 // When the trip does cross, ALSO query the next period's metric and
254 // keep whichever path is cheaper under the true time-propagating
255 // walk (recomputeCosts). Exact for the common single-crossing case
256 // where the optimum follows one period's structure; trips spanning
257 // several periods keep the better of the two candidates.
258 if (myPeriodEnd != nullptr && myQuery.get_distance() > 0) {
259 const SUMOTime boundary = myPeriodEnd(msTime);
260 if (boundary != SUMOTime_MAX
261 && msTime + TIME2STEPS((double)myQuery.get_distance() / 100.) >= boundary) {
262 MetricPtr altMetric = myMetricProvider(vClass, boundary, vehicle);
263 if (altMetric != nullptr && altMetric != metric
264 && runQuery(altMetric, *sources, *targets, vehicle, t)) {
265 std::vector<const E*> altPath;
266 buildPath(from, to, fromTaz, toTaz, altPath);
267 visited += (int)altPath.size();
268 if (this->recomputeCosts(altPath, vehicle, msTime)
269 < this->recomputeCosts(path, vehicle, msTime)) {
270 path.swap(altPath);
271 }
272 }
273 }
274 }
275 into.insert(into.end(), path.begin(), path.end());
276 this->endQuery(visited);
277 return true;
278 }
279
286 void prohibit(const Prohibitions& toProhibit) {
287 myProhibited = toProhibit;
288 myProhibitionActive = !toProhibit.empty();
289 myFallback->prohibit(toProhibit);
290 }
291
292 bool supportsProhibitions() const {
293 return true; // handled by delegating to the fallback
294 }
295
296 void setBulkMode(const bool mode) {
298 myFallback->setBulkMode(mode);
299 }
300
303 void setMsgHandler(MsgHandler* const errorMsgHandler) {
305 myFallback->setMsgHandler(errorMsgHandler);
306 }
307
308private:
318 bool runQuery(MetricPtr metric, const std::vector<unsigned>& sources,
319 const std::vector<unsigned>& targets, const V* const vehicle, double t) {
320 if (metric != myBoundMetric) {
323 } else {
324 myQuery.reset();
325 }
326 const double bigEff = (double)(RoutingKit::inf_weight - 1) / 100.0;
327 for (const unsigned s : sources) {
328 const double eff = this->getEffort(myGraph->edgeOf(s), vehicle, t);
329 const unsigned d = eff < bigEff ? (unsigned)llround(eff * 100.0) : RoutingKit::inf_weight - 1;
330 myQuery.add_source(s, d);
331 }
332 for (const unsigned tt : targets) {
333 myQuery.add_target(tt, 0);
334 }
335 myQuery.run();
337 }
338
341 void buildPath(const E* from, const E* to, bool fromTaz, bool toTaz,
342 std::vector<const E*>& path) {
343 const std::vector<unsigned> nodePath = myQuery.get_node_path();
344 if (fromTaz) {
345 path.push_back(from);
346 }
347 myGraph->expandNodePath(nodePath, path);
348 if (toTaz) {
349 path.push_back(to);
350 }
351 }
352
364 for (const auto& item : myProhibited) {
365 if ((item.second.permissions & vClass) != vClass // forbids vClass here...
366 && (item.first->getPermissions() & vClass) != 0) { // ...but edge still permits it live
367 return false;
368 }
369 }
370 return true;
371 }
372
373private:
374 const GRAPH* myGraph; // shared, immutable, not owned
377 MetricPtr myBoundMetric = nullptr; // metric myQuery is currently bound to
379 PeriodEnd myPeriodEnd; // may be nullptr (static weights)
380 ResetHook myOnReset; // may be nullptr (nobody to notify)
382 Prohibitions myProhibited; // last set installed via prohibit()
383
384private:
385 CCHRouter& operator=(const CCHRouter&) = delete;
386};
long long int SUMOTime
Definition GUI.h:36
#define TL(string)
Definition MsgHandler.h:304
#define STEPS2TIME(x)
Definition SUMOTime.h:58
constexpr SUMOTime TIME2STEPS(double x)
Definition SUMOTime.h:60
#define SUMOTime_MAX
Definition SUMOTime.h:34
SUMOVehicleClass
Definition of vehicle classes to differ between different lane usage and authority types.
@ SVC_PASSENGER
vehicle is a passenger car (a "normal" car)
Contraction-hierarchy router over the edge graph mapped by GRAPH.
Definition CCHRouter.h:83
bool prohibitionsCoveredByMetric(SUMOVehicleClass vClass) const
Can the per-class CCH metric already express every edge this prohibition set forbids for vClass?...
Definition CCHRouter.h:363
SUMOTime(* PeriodEnd)(SUMOTime)
the end of the weight period containing the given time (SUMOTime_MAX = weights are static)....
Definition CCHRouter.h:95
bool myProhibitionActive
Definition CCHRouter.h:381
SUMOAbstractRouter< E, V >::Prohibitions Prohibitions
Definition CCHRouter.h:102
void setMsgHandler(MsgHandler *const errorMsgHandler)
keep the fallback reporting through the same handler (duarouter swaps handlers around route repair; C...
Definition CCHRouter.h:303
SUMOAbstractRouter< E, V >::Operation Operation
Definition CCHRouter.h:101
MetricPtr myBoundMetric
Definition CCHRouter.h:377
RoutingKit::CustomizableContractionHierarchyQuery myQuery
Definition CCHRouter.h:376
CCHRouter & operator=(const CCHRouter &)=delete
void(* ResetHook)(const V *)
the host's efforts changed behind the metrics and it reset the router (SUMOAbstractRouter::reset – ma...
Definition CCHRouter.h:100
void prohibit(const Prohibitions &toProhibit)
prohibitions: a closure that is already a live permission change is served by the per-class metric (C...
Definition CCHRouter.h:286
CCHRouter(CCHRouter *other)
clone constructor: share graph + provider, clone the fallback, fresh query scratch
Definition CCHRouter.h:122
void setBulkMode(const bool mode)
Definition CCHRouter.h:296
virtual void reset(const V *const vehicle)
the host's efforts changed (see ResetHook): notify the metric store and reset the fallback's caches....
Definition CCHRouter.h:142
ResetHook myOnReset
Definition CCHRouter.h:380
const RoutingKit::CustomizableContractionHierarchyMetric * MetricPtr
Definition CCHRouter.h:85
void buildPath(const E *from, const E *to, bool fromTaz, bool toTaz, std::vector< const E * > &path)
expand the last query's node path, bracketing it with the TAZ connectors where the endpoints are zone...
Definition CCHRouter.h:341
MetricPtr(* MetricProvider)(SUMOVehicleClass, SUMOTime, const V *)
supplies the metric for a vehicle class at a query time, or nullptr => fall back. The simulation igno...
Definition CCHRouter.h:90
PeriodEnd myPeriodEnd
Definition CCHRouter.h:379
bool runQuery(MetricPtr metric, const std::vector< unsigned > &sources, const std::vector< unsigned > &targets, const V *const vehicle, double t)
Bind the query to metric (rebinding only on change), seed all sources and targets and run it; true if...
Definition CCHRouter.h:318
virtual SUMOAbstractRouter< E, V > * clone()
Definition CCHRouter.h:134
Prohibitions myProhibited
Definition CCHRouter.h:382
SUMOAbstractRouter< E, V > * myFallback
Definition CCHRouter.h:378
virtual ~CCHRouter()
Definition CCHRouter.h:130
MetricProvider myMetricProvider
Definition CCHRouter.h:375
const GRAPH * myGraph
Definition CCHRouter.h:374
bool supportsProhibitions() const
Definition CCHRouter.h:292
CCHRouter(const GRAPH *graph, MetricProvider provider, Operation operation, const bool unbuildIsWarning, SUMOAbstractRouter< E, V > *fallback, PeriodEnd periodEnd=nullptr, ResetHook onReset=nullptr)
Constructor.
Definition CCHRouter.h:112
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...
Definition CCHRouter.h:149
virtual void inform(std::string msg, bool addType=true)
adds a new error to the list
void informf(const std::string &format, T value, Targs... Fargs)
adds a new formatted message
Definition MsgHandler.h:116
static std::string getIDSecure(const T *obj, const std::string &fallBack="NULL")
get an identifier for Named-like object which may be Null
Definition Named.h:66
virtual void setBulkMode(const bool mode)
std::map< const E *, RouterProhibition > Prohibitions
double getEffort(const E *const e, const V *const v, double t) const
virtual double recomputeCosts(const std::vector< const E * > &edges, const V *const v, SUMOTime msTime, double *lengthp=nullptr) const
virtual void setMsgHandler(MsgHandler *const errorMsgHandler)
void endQuery(int visits)
MsgHandler * myErrorMsgHandler
the handler for routing errors
const Graph * graph
CustomizableContractionHierarchyMetric * metric
CustomizableContractionHierarchyQuery & add_source(unsigned s, unsigned dist_to_s=0)
CustomizableContractionHierarchyQuery & add_target(unsigned t, unsigned dist_to_t=0)