/* ======================================================================== *\ ! ! * ! * This file is part of MARS, the MAGIC Analysis and Reconstruction ! * Software. It is distributed to you in the hope that it can be a useful ! * and timesaving tool in analysing Data of imaging Cerenkov telescopes. ! * It is distributed WITHOUT ANY WARRANTY. ! * ! * Permission to use, copy, modify and distribute this software and its ! * documentation for any purpose is hereby granted without fee, ! * provided that the above copyright notice appear in all copies and ! * that both that copyright notice and this permission notice appear ! * in supporting documentation. It is provided "as is" without express ! * or implied warranty. ! * ! ! ! Author(s): Thomas Bretz, 01/2002 ! Author(s): Wolfgang Wittek 11/2003 ! ! Copyright: MAGIC Software Development, 2000-2005 ! ! \* ======================================================================== */ ///////////////////////////////////////////////////////////////////////////// // // MFEventSelector2 // // This is a filter to make a selection of events from a file, according to // a certain requested distribution in a given parameter (or combination // of parameters). The distribution is passed to the class through a histogram // of the relevant parameter(s) contained in an object of type MH3. The filter // will return true or false in each event such that the final distribution // of the parameter(s) for the events surviving the filter is the desired one. // The selection of which events are kept in each bin of the parameter(s) is // made at random (obviously the selection probability depends on the // values of the parameters, and is dictated by the input histogram). // // This procedure requires the determination of the original distribution // of the given parameters for the total sample of events on the input file. // If the event loop contains a filter with name "FilterSelector2", this // filter will be applied when determining the original distribution. // // See Constructor for more instructions and also the example below: // // -------------------------------------------------------------------- // // void select() // { // MParList plist; // MTaskList tlist; // // MStatusDisplay *d=new MStatusDisplay; // // plist.AddToList(&tlist); // // MReadTree read("Events", "myinputfile.root"); // read.DisableAutoScheme(); // // Accelerate execution... // // read.EnableBranch("MMcEvt.fTelescopeTheta"); // // // create nominal distribution (theta converted into degrees) // MH3 nomdist("r2d(MMcEvt.fTelescopeTheta)"); // MBinning binsx; // binsx.SetEdges(5, 0, 45); // five bins from 0deg to 45deg // MH::SetBinning(&nomdist.GetHist(), &binsx); // // // use this to create a nominal distribution in 2D // // MH3 nomdist("r2d(MMcEvt.fTelescopeTheta)", "MMcEvt.fEnergy"); // // MBinning binsy; // // binsy.SetEdgesLog(5, 10, 10000); // // MH::SetBinning((TH2*)&nomdist.GetHist(), &binsx, &binsy); // // // Fill the nominal distribution with whatever you want: // for (int i=0; iRndm(). You may // control this procedure using the global object gRandom. // // Because of the random numbers this works best for huge samples... // // Don't try to use this filter for the reading task or as a selector // in the reading task: This won't work! // // Remark: You can also use the filter together with MContinue // // // FIXME: Merge MFEventSelector and MFEventSelector2 // ///////////////////////////////////////////////////////////////////////////// #include "MFEventSelector2.h" #include // gRandom #include // TCanvas #include "MH3.h" // MH3 #include "MRead.h" // MRead #include "MEvtLoop.h" // MEvtLoop #include "MTaskList.h" // MTaskList #include "MBinning.h" // MBinning #include "MContinue.h" // #include "MFillH.h" // MFillH #include "MParList.h" // MParList #include "MStatusDisplay.h" // MStatusDisplay #include "MLog.h" #include "MLogManip.h" ClassImp(MFEventSelector2); using namespace std; const TString MFEventSelector2::gsDefName = "MFEventSelector2"; const TString MFEventSelector2::gsDefTitle = "Filter to select events with a given distribution"; // -------------------------------------------------------------------------- // // Constructor. Takes a reference to an MH3 which gives you // 1) The nominal distribution. The distribution is renormalized, so // that the absolute values do not matter. To crop the distribution // to a nominal value of total events use SetNumMax // 2) The parameters histogrammed in MH3 are those on which the // event selector will work, eg // MH3 hist("MMcEvt.fTelescopeTheta", "MMcEvt.fEnergy"); // Would result in a redistribution of Theta and Energy. // 3) Rules are also accepted in the argument of MH3, for instance: // MH3 hist("MMcEvt.fTelescopeTheta"); // would result in redistributing Theta, while // MH3 hist("cos(MMcEvt.fTelescopeTheta)"); // would result in redistributing cos(Theta). // // If the reference distribution doesn't contain entries (GetEntries()==0) // the original distribution will be used as the nominal distribution; // note that also in this case a dummy nominal distribution has to be // provided in the first argument (the dummy distribution defines the // variable(s) of interest, their binnings and their requested ranges; // events outside these ranges won't be accepted). // // Set default name of filter to be applied when determining the original // distribution for all data on the input file to "FilterSelector2" // MFEventSelector2::MFEventSelector2(MH3 &hist, const char *name, const char *title) : fHistOrig(NULL), fHistNom(&hist), fHistRes(NULL), fDataX(hist.GetRule('x')), fDataY(hist.GetRule('y')), fDataZ(hist.GetRule('z')), fNumMax(-1), fCanvas(0), fFilterName("FilterSelector2"), fHistIsProbability(kFALSE), fUseOrigDist(kTRUE) { fName = name ? (TString)name : gsDefName; fTitle = title ? (TString)title : gsDefTitle; } // -------------------------------------------------------------------------- // // Delete fHistRes if instatiated // MFEventSelector2::~MFEventSelector2() { if (fHistRes) delete fHistRes; } //--------------------------------------------------------------------------- // // Recreate a MH3 from fHistNom used as a template. Copy the Binning // from fHistNom to the new histogram, and return a pointer to the TH1 // base class of the MH3. // TH1 &MFEventSelector2::InitHistogram(MH3* &hist) { // if fHistRes is already allocated delete it first if (hist) delete hist; // duplicate the fHistNom histogram hist = (MH3*)fHistNom->New(); // copy binning from one histogram to the other one MH::SetBinning(&hist->GetHist(), &fHistNom->GetHist()); return hist->GetHist(); } // -------------------------------------------------------------------------- // // Try to read the present distribution from the file. Therefore the // Reading task of the present loop is used in a new eventloop. // Bool_t MFEventSelector2::ReadDistribution(MRead &read, MFilter *filter) { if (read.GetEntries() > kMaxUInt) // FIXME: LONG_MAX ??? { *fLog << err << "kIntMax exceeded." << endl; return kFALSE; } *fLog << inf; fLog->Separator("MFEventSelector2::ReadDistribution"); *fLog << " - Start of eventloop to generate the original distribution..." << endl; if (filter != NULL) *fLog << " - filter used: " << filter->GetDescriptor() << endl; MEvtLoop run("ReadDistribution"); MParList plist; MTaskList tlist; plist.AddToList(&tlist); run.SetParList(&plist); MBinning binsx("BinningMH3X"); MBinning binsy("BinningMH3Y"); MBinning binsz("BinningMH3Z"); binsx.SetEdges(fHistNom->GetHist(), 'x'); binsy.SetEdges(fHistNom->GetHist(), 'y'); binsz.SetEdges(fHistNom->GetHist(), 'z'); plist.AddToList(&binsx); plist.AddToList(&binsy); plist.AddToList(&binsz); MFillH fill(fHistOrig); fill.SetName("FillHistOrig"); fill.SetBit(MFillH::kDoNotDisplay); tlist.AddToList(&read); MContinue contfilter(filter); if (filter != NULL) { contfilter.SetName("ContFilter"); tlist.AddToList(&contfilter); } tlist.AddToList(&fill); run.SetDisplay(fDisplay); if (!run.Eventloop()) { *fLog << err << dbginf << "Evtloop failed... abort." << endl; return kFALSE; } tlist.PrintStatistics(); *fLog << inf; *fLog << "MFEventSelector2::ReadDistribution:" << endl; *fLog << " - Original distribution has " << fHistOrig->GetHist().GetEntries() << " entries." << endl; *fLog << " - End of eventloop to generate the original distribution." << endl; fLog->Separator(); return read.Rewind(); } // -------------------------------------------------------------------------- // // After reading the histograms the arrays used for the random event // selection are created. If a MStatusDisplay is set the histograms are // displayed there. // void MFEventSelector2::PrepareHistograms() { TH1 &ho = fHistOrig->GetHist(); //------------------- // if requested // set the nominal distribution equal to the original distribution const Bool_t fUseOrigDist = fHistNom->GetHist().GetEntries()==0; TH1 *hnp = fUseOrigDist ? (TH1*)(fHistOrig->GetHist()).Clone() : &fHistNom->GetHist(); TH1 &hn = *hnp; //-------------------- // normalize to number of counts in primary distribution hn.Scale(1./hn.Integral()); MH3 *h3 = NULL; TH1 &hist = InitHistogram(h3); hist.Divide(&hn, &ho); hist.Scale(1./hist.GetMaximum()); if (fCanvas) { fCanvas->Clear(); fCanvas->Divide(2,2); fCanvas->cd(1); gPad->SetBorderMode(0); hn.DrawCopy(); fCanvas->cd(2); gPad->SetBorderMode(0); ho.DrawCopy(); } hn.Multiply(&ho, &hist); hn.SetTitle("Resulting Nominal Distribution"); if (fNumMax>0) { *fLog << inf; *fLog << "MFEventSelector2::PrepareHistograms:" << endl; *fLog << " - requested number of events = " << fNumMax << endl; *fLog << " - maximum number of events possible = " << hn.Integral() << endl; if (fNumMax > hn.Integral()) { *fLog << warn << "WARNING - Requested no.of events (" << fNumMax; *fLog << ") is too high... reduced to " << hn.Integral() << endl; } else hn.Scale(fNumMax/hn.Integral()); } hn.SetEntries(hn.Integral()+0.5); if (fCanvas) { fCanvas->cd(3); gPad->SetBorderMode(0); hn.DrawCopy(); fCanvas->cd(4); gPad->SetBorderMode(0); fHistRes->Draw(); } delete h3; const Int_t num = fHistRes->GetNbins(); fIs.Set(num); fNom.Set(num); for (int i=0; iGetDimension()) { case 3: if (!fDataZ.PreProcess(parlist)) { *fLog << err << "Preprocessing of rule for z-axis failed... abort." << endl; return kFALSE; } // FALLTHROUGH! case 2: if (!fDataY.PreProcess(parlist)) { *fLog << err << "Preprocessing of rule for y-axis failed... abort." << endl; return kFALSE; } // FALLTHROUGH! case 1: if (!fDataX.PreProcess(parlist)) { *fLog << err << "Preprocessing of rule for x-axis failed... abort." << endl; return kFALSE; } } return kTRUE; } // -------------------------------------------------------------------------- // // PreProcess the filter. Means: // 1) Preprocess the rules // 2) Read The present distribution from the file. // 3) Initialize the histogram for the resulting distribution // 4) Prepare the random selection // 5) Repreprocess the reading and filter task. // Int_t MFEventSelector2::PreProcess(MParList *parlist) { memset(fCounter, 0, sizeof(fCounter)); MTaskList *tasklist = (MTaskList*)parlist->FindObject("MTaskList"); if (!tasklist) { *fLog << err << "MTaskList not found... abort." << endl; return kFALSE; } if (!PreProcessData(parlist)) return kFALSE; fHistNom->SetTitle(fHistIsProbability ? "ProbabilityDistribution" : "Users Nominal Distribution"); if (fHistIsProbability) return kTRUE; InitHistogram(fHistOrig); InitHistogram(fHistRes); fHistOrig->SetTitle("Primary Distribution"); fHistRes->SetTitle("Resulting Distribution"); // Initialize online display if requested fCanvas = fDisplay ? &fDisplay->AddTab(GetName()) : NULL; if (fCanvas) fHistOrig->Draw(); // Generate primary distribution MRead *read = (MRead*)tasklist->FindObject("MRead"); if (!read) { *fLog << err << "MRead not found in tasklist... abort." << endl; return kFALSE; } MFilter *filter = (MFilter*)tasklist->FindObject(fFilterName); if (!filter || !filter->InheritsFrom(MFilter::Class())) { *fLog << inf << "No filter will be used when making the original distribution" << endl; filter = NULL; } if (!ReadDistribution(*read, filter)) return kFALSE; // Prepare histograms and arrays for selection PrepareHistograms(); *fLog << all << "PreProcess..." << flush; if (filter != NULL) { const Int_t rcf = filter->CallPreProcess(parlist); if (rcf!=kTRUE) return rcf; } const Int_t rcr = read->CallPreProcess(parlist); return rcr; } // -------------------------------------------------------------------------- // // Part of Process(). Select() at the end checks whether a selection should // be done or not. Under-/Overflowbins are rejected. // Bool_t MFEventSelector2::Select(Int_t bin) { // under- and overflow bins are not accepted if (bin<0) return kFALSE; Bool_t rc = kFALSE; if (gRandom->Rndm()*fIs[bin]<=fNom[bin]) { // how many events do we still want to read in this bin fNom[bin]--; rc = kTRUE; // fill bin (same as Fill(valx, valy, valz)) TH1 &h = fHistRes->GetHist(); h.AddBinContent(bin+1); h.SetEntries(h.GetEntries()+1); } // how many events are still pending to be read fIs[bin]--; return rc; } // -------------------------------------------------------------------------- // Bool_t MFEventSelector2::SelectProb(Int_t ibin) const { // // If value is outside histogram range, accept event // return ibin<0 ? kTRUE : fHistNom->GetHist().GetBinContent(ibin+1) > gRandom->Uniform(); } // -------------------------------------------------------------------------- // // fIs[i] contains the distribution of the events still to be read from // the file. fNom[i] contains the number of events in each bin which // are requested. // The events are selected by: // gRandom->Rndm()*fIs[bin]<=fNom[bin] // Int_t MFEventSelector2::Process() { // get x,y and z (0 if fData not valid) const Double_t valx=fDataX.GetValue(); const Double_t valy=fDataY.GetValue(); const Double_t valz=fDataZ.GetValue(); // don't except the event if it is outside the axis range // of the requested distribution const Int_t ibin = fHistNom->FindFixBin(valx, valy, valz)-1; if (!fHistIsProbability) { if (ibin < 0) { fResult = kFALSE; fCounter[1]++; return kTRUE; } } // check whether a selection should be made fResult = fHistIsProbability ? SelectProb(ibin) : Select(ibin); if (!fResult) { fCounter[2]++; return kTRUE; } fCounter[0]++; return kTRUE; } // -------------------------------------------------------------------------- // // Update online display if set. // Int_t MFEventSelector2::PostProcess() { //--------------------------------- if (GetNumExecutions()>0) { *fLog << inf << endl; *fLog << GetDescriptor() << " execution statistics:" << endl; *fLog << dec << setfill(' '); *fLog << " " << setw(7) << fCounter[1] << " (" << setw(3) << (int)((Float_t)(fCounter[1]*100)/(Float_t)(GetNumExecutions())+0.5) << "%) Events not selected due to under/over flow" << endl; *fLog << " " << setw(7) << fCounter[2] << " (" << setw(3) << (int)((Float_t)(fCounter[2]*100)/(Float_t)(GetNumExecutions())+0.5) << "%) Events not selected due to requested distribution" << endl; *fLog << " " << fCounter[0] << " (" << (int)((Float_t)(fCounter[0]*100)/(Float_t)(GetNumExecutions())+0.5) << "%) Events selected" << endl; *fLog << endl; } //--------------------------------- if (fDisplay && fDisplay->HasCanvas(fCanvas)) { fCanvas->cd(4); fHistRes->DrawClone("nonew"); fCanvas->Modified(); fCanvas->Update(); } return kTRUE; }