This lesson is superseded (link to newer version)

The Source

Overview

Teaching: 10 min
Exercises: 30 min
Questions
  • What are the elements of the source of an EDAnalyzer?

  • How do I modify the source to get additional information?

Objectives
  • Learn the basic structure of the C++ implementation of an EDAnalyzer.

  • Learn the basics on how to modify the source in order to do perform analysis.

Playing with the DemoAnalyzer.cc file

The DemoAnalyzer.cc file is the main file of our EDAnalyzer. As it was mentioned, the default structure is always the same. Let’s look at what is inside using an editor like vi (here you can find a good cheatsheet for that editor):

vi Demo/DemoAnalyzer/src/DemoAnalyzer.cc

The first thing that you will see is a set of includes:

// system include files
#include <memory>

// user include files
#include "FWCore/Framework/interface/Frameworkfwd.h"
#include "FWCore/Framework/interface/EDAnalyzer.h"

#include "FWCore/Framework/interface/Event.h"
#include "FWCore/Framework/interface/MakerMacros.h"

#include "FWCore/ParameterSet/interface/ParameterSet.h"

These are the most basic Framework classes that are needed to mobilize the CMSSW machinery. In particular, notice the Event.h class. This class contains essentially all the accessors that are needed to extract information from the Event, i.e., from the particle collision. Another important class is the ParameterSet.h. This one will allow us to extract configuration parameters, which can be manipulated using the Demo/DemoAnalyzer/demoanalyzer_cfg.py python file.

Something important to take into account is that you can learn a lot about the sort of information you have access to by exploring the code in the CMSSW repository on Github. For instance, you can look at the Event.h header and check all the available methods. You will notice, for instance, the presence of the getByLabel accessors; we will be using one these to access physics objects.

When exploring CMSSW code on Github, remember to choose the CMSSW_5_3_X branch.

Including muon headers

Let’s pretend that we are interested in extracting the energy of all the muons in the event. We would need to add the appropriate classes for this. After quickly reviewing this guide, we conclude that we need to add these two header lines to our analyzer:

//classes to extract Muon information
#include "DataFormats/MuonReco/interface/Muon.h"
#include "DataFormats/MuonReco/interface/MuonFwd.h"

Let’s add them at the end of the header section together with the standard vector C++ library:

#include<vector>

So our header section becomes:

// system include files
#include <memory>

// user include files
#include "FWCore/Framework/interface/Frameworkfwd.h"
#include "FWCore/Framework/interface/EDAnalyzer.h"

#include "FWCore/Framework/interface/Event.h"
#include "FWCore/Framework/interface/MakerMacros.h"

#include "FWCore/ParameterSet/interface/ParameterSet.h"


//classes to extract Muon information
#include "DataFormats/MuonReco/interface/Muon.h"
#include "DataFormats/MuonReco/interface/MuonFwd.h"

#include<vector>

Next, you will see the class declaration:

//
// class declaration
//

class DemoAnalyzer : public edm::EDAnalyzer {
   public:
      explicit DemoAnalyzer(const edm::ParameterSet&);
      ~DemoAnalyzer();

      static void fillDescriptions(edm::ConfigurationDescriptions& descriptions);


   private:
      virtual void beginJob() ;
      virtual void analyze(const edm::Event&, const edm::EventSetup&);
      virtual void endJob() ;

      virtual void beginRun(edm::Run const&, edm::EventSetup const&);
      virtual void endRun(edm::Run const&, edm::EventSetup const&);
      virtual void beginLuminosityBlock(edm::LuminosityBlock const&, edm::EventSetup const&);
      virtual void endLuminosityBlock(edm::LuminosityBlock const&, edm::EventSetup const&);

      // ----------member data ---------------------------
};

The first thing one notices is that our class inherits from the edm::EDAnalyzer class. It follows the same structure as any class in C++. The declaration of the methods reflect the functionality needed for particle physics analysis. Their implementation are further below in the same file.

Declaring info containers

Let’s add the declaration of a vector for our energy values:

std::vector<float> muon_e;

This section becomes:

//
// class declaration
//

class DemoAnalyzer : public edm::EDAnalyzer {
   public:
      explicit DemoAnalyzer(const edm::ParameterSet&);
      ~DemoAnalyzer();

      static void fillDescriptions(edm::ConfigurationDescriptions& descriptions);


