Belle II Software  release-08-01-10
MillepedeAlgorithm.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 #include <alignment/calibration/MillepedeAlgorithm.h>
10 
11 #include <alignment/dataobjects/MilleData.h>
12 #include <alignment/dataobjects/PedeSteering.h>
13 #include <alignment/GlobalLabel.h>
14 #include <alignment/GlobalParam.h>
15 #include <alignment/GlobalTimeLine.h>
16 #include <alignment/Manager.h>
17 #include <alignment/PedeApplication.h>
18 #include <alignment/PedeResult.h>
19 #include <framework/database/EventDependency.h>
20 #include <framework/utilities/FileSystem.h>
21 
22 #include <TH1F.h>
23 #include <TTree.h>
24 
25 
26 
27 using namespace std;
28 using namespace Belle2;
29 using namespace alignment;
30 
31 MillepedeAlgorithm::MillepedeAlgorithm() : CalibrationAlgorithm("MillepedeCollector")
32 {
33  setDescription("Millepede calibration & alignment algorithm");
34 }
35 
37 {
38  auto chisqHist = getObjectPtr<TH1F>("chi2_per_ndf");
39  B2INFO(" Mean of Chi2 / NDF of tracks before calibration: " << chisqHist->GetMean());
40 
41  if (chisqHist->GetEntries() < m_minEntries) {
42  B2INFO("Less than " << m_minEntries << " collected: " << chisqHist->GetEntries() << ". Return c_NotEnoughData.");
43  return c_NotEnoughData;
44  }
45 
46  // Write out binary files from tree and add to steering
48  auto mille = getObjectPtr<MilleData>("mille");
49  for (auto file : mille->getFiles()) {
50  if (!FileSystem::fileExists(file)) {
51  B2ERROR("Missing file: " << file);
52  continue;
53  }
54  m_steering.addFile(file);
55  }
56 
57  // Run calibration on steering
59 
60  if (!m_pede.success() || !m_result.isValid()) {
61  B2ERROR(m_pede.getExitMessage());
62  return c_Failure;
63  }
64 
65  if (m_result.getNoParameters() == 0) {
66  B2INFO("No parameters to store. Failure.");
67  return c_Failure;
68  }
69 
70 // for (int ipar = 0; ipar < m_result.getNoParameters(); ipar++) {
71 // if (!m_result.isParameterDetermined(ipar)) {
72 // if (!m_result.isParameterFixed(ipar)) {
73 // ++undeterminedParams;
74 // }
75 // }
76 // }
77 
78 
79 
80  // This function gives you the vector of ExpRuns that were requested for this execution only
81  auto expRuns = getRunList();
82 
83  // Do not use full data range - would not work with SequentialRunByRun strategy:
84  //auto expRuns = getRunListFromAllData();
85 
86  int undeterminedParams = 0;
87  double maxCorrectionPull = 0.;
88  int maxCorrectionPullLabel = 0;
89  int nParams = 0;
90  double paramChi2 = 0.;
91 
92  if (m_events.empty()) {
93  GlobalParamVector result(m_components);
94  GlobalParamVector errors(m_components);
95  GlobalParamVector corrections(m_components);
96 
97  GlobalCalibrationManager::initGlobalVector(result);
98  GlobalCalibrationManager::initGlobalVector(errors);
99  GlobalCalibrationManager::initGlobalVector(corrections);
100 
101  // <UniqueID, ElementID, ParameterId, value>
102  std::vector<std::tuple<unsigned short, unsigned short, unsigned short, double>> resultTuple;
103 
104  for (auto& exprun : expRuns) {
105  auto event1 = EventMetaData(1, exprun.second, exprun.first);
106  result.loadFromDB(event1);
107  errors.construct();
108  corrections.construct();
109  break;
110  }
111 
112  // Construct all remaining components not loaded from DB
113  // to easily create new objects not previously in DB :-)
114  // TODO: remove?
115  result.construct();
116 
117 
118 
119  // Loop over all determined parameters:
120  for (int ipar = 0; ipar < m_result.getNoParameters(); ipar++) {
121  if (!m_result.isParameterDetermined(ipar)) {
122  if (!m_result.isParameterFixed(ipar)) {
123  ++undeterminedParams;
124  }
125  continue;
126  }
127 
129  double correction = m_result.getParameterCorrection(ipar);
130  double error = m_result.getParameterError(ipar);
131  double pull = correction / error;
132 
133  ++nParams;
134  paramChi2 += pull * pull;
135 
136  if (fabs(pull) > fabs(maxCorrectionPull)) {
137  maxCorrectionPull = pull;
138  maxCorrectionPullLabel = label.label();
139  }
140 
141  if (m_invertSign) correction = - correction;
142 
143  result.updateGlobalParam(correction, label.getUniqueId(), label.getElementId(), label.getParameterId());
144  errors.setGlobalParam(error, label.getUniqueId(), label.getElementId(), label.getParameterId());
145  corrections.setGlobalParam(correction, label.getUniqueId(), label.getElementId(), label.getParameterId());
146 
147  resultTuple.push_back({label.getUniqueId(), label.getElementId(), label.getParameterId(), correction});
148 
149  }
150 
151  result.postReadFromResult(resultTuple);
152 
153  for (auto object : result.releaseObjects()) {
154  saveCalibration(object);
155  }
156  for (auto object : errors.releaseObjects()) {
157  saveCalibration(object, DataStore::objectName(object->IsA(), "") + "_ERRORS");
158  }
159  for (auto object : corrections.releaseObjects()) {
160  saveCalibration(object, DataStore::objectName(object->IsA(), "") + "_CORRECTIONS");
161  }
162  } else {
163 
164  GlobalParamVector gpv(m_components);
165  GlobalCalibrationManager::initGlobalVector(gpv);
166  GlobalLabel label_system;
167 
168  alignment::timeline::GlobalParamTimeLine timeline(m_events, label_system, gpv);
169  alignment::timeline::GlobalParamTimeLine timeline_errors(m_events, label_system, gpv);
170  alignment::timeline::GlobalParamTimeLine timeline_corrections(m_events, label_system, gpv);
171  timeline.loadFromDB();
172  timeline_corrections.loadFromDB();
173  timeline_errors.loadFromDB();
174 
175  // Loop over all determined parameters:
176  for (int ipar = 0; ipar < m_result.getNoParameters(); ipar++) {
177  if (!m_result.isParameterDetermined(ipar)) {
178  if (!m_result.isParameterFixed(ipar)) {
179  ++undeterminedParams;
180  }
181  continue;
182  }
183 
185  double correction = m_result.getParameterCorrection(ipar);
186  double error = m_result.getParameterError(ipar);
187  double pull = correction / error;
188 
189  ++nParams;
190  paramChi2 += pull * pull;
191 
192  if (fabs(pull) > fabs(maxCorrectionPull)) {
193  maxCorrectionPull = pull;
194  maxCorrectionPullLabel = label.label();
195  }
196 
197  if (m_invertSign) correction = - correction;
198 
199  timeline.updateGlobalParam(label, correction);
200  timeline_errors.updateGlobalParam(label, error, true);
201  timeline_corrections.updateGlobalParam(label, correction, true);
202  }
203 
204  //TODO: if data do not cover all parts of timeline, this will lead to memory leak for
205  // objects not being saved in DB -> delete them here
206  auto objects = timeline.releaseObjects();
207  for (auto iov_obj : objects) {
208  if (iov_obj.second && !iov_obj.first.overlap(getIovFromAllData()).empty()) {
209  if (auto evdep = dynamic_cast<EventDependency*>(iov_obj.second)) {
210  saveCalibration(iov_obj.second, DataStore::objectName(evdep->getAnyObject()->IsA(), ""),
211  iov_obj.first.overlap(getIovFromAllData()));
212  } else {
213  saveCalibration(iov_obj.second, DataStore::objectName(iov_obj.second->IsA(), ""), iov_obj.first.overlap(getIovFromAllData()));
214  }
215  } else {
216  if (iov_obj.second) delete iov_obj.second;
217  }
218  }
219 
220  auto objects_errors = timeline_errors.releaseObjects();
221  for (auto iov_obj : objects_errors) {
222  if (iov_obj.second && !iov_obj.first.overlap(getIovFromAllData()).empty()) {
223  if (auto evdep = dynamic_cast<EventDependency*>(iov_obj.second)) {
224  saveCalibration(iov_obj.second, DataStore::objectName(evdep->getAnyObject()->IsA(), "") + "_ERRORS",
225  iov_obj.first.overlap(getIovFromAllData()));
226  } else {
227  saveCalibration(iov_obj.second, DataStore::objectName(iov_obj.second->IsA(), "") + "_ERRORS",
228  iov_obj.first.overlap(getIovFromAllData()));
229  }
230  } else {
231  if (iov_obj.second) delete iov_obj.second;
232  }
233  }
234 
235  auto objects_corrections = timeline_corrections.releaseObjects();
236  for (auto iov_obj : objects_corrections) {
237  if (iov_obj.second && !iov_obj.first.overlap(getIovFromAllData()).empty()) {
238  if (auto evdep = dynamic_cast<EventDependency*>(iov_obj.second)) {
239  saveCalibration(iov_obj.second, DataStore::objectName(evdep->getAnyObject()->IsA(), "") + "_CORRECTIONS",
240  iov_obj.first.overlap(getIovFromAllData()));
241  } else {
242  saveCalibration(iov_obj.second, DataStore::objectName(iov_obj.second->IsA(), "") + "_CORRECTIONS",
243  iov_obj.first.overlap(getIovFromAllData()));
244  }
245  } else {
246  if (iov_obj.second) delete iov_obj.second;
247  }
248  }
249 
250  }
251 
252  if (undeterminedParams) {
253  B2WARNING("There are " << undeterminedParams << " undetermined parameters.");
255  B2WARNING("Not enough data for calibration.");
256  return c_NotEnoughData;
257  }
258  }
259 
260  //commit();
261 
262  if (paramChi2 / nParams > 1. || fabs(maxCorrectionPull) > 10.) {
263  B2INFO("Largest correction/error is " << maxCorrectionPull << " for parameter with label " << maxCorrectionPullLabel);
264  B2INFO("Parameter corrections Chi2/NDF, e.g. sum[(correction/error)^2]/#params = " << paramChi2 / nParams
265  << " = " << paramChi2 << " / " << nParams << " > 1.");
266 
267  B2INFO("Requesting iteration.");
268  return c_Iterate;
269  }
270 
271  return c_OK;
272 }
273 
275 {
276  // Clear list of binaries in steering
278 
279  // Temp file name
280  const std::string milleFileName("gbl-data.mille");
281 
282  // For no entries, no binary file is created
283  auto gblDataTree = getObjectPtr<TTree>("GblDataTree");
284  if (!gblDataTree) {
285  B2WARNING("No GBL data tree object in collected data.");
286  return;
287  } else if (!gblDataTree->GetEntries()) {
288  B2WARNING("No trajectories in GBL data tree.");
289  return;
290  }
291 
292  // Create new mille binary
293  auto milleBinary = new gbl::MilleBinary(milleFileName);
294 
295  // Containers for GBL data
296  double aValue(0.);
297  double aErr(0.);
298  std::vector<unsigned int>* indLocal;
299  std::vector<double>* derLocal;
300  std::vector<int>* labGlobal;
301  std::vector<double>* derGlobal;
302 
303  // Read vectors of GblData from tree branch
304  std::vector<gbl::GblData>* currentGblData = new std::vector<gbl::GblData>();
305  gblDataTree->SetBranchAddress("GblData", &currentGblData);
306 
307  B2INFO("Writing Millepede binary files...");
308  for (unsigned int iRecord = 0; iRecord < gblDataTree->GetEntries(); ++iRecord) {
309  gblDataTree->GetEntry(iRecord);
310 
311  if (!currentGblData)
312  continue;
313 
314  for (gbl::GblData& theData : *currentGblData) {
315  theData.getAllData(aValue, aErr, indLocal, derLocal, labGlobal,
316  derGlobal);
317  milleBinary->addData(aValue, aErr, *indLocal, *derLocal, *labGlobal,
318  *derGlobal);
319  }
320  milleBinary->writeRecord();
321  }
322  // Closes the file
323  delete milleBinary;
324  B2INFO("Millepede binary files written.");
325 
326  // Add binary to steering
327  m_steering.addFile(milleFileName);
328 }
Base class for calibration algorithms.
IntervalOfValidity getIovFromAllData() const
Get the complete IoV from inspection of collected data.
void saveCalibration(TClonesArray *data, const std::string &name)
Store DBArray payload with given name with default IOV.
void setDescription(const std::string &description)
Set algorithm description (in constructor)
EResult
The result of calibration.
@ c_OK
Finished successfuly =0 in Python.
@ c_Iterate
Needs iteration =1 in Python.
@ c_NotEnoughData
Needs more data =2 in Python.
@ c_Failure
Failed =3 in Python.
const std::vector< Calibration::ExpRun > & getRunList() const
Get the list of runs for which calibration is called.
static std::string objectName(const TClass *t, const std::string &name)
Return the storage name for an object of the given TClass and name.
Definition: DataStore.cc:151
Class for handling changing conditions as a function of event number.
Store event, run, and experiment numbers.
Definition: EventMetaData.h:33
static bool fileExists(const std::string &filename)
Check if the file with given filename exists.
Definition: FileSystem.cc:32
Class to convert to/from global labels for Millepede II to/from detector & parameter identificators.
Definition: GlobalLabel.h:41
void prepareMilleBinary()
Write out binary files from data in tree with GBL data to be used by Millepede and add them to steeri...
int m_minEntries
Minimum entries collected - report NotEnoughData for less.
std::vector< std::string > m_components
Components (BeamSpot...) to calibrate or empty for all available in data.
std::vector< EventMetaData > m_events
The events at which payloads can change for time-dep calibration (translation from time IDs (aka cont...
bool m_invertSign
Add (true) or subtract (false) corrections?
alignment::PedeResult & result()
Get the result (invalid until executed) to get parameters etc.
alignment::PedeApplication m_pede
The Pede application (unsuccesfull until execution)
virtual EResult calibrate() override
Run algo on data.
alignment::PedeResult m_result
The result (invalid until execution)
bool m_ignoreUndeterminedParams
Report failure(false) or success (true) even if some parameters could not be determined.
PedeSteering m_steering
The steering with commands.
void clearFiles()
Clear list of files.
Definition: PedeSteering.h:53
void addFile(std::string filename, double weight=1.)
Add a file (optionally with weight) to list of binary files.
Definition: PedeSteering.cc:69
bool success()
Was Pede successfull (can the result be used)?
PedeResult calibrate(PedeSteering &steering)
Run Pede and return full result with parameter corrections.
std::string getExitMessage() const
Returns the Pede exit message (from millepede.end file)
double getParameterCorrection(unsigned int parameterIndex)
Get determined correction of parameter at index.
Definition: PedeResult.cc:136
bool isParameterDetermined(unsigned int parameterIndex)
Is parameter at given index determined?
Definition: PedeResult.cc:156
int getNoParameters() const
Get number of parameters in result (for looping over)
Definition: PedeResult.h:38
bool isParameterFixed(unsigned int parameterIndex)
Is parameter at given index fixed?
Definition: PedeResult.cc:151
unsigned int getParameterLabel(unsigned int parameterIndex)
Get label of parameter at index.
Definition: PedeResult.cc:131
double getParameterError(unsigned int parameterIndex)
Get correction error of parameter at index.
Definition: PedeResult.cc:141
bool isValid()
Was the object initialized properly from a result file?
Definition: PedeResult.h:34
Convenient class to automatically create payloads from allowed time depedence of parameter,...
std::vector< std::pair< IntervalOfValidity, TObject * > > releaseObjects()
Release all the objects (you become the owner!) for DB storage.
void loadFromDB()
Load every single payload with the content in database at its corresponding event (when it should sta...
void updateGlobalParam(GlobalLabel label, double correction, bool resetParam=false)
Add a correction to any payload's parameter in the timeline.
Data (block) for independent scalar measurement.
Definition: GblData.h:55
Millepede-II (binary) record.
Definition: MilleBinary.h:68
Abstract base class for different kinds of events.