9 #include <framework/logging/Logger.h>
10 #include <framework/gearbox/Unit.h>
11 #include <generators/particlegun/ParticleGun.h>
37 double randomPolyline(
size_t n,
const double* x,
const double* y)
41 std::vector<double> weights(n - 1);
43 for (
size_t i = 0; i < n - 1; ++i) {
44 weights[i] = (x[i + 1] - x[i]) * max(y[i], y[i + 1]);
50 double weight = gRandom->Uniform(0, sumw);
52 for (; segment < n - 1; ++segment) {
53 weight -= weights[segment];
54 if (weight <= 0)
break;
56 const double x1 = x[segment];
57 const double x2 = x[segment + 1];
58 const double xx = gRandom->Uniform(x1, x2);
60 const double y1 = y[segment];
61 const double y2 = y[segment + 1];
62 const double yy = (y2 == y1) ? y1 : y1 + (xx - x1) / (x2 - x1) * (y2 - y1);
64 const double randY = gRandom->Uniform(0, max(y1, y2));
66 if (randY < yy)
return xx;
73 double ParticleGun::generateValue(EDistribution dist,
const vector<double>& params)
79 case c_uniformDistribution:
80 case c_uniformPtDistribution:
81 return gRandom->Uniform(params[0], params[1]);
82 case c_inversePtDistribution:
83 return 1. / gRandom->Uniform(1. / params[1], 1. / params[0]);
84 case c_uniformCosDistribution:
85 return acos(gRandom->Uniform(cos(params[0]), cos(params[1])));
86 case c_uniformLogDistribution:
87 case c_uniformLogPtDistribution:
88 return exp(gRandom->Uniform(log(params[0]), log(params[1])));
89 case c_normalDistribution:
90 case c_normalPtDistribution:
91 return gRandom->Gaus(params[0], params[1]);
92 case c_normalCosDistribution:
93 return acos(gRandom->Gaus(params[0], params[1]));
94 case c_discreteSpectrum:
95 case c_discretePtSpectrum:
98 for (
size_t i = params.size() / 2; i < params.size(); ++i) rand += params[i];
100 rand = gRandom->Uniform(0, rand);
102 for (
size_t i = params.size() / 2; i < params.size(); ++i) {
105 if (rand <= 0)
return params[i - (params.size() / 2)];
107 B2FATAL(
"Something wrong with picking fixed spectra values");
108 case c_polylineDistribution:
109 case c_polylinePtDistribution:
110 return randomPolyline(params.size() / 2, params.data(), params.data() + params.size() / 2);
111 case c_polylineCosDistribution:
112 return acos(randomPolyline(params.size() / 2, params.data(), params.data() + params.size() / 2));
114 B2FATAL(
"Unknown distribution");
122 double vx = generateValue(m_params.xVertexDist, m_params.xVertexParams);
123 double vy = generateValue(m_params.yVertexDist, m_params.yVertexParams);
124 double vz = generateValue(m_params.zVertexDist, m_params.zVertexParams);
127 double timeOffset = generateValue(m_params.timeDist, m_params.timeParams);
130 int nTracks =
static_cast<int>(m_params.nTracks);
131 if (m_params.nTracks <= 0) {
132 nTracks = m_params.pdgCodes.size();
133 }
else if (m_params.varyNumberOfTracks) {
134 nTracks = gRandom->Poisson(m_params.nTracks);
138 double firstMomentum;
139 for (
int i = 0; i < nTracks; ++i) {
141 p.setStatus(MCParticle::c_PrimaryParticle);
142 if (m_params.pdgCodes.size() == 1) {
144 p.setPDG(m_params.pdgCodes[0]);
145 }
else if (m_params.nTracks <= 0) {
147 p.setPDG(m_params.pdgCodes[i]);
150 int index =
static_cast<int>(gRandom->Uniform(m_params.pdgCodes.size()));
151 p.setPDG(m_params.pdgCodes[index]);
154 p.setFirstDaughter(0);
155 p.setLastDaughter(0);
158 double momentum = generateValue(m_params.momentumDist, m_params.momentumParams);
161 if (m_params.fixedMomentumPerEvent) {
162 i == 0 ? firstMomentum = momentum : momentum = firstMomentum;
164 double phi = generateValue(m_params.phiDist, m_params.phiParams);
165 double theta = generateValue(m_params.thetaDist, m_params.thetaParams);
167 double pt = momentum * sin(theta);
168 if (m_params.momentumDist == c_uniformPtDistribution || m_params.momentumDist == c_normalPtDistribution ||
169 m_params.momentumDist == c_inversePtDistribution || m_params.momentumDist == c_polylinePtDistribution ||
170 m_params.momentumDist == c_uniformLogPtDistribution || m_params.momentumDist == c_discretePtSpectrum) {
173 momentum = (sin(theta) > 0) ? (pt / sin(theta)) : numeric_limits<double>::max();
176 double pz = momentum * cos(theta);
177 double px = pt * cos(phi);
178 double py = pt * sin(phi);
179 double m = p.getMass();
180 double e =
sqrt(momentum * momentum + m * m);
182 p.setMomentum(px, py, pz);
184 p.setProductionVertex(vx, vy, vz);
185 p.addStatus(MCParticle::c_StableInGenerator);
189 p.setDecayTime(numeric_limits<double>::infinity());
192 p.setProductionTime(timeOffset);
194 if (m_params.independentVertices) {
196 vx = generateValue(m_params.xVertexDist, m_params.xVertexParams);
197 vy = generateValue(m_params.yVertexDist, m_params.yVertexParams);
198 vz = generateValue(m_params.zVertexDist, m_params.zVertexParams);
205 bool ParticleGun::setParameters(
const Parameters& p)
217 std::map<EDistribution, std::string> distributionNames = {
218 {c_fixedValue,
"fixed"},
219 {c_uniformDistribution,
"uniform"},
220 {c_uniformPtDistribution,
"uniformPt"},
221 {c_uniformCosDistribution,
"uniformCos"},
222 {c_uniformLogDistribution,
"uniformLog"},
223 {c_uniformLogPtDistribution,
"uniformLogPt"},
224 {c_normalDistribution,
"normal"},
225 {c_normalPtDistribution,
"normalPt"},
226 {c_normalCosDistribution,
"normalCos"},
227 {c_inversePtDistribution,
"inversePt"},
228 {c_polylineDistribution,
"polyline"},
229 {c_polylinePtDistribution,
"polylinePt"},
230 {c_polylineCosDistribution,
"polylineCos"},
231 {c_discreteSpectrum,
"discrete"},
232 {c_discretePtSpectrum,
"discretePt"}
235 auto getDist = [&p](
const std::string & dist) -> EDistribution {
236 if (dist ==
"momentum")
return p.momentumDist;
237 if (dist ==
"xVertex")
return p.xVertexDist;
238 if (dist ==
"yVertex")
return p.yVertexDist;
239 if (dist ==
"zVertex")
return p.zVertexDist;
240 if (dist ==
"theta")
return p.thetaDist;
241 if (dist ==
"phi")
return p.phiDist;
242 if (dist ==
"timeOffset")
return p.timeDist;
243 throw std::runtime_error(
"wrong parameter");
246 auto getPars = [&p](
const std::string & dist) ->
const std::vector<double>& {
247 if (dist ==
"momentum")
return p.momentumParams;
248 if (dist ==
"xVertex")
return p.xVertexParams;
249 if (dist ==
"yVertex")
return p.yVertexParams;
250 if (dist ==
"zVertex")
return p.zVertexParams;
251 if (dist ==
"theta")
return p.thetaParams;
252 if (dist ==
"phi")
return p.phiParams;
253 if (dist ==
"timeOffset")
return p.timeParams;
254 throw std::runtime_error(
"wrong parameter");
258 auto excludeDistribution = [&](
const std::string & dist, EDistribution excluded) {
259 if (getDist(dist) == excluded) {
260 B2ERROR(distributionNames[excluded] <<
" is not allowed for " << dist <<
" generation");
266 for (
auto dist : {
"momentum",
"xVertex",
"yVertex",
"zVertex"}) {
267 excludeDistribution(dist, c_uniformCosDistribution);
268 excludeDistribution(dist, c_normalCosDistribution);
269 excludeDistribution(dist, c_polylineCosDistribution);
271 for (
auto dist : {
"xVertex",
"yVertex",
"zVertex",
"phi",
"theta",
"timeOffset"}) {
272 excludeDistribution(dist, c_uniformPtDistribution);
273 excludeDistribution(dist, c_uniformLogPtDistribution);
274 excludeDistribution(dist, c_normalPtDistribution);
275 excludeDistribution(dist, c_inversePtDistribution);
276 excludeDistribution(dist, c_polylinePtDistribution);
280 if (p.pdgCodes.empty()) {
281 B2ERROR(
"No pdgCodes specified for generation");
286 for (
auto par : {
"momentum",
"xVertex",
"yVertex",
"zVertex",
"theta",
"phi",
"timeOffset"}) {
287 const EDistribution dist = getDist(par);
288 size_t minParams = (dist == c_fixedValue) ? 1 : 2;
289 if (dist == c_polylineDistribution || dist == c_polylinePtDistribution || dist == c_polylineCosDistribution) minParams = 4;
290 const size_t hasParams = getPars(par).size();
291 if (hasParams < minParams) {
292 B2ERROR(par <<
" generation: " << distributionNames[dist]
293 <<
" distribution requires at least " << minParams
294 <<
" parameters, " << hasParams <<
" given");
298 if (dist == c_discreteSpectrum || dist == c_discretePtSpectrum ||
299 dist == c_polylineDistribution || dist == c_polylinePtDistribution || dist == c_polylineCosDistribution) {
300 if ((hasParams % 2) != 0) {
301 B2ERROR(par <<
" generation: " << distributionNames[dist] <<
" requires an even number of parameters");
303 }
else if (dist == c_discreteSpectrum || dist == c_discretePtSpectrum || hasParams >= 4) {
306 const std::vector<double>& pvec = getPars(par);
307 const std::string parname = (dist == c_discreteSpectrum || dist == c_discretePtSpectrum)
308 ?
"weight" :
"y coordinate";
310 if ((dist != c_discreteSpectrum) && (dist != c_discretePtSpectrum)) {
311 for (
size_t i = 0; i < (hasParams / 2) - 1; ++i) {
312 if (pvec[i] > pvec[i + 1]) {
313 B2ERROR(par <<
" generation: " << distributionNames[dist] <<
" requires x coordinates in ascending order");
319 auto minmax = std::minmax_element(pvec.begin() + hasParams / 2, pvec.end());
320 if (*minmax.first < 0) {
321 B2ERROR(par <<
" generation: " << distributionNames[dist] <<
" requires "
322 << parname <<
"s to be non-negative");
325 if (*minmax.second <= 0) {
326 B2ERROR(par <<
" generation: " << distributionNames[dist] <<
" requires at least one "
327 << parname <<
" to be positive");
335 if (p.momentumDist == c_inversePtDistribution && p.momentumParams.size() >= 2) {
336 if (p.momentumParams[0] == 0 || p.momentumParams[1] == 0) {
337 B2ERROR(
"inversePt distribution does not allow zero momentum");
343 if (ok) { m_params = p; }
Class to represent Particle data in graph.
Class to build, validate and sort a particle decay chain.
double sqrt(double a)
sqrt for double
GraphParticle & addParticle()
Add new particle to the graph.
Abstract base class for different kinds of events.