Hyperiso 1.0.3
Modular flavour-physics calculations, Wilson coefficients and statistical inference
Loading...
Searching...
No Matches
ObsManager.cpp
Go to the documentation of this file.
1#include "ObsManager.h"
2
3
4ObsManager::ObsManager(ObservablePortsConfig obs_port_conf, bool init_default_decays) : obs_port_conf(obs_port_conf) {
5 auto& sm = *obs_port_conf.iobspp_sm;
6 double mu_W = sm(ParamId{ParameterType::WILSON, "EW_SCALE", 1}, DataType::VALUE);
7 double mu_b = sm(ParamId{ParameterType::WILSON, "B_SCALE", 1}, DataType::VALUE);
8 if (init_default_decays) {
9 this->decays = {
10 {DecayMapper::to_id(Decays::B__D_l_nu), std::make_shared<BDlnuDecay> (QCDOrder::NONE, mu_W, mu_b, obs_port_conf)},
11 {DecayMapper::to_id(Decays::B__Dstar_l_nu), std::make_shared<BDstarlnuDecay> (QCDOrder::NONE, mu_W, mu_b, obs_port_conf)},
12 {DecayMapper::to_id(Decays::B__Kstar_gamma), std::make_shared<BKstarGammaDecay> (QCDOrder::NONE, mu_W, mu_b, obs_port_conf)},
13 {DecayMapper::to_id(Decays::B__l_l), std::make_shared<BllDecay> (QCDOrder::NONE, mu_W, mu_b, obs_port_conf)},
14 {DecayMapper::to_id(Decays::B__l_nu), std::make_shared<BlnuDecay> (QCDOrder::NONE, mu_W, mu_b, obs_port_conf)},
15 {DecayMapper::to_id(Decays::B__Xs_gamma), std::make_shared<BXsDecay> (QCDOrder::NONE, mu_W, mu_b, obs_port_conf)},
16 {DecayMapper::to_id(Decays::B__Xs_l_l), std::make_shared<BXsllDecay> (QCDOrder::NONE, mu_W, mu_b, obs_port_conf)},
17 {DecayMapper::to_id(Decays::M0_Mix), std::make_shared<M0Mixing> (QCDOrder::NONE, mu_W, mu_b, obs_port_conf)},
18 {DecayMapper::to_id(Decays::B__Kstar_l_l), std::make_shared<BKstarllDecay> (QCDOrder::NONE, mu_W, mu_b, obs_port_conf)},
19 {DecayMapper::to_id(Decays::B__K_l_l), std::make_shared<BKllDecay> (QCDOrder::NONE, mu_W, mu_b, obs_port_conf)},
20 {DecayMapper::to_id(Decays::Bs__phi_l_l), std::make_shared<BsPhiDecay> (QCDOrder::NONE, mu_W, mu_b, obs_port_conf)},
21 {DecayMapper::to_id(Decays::Lambda_b__Lambda_l_l), std::make_shared<LbLllDecay> (QCDOrder::NONE, mu_W, mu_b, obs_port_conf)},
22 {DecayMapper::to_id(Decays::K__l_l), std::make_shared<KllDecay> (QCDOrder::NONE, mu_W, mu_b, obs_port_conf)},
23 {DecayMapper::to_id(Decays::K__pi_nu_nu), std::make_shared<KPinunuDecay> (QCDOrder::NONE, mu_W, mu_b, obs_port_conf)},
24 {DecayMapper::to_id(Decays::K__l_nu), std::make_shared<KlnuDecay> (QCDOrder::NONE, mu_W, mu_b, obs_port_conf)},
25 {DecayMapper::to_id(Decays::D__l_nu), std::make_shared<DlnuDecay> (QCDOrder::NONE, mu_W, mu_b, obs_port_conf)},
26 {DecayMapper::to_id(Decays::Ds__l_nu), std::make_shared<DslnuDecay> (QCDOrder::NONE, mu_W, mu_b, obs_port_conf)},
27 };
28 }
29}
30
32 LOG_VERBOSE("Adding observable", ObservableMapper::str(id), "to manager");
33
34 // Dynamic routing: do not convert ObservableId back to the legacy Observables enum.
35 // Builtin observables are resolved by the static cache in DecayMapper::get_decay_id(),
36 // while custom observables are resolved by the runtime DecayGraph.
38
39 auto dec_it = decays.find(dec_id);
40 if (dec_it == decays.end()) {
41 LOG_ERROR("KeyError", "Observable", ObservableMapper::str(id),
42 "is attached to decay", dec_id.str(),
43 "but this decay is not registered in ObsManager.");
44 }
45
46 if (dec_it->second->is_observable_binned(id)) {
48 "APIError",
49 "Observable", ObservableMapper::str(id),
50 "requires q² bins in decay", DecayMapper::str(dec_id),
51 ". Use add_observable(BinnedObservableId, ...) or add_observables(decay, ..., bin)."
52 );
53 }
54
55 dec_it->second->set_order(order);
56
57 auto obs_ptr = std::make_shared<Observable>(id, dec_it->second, obs_port_conf.iobspp_sm);
58 obss.emplace(id, obs_ptr);
59
60 if (add_deps) {
62 }
63
64 return *this;
65}
66
69 return add_obs(obs_id, order, add_deps);
70}
71
74}
75
77 auto dec_it = this->decays.find(decay);
78 if (dec_it == this->decays.end()) {
79 LOG_ERROR("KeyError", "Decay", decay.str(), "is not registered in ObsManager.");
80 }
81 return dec_it->second->is_binned();
82}
83
87
90 auto dec_it = this->decays.find(dec_id);
91 if (dec_it == this->decays.end()) {
92 LOG_ERROR("KeyError", "Observable", ObservableMapper::str(obs),
93 "is attached to decay", dec_id.str(),
94 "but this decay is not registered in ObsManager.");
95 }
96 return dec_it->second->is_observable_binned(obs);
97}
98
103
105 const std::string obs_name = ObservableMapper::str(id.s);
106
108 "Adding binned observable",
109 obs_name,
110 "with bin [",
111 id.p.first,
112 ",",
113 id.p.second,
114 "]"
115 );
116
117 // Dynamic routing: binned observables are attached through their underlying ObservableId.
118 // This supports builtin ids and custom ids as long as the custom observable was linked to
119 // a DecayId and the corresponding DecayParent was registered in the manager.
121
122 auto dec_it = this->decays.find(dec_id);
123 if (dec_it == this->decays.end()) {
124 LOG_ERROR(
125 "KeyError",
126 "Decay",
127 dec_id.str(),
128 "not present in ObsManager for observable",
129 obs_name
130 );
131 }
132
133 if (!dec_it->second->is_observable_binned(id.s)) {
134 LOG_ERROR(
135 "APIError",
136 "Observable", obs_name,
137 "does not require q² bins in decay", DecayMapper::str(dec_id),
138 ". Use add_observable(ObservableId, ...) or add_observables(decay, ...)."
139 );
140 }
141
142 dec_it->second->set_order(order);
143
144 if (!this->obss.contains(id.s)) {
145 auto obs_ptr = std::make_shared<Observable>(
146 id.s,
147 dec_it->second,
148 obs_port_conf.iobspp_sm
149 );
150
151 obss.emplace(id.s, obs_ptr);
152
153 if (add_deps) {
154 add_all_obs_deps(id.s);
155 }
156 }
157
158 dec_it->second->add_bin(id.p);
159
161 "Successfully added bin [",
162 id.p.first,
163 ",",
164 id.p.second,
165 "] for observable",
166 obs_name,
167 "in decay",
168 dec_id.str()
169 );
170
171 return *this;
172}
173
175{
176 id = ensure_present(id, false);
177 auto dec_id = DecayMapper::get_decay_id(id);
178 obss.erase(id);
179
180 if (dec_id.has_value() && active_decay.has_value() && active_decay.value() == dec_id.value()) {
181 bool still_used = false;
182 for (const auto& [obs_id, _] : obss) {
183 auto other_dec = DecayMapper::get_decay_id(obs_id);
184 if (other_dec.has_value() && other_dec.value() == dec_id.value()) {
185 still_used = true;
186 break;
187 }
188 }
189 if (!still_used) {
190 auto dec_it = decays.find(dec_id.value());
191 if (dec_it != decays.end()) {
192 dec_it->second->disable();
193 }
194 active_decay.reset();
195 }
196 }
197
198 return *this;
199}
200
201std::vector<ObservableValue> ObsManager::evaluate(ObservableId id) {
202 select_decay(id);
203 return obss.at(ensure_present(id))->compute();
204}
205
206std::vector<ObservableValue> ObsManager::evaluate(Observables id) {
208 return evaluate(obs_id);
209}
210
212 ObservableId obs_id = id.s;
213
214 if (!is_observable_binned(obs_id)) {
215 LOG_ERROR("APIError", "Observable", ObservableMapper::str(obs_id),
216 "does not accept q² bins. Use compute_observable(ObservableId) instead.");
217 }
218
219 auto results = evaluate(obs_id);
220
221 for (auto result : results) {
222 if (result.bin.value_or(std::pair<double, double>({0.,0.})) == id.p) {
223 return result;
224 }
225 }
226 return results[0];
227}
228
229std::map<ObservableId, std::vector<ObservableValue>> ObsManager::evaluate_all() {
230 this->enable_obs();
231 std::map<ObservableId, std::vector<ObservableValue>> all_ests;
232 for (auto &[k, k_ptr] : obss) {
233 all_ests.emplace(k, k_ptr->compute());
234 }
235 return all_ests;
236}
237
238void ObsManager::add_custom_decay(DecayId id, std::shared_ptr<DecayParent> ptr) {
239 ptr->bind_wilson_builder(this->obs_port_conf.iobswb);
240 // ptr->load_params();
241 this->decays[id] = std::move(ptr);
242}
243
246
247 return add_obs_dep(obs_id, param);
248}
249
251 // Builtin observables still use the generated DependenciesHelper allow-list.
252 // Runtime/custom observables do not appear in that generated table, so their
253 // dependencies are accepted as user-declared metadata.
254 if (ObservableMapper::enum_of(id).has_value() && !DependenciesHelper::is_param_allowed(id, param)) {
255 LOG_WARN("Observable", ObservableMapper::str(id), "doesn't depend on parameter (", param.block, ",", param.code, "). Ignoring.");
256 return;
257 }
258
259 obss.at(ensure_present(id))->add_dependence(param);
260}
261
262void ObsManager::add_obs_deps(Observables id, std::unordered_set<ParamId> params) {
264
265 return add_obs_deps(obs_id, params);
266}
267
268void ObsManager::add_obs_deps(ObservableId id, std::unordered_set<ParamId> params) {
269 std::unordered_set<ParamId> accepted;
270 const bool is_builtin = ObservableMapper::enum_of(id).has_value();
271
272 for (auto &p : params) {
273 if (is_builtin && !DependenciesHelper::is_param_allowed(id, p)) {
274 LOG_WARN("Observable", ObservableMapper::str(id), "doesn't depend on parameter (", p.block, ",", p.code, "). Ignoring.");
275 continue;
276 }
277 accepted.insert(p);
278 }
279
280 obss.at(ensure_present(id))->add_dependences(std::move(accepted));
281}
282
285
286 return add_all_obs_deps(obs_id);
287}
288
290 try {
292 LOG_DEBUG("Found", allowed.size(), "allowed parameters for observable", ObservableMapper::str(id));
293 obss.at(ensure_present(id))->add_dependences(allowed);
294 } catch (const std::out_of_range&) {
295 LOG_WARN("No dependency map for observable", ObservableMapper::str(id),
296 "- adding it without automatic dependencies. Use add_obs_deps(...) "
297 "or LambdaObservableConfig::dependencies for runtime observables.");
298 }
299}
300
301std::unordered_set<ParamId> ObsManager::get_all_ops_deps(ObservableId id) {
302 if (obss.contains(id)) {
303 const auto& attached = obss.at(id)->get_dependences();
304 if (!attached.empty()) {
305 return attached;
306 }
307 }
308
309 try {
311 } catch (const std::out_of_range&) {
312 LOG_WARN("No dependency map for observable", ObservableMapper::str(id),
313 "- returning an empty dependency set.");
314 return {};
315 }
316}
317
318std::vector<BinnedObservableId> ObsManager::get_current_obss() {
319 std::vector<BinnedObservableId> ids;
320 for (const auto& [oid, _] : this->obss) {
321 DecayId dec_id = DecayMapper::get_decay_id(oid).value();
322 auto decay = this->decays.at(dec_id);
323 auto bins = decay->get_bins();
324 if (!decay->is_observable_binned(oid) || !bins.has_value()) {
325 ids.emplace_back(oid);
326 continue;
327 }
328 for (auto bin : bins.value())
329 ids.emplace_back(oid, bin);
330 }
331 return ids;
332}
333
334std::shared_ptr<Observable> ObsManager::get_obs(Observables id){
336 return get_obs(obs_id);
337}
338
339std::shared_ptr<Observable> ObsManager::get_obs(ObservableId id){
340 return this->obss.at(ensure_present(id));
341}
342
344 // Dynamic routing: use DecayMapper::get_decay_id_or_throw(id), which first checks
345 // runtime links for custom observables and then falls back to builtin mappings.
347
348 auto target_it = this->decays.find(target);
349 if (target_it == this->decays.end()) {
350 LOG_ERROR(
351 "KeyError",
352 "Observable",
354 "is attached to decay",
355 target.str(),
356 "but this decay is not registered in ObsManager."
357 );
358 }
359
360 if (active_decay.has_value() && active_decay.value() != target) {
361 auto previous_it = this->decays.find(active_decay.value());
362 if (previous_it != this->decays.end()) {
363 previous_it->second->disable();
364 }
365 } else if (!active_decay.has_value()) {
366 // After enable_obs() the manager may be in multi-active mode. Collapse
367 // back to one active decay before evaluating a single observable.
368 for (auto& [dec_id, decay] : this->decays) {
369 if (dec_id != target && decay && decay->is_enabled()) {
370 decay->disable();
371 }
372 }
373 }
374
375 target_it->second->enable();
376 active_decay = target;
377}
378
379void ObsManager::set_decay_config(Decays dec, std::any config) {
380 this->decays.at(DecayMapper::to_id(dec))->set_config(config);
381}
382
383ObservableId ObsManager::ensure_present(Observables id, bool) {
385 return ensure_present(obs_id);
386}
387
388ObservableId ObsManager::ensure_present(ObservableId id, bool critical) {
389 if (!obss.contains(id)) {
390 if (critical)
391 LOG_ERROR("KeyError", "Observable manager doesn't contain observable", ObservableMapper::str(id));
392 else
393 LOG_WARN("Observable manager doesn't contain observable", ObservableMapper::str(id));
394 }
395 return id;
396}
397
399 for (auto& elem: this->decays) {
400 if (elem.second && elem.second->is_enabled()) {
401 elem.second->load_params();
402 }
403 }
404}
405
407 std::unordered_set<DecayId> unique_decays;
408 for (auto& elem: this->obss) {
409 auto id = DecayMapper::get_decay_id(elem.first);
410 if (!id.has_value()) {
411 LOG_ERROR("ValueError", "DecayId does not exist for", elem.first.str());
412 }
413 unique_decays.emplace(id.value());
414 }
415
416 for (auto& did : unique_decays) {
417 auto dec_it = this->decays.find(did);
418 if (dec_it == this->decays.end()) {
419 LOG_ERROR("KeyError", "Decay", did.str(), "not present in ObsManager.");
420 }
421 dec_it->second->enable();
422 }
423
424 // Multiple decays may now be active. The next select_decay() call will
425 // collapse back to a single active decay and disable the others.
426 active_decay.reset();
427}
428
429
431 auto dec_id = DecayMapper::to_id(dec);
432 this->decays.at(dec_id)->set_n_threads(n_threads);
433 return *this;
434}
435
436
438 DecayThreadSnapshot snapshot;
439 for (const auto& [decay_id, decay] : decays) {
440 if (decay && decay->supports_thread_config()) {
441 snapshot.emplace(decay_id, decay->get_n_threads());
442 }
443 }
444 return snapshot;
445}
446
447void ObsManager::set_all_decay_threads(size_t n_threads) {
448 for (auto& [_, decay] : decays) {
449 if (decay && decay->supports_thread_config()) {
450 decay->set_n_threads(n_threads);
451 }
452 }
453}
454
456 for (const auto& [decay_id, n_threads] : snapshot) {
457 auto it = decays.find(decay_id);
458 if (it != decays.end() && it->second && it->second->supports_thread_config()) {
459 it->second->set_n_threads(n_threads);
460 }
461 }
462}
463
465 DecayConfigSnapshot snapshot;
466 for (const auto& [decay_id, decay] : decays) {
467 if (!decay) {
468 continue;
469 }
470
471 auto cfg = decay->get_config();
472 if (cfg.has_value()) {
473 snapshot.emplace(decay_id, std::move(cfg));
474 }
475 }
476 return snapshot;
477}
478
480 for (const auto& [decay_id, cfg] : snapshot) {
481 auto it = decays.find(decay_id);
482 if (it != decays.end() && it->second && cfg.has_value()) {
483 it->second->set_config(cfg);
484 }
485 }
486}
487
488std::vector<ObservableSelectionSnapshot> ObsManager::snapshot_observable_selection() const {
489 std::vector<ObservableSelectionSnapshot> out;
490
491 for (const auto& [obs_id, obs] : obss) {
493 auto dec_it = decays.find(decay_id);
494 if (dec_it == decays.end() || !dec_it->second) {
495 LOG_ERROR("KeyError", "Decay", decay_id.str(), "not present while snapshotting observable", ObservableMapper::str(obs_id));
496 }
497
498 const bool binned_obs = dec_it->second->is_observable_binned(obs_id);
499 const auto order = dec_it->second->get_order();
500 const auto deps = obs ? obs->get_dependences() : std::unordered_set<ParamId>{};
501
502 if (binned_obs) {
503 auto bins = dec_it->second->get_bins();
504 if (!bins.has_value() || bins->empty()) {
505 out.push_back(ObservableSelectionSnapshot{BinnedObservableId(obs_id), order, deps, true});
506 continue;
507 }
508 for (const auto& bin : bins.value()) {
509 out.push_back(ObservableSelectionSnapshot{BinnedObservableId(obs_id, bin), order, deps, true});
510 }
511 } else {
512 out.push_back(ObservableSelectionSnapshot{BinnedObservableId(obs_id), order, deps, false});
513 }
514 }
515
516 return out;
517}
518
522
524 return set_decay_threads(Decays::B__K_l_l, n_threads);
525}
526
528 return set_decay_threads(Decays::Bs__phi_l_l, n_threads);
529}
530
Observables
Definition GeneralEnum.h:4
QCDOrder
Decays
@ B__Kstar_l_l
@ K__pi_nu_nu
@ B__Dstar_l_nu
@ Bs__phi_l_l
@ B__Kstar_gamma
@ B__Xs_gamma
@ Lambda_b__Lambda_l_l
#define LOG_ERROR(type,...)
Macro for logging error messages and terminating the application.
Definition Logger.h:41
#define LOG_DEBUG(...)
Macro for logging debug messages.
Definition Logger.h:45
#define LOG_VERBOSE(...)
Macro for logging verbose messages.
Definition Logger.h:47
#define LOG_WARN(...)
Macro for logging warning messages.
Definition Logger.h:40
High-level manager for observable evaluation.
std::unordered_map< DecayId, size_t > DecayThreadSnapshot
Definition ObsManager.h:79
std::unordered_map< DecayId, std::any > DecayConfigSnapshot
Definition ObsManager.h:80
static DecayId get_decay_id_or_throw(const ObservableId &obs)
Throwing variant of get_decay_id().
static std::optional< DecayId > get_decay_id(const ObservableId &obs)
Return the parent decay id of an observable id.
static bool is_param_allowed(Observables id, ParamId pid)
Tests whether a given parameter is allowed for a given observable.
static std::unordered_set< ParamId > get_allowed_parameters(Observables id)
Returns the set of allowed parameters for a given observable.
static std::optional< Observables > enum_of(const IdOf< ObservableTag > &id)
Attempts to recover the enum value associated with an identifier.
static IdOf< DecayTag > to_id(Decays e)
Converts an enum value to an IdOf<Tag>.
static std::string str(const IdOf< ObservableTag > &id)
Returns the string representation of an identifier.
bool is_decay_binned(Decays decay) const
Return whether this decay has at least one observable requiring q² bins.
ObsManager set_decay_threads(Decays dec, size_t n_threads)
Set the thread option for a decay that supports parallel cache filling.
std::map< ObservableId, std::vector< ObservableValue > > evaluate_all()
Evaluate all currently registered observables.
void restore_decay_configs(const DecayConfigSnapshot &snapshot)
Restore runtime decay configurations on matching decays.
void set_decay_config(Decays dec, std::any config)
Set a decay configuration object.
std::unordered_set< ParamId > get_all_ops_deps(ObservableId id)
Get the full allowed dependency set for a given observable.
std::vector< BinnedObservableId > get_current_obss()
Get the set of observable ids currently registered in the manager.
void add_custom_decay(DecayId id, std::shared_ptr< DecayParent > ptr)
Register a custom decay implementation.
void add_obs_deps(Observables id, std::unordered_set< ParamId > params)
Add a set of dependencies (parameters) to an observable (public enum).
ObsManager set_lblll_threads(size_t n_threads)
Set the thread option for the Lambda_b -> Lambda l+ l- decay.
void reload_params()
Reload cached parameters for all registered decays.
ObsManager remove_obs(Observables id)
Remove an observable (public enum) from the manager.
void set_all_decay_threads(size_t n_threads)
Set all thread-configurable decays to the same thread count.
void add_obs_dep(Observables id, ParamId param)
Add a single dependency (parameter) to an observable (public enum).
ObsManager set_bkstarll_threads(size_t n_threads)
Set the thread option for the B -> K* l+ l- decay.
std::vector< ObservableValue > evaluate(Observables id)
Evaluate a single observable (public enum).
ObsManager(ObservablePortsConfig obs_port_conf, bool init_default_decays=true)
Construct an observable manager with the required ports.
Definition ObsManager.cpp:4
void add_all_obs_deps(Observables id)
Attach all allowed dependencies to an observable (public enum).
void select_decay(ObservableId id)
Enable the decay needed by an observable and disable all others.
DecayThreadSnapshot snapshot_decay_threads() const
Snapshot thread settings for all decays with thread configuration support.
bool is_observable_binned(Observables obs) const
Return whether a specific observable requires q² bins.
DecayConfigSnapshot snapshot_decay_configs() const
Snapshot runtime decay configurations that expose one.
ObsManager set_bsphi_threads(size_t n_threads)
Set the thread option for the Bs -> phi l+ l- decay.
void enable_obs()
Enable all observables' decays sequentially.
void restore_decay_threads(const DecayThreadSnapshot &snapshot)
Restore a previous decay thread snapshot.
std::shared_ptr< Observable > get_obs(Observables id)
Retrieve an observable (public enum).
ObsManager add_obs(Observables id, QCDOrder order, bool add_deps=false)
Add an observable (public enum) to the manager.
ObsManager set_bkll_threads(size_t n_threads)
Set the thread option for the B -> K l+ l- decay.
std::vector< ObservableSelectionSnapshot > snapshot_observable_selection() const
Capture selected observables, bins, orders and dependencies.
const std::string & str() const
Returns the underlying string.
Identifies an observable together with a numerical bin.
Dependency container (“ports”) for observable computations.
std::shared_ptr< IObsWilsonBuilder > iobswb
Builder used to (re)build Wilson coefficient groups on demand.
std::shared_ptr< IObsParameterProxy< ParamId, DataType, std::string, LhaID > > iobspp_sm
Parameter proxy for SM / Wilson / observable parameters.
Container for a computed observable value, optionally binned.
Composite identifier for a single parameter.
Definition ParamID.h:57
BlockName block
Name of the block where the parameter is stored.
Definition ParamID.h:73
LhaID code
Index or multi-index of the parameter inside the block.
Definition ParamID.h:82