Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lambda reconstruction in ZDC #1731

Open
wants to merge 77 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
93f65f8
parameters for the lambda paper
sebouh137 Dec 5, 2024
e2e011a
added IDOLA algorithm
sebouh137 Feb 7, 2025
6804919
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 7, 2025
b3477d8
Update JEventProcessorPODIO.cc
sebouh137 Feb 7, 2025
36c0b1a
Update reco.cc
sebouh137 Feb 7, 2025
89c050b
parameters for the lambda paper
sebouh137 Dec 5, 2024
cefdbcb
Merge branch 'lambda_paper' of https://github.com/eic/EICrecon into l…
sebouh137 Feb 10, 2025
c435616
added IDOLA algorithm
sebouh137 Feb 7, 2025
ed821c9
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 7, 2025
a820190
Update JEventProcessorPODIO.cc
sebouh137 Feb 7, 2025
3595a61
Update reco.cc
sebouh137 Feb 7, 2025
0a75b7c
Merge branch 'lambda_zdc' of https://github.com/eic/EICrecon into lam…
sebouh137 Feb 10, 2025
b9886c5
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 10, 2025
1e89caa
updated version that passes the tests I have placed on it
sebouh137 Feb 10, 2025
e8f2042
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 10, 2025
ddb6aac
appease the obnoxious entity known as iwyu
sebouh137 Feb 11, 2025
ef5fafa
undid some changes that iwyu told me to do but broke the build
sebouh137 Feb 11, 2025
061b0b6
Update FarForwardNeutralsReconstruction.cc
sebouh137 Feb 11, 2025
5ba6825
Update PIDLookup.cc
sebouh137 Feb 11, 2025
24abf91
Update src/algorithms/reco/FarForwardLambdaReconstruction.cc
sebouh137 Feb 14, 2025
9c2913c
Update src/algorithms/reco/FarForwardNeutralsReconstruction.cc
sebouh137 Feb 14, 2025
48e2441
Update src/algorithms/reco/FarForwardNeutralsReconstruction.cc
sebouh137 Feb 14, 2025
7cdd67e
Update src/algorithms/reco/FarForwardNeutralsReconstruction.cc
sebouh137 Feb 14, 2025
e51f411
Update src/algorithms/reco/FarForwardLambdaReconstruction.cc
sebouh137 Feb 14, 2025
2933e72
Merge branch 'main' into lambda_zdc
sebouh137 Feb 14, 2025
05cf862
use particle service
sebouh137 Feb 17, 2025
18c724a
fixed issue with max width
sebouh137 Feb 17, 2025
5d9a79b
Update src/algorithms/reco/FarForwardNeutralsReconstruction.h
sebouh137 Feb 17, 2025
a23b8de
Merge branch 'main' into lambda_zdc
sebouh137 Feb 18, 2025
0bf64b0
Update src/algorithms/reco/FarForwardNeutralsReconstruction.h
sebouh137 Feb 18, 2025
4bea111
Update src/algorithms/reco/FarForwardNeutralsReconstructionConfig.h
sebouh137 Feb 18, 2025
05bdb57
Update src/algorithms/reco/FarForwardLambdaReconstructionConfig.h
sebouh137 Feb 18, 2025
e3d668d
some changes in response to feedback
sebouh137 Feb 18, 2025
ad26945
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 18, 2025
6207ff5
iwyu
sebouh137 Feb 19, 2025
fa38f68
Merge branch 'lambda_zdc' of github.com:eic/EICrecon into lambda_zdc
sebouh137 Feb 19, 2025
1ee3062
use a single reconstructed particle collection for the photons and ne…
sebouh137 Feb 19, 2025
a4053ff
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 19, 2025
7b96b5d
iwyu
sebouh137 Feb 19, 2025
ab47825
Merge branch 'lambda_zdc' of github.com:eic/EICrecon into lambda_zdc
sebouh137 Feb 19, 2025
063923a
trace stuff
sebouh137 Feb 20, 2025
aed06ff
trace stuff
sebouh137 Feb 20, 2025
e1a585d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 20, 2025
a999b61
Update src/factories/reco/FarForwardLambdaReconstruction_factory.h
sebouh137 Feb 20, 2025
5b143eb
removed old neutron algorithm
sebouh137 Feb 20, 2025
0133331
Merge branch 'lambda_zdc' of github.com:eic/EICrecon into lambda_zdc
sebouh137 Feb 20, 2025
f4f1849
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 20, 2025
ebbf118
formatting
sebouh137 Feb 20, 2025
f1aff47
Update src/factories/reco/FarForwardNeutralsReconstruction_factory.h
sebouh137 Feb 20, 2025
1c44bb9
Merge branch 'lambda_zdc' of github.com:eic/EICrecon into lambda_zdc
sebouh137 Feb 20, 2025
96d9d72
trace
sebouh137 Feb 20, 2025
b78a524
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 20, 2025
ed93985
formatting
sebouh137 Feb 20, 2025
939ef9e
Merge branch 'lambda_zdc' of github.com:eic/EICrecon into lambda_zdc
sebouh137 Feb 20, 2025
044fea6
...
sebouh137 Feb 20, 2025
8855bbe
removed src/tests/algorithms_test/reco_FarForwardNeutronReconstructio…
sebouh137 Feb 20, 2025
45aa86b
...
sebouh137 Feb 20, 2025
c3bbcd2
Merge branch 'main' into lambda_zdc
sebouh137 Feb 20, 2025
1400083
iwyu
sebouh137 Feb 20, 2025
e1c8ae7
Merge branch 'lambda_zdc' of github.com:eic/EICrecon into lambda_zdc
sebouh137 Feb 20, 2025
3a21bf8
Update src/algorithms/reco/FarForwardLambdaReconstruction.cc
sebouh137 Feb 21, 2025
a9e5879
Update src/algorithms/reco/FarForwardNeutralsReconstruction.cc
sebouh137 Feb 21, 2025
ed1cd8f
loop through neutrals
sebouh137 Feb 21, 2025
1b5d0ad
merge
sebouh137 Feb 21, 2025
e012ead
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 21, 2025
c038256
Update FarForwardLambdaReconstruction.cc
sebouh137 Feb 21, 2025
12912b7
Merge branch 'main' into lambda_zdc
sebouh137 Feb 21, 2025
450ad62
Merge branch 'main' into lambda_zdc
sebouh137 Feb 22, 2025
18e1977
use a single collection for the lambda and its CM decay products
sebouh137 Feb 22, 2025
6bdf357
merge
sebouh137 Feb 22, 2025
5bd9f90
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 22, 2025
d11fd82
Update src/algorithms/reco/FarForwardLambdaReconstruction.cc
sebouh137 Feb 23, 2025
a505c0c
Update src/algorithms/reco/FarForwardLambdaReconstruction.cc
sebouh137 Feb 23, 2025
89b4f95
Update src/algorithms/reco/FarForwardLambdaReconstruction.h
sebouh137 Feb 23, 2025
e8f158a
Update src/algorithms/reco/FarForwardLambdaReconstruction.cc
sebouh137 Feb 23, 2025
351f7c4
iwyu
sebouh137 Feb 23, 2025
0c5bc84
Merge branch 'lambda_zdc' of github.com:eic/EICrecon into lambda_zdc
sebouh137 Feb 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/algorithms/calorimetry/HEXPLIT.cc
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ void HEXPLIT::process(const HEXPLIT::Input& input,
auto [subcellHits] = output;

double MIP=m_cfg.MIP/dd4hep::GeV;
double delta=m_cfg.delta_in_MIPs*MIP;
double Emin=m_cfg.Emin_in_MIPs*MIP;
double tmax=m_cfg.tmax/dd4hep::ns;

Expand Down Expand Up @@ -125,7 +126,7 @@ void HEXPLIT::process(const HEXPLIT::Input& input,
}
double weights[SUBCELLS];
for(int k=0; k<NEIGHBORS; k++){
Eneighbors[k]=std::max(Eneighbors[k],MIP);
Eneighbors[k]=std::max(Eneighbors[k],delta);
}
double sum_weights=0;
for(int k=0; k<SUBCELLS; k++){
Expand Down
1 change: 1 addition & 0 deletions src/algorithms/calorimetry/HEXPLITConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace eicrecon {
struct HEXPLITConfig {
double MIP{472.*dd4hep::keV};
double Emin_in_MIPs{0.1};
double delta_in_MIPs{0.01};
double tmax{325*dd4hep::ns};
};

Expand Down
1 change: 1 addition & 0 deletions src/algorithms/pid_lut/PIDLookup.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <edm4eic/MCRecoParticleAssociationCollection.h>
#include <edm4eic/ReconstructedParticleCollection.h>
#include <edm4hep/MCParticleCollection.h>
#include <edm4hep/Vector3f.h>
#include <edm4hep/utils/vector_utils.h>
#include <fmt/core.h>
#include <podio/podioVersion.h>
Expand Down
139 changes: 139 additions & 0 deletions src/algorithms/reco/FarForwardLambdaReconstruction.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright (C) 2025 Sebouh Paul

#include <Evaluator/DD4hepUnits.h>
#include <TVector3.h>
#include <edm4eic/ReconstructedParticleCollection.h>
#include <edm4hep/Vector3f.h>
#include <edm4hep/utils/vector_utils.h>
#include <math.h>
#include <gsl/pointers>


#include "FarForwardLambdaReconstruction.h"
#include "TLorentzVector.h"
/**
Creates Lambda candidates from a neutron and two photons from a pi0 decay
*/

namespace eicrecon {

void FarForwardLambdaReconstruction::init() {

}

/* converts one type of vector format to another */
void toTVector3(TVector3& v1, const edm4hep::Vector3f& v2){
v1.SetXYZ(v2.x,v2.y,v2.z);
}


void FarForwardLambdaReconstruction::process(const FarForwardLambdaReconstruction::Input& input,
const FarForwardLambdaReconstruction::Output& output) const {
const auto [neutrons, gammas] = input;
auto [out_lambdas, out_decay_products] = output;

if (neutrons->size()!=1 || gammas->size()!=2)
return;

double En=(*neutrons)[0].getEnergy();
double pn=sqrt(En*En-m_neutron*m_neutron);
double E1=(*gammas)[0].getEnergy();
double E2=(*gammas)[1].getEnergy();
TVector3 xn;
TVector3 x1;
TVector3 x2;

toTVector3(xn,(*neutrons)[0].getReferencePoint()*dd4hep::mm);
toTVector3(x1,(*gammas)[0].getReferencePoint()*dd4hep::mm);
toTVector3(x2,(*gammas)[1].getReferencePoint()*dd4hep::mm);

xn.RotateY(-m_cfg.rot_y);
x1.RotateY(-m_cfg.rot_y);
x2.RotateY(-m_cfg.rot_y);


/*std::cout << "nx recon " << xn.X() << " g1x recon " << x1.X() << " g2x recon " << x2.X() << std::endl;

std::cout << "nz recon " << xn.Z() << " g1z recon " << x1.Z() << " g2z recon " << x2.Z() << " z face=" << m_cfg.zmax<< std::endl;*/


TVector3 vtx(0,0,0);
double f=0;
double df=0.5;
double theta_open_expected=2*asin(m_pi0/(2*sqrt(E1*E2)));
for(int i = 0; i<m_cfg.iterations; i++){
TLorentzVector n(pn*(xn-vtx).Unit(), En);
TLorentzVector g1(E1*(x1-vtx).Unit(), E1);
TLorentzVector g2(E2*(x2-vtx).Unit(), E2);
double theta_open=g1.Angle(g2.Vect());
TLorentzVector lambda=n+g1+g2;
if (theta_open>theta_open_expected)
f-=df;
else if (theta_open<theta_open_expected)
f+=df;

vtx=lambda.Vect()*(f*m_cfg.zmax/lambda.Z());
df/=2;

if (i==m_cfg.iterations-1){
double mass_rec=lambda.M();
if (abs(mass_rec-m_lambda)>m_cfg.lambda_max_mass_dev)
return;

// rotate everything back to the lab coordinates.
vtx.RotateY(m_cfg.rot_y);
lambda.RotateY(m_cfg.rot_y);
n.RotateY(m_cfg.rot_y);
g1.RotateY(m_cfg.rot_y);
g2.RotateY(m_cfg.rot_y);

auto b=-lambda.BoostVector();
n.Boost(b);
g1.Boost(b);
g2.Boost(b);

//convert vertex to mm:
vtx=vtx*(1/dd4hep::mm);

auto rec_part = out_lambdas->create();
rec_part.setPDG(3122);
//edm4hep::Vector3f position(vtx.X(), vtx.Y(), vtx.Z());
//edm4hep::Vector3f momentum(lambda.X(), lambda.Y(), lambda.Z());

rec_part.setEnergy(lambda.E());
rec_part.setMomentum({static_cast<float>(lambda.X()), static_cast<float>(lambda.Y()), static_cast<float>(lambda.Z())});
rec_part.setReferencePoint({static_cast<float>(vtx.X()), static_cast<float>(vtx.Y()), static_cast<float>(vtx.Z())});
rec_part.setCharge(0);
rec_part.setMass(mass_rec);

rec_part = out_decay_products->create();
rec_part.setPDG(2112);
rec_part.setEnergy(n.E());
rec_part.setMomentum({static_cast<float>(n.X()), static_cast<float>(n.Y()), static_cast<float>(n.Z())});
rec_part.setReferencePoint({static_cast<float>(vtx.X()), static_cast<float>(vtx.Y()), static_cast<float>(vtx.Z())});
rec_part.setCharge(0);
rec_part.setMass(m_neutron);

rec_part = out_decay_products->create();
rec_part.setPDG(22);
rec_part.setEnergy(g1.E());
rec_part.setMomentum({static_cast<float>(g1.X()), static_cast<float>(g1.Y()), static_cast<float>(g1.Z())});
rec_part.setReferencePoint({static_cast<float>(vtx.X()), static_cast<float>(vtx.Y()), static_cast<float>(vtx.Z())});
rec_part.setCharge(0);
rec_part.setMass(0);

rec_part = out_decay_products->create();
rec_part.setPDG(22);
rec_part.setEnergy(g2.E());
rec_part.setMomentum({static_cast<float>(g2.X()), static_cast<float>(g2.Y()), static_cast<float>(g2.Z())});
rec_part.setReferencePoint({static_cast<float>(vtx.X()), static_cast<float>(vtx.Y()), static_cast<float>(vtx.Z())});
rec_part.setCharge(0);
rec_part.setMass(0);

return;

}
}
}
}
51 changes: 51 additions & 0 deletions src/algorithms/reco/FarForwardLambdaReconstruction.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright (C) 2025 Sebouh Paul

#pragma once
#include <DD4hep/Detector.h>
#include <algorithms/algorithm.h>
#include <algorithms/geo.h>
#include <edm4eic/ReconstructedParticleCollection.h>
#include <spdlog/logger.h>
#include <gsl/pointers>
#include <memory>
#include <string> // for basic_string
#include <string_view> // for string_view

#include "algorithms/interfaces/WithPodConfig.h"
#include "algorithms/reco/FarForwardLambdaReconstructionConfig.h"

namespace eicrecon {

using FarForwardLambdaReconstructionAlgorithm = algorithms::Algorithm<
algorithms::Input<
const edm4eic::ReconstructedParticleCollection,
const edm4eic::ReconstructedParticleCollection
>,
algorithms::Output<
edm4eic::ReconstructedParticleCollection,
edm4eic::ReconstructedParticleCollection
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than duplicate collections (since the decay products here will be a subset of the input collections), I think there are a couple approaches that might be more natural here! And this actually raises a point I don't think we've fully discussed with the PWGs: how do we handle decay products in our data model?

The 1st approach (and the one I'd suggest going with now) would be to utilize the one-to-many relations between ReconstructedParticle and other ReconstructedParticles:

    OneToManyRelations:
      - edm4eic::Cluster     clusters       // Clusters used for this particle
      - edm4eic::Track       tracks         // Tracks used for this particle
      - edm4eic::ReconstructedParticle particles // Reconstructed particles that have been combined to this particle
      - edm4hep::ParticleID  particleIDs    // All associated particle IDs for this particle (not sorted by likelihood)

The 2nd would be to have a ReconstructedParticle-to-ReconstructedParticle association to keep track of parent-to-decay relations.

Sidebar: speaking personally, I'm in favor the 1st approach since I tend to conceptualize identifying resonances as "re-combining" their decay products, and since reconstruction goes in reverse time-ordering in a certain sense (earlier stages of the collision are reconstructed later in the algorithmic chain).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd have to think about this. The way this works right now is that we have a collection of neutrons and photons (which I have now merged) in the lab frame, and the output is a collection of lambdas in the lab frame, and a collection of lambda decay products in the lambda CM frame. Is it possible to have the lambda reconstruction modify the particles in the input collection (ie, change their reference point to where the reconstructed lambda decayed, and the momentum direction to point to this point)? Still, I would like some way to store the direction of the neutron in the lambda's CM frame, since that's important for polarization measurements ... not sure the best way to do this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahhh, I see. Unfortunately, you won't be able to modify the input collections since collections are immutable. But I think there's a couple of ways we could go about this!

  • On one hand, as long as the production vertex is saved, the boosting can always be done after reconstruction in analysis since you'll have the decay products identified via the relations. I think this might be the simplest approach here, since you have immediate access to the decay products via relations...
  • Another approach could be to add a new algorithm that boosts a collection of particles into the CM frame of a specified resonance. This is similar to the approach we took for the Breit frame transformation. This might get a little tricky for analysis, though, since you only need a subset of the reconstructed neutrals in the ZDC...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The kinematics of the decay products are not the same as the ones from the neutrals reconstruction, since the vertex position is no longer assumed to be at the origin (so the direction of their momenta changes when doing this).

I think I have found a work-around here. I am storing the CM-frame decay products in the same collection as the lambdas, and then adding these particles to the list of particles associated with the lambda.

Copy link
Contributor

@ruse-traveler ruse-traveler Feb 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't do that: that runs counter to all other reconstructed particle collections, and will make things confusing by mixing different levels of information in a single branch (the resonance vs. the decay products).

If you have the production vertex, the original momenta (wrt. the origin) of the decay products, and the lambda momentum, wouldn't it be straightforward enough to do the transformation downstream? Either in an analysis script or in subsequent algorithm?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I've been mulling it over, and wanted to clarify my thoughts here:

  • While I would prefer the boosting be moved to either a downstream algorithm or analysis code, I am willing to compromise on leaving it in this algorithm for now and make factorizing this part of the algorithm a to-do
  • If we do leave in this algorithm, though, I think it would be best to write out the lambdas and boosted decay products in separate collections, since
    • This will make it easier to factorize later,
    • And makes sure we aren't mixing levels of information in each collection!
  • The one-to-many relations require some care, though...
    • So far, relations almost always point backwards to inputs (e.g. the cluster-hit relations) or horizontally to objects grouped together (e.g. the PseudoCluster)
    • This raises the question of how the boosted particles should relate to lambdas and the input particles... My thinking so far is:
      1. The lambda one-to-many relations point backwards to the (unboosted) input particles used to reconstruct the lambda,
      2. And the boosted particle one-to-many relations point backwards to the original, unboosted particle that got boosted (so "many" = 1 here)...
    • The rationale for this scheme is to preserve the algorithmic flow being done here...

What are your thoughts, @veprbl and @wdconinc? How do the FCC folks typically handle situations like this?

>
>;
class FarForwardLambdaReconstruction :
public FarForwardLambdaReconstructionAlgorithm,
public WithPodConfig<FarForwardLambdaReconstructionConfig> {
public:
FarForwardLambdaReconstruction(std::string_view name)
: FarForwardLambdaReconstructionAlgorithm{name,
{"inputNeutrons", "inputPhotons"},
{"outputLambda", "outputLambdaDecayProducts"},
"Reconstructs lambda candidates and their decay products from the reconstructed neutrons and photons"} {}

void init() final;
void process(const Input&, const Output&) const final;

private:
std::shared_ptr<spdlog::logger> m_log;
double m_neutron{0.93956542052};
double m_pi0{0.1349768277676847};
double m_lambda{1.115683138712051};
const dd4hep::Detector* m_detector{algorithms::GeoSvc::instance().detector()};

};
} // namespace eicrecon
21 changes: 21 additions & 0 deletions src/algorithms/reco/FarForwardLambdaReconstructionConfig.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright (C) 2025 Sebouh Paul
#pragma once
#include <float.h>
#include <DD4hep/Detector.h>


namespace eicrecon {

struct FarForwardLambdaReconstructionConfig {
/** transformation from global coordinates to proton-frame coordinates*/
double rot_y=-0.025;
/** distance to the ZDC */
double zmax=35800*dd4hep::mm;
/** maximum deviation between reconstructed mass and PDG mass */
double lambda_max_mass_dev=0.030*dd4hep::GeV;
/** number of iterations for the IDOLA algorithm */
int iterations=10;
};

} // eicrecon
133 changes: 133 additions & 0 deletions src/algorithms/reco/FarForwardNeutralsReconstruction.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright (C) 2025 Sebouh Paul

#include <edm4eic/ClusterCollection.h>
#include <edm4eic/ReconstructedParticleCollection.h>
#include <edm4hep/Vector3f.h>
#include <edm4hep/utils/vector_utils.h>
#include <cmath>
#include <gsl/pointers>
#include <stdexcept>
#include <vector>

#include "FarForwardNeutralsReconstruction.h"

/**
Creates photon candidate Reconstructed Particles, using clusters which fulfill cuts on the position of their CoG position, length (sqrt of largest eigenvector of their moment matrix), and width (sqrt of second largest eigenvector of their moment matrix). Its energy is the energy of the cluster times a correction factor.
Also creates a "neutron candidate" Reconstructed Particle consisting of all remaining clusters in the collection. Its energy is the sum of the energies of the constituent clusters
times a correction factor, and its direction is the direction from the origin to the position
of the most energetic cluster. The correction factors in both cases are given by 1/(1+c[0]+c[1]/sqrt(E)+c[2]/E),
where c is the coefficients and E is the uncorrected energy in GeV. Different correction coefficients are used for photons vs for neutrons. This form was chosen
empirically based on the discrepancies in single-neutron and single-photon MC simulations between the uncorrected
reconstructed energies and the truth energies of the neutrons. The parameter values should be co-tuned with those of the clustering algorithm being used.
*/

namespace eicrecon {

void FarForwardNeutralsReconstruction::init() {
if (m_cfg.n_scale_corr_coeff_hcal.size() < 3) {
error("Invalid configuration. m_cfg.n_scale_corr_coeff_hcal should have at least 3 parameters");
throw std::runtime_error("Invalid configuration. m_cfg.n_scale_corr_coeff_hcal should have at least 3 parameters");
}
if (m_cfg.gamma_scale_corr_coeff_hcal.size() < 3) {
error("Invalid configuration. m_cfg.gamma_scale_corr_coeff_ecal should have at least 3 parameters");
throw std::runtime_error("Invalid configuration. m_cfg.gamma_scale_corr_coeff_ecal should have at least 3 parameters");
}
}
/** calculates the correction for a given uncorrected total energy and a set of coefficients*/
double FarForwardNeutralsReconstruction::calc_corr(double Etot, const std::vector<double>& coeffs) const{
return coeffs[0]+coeffs[1]/sqrt(Etot)+coeffs[2]/Etot;
}

/**
check that the cluster position is within the correct range,
and that the sqrt(largest eigenvalue) is less than gamma_max_length,
and that the sqrt(second largest eigenvalue) is less than gamma_max_width
*/
bool FarForwardNeutralsReconstruction::isGamma(const edm4eic::Cluster& cluster) const{
double l1=sqrt(cluster.getShapeParameters(4))*dd4hep::mm;
double l2=sqrt(cluster.getShapeParameters(5))*dd4hep::mm;
double l3=sqrt(cluster.getShapeParameters(6))*dd4hep::mm;

//z in the local coordinates
double z= (cluster.getPosition().z*cos(m_cfg.rot_y)+cluster.getPosition().x*sin(m_cfg.rot_y))*dd4hep::mm;

/*std::cout << "zcut=" << m_cfg.gamma_zmax << " z recon=" << z << std::endl;
std::cout << "l1=" << l1 << " l2=" << l2 << " l3=" << l3 << std::endl;
std::cout << "max length=" << m_cfg.gamma_max_length << " max width=" << m_cfg.gamma_max_width << std::endl;*/
if (z> m_cfg.gamma_zmax)
return false;
if (l1> m_cfg.gamma_max_length || l2> m_cfg.gamma_max_length || l3 > m_cfg.gamma_max_length)
return false;
if ((l1> m_cfg.gamma_max_width) + (l2> m_cfg.gamma_max_width) + (l3 > m_cfg.gamma_max_width)>2)
return false;
return true;

}



void FarForwardNeutralsReconstruction::process(const FarForwardNeutralsReconstruction::Input& input,
const FarForwardNeutralsReconstruction::Output& output) const {
const auto [clustersHcal] = input;
auto [out_neutrons, out_gammas] = output;

double Etot_hcal=0;
double Emax=0;
edm4hep::Vector3f n_position;
for (const auto& cluster : *clustersHcal) {
double E = cluster.getEnergy();

if(isGamma(cluster)){
auto rec_part = out_gammas->create();
rec_part.setPDG(22);
edm4hep::Vector3f position = cluster.getPosition();
double corr=calc_corr(E,m_cfg.gamma_scale_corr_coeff_hcal);
E=E/(1+corr);
double p = E;
double r = edm4hep::utils::magnitude(position);
edm4hep::Vector3f momentum = position * (p / r);
rec_part.setEnergy(E);
rec_part.setMomentum(momentum);
rec_part.setReferencePoint(position);
rec_part.setCharge(0);
rec_part.setMass(0);
rec_part.addToClusters(cluster);
continue;
}

Etot_hcal += E;
if (E > Emax){
Emax = E;
n_position = cluster.getPosition();
}
}

double Etot=Etot_hcal;
if (Etot > 0 && Emax > 0){
auto rec_part = out_neutrons->create();
double corr=calc_corr(Etot,m_cfg.n_scale_corr_coeff_hcal);
Etot_hcal=Etot_hcal/(1+corr);
//corr=calc_corr(Etot,m_cfg.scale_corr_coeff_ecal);
//Etot_ecal=Etot_ecal/(1+corr);
Etot=Etot_hcal;//+Etot_ecal;
rec_part.setEnergy(Etot);
rec_part.setPDG(2112);
double p = sqrt(Etot*Etot-m_neutron*m_neutron);
double r = edm4hep::utils::magnitude(n_position);
edm4hep::Vector3f momentum = n_position * (p / r);
rec_part.setMomentum(momentum);
rec_part.setReferencePoint(n_position);
rec_part.setCharge(0);
rec_part.setMass(m_neutron);
for (const auto& cluster : *clustersHcal){
rec_part.addToClusters(cluster);
}
/*for (const auto& cluster : *clustersEcal){
rec_part.addToClusters(cluster);
}*/
}
//m_log->debug("Found {} neutron candidates", out_neutrons->size());

}
}
Loading
Loading