Jpp  master_rocky-40-g5f0272dcd
the software that should make you happy
JFit.cc
Go to the documentation of this file.
1 #include <string>
2 #include <iostream>
3 #include <iomanip>
4 #include <vector>
5 #include <algorithm>
6 #include <chrono>
7 
8 #include "TROOT.h"
9 #include "TFile.h"
10 #include "TKey.h"
11 #include "TH1.h"
12 #include "TGraph.h"
13 #include "TProfile.h"
14 #include "TF1.h"
15 #include "TFitResult.h"
16 #include "TString.h"
17 #include "TRegexp.h"
18 
19 #include "JROOT/JMinimizer.hh"
20 
21 #include "JLang/JType.hh"
22 #include "JLang/JTypeList.hh"
23 #include "JLang/JToken.hh"
24 #include "JTools/JRange.hh"
25 
26 #include "JGizmo/JRootObjectID.hh"
27 #include "JGizmo/JGizmoToolkit.hh"
28 
29 #include "Jeep/JPrint.hh"
30 #include "Jeep/JParser.hh"
31 #include "Jeep/JMessage.hh"
32 
33 
34 namespace {
35 
36  using namespace JPP;
37 
38  /**
39  * Auxiliary class for ROOT fit.
40  */
41  struct JFit {
42  /**
43  * Constructor.
44  *
45  * \param object object
46  * \param fcn fcn
47  * \param option option
48  */
49  JFit(TObject& object,
50  TF1* fcn,
51  const std::string& option) :
52  object(object),
53  fcn (fcn),
54  option(option)
55  {}
56 
57 
58  /**
59  * Attempt fit for given data type.
60  *
61  * \param type data type
62  */
63  template<class T>
64  void operator()(const JType<T>& type)
65  {
66  try {
67  result = dynamic_cast<T&>(object).Fit(fcn, option.c_str(), "same");
68  }
69  catch(const std::exception&) {}
70  }
71 
72  TObject& object;
73  TF1* fcn;
74  std::string option;
75  TFitResultPtr result;
76  };
77 }
78 
79 
80 /**
81  * \file
82  * General purpose fit program for 1D ROOT objects.\n
83  * The option <tt>-f</tt> corresponds to <tt><file name>:<object name></tt>.
84  *
85  * The expressions for the fit function, start and fixed values should comply
86  * with ROOT class TFormula.
87  *
88  * In the expressions of the start and fixed values, names of member methods
89  * of corresponding class of the fit object may appear,
90  * such as TH1::GetMaximum, TH1::GetRMS, etc., e.g:
91  * <pre>
92  * -F "[0]*exp(x/[1])+[2]"
93  * -@ "p0 = GetMaximum; p1 = 2*GetRMS"
94  * -= "p2 = 0"
95  * </pre>
96  * The result of the formulas for the start and fixed values will be evaluated
97  * for each histogram separately.
98  * \author mdejong
99  */
100 int main(int argc, char **argv)
101 {
102  using namespace std;
103  using namespace JPP;
104 
105  typedef JToken<';'> JToken_t;
106  typedef JRange<double> JRange_t;
107 
108  vector<JRootObjectID> inputFile;
109  string outputFile;
110  string formula;
111  vector<JToken_t> startValues;
112  vector<JToken_t> startErrors;
113  vector<JToken_t> fixedValues;
114  vector<JToken_t> limitValues;
115  JRange_t X;
116  JRange_t Y;
117  char project;
118  string option;
119  int debug;
120 
121  try {
122 
123  JParser<> zap("General purpose fit program for 1D ROOT objects.");
124 
125  zap['f'] = make_field(inputFile, "<input file>:<object name>");
126  zap['o'] = make_field(outputFile, "ROOT file with fit results") = "fit.root";
127  zap['F'] = make_field(formula, "fit formula, e.g: \"[0]+[1]*x\"");
128  zap['@'] = make_field(startValues, "start values, e.g: \"p0 = GetMaximum;\"");
129  zap['E'] = make_field(startErrors, "start errors, e.g: \"p0 = 0.01 * GetMaximum;\"") = JPARSER::initialised();
130  zap['='] = make_field(fixedValues, "fixed values, e.g: \"p0 = GetMaximum;\"") = JPARSER::initialised();
131  zap['R'] = make_field(limitValues, "limit values, e.g: \"p0 = <lower limit> <upper limit>;\"") = JPARSER::initialised();
132  zap['x'] = make_field(X, "abscissa range") = JRange_t();
133  zap['y'] = make_field(Y, "ordinate range") = JRange_t();
134  zap['P'] = make_field(project, "projection") = '\0', 'x', 'X', 'y', 'Y';
135  zap['O'] = make_field(option, "Fit option") = "";
136  zap['M'] = make_field(minimizer, "ROOT minimizer type and algorithm [debug]") = JMinimizer();
137  zap['d'] = make_field(debug) = 1;
138 
139  zap(argc, argv);
140  }
141  catch(const exception &error) {
142  FATAL(error.what() << endl);
143  }
144 
145 
146  if (option.find('O') == string::npos) { option += "O"; }
147  if (option.find("R") == string::npos) { option += "R"; }
148  if (option.find("S") == string::npos) { option += "S"; }
149  //if (option.find('N') == string::npos) { option += "N"; }
150  if (debug == 0 && option.find('Q') == string::npos) { option += "Q"; }
151 
152  bool px = (project == 'x' || project == 'X'); // projection on x-axis
153  bool py = (project == 'y' || project == 'Y'); // projection on y-axis
154 
155  if (py) {
156  swap(Y, X); // Y becomes x-axis range and X becomes y-axis range
157  }
158 
159 
160  TFile out(outputFile.c_str(), "recreate");
161 
162 
163  TF1* fcn = new TF1("user", formula.c_str());
164 
165  fcn->SetNpx(1000);
166 
167  if (fcn->IsZombie()) {
168  FATAL("Function: " << formula << " is zombie." << endl);
169  }
170 
171  for (vector<JRootObjectID>::const_iterator input = inputFile.begin(); input != inputFile.end(); ++input) {
172 
173  DEBUG("Input: " << *input << endl);
174 
175  TDirectory* dir = getDirectory(*input);
176 
177  if (dir == NULL) {
178  ERROR("File: " << input->getFullFilename() << " not opened." << endl);
179  continue;
180  }
181 
182  const TRegexp regexp(input->getObjectName());
183 
184  TIter iter(dir->GetListOfKeys());
185 
186  for (TKey* key; (key = (TKey*) iter.Next()) != NULL; ) {
187 
188  const TString tag(key->GetName());
189 
190  DEBUG("Key: " << tag << " match = " << tag.Contains(regexp) << endl);
191 
192  // option match
193 
194  if (tag.Contains(regexp) && isTObject(key)) {
195 
196  TObject* object = key->ReadObj();
197 
198  try {
199 
200  TProfile& h1 = dynamic_cast<TProfile&>(*object);
201 
202  object = h1.ProjectionX();
203  }
204  catch(exception&) {}
205 
206  try {
207 
208  TH2& h2 = dynamic_cast<TH2&>(*object);
209 
210  if (px) {
211 
212  object = h2.ProjectionX("_px",
213  h2.GetYaxis()->FindBin(Y.getLowerLimit()),
214  h2.GetYaxis()->FindBin(Y.getUpperLimit()));
215 
216  } else if (py) {
217 
218  object = h2.ProjectionY("_py",
219  h2.GetXaxis()->FindBin(Y.getLowerLimit()),
220  h2.GetXaxis()->FindBin(Y.getUpperLimit()));
221 
222  } else {
223 
224  ERROR("For 2D histograms, use option option -P for projections." << endl);
225 
226  continue;
227  }
228  }
229  catch(exception&) {}
230 
231 
232  // set fit parameters
233 
234  try {
235 
236  for (vector<JToken_t>::const_iterator i = startValues.begin(); i != startValues.end(); ++i) {
237  fcn->SetParameter(getParameter(*i), getValue(*i,object));
238  }
239 
240  for (Int_t i = 0; i != fcn->GetNpar(); ++i) {
241  fcn->SetParError (i, 0.0);
242  }
243 
244  for (vector<JToken_t>::const_iterator i = startErrors.begin(); i != startErrors.end(); ++i) {
245  fcn->SetParError (getParameter(*i), getValue(*i,object));
246  }
247 
248  for (vector<JToken_t>::const_iterator i = fixedValues.begin(); i != fixedValues.end(); ++i) {
249  fcn->FixParameter(getParameter(*i), getValue(*i,object));
250  }
251 
252  for (vector<JToken_t>::const_iterator i = limitValues.begin(); i != limitValues.end(); ++i) {
253  fcn->SetParLimits(getParameter(*i), getValue(*i,0), getValue(*i,1));
254  }
255  }
256  catch(JLANG::JParseError& error) {
257  FATAL(error << endl);
258  }
259 
260  DEBUG("Start values " << object->GetName() << endl);
261 
262  for (int j = 0; j != fcn->GetNpar(); ++j) {
263  DEBUG(left << setw(12) << fcn->GetParName (j) << ' ' <<
264  SCIENTIFIC(12,5) << fcn->GetParameter(j) << endl);
265  }
266 
267  Double_t xmin = numeric_limits<Double_t>::max();
268  Double_t xmax = numeric_limits<Double_t>::lowest();
269 
270  {
271  TH1* h1 = dynamic_cast<TH1*>(object);
272 
273  if (h1 != NULL) {
274  xmin = min(xmin, h1->GetXaxis()->GetXmin());
275  xmax = max(xmax, h1->GetXaxis()->GetXmax());
276  }
277  }
278 
279  {
280  TGraph* g1 = dynamic_cast<TGraph*>(object);
281 
282  if (g1 != NULL) {
283  for (Int_t i = 0; i != g1->GetN(); ++i) {
284  xmin = min(xmin, g1->GetX()[i]);
285  xmax = max(xmax, g1->GetX()[i]);
286  }
287  }
288  }
289 
290  if (X != JRange_t()) {
291  xmin = X.getLowerLimit();
292  xmax = X.getUpperLimit();
293  }
294 
295  fcn->SetRange(xmin, xmax);
296 
297 
298  // execute fit
299 
300  const chrono::steady_clock::time_point t0 = chrono::steady_clock::now();
301 
302  JFit fit(*object, fcn, option);
303 
305 
306  const chrono::steady_clock::time_point t1 = chrono::steady_clock::now();
307 
308  if (fit.result != -1) {
309 
310  // output fit results
311 
312  NOTICE("Fit values " << object->GetName() << endl);
313  NOTICE("Fit formula " << formula << endl);
314  NOTICE("chi2/NDF " << FIXED(7,3) << fit.result->Chi2() << '/' << fit.result->Ndf() << ' ' << (fit.result->IsValid() ? "" : "failed") << endl);
315  NOTICE("Number of calls " << fit.result->NCalls() << endl);
316  NOTICE("Elapsed time [us] " << setw(8) << chrono::duration_cast<chrono::microseconds>(t1 - t0).count() << endl);
317 
318  for (int j = 0; j != fcn->GetNpar(); ++j) {
319  NOTICE(left << setw(12) << fcn->GetParName (j) << ' ' <<
320  SCIENTIFIC(12,5) << fcn->GetParameter(j) << " +/- " <<
321  SCIENTIFIC(12,5) << fcn->GetParError (j) << endl);
322  }
323 
324  } else {
325 
326  WARNING("Object: not compatible with ROOT Fit." << endl);
327  }
328 
329  out.cd();
330 
331  object->Write();
332  }
333  }
334 
335  dir->Close();
336  }
337 
338  out.Write();
339  out.Close();
340 }
string outputFile
int main(int argc, char **argv)
Definition: JFit.cc:100
General purpose messaging.
#define DEBUG(A)
Message macros.
Definition: JMessage.hh:62
#define ERROR(A)
Definition: JMessage.hh:66
#define NOTICE(A)
Definition: JMessage.hh:64
#define FATAL(A)
Definition: JMessage.hh:67
int debug
debug level
Definition: JSirene.cc:69
#define WARNING(A)
Definition: JMessage.hh:65
static JMinimizer minimizer
ROOT minimizer.
Definition: JMinimizer.hh:80
Utility class to parse command line options.
#define make_field(A,...)
macro to convert parameter to JParserTemplateElement object
Definition: JParser.hh:2142
I/O formatting auxiliaries.
Double_t g1(const Double_t x)
Function.
Definition: JQuantiles.cc:25
Auxiliary class to define a range between two values.
Exception for parsing value.
Definition: JException.hh:198
Wrapper class around string.
Definition: JToken.hh:26
Utility class to parse command line options.
Definition: JParser.hh:1698
T getLowerLimit() const
Get lower limit.
Definition: JRange.hh:202
T getUpperLimit() const
Get upper limit.
Definition: JRange.hh:213
const double xmax
Definition: JQuadrature.cc:24
const double xmin
Definition: JQuadrature.cc:23
double getValue(const JScale_t scale)
Get numerical value corresponding to scale.
Definition: JScale.hh:47
int getParameter(const std::string &text)
Get parameter number from text string.
TDirectory * getDirectory(const JRootObjectID &id)
Get TDirectory pointer.
bool isTObject(const TKey *key)
Check if given key corresponds to a TObject.
JObject_t & for_each(JObject_t &object, JType< JTypeList< JHead_t, JTail_t > > typelist)
For each data type method.
Definition: JTypeList.hh:415
This name space includes all other name spaces (except KM3NETDAQ, KM3NET and ANTARES).
JRootfit_t< JF1_t > Fit(const TH1 &h1, const JF1_t &f1, const index_list &ls=index_list(), const range_type &X=range_type())
Global fit fuction.
Definition: JRootfit.hh:1247
int j
Definition: JPolint.hh:792
Definition: JSTDTypes.hh:14
Auxiliary data structure for floating point format specification.
Definition: JManip.hh:448
Type definition of range.
Definition: JHead.hh:43
Acoustic single fit.
JFit()
Default constructor.
Type list.
Definition: JTypeList.hh:23
Auxiliary class for a type holder.
Definition: JType.hh:19
Auxiliary data structure to define ROOT minimizer.
Definition: JMinimizer.hh:14
Empty structure for specification of parser element that is initialised (i.e. does not require input)...
Definition: JParser.hh:68
Auxiliary data structure for floating point format specification.
Definition: JManip.hh:488
Definition: JRoot.hh:19