Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
CCHMetricFamily.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 family of per-vehicle-type CCH metrics over one shared CCHGraph -- the
19// MetricProvider machinery behind CCHRouter, shared between the simulation
20// and duarouter the same way CCHGraph shares the topology.
21//
22// Keying by the vehicle TYPE (rather than the class) makes everything the
23// effort function reads from the vehicle exact per metric: the type's
24// maximum speed, per-class edge speed restrictions, routing preferences,
25// the bicycle speed table and one frozen weights.random-factor realization;
26// only individual speed-factor draws within one type share a metric (the
27// same approximation the CH family makes with its per-class prototype
28// vehicles, but at finer granularity).
29//
30// Two modes:
31//
32// STATIC -- weights never change while a (type, weight period) pair is in
33// use. Metrics are customized lazily on the first query of a pair under a
34// mutex and cached (duarouter, marouter and the simulation's free-flow
35// routers). When the efforts do change between queries -- a runtime
36// permission flip, marouter's travel times after an assignment iteration
37// -- the host flags it (flagStale) and every cached metric is refilled
38// and re-customized on the next query.
39//
40// LIVE -- weights track an adaptive speed table (the rerouting device).
41// Metrics are double-buffered: worker threads acquire-load the published
42// front while the owner refills and customizes the back at its barrier
43// and flips, so queries never observe a metric mid-customization. The
44// customize is sparse (RoutingKit partial customization over the edges
45// whose effort actually moved, behind a configurable deadband) with
46// wholesale refills after permission flips. Staleness is bounded to one
47// barrier -- CH's weightPeriod semantics: once any metric exists,
48// customize(now) must run at every barrier where an effort moved (see
49// atBarrier), and an epoch pair guards the bound as an invariant.
50//
51// The family is intentionally ignorant of WHEN to customize -- only the
52// owner knows when no query is in flight (the simulation's adaptation
53// barrier, duarouter's mutex) -- and of HOW reference vehicles are built
54// (injected factory; V construction is host-specific).
55/****************************************************************************/
56#pragma once
57#include <config.h>
58
59#include <atomic>
60#include <cmath>
61#include <limits>
62#include <map>
63#include <memory>
64#include <mutex>
65#include <utility>
66#include <vector>
70
71
72// ===========================================================================
73// class definitions
74// ===========================================================================
84template<class E, class V, class K>
86public:
98 typedef V* (*RefVehicleFactory)(const K*, int);
101 typedef void (*WeightPatch)(const Graph*, const V*, std::vector<unsigned>&);
102
113 SUMOTime begin, SUMOTime weightPeriod,
114 RefVehicleFactory factory, WeightPatch patch) :
115 myGraph(graph), myFillEffort(fillEffort), myGateEffort(nullptr),
116 myFactory(factory), myPatch(patch), myLive(false),
117 myBegin(begin), myWeightPeriod(weightPeriod),
119 }
120
153 EffortOperation gateEffort, double updateFactor,
154 double updateConstant, RefVehicleFactory factory,
155 int ensembleK = 1) :
156 myGraph(graph), myFillEffort(fillEffort), myGateEffort(gateEffort),
157 myFactory(factory), myPatch(nullptr), myLive(true),
159 myUpdateFactor(updateFactor), myUpdateConstant(updateConstant),
160 myEnsembleK(ensembleK > 1 ? ensembleK : 1) {
161 const unsigned space = myGraph->edgeIdSpace();
162 for (int i = 0; i < 2; i++) {
163 // NaN = "never applied": the first pending occurrence always
164 // passes the deadband and primes the entry
165 myAppliedEffort[i].assign(space, std::numeric_limits<double>::quiet_NaN());
166 myPendingFlag[i].assign(space, 0);
167 }
168 }
169
171 for (LiveMetric* c : myLiveMetrics) {
172 delete c->refVehicle;
173 delete c;
174 }
175 for (auto& item : myStaticMetrics) {
176 delete item.second.refVehicle;
177 }
178 }
179
182
192 MetricPtr get(const K* key, SUMOVehicleClass vClass, SUMOTime time, const V* veh) {
193 std::lock_guard<std::mutex> lock(myStaticLock);
194 if (myStale) {
195 for (auto& item : myStaticMetrics) {
196 StaticMetric& sm = item.second;
197 fillStatic(sm, item.first.second, time, sm.refVehicle != nullptr ? sm.refVehicle : veh, veh);
198 sm.metric->customize();
199 }
200 myStale = false;
201 }
202 const int period = periodOf(time);
203 const std::pair<const K*, int> mapKey(key, period);
204 auto it = myStaticMetrics.find(mapKey);
205 if (it == myStaticMetrics.end()) {
206 StaticMetric& sm = myStaticMetrics[mapKey];
207 sm.vClass = vClass;
208 const V* ref = veh;
209 if (myFactory != nullptr) {
210 sm.refVehicle = myFactory(key, 0);
211 ref = sm.refVehicle;
212 }
213 fillStatic(sm, period, time, ref, veh);
215 myGraph->cch(), sm.weights));
216 sm.metric->customize();
217 return sm.metric.get();
218 }
219 return it->second.metric.get();
220 }
221
226 return SUMOTime_MAX;
227 }
228 return myBegin + (periodOf(time) + 1) * myWeightPeriod;
229 }
230
236 void flagStale() {
237 std::lock_guard<std::mutex> lock(myStaticLock);
238 myStale = true;
239 }
240
243 flagStale();
244 }
246
249
253 void seedKey(const K* key) {
254 if (myByKey.count(key) == 0) {
255 std::vector<LiveMetric*>& slots = myByKey[key];
256 for (int k = 0; k < myEnsembleK; k++) {
257 slots.push_back(buildLiveMetric(key, k));
258 }
259 }
260 }
261
264 void markDirty(const E* e) {
265 const int id = e->getNumericalID();
266 if (id < 0 || id >= (int)myPendingFlag[0].size()) {
267 return;
268 }
269 for (int i = 0; i < 2; i++) {
270 if (!myPendingFlag[i][id]) {
271 myPendingFlag[i][id] = 1;
272 myPendingList[i].push_back(e);
273 }
274 }
275 }
276
282 void invalidateEdge(const E* e) {
283 const int id = e->getNumericalID();
284 if (id < 0 || id >= (int)myPendingFlag[0].size()) {
285 return;
286 }
287 markDirty(e);
288 // NaN sentinel: the deadband always passes, so the flip reaches the
289 // metric at the next barrier even though the speed table did not move
290 myAppliedEffort[0][id] = std::numeric_limits<double>::quiet_NaN();
291 myAppliedEffort[1][id] = std::numeric_limits<double>::quiet_NaN();
293 myMetricStale.store(true, std::memory_order_release);
294 }
295
305 void atBarrier(double now, bool effortsMoved) {
306 if (effortsMoved) {
307 mySpeedEpoch.fetch_add(1, std::memory_order_release);
308 }
309 if (myQueried.exchange(false, std::memory_order_acq_rel)
310 || myMetricStale.load(std::memory_order_acquire)
311 || (effortsMoved && !myLiveMetrics.empty())) {
312 customize(now);
313 }
314 }
315
320 void customize(double now) {
321 // types that queried since the last barrier without a metric
322 // (streamed route files, TraCI-added types): create their state now
323 {
324 std::lock_guard<std::mutex> lock(myWantedLock);
325 for (const K* key : myWantedKeys) {
326 seedKey(key);
327 }
328 myWantedKeys.clear();
329 }
330 if (myLiveMetrics.empty()) {
331 return;
332 }
333 // buffers flip in lockstep across types (every call processes all)
334 const int backShared = 1 - myLiveMetrics.front()->frontIndex;
335 // Permission flip pending: run a FULL refill on this pass's back
336 // buffer (the other buffer gets its full refill on the next pass --
337 // myFullFillsPending counts both down). The fills below re-prime the
338 // graph's connection masks, which the flip's caller invalidated.
339 bool forceFull = false;
340 if (myFullFillsPending > 0) {
342 forceFull = true;
343 }
344 // Deadband pass, once, on the type-shared gate effort: accept edges
345 // whose effort moved by more than BOTH bounds since this buffer last
346 // applied them; rejected edges stay pending (see the constructor doc)
347 std::vector<const E*> accepted;
348 if (!forceFull && myLiveMetrics.front()->metric[backShared] != nullptr) {
349 std::vector<const E*> stillPending;
350 for (const E* e : myPendingList[backShared]) {
351 const int id = e->getNumericalID();
352 const double effNow = myGateEffort(e, nullptr, now);
353 const double effApplied = myAppliedEffort[backShared][id];
354 bool pass = true;
355 // without a threshold every marked edge passes: the gate effort may be clamped
356 // at the minimum travel time while a type with speedFactor > 1 still sees changes
357 if ((myUpdateFactor > 1. || myUpdateConstant > 0.) && !std::isnan(effApplied) && effApplied > 0. && effNow > 0.) {
358 const double hi = MAX2(effNow, effApplied);
359 const double lo = MIN2(effNow, effApplied);
360 pass = (hi / lo > myUpdateFactor) && (hi - lo > myUpdateConstant);
361 }
362 if (pass) {
363 accepted.push_back(e);
364 myAppliedEffort[backShared][id] = effNow;
365 myPendingFlag[backShared][id] = 0;
366 } else {
367 stillPending.push_back(e);
368 }
369 }
370 myPendingList[backShared].swap(stillPending);
371 }
372 for (LiveMetric* c : myLiveMetrics) {
373 const int back = 1 - c->frontIndex;
374 // Fill from the live efforts, masking arcs the class is not
375 // permitted on -- this is where per-class permissions AND active
376 // closures become inf_weight. The fill reference is the type's
377 // OWNED refVehicle, so the effort captures the type's extras
378 // exactly (including each folded via edge inside viaChainEffort)
379 // and freezes ONE random realization per metric -- the same
380 // approximation CHRouterWrapper makes per hierarchy; exact
381 // per-vehicle randomization remains the domain of the exact
382 // routers.
383 if (c->metric[back] == nullptr) {
384 // first use of this buffer for THIS type only (streamed/late
385 // vType): no previous state to diff against for this type,
386 // but every other already-running type's sparse deadband
387 // state is unaffected, so this branch must NOT trigger the
388 // shared wipe below (which would reset the type-shared
389 // myPendingList/myAppliedEffort for everybody just because
390 // one type is new)
391 myGraph->fillInputWeights(myFillEffort, c->vClass, c->refVehicle, now, c->weight[back]);
392 c->metric[back] = std::make_shared<RoutingKit::CustomizableContractionHierarchyMetric>(
393 myGraph->cch(), c->weight[back]);
394 c->metric[back]->customize(); // serial; avoids OpenMP oversubscription under FOX
395 } else if (forceFull) {
396 // permission flip: wholesale refill in place from the
397 // re-primed masks and live permissions (the metric already
398 // references weight[back]), then a full customize. This is
399 // the ONLY case that invalidates the type-shared deadband
400 // state below, since it is the only one that actually
401 // changes what "applied" means for every type at once.
402 myGraph->fillInputWeights(myFillEffort, c->vClass, c->refVehicle, now, c->weight[back]);
403 c->metric[back]->customize();
404 } else {
405 // SPARSE PATH: metric[back] is the customization of the
406 // current contents of weight[back]. Recompute only the arcs
407 // of accepted edges (edge->arc reverse image), write the
408 // ones that moved, and propagate through affected triangles
409 // only. Cost scales with traffic transitions, not with the
410 // network.
411 std::vector<unsigned>& applied = c->weight[back];
412 c->partial->reset(myGraph->cch());
413 unsigned changed = 0;
414 for (const E* e : accepted) {
415 for (const unsigned a : myGraph->arcsOfEdge(e)) {
416 const unsigned newW = myGraph->computeArcWeight(a, myFillEffort, c->vClass, c->refVehicle, now);
417 if (newW != applied[a]) {
418 applied[a] = newW;
419 c->partial->update_arc(a); // takes INPUT arc ids
420 ++changed;
421 }
422 }
423 }
424 if (changed > 0) {
425 c->partial->customize(*c->metric[back]);
426 }
427 }
428 // publish lock-free: release store at the barrier, worker
429 // acquire-loads on the hot path
430 c->frontIndex = back;
431 c->front.store(c->metric[back].get(), std::memory_order_release);
432 }
433 if (forceFull) {
434 // a permission flip's full refill matched every arc to the live
435 // efforts/masks for this buffer: type-shared deadband entries
436 // are stale; NaN re-arms the first-change auto-pass. (A plain
437 // per-type first-fill above does NOT reach here -- it must not
438 // wipe deadband state that other, already-running types still
439 // depend on.)
440 for (const E* e : myPendingList[backShared]) {
441 myPendingFlag[backShared][e->getNumericalID()] = 0;
442 }
443 myPendingList[backShared].clear();
444 myAppliedEffort[backShared].assign(myAppliedEffort[backShared].size(),
445 std::numeric_limits<double>::quiet_NaN());
446 }
447 // every published front now reflects the live permissions (the other
448 // buffer, if still pending a full refill, is repaired before its
449 // next publish) and the live efforts as of this barrier (both epoch
450 // counters only move on the owner's thread at the barrier, so the
451 // pair cannot tear)
452 myMetricStale.store(false, std::memory_order_release);
453 myPublishedEpoch.store(mySpeedEpoch.load(std::memory_order_relaxed),
454 std::memory_order_release);
455 }
456
461 MetricPtr published(const K* key, const std::string& vehID) {
462 const auto it = myByKey.find(key);
463 if (it == myByKey.end()) {
464 // streamed-in type without a metric yet: register it for
465 // creation at the next barrier and fall back
466 {
467 std::lock_guard<std::mutex> lock(myWantedLock);
468 myWantedKeys.push_back(key);
469 }
470 myQueried.store(true, std::memory_order_relaxed);
471 return nullptr;
472 }
473 // mark the metric as consumed. Test before set: an unconditional
474 // store from every query would ping-pong the cache line between
475 // cores; the read is shared and the store fires once per barrier
476 // window (relaxed: only gates bootstrap/re-arm, never visibility)
477 if (!myQueried.load(std::memory_order_relaxed)) {
478 myQueried.store(true, std::memory_order_relaxed);
479 }
480 // A permission flip that is not yet customized in makes every
481 // published metric unusable: it may still route THROUGH a
482 // just-closed edge, and CCHRouter's coverage test consults the LIVE
483 // permissions, which would wrongly bless the query. Route exactly
484 // until the barrier repairs the metrics.
485 if (myMetricStale.load(std::memory_order_acquire)) {
486 return nullptr;
487 }
488 // Safety invariant: never serve a metric the efforts have moved away
489 // from -- the exact routers see live weights and CH is bounded to
490 // one weightPeriod. atBarrier re-customizes at every moved barrier
491 // once metrics exist, so this cannot fire in steady state; if a gap
492 // ever opens, this burst routes on the exact fallback (live weights)
493 // and the queried flag set above arms the customize that closes it.
494 if (myPublishedEpoch.load(std::memory_order_acquire)
495 != mySpeedEpoch.load(std::memory_order_acquire)) {
496 return nullptr;
497 }
498 // stable slot assignment: FNV-1a over the id (std::hash is
499 // implementation-defined and would break cross-platform test
500 // reproducibility)
501 const LiveMetric* c = myEnsembleK == 1 ? it->second.front()
502 : it->second[fnv1a(vehID) % myEnsembleK];
503 return c->front.load(std::memory_order_acquire);
504 }
505
507 bool empty() const {
508 return myLiveMetrics.empty();
509 }
511
512private:
515 struct LiveMetric {
516 std::vector<unsigned> weight[2]; // ping-pong input-weight buffers, live whole run
517 std::shared_ptr<RoutingKit::CustomizableContractionHierarchyMetric> metric[2];
518 std::atomic<const RoutingKit::CustomizableContractionHierarchyMetric*> front{nullptr};
519 int frontIndex = 1; // first customize uses back = 0
522 V* refVehicle = nullptr;
525 std::shared_ptr<RoutingKit::CustomizableContractionHierarchyPartialCustomization> partial;
526 };
527
532 std::vector<unsigned> weights;
533 std::unique_ptr<RoutingKit::CustomizableContractionHierarchyMetric> metric;
537 V* refVehicle = nullptr;
539 bool filled = false;
542 std::vector<std::pair<unsigned, unsigned> > patched;
543 };
544
547 int periodOf(SUMOTime time) const {
548 if (myWeightPeriod > 0 && myWeightPeriod != SUMOTime_MAX && time > myBegin) {
549 return (int)((time - myBegin) / myWeightPeriod);
550 }
551 return 0;
552 }
553
563 void fillStatic(StaticMetric& sm, int period, SUMOTime time, const V* ref, const V* veh) {
564 const double fillTime = myWeightPeriod == SUMOTime_MAX
565 ? STEPS2TIME(time) : STEPS2TIME(myBegin + period * myWeightPeriod);
566 myGraph->fillInputWeights(myFillEffort, sm.vClass, ref, fillTime, sm.weights);
567 if (!sm.filled) {
568 sm.filled = true;
569 if (myPatch != nullptr) {
570 const std::vector<unsigned> unpatched(sm.weights);
571 myPatch(myGraph, veh, sm.weights);
572 for (unsigned a = 0; a < (unsigned)sm.weights.size(); a++) {
573 if (sm.weights[a] != unpatched[a]) {
574 sm.patched.emplace_back(a, sm.weights[a]);
575 }
576 }
577 }
578 } else {
579 for (const auto& p : sm.patched) {
580 sm.weights[p.first] = p.second;
581 }
582 }
583 }
584
586 static uint64_t fnv1a(const std::string& s) {
587 uint64_t h = 1469598103934665603ull;
588 for (const char ch : s) {
589 h = (h ^ (unsigned char)ch) * 1099511628211ull;
590 }
591 return h;
592 }
593
596 LiveMetric* buildLiveMetric(const K* key, int slot) {
597 LiveMetric* c = new LiveMetric();
598 c->refVehicle = myFactory(key, slot);
599 c->vClass = c->refVehicle->getVClass();
600 c->weight[0].resize(myGraph->arcCount());
601 c->weight[1].resize(myGraph->arcCount());
602 c->partial = std::make_shared<RoutingKit::CustomizableContractionHierarchyPartialCustomization>(myGraph->cch());
603 myLiveMetrics.push_back(c);
604 return c;
605 }
606
618 const bool myLive;
619
624 std::map<std::pair<const K*, int>, StaticMetric> myStaticMetrics;
625 std::mutex myStaticLock;
628 bool myStale = false;
630
637 int myEnsembleK = 1;
640 std::vector<LiveMetric*> myLiveMetrics;
641 std::map<const K*, std::vector<LiveMetric*> > myByKey;
645 std::vector<const K*> myWantedKeys;
646 std::mutex myWantedLock;
649 std::atomic<bool> myQueried{false};
652 std::atomic<bool> myMetricStale{false};
658 std::atomic<uint64_t> mySpeedEpoch{0};
659 std::atomic<uint64_t> myPublishedEpoch{0};
668 std::vector<double> myAppliedEffort[2];
669 std::vector<char> myPendingFlag[2];
670 std::vector<const E*> myPendingList[2];
672
673private:
676};
long long int SUMOTime
Definition GUI.h:36
#define STEPS2TIME(x)
Definition SUMOTime.h:58
#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)
T MIN2(T a, T b)
Definition StdDefs.h:80
T MAX2(T a, T b)
Definition StdDefs.h:86
Metric-independent RoutingKit CCH topology over the PURE road graph.
Definition CCHGraph.h:97
unsigned edgeIdSpace() const
size of the edge numerical-id space the graph was built over (for callers keeping per-edge-id side ar...
Definition CCHGraph.h:245
unsigned arcCount() const
number of input arcs (== number of mapped connections)
Definition CCHGraph.h:239
double(* EffortOperation)(const E *const, const V *const, double)
effort callback signature, matching SUMOAbstractRouter::Operation
Definition CCHGraph.h:100
void fillInputWeights(EffortOperation effort, SUMOVehicleClass maskClass, const V *veh, double time, std::vector< unsigned > &weight) const
Fill a centisecond input-weight buffer for one vehicle class.
Definition CCHGraph.h:347
const RoutingKit::CustomizableContractionHierarchy & cch() const
the immutable CCH (share const& across clones)
Definition CCHGraph.h:234
const std::vector< unsigned > & arcsOfEdge(const E *e) const
the arcs whose weight depends on the given edge (arcs it heads plus arcs whose folded via chain conta...
Definition CCHGraph.h:281
unsigned computeArcWeight(unsigned a, EffortOperation effort, SUMOVehicleClass maskClass, const V *veh, double time) const
Recompute the input weight of one arc – the exact per-arc body of fillInputWeights (same masking,...
Definition CCHGraph.h:326
per-vehicle-type CCH metric store over a shared CCHGraph
std::map< const K *, std::vector< LiveMetric * > > myByKey
bool myStale
the efforts changed behind the cached metrics: refill and re-customize them all on the next get() (se...
std::vector< const E * > myPendingList[2]
MetricPtr get(const K *key, SUMOVehicleClass vClass, SUMOTime time, const V *veh)
the metric for (type key, period of time), built on the first query of the pair; nullptr before init....
void seedKey(const K *key)
allocate the metric state (all ensemble slots) for one type; call only while no query is in flight (s...
const Graph * myGraph
the immutable shared topology
EffortOperation myFillEffort
the effort the metrics are filled from
std::map< std::pair< const K *, int >, StaticMetric > myStaticMetrics
int periodOf(SUMOTime time) const
the weight period containing time: 0 is everything before begin and the whole run when the weights ar...
WeightPatch myPatch
post-fill weight hook (STATIC)
std::mutex myWantedLock
const bool myLive
LIVE or STATIC (fixed at construction)
std::vector< LiveMetric * > myLiveMetrics
every type's state, in creation order (barrier iteration) and by key (query lookup; only mutated whil...
void atBarrier(double now, bool effortsMoved)
the owner's customization barrier: bump the speed epoch when an effort moved and re-customize when it...
int myFullFillsPending
how many customize passes must run a FULL refill after a permission flip: 2 = both ping-pong buffers ...
std::atomic< bool > myQueried
whether a query arrived since the last barrier (bootstraps type creation and re-arms after resets – s...
RefVehicleFactory myFactory
reference-vehicle factory (required LIVE, optional STATIC)
std::atomic< uint64_t > myPublishedEpoch
void customize(double now)
refill + customize + publish every type's metric from the live efforts and permissions....
std::vector< const K * > myWantedKeys
types that queried but have no metric yet (demand streams, so types can appear after seeding); querie...
V *(* RefVehicleFactory)(const K *, int)
builds an OWNED effort-reference vehicle for a type: never registered, counted or inserted – it exist...
CCHMetricFamily(const Graph *graph, EffortOperation fillEffort, SUMOTime begin, SUMOTime weightPeriod, RefVehicleFactory factory, WeightPatch patch)
construct a STATIC family (lazy build per (type, period))
void fillStatic(StaticMetric &sm, int period, SUMOTime time, const V *ref, const V *veh)
(re)fill a STATIC metric's input weights from the live efforts. A weight-period grid evaluates at the...
CCHMetricFamily & operator=(const CCHMetricFamily &)=delete
bool empty() const
whether any type has live metric state yet
void flagStale()
the efforts behind the cached metrics changed – a runtime permission change in the simulation (the ca...
const RoutingKit::CustomizableContractionHierarchyMetric * MetricPtr
void invalidateEdge(const E *e)
a runtime permission change (closure / re-opening) hit this edge: queue it bypassing the deadband and...
std::atomic< uint64_t > mySpeedEpoch
barrier counter of the tracked efforts and the counter value the published metrics were customized fr...
CCHMetricFamily(const Graph *graph, EffortOperation fillEffort, EffortOperation gateEffort, double updateFactor, double updateConstant, RefVehicleFactory factory, int ensembleK=1)
construct a LIVE family (double-buffered, barrier-customized)
std::atomic< bool > myMetricStale
a permission flip has not yet been customized into the published metrics (queries divert to the exact...
CCHGraph< E, V > Graph
SUMOTime periodEnd(SUMOTime time) const
the end of the weight period containing the given time (SUMOTime_MAX when the weights are static); CC...
int myEnsembleK
frozen random-factor realizations per type (see the LIVE ctor)
EffortOperation myGateEffort
the type-independent effort of the LIVE deadband
Graph::EffortOperation EffortOperation
MetricPtr published(const K *key, const std::string &vehID)
the published metric for a type key and querying vehicle id (the id picks the ensemble slot – see the...
void flagPermissionsStale()
flagStale() under the simulation's name for the permission case
std::vector< double > myAppliedEffort[2]
per-buffer edge state for the sparse path: the gate effort each edge's arcs were last filled from (Na...
static uint64_t fnv1a(const std::string &s)
stable 64-bit FNV-1a for the ensemble slot assignment
void markDirty(const E *e)
queue an edge whose gate effort changed (both buffers; the deadband applies later,...
void(* WeightPatch)(const Graph *, const V *, std::vector< unsigned > &)
post-fill hook on the input weights (STATIC mode), e.g. duarouter masking the edges that restrict a t...
LiveMetric * buildLiveMetric(const K *key, int slot)
allocate LIVE metric state for one (type, ensemble slot) (owner's thread only)
std::vector< char > myPendingFlag[2]
std::mutex myStaticLock
CCHMetricFamily(const CCHMetricFamily &)=delete
const Graph * graph
per-type LIVE metric state. Heap-owned: the atomic makes it non-movable, so it cannot live in a map b...
V * refVehicle
OWNED effort-reference vehicle (see RefVehicleFactory)
std::vector< unsigned > weight[2]
std::atomic< const RoutingKit::CustomizableContractionHierarchyMetric * > front
std::shared_ptr< RoutingKit::CustomizableContractionHierarchyPartialCustomization > partial
partial-customization worker (queue over the shared CCH); one per metric – its queue is drained by ev...
std::shared_ptr< RoutingKit::CustomizableContractionHierarchyMetric > metric[2]
per-(type, period) STATIC metric state. RoutingKit metrics BORROW their input-weight buffer,...
std::vector< unsigned > weights
V * refVehicle
OWNED reference vehicle (nullptr when filling with the querying vehicle)
std::vector< std::pair< unsigned, unsigned > > patched
the (arc, weight) pairs the patch wrote on the first fill, re-applied on every refill
bool filled
whether the first fill (which runs the patch) is done
std::unique_ptr< RoutingKit::CustomizableContractionHierarchyMetric > metric