// Author: Gero Flucke <mailto:gero.flucke@desy.de>
//______________________________________________
// last change: $Date: 2005/11/26 20:04:56 $
// by : $author$
//
// ABC for special fitting ('helper') classes
// to fit the Delta-m distribution for D* analysis
// Use DstarDmFitter to do these fits!
#include "DstarDmFitterSpecial_I.h"
#include <TVirtualFitter.h>
#include <TROOT.h>
#include <TH1.h>
#include <TF1.h>
#include <TMath.h>
#include <TDatabasePDG.h>
#include <iostream>
using namespace std;
ClassImp(DstarDmFitterSpecial_I)
Double_t DstarDmFitterSpecial_I::fgSigmaDefault = 0.0010;
Double_t DstarDmFitterSpecial_I::fgMeanDefault = 0.14544;
Double_t DstarDmFitterSpecial_I::fgBackgrExpDefault = 0.45;
Double_t DstarDmFitterSpecial_I::fgBackgrSqrDefault = 18.;
Double_t DstarDmFitterSpecial_I::fgUpperBackgrSqrDefault = 36.;
Double_t DstarDmFitterSpecial_I::fgLowerBackgrSqrDefault = 0.;
Double_t DstarDmFitterSpecial_I::fgUpperBackgrExpDefault = 0.75;
Double_t DstarDmFitterSpecial_I::fgLowerBackgrExpDefault = 0.15;
const Double_t DstarDmFitterSpecial_I::fgUpFitLimitDefault = 0.1675;
Double_t DstarDmFitterSpecial_I::fgUpFitLimit =
DstarDmFitterSpecial_I::fgUpFitLimitDefault;
const Double_t DstarDmFitterSpecial_I::fgMassPion =
TDatabasePDG::Instance()->GetParticle(211)->Mass();
const Double_t DstarDmFitterSpecial_I::kSqrtOf2Pi = TMath::Sqrt(2.*TMath::Pi());
Double_t DstarDmFitterSpecial_I::fgBinSize = -1.0; // garbage init
Double_t DstarDmFitterSpecial_I::fgShiftWcHist = -1.0; // garbage init
Double_t DstarDmFitterSpecial_I::fgEndRightCharge = -1.; // garbage init
Bool_t DstarDmFitterSpecial_I::fgDebug = kFALSE;
//*********************************************************
DstarDmFitterSpecial_I::DstarDmFitterSpecial_I() : fHist(NULL), fHistWc(NULL),
fIsMC(kFALSE), fDetectIsMC(kFALSE), fFitOption("EMRIL")
{
// default ctor., determines only default fit option and IsMc flag
/*
fitoption
= "W" Set all errors to 1
= "I" Use integral of function in bin instead of value at bin
center
= "L" Use Loglikelihood method (default is chisquare method)
= "U" Use a User specified fitting algorithm (via SetFCN)
= "Q" Quiet mode (minimum printing)
= "V" Verbose mode (default is between Q and V)
= "E" Perform better Errors estimation using Minos technique
= "M" More. Improve fit results
= "R" Use the Range specified in the function range
= "N" Do not store the graphics function, do not draw
= "0" Do not plot the result of the fit. By default the
fitted function is drawn unless the
option"N" above is specified.
= "+" Add this new fitted function to the list of fitted
functions (by default, any previous function
is deleted)
*/
}
//*********************************************************
const char* DstarDmFitterSpecial_I::GetHistName() const
{
// returns name of the histogram that should be fitted
if(fHist) return fHist->GetName();
else {
this->Warning("GetHistName","There is no histogram!");
return "There is no histogram!";
}
}
//******************************************************
Double_t DstarDmFitterSpecial_I::GetWcBackgrNormScale() const
{
// should be implemented in derived classes:
// if fitted with WC hist gives the ratio between the background normalisations: U_n,wc/U_n
this->AbstractMethod("GetWcBackgrNormScale");
return 0.;
}
//******************************************************
Double_t DstarDmFitterSpecial_I::GetHistBinSize() const
{
// evaluates the bin size of the histogram (and -if existing- of WC hist)
// and prints warnings if it's not constant
if(!fHist){
this->Error("GetHistBinSize","There is no histogram! Return 100.");
return -100.;
}
Int_t ind = fHist->FindBin(fgMeanDefault);
const Double_t binSize = fHist->GetBinLowEdge(ind+1) - fHist->GetBinLowEdge(ind);
// I don't check binSize not to be 0,
// but I do check that it's more or less constant:
Int_t nBins = fHist->GetNbinsX();
for(Int_t i = 1; i <= nBins; i++){
Double_t actBinSize =
Double_t(fHist->GetBinLowEdge(i+1) - fHist->GetBinLowEdge(i));
if(TMath::Abs(binSize - actBinSize)/binSize >0.0001){//0.01% deviat.
this->Error("GetHistBinSize",
"histogram does NOT have constant binSize!");
return -100.;
}
}
if(fHistWc){
nBins = fHistWc->GetNbinsX();
for(Int_t i = 1; i <= nBins; i++){
Double_t actBinSize = Double_t(fHist->GetBinLowEdge(i+1) - fHist->GetBinLowEdge(i));
if(TMath::Abs(binSize - actBinSize)/binSize >0.0001){//0.01% deviat.
this->Error("GetHistBinSize",
"wrong charge histogram does NOT have constant binSize!");
return -100.;
}
}
}
return binSize;
}
//********************************************************
TH1* DstarDmFitterSpecial_I::CreateMergedHist(const TH1* h, const TH1* hWc,
Double_t& shiftWc) const
{
// creates a hist with the bins of h, followed by an empty bin and then come the bins
// of hWc. shiftWc is filled with the value that the bins egdes of hWc are shifted
if(!h) {
if(!hWc) return NULL;
else return NULL; // FIXME: we could do sth. here...
} else if(!hWc){
return static_cast<TH1*>(h->Clone());
}
TArrayD bins(h->GetNbinsX() + hWc->GetNbinsX() + 2);
for(Int_t i = 0; i < h->GetNbinsX(); ++i){
bins[i] = h->GetBinLowEdge(i+1);
}
bins[h->GetNbinsX()] = h->GetXaxis()->GetXmax();
const Double_t startWcHist = bins[h->GetNbinsX()] + h->GetBinWidth(h->GetNbinsX());
const Double_t offset = startWcHist - hWc->GetBinLowEdge(0);
for(Int_t i = h->GetNbinsX() + 1; i < bins.GetSize()-1; ++i){
bins[i] = hWc->GetBinLowEdge(i - h->GetNbinsX()) + offset;
}
bins[bins.GetSize()-1] = hWc->GetXaxis()->GetXmax() + offset;
TH1* result = new TH1F(Form("%sAnd%s", h->GetName(), hWc->GetName()),
Form("%s and %s", h->GetTitle(), hWc->GetTitle()),
bins.GetSize()-1, bins.GetArray());
for(Int_t i = 1; i <= h->GetNbinsX(); ++i){
result->SetBinContent(i, h->GetBinContent(i));
if(h->GetSumw2N() || hWc->GetSumw2N()) result->SetBinError(i, h->GetBinError(i));
}
for(Int_t i = 1; i <= hWc->GetNbinsX(); ++i){
result->SetBinContent(h->GetNbinsX() + i + 1, hWc->GetBinContent(i));
if(h->GetSumw2N() || hWc->GetSumw2N())
result->SetBinError(h->GetNbinsX() + i + 1, hWc->GetBinError(i));
}
shiftWc = offset;
return result;
}
//********************************************************
void DstarDmFitterSpecial_I::SetHist(TH1* hist, Bool_t defaultSigmaMeanExpSq)
{
// for backward comp.
this->SetHist(hist, NULL, defaultSigmaMeanExpSq);
}
//********************************************************
void DstarDmFitterSpecial_I::SetHist(TH1* hist, TH1* hWc, Bool_t defaultSigmaMeanExpSq)
{
// interface to set a TH1* hist (may not be a TH2* or TH3*!) and WC background hist
// if defaultSigmaMeanExpSq is kTRUE (it's default is kFALSE),
// default values are set
// for Sigma, Mean, BackgrExp, BackgrSqr
// else they are taken as they are (e.g. from the last fit)
if(hist && hist->GetDimension() > 1) {
this->Error("SetHist","Histogram given to fit has dimension %d!", hist->GetDimension());
}
if(hWc && hWc->GetDimension() > 1){
this->Error("SetHist","Wrong charge histogram given to fit has dimension %d!",
hWc->GetDimension());
}
this->SetHistReal(hist, hWc, defaultSigmaMeanExpSq);
}
//********************************************************
Double_t DstarDmFitterSpecial_I::DetermBackgrStartParam()
{
// determines start parameter for background fit
// and checks whether fHist is backgroundless MC (but fNoIsMC == true): no MC assumption!
// (for data determines only normalisation)
// get integral of background region:
Float_t backgrRangeLow = fgMeanDefault + 2.5*fgSigmaDefault;
Int_t binLow = fHist->FindBin(backgrRangeLow);
Int_t binHigh = fHist->FindBin(fUpFitLimit);
Double_t backgrIntegr = fHist->Integral(binLow, binHigh);
// get integral of signal region:
Int_t binSigLow = fHist->FindBin(fgMeanDefault - 2.*fgSigmaDefault);
Int_t binSigHigh = fHist->FindBin(fgMeanDefault + 2.*fgSigmaDefault);
Double_t signalRegIntegr = fHist->Integral(binSigLow, binSigHigh);
Double_t backgrNormStart = 0.;
if(fDetectIsMC && (backgrIntegr / signalRegIntegr) < 0.2){ // MC ( ~0.04)
cout << "assume MC: no background!" << endl;
backgrNormStart= 0.;
fIsMC = kTRUE;
} else { // normal data
backgrNormStart = fHist->Integral(1, binHigh) - 0.25 * signalRegIntegr;//assume S/U = 1:3
// backgrNormStart = backgrIntegr / (binHigh-binLow+1);
// backgrNormStart /= TMath::Power((fUpFitLimit - backgrRangeLow),fgBackgrExpDefault)
// * GetHistBinSize();
cout << "Start parameter of background normalisation: "
<< backgrNormStart << endl;
fIsMC = kFALSE;
}
return backgrNormStart;
}
//********************************************************
Int_t DstarDmFitterSpecial_I::UseLikelihoodOption(TH1* hist) const
{
// determine use of likelihood option in fit, dependent on given option and
// hists with weights:
// 2 if fitoption contains LL
// 0 if fitoption does not contain any L
// or if exact one L, but filled with weights
// 1 else
//
if(!fFitOption.Contains("L")) return 0;
if(fFitOption.Contains("LL")) return 2;
// so we have no 'L' but not 'LL': if we have weights, no likelihood!
if(hist->GetSumw2N()){ // hist has some error values stored...
for(Int_t i = 1; i <= hist->GetNbinsX(); ++i){
if(!hist->GetBinContent(i)) continue; // skip zero bins - we might have set there error=1!
const Double_t error = hist->GetBinError(i);
if(TMath::Abs(TMath::Abs(hist->GetBinContent(i)) - error*error) > 1.e-10){
#if ROOT_VERSION_CODE > ROOT_VERSION(3,3,0)
this->Info("UseLikelihoodOption", "No likelihood (weights!), option 'LL' to enforce");
#else
this->Warning("UseLikelihoodOption","No likelihood (weights!), option 'LL' to enforce");
#endif
return 0;
}
}
}
return 1;
// true if more than 1 bin above fgMeanDefault+ 2.*fgSigmaDefault has <= 5 entries
// Int_t numLowStatBins = 0;
// const Int_t lastBin = TMath::Min(hist->FindBin(this->GetUpFitLimit()), hist->GetNbinsX());
// for(Int_t i = TMath::Max(hist->FindBin(fgMeanDefault+ 2.*fgSigmaDefault),1); i < lastBin;++i){
// if(hist->GetBinContent(i) <= 5) {
// if(++numLowStatBins > 1) {
// cout << "Switched to likelihood!" << endl;
// return 1;
// }
// }
// }
// return 0;
}
//********************************************************
Bool_t DstarDmFitterSpecial_I::TreatZeroErrorBins(TH1* hist) const
{
// Assign an error to all bins with error = 0. to keep them, in the fit also for chi^2-fit.
// The error is set to 1. if no squares of weights are stored with the hist, otherwise
// to the mean of the errors of the first bin on the left and on the right that have
// error != 0. Only bins in the fitrange (m(pi) -> upfitlimit) are used for that.
// False if no hist given or all bins in range have error == 0.
if(!hist) return kFALSE;
const Int_t firstBin = hist->FindBin(fgMassPion);
const Int_t lastBin = TMath::Min(hist->FindBin(this->GetUpFitLimit()), hist->GetNbinsX());
const Bool_t storedWeights = hist->GetSumw2N();
Int_t numProbBins = 0;
TArrayD newError(lastBin + 1); // little longer than needed: no mix up of indices...
for(Int_t i = firstBin; i <= lastBin; ++i){
if(hist->GetBinError(i) == 0.){
++numProbBins;
if(!storedWeights){
newError[i] = 1.;
} else {
Int_t binLeft = TMath::Max(i - 1, firstBin);
Double_t errorLeft = 0.;
while(binLeft >= firstBin){
errorLeft = hist->GetBinError(binLeft--);// postfix decrement
if(errorLeft) break;
}
Int_t binRight = TMath::Min(i + 1, lastBin);
Double_t errorRight = 0.;
while(binRight <= lastBin){
errorRight = hist->GetBinError(binRight++); // postfix increment
if(errorRight) break;
}
// newError[i] is stored and not directly set: binLeft could not have 0 any more...
if(errorLeft && errorRight){
newError[i] = (errorLeft + errorRight)/2.;
} else if(errorLeft){
newError[i] = errorLeft;
} else if(errorRight){
newError[i] = errorRight;
} else {
this->Warning("TreatZeroErrorBins",
"No bin has any error, set err(%d) = max(1,sqrt(|bincontent|)", i);
newError[i] = TMath::Max(1., TMath::Sqrt(TMath::Abs(hist->GetBinContent(i))));
}
}
}
}
if(numProbBins == lastBin - firstBin + 1){ // all bins had no error!
return kFALSE;
} else {
Int_t manip = 0;
for(Int_t i = firstBin; i <= lastBin; ++i){
if(newError[i]){ // == 0 means: not manipulated
if(fgDebug) this->Info("TreatZeroErrorBins", "manipulate error of bin %d to %f",
i, newError[i]);
hist->SetBinError(i, newError[i]);
++manip;
}
}
if(manip) this->Info("TreatZeroErrorBins", "manipulate error of %d bins", manip);
return kTRUE;
}
}
TString DstarDmFitterSpecial_I::GetSimpleFitOption(const char* opt) const
{
TString option(opt);
option.ToUpper();
option.ReplaceAll("I", NULL);
option.ReplaceAll("M", NULL);
option.ReplaceAll("E", NULL);
option.ReplaceAll("L", NULL);
option += "NQ"; // add 'Q'uiet
return option;
}
Bool_t DstarDmFitterSpecial_I::IsEquivalentOpt(const TString& optOrig,
const TString& opt2) const
{
TString origToManip(optOrig);
origToManip.ToUpper();
TString opt2ToManip(opt2);
opt2ToManip.ToUpper();
origToManip.ReplaceAll("N", NULL);
opt2ToManip.ReplaceAll("N", NULL);
origToManip.ReplaceAll("Q", NULL);
opt2ToManip.ReplaceAll("Q", NULL);
origToManip.ReplaceAll("0", NULL);
opt2ToManip.ReplaceAll("0", NULL);
if(origToManip.Contains("R") && opt2ToManip.Contains("R")){
origToManip.ReplaceAll("R", NULL);
opt2ToManip.ReplaceAll("R", NULL);
}
if(origToManip.Contains("I") && opt2ToManip.Contains("I")){
origToManip.ReplaceAll("I", NULL);
opt2ToManip.ReplaceAll("I", NULL);
}
if(origToManip.Contains("M") && opt2ToManip.Contains("M")){
origToManip.ReplaceAll("M", NULL);
opt2ToManip.ReplaceAll("M", NULL);
}
if(origToManip.Contains("E") && opt2ToManip.Contains("E")){
origToManip.ReplaceAll("E", NULL);
opt2ToManip.ReplaceAll("E", NULL);
}
if(origToManip.Contains("LL") && opt2ToManip.Contains("LL")){
origToManip.ReplaceAll("LL", NULL);
opt2ToManip.ReplaceAll("LL", NULL);
} else if(origToManip.CountChar('L') == 1 && opt2ToManip.CountChar('L') == 1){//not OK for LXL
origToManip.ReplaceAll("L", NULL);
opt2ToManip.ReplaceAll("L", NULL);
}
return (origToManip.Length() == 0 && opt2ToManip.Length() == 0);
}
Int_t DstarDmFitterSpecial_I::RealFit(TH1* histToFit, TF1* func) const
{
// calling histToFit->Fit(func, option) where option first is a simple version
// of fFitOption and then increasing (L, I, E, M) when given/needed
// return result of histToFit->Fit(func, fFitOption) or the first failing of prior tries.
// If likelihood method is NOT used, call TreatZeroErrorBins for fHist and fHistWc,
// if there is a problem, return -10 and don't fit.
// TVirtualFitter::SetMaxIterations(500);
// TVirtualFitter::SetPrecision();//default
// TVirtualFitter::SetPrecision(5.*TVirtualFitter::GetPrecision());
TString fitOpt = this->GetSimpleFitOption(fFitOption);
if(fHist != histToFit) this->Warning("RealFit", "test only right charge hist for liklihood");
const Int_t useLikelihood = this->UseLikelihoodOption(fHist);
if(useLikelihood == 0){
if(!this->TreatZeroErrorBins(fHist)){
this->Error("RealFit", "Cannot fit hist with all errors = 0");
return -10;
}
if(fHistWc && !this->TreatZeroErrorBins(fHistWc)){
this->Error("RealFit", "Cannot fit histWc with all errors = 0");
return -10;
}
}
Int_t fitResult = this->RealFit(histToFit, func, fitOpt);
if(fitResult == 0 && useLikelihood) {
fitOpt += 'L';
if(useLikelihood == 2) fitOpt += 'L';
if(this->IsEquivalentOpt(fitOpt, fFitOption)) fitOpt = fFitOption+'0';
fitResult = this->RealFit(histToFit, func, fitOpt);
}
if(fitResult == 0 && fFitOption.Contains("I", TString::kIgnoreCase)
&& fitOpt != (fFitOption+'0')){
fitOpt += 'I';
if(this->IsEquivalentOpt(fitOpt, fFitOption)) fitOpt = fFitOption+'0';
fitResult = this->RealFit(histToFit, func, fitOpt);
}
if(fitResult == 0 && fFitOption.Contains("M", TString::kIgnoreCase)
&& fitOpt != (fFitOption+'0')){
fitOpt += 'M';
if(this->IsEquivalentOpt(fitOpt, fFitOption)) fitOpt = fFitOption+'0';
fitResult = this->RealFit(histToFit, func, fitOpt);
}
if(fitResult == 0 && fFitOption.Contains("E", TString::kIgnoreCase)
&& fitOpt != (fFitOption+'0')){
fitOpt += 'E';
if(this->IsEquivalentOpt(fitOpt, fFitOption)) fitOpt = fFitOption+'0';
fitResult = this->RealFit(histToFit, func, fitOpt);
}
if(fitResult == 0 && fitOpt != (fFitOption+'0')){
fitOpt = fFitOption+'0';
if(useLikelihood == 0) fitOpt.ReplaceAll("L", NULL);
fitResult = this->RealFit(histToFit, func, fitOpt);
}
if(fitResult != 0){
this->Error("RealFit", "fit finally unsuccessful, last used option %s", fitOpt.Data());
delete histToFit->GetListOfFunctions()->Remove(histToFit->FindObject(func->GetName()));
} else if(!fFitOption.Contains("0")){
histToFit->GetFunction(func->GetName())->ResetBit(TF1::kNotDraw);
}
return fitResult;
}
Int_t DstarDmFitterSpecial_I::RealFit(TH1* histToFit, TF1* func, const char* fitOpt) const
{
// fit hist with func and option
// first time displaces start parameter by factor 0.98, if no sucess tries several times
// if no success, func's params are reset to old values
// return fit result of last attempt
if(fgDebug) this->Info("RealFit","fit option %s", fitOpt);
Double_t params[50];// 7 is maximum...
func->GetParameters(params);
this->MultiplyFreeParams(func, 0.98);
Int_t fitResult = histToFit->Fit(func, fitOpt);
if(fitResult != 0){
TString nonQuietOpt(fitOpt);
nonQuietOpt.ToUpper();
nonQuietOpt.ReplaceAll("Q", NULL);
Long_t tries = 1;// Long_t for TMath::Odd
do {
this->Warning("RealFit","%s failed, try again (%d) with modified parameters (non quiet)",
fitOpt, tries);
++tries;
func->SetParameters(params);
Double_t factor = 1.;
if(TMath::Odd(tries)) factor += (tries * 0.01);// first time: tries == 2, so 1.02, 1.04,
else factor -= (tries * 0.01);// .97, .95, .93
this->MultiplyFreeParams(func, factor);
fitResult = histToFit->Fit(func, nonQuietOpt);
if(fitResult == 0) break;
} while (tries < 10);
}
if(fitResult != 0) func->SetParameters(params); // reset to old values
return fitResult;
}
Int_t DstarDmFitterSpecial_I::MultiplyFreeParams(TF1* func, Double_t factor) const
{
// multiply all non-fixed parameters of func by factor, returns number of non-free params
Int_t changed = 0;
for(Int_t i = 0; i < func->GetNpar(); ++i){
Double_t parMin = 0., parMax = 0.;
func->GetParLimits(i, parMin, parMax);
Bool_t fixed = kTRUE;
if(!(parMin*parMax != 0. && parMin >= parMax)){ // cf. TF1::GetNumberFreeParameters()
func->SetParameter(i, func->GetParameter(i) * factor);// set if not fixed
++changed;
fixed = kFALSE;
}
if(!fixed && func->GetParameter(i) <= 0. && strcmp(func->GetParName(i), "#sigma") == 0){
this->Info("MultiplyFreeParams", "#sigma manipulated from %f to default %f",
func->GetParameter(i), fgSigmaDefault);
func->SetParameter(i, fgSigmaDefault);
}
if(!fixed && strcmp(func->GetParName(i), "#mu") == 0 &&
(func->GetParameter(i) < 0.14 || func->GetParameter(i) > 0.15) ){
this->Info("MultiplyFreeParams", "#mu manipulated from %f to default %f",
func->GetParameter(i), fgMeanDefault);
func->SetParameter(i, fgMeanDefault);
}
if(fgDebug) {
cout << func->GetParName(i) << " = " << func->GetParameter(i) << " ";
if(fixed) cout << "(fix) ";
}
}
if(fgDebug) cout << " (from MultiplyFreeParams)" << endl;
if(changed != func->GetNumberFreeParameters()){
this->Error("MultiplyFreeParams", "Changed %d, not %d parameters.",
changed, func->GetNumberFreeParameters());
}
return changed;
}
ROOT page - Class index - Top of the page
This page has been automatically generated. If you have any comments or suggestions about the page layout send a mail to ROOT support, or contact the developers with any questions or problems regarding ROOT.