#include #include #include #include #include #include #include "TROOT.h" #include "TFile.h" #include "TKey.h" #include "TH3.h" #include "TProfile.h" #include "TF3.h" #include "TFitResult.h" #include "TString.h" #include "TRegexp.h" #include "JROOT/JMinimizer.hh" #include "JLang/JType.hh" #include "JLang/JTypeList.hh" #include "JLang/JToken.hh" #include "JTools/JRange.hh" #include "JGizmo/JRootObjectID.hh" #include "JGizmo/JGizmoToolkit.hh" #include "Jeep/JPrint.hh" #include "Jeep/JParser.hh" #include "Jeep/JMessage.hh" namespace { using namespace JPP; const char* const _F3 = ".f3"; //!< extension of name for fit function /** * Auxiliary class for ROOT fit. */ struct JFit { /** * Constructor. * * \param object object * \param fcn fcn * \param option option */ JFit(TObject& object, TF3* fcn, const std::string& option) : object(object), fcn (fcn), option(option) {} /** * Attempt fit for given data type. * * \param type data type */ template void operator()(const JType& type) { try { result = dynamic_cast(object).Fit(fcn, option.c_str(), "same"); } catch(const std::exception&) {} } TObject& object; TF3* fcn; std::string option; TFitResultPtr result; }; } /** * \file * General purpose fit program for 3D ROOT objects.\n * The option -f corresponds to \:\. * * The expressions for the fit function, start and fixed values should comply * with ROOT class TFormula. * * In the expressions of the start and fixed values, names of member methods * of corresponding class of the fit object may appear, * such as TH1::GetMaximum, TH1::GetRMS, etc., e.g: *
 *      -F "[0]*exp(-0.5*x*x/([1]*[1])*exp(-0.5*y*y/([2]*[2])*exp(-0.5*z*z/([3]*[3])"
 *      -@ "p0 = GetMaximum; p1 = 2*GetRMS(1); .."
 *      -= "p3 = 1"
 * 