  private:
     virtual void beginJob() ;
     virtual void analyze(const edm::Event&, const edm::EventSetup&);
     virtual void endJob() ;

     virtual void beginRun(edm::Run const&, edm::EventSetup const&);
     virtual void endRun(edm::Run const&, edm::EventSetup const&);
     virtual void beginLuminosityBlock(edm::LuminosityBlock const&, edm::EventSetup const&);
     virtual void endLuminosityBlock(edm::LuminosityBlock const&, edm::EventSetup const&);

     // ----------member data ---------------------------
    
     std::vector<float> muon_e; //energy values for muons in the event
};

Next, we can see the constructor and destructor of our DemoAnalyzer class:

// constructors and destructor
//
DemoAnalyzer::DemoAnalyzer(const edm::ParameterSet& iConfig)

{
   //now do what ever initialization is needed

}


DemoAnalyzer::~DemoAnalyzer()
{

   // do anything here that needs to be done at desctruction time
   // (e.g. close files, deallocate resources etc.)

}

Note that a ParameterSet object is passed to the constructor. This is then the place where we will read any configuration we might end up implementing through our Demo/DemoAnalyzer/demoanalyzer_cfg.py python configuration file.

The heart of the source file is the analyze method:

// ------------ method called for each event  ------------
void
DemoAnalyzer::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup)
{
   using namespace edm;



#ifdef THIS_IS_AN_EVENT_EXAMPLE
   Handle<ExampleData> pIn;
   iEvent.getByLabel("example",pIn);
#endif

#ifdef THIS_IS_AN_EVENTSETUP_EXAMPLE
   ESHandle<SetupData> pSetup;
   iSetup.get<SetupRecord>().get(pSetup);
#endif
}

Anything that goes inside this routine will loop over all available events. The CMSSW Framework will take care of that, so you do not really have to write a for loop to go over all events. Note that an edm::Event object and a edm::EventSetup object are passed by default. While from the Event we can extract information like physics objects, from the EventSetup we can get information like trigger prescales.

Get the muons energy

Now let’s add a few lines in the analyzer so we can retrieve the energy of all the muons in each event. We will print out this information as an example. Again, after checking out this guide, the analyze method becomes:

// ------------ method called for each event  ------------
void DemoAnalyzer::analyze(const edm::Event& iEvent, const edm::EventSetup& iSetup)
{
 using namespace edm;
 
 //clean the container
 muon_e.clear();
 
 //define the handler and get by label
 Handle<reco::MuonCollection> mymuons;
 iEvent.getByLabel("muons", mymuons);

 //if collection is valid, loop over muons in event
 if(mymuons.isValid()){
    for (reco::MuonCollection::const_iterator itmuon=mymuons->begin(); itmuon!=mymuons->end(); ++itmuon){
        muon_e.push_back(itmuon->energy());
    }
 }

 //print the vector
 for(unsigned int i=0; i < muon_e.size(); i++){
    std::cout <<"Muon # "<<i<<" with E = "<<muon_e.at(i)<<" GeV."<<std::endl;
 }

#ifdef THIS_IS_AN_EVENT_EXAMPLE
  Handle<ExampleData> pIn;
  iEvent.getByLabel("example",pIn);
#endif

#ifdef THIS_IS_AN_EVENTSETUP_EXAMPLE
  ESHandle<SetupData> pSetup;
  iSetup.get<SetupRecord>().get(pSetup);
#endif
}

The other methods are designed to execute instructions according to their own name description.

// ------------ method called once each job just before starting event loop  ------------
void
DemoAnalyzer::beginJob()
{
}

// ------------ method called once each job just after ending the event loop  ------------
void
DemoAnalyzer::endJob()
{
}

// ------------ method called when starting to processes a run  ------------
void
DemoAnalyzer::beginRun(edm::Run const&, edm::EventSetup const&)
{
}

// ------------ method called when ending the processing of a run  ------------
void
DemoAnalyzer::endRun(edm::Run const&, edm::EventSetup const&)
{
}

// ------------ method called when starting to processes a luminosity block  ------------
void
DemoAnalyzer::beginLuminosityBlock(edm::LuminosityBlock const&, edm::EventSetup const&)
{
}

