Run on all hits; returned hit indices refer to the input vector.
234 {
235 hits_ = &hits;
236 lcs_.clear();
237 layer_of_.assign(hits.size(), 0);
238 std::map<int, std::vector<int>> by_layer;
239 for (std::size_t i = 0; i < hits.size(); i++) {
240 if (hits[i].getEnergy() <= p_.hit_ecut_ || hits[i].getEnergy() <= 0)
241 continue;
243 by_layer[layer_of_[i]].push_back(i);
244 }
245 for (const auto& [layer, idx] : by_layer) cluster2D(idx);
246 cluster3D();
247
248
249 core_.assign(hits.size(), 0);
250 for (const auto& lc : lcs_)
251 if (lc.cid_ >= 0)
252 for (int h : lc.hits_) core_[h] = 1;
253
254
255 int ncl = 0;
256 for (const auto& lc : lcs_) ncl = std::max(ncl, lc.cid_ + 1);
257 std::vector<std::vector<int>> members(ncl);
258 for (const auto& lc : lcs_)
259 if (lc.cid_ >= 0)
260 members[lc.cid_].insert(members[lc.cid_].end(), lc.hits_.begin(),
261 lc.hits_.end());
262 auto fit_all = [&](std::vector<Cluster>& cls,
263 const std::vector<std::vector<int>>& mem) {
264 cls.assign(mem.size(),
Cluster{});
265 for (std::size_t k = 0; k < mem.size(); k++)
266 fitAxis(cls[k], mem[k], std::vector<double>(mem[k].size(), 1.));
267 };
268 std::vector<Cluster> cls;
269 fit_all(cls, members);
270
271
272 for (const auto& lc : lcs_) {
273 if (lc.cid_ >= 0) continue;
274 int best = -1;
275 double best_d = p_.attach_radius_;
276 for (std::size_t k = 0; k < cls.size(); k++) {
277 double d = dist(lc.x_, lc.y_, cls[k].axisX(lc.z_), cls[k].axisY(lc.z_));
278 if (d < best_d) {
279 best_d = d;
280 best = k;
281 }
282 }
283 if (best >= 0) {
284 members[best].insert(members[best].end(), lc.hits_.begin(),
285 lc.hits_.end());
286 } else {
287 members.push_back(lc.hits_);
288 for (int h : lc.hits_) core_[h] = 1;
289 }
290 }
291 fit_all(cls, members);
292
293
294 {
295 std::vector<int> order(cls.size());
296 std::iota(order.begin(), order.end(), 0);
297 std::sort(order.begin(), order.end(),
298 [&](int a, int b) { return cls[a].e_ < cls[b].e_; });
299 std::vector<double> esum(cls.size());
300 std::vector<int> nlay(cls.size());
301 for (std::size_t k = 0; k < cls.size(); k++) {
302 esum[k] = cls[k].e_;
303 std::vector<int> ls;
304 for (int h : members[k]) ls.push_back(layer_of_[h]);
305 std::sort(ls.begin(), ls.end());
306 nlay[k] = std::unique(ls.begin(), ls.end()) - ls.begin();
307 }
308 for (int sat : order) {
309 int best = -1;
310 double best_d = p_.sc_radius_;
311 bool short_sat = nlay[sat] < p_.sc_min_layers_;
312 for (std::size_t k = 0; k < cls.size(); k++) {
313
314 if (static_cast<int>(k) == sat || members[k].empty() ||
315 esum[k] <= esum[sat] || nlay[k] < p_.sc_min_layers_ ||
316 (!short_sat && esum[sat] >= p_.sc_ratio_ * esum[k]))
317 continue;
318 double d = dist(cls[sat].x_, cls[sat].y_, cls[k].axisX(cls[sat].z_),
319 cls[k].axisY(cls[sat].z_));
320 if (d < best_d) {
321 best_d = d;
322 best = k;
323 }
324 }
325 if (best < 0) continue;
326 members[best].insert(members[best].end(), members[sat].begin(),
327 members[sat].end());
328 for (int h : members[sat]) core_[h] = 0;
329 esum[best] += esum[sat];
330 members[sat].clear();
331 }
332 std::vector<std::vector<int>> left;
333 for (auto& m : members)
334 if (!m.empty()) left.push_back(std::move(m));
335 members = std::move(left);
336 fit_all(cls, members);
337 }
338
339
340 std::vector<std::vector<int>> kept;
341 for (std::size_t k = 0; k < cls.size(); k++)
342 if (cls[k].e_ >= p_.min_cluster_energy_) kept.push_back(members[k]);
343 members = std::move(kept);
344 fit_all(cls, members);
345 std::vector<std::vector<double>> fracs(members.size());
346 for (std::size_t k = 0; k < members.size(); k++)
347 fracs[k].assign(members[k].size(), 1.);
348
349
350 std::vector<int> big;
351 for (std::size_t k = 0; k < cls.size(); k++)
352 if (cls[k].e_ >= p_.share_min_energy_) big.push_back(k);
353 if (p_.share_ && big.size() > 1) {
354 std::vector<int> shared_hits;
355 for (int k : big)
356 shared_hits.insert(shared_hits.end(), members[k].begin(),
357 members[k].end());
358 std::vector<std::vector<int>> mem(big.size());
359 std::vector<std::vector<double>> fr(big.size());
360 for (int it = 0; it < p_.share_iters_; it++) {
361 for (auto& v : mem) v.clear();
362 for (auto& v : fr) v.clear();
363 for (int h : shared_hits) {
364 double hz = hits[h].getZPos();
365 double lam =
lambda(layer_of_[h]);
366 std::vector<double> w(big.size());
367 double sw{0};
368 for (std::size_t b = 0; b < big.size(); b++) {
369 const auto& c = cls[big[b]];
370 double d = dist(hits[h].getXPos(), hits[h].getYPos(), c.axisX(hz),
371 c.axisY(hz));
372 w[b] = c.e_ * std::exp(-d / lam);
373 sw += w[b];
374 }
375 if (sw <= 0) {
376
377 std::size_t bb = 0;
378 double dmin = std::numeric_limits<double>::max();
379 for (std::size_t b = 0; b < big.size(); b++) {
380 const auto& c = cls[big[b]];
381 double d = dist(hits[h].getXPos(), hits[h].getYPos(), c.axisX(hz),
382 c.axisY(hz));
383 if (d < dmin) {
384 dmin = d;
385 bb = b;
386 }
387 }
388 w.assign(big.size(), 0.);
389 w[bb] = sw = 1.;
390 }
391 for (std::size_t b = 0; b < big.size(); b++) {
392 double f = w[b] / sw;
393 if (f < 1e-4) continue;
394 mem[b].push_back(h);
395 fr[b].push_back(f);
396 }
397 }
398 for (std::size_t b = 0; b < big.size(); b++) {
399 fitAxis(cls[big[b]], mem[b], fr[b]);
400 }
401 }
402 for (std::size_t b = 0; b < big.size(); b++) {
403 members[big[b]] = mem[b];
404 fracs[big[b]] = fr[b];
405 }
406
407 for (std::size_t i = 0; i < big.size(); i++) {
408 for (std::size_t j = i + 1; j < big.size(); j++) {
409 auto& a = cls[big[i]];
410 auto& b = cls[big[j]];
411 if (members[big[i]].empty() || members[big[j]].empty()) continue;
412 double zz = a.e_ < b.e_ ? a.z_ : b.z_;
413 double d = dist(a.axisX(zz), a.axisY(zz), b.axisX(zz), b.axisY(zz));
414
415 bool ghost =
416 d < p_.ghost_dist_ &&
417 std::min(a.e_, b.e_) < p_.ghost_ratio_ * std::max(a.e_, b.e_);
418 if (d > p_.coincide_dist_ && !ghost) continue;
419 std::map<int, double> f;
420 for (std::size_t k = 0; k < members[big[i]].size(); k++)
421 f[members[big[i]][k]] += fracs[big[i]][k];
422 for (std::size_t k = 0; k < members[big[j]].size(); k++)
423 f[members[big[j]][k]] += fracs[big[j]][k];
424 members[big[i]].clear();
425 fracs[big[i]].clear();
426 for (auto& [h, v] : f) {
427 members[big[i]].push_back(h);
428 fracs[big[i]].push_back(v);
429 }
430 members[big[j]].clear();
431 fracs[big[j]].clear();
432 fitAxis(a, members[big[i]], fracs[big[i]]);
433 }
434 }
435 }
436
437
438 std::vector<Cluster> out;
439 for (std::size_t k = 0; k < members.size(); k++) {
441 fitAxis(c, members[k], fracs[k]);
442 if (c.e_ < p_.min_cluster_energy_) continue;
443 c.hits_ = members[k];
444 c.frac_ = fracs[k];
445 double sxx{0}, syy{0}, szz{0}, sx{0}, sy{0}, e0{0};
446 for (std::size_t i = 0; i < c.hits_.size(); i++) {
447 const auto& h = hits[c.hits_[i]];
448 double e = c.frac_[i] * h.getEnergy();
449 sx += e * h.getXPos();
450 sy += e * h.getYPos();
451 sxx += e * h.getXPos() * h.getXPos();
452 syy += e * h.getYPos() * h.getYPos();
453 szz += e * (h.getZPos() - c.z_) * (h.getZPos() - c.z_);
454 if (layer_of_[c.hits_[i]] == 0) {
455 c.x0_ += e * h.getXPos();
456 c.y0_ += e * h.getYPos();
457 c.z0_ = h.getZPos();
458 e0 += e;
459 }
460 }
461 sx /= c.e_;
462 sy /= c.e_;
463 c.rms_x_ = std::sqrt(std::max(0., sxx / c.e_ - sx * sx));
464 c.rms_y_ = std::sqrt(std::max(0., syy / c.e_ - sy * sy));
465 c.rms_z_ = std::sqrt(szz / c.e_);
466 if (e0 > 0) {
467 c.x0_ /= e0;
468 c.y0_ /= e0;
469 }
470 out.push_back(std::move(c));
471 }
472 std::sort(out.begin(), out.end(),
474 return out;
475}
double lambda(int layer) const
Profile slope used at a layer [mm].
Extension of DetectorID providing access to ECal layers and cell numbers in a hex grid.
int layer() const
Get the value of the layer field from the ID.