* The result of the formulas for the start and fixed values will be evaluated * for each histogram separately. * \author mdejong */ int main(int argc, char **argv) { using namespace std; using namespace JPP; typedef JToken<';'> JToken_t; typedef JRange JRange_t; vector inputFile; string outputFile; string formula; vector startValues; vector startErrors; vector fixedValues; vector limitValues; JRange_t X; JRange_t Y; JRange_t Z; string option; bool writeFits; int debug; try { JParser<> zap("General purpose fit program for 2D ROOT objects."); zap['f'] = make_field(inputFile, ":"); zap['o'] = make_field(outputFile, "ROOT file with fit results") = "fit.root"; zap['F'] = make_field(formula, "fit formula, e.g: \"[0]+[1]*x\""); zap['@'] = make_field(startValues, "start values, e.g: \"p0 = GetMaximum;\""); zap['E'] = make_field(startErrors, "start errors, e.g: \"p0 = 0.01 * GetMaximum;\"") = JPARSER::initialised(); zap['='] = make_field(fixedValues, "fixed values, e.g: \"p0 = GetMaximum;\"") = JPARSER::initialised(); zap['R'] = make_field(limitValues, "limit values, e.g: \"p0 = ;\"") = JPARSER::initialised(); zap['x'] = make_field(X, "abscissa range") = JRange_t(); zap['y'] = make_field(Y, "abscissa range") = JRange_t(); zap['z'] = make_field(Z, "abscissa range") = JRange_t(); zap['O'] = make_field(option, "Fit option") = ""; zap['w'] = make_field(writeFits, "write fit function(s) to ROOT file " << "(\"" << _F3 << "\")"); zap['M'] = make_field(minimizer, "ROOT minimizer type and algorithm [debug]") = JMinimizer(); zap['d'] = make_field(debug) = 1; zap(argc, argv); } catch(const exception &error) { FATAL(error.what() << endl); } if (option.find('O') == string::npos) { option += "O"; } if (option.find("R") == string::npos) { option += "R"; } if (option.find("S") == string::npos) { option += "S"; } //if (option.find('N') == string::npos) { option += "N"; } if (debug == 0 && option.find('Q') == string::npos) { option += "Q"; } TFile out(outputFile.c_str(), "recreate"); TF3* fcn = new TF3("user", formula.c_str()); fcn->SetNpx(300); fcn->SetNpy(300); fcn->SetNpz(300); if (fcn->IsZombie()) { FATAL("Function: " << formula << " is zombie." << endl); } for (vector::const_iterator input = inputFile.begin(); input != inputFile.end(); ++input) { DEBUG("Input: " << *input << endl); TDirectory* dir = getDirectory(*input); if (dir == NULL) { ERROR("File: " << input->getFullFilename() << " not opened." << endl); continue; } const TRegexp regexp(input->getObjectName()); TIter iter(dir->GetListOfKeys()); for (TKey* key; (key = (TKey*) iter.Next()) != NULL; ) { const TString tag(key->GetName()); DEBUG("Key: " << tag << " match = " << tag.Contains(regexp) << endl); // option match if (tag.Contains(regexp) && isTObject(key)) { TObject* object = key->ReadObj(); // set fit parameters try { for (vector::const_iterator i = startValues.begin(); i != startValues.end(); ++i) { fcn->SetParameter(getParameter(*i), getValue(*i,object)); } for (Int_t i = 0; i != fcn->GetNpar(); ++i) { fcn->SetParError (i, 0.0); } for (vector::const_iterator i = startErrors.begin(); i != startErrors.end(); ++i) { fcn->SetParError (getParameter(*i), getValue(*i,object)); } for (vector::const_iterator i = fixedValues.begin(); i != fixedValues.end(); ++i) { fcn->FixParameter(getParameter(*i), getValue(*i,object)); } for (vector::const_iterator i = limitValues.begin(); i != limitValues.end(); ++i) { fcn->SetParLimits(getParameter(*i), getValue(*i,0), getValue(*i,1)); } } catch(JLANG::JParseError& error) { FATAL(error << endl); } DEBUG("Start values " << object->GetName() << endl); for (int i = 0; i != fcn->GetNpar(); ++i) { DEBUG(left << setw(12) << fcn->GetParName (i) << ' ' << SCIENTIFIC(12,5) << fcn->GetParameter(i) << endl); } Double_t xmin = numeric_limits::max(); Double_t xmax = numeric_limits::lowest(); Double_t ymin = numeric_limits::max(); Double_t ymax = numeric_limits::lowest(); Double_t zmin = numeric_limits::max(); Double_t zmax = numeric_limits::lowest(); { TH3* h3 = dynamic_cast(object); if (h3 != NULL) { xmin = min(xmin, h3->GetXaxis()->GetXmin()); xmax = max(xmax, h3->GetXaxis()->GetXmax()); ymin = min(ymin, h3->GetYaxis()->GetXmin()); ymax = max(ymax, h3->GetYaxis()->GetXmax()); zmin = min(zmin, h3->GetZaxis()->GetXmin()); zmax = max(zmax, h3->GetZaxis()->GetXmax()); } } if (X != JRange_t()) { xmin = X.getLowerLimit(); xmax = X.getUpperLimit(); } if (Y != JRange_t()) { ymin = Y.getLowerLimit(); ymax = Y.getUpperLimit(); } if (Z != JRange_t()) { zmin = Z.getLowerLimit(); zmax = Z.getUpperLimit(); } fcn->SetRange(xmin, ymin, zmin, xmax, ymax, zmax); // execute fit const chrono::steady_clock::time_point t0 = chrono::steady_clock::now(); JFit fit(*object, fcn, option); for_each(fit, JType()); const chrono::steady_clock::time_point t1 = chrono::steady_clock::now(); if (fit.result != -1) { // output fit results NOTICE("Fit values " << object->GetName() << endl); NOTICE("Fit formula " << formula << endl); NOTICE("chi2/NDF " << FIXED(7,3) << fit.result->Chi2() << '/' << fit.result->Ndf() << ' ' << (fit.result->IsValid() ? "" : "failed") << endl); NOTICE("Number of calls " << fit.result->NCalls() << endl); NOTICE("Elapsed time [us] " << setw(8) << chrono::duration_cast(t1 - t0).count() << endl); for (int j = 0; j != fcn->GetNpar(); ++j) { NOTICE(left << setw(12) << fcn->GetParName (j) << ' ' << SCIENTIFIC(12,5) << fcn->GetParameter(j) << " +/- " << SCIENTIFIC(12,5) << fcn->GetParError (j) << endl); } } else { WARNING("Object: not compatible with ROOT Fit." << endl); } out.cd(); object->Write(); fcn ->Write(); if (writeFits) { TObject* p = object->Clone(MAKE_CSTRING(object->GetName() << _F3)); { TH2* h2 = dynamic_cast(p); if (h2 != NULL) { for (Int_t ix = 1; ix <= h2->GetXaxis()->GetNbins(); ++ix) { for (Int_t iy = 1; iy <= h2->GetYaxis()->GetNbins(); ++iy) { for (Int_t iz = 1; iz <= h2->GetZaxis()->GetNbins(); ++iz) { const Double_t x = h2->GetXaxis()->GetBinCenter(ix); const Double_t y = h2->GetYaxis()->GetBinCenter(iy); const Double_t z = h2->GetZaxis()->GetBinCenter(iz); h2->SetBinContent(ix, iy, fcn->Eval(x,y,z)); h2->SetBinError (ix, iy, 0.0); } } } } } } } } dir->Close(); } out.Write(); out.Close(); }