// ------------ method called when ending the processing of a luminosity block  ------------
void
DemoAnalyzer::endLuminosityBlock(edm::LuminosityBlock const&, edm::EventSetup const&)
{
}

For instance, any instructions placed inside the beginRun routine will be executed every time the Framework sees a new Run (a Run is determined by the start and stop of the acquisition of the CMS detector). During the workshop, we will use the beginJob and endJob routines to book histograms and write output files.

Let’s compile

scram b

Look at the output

Well, it fails.

>> Local Products Rules ..... started
>> Local Products Rules ..... done
>> Building CMSSW version CMSSW_5_3_32 ----
>> Subsystem FT_53_LV5_AN1 built
>> Entering Package Demo/DemoAnalyzer
>> Creating project symlinks
  src/Demo/DemoAnalyzer/python -> python/Demo/DemoAnalyzer
Entering library rule at Demo/DemoAnalyzer
>> Compiling edm plugin /home/cmsusr/test/CMSSW_5_3_32/src/Demo/DemoAnalyzer/src/DemoAnalyzer.cc
In file included from /opt/cms/slc6_amd64_gcc472/cms/cmssw/CMSSW_5_3_32/src/DataFormats/TrackingRecHit/interface/TrackingRecHit.h:4:0,
                from /opt/cms/slc6_amd64_gcc472/cms/cmssw/CMSSW_5_3_32/src/DataFormats/TrackingRecHit/interface/RecSegment.h:17,
                from /opt/cms/slc6_amd64_gcc472/cms/cmssw/CMSSW_5_3_32/src/DataFormats/DTRecHit/interface/DTRecSegment4D.h:16,
                from /opt/cms/slc6_amd64_gcc472/cms/cmssw/CMSSW_5_3_32/src/DataFormats/DTRecHit/interface/DTRecSegment4DCollection.h:20,
                from /opt/cms/slc6_amd64_gcc472/cms/cmssw/CMSSW_5_3_32/src/DataFormats/MuonReco/interface/MuonSegmentMatch.h:6,
                from /opt/cms/slc6_amd64_gcc472/cms/cmssw/CMSSW_5_3_32/src/DataFormats/MuonReco/interface/MuonChamberMatch.h:5,
                from /opt/cms/slc6_amd64_gcc472/cms/cmssw/CMSSW_5_3_32/src/DataFormats/MuonReco/interface/Muon.h:17,
                from /home/cmsusr/test/CMSSW_5_3_32/src/Demo/DemoAnalyzer/src/DemoAnalyzer.cc:34:
/opt/cms/slc6_amd64_gcc472/cms/cmssw/CMSSW_5_3_32/src/DataFormats/CLHEP/interface/AlgebraicObjects.h:8:33: fatal error: CLHEP/Matrix/Vector.h: No such file or directory compilation terminated.
In file included from /opt/cms/slc6_amd64_gcc472/cms/cmssw/CMSSW_5_3_32/src/DataFormats/TrackingRecHit/interface/TrackingRecHit.h:4:0,
                from /opt/cms/slc6_amd64_gcc472/cms/cmssw/CMSSW_5_3_32/src/DataFormats/TrackingRecHit/interface/RecSegment.h:17,
                from /opt/cms/slc6_amd64_gcc472/cms/cmssw/CMSSW_5_3_32/src/DataFormats/DTRecHit/interface/DTRecSegment4D.h:16,
                from /opt/cms/slc6_amd64_gcc472/cms/cmssw/CMSSW_5_3_32/src/DataFormats/DTRecHit/interface/DTRecSegment4DCollection.h:20,
                from /opt/cms/slc6_amd64_gcc472/cms/cmssw/CMSSW_5_3_32/src/DataFormats/MuonReco/interface/MuonSegmentMatch.h:6,
                from /opt/cms/slc6_amd64_gcc472/cms/cmssw/CMSSW_5_3_32/src/DataFormats/MuonReco/interface/MuonChamberMatch.h:5,
                from /opt/cms/slc6_amd64_gcc472/cms/cmssw/CMSSW_5_3_32/src/DataFormats/MuonReco/interface/Muon.h:17,
                from /home/cmsusr/test/CMSSW_5_3_32/src/Demo/DemoAnalyzer/src/DemoAnalyzer.cc:34:
