10 #include "art/Framework/Core/EDProducer.h"
11 #include "art/Framework/Core/ModuleMacros.h"
12 #include "art/Framework/Principal/Event.h"
13 #include "art/Framework/Principal/Handle.h"
14 #include "art/Framework/Principal/Run.h"
15 #include "art/Framework/Principal/SubRun.h"
16 #include "canvas/Utilities/InputTag.h"
17 #include "fhiclcpp/ParameterSet.h"
18 #include "messagefacility/MessageLogger/MessageLogger.h"
35 #include "sbnobj/Common/Reco/PCAnglePlane.h"
36 #include "sbnobj/Common/Reco/PCAngleKink.h"
42 class PCAngleKinkFinder;
59 void produce(art::Event&
e)
override;
79 FitResult Fit(
const std::vector<sbn::PCAngle> &angles,
unsigned lo,
unsigned hi,
unsigned center,
float est_angle,
float est_pitch);
92 sbn::PCAngleKink kink;
93 kink.maxWire = max.wire;
95 kink.fwhm_distance = sqrt((hi.hit_time_cm - lo.hit_time_cm) * (hi.hit_time_cm - lo.hit_time_cm)
96 + (hi.hit_wire_cm - lo.hit_wire_cm) * (hi.hit_wire_cm - lo.hit_wire_cm));
97 kink.max_angle = max.angle;
98 kink.est_angle = est_angle;
100 kink.position_max = {max.hit_time_cm, max.hit_wire_cm};
101 kink.position_lo = {lo.hit_time_cm, lo.hit_wire_cm};
102 kink.position_hi = {hi.hit_time_cm, hi.hit_wire_cm};
104 kink.vec_lo_at_max = max.lo_vector;
105 kink.vec_hi_at_max = max.hi_vector;
107 kink.vec_lo_at_halfmax_lo = lo.lo_vector;
108 kink.vec_hi_at_halfmax_lo = lo.hi_vector;
110 kink.vec_lo_at_halfmax_hi = hi.lo_vector;
111 kink.vec_hi_at_halfmax_hi = hi.hi_vector;
113 kink.fit_chi2 = fit.
chi2;
114 kink.fit_angle = fit.
angle;
115 kink.fit_pitch = fit.
pitch;
121 std::tuple<std::vector<sbn::PCAngle>,
unsigned>
FlattenBranch(
const sbn::PCAnglePlane &plane,
unsigned i_branch,
bool allow_incomplete,
float min_dist) {
122 std::vector<sbn::PCAngle> ret;
124 std::cout <<
"Flattening angles for branch: " << i_branch <<
" plane: " << plane.plane.Plane <<
" hierarchy: ";
125 for (
int id: plane.branchHierarchy[i_branch])
std::cout <<
id <<
" ";
129 for (
unsigned i_branch_hierarchy = 0; i_branch_hierarchy < plane.branchHierarchy[i_branch].size(); i_branch_hierarchy++) {
131 unsigned this_branch =
std::distance(plane.branchIDs.begin(), std::find(plane.branchIDs.begin(), plane.branchIDs.end(), plane.branchHierarchy[i_branch][i_branch_hierarchy]));
132 if (this_branch == plane.branchIDs.size()) {
136 std::cout <<
"BRANCH CHECK: " << plane.branchHierarchy[i_branch][i_branch_hierarchy] <<
" " << this_branch <<
" " << i_branch << std::endl;
137 for (
int i = plane.angles[this_branch].size()-1; i >= 0; i--) {
139 bool is_complete = (plane.angles[this_branch][i].complete || allow_incomplete) &&
140 plane.angles[this_branch][i].dist_to_lo > min_dist && plane.angles[this_branch][i].dist_to_hi > min_dist &&
141 plane.angles[this_branch][i].angle > -99.;
143 if (!is_complete)
continue;
146 bool found_hit =
false;
147 if (i_branch_hierarchy > 0) {
148 int i_check_branch_hierarchy = i_branch_hierarchy - 1;
149 unsigned chk_branch = plane.branchIDs.size();
150 int check_hitID = plane.angles[this_branch][i].hitID;
151 while (i_check_branch_hierarchy >= 0 && chk_branch == plane.branchIDs.size()) {
152 chk_branch =
std::distance(plane.branchIDs.begin(), std::find(plane.branchIDs.begin(), plane.branchIDs.end(), plane.branchHierarchy[i_branch][i_check_branch_hierarchy]));
153 i_check_branch_hierarchy --;
155 if (chk_branch < plane.branchIDs.size()) {
156 for (
unsigned j = 0; j < plane.angles[chk_branch].size(); j++) {
157 if (plane.angles[chk_branch][j].hitID == check_hitID) {
165 if (found_hit)
continue;
168 ret.push_back(plane.angles[this_branch][i]);
173 std::reverse(ret.begin(), ret.end());
176 unsigned n_branch_hits = 0;
177 for (
unsigned i = 0; i < plane.angles[i_branch].size(); i++) {
178 n_branch_hits += (plane.angles[i_branch][i].complete || allow_incomplete) &&
179 plane.angles[i_branch][i].dist_to_lo > min_dist && plane.angles[i_branch][i].dist_to_hi > min_dist &&
180 plane.angles[i_branch][i].angle > -99.;
184 unsigned branch_start = ret.size() - n_branch_hits;
185 return {ret, branch_start};
189 float EstimateHeight(
const std::vector<sbn::PCAngle> &angles,
unsigned lo,
unsigned hi) {
190 static const unsigned NAVG = 3;
191 static const unsigned IAVG = (NAVG - 1) / 2;
194 if (hi - lo + 1 < NAVG)
return -1.;
196 std::vector<float> avgd;
198 for (
unsigned i = IAVG + lo; i <= hi - IAVG; i++) {
200 for (
unsigned j = i - IAVG; j < i - IAVG + NAVG; j++) {
201 sum += angles[j].angle;
204 avgd.push_back(sum / NAVG);
207 return *std::max_element(avgd.begin(), avgd.end());
211 int FindMaxIndex(
const std::vector<sbn::PCAngle> &angles,
unsigned lo,
unsigned hi) {
212 static const unsigned NAVG = 3;
213 static const unsigned IAVG = (NAVG - 1) / 2;
216 if (hi - lo + 1 < (
int)NAVG)
return -1.;
218 std::vector<float> avgd;
220 for (
unsigned i = lo + IAVG; i <= hi - IAVG; i++) {
222 for (
unsigned j = i - IAVG; j < i - IAVG + NAVG; j++) {
223 sum += angles[j].angle;
226 avgd.push_back(sum / NAVG);
230 unsigned avgd_index =
std::distance(avgd.begin(), std::max_element(avgd.begin(), avgd.end()));
233 unsigned angles_index = avgd_index + IAVG + lo;
238 int FindFWHMHiIndex(
const std::vector<sbn::PCAngle> &angles,
unsigned start,
unsigned end,
float amp,
float threshold) {
240 if (amp < 0.)
return -1;
243 int halfmax_ind = -1;
249 for (
unsigned i = end; i < angles.size(); i++) {
250 if (angles[i].
angle < amp/2.) {
258 int FindFWHMLoIndex(
const std::vector<sbn::PCAngle> &angles,
unsigned start,
unsigned end,
float amp,
float threshold) {
260 if (amp < 0.)
return -1;
263 int halfmax_ind = -1;
265 for (
int i = start; i >= 0; i--) {
266 if (angles[i].
angle < amp / 2.) {
277 fPFParticleLabel(
p.get<std::string>(
"PFParticleLabel")),
278 fAngleLabel(
p.get<std::string>(
"AngleLabel")),
279 fHitThreshold(
p.get<
float>(
"HitThreshold")),
280 fMinDist(
p.get<
float>(
"MinDist")),
281 fAllowIncomplete(
p.get<
bool>(
"AllowIncomplete")),
282 fPCARadius(
p.get<
float>(
"PCARadius")),
283 fFitResolution(
p.get<
float>(
"FitResolution", 0.05))
285 produces<std::vector<sbn::PCAngleKink>>();
286 produces<art::Assns<recob::PFParticle, sbn::PCAngleKink>>();
291 if (dist_to_center >= radius)
return 1.;
293 double theta =
angle;
294 double A = dist_to_center;
295 double B = radius - dist_to_center;
297 double l = mean_distance;
303 double M01 = (B*(B + l)*(-6*A*(A + l)*(A + B - 2*R)*sin(theta) + (3*B*(A + B)*(B + l) - 6*B*(B + l)*R + 2*(2*B + l)*R*R)*sin(2*theta)))/(24.*l*R*R);
304 double M00 = (A*(A + l)*(3*A*(A + B)*(A + l) - 6*A*(A + l)*R + 2*(2*A + l)*R*R) + B*(B + l)*cos(theta)*(-6*A*(A + l)*(A + B - 2*R) + (3*B*(A + B)*(B + l) - 6*B*(B + l)*R + 2*(2*B + l)*R*R)*cos(theta)))/(12.*l*R*R);
305 double M11 = (B*(B + l)*(3*B*(A + B)*(B + l) - 6*B*(B + l)*R + 2*(2*B + l)*R*R)*sin(theta)*sin(theta))/(12.*l*R*R);
307 double trace = M00 + M11;
308 double det = M00 * M11 - M01 * M01;
309 double eigen = 0.5 * (trace + sqrt(trace * trace - 4 *det));
311 double vecB = eigen - M00;
313 return vecA / sqrt(vecA * vecA + vecB * vecB);
317 double angle = par[0];
319 double radius = par[2];
320 double mean_distance = par[3];
323 for (
unsigned i = 0; i < fFitDataX.size(); i++) {
324 double expectation = acos(
ExpectedCosAngle(fFitDataX[i], radius, mean_distance, angle));
325 chi2 += (expectation - fFitDataY[i]) * (expectation - fFitDataY[i]) / (fFitDataY[i] * fFitDataY[i] * res * res);
328 result = chi2 / fFitDataX.size();
335 for (
unsigned i = lo; i <= hi; i++) {
336 fFitDataX.push_back( sqrt((angles[i].hit_time_cm - angles[center].hit_time_cm) * (angles[i].hit_time_cm - angles[center].hit_time_cm) +
337 (angles[i].hit_wire_cm - angles[center].hit_wire_cm) * (angles[i].hit_wire_cm - angles[center].hit_wire_cm)) );
338 fFitDataY.push_back(angles[i].
angle);
345 fitter.SetParameter(0,
"angle", est_angle, 0.1, 0, M_PI);
346 fitter.SetParameter(1,
"resolution", fFitResolution, 0., fFitResolution, fFitResolution);
347 fitter.SetParameter(2,
"radius", fPCARadius, 0., fPCARadius, fPCARadius);
348 fitter.SetParameter(3,
"pitch", est_pitch, 0., est_pitch, est_pitch);
349 fitter.GetMinuit()->FixParameter(1);
350 fitter.GetMinuit()->FixParameter(2);
351 fitter.GetMinuit()->FixParameter(3);
353 fitter.ExecuteCommand(
"MIGRAD", 0, 0);
357 ret.
angle = fitter.GetParameter(0);
358 ret.
pitch = fitter.GetParameter(3);
366 fitter.GetStats(chi2, edm, errdef, nvpar, nparx);
367 std::cout <<
"Fit chi2: " << chi2 << std::endl;
378 std::unique_ptr<std::vector<sbn::PCAngleKink>> outKinks(
new std::vector<sbn::PCAngleKink>);
379 std::unique_ptr<art::Assns<recob::PFParticle, sbn::PCAngleKink>> assn(
new art::Assns<recob::PFParticle, sbn::PCAngleKink>);
381 art::PtrMaker<sbn::PCAngleKink> kinkPtrMaker {evt};
384 art::Handle<std::vector<recob::PFParticle>> pfparticle_handle;
385 evt.getByLabel(fPFParticleLabel, pfparticle_handle);
387 std::vector<art::Ptr<recob::PFParticle>> pfparticles;
388 art::fill_ptr_vector(pfparticles, pfparticle_handle);
391 std::map<unsigned, art::Ptr<recob::PFParticle>> id_to_pfp;
392 for (
unsigned i = 0; i < pfparticles.size(); i++) {
393 id_to_pfp[pfparticles[i]->Self()] = pfparticles[i];
396 art::FindManyP<sbn::PCAnglePlane> pfparticleAngles(pfparticles, evt, fAngleLabel);
399 for (
unsigned i_part = 0; i_part < pfparticles.size(); i_part++) {
400 art::Ptr<recob::PFParticle> thisPFP = pfparticles[i_part];
401 std::cout <<
"NEW Particle: " << thisPFP->Self() << std::endl;
402 const std::vector<art::Ptr<sbn::PCAnglePlane>> &thisAngles = pfparticleAngles.at(i_part);
404 if (!thisAngles.size())
continue;
406 std::vector<sbn::PCAngleKink> thisKinks;
407 for (
const art::Ptr<sbn::PCAnglePlane> &
angle: thisAngles) {
408 for (
unsigned i_branch = 0; i_branch <
angle->nBranches; i_branch++) {
410 auto [branchAngles, branch_start] =
FlattenBranch(*
angle, i_branch, fAllowIncomplete, fMinDist);
412 int hit_start_ind = -1;
413 for (
unsigned i_hit = branch_start; i_hit < branchAngles.size(); i_hit++) {
414 bool this_in_hit = branchAngles[i_hit].angle > fHitThreshold;
416 if (!in_hit && this_in_hit) {
417 hit_start_ind = i_hit;
420 else if (in_hit && (!this_in_hit || i_hit+1 == branchAngles.size())) {
421 float height =
EstimateHeight(branchAngles, hit_start_ind, i_hit);
423 int hi_hit_ind =
FindFWHMHiIndex(branchAngles, hit_start_ind, i_hit, height, fHitThreshold);
424 int lo_hit_ind =
FindFWHMLoIndex(branchAngles, hit_start_ind, i_hit, height, fHitThreshold);
427 if (hi_hit_ind >= 0 && lo_hit_ind >= 0) {
428 std::cout <<
"NEW KINK: Particle: " << thisPFP->Self() <<
" Branch Ind: " << i_branch <<
" Plane: " <<
angle->plane.Plane << std::endl;
429 std::cout <<
"EST HEIGHT: " << height <<
" IND LO: " << lo_hit_ind <<
" IND HI: " << hi_hit_ind << std::endl;
432 int max_ind =
FindMaxIndex(branchAngles, lo_hit_ind, hi_hit_ind);
435 float dist_lo_sum = 0.;
436 for (
int i = lo_hit_ind; i < max_ind; i++) {
437 dist_lo_sum += sqrt((branchAngles[i].hit_time_cm - branchAngles[i+1].hit_time_cm) * (branchAngles[i].hit_time_cm - branchAngles[i+1].hit_time_cm) +
438 (branchAngles[i].hit_wire_cm - branchAngles[i+1].hit_wire_cm) * (branchAngles[i].hit_wire_cm - branchAngles[i+1].hit_wire_cm));
442 float dist_hi_sum = 0.;
443 for (
int i = max_ind; i < hi_hit_ind; i++) {
444 dist_hi_sum += sqrt((branchAngles[i].hit_time_cm - branchAngles[i+1].hit_time_cm) * (branchAngles[i].hit_time_cm - branchAngles[i+1].hit_time_cm) +
445 (branchAngles[i].hit_wire_cm - branchAngles[i+1].hit_wire_cm) * (branchAngles[i].hit_wire_cm - branchAngles[i+1].hit_wire_cm));
448 float pitch = (dist_lo_sum + dist_hi_sum) / (hi_hit_ind - lo_hit_ind);
453 const sbn::PCAngle &max = branchAngles[max_ind];
454 const sbn::PCAngle &lo = branchAngles[lo_hit_ind];
455 const sbn::PCAngle &hi = branchAngles[hi_hit_ind];
457 sbn::PCAngleKink kink =
BuildKink(max, lo, hi, fit, height);
458 thisKinks.push_back(kink);
464 in_hit = this_in_hit;
470 for (
const sbn::PCAngleKink &
k: thisKinks) {
471 outKinks->push_back(
k);
472 art::Ptr<sbn::PCAngleKink> thisKinkPtr = kinkPtrMaker(outKinks->size()-1);
473 assn->addSingle(thisPFP, thisKinkPtr);
477 evt.put(std::move(outKinks));
478 evt.put(std::move(assn));
PCAngleKinkFinder & operator=(PCAngleKinkFinder const &)=delete
then if[["$THISISATEST"==1]]
FitResult Fit(const std::vector< sbn::PCAngle > &angles, unsigned lo, unsigned hi, unsigned center, float est_angle, float est_pitch)
then echo unknown compiler flag
PCAngleKinkFinder(fhicl::ParameterSet const &p)
Declaration of signal hit object.
int FindFWHMLoIndex(const std::vector< sbn::PCAngle > &angles, unsigned start, unsigned end, float amp, float threshold)
int FindMaxIndex(const std::vector< sbn::PCAngle > &angles, unsigned lo, unsigned hi)
static std::vector< float > fFitDataY
std::tuple< std::vector< sbn::PCAngle >, unsigned > FlattenBranch(const sbn::PCAnglePlane &plane, unsigned i_branch, bool allow_incomplete, float min_dist)
M::value_type trace(const M &m)
Access the description of detector geometry.
void FitLikelihood(int &npar, double *g, double &result, double *par, int flag)
double distance(geo::Point_t const &point, CathodeDesc_t const &cathode)
Returns the distance of a point from the cathode.
static unsigned fFitCenter
auto end(FixedBins< T, C > const &) noexcept
Declaration of cluster object.
double ExpectedCosAngle(double dist_to_center, double radius, double mean_distance, double angle)
Declaration of basic channel signal object.
finds tracks best matching by angle
static std::vector< float > fFitDataX
void produce(art::Event &e) override
sbn::PCAngleKink BuildKink(const sbn::PCAngle &max, const sbn::PCAngle &lo, const sbn::PCAngle &hi, const sbn::PCAngleKinkFinder::FitResult &fit, float est_angle)
float EstimateHeight(const std::vector< sbn::PCAngle > &angles, unsigned lo, unsigned hi)
int FindFWHMHiIndex(const std::vector< sbn::PCAngle > &angles, unsigned start, unsigned end, float amp, float threshold)
art framework interface to geometry description
BEGIN_PROLOG could also be cout
std::string fPFParticleLabel