CMS 3D CMS Logo

SiStripLorentzAngleCalibration.cc
Go to the documentation of this file.
1 
16 // include 'locally':
18 #include "TreeStruct.h"
19 
24 //#include "CalibTracker/Records/interface/SiStripDependentRecords.h"
26 
29 
36 
41 
42 #include "TTree.h"
43 #include "TFile.h"
44 #include "TString.h"
45 
46 #include <vector>
47 #include <map>
48 #include <sstream>
49 #include <cstdio>
50 #include <memory>
51 #include <functional>
52 
54 public:
57 
59  ~SiStripLorentzAngleCalibration() override = default;
60 
62  unsigned int numParameters() const override;
63 
66  unsigned int derivatives(std::vector<ValuesIndexPair> &outDerivInds,
68  const TrajectoryStateOnSurface &tsos,
69  const edm::EventSetup &setup,
70  const EventInfo &eventInfo) const override;
71 
74  bool setParameter(unsigned int index, double value) override;
75 
78  bool setParameterError(unsigned int index, double error) override;
79 
82  double getParameter(unsigned int index) const override;
83 
86  double getParameterError(unsigned int index) const override;
87 
88  // /// Call at beginning of job:
90 
92  void beginRun(const edm::Run &, const edm::EventSetup &) override;
93 
96  void endOfJob() override;
97 
98 private:
104  double effectiveThickness(const GeomDet *det, int16_t mode, const edm::EventSetup &setup) const;
105 
108  double getParameterForDetId(unsigned int detId, edm::RunNumber_t run) const;
109 
110  void writeTree(const SiStripLorentzAngle *lorentzAngle,
111  const std::map<unsigned int, TreeStruct> &treeInfo,
112  const char *treeName) const;
113  SiStripLorentzAngle createFromTree(const char *fileName, const char *treeName) const;
114 
116  int16_t readoutMode_;
117  const bool saveToDB_;
120  const std::vector<std::string> mergeFileNames_;
121 
123 
124  std::map<align::RunNumber, SiStripLorentzAngle> cachedLorentzAngleInputs_;
127 
128  std::vector<double> parameters_;
129  std::vector<double> paramUncertainties_;
130 
131  std::unique_ptr<TkModuleGroupSelector> moduleGroupSelector_;
133 };
134 
135 //======================================================================
136 //======================================================================
137 //======================================================================
138 
141  readoutModeName_(cfg.getParameter<std::string>("readoutMode")),
142  saveToDB_(cfg.getParameter<bool>("saveToDB")),
143  recordNameDBwrite_(cfg.getParameter<std::string>("recordNameDBwrite")),
144  outFileName_(cfg.getParameter<std::string>("treeFile")),
145  mergeFileNames_(cfg.getParameter<std::vector<std::string> >("mergeTreeFiles")),
146  moduleGroupSelCfg_(cfg.getParameter<edm::ParameterSet>("LorentzAngleModuleGroups")) {
147  // SiStripLatency::singleReadOutMode() returns
148  // 1: all in peak, 0: all in deco, -1: mixed state
149  // (in principle one could treat even mixed state APV by APV...)
150  if (readoutModeName_ == "peak") {
152  } else if (readoutModeName_ == "deconvolution") {
154  } else {
155  throw cms::Exception("BadConfig") << "SiStripLorentzAngleCalibration:\n"
156  << "Unknown mode '" << readoutModeName_
157  << "', should be 'peak' or 'deconvolution' .\n";
158  }
159 }
160 
161 //======================================================================
162 unsigned int SiStripLorentzAngleCalibration::numParameters() const { return parameters_.size(); }
163 
164 //======================================================================
166  // no action needed if the LA record didn't change
167  if (!(watchLorentzAngleRcd_.check(setup)))
168  return;
169 
170  const auto runNumber = run.run();
172 
173  // avoid infinite loop due to wrap-around of unsigned variable 'i' including
174  // arrow from i to zero and a nice smiley ;)
175  for (unsigned int i = moduleGroupSelector_->numIovs(); i-- > 0;) {
176  const auto firstRunOfIOV = moduleGroupSelector_->firstRunOfIOV(i);
177  if (runNumber >= firstRunOfIOV) {
178  firstRun = firstRunOfIOV;
179  break;
180  }
181  }
182 
183  edm::ESHandle<SiStripLorentzAngle> lorentzAngleHandle;
184  const auto &lorentzAngleRcd = setup.get<SiStripLorentzAngleRcd>();
185  lorentzAngleRcd.get(readoutModeName_, lorentzAngleHandle);
187  cachedLorentzAngleInputs_.emplace(firstRun, SiStripLorentzAngle(*lorentzAngleHandle));
188  } else {
189  if (lorentzAngleRcd.validityInterval().first().eventID().run() > firstRun &&
190  lorentzAngleHandle->getLorentzAngles() // only bad if non-identical values
191  != cachedLorentzAngleInputs_[firstRun].getLorentzAngles()) { // (comparing maps)
192  // Maps are containers sorted by key, but comparison problems may arise from
193  // 'floating point comparison' problems (FIXME?)
194  throw cms::Exception("BadInput") << "Trying to cache SiStripLorentzAngle payload for a run (" << runNumber
195  << ") in an IOV (" << firstRun << ") that was already cached.\n"
196  << "The following record in your input database tag has an IOV "
197  << "boundary that does not match your IOV definition:\n"
198  << " - SiStripLorentzAngleRcd '" << lorentzAngleRcd.key().name() << "' (since "
199  << lorentzAngleRcd.validityInterval().first().eventID().run() << ")\n";
200  }
201  }
202 
205 }
206 
207 //======================================================================
208 unsigned int SiStripLorentzAngleCalibration::derivatives(std::vector<ValuesIndexPair> &outDerivInds,
210  const TrajectoryStateOnSurface &tsos,
211  const edm::EventSetup &setup,
212  const EventInfo &eventInfo) const {
213  outDerivInds.clear();
214 
216  setup.get<SiStripLatencyRcd>().get(latency);
217  const int16_t mode = latency->singleReadOutMode();
218  if (mode == readoutMode_) {
219  if (hit.det()) { // otherwise 'constraint hit' or whatever
220 
221  const int index =
222  moduleGroupSelector_->getParameterIndexFromDetId(hit.det()->geographicalId(), eventInfo.eventId().run());
223  if (index >= 0) { // otherwise not treated
225  setup.get<IdealMagneticFieldRecord>().get(magneticField);
226  const GlobalVector bField(magneticField->inTesla(hit.det()->surface().position()));
227  const LocalVector bFieldLocal(hit.det()->surface().toLocal(bField));
228  const double dZ = this->effectiveThickness(hit.det(), mode, setup);
229  // shift due to LA: dx = tan(LA) * dz/2 = mobility * B_y * dz/2,
230  // '-' since we have derivative of the residual r = hit - trk and mu is part of trk model
231  // (see GF's presentation in alignment meeting 25.10.2012,
232  // https://indico.cern.ch/conferenceDisplay.py?confId=174266#2012-10-25)
233  // Hmm! StripCPE::fillParams() defines, together with
234  // StripCPE::driftDirection(...):
235  // drift.x = -mobility * by * thickness (full drift from backside)
236  // So '-' already comes from that, not from mobility being part of
237  // track model...
238  // GM: sign convention is the same as for pixel LA, i.e. adopt it here, too
239  const double xDerivative = bFieldLocal.y() * dZ * -0.5; // parameter is mobility!
240  const double yDerivative = bFieldLocal.x() * dZ * 0.5; // parameter is mobility!
241  if (xDerivative || yDerivative) { // If field is zero, this is zero: do not return it
242  const Values derivs{xDerivative, yDerivative};
243  outDerivInds.push_back(ValuesIndexPair(derivs, index));
244  }
245  }
246  } else {
247  edm::LogWarning("Alignment") << "@SUB=SiStripLorentzAngleCalibration::derivatives1"
248  << "Hit without GeomDet, skip!";
249  }
250  } else if (mode != kDeconvolutionMode && mode != kPeakMode) {
251  // warn only if unknown/mixed mode
252  edm::LogWarning("Alignment") << "@SUB=SiStripLorentzAngleCalibration::derivatives2"
253  << "Readout mode is " << mode << ", but looking for " << readoutMode_ << " ("
254  << readoutModeName_ << ").";
255  }
256 
257  return outDerivInds.size();
258 }
259 
260 //======================================================================
262  if (index >= parameters_.size()) {
263  return false;
264  } else {
266  return true;
267  }
268 }
269 
270 //======================================================================
272  if (index >= paramUncertainties_.size()) {
273  return false;
274  } else {
276  return true;
277  }
278 }
279 
280 //======================================================================
282  return (index >= parameters_.size() ? 0. : parameters_[index]);
283 }
284 
285 //======================================================================
287  return (index >= paramUncertainties_.size() ? 0. : paramUncertainties_[index]);
288 }
289 
290 //======================================================================
292  AlignableMuon * /*aliMuon*/,
293  AlignableExtras * /*aliExtras*/) {
294  //specify the sub-detectors for which the LA is determined
295  const std::vector<int> sdets = {SiStripDetId::TIB, SiStripDetId::TOB, SiStripDetId::TID, SiStripDetId::TEC};
296  moduleGroupSelector_ = std::make_unique<TkModuleGroupSelector>(aliTracker, moduleGroupSelCfg_, sdets);
297 
298  parameters_.resize(moduleGroupSelector_->getNumberOfParameters(), 0.);
299  paramUncertainties_.resize(moduleGroupSelector_->getNumberOfParameters(), 0.);
300 
301  edm::LogInfo("Alignment") << "@SUB=SiStripLorentzAngleCalibration"
302  << "Created with name " << this->name() << " for readout mode '" << readoutModeName_
303  << "',\n"
304  << this->numParameters() << " parameters to be determined."
305  << "\nsaveToDB = " << saveToDB_ << "\n outFileName = " << outFileName_
306  << "\n N(merge files) = " << mergeFileNames_.size()
307  << "\n number of IOVs = " << moduleGroupSelector_->numIovs();
308 
309  if (!mergeFileNames_.empty()) {
310  edm::LogInfo("Alignment") << "@SUB=SiStripLorentzAngleCalibration"
311  << "First file to merge: " << mergeFileNames_[0];
312  }
313 }
314 
315 //======================================================================
317  // loginfo output
318  std::ostringstream out;
319  out << "Parameter results for readout mode '" << readoutModeName_ << "'\n";
320  for (unsigned int iPar = 0; iPar < parameters_.size(); ++iPar) {
321  out << iPar << ": " << parameters_[iPar] << " +- " << paramUncertainties_[iPar] << "\n";
322  }
323  edm::LogInfo("Alignment") << "@SUB=SiStripLorentzAngleCalibration::endOfJob" << out.str();
324 
325  std::map<unsigned int, TreeStruct> treeInfo; // map of TreeStruct for each detId
326 
327  // now write 'input' tree
328  const std::string treeName{this->name() + '_' + readoutModeName_ + '_'};
329  std::vector<const SiStripLorentzAngle *> inputs{};
330  inputs.reserve(moduleGroupSelector_->numIovs());
331  for (unsigned int iIOV = 0; iIOV < moduleGroupSelector_->numIovs(); ++iIOV) {
332  const auto firstRunOfIOV = moduleGroupSelector_->firstRunOfIOV(iIOV);
333  inputs.push_back(this->getLorentzAnglesInput(firstRunOfIOV)); // never NULL
334  this->writeTree(inputs.back(),
335  treeInfo,
336  (treeName + "input_" + std::to_string(firstRunOfIOV)).c_str()); // empty treeInfo for input...
337 
338  if (inputs.back()->getLorentzAngles().empty()) {
339  edm::LogError("Alignment") << "@SUB=SiStripLorentzAngleCalibration::endOfJob"
340  << "Input Lorentz angle map is empty ('" << readoutModeName_
341  << "' mode), skip writing output!";
342  return;
343  }
344  }
345 
346  const unsigned int nonZeroParamsOrErrors = // Any determined value?
347  count_if(parameters_.begin(), parameters_.end(), [](auto c) { return c != 0.; }) +
348  count_if(paramUncertainties_.begin(), paramUncertainties_.end(), [](auto c) { return c != 0.; });
349 
350  for (unsigned int iIOV = 0; iIOV < moduleGroupSelector_->numIovs(); ++iIOV) {
351  auto firstRunOfIOV = static_cast<cond::Time_t>(moduleGroupSelector_->firstRunOfIOV(iIOV));
353  // Loop on map of values from input and add (possible) parameter results
354  for (const auto &iterIdValue : inputs[iIOV]->getLorentzAngles()) {
355  // type of 'iterIdValue' is pair<unsigned int, float>
356  const auto detId = iterIdValue.first; // key of map is DetId
357  // Some code one could use to miscalibrate wrt input:
358  // double param = 0.;
359  // const DetId id(detId);
360  // if (id.subdetId() == 3) { // TIB
361  // param = (readoutMode_ == kPeakMode ? -0.003 : -0.002);
362  // } else if (id.subdetId() == 5) { // TOB
363  // param = (readoutMode_ == kPeakMode ? 0.005 : 0.004);
364  // }
365  const double param = this->getParameterForDetId(detId, firstRunOfIOV);
366  // put result in output, i.e. sum of input and determined parameter:
367  auto value = iterIdValue.second + static_cast<float>(param);
368  output.putLorentzAngle(detId, value);
369  const int paramIndex = moduleGroupSelector_->getParameterIndexFromDetId(detId, firstRunOfIOV);
370  treeInfo[detId] = TreeStruct(param, this->getParameterError(paramIndex), paramIndex);
371  }
372 
373  if (saveToDB_ || nonZeroParamsOrErrors != 0) { // Skip writing mille jobs...
374  this->writeTree(&output, treeInfo, (treeName + Form("result_%lld", firstRunOfIOV)).c_str());
375  }
376 
377  if (saveToDB_) { // If requested, write out to DB
379  if (dbService.isAvailable()) {
380  dbService->writeOne(&output, firstRunOfIOV, recordNameDBwrite_);
381  } else {
382  edm::LogError("BadConfig") << "@SUB=SiStripLorentzAngleCalibration::endOfJob"
383  << "No PoolDBOutputService available, but saveToDB true!";
384  }
385  }
386  } // end loop on IOVs
387 }
388 
389 //======================================================================
391  int16_t mode,
392  const edm::EventSetup &setup) const {
393  if (!det)
394  return 0.;
395  double dZ = det->surface().bounds().thickness(); // it is a float only...
396  const SiStripDetId id(det->geographicalId());
398  // FIXME: which one? DepRcd->get(handle) or Rcd->get(readoutModeName_, handle)??
399  // setup.get<SiStripBackPlaneCorrectionDepRcd>().get(backPlaneHandle); // get correct mode
400  setup.get<SiStripBackPlaneCorrectionRcd>().get(readoutModeName_, backPlaneHandle);
401  const double bpCor = backPlaneHandle->getBackPlaneCorrection(id); // it's a float...
402  // std::cout << "bpCor " << bpCor << " in subdet " << id.subdetId() << std::endl;
403  dZ *= (1. - bpCor);
404 
405  return dZ;
406 }
407 
408 //======================================================================
410  const auto &resolvedRun = run > 0 ? run : currentIOV_;
411  // For parallel processing in Millepede II, create SiStripLorentzAngle
412  // from info stored in files of parallel jobs and check that they are identical.
413  // If this job has run on events, still check that LA is identical to the ones
414  // from mergeFileNames_.
415  const std::string treeName{this->name() + "_" + readoutModeName_ + "_input_" + std::to_string(resolvedRun)};
416  for (const auto &iFile : mergeFileNames_) {
417  auto la = this->createFromTree(iFile.c_str(), treeName.c_str());
418  // siStripLorentzAngleInput_ could be non-null from previous file of this loop
419  // or from checkLorentzAngleInput(..) when running on data in this job as well
421  cachedLorentzAngleInputs_[resolvedRun] = la;
423  currentIOV_ = resolvedRun;
424  } else {
425  // FIXME: about comparison of maps see comments in checkLorentzAngleInput
426  if (!la.getLorentzAngles().empty() && // single job might not have got events
427  la.getLorentzAngles() != siStripLorentzAngleInput_->getLorentzAngles()) {
428  // Throw exception instead of error?
429  edm::LogError("NoInput") << "@SUB=SiStripLorentzAngleCalibration::getLorentzAnglesInput"
430  << "Different input values from tree " << treeName << " in file " << iFile << ".";
431  }
432  }
433  }
434 
435  if (!siStripLorentzAngleInput_) { // no files nor ran on events
436  // [] operator default-constructs an empty SiStripLorentzAngle object in place:
438  currentIOV_ = resolvedRun;
439  edm::LogError("NoInput") << "@SUB=SiStripLorentzAngleCalibration::getLorentzAnglesInput"
440  << "No input, create an empty one ('" << readoutModeName_ << "' mode)!";
441  } else if (siStripLorentzAngleInput_->getLorentzAngles().empty()) {
442  edm::LogError("NoInput") << "@SUB=SiStripLorentzAngleCalibration::getLorentzAnglesInput"
443  << "Empty result ('" << readoutModeName_ << "' mode)!";
444  }
445 
447 }
448 
449 //======================================================================
451  const int index = moduleGroupSelector_->getParameterIndexFromDetId(detId, run);
452 
453  return (index < 0 ? 0. : parameters_[index]);
454 }
455 
456 //======================================================================
458  const std::map<unsigned int, TreeStruct> &treeInfo,
459  const char *treeName) const {
460  if (!lorentzAngle)
461  return;
462 
463  TFile *file = TFile::Open(outFileName_.c_str(), "UPDATE");
464  if (!file) {
465  edm::LogError("BadConfig") << "@SUB=SiStripLorentzAngleCalibration::writeTree"
466  << "Could not open file '" << outFileName_ << "'.";
467  return;
468  }
469 
470  TTree *tree = new TTree(treeName, treeName);
471  unsigned int id = 0;
472  float value = 0.;
473  TreeStruct treeStruct;
474  tree->Branch("detId", &id, "detId/i");
475  tree->Branch("value", &value, "value/F");
476  tree->Branch("treeStruct", &treeStruct, TreeStruct::LeafList());
477 
478  for (auto iterIdValue = lorentzAngle->getLorentzAngles().begin();
479  iterIdValue != lorentzAngle->getLorentzAngles().end();
480  ++iterIdValue) {
481  // type of (*iterIdValue) is pair<unsigned int, float>
482  id = iterIdValue->first; // key of map is DetId
483  value = iterIdValue->second;
484  // type of (*treeStructIter) is pair<unsigned int, TreeStruct>
485  auto treeStructIter = treeInfo.find(id); // find info for this id
486  if (treeStructIter != treeInfo.end()) {
487  treeStruct = treeStructIter->second; // info from input map
488  } else { // if none found, fill at least parameter index (using 1st IOV...)
489  const cond::Time_t run1of1stIov = moduleGroupSelector_->firstRunOfIOV(0);
490  const int ind = moduleGroupSelector_->getParameterIndexFromDetId(id, run1of1stIov);
491  treeStruct = TreeStruct(ind);
492  }
493  tree->Fill();
494  }
495  tree->Write();
496  delete file; // tree vanishes with the file...
497 }
498 
499 //======================================================================
501  // Check for file existence on your own to work around
502  // https://hypernews.cern.ch/HyperNews/CMS/get/swDevelopment/2715.html:
503  TFile *file = nullptr;
504  FILE *testFile = fopen(fileName, "r");
505  if (testFile) {
506  fclose(testFile);
507  file = TFile::Open(fileName, "READ");
508  } // else not existing, see error below
509 
510  TTree *tree = nullptr;
511  if (file)
512  file->GetObject(treeName, tree);
513 
515  if (tree) {
516  unsigned int id = 0;
517  float value = 0.;
518  tree->SetBranchAddress("detId", &id);
519  tree->SetBranchAddress("value", &value);
520 
521  const Long64_t nEntries = tree->GetEntries();
522  for (Long64_t iEntry = 0; iEntry < nEntries; ++iEntry) {
523  tree->GetEntry(iEntry);
524  result.putLorentzAngle(id, value);
525  }
526  } else { // Warning only since could be parallel job on no events.
527  edm::LogWarning("Alignment") << "@SUB=SiStripLorentzAngleCalibration::createFromTree"
528  << "Could not get TTree '" << treeName << "' from file '" << fileName
529  << (file ? "'." : "' (file does not exist).");
530  }
531 
532  delete file; // tree will vanish with file
533  return result;
534 }
535 
536 //======================================================================
537 //======================================================================
538 // Plugin definition
539 
541 
RunNumber_t run() const
Definition: EventID.h:39
const TimeTypeSpecs timeTypeSpecs[]
Definition: Time.cc:22
RunNumber_t run() const
Definition: RunBase.h:40
Time_t beginValue
Definition: Time.h:45
int16_t singleReadOutMode() const
const std::vector< std::string > mergeFileNames_
SiStripLorentzAngleCalibration(const edm::ParameterSet &cfg)
Constructor.
const std::string & name() const
name of this calibration
def setup(process, global_tag, zero_tesla=False)
Definition: GeneralSetup.py:2
const Bounds & bounds() const
Definition: Surface.h:120
std::map< align::RunNumber, SiStripLorentzAngle > cachedLorentzAngleInputs_
void writeTree(const SiStripLorentzAngle *lorentzAngle, const std::map< unsigned int, TreeStruct > &treeInfo, const char *treeName) const
const edm::EventID eventId() const
const Plane & surface() const
The nominal surface of the GeomDet.
Definition: GeomDet.h:42
bool setParameter(unsigned int index, double value) override
double getParameterForDetId(unsigned int detId, edm::RunNumber_t run) const
define event information passed to algorithms
edm::ESWatcher< SiStripLorentzAngleRcd > watchLorentzAngleRcd_
structure to store algorithm results in a TTree
Definition: TreeStruct.h:6
const std::map< unsigned int, float > & getLorentzAngles() const
unsigned long long Time_t
Definition: Time.h:16
ProductT const & get(ESGetToken< ProductT, DepRecordT > const &iToken) const
~SiStripLorentzAngleCalibration() override=default
Destructor.
const GeomDet * det() const
LocalPoint toLocal(const GlobalPoint &gp) const
std::pair< Values, unsigned int > ValuesIndexPair
x- and y-values
bool isAvailable() const
Definition: Service.h:40
DetId geographicalId() const
The label of this GeomDet.
Definition: GeomDet.h:79
void writeOne(T *payload, Time_t time, const std::string &recordName, bool withlogging=false)
Definition: value.py:1
SiStripLorentzAngle createFromTree(const char *fileName, const char *treeName) const
virtual GlobalVector inTesla(const GlobalPoint &gp) const =0
Field value ad specified global point, in Tesla.
std::pair< double, double > Values
double getParameterError(unsigned int index) const override
Detector identifier class for the strip tracker.
Definition: SiStripDetId.h:17
double getParameter(unsigned int index) const override
virtual float thickness() const =0
std::unique_ptr< TkModuleGroupSelector > moduleGroupSelector_
static const char * LeafList()
Definition: TreeStruct.h:16
double effectiveThickness(const GeomDet *det, int16_t mode, const edm::EventSetup &setup) const
in non-peak mode the effective thickness is reduced...
void beginRun(const edm::Run &, const edm::EventSetup &) override
Call at beginning of run:
bool check(const edm::EventSetup &iSetup)
Definition: ESWatcher.h:52
const SiStripLorentzAngle * getLorentzAnglesInput(const align::RunNumber &=0)
HLT enums.
unsigned int numParameters() const override
How many parameters does this calibration define?
T get() const
Definition: EventSetup.h:71
bool setParameterError(unsigned int index, double error) override
void beginOfJob(AlignableTracker *tracker, AlignableMuon *muon, AlignableExtras *extras) override
firstRun
Definition: dataset.py:940
#define DEFINE_EDM_PLUGIN(factory, type, name)
unsigned int RunNumber_t
eventInfo
add run, event number and lumi section
Definition: tree.py:1
unsigned int derivatives(std::vector< ValuesIndexPair > &outDerivInds, const TransientTrackingRecHit &hit, const TrajectoryStateOnSurface &tsos, const edm::EventSetup &setup, const EventInfo &eventInfo) const override
Constructor of the full muon geometry.
Definition: AlignableMuon.h:37
latency
hardware algo
const PositionType & position() const
Definition: Run.h:45
cond::RealTimeType< cond::runnumber >::type RunNumber
Definition: Utilities.h:38