Creating a Monitoring Module for AlignmentProducer
Complete:
Goal of this page
See
Alignment algorithms::Monitoring for an introduction to the
CommonAlignmentMonitor
package. This page explains how to make your own histogram module.
A consistent release
You need to start with a consistent set of tags to compile and run the
AlignmentProducer
, because (a) you'll need it to test your module and (b) what else would you be monitoring?
(Need to update if this package is ever applied to calibration...)
Update: version 1_5_0 of AlignmentProducer works with the latest version of CommonAlignmentMonitor (V00-02-02). Use these or a later version. If you need to see the recipe for 1_3_X and 1_4_X, look in the history of this twiki page (r5).
Your new monitor
You will make a new subclass of
AlignmentMonitorBase
, following
AlignmentMoniterHIP
as a model. The name should conform to
AlignmentMonitorXXX
to keep all histogram modules in the same corner of
SEAL
namespace. Change directories to
Alignment/CommonAlignmentMonitor/plugins/
and create a new file named
AlignmentMonitorXXX.cc
. (If using the old plugin manager, put the file in
/src/
.) Fill the new file with
// -*- C++ -*-
//
// Package: CommonAlignmentProducer
// Class : AlignmentMonitorXXX
//
// Implementation:
// <Notes on implementation>
//
// Original Author: Who?
// Created: When?
// $Id: SWGuideAlignmentMonitors.txt,v 1.6 2007/07/09 13:52:15 pivarski Exp $
//
#include "Alignment/CommonAlignmentMonitor/interface/AlignmentMonitorPluginFactory.h"
#include "Alignment/CommonAlignmentMonitor/interface/AlignmentMonitorBase.h"
class AlignmentMonitorXXX: public AlignmentMonitorBase {
public:
AlignmentMonitorXXX(const edm::ParameterSet& cfg): AlignmentMonitorBase(cfg) { };
~AlignmentMonitorXXX() {};
void book();
void event(const edm::EventSetup &iSetup, const ConstTrajTrackPairCollection& iTrajTracks);
void afterAlignment(const edm::EventSetup &iSetup);
private:
};
void AlignmentMonitorHIP::book() {
}
void AlignmentMonitorHIP::event(const edm::EventSetup &iSetup, const ConstTrajTrackPairCollection& tracks) {
}
void AlignmentMonitorHIP::afterAlignment(const edm::EventSetup &iSetup) {
}
// DEFINE_SEAL_MODULE();
// DEFINE_SEAL_PLUGIN(AlignmentMonitorPluginFactory, AlignmentMonitorXXX, "AlignmentMonitorXXX");
DEFINE_EDM_PLUGIN(AlignmentMonitorPluginFactory, AlignmentMonitorXXX, "AlignmentMonitorXXX");
If using the old plugin manager, you need to use the
DEFINE_SEAL_MODULE()
,
DEFINE_SEAL_PLUGIN()
sequence; if using the new plugin manager (
1_5_0_pre1
or later), use
DEFINE_EDM_PLUGIN()
. Also, if using the new plugin manager, add
<library name="AlignmentMonitorXXX" file="AlignmentMonitorXXX.cc">
<use name=Alignment/CommonAlignmentMonitor>
<use name=TrackingTools/TrackFitters>
<flags EDM_PLUGIN=1>
</library>
to
Alignment/CommonAlignmentMonitors/plugins/BuildFile
.
One last thing. (Sorry about this mess.) If you're using the old plugin manager, you can't compile multiple monitors because the
DEFINE_SEAL_MODULE()
line should appear only once. The proper way to handle this would have been to make a
SealModule.cc
file, and split all the monitors into
.cc
and
.h
files. To get a local copy working, just delete
AlignmentMonitorHIP.cc
. We'll have to adapt everything to the new plugin manager to put it in the database anyway.
Now it should compile.
I've found that sometimes new monitors are not automatically loaded into
SEAL
. To force it to be loaded, run
SealPluginsDump | grep AlignmentMonitor
This command is available after you run
eval `scramv1 run -sh`
or
-csh
.
Adding histograms
1. Declare your histogram in the
private:
field of your class definition, e.g.
private:
TH1F *m_hist;
The histogram may be anything that descends from
TH1
(that is, any ROOT histogram, including 2D histograms and profile plots).
2. Book it in the
book()
method like this:
void AlignmentMonitorHIP::book() {
m_hist = (TH1F*)(add("/", new TH1F("hist", "normal constructor", 100, 0., 1.)));
}
The
add()
function manages the ROOT file, handling iterations and job collection automatically. Let's step through the program flow:
- First, the ROOT constructor (in this case,
TH1F
) does its thing in the normal way.
- This is passed to
add()
with a string "/"
. The string tells add()
where in the ROOT file the histogram belongs.
- If this is a new ROOT file (first iteration),
add()
will create whatever parent directories are needed and place the histogram at that point in the file.
- If this is an old ROOT file (second or later iteration),
add()
will find the old histogram in the file and throw away the new one you just made.
- The
add()
function returns a histogram, either the new one or the old one. (Unfortunately, you need to cast it. But that isn't so bad, because the cast isn't very far away from the constructor, so it's easy to check for incorrect casts.)
On the first iteration,
book()
books the histograms and saves them in the normal way, but on subsequent iterations,
book()
is actually resetting your
m_hist
pointers to the histograms they belong to: the newly constructed histogram becomes a look-up specifier that is discarded when the search is complete. This is convenient when an iterative algorithm is executed in several invocations of
cmsRun
, and we want histograms that use the iteration number as data (e.g. residual RMS versus iteration number).
Most histograms, however, should be reset with each iteration. Better still, they should be booked anew so that iteration N results can be compared with iteration N+1. To do this, replace the
"/"
string with
"/iterN/"
(both slashes are necessary). This is code for
"/iter1/"
on the first iteration,
"/iter2/"
on the second iteration, etc. The histogram specified by the constructor will not be thrown away; it will be placed into the directory appropriate to the current iteration. You can make arbitrarily deep directory specifications, such as "/iterN/residprofiles/" and "/iterN/overlapplots/station11/": the whole subdirectory structure will be copied from iteration to iteration.
Directory names always begin and end with slashes. Do not use the standard ROOT
TFile
and
TDirectory
tools: make all directories through the
add()
function. Don't make a directory named "/iter2/" or something--- that's just asking for trouble.
The
add()
function also merges histograms in a collection job after parallel processing. In this case, newly-constructed histograms are saved into the grand total ROOT file, and
add()
uses the names and directories to find all the subjob histograms that need to be merged into the grand total. All of that happens automatically.
You may be wondering, I see a "new", where's the "delete"? It happens at the end of the iteration in AlignmentMonitorBase, but only for histograms that
should be deleted at the end of each iteration (histograms in the "iterN" directories). That is to say, your plugin creates references to histograms, and the AlignmentMonitor framework "steals" those references; you are no longer responsible for deleting them. So don't delete them, or you'll cause a segmentation fault!
3. Fill your histograms. You may do this in
event()
or
afterAlignment()
, whichever is appropriate.
The
event(const edm::EventSetup &iSetup, const
ConstTrajTrackPairCollection
&tracks)
function is called in the event loop, and it supplies a list of trajectory-track pairs. Here's how to iterate over them:
for (ConstTrajTrackPairCollection::const_iterator iter = tracks.begin(); iter != tracks.end(); ++iter) {
const Trajectory *traj = iter->first;
const reco::Track *track = iter->second;
std::vector<TrajectoryMeasurement> trajectoryIntersections = traj->measurements();
for (std::vector<TrajectoryMeasurement>::const_iterator interIter = trajectoryIntersections.begin(); interIter != trajectoryIntersections.end(); ++interIter) {
const TrajectoryMeasurement trajectoryIntersection = *interIter;
const TransientTrackingRecHit *hit = &(*interIter.recHit());
...
}
}
The
afterAlignment(const edm::EventSetup &iSetup)
function is called at the end of an iteration, after updating the alignable geometry (
AlignableTracker
and
AlignableMuon
) but before updating the production geometry (
TrackerGeometry
,
DTGeometry
, and
CSCGeometry
). You can walk through the
AlignableTracker
,
AlignableMuon
, or
AlignableParameterStore
hierarchies to get the new orientations.
You have access to the current
AlignableTracker
through
pTracker()
, the
AlignableMuon
through
pMuon()
, the
AlignmentParameterStore
through
pStore()
, and the
AlignableNavigator
through
pNavigator()
(all are pointers, NULL if not defined). You can also access the current iteration number through
iteration()
(it starts with 1).
Ntuples or TTrees
Before creating thousands of static histograms, it's good to explore the data with an ntuple, at least to get the binning right. The
add()
function described above places any
TObject
in the file, including
TTrees
, so you can do the following in
book()
:
m_tree = (TTree*)(add("/iterN/", new TTree("tree", "tree")));
However, we still need to add branches, and
AlignmentMonitorBase
has no equivalent of
add()
for branches. This means that if you simply type
m_tree->Branch("x", &m_x, "x/F");
m_tree->Branch("y", &m_y, "y/F");
m_tree->Branch("z", &m_z, "z/F");
then new branches will be added and re-added with every iteration. You can get around this by putting the above in an
if (iteration() == 1) { }
block, but then on subsequent iterations, you'll have no way to access
"x/F"
,
"y/F"
, and
"z/F"
. Therefore, if your ntuple is not in an
"/iterN/"
directory, be sure that you only
Fill()
it in iteration 1 (or you will fill it with zeros, or
NaNs, or random addresses in memory). If the ntuple is in
"/iterN/"
, adding branches will work, since it's a new ntuple with each iteration.
Here are some hard-and-fast rules: if you want to create a new ntuple with each iteration, do so like this:
m_tree = (TTree*)(add("/iterN/", new TTree("tree", "tree"))
m_tree->Branch("x", &m_x, "x/F")
If you want to create one ntuple, for use in all iterations, do so like this:
m_tree = ((TTree*)(add("/", new TTree("tree", "tree"))
if (iteration() == 1) {
m_tree->Branch("x", &m_x, "x/F")
}
and then you
must use
replace AlignmentProducer.maxLoops = 22
to iterate, you cannot call
cmsRun
multiple times.
Ntuples are not merged in a collection job. It's unclear whether we'd want to copy all the ntuple data or merely link it up with a
TChain
. Anyway, we'll only be parallel processing when we understand our algorithms well enough to have static histograms, anyway.
Configuring AlignmentProducer
to use your new monitoring package
That was described in
Alignment algorithms::Monitoring.
Recipes
Histogram code will involve a lot of similar constructions, such as calculating residuals. Here are some code snippets that you can use to quickly make your plots. I encourage others to add to this list (that's what wikis are for)!
Calculating residuals
#include "TrackingTools/TrackFitters/interface/TrajectoryStateCombiner.h"
and in your loop over hits,
const DetId id = hit->geographicalId();
if (hit->isValid() && pNavigator()->detAndSubdetInMap(id)) {
TrajectoryStateOnSurface combinedTrajInter = tsoscomb.combine(trajectoryIntersection.forwardPredictedState(), trajectoryIntersection.backwardPredictedState());
double residual = combinedTrajInter.localPosition().x() - hit->localPosition().x());
}
Booking histograms from selected Alignables
In the class declaration,
std::map<Alignable*, TH1F*> m_residuals;
and in
book()
,
std::vector<Alignable*> alignables = pStore()->alignables();
for (std::vector<Alignable*>::const_iterator it = alignables.begin(); it != alignables.end(); ++it) {
char name[256], title[256];
sprintf(name, "xresid%d", (*it)->geomDetId().rawId());
sprintf(title, "x residual for DetId %d (cm)", (*it)->geomDetId().rawId());
m_residuals[*it] = (TH1F*)(add("/iterN/", new TH1F(name, title, 100, -5., 5.)));
}
and then in
event()
,
const DetId id = hit->geographicalId();
Alignable *alignable = pNavigator()->alignableFromDetId(id);
std::map<Alignable*, TH1F*>::const_iterator search = m_residuals.find(alignable);
while (search == m_residuals.end() && (alignable = alignable->mother())) search = m_residuals.find(alignable);
if (search != m_residuals.end()) {
search->second->FIll(residual);
}
Review Status
Responsible: Main.pivarski
Last reviewed by: Reviewer