/opt/cms/slc6_amd64_gcc472/cms/cmssw/CMSSW_5_3_32/src/DataFormats/CLHEP/interface/AlgebraicObjects.h:8:33: fatal error: CLHEP/Matrix/Vector.h: No such file or directory
compilation terminated.
gmake: *** [tmp/slc6_amd64_gcc472/src/Demo/DemoAnalyzer/src/DemoDemoAnalyzer/DemoAnalyzer.o] Error 1
gmake: *** [There are compilation/build errors. Please see the detail log above.] Error 2

This is because the Muon classes we added introduced some dependencies that need to be taken care of in the BuildFile.xml

So let’s modify the Demo/DemoAnalyzer/BuildFile.xml to include DataFormats/MuonReco dependencies. It should look like:

<use name="FWCore/Framework"/>
<use name="FWCore/PluginManager"/>
<use name="DataFormats/MuonReco"/>
<use name="FWCore/ParameterSet"/>
<flags EDM_PLUGIN="1"/>
<export>
   <lib name="1"/>
</export>

Now, if you compile again, it should work. Then, we can run with the cmsRun executable:

cmsRun Demo/DemoAnalyzer/demoanalyzer_cfg.py > mylog.log 2>&1 &

Well, it turns out that in the 10 events you looped over, there weren’t many muons. Of course, the ROOT file over which you are running is from an ElectronHad(ronic) datset, so it won’t have that many. In any case, your log file should output something like:

cat mylog.log
18-Sep-2020 08:22:40 CEST  Initiating request to open file root://eospublic.cern.ch//eos/opendata/cms/Run2011A/ElectronHad/AOD/12Oct2013-v1/20001/001F9231-F141-E311-8F76-003048F00942.root
18-Sep-2020 08:22:42 CEST  Successfully opened file root://eospublic.cern.ch//eos/opendata/cms/Run2011A/ElectronHad/AOD/12Oct2013-v1/20001/001F9231-F141-E311-8F76-003048F00942.root
Begin processing the 1st record. Run 166782, Event 340184599, LumiSection 309 at 18-Sep-2020 08:22:56.695 CEST
Begin processing the 2nd record. Run 166782, Event 340185007, LumiSection 309 at 18-Sep-2020 08:22:56.716 CEST
Begin processing the 3rd record. Run 166782, Event 340187903, LumiSection 309 at 18-Sep-2020 08:22:56.717 CEST
Begin processing the 4th record. Run 166782, Event 340227487, LumiSection 309 at 18-Sep-2020 08:22:56.717 CEST
Begin processing the 5th record. Run 166782, Event 340210607, LumiSection 309 at 18-Sep-2020 08:22:56.717 CEST
Begin processing the 6th record. Run 166782, Event 340256207, LumiSection 309 at 18-Sep-2020 08:22:56.718 CEST
Begin processing the 7th record. Run 166782, Event 340165759, LumiSection 309 at 18-Sep-2020 08:22:56.718 CEST
Begin processing the 8th record. Run 166782, Event 340396487, LumiSection 309 at 18-Sep-2020 08:22:56.718 CEST
Muon # 0 with E = 27.3492 GeV.
Begin processing the 9th record. Run 166782, Event 340390767, LumiSection 309 at 18-Sep-2020 08:22:56.720 CEST
Begin processing the 10th record. Run 166782, Event 340435263, LumiSection 309 at 18-Sep-2020 08:22:56.720 CEST
18-Sep-2020 08:22:56 CEST  Closed file root://eospublic.cern.ch//eos/opendata/cms/Run2011A/ElectronHad/AOD/12Oct2013-v1/20001/001F9231-F141-E311-8F76-003048F00942.root

=============================================

MessageLogger Summary

 type     category        sev    module        subroutine        count    total
 ---- -------------------- -- ---------------- ----------------  -----    -----
    1 fileAction           -s file_close                             1        1
    2 fileAction           -s file_open                              2        2

 type    category    Examples: run/evt        run/evt          run/evt
 ---- -------------------- ---------------- ---------------- ----------------
    1 fileAction           PostEndRun                        
    2 fileAction           pre-events       pre-events       

Severity    # Occurrences   Total Occurrences
--------    -------------   -----------------
System                  3                   3

Key Points

  • The C++ source file of an EDAnalyzer is taylored for particle physics analysis under the CMSSW Framework.

  • This source file needs to be modified according to the analyzer needs