//Renata Kopecna #include #include #include // std::istringstream #include #include #include #include #include #include #include #include #include #include #include #include #include int mainfit(fcnc::options opts, basic_params params, bool fitReference, bool fitToy, bool likelihoodScan, bool FeldmanCousin){ //TODO bool fitData = !fitToy; //TODO: add a test where I cna try this on a toy file if(fitData) spdlog::info("[FIT]\tFit signal data"); //signal data << FINAL FIT!! if(fitReference)spdlog::info("[FIT]\tFit reference channel: J/psi K*+"); //reference channel only //-------------------------------- // Set all the options //-------------------------------- //TODO: declare maybe in the header or elsewhere const bool UseBinnedFit = !fitReference; const bool SimultaneousFit = true; const bool FixAngularBckgnd = false; //Fix the background to the shape of the upper mass sideband const bool constrainBmass = false; const bool FixedSwave = !fitReference; //TODO const bool ConstrainedSwave = false; //TODO const bool ConstrainedFS = false; //TODO const bool LoadFSfrom2Dfit = !fitReference && !(opts.systematic > -1); //const bool JpsiWithRareStats = false; //Try to fit the Jpsi fit with only a subsample of events //TODO const double fractionOfStats = 1.0; //Use only a fraction of statistics, eg for half set this to 0.5 //TODO: set the fraction of stats accordingly if JpsiWithRareStats; will make my life easier const bool NP = false; //TODO const bool Blind = fitData && !fitReference; const bool plotPulls = true; //Do you wanna pull plots with or without pulls? bool plotSignalRegion = fitReference; //Plot only the region around B mass, makes nicer plots const unsigned int nBins = UseBinnedFit ? opts.get_nQ2bins() : 1; if (fitToy && params.polarity!= 0){ spdlog::warn("Your polarity is not set to both ofr whatever reason when fitting a toy!"); spdlog::warn("Setting the polarity to 0!"); params.polarity = 0; } //------------------------------------------------------------------------------------------------ if((int)ConstrainedSwave + (int) ConstrainedFS + (int) LoadFSfrom2Dfit > 1){ //If at least two of those, then crash spdlog::error("Use only one option to constrain FS:"); spdlog::error("Constrained sWave" + boolToString(ConstrainedSwave)); spdlog::error("Constrained FS" + boolToString(ConstrainedFS)); spdlog::error("Load FS from 2D fit" + boolToString(LoadFSfrom2Dfit)); assert(0); } opts.fit_full_angular_bkg = true; //fold also the bkg? opts.individual_penalties = false; //If blinded, don't plot anything opts.write_eps = !Blind; opts.write_pdf = false; opts.write_C = false; //Fit both angles and mass opts.only_angles = false; opts.only_Bmass = false; //Fit swave? opts.swave = true; //Use weights opts.weighted_fit = true; //What framework? opts.shift_lh = false; opts.hesse_postrun = true; opts.squared_hesse = true;//params.folding>-1; opts.minos_errors = false;// params.folding==-1; //Flat background? opts.flat_bkg = false; //What order of bkg opts.bkg_order_costhetak = 5; opts.fit_mkpi = true; //Use fit to Kpi mass? opts.use_mkpi = false; //Do the 5D fit? //TODO: probably doesn't work now opts.simple_mkpi = false; opts.isobar = false; opts.asymptotic = false; //Use the improved hesse calculation? //Check that sWave is on when fitting Kpi mass assert(!(opts.fit_mkpi && !opts.swave)); assert(!(opts.use_mkpi && !opts.swave)); //Use angular acceptance corrections opts.update_efficiencies = true; //generate nametag for root file to save results: std::string results_file = final_result_name(fitReference, false, params, SimultaneousFit, nBins, params.Run, fitToy, fractionOfStats, NP, -1); //FitOnlyRun is not reflected in opts here TODO //Set the available PDFs: this depends on the selected Run for now, so if only one is selected, run over one pdf, if both, use two std::vector pdf_idx; if (params.Run == 1 || params.Run == 12) pdf_idx.push_back(1); if (params.Run == 2 || params.Run == 12) pdf_idx.push_back(2); //-------------------------------- // Load data //-------------------------------- spdlog::info("[FIT]\tLoading data..."); std::vector>events; if(fitToy){ events.push_back(fcnc::load_events(get_finalToys_file(fitReference,nBins, SimultaneousFit, params,1),"Events", -1)); //Run1 events.push_back(fcnc::load_events(get_finalToys_file(fitReference,nBins, SimultaneousFit, params,2),"Events", -1)); //Run2 } else{ std::vector tmp = fcnc::load_events(get_theFCNCpath(0,1), "Events", -1); //Run1 if (!fitReference) events.push_back(fcnc::filterResonances(tmp)); else events.push_back(tmp); tmp = fcnc::load_events(get_theFCNCpath(0,2), "Events", -1); //Run2 if (!fitReference) events.push_back(fcnc::filterResonances(tmp)); else events.push_back(tmp); } //check that the number of pdfs is the same as number of event vectors if (pdf_idx.size() != events.size()){ spdlog::error("Something went very wrong when loading the events and setting the pdfs."); spdlog::error("The number of PDFs != number of event vectors: {0:d} vs {1:d}",pdf_idx.size(), events.size()); return 5; } //we are good to go, now; how many individual pdfs are used? const UInt_t nPDFs = pdf_idx.size(); //Control print of the loaded events UInt_t N_tot = 0; for (UInt_t n = 0; n < nPDFs; n++){ spdlog::debug("Event vector {0:d}:\t"+(Blind?"Larger 1 ":std::to_string(events.at(n).size())),n); if (events.at(n).size()==0){ spdlog::error("Empty event vector!"); return 404; } N_tot += events.at(n).size(); } spdlog::info("Total number of used events:\t{0:d}", N_tot); //Set fit ranges //TODO: just move it to init angle parameters double angleRange = 1.0; double PprimeRangeScale = 10.0; if (params.usePprime) angleRange *= PprimeRangeScale; double angleStepSize = 0.1; //get signal fractions and event numbers //TODO: fix this when you know whether we need the eventNumbers here specifically double sig_frac[nBins]; unsigned int event_numbers[nBins][nPDFs]; for(unsigned int b = 0; b < nBins; b++){ for(unsigned int n = 0; n < nPDFs; n++){ //get event numbers and signal fraction from global function, given the total number of events //TODO: check what to do with JpsiWithRareStats EventNumbers(b, n, sig_frac[b], event_numbers[b][n], N_tot, nBins, nPDFs); event_numbers[b][n] *= fractionOfStats; } } //Control print for(unsigned int b = 0; b < nBins; b++){ for(unsigned int n = 0; n < nPDFs; n++){ spdlog::debug("q2bin={0:d}\tPDF={1:d}\tf_sig={2:f}\tN={3:d}", b, n, sig_frac[b], event_numbers[b][n]); } } //create the fitter, plotter, parameteres and pdfs fcnc::fitter f(&opts); fcnc::folder fldr(&opts); fcnc::options theOptions[nPDFs]; fcnc::bu2kstarmumu_plotter * thePlotter[nPDFs]; std::vector theParams [nBins]; std::vector theProbs [nBins]; std::vector< std::vector* > selection[nBins]; //these parameters are common: std::vector common_params = param_string(opts, false); //set MC to true to avoid background there spdlog::info("Shared parameters: " + convert_vector_to_string(common_params)); //set common parameters: if(SimultaneousFit) f.set_common_parameters(common_params); //Get the name of files needed for constraining std::string signalMCFile = final_result_name_MC(params, nBins, false, false, SimultaneousFit, false, false); std::string refrenceMCFile = final_result_name_MC(params, 1, true, false, SimultaneousFit, false, true); bool takeAngBkgFromRef = true; std::string upperMassBkgFile = final_result_name_bkg(takeAngBkgFromRef ? 1 : nBins, takeAngBkgFromRef, false, true, false, params); std::string upperMassKpiBkgFile = final_result_name_bkg(takeAngBkgFromRef ? 1 : nBins, takeAngBkgFromRef, false, true, true, params); std::string RefMassFile = final_result_name_mass(true,1,true,params,params.Run); for(UInt_t n = 0; n < nPDFs; n++){ spdlog::debug("PDF {0:d}", n); UInt_t idx = pdf_idx.at(n); opts.update_angle_ranges(); //Set angles in options back to defaults opts.update_efficiencies = true; //This ensures the acceptance weights to be taken into account. //Set the label if (fitToy) opts.plot_label = "Toy sample"; else opts.plot_label = "LHCb data"; opts.name = std::to_string(idx+1) + "_"+ get_eps_label(fitReference,false,false,fitToy, nBins, -1,-SimultaneousFit,params); //TODO: FIX THIS!!! //Set the options and init the plotter theOptions[n] = opts; theOptions[n].run = pdf_idx.at(n); //Set proper run to options thePlotter[n] = new fcnc::bu2kstarmumu_plotter(&theOptions[n]); for(unsigned int b = 0; b < nBins; b++){ theOptions[n].q2_min = theOptions[n].TheQ2binsmin.at(b) ; theOptions[n].q2_max = theOptions[n].TheQ2binsmax.at(b); //create parameter sets fcnc::bu2kstarmumu_parameters * leParameters = new fcnc::bu2kstarmumu_parameters(&theOptions[n]); //create PDFs fcnc::bu2kstarmumu_pdf * lePDF = new fcnc::bu2kstarmumu_pdf(&theOptions[n], leParameters); //define center of q2bin as effective q2 by hand leParameters->eff_q2.init_fixed(bin_center_q2(theOptions[n],b)); //Init mass std::string fileName_dataMass = final_result_name_mass(true, 1, true, params, params.Run); leParameters->init_Bmass(fileName_dataMass,pdf_idx.at(n),0.5,fixConstr(false,constrainBmass)); //initiate the sig/bkg fraction for mass fit leParameters->f_sig.init(fitReference ? 0.8 : 0.3, 0.0, 1.0, 0.05); //Init mass parameters leParameters->init_mass_parameters(n,nBins,b,0.01); leParameters->fix_param_from_rootfile(fitReference ? refrenceMCFile : signalMCFile, {"alpha_1","alpha_2","n_1","n_2"}, idx, b); //Fix the mass mean to the one from reference channel if (!fitReference) leParameters->init_Bmass(RefMassFile, pdf_idx.at(n), 0.0, fixConstr(true,false)); //Init the ratio of sigmas in signal MC/reference MC if (!fitReference) leParameters->m_scale.init_fixed(get_sigmaRatio_fromMC(params,nBins,b,pdf_idx.at(n))); else leParameters->m_scale.init_fixed(1.0); //Init mass background leParameters->init_mass_background_parameters(nBins,b,opts.fit_lambda); //(de)activate S-wave double swaveStepSize = opts.swave && !FixedSwave ? 0.1 : 0.0; //Init the Kstar mass fit if(opts.fit_mkpi || opts.use_mkpi){ //p-wave leParameters->init_mkpi_pWave_parameters(fitReference,0.0); leParameters->init_kpi_background_parameters(fitReference,0.05); //s-wave if (opts.swave) leParameters->init_mkpi_sWave_parameters(fitReference, 0.0); } //Init the sWave if (opts.swave){ leParameters->init_sWave_parameters(swaveStepSize); leParameters->get_param_from_rootfile(RefMassFile, {"FS"}, idx, b,fixConstr(!fitReference,ConstrainedFS)); } //Add angular parameters leParameters->init_angular_parameters(nBins,b,angleStepSize,angleRange, Blind); //Add background to angular observables if(!theOptions[n].flat_bkg){ //If bkg not flat, init based on upper mass sideband fit leParameters->get_param_from_rootfile(upperMassBkgFile, PAR_BKG_STRING(opts.folding,opts.bkg_order_costhetal,opts.bkg_order_costhetak), params.Run,b,fixConstr(FixAngularBckgnd,false)); if (params.folding ==4){ leParameters->cbkgctk2.init(-1.75,-3.0,1.5,0.05); leParameters->cbkgctk4.init(-1.75,-3.0,1.5,0.05); } } //make sure all configured values are also the start_value: leParameters->take_current_as_start(); //Add the parameters and the pdf into the vectors theParams[b].push_back(leParameters); theProbs [b].push_back(lePDF); spdlog::info("[PDF{0:d}]\tSaved PDF and parameters!", n); //Load the events std::vector * leEvents = new std::vector; //choose events from the event vector and sort corresponding to q2bin or non-resonant: //If use all events, just keep the numbers as they are UInt_t NNN = events[n].size()*fractionOfStats; // UInt_t NNN = fractionOfStats == 1 ? events[n].size() : 0.5 + event_numbers[b][n]; //This randomly pre-selected list of events is used, if not all events are wanted (eg if JpsiWithRareStats) std::vector event_idxs(NNN); if(fractionOfStats != 1.0){ event_idxs = fcnc::GetNOutOfM(NNN, events[n].size(), 0, false, false); } else{ std::iota (std::begin(event_idxs), std::end(event_idxs), 0); //This just puts 0,1,2,3,... into a vector } spdlog::info("Running fitter with a total number of events of: {0:d}", event_idxs.size()); for_indexed(auto e: event_idxs){ //if (i%2!=0) continue; fcnc::event meas = events[n].at(e); //Cut on B mass if(meas.m < B_MASS_LOW || meas.m > B_MASS_HIGH) continue; //Select either magUp or magDown if(params.polarity==1 && meas.magnet > 0) continue; if(params.polarity==-1 && meas.magnet < 0) continue; //Select only events in the given bin if(meas.q2 < theOptions[n].TheQ2binsmin.at(b) || meas.q2 > theOptions[n].TheQ2binsmax.at(b)) continue; //Cut on Kpi mass if(meas.mkpi < opts.mkpi_min || meas.mkpi > opts.mkpi_max) continue; //Fold if needed if(!filterFldFour(&meas, &theOptions[n])) continue; //remove ctk events if(!opts.full_angular) fldr.fold(&meas); leEvents->push_back(meas); } //Load the acceptance correction lePDF->load_coeffs_eff_phsp_4d(); lePDF->update_cached_normalization(leParameters); lePDF->update_cached_efficiencies(leParameters, leEvents); spdlog::info("[PDF{0:d}]\tFinished selecting the events: {1:d}",n, leEvents->size()); //save event vector in vector selection[b].push_back(leEvents); if(selection[b].back()->size() > 0){ spdlog::info("[PDF{0:d}]\t[BIN{1:d}]\tDone!", n, b); } else{ spdlog::critical("No events found for PDF={0:d} and q2-bin={1:d}. Exit!",n,b); assert(0); } } //end loop over bins theOptions[n].update_efficiencies = false; //Prevent multiple weights } //end loop over PDFs //Measure the time for the fit: runTime timer = runTime(); //Save the fit results std::vectorfit_results[nBins]; std::vectorf_sigs[nBins]; std::vectorf_sigserr[nBins]; std::vectorevts_cntr[nBins]; //-------------------------------- // FIT //-------------------------------- spdlog::info("[FIT]\tFit started."); for(unsigned int b = 0; b < nBins; b++){ //Start the clock timer.start(); time_t startTime = time(0); spdlog::info("[START]\tStart the fit for bin #{0:d}", b); //fit the events: const bool do_fit = !FeldmanCousin; //TODO: ask David wtf int fitresult = 0; if(do_fit){ if(!SimultaneousFit){ for(UInt_t n = 0; n < nPDFs; n++){ UInt_t idx = pdf_idx.at(n); spdlog::info("[FIT{0:d}]\tRunning the fitter...", idx); fitresult = f.fit(theProbs[b].at(n), theParams[b].at(n), selection[b].at(n)); fit_results[b].push_back(fitresult); } } else{ spdlog::info("[FIT]\tFitting the following events simultaenously:"); for(UInt_t s = 0; s < selection[b].size(); s++) spdlog::info("PDF #{0:d}: {1:d}", pdf_idx.at(s), selection[b].at(s)->size()); fitresult = f.fit(theProbs[b], theParams[b], selection[b]); fit_results[b].push_back(fitresult); spdlog::info("Q2BIN={0:d}\tLLH={1:f}", b, f.likelihood()); //run likelihood profile scan //TODO: fix and implement if(likelihoodScan) return 5; //run feldman cousins //TODO: fix and implement if(FeldmanCousin) return 5; } } else{ //in case of not fit, just update the pdfs....? for(UInt_t n = 0; n < nPDFs; n++){ theProbs[b].at(n)->update_cached_efficiencies(theParams[b].at(n), selection[b].at(n)); fit_results[b].push_back(fitresult); } } //Stop the clock timer.stop(startTime); //save signal fraction and event number for each bin and each pdf: for(UInt_t n = 0; n < nPDFs; n++){ f_sigs[b] .push_back(((fcnc::bu2kstarmumu_parameters *) theParams[b].at(n))->f_sig.get_value()); f_sigserr[b].push_back(((fcnc::bu2kstarmumu_parameters *) theParams[b].at(n))->f_sig.get_error()); evts_cntr[b].push_back(selection[b].at(n)->size()); } //plot each set of pdfs with the data points: bool do_plot = !(likelihoodScan || FeldmanCousin); if (do_plot){ for(UInt_t n = 0; n < nPDFs; n++){ std::string eps_label = get_eps_label(fitReference,false, false, fitToy, params.nBins, b, params.polarity, SimultaneousFit, theOptions[n]); theOptions[n].q2_label = q2_label(opts.TheQ2binsmin.at(b), opts.TheQ2binsmax.at(b)); spdlog::info("[PLOT]\t"+eps_label); thePlotter[n]->SetPulls(plotPulls); if (plotSignalRegion) thePlotter[n]->plot_data((fcnc::bu2kstarmumu_pdf*)theProbs[b].at(n), (fcnc::bu2kstarmumu_parameters*)theParams[b].at(n), selection[b].at(n), get_MainFitPlot_path(), eps_label, plotSignalRegion); thePlotter[n]->plot_data((fcnc::bu2kstarmumu_pdf*)theProbs[b].at(n), (fcnc::bu2kstarmumu_parameters*)theParams[b].at(n), selection[b].at(n), get_MainFitPlot_path(), eps_label+"_incBkg", false); } //plot both PDFs together std::vector * prober = (std::vector *) & theProbs[b]; std::string eps_label = get_eps_label(fitReference,false, false, fitToy, params.nBins, b, params.polarity, SimultaneousFit, opts); std::vector * paramser = (std::vector *) & theParams[b]; thePlotter[0]->SetPulls(plotPulls); if (plotSignalRegion) thePlotter[0]->plot_added_pdfs(prober, paramser, & selection[b], get_MainFitPlot_path(), eps_label, plotSignalRegion); //TODO add prefix to the plots thePlotter[0]->plot_added_pdfs(prober, paramser, & selection[b], get_MainFitPlot_path(), eps_label+"_incBkg", false); }//end do_plot }//end loop over bins for fitter if(likelihoodScan || FeldmanCousin) return 5; //Not implemented yet //-------------------------------- // Print & Save //-------------------------------- //Print running time timer.print(nBins); if (!Blind) print_all_parameters(nBins, pdf_idx, theParams, spdlog::level::debug); //Save the fit results into a txt file in case for(UInt_t n = 0; n < nPDFs; n++){ for(unsigned int b = 0; b < nBins; b++){ //Print all fit results for(unsigned int b = 0; b < nBins; b++){ spdlog::info("[BIN{0:d}]:\tFitresult: {1:d}", b, fit_results[n].at(b)); } std::string txtFile = get_MainFitResult_path() + "fitresult_" + get_eps_label(fitReference,false, false, fitToy, params.nBins, b, params.polarity, SimultaneousFit, theOptions[n])+ ".txt"; ((fcnc::bu2kstarmumu_parameters *) theParams[b].at(n))->save_param_values(txtFile); } } //Print signal yield in the terminal and to a tex file if (!Blind){ print_sig_yields(nBins, pdf_idx, evts_cntr, f_sigs, f_sigserr); print_bkg_yields(nBins, pdf_idx, evts_cntr, f_sigs, f_sigserr); } print_sig_yields_tex(get_eps_label(fitReference,false, false, fitToy, params.nBins, -1, params.polarity, SimultaneousFit, opts), nBins, pdf_idx, &opts, evts_cntr, f_sigs, f_sigserr); //saving results to root file TODO save_results(results_file,nBins,pdf_idx,fit_results,theParams,SimultaneousFit,&opts); spdlog::info("Finished fit."); return 0; }