aegir
Phlex-based simulation framework for the SHiP experiment.
Loading...
Searching...
No Matches
mc_particle_source.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 CERN for the benefit of the SHiP Collaboration
2//
3// SPDX-License-Identifier: LGPL-3.0-or-later
4
5// mc_particle_source.hpp — shared helper for event-generator sources
6//
7// A Phlex source registers its data-product providers implicitly: a
8// phlex::source implements create_providers(selector), returning the
9// provider bundles that satisfy the requested product. Every aegir event
10// generator publishes the same products — the "mc_particles" collection
11// (std::vector<SHiP::MCParticle>) and the "event_header" record
12// (SHiP::EventHeader), both on the "event" layer — so the bundle
13// construction is factored out here.
14//
15// The header carries the per-event weight. Sources that sample uniformly
16// leave the generator empty and get the unweighted default (weight 1.0,
17// no originating id), which keeps the output schema identical across all
18// workflows; sources reading a weighted record (eventcalc_source) pass a
19// generator.
20//
21// Implicit providers must name the stage their products belong to, and
22// Phlex rejects the reserved "CURRENT". Phlex does not pass the job's stage
23// to sources, so each generator source reads it from a `stage` key in its
24// own configuration block; the workflows set it to the job's stage.
25
26#pragma once
27
28#include <SHiP/EventHeader.hpp>
29#include <SHiP/MCParticle.hpp>
30#include <functional>
31#include <stdexcept>
32#include <string>
33#include <utility>
34#include <vector>
35
36#include "phlex/concurrency.hpp"
37#include "phlex/configuration.hpp"
38#include "phlex/core/product_selector.hpp"
39#include "phlex/model/data_cell_index.hpp"
40#include "phlex/model/products.hpp"
41#include "phlex/source.hpp"
42
43namespace aegir {
44
45// Generator signature: produce the particles for a single data cell (event).
47 std::function<std::vector<SHiP::MCParticle>(phlex::data_cell_index const&)>;
48
49// Generator signature: produce the metadata for a single data cell. May be
50// empty, in which case the unweighted default is published.
52 std::function<SHiP::EventHeader(phlex::data_cell_index const&)>;
53
54// Read the required `stage` key of a generator source's configuration block.
55[[nodiscard]] inline std::string source_stage(
56 phlex::configuration const& config, std::string const& component) {
57 auto stage = config.get_if_present<std::string>("stage");
58 if (!stage || stage->empty() || *stage == "CURRENT") {
59 throw std::runtime_error(
60 component +
61 ": the source block needs a 'stage' key naming the job's stage "
62 "(e.g. stage: 'simulation'); 'CURRENT' is reserved");
63 }
64 return std::move(*stage);
65}
66
67// Build the implicit-provider bundle(s) for a source emitting the
68// "mc_particles" and "event_header" products in the given stage, honouring
69// the framework's product selector. The generators are invoked once per data
70// cell and their results type-erased into Phlex products.
71inline phlex::provider_bundles mc_particle_provider_bundles(
72 phlex::product_selector const& selector, std::string const& stage,
73 mc_particle_generator generate, phlex::concurrency max_concurrency,
74 event_header_generator generate_header = {}) {
75 using namespace phlex::experimental;
76
77 phlex::provider_bundles bundles;
78 std::string const layer = "event";
79
80 product_specification particles_spec{
81 algorithm_name::create("mc_particles"), identifier{"particles"},
82 make_type_id<std::vector<SHiP::MCParticle>>()};
83
84 if (selector.match(particles_spec, identifier{layer}, identifier{stage})) {
85 bundles.push_back(phlex::provider_bundle{
86 .provider_function =
87 [generate = std::move(generate)](phlex::data_cell_index const& id)
88 -> product_ptr { return product_for(generate(id)); },
89 .max_concurrency = max_concurrency,
90 .spec = std::move(particles_spec),
91 .layer = layer,
92 .stage = stage});
93 }
94
95 product_specification header_spec{algorithm_name::create("event_header"),
96 identifier{"header"},
97 make_type_id<SHiP::EventHeader>()};
98
99 if (selector.match(header_spec, identifier{layer}, identifier{stage})) {
100 bundles.push_back(phlex::provider_bundle{
101 .provider_function =
102 [generate_header = std::move(generate_header)](
103 phlex::data_cell_index const& id) -> product_ptr {
104 // Unweighted default: every event counts once, no provenance.
105 if (!generate_header) {
106 return product_for(SHiP::EventHeader{});
107 }
108 return product_for(generate_header(id));
109 },
110 .max_concurrency = max_concurrency,
111 .spec = std::move(header_spec),
112 .layer = layer,
113 .stage = stage});
114 }
115
116 return bundles;
117}
118
119} // namespace aegir
Definition hepmc_status.hpp:17
std::function< std::vector< SHiP::MCParticle >(phlex::data_cell_index const &)> mc_particle_generator
Definition mc_particle_source.hpp:47
std::string source_stage(phlex::configuration const &config, std::string const &component)
Definition mc_particle_source.hpp:55
std::function< SHiP::EventHeader(phlex::data_cell_index const &)> event_header_generator
Definition mc_particle_source.hpp:52
phlex::provider_bundles mc_particle_provider_bundles(phlex::product_selector const &selector, std::string const &stage, mc_particle_generator generate, phlex::concurrency max_concurrency, event_header_generator generate_header={})
Definition mc_particle_source.hpp:71