Finding CollectionID

Hello FCC community,

I am trying to figure out what is the collection of references for each object means since for example Jets and MET have six references while others like muons have just one reference. It is written in the EDM4HEP explanation here FCCAnalyses/examples/basics at basicexamples · HEP-FCC/FCCAnalyses · GitHub
that I should know that from the collectionID and find what collection it points to from this list Electron (1), Muon (2), AllMuon (3), EFlowNeutralHadron (4), Particle (5), Photon (6), ReconstructedParticles (7), EFlowPhoton (8), MCRecoAssociations (9), MissingET (10), ParticleIDs (11), Jet (12), EFlowTrack (13), EFlowTrack_1 (14)

It says that for example collectionID for muon#0 is number 7 so it belongs to the reconstructed particle collection. But When I opened one of the files, I found the muon#0 collectionID to be 15 which is not present the list.

Does anyone know how to this works?

Hi @ymahmoud,

there are two types of collections here. One is regular collection of type edm4hep::ReconstructedParticleCollection (this is “Jets”, “MissingET” and “ReconstructedParticles” collections) and the other type is a subset collection which also appears as edm4hep::ReconstructedParticleCollection. In this case “Muon” collection is subset collection of “ReconstructedParticles” collection.

The subset collection needs to refer only to the original collection, so there is only one reference.
The ordinary edm4hep::ReconstructedParticleCollection can refer to other types of collections (six in total). You can find the list of those collections here under “OneToOneRelations” and “OneToManyRelations”

Be aware, that there are few file level differences between spring2021, winter2023 and current productions as EDM4hep/Podio is evolving.

Best,
Juraj

Thank you @jsmiesko. I have read the yaml file but it doesn’t contain information on the jet collection. Are jet information stored in Reconstructed particles collection too?

Best
yehia

Hi @ymahmoud,

The “Jet” object collection is of type edm4hep::ReconstructedParticleCollection and it is separate collection from “ReconstructedParticles”. It is not subset collection.

Best,
Juraj

Thank you @jsmiesko and sorry for asking too many questions since I am new to FCC.

For the jet collection, how can I know which clustering algorithm was used to obtain this jet collection?

Also, are the particles that were used for the jet clustering also stored in ReconstructedParticles ?

Best
Yehia

Hi @ymahmoud,

there are no metadata like this in the pre-generated samples (spring2021, winter2023) you will probably need to find this information in the appropriate Delphes card. For the pre-generated samples they are hosted here (notice different branches for different campaigns). @selvaggi will probably know more details.

I checked winter2023 campaign and the objects from the “Jet” collection point to the particles from the “ReconstructedParticles” collection.

Best,
Juraj

Hi @ymahmoud

In general we do not recommend using the default jet and MET definitions in the samples. These are produced in the default sequences in Delphes and are configured for exclusive jet clustering for 2 jets, using the ee-durham kT algorithm. For MET, it is the missing transverse momentum (used at hadron colliders), whereas at lepton colliders we have access to the full missing energy vector.

The recommendation is that each analysis should perform jet clustering on the fly in FCCAnaysis. A snippet to do the jet clustering can be found here (I will move this example soon to the main FCCAnalyses repository):

This uses the ee-durham kT algorithm that exclusively clusters for 2 jets. You can change the parameters to cluster according to your needs. More info can be found here:

The missing energy can be recomputed knowing the center-of-mass of the collisions (usually 240 GeV for the Higgs):

Let us know if you have any other questions.

Best,
Jan

Thank you @jaeyserm, this was really helpful.

I tried running the jet clustering algorithm and it ran just fine. But I get an error when using this FCCAnalyses::makeLorentzVectors or FCCAnalyses::jetTruthFinder
it says no member function with this name in FCCAnalysis namespace

Also in jetTruthFinder, how do you take parameters like ReconstructedParticles as an argument without defining them first.
Also, what does jetTruthFinder do?

Best,
Yehia

Hi Yehia,

The JetTruthFinder function is a simple algorithm to know the original/true flavor of the clustered jet (e.g. b, c, …). You can certainly remove these lines if you’re not interested in knowing the jet flavor. In case you do want to know, you need to include the C++ function in your analyzer. The corresponding code can be found here: FCCAnalyzer/include/utils.h at main · jeyserma/FCCAnalyzer · GitHub

Best,
Jan

Thank you @jaeyserm,

How can I make changes to the code locally? I tried to change the code in analyzers/dataframe but the changes don’t manifest themselves when I do fccanalysis run. I even commented out some important code so it gives an error but it didn’t.

I am right now trying to add FCCAnalyses::makeLorentzVectors in analyzers/dataframe but it doesn’t work.
I added it to existing file called Utils.h which is in analyzers/dataframe/FCCAnalyses and added a file Utils.cc in analyzers/dataframe/src

Here are the contents of both file

Utils.h

