{ "cells": [ { "cell_type": "markdown", "id": "5dcf5e4d", "metadata": {}, "source": [ "# fit2dHist\n", "Example to fit two histograms at the same time via the Fitter class.\n", "\n", "To execute this tutorial, you can do:\n", "\n", "```cpp\n", "root > .x fit2dHist.C (executing via cling, slow)\n", "```\n", "\n", " or\n", "```cpp\n", "root > .x fit2dHist.C+ (executing via ACLIC , fast, with Minuit)\n", "root > .x fit2dHist.C+(2) (executing via ACLIC , fast, with Minuit2)\n", "```\n", "\n", " or using the option to fit independently the 2 histos\n", "```cpp\n", "root > .x fit2dHist.C+(10) (via ACLIC, fast, independent fits with Minuit)\n", "root > .x fit2dHist.C+(12) (via ACLIC, fast, independent fits with Minuit2)\n", "```\n", "\n", "Note that you can also execute this script in batch with eg,\n", "```cpp\n", " root -b -q \"fit2dHist.C+(12)\"\n", "```\n", "\n", "or execute interactively from the shell\n", "```cpp\n", " root fit2dHist.C+\n", " root \"fit2dHist.C+(12)\"\n", "```\n", "\n", "\n", "\n", "\n", "**Author:** : Lorenzo Moneta, Rene Brun 18/01/2006 \n", "This notebook tutorial was automatically generated with ROOTBOOK-izer from the macro found in the ROOT repository on Tuesday, May 19, 2026 at 08:24 PM." ] }, { "cell_type": "code", "execution_count": 1, "id": "0d54ccc0", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:44.106385Z", "iopub.status.busy": "2026-05-19T20:24:44.106275Z", "iopub.status.idle": "2026-05-19T20:24:44.111962Z", "shell.execute_reply": "2026-05-19T20:24:44.111679Z" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "input_line_44:31:44: error: use of undeclared identifier 'my2Dfunc'\n", " tmp = (h1->GetBinContent(ix,iy) - my2Dfunc(x,(double*) p))/h1->GetBinError(ix,iy);\n", " ^\n", "input_line_44:42:44: error: use of undeclared identifier 'my2Dfunc'\n", " tmp = (h2->GetBinContent(ix,iy) - my2Dfunc(x,(double *) p))/h2->GetBinError(ix,iy);\n", " ^\n" ] } ], "source": [ "%%cpp -d\n", "class MyFcn {\n", " public:\n", " TH2D *h1 = nullptr;\n", " TH2D *h2 = nullptr;\n", " int npfits = 0;\n", "\n", " MyFcn(TH2D * _h1, TH2D * _h2) :\n", " h1(_h1), h2(_h2) {}\n", "\n", " double operator()(const double *p) {\n", "\n", " TAxis *xaxis1 = h1->GetXaxis();\n", " TAxis *yaxis1 = h1->GetYaxis();\n", " TAxis *xaxis2 = h2->GetXaxis();\n", " TAxis *yaxis2 = h2->GetYaxis();\n", "\n", " int nbinX1 = h1->GetNbinsX();\n", " int nbinY1 = h1->GetNbinsY();\n", " int nbinX2 = h2->GetNbinsX();\n", " int nbinY2 = h2->GetNbinsY();\n", "\n", " double chi2 = 0;\n", " double x[2];\n", " double tmp;\n", " npfits = 0;\n", " for (int ix = 1; ix <= nbinX1; ++ix) {\n", " x[0] = xaxis1->GetBinCenter(ix);\n", " for (int iy = 1; iy <= nbinY1; ++iy) {\n", " if ( h1->GetBinError(ix,iy) > 0 ) {\n", " x[1] = yaxis1->GetBinCenter(iy);\n", " tmp = (h1->GetBinContent(ix,iy) - my2Dfunc(x,(double*) p))/h1->GetBinError(ix,iy);\n", " chi2 += tmp*tmp;\n", " npfits++;\n", " }\n", " }\n", " }\n", " for (int ix = 1; ix <= nbinX2; ++ix) {\n", " x[0] = xaxis2->GetBinCenter(ix);\n", " for (int iy = 1; iy <= nbinY2; ++iy) {\n", " if ( h2->GetBinError(ix,iy) > 0 ) {\n", " x[1] = yaxis2->GetBinCenter(iy);\n", " tmp = (h2->GetBinContent(ix,iy) - my2Dfunc(x,(double *) p))/h2->GetBinError(ix,iy);\n", " chi2 += tmp*tmp;\n", " npfits++;\n", " }\n", " }\n", " }\n", " return chi2;\n", "}\n", "};" ] }, { "cell_type": "markdown", "id": "a7d2b139", "metadata": {}, "source": [ " Definition of a helper function: " ] }, { "cell_type": "code", "execution_count": 2, "id": "42747a61", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:44.131364Z", "iopub.status.busy": "2026-05-19T20:24:44.131230Z", "iopub.status.idle": "2026-05-19T20:24:44.134622Z", "shell.execute_reply": "2026-05-19T20:24:44.134331Z" } }, "outputs": [], "source": [ "%%cpp -d\n", "\n", "#include \"TH2D.h\"\n", "#include \"TF2.h\"\n", "#include \"TCanvas.h\"\n", "#include \"TStyle.h\"\n", "#include \"TRandom3.h\"\n", "#include \"Fit/Fitter.h\"\n", "#include \"TList.h\"\n", "\n", "#include \n", "\n", "double gauss2D(double *x, double *par) {\n", " double z1 = double((x[0]-par[1])/par[2]);\n", " double z2 = double((x[1]-par[3])/par[4]);\n", " return par[0]*exp(-0.5*(z1*z1+z2*z2));\n", "}" ] }, { "cell_type": "markdown", "id": "7890e258", "metadata": {}, "source": [ " Definition of a helper function: " ] }, { "cell_type": "code", "execution_count": 3, "id": "fdbca3a9", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:44.144065Z", "iopub.status.busy": "2026-05-19T20:24:44.143932Z", "iopub.status.idle": "2026-05-19T20:24:44.146050Z", "shell.execute_reply": "2026-05-19T20:24:44.145763Z" } }, "outputs": [], "source": [ "%%cpp -d\n", "double my2Dfunc(double *x, double *par) {\n", " return gauss2D(x,&par[0]) + gauss2D(x,&par[5]);\n", "}" ] }, { "cell_type": "markdown", "id": "a4b8fcff", "metadata": {}, "source": [ " Definition of a helper function: " ] }, { "cell_type": "code", "execution_count": 4, "id": "8d8d0c7b", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:44.155315Z", "iopub.status.busy": "2026-05-19T20:24:44.155199Z", "iopub.status.idle": "2026-05-19T20:24:44.161740Z", "shell.execute_reply": "2026-05-19T20:24:44.161342Z" } }, "outputs": [], "source": [ "%%cpp -d\n", "\n", "void FillHisto(TH2D * h, int n, double * p) {\n", "\n", "\n", " const double mx1 = p[1];\n", " const double my1 = p[3];\n", " const double sx1 = p[2];\n", " const double sy1 = p[4];\n", " const double mx2 = p[6];\n", " const double my2 = p[8];\n", " const double sx2 = p[7];\n", " const double sy2 = p[9];\n", " //const double w1 = p[0]*sx1*sy1/(p[5]*sx2*sy2);\n", " const double w1 = 0.5;\n", "\n", " double x, y;\n", " for (int i = 0; i < n; ++i) {\n", " // generate randoms with larger Gaussians\n", " gRandom->Rannor(x,y);\n", "\n", " double r = gRandom->Rndm(1);\n", " if (r < w1) {\n", " x = x*sx1 + mx1;\n", " y = y*sy1 + my1;\n", " }\n", " else {\n", " x = x*sx2 + mx2;\n", " y = y*sy2 + my2;\n", " }\n", " h->Fill(x,y);\n", "\n", " }\n", "}" ] }, { "cell_type": "markdown", "id": "09dd8317", "metadata": {}, "source": [ " Arguments are defined. " ] }, { "cell_type": "code", "execution_count": 5, "id": "2ba03634", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:44.162859Z", "iopub.status.busy": "2026-05-19T20:24:44.162748Z", "iopub.status.idle": "2026-05-19T20:24:44.489938Z", "shell.execute_reply": "2026-05-19T20:24:44.489446Z" } }, "outputs": [], "source": [ "int option=1;" ] }, { "cell_type": "markdown", "id": "127dc955", "metadata": {}, "source": [ "create two histograms" ] }, { "cell_type": "code", "execution_count": 6, "id": "8596024b", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:44.491547Z", "iopub.status.busy": "2026-05-19T20:24:44.491433Z", "iopub.status.idle": "2026-05-19T20:24:44.710925Z", "shell.execute_reply": "2026-05-19T20:24:44.705243Z" } }, "outputs": [], "source": [ "int nbx1 = 50;\n", "int nby1 = 50;\n", "int nbx2 = 50;\n", "int nby2 = 50;\n", "double xlow1 = 0.;\n", "double ylow1 = 0.;\n", "double xup1 = 10.;\n", "double yup1 = 10.;\n", "double xlow2 = 5.;\n", "double ylow2 = 5.;\n", "double xup2 = 20.;\n", "double yup2 = 20.;\n", "\n", "auto h1 = new TH2D(\"h1\",\"core\",nbx1,xlow1,xup1,nby1,ylow1,yup1);\n", "auto h2 = new TH2D(\"h2\",\"tails\",nbx2,xlow2,xup2,nby2,ylow2,yup2);\n", "\n", "double iniParams[10] = { 100, 6., 2., 7., 3, 100, 12., 3., 11., 2. };" ] }, { "cell_type": "markdown", "id": "f7275558", "metadata": {}, "source": [ "create fit function" ] }, { "cell_type": "code", "execution_count": 7, "id": "ca32064a", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:44.717374Z", "iopub.status.busy": "2026-05-19T20:24:44.717241Z", "iopub.status.idle": "2026-05-19T20:24:44.923354Z", "shell.execute_reply": "2026-05-19T20:24:44.922714Z" } }, "outputs": [], "source": [ "TF2 * func = new TF2(\"func\",my2Dfunc,xlow2,xup2,ylow2,yup2, 10);\n", "func->SetParameters(iniParams);" ] }, { "cell_type": "markdown", "id": "c760fc7a", "metadata": {}, "source": [ "fill Histos" ] }, { "cell_type": "code", "execution_count": 8, "id": "71cd3934", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:44.925537Z", "iopub.status.busy": "2026-05-19T20:24:44.925412Z", "iopub.status.idle": "2026-05-19T20:24:45.279782Z", "shell.execute_reply": "2026-05-19T20:24:45.278549Z" } }, "outputs": [], "source": [ "int n1 = 1000000;\n", "int n2 = 1000000;\n", "FillHisto(h1,n1,iniParams);\n", "FillHisto(h2,n2,iniParams);" ] }, { "cell_type": "markdown", "id": "d6319929", "metadata": {}, "source": [ "scale histograms to same heights (for fitting)" ] }, { "cell_type": "code", "execution_count": 9, "id": "712d5ccd", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:45.281462Z", "iopub.status.busy": "2026-05-19T20:24:45.281337Z", "iopub.status.idle": "2026-05-19T20:24:45.483798Z", "shell.execute_reply": "2026-05-19T20:24:45.483126Z" } }, "outputs": [], "source": [ "double dx1 = (xup1-xlow1)/double(nbx1);\n", "double dy1 = (yup1-ylow1)/double(nby1);\n", "double dx2 = (xup2-xlow2)/double(nbx2);\n", "double dy2 = (yup2-ylow2)/double(nby2);" ] }, { "cell_type": "markdown", "id": "3589f11d", "metadata": {}, "source": [ "scale histo 2 to scale of 1" ] }, { "cell_type": "code", "execution_count": 10, "id": "c33713c6", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:45.485857Z", "iopub.status.busy": "2026-05-19T20:24:45.485736Z", "iopub.status.idle": "2026-05-19T20:24:45.701431Z", "shell.execute_reply": "2026-05-19T20:24:45.695668Z" } }, "outputs": [], "source": [ "h2->Sumw2();\n", "h2->Scale( ( double(n1) * dx1 * dy1 ) / ( double(n2) * dx2 * dy2 ) );\n", "\n", "bool global = false;\n", "if (option > 10) global = true;" ] }, { "cell_type": "markdown", "id": "c3072c83", "metadata": {}, "source": [ "do global combined fit" ] }, { "cell_type": "code", "execution_count": 11, "id": "ffe7f921", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:45.715026Z", "iopub.status.busy": "2026-05-19T20:24:45.714890Z", "iopub.status.idle": "2026-05-19T20:24:45.916916Z", "shell.execute_reply": "2026-05-19T20:24:45.916621Z" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "input_line_62:10:4: error: unknown type name 'MyFcn'\n", " MyFcn myFcn(h1,h2);\n", " ^\n" ] } ], "source": [ "if (global) {\n", " // fill data structure for fit (coordinates + values + errors)\n", " std::cout << \"Do global fit\" << std::endl;\n", " // fit now all the function together\n", "\n", " ROOT::Fit::Fitter fitter;\n", " //The default minimizer is Minuit, you can also try Minuit2\n", "\n", " MyFcn myFcn(h1,h2);\n", " fitter.SetFCN(10, myFcn);\n", " if (option%10 == 2) fitter.Config().SetMinimizer(\"Minuit2\");\n", "\n", " // set parameter initial value, name and step size\n", " for (int i = 0; i < 10; ++i) {\n", " fitter.Config().ParSettings(i) = ROOT::Fit::ParameterSettings(func->GetParName(i), func->GetParameter(i), 0.01);\n", " }\n", "\n", " bool ret = fitter.FitFCN();\n", " if (!ret) {\n", " Error(\"fit2DHist\",\"Fit Failed to converge\");\n", " return -1;\n", " }\n", "\n", " //get result\n", " double minParams[10];\n", " double parErrors[10];\n", " for (int i = 0; i < 10; ++i) {\n", " minParams[i] = fitter.Result().Parameter(i);\n", " parErrors[i] = fitter.Result().Error(i);\n", " }\n", " double chi2 = fitter.Result().MinFcnValue();\n", " double edm = fitter.Result().Edm();\n", " int npfits = myFcn.npfits;\n", "\n", " func->SetParameters(minParams);\n", " func->SetParErrors(parErrors);\n", " func->SetChisquare(chi2);\n", " int ndf = npfits - fitter.Result().NFreeParameters();\n", " func->SetNDF(ndf);\n", "\n", " // add to list of functions\n", " h1->GetListOfFunctions()->Add(func);\n", " h2->GetListOfFunctions()->Add(func);\n", "}\n", "else {\n", " // fit independently\n", " h1->Fit(func, \"0\");\n", " h2->Fit(func, \"0\");\n", "}" ] }, { "cell_type": "markdown", "id": "c6b7d588", "metadata": {}, "source": [ "Create a new canvas." ] }, { "cell_type": "code", "execution_count": 12, "id": "2591b8f4", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:45.941559Z", "iopub.status.busy": "2026-05-19T20:24:45.941418Z", "iopub.status.idle": "2026-05-19T20:24:46.266182Z", "shell.execute_reply": "2026-05-19T20:24:46.265617Z" } }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "
\n", "
\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "TCanvas * c1 = new TCanvas(\"c1\",\"Two HIstogram Fit example\",100,10,900,800);\n", "c1->Divide(2,2);\n", "gStyle->SetOptFit();\n", "gStyle->SetStatY(0.6);\n", "\n", "c1->cd(1);\n", "h1->Draw();\n", "func->SetRange(xlow1,ylow1,xup1,yup1);\n", "func->DrawCopy(\"cont3 same\");\n", "c1->cd(2);\n", "h1->Draw(\"lego\");\n", "func->DrawCopy(\"surf1 same\");\n", "c1->cd(3);\n", "func->SetRange(xlow2,ylow2,xup2,yup2);\n", "h2->Draw();\n", "func->DrawCopy(\"cont3 same\");\n", "c1->cd(4);\n", "h2->Draw(\"lego\");\n", "gPad->SetLogz();\n", "func->Draw(\"surf1 same\");\n", "\n", "return 0;" ] }, { "cell_type": "markdown", "id": "eff07ffe", "metadata": {}, "source": [ "Draw all canvases " ] }, { "cell_type": "code", "execution_count": 13, "id": "24925018", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:46.269738Z", "iopub.status.busy": "2026-05-19T20:24:46.269585Z", "iopub.status.idle": "2026-05-19T20:24:46.476271Z", "shell.execute_reply": "2026-05-19T20:24:46.475521Z" } }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "
\n", "
\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "gROOT->GetListOfCanvases()->Draw()" ] } ], "metadata": { "kernelspec": { "display_name": "ROOT C++", "language": "c++", "name": "root" }, "language_info": { "codemirror_mode": "text/x-c++src", "file_extension": ".C", "mimetype": " text/x-c++src", "name": "c++" } }, "nbformat": 4, "nbformat_minor": 5 }