#ifndef  UTILS_ANALYZERS_H
#define  UTILS_ANALYZERS_H
#include <cmath>
#include "defines.h"
#include "TLorentzVector.h"
#include "ROOT/RVec.hxx"
namespace FCCAnalyses {
  namespace Utils {
    template<typename T> inline auto getsize( T& vec){ return vec.size();};
    template<typename T> inline ROOT::VecOps::RVec<ROOT::VecOps::RVec<T> >  as_vector(const ROOT::VecOps::RVec<T>& in){return ROOT::VecOps::RVec<ROOT::VecOps::RVec<T> >(1, in);};                        >
   ROOT::VecOps::RVec<TLorentzVector>  makeLorentzVectors(ROOT::VecOps::RVec<float> jets_px, ROOT::VecOps::RVec<float> jets_py, ROOT::VecOps::RVec<float> jets_pz, ROOT::VecOps::RVec<float> jets_e);
  }
}
#endif


And here is Utils.cc file

#include <cmath>
#include <vector>
#include <math.h>
//#include "TLorentzVector.h"
//#include "ROOT/RVec.hxx"
#include "FCCAnalyses/Utils.h"

namespace FCCAnalyses{
namespace Utils {
ROOT::VecOps::RVec<TLorentzVector>  makeLorentzVectors(ROOT::VecOps::RVec<float> jets_px, ROOT::VecOps::RVec<float> jets_py, ROOT::VecOps::RVec<float> jets_pz, ROOT::VecOps::RVec<float> jets_e){
  ROOT::VecOps::RVec<TLorentzVector> result;
    for(int i=0; i<jets_px.size(); i++) {
        TLorentzVector tlv;
        tlv.SetPxPyPzE(jets_px[i], jets_py[i], jets_pz[i], jets_e[i]);
        result.push_back(tlv);
    }
    return result;

   }
  }
}


Of course now I use FCCAnalyses::Utils::makeLorentzVectors
Best
Yehia

Hi Yehia,

You can easily include your own custom header file that is compiled automatically once executed. Save the C++ code below to myfunctions.h (in the same directory as your python analysis script), and then include that in your python analysis file:

includePaths = ["myfunctions.h"]

The C++ header function goes as follows:

#ifndef FCCANALYZER_MYUTILS_H
#define FCCANALYZER_MYUTILS_H

#include <cmath>
#include <vector>
#include <math.h>

#include "TLorentzVector.h"
#include "ROOT/RVec.hxx"
#include "edm4hep/ReconstructedParticleData.h"
#include "edm4hep/MCParticleData.h"
#include "edm4hep/ParticleIDData.h"
#include "ReconstructedParticle2MC.h"


namespace FCCAnalyses {

Vec_i jetTruthFinder(std::vector<std::vector<int>> constituents, Vec_rp reco, Vec_mc mc, Vec_i mcind) {
    // jet truth=finder: match the gen-level partons (eventually with gluons) with the jet constituents
    // matching by mimimizing the sum of dr of the parton and all the jet constituents 

    Vec_tlv genQuarks; // Lorentz-vector of potential partons (gen-level)
    Vec_i genQuarks_pdgId; // corresponding PDG ID
    for(size_t i = 0; i < mc.size(); ++i) {
        int pdgid = abs(mc.at(i).PDG);
        if(pdgid > 6) continue; // only quarks 
        //if(pdgid > 6 and pdgid != 21) continue; // only quarks and gluons
        TLorentzVector tlv;
        tlv.SetXYZM(mc.at(i).momentum.x,mc.at(i).momentum.y,mc.at(i).momentum.z,mc.at(i).mass);
        genQuarks.push_back(tlv);
        genQuarks_pdgId.push_back(mc.at(i).PDG);
    }

    Vec_tlv recoParticles; // Lorentz-vector of all reconstructed particles
    for(size_t i = 0; i < reco.size(); ++i) {
        auto & p = reco[i];
        TLorentzVector tlv;
        tlv.SetXYZM(p.momentum.x, p.momentum.y, p.momentum.z, p.mass);
        recoParticles.push_back(tlv);
    }

    Vec_i usedIdx;
    Vec_i result;
    for(size_t iJet = 0; iJet < constituents.size(); ++iJet) {
        Vec_d dr;
        for(size_t iGen = 0; iGen < genQuarks.size(); ++iGen) {
            if(std::find(usedIdx.begin(), usedIdx.end(), iGen) != usedIdx.end()) {
                dr.push_back(1e99); // set infinite dr, skip
                continue;
            }
            dr.push_back(0);
            for(size_t i = 0; i < constituents[iJet].size(); ++i) {
                dr[iGen] += recoParticles[constituents[iJet][i]].DeltaR(genQuarks[iGen]);
            }
        }
        int maxDrIdx = std::min_element(dr.begin(),dr.end()) - dr.begin();
        usedIdx.push_back(maxDrIdx);
        result.push_back(genQuarks_pdgId[maxDrIdx]);

    }
    return result;
}


}

#endif

Best,
Jan

Thank you @jaeyserm That solved my problem.