{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "55cde081",
   "metadata": {},
   "source": [
    "# rf514_RooCustomizer\n",
    "Using the RooCustomizer to create multiple PDFs that share a lot of properties, but have unique parameters for each category.\n",
    "As an extra complication, some of the new parameters need to be functions of a mass parameter.\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "**Author:** Harshal Shende, Stephan Hageboeck (C++ version)  \n",
    "<i><small>This notebook tutorial was automatically generated with <a href= \"https://github.com/root-project/root/blob/master/documentation/doxygen/converttonotebook.py\">ROOTBOOK-izer</a> from the macro found in the ROOT repository  on Tuesday, May 19, 2026 at 08:32 PM.</small></i>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "095afe43",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:32:48.603309Z",
     "iopub.status.busy": "2026-05-19T20:32:48.603189Z",
     "iopub.status.idle": "2026-05-19T20:32:49.742683Z",
     "shell.execute_reply": "2026-05-19T20:32:49.742097Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[#0] WARNING:InputArguments -- The parameter 'sigmaG' with range [-inf, inf] of the RooGaussian 'gauss' exceeds the safe range of (0, inf). Advise to limit its range.\n",
      "The proto model before customisation:\n",
      "\n",
      "0x56324c29c5e0 RooAddPdf::model = 750.5/1 [Auto,Clean] \n",
      "  0x563248c54720/V- RooGaussian::gauss = 0 [Auto,Dirty] \n",
      "    0x56324b8c6180/V- RooRealVar::Energy = 1500\n",
      "    0x56324b8fd0d0/V- RooRealVar::meanG = 100\n",
      "    0x56324ba5c4e0/V- RooRealVar::sigmaG = 3\n",
      "  0x563248c07920/V- RooRealVar::yieldSig = 1\n",
      "  0x56324bbec8c0/V- RooPolynomial::linear = 1501 [Auto,Dirty] \n",
      "    0x56324b8c6180/V- RooRealVar::Energy = 1500\n",
      "    0x56324bae1d50/V- RooRealVar::pol1 = 1\n",
      "  0x56324bc36150/V- RooRealVar::yieldBkg = 1\n"
     ]
    }
   ],
   "source": [
    "import ROOT\n",
    "\n",
    "E = ROOT.RooRealVar(\"Energy\", \"Energy\", 0, 3000)\n",
    "\n",
    "meanG = ROOT.RooRealVar(\"meanG\", \"meanG\", 100.0, 0.0, 3000.0)\n",
    "sigmaG = ROOT.RooRealVar(\"sigmaG\", \"sigmaG\", 3.0)\n",
    "gauss = ROOT.RooGaussian(\"gauss\", \"gauss\", E, meanG, sigmaG)\n",
    "\n",
    "pol1 = ROOT.RooRealVar(\"pol1\", \"Constant of the polynomial\", 1, -10, 10)\n",
    "linear = ROOT.RooPolynomial(\"linear\", \"linear\", E, pol1)\n",
    "\n",
    "yieldSig = ROOT.RooRealVar(\"yieldSig\", \"yieldSig\", 1, 0, 1.0e4)\n",
    "yieldBkg = ROOT.RooRealVar(\"yieldBkg\", \"yieldBkg\", 1, 0, 1.0e4)\n",
    "\n",
    "model = ROOT.RooAddPdf(\"model\", \"S + B model\", [gauss, linear], [yieldSig, yieldBkg])\n",
    "\n",
    "print(\"The proto model before customisation:\\n\")\n",
    "model.Print(\"T\")  # \"T\" prints the model as a tree"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7dfcdccc",
   "metadata": {},
   "source": [
    "Build the categories"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "85a20c0a",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:32:49.744140Z",
     "iopub.status.busy": "2026-05-19T20:32:49.743999Z",
     "iopub.status.idle": "2026-05-19T20:32:49.861692Z",
     "shell.execute_reply": "2026-05-19T20:32:49.861136Z"
    }
   },
   "outputs": [],
   "source": [
    "sample = ROOT.RooCategory(\"sample\", \"sample\", {\"Sample1\": 1, \"Sample2\": 2, \"Sample3\": 3})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0977b2e8",
   "metadata": {},
   "source": [
    "Start to customise the proto model that was defined above.\n",
    "---------------------------------------------------------------------------"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "021daa54",
   "metadata": {},
   "source": [
    "We need two sets for bookkeeping of PDF nodes:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "65940213",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:32:49.863500Z",
     "iopub.status.busy": "2026-05-19T20:32:49.863374Z",
     "iopub.status.idle": "2026-05-19T20:32:49.974030Z",
     "shell.execute_reply": "2026-05-19T20:32:49.973489Z"
    }
   },
   "outputs": [],
   "source": [
    "newLeaves = ROOT.RooArgSet()\n",
    "allCustomiserNodes = ROOT.RooArgSet()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "793229b2",
   "metadata": {},
   "source": [
    "1. Each sample should have its own mean for the gaussian\n",
    "The customiser will make copies of `meanG` for each category.\n",
    "These will all appear in the set `newLeaves`, which will own the new nodes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "55646d5a",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:32:49.975704Z",
     "iopub.status.busy": "2026-05-19T20:32:49.975547Z",
     "iopub.status.idle": "2026-05-19T20:32:50.088984Z",
     "shell.execute_reply": "2026-05-19T20:32:50.088438Z"
    }
   },
   "outputs": [],
   "source": [
    "cust = ROOT.RooCustomizer(model, sample, newLeaves, allCustomiserNodes)\n",
    "cust.splitArg(meanG, sample)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0ca26fe7",
   "metadata": {},
   "source": [
    "2. Each sample should have its own signal yield, but there is an extra complication:\n",
    "We need the yields 1 and 2 to be a function of the variable \"mass\".\n",
    "For this, we pre-define nodes with exactly the names that the customiser would have created automatically,\n",
    "that is, \"<nodeName>_<categoryName>\", and we register them in the set of customiser nodes.\n",
    "The customiser will pick them up instead of creating new ones.\n",
    "If we don't provide one (e.g. for \"yieldSig_Sample3\"), it will be created automatically by cloning `yieldSig`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "35874bfc",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:32:50.091024Z",
     "iopub.status.busy": "2026-05-19T20:32:50.090888Z",
     "iopub.status.idle": "2026-05-19T20:32:50.221483Z",
     "shell.execute_reply": "2026-05-19T20:32:50.220958Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mass = ROOT.RooRealVar(\"M\", \"M\", 1, 0, 12000)\n",
    "yield1 = ROOT.RooFormulaVar(\"yieldSig_Sample1\", \"Signal yield in the first sample\", \"M/3.360779\", mass)\n",
    "yield2 = ROOT.RooFormulaVar(\"yieldSig_Sample2\", \"Signal yield in the second sample\", \"M/2\", mass)\n",
    "allCustomiserNodes.add(yield1)\n",
    "allCustomiserNodes.add(yield2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e680f0d7",
   "metadata": {},
   "source": [
    "Instruct the customiser to replace all yieldSig nodes for each sample:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "e49b025e",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:32:50.223109Z",
     "iopub.status.busy": "2026-05-19T20:32:50.222981Z",
     "iopub.status.idle": "2026-05-19T20:32:50.326293Z",
     "shell.execute_reply": "2026-05-19T20:32:50.325792Z"
    }
   },
   "outputs": [],
   "source": [
    "cust.splitArg(yieldSig, sample)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a764d568",
   "metadata": {},
   "source": [
    "Now we can start building the PDFs for all categories:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "75502396",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:32:50.328471Z",
     "iopub.status.busy": "2026-05-19T20:32:50.328339Z",
     "iopub.status.idle": "2026-05-19T20:32:50.437253Z",
     "shell.execute_reply": "2026-05-19T20:32:50.436727Z"
    }
   },
   "outputs": [],
   "source": [
    "pdf1 = cust.build(\"Sample1\")\n",
    "pdf2 = cust.build(\"Sample2\")\n",
    "pdf3 = cust.build(\"Sample3\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4f4c03e5",
   "metadata": {},
   "source": [
    "And we inspect the two PDFs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "897663fd",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:32:50.439020Z",
     "iopub.status.busy": "2026-05-19T20:32:50.438893Z",
     "iopub.status.idle": "2026-05-19T20:32:50.549185Z",
     "shell.execute_reply": "2026-05-19T20:32:50.548636Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "PDF 1 with a yield depending on M:\n",
      "\n",
      "\n",
      "PDF 2 with a yield depending on M:\n",
      "\n",
      "\n",
      "PDF 3 with a free yield:\n",
      "\n",
      "\n",
      "The following leaves have been created automatically while customising:\n",
      "\n",
      "0x56324cebb140 RooAddPdf::model_Sample1 = 1156.8/1 [Auto,Clean] \n",
      "  0x56324cee63e0/V- RooGaussian::gauss_Sample1 = 0 [Auto,Dirty] \n",
      "    0x56324b8c6180/V- RooRealVar::Energy = 1500\n",
      "    0x56324ce13c00/V- RooRealVar::meanG_Sample1 = 100\n",
      "    0x56324ba5c4e0/V- RooRealVar::sigmaG = 3\n",
      "  0x56324bc14e30/V- RooFormulaVar::yieldSig_Sample1 = 0.29755 [Auto,Clean] \n",
      "    0x56324ccdbc90/V- RooRealVar::M = 1\n",
      "  0x56324bbec8c0/V- RooPolynomial::linear = 1501 [Auto,Dirty] \n",
      "    0x56324b8c6180/V- RooRealVar::Energy = 1500\n",
      "    0x56324bae1d50/V- RooRealVar::pol1 = 1\n",
      "  0x56324bc36150/V- RooRealVar::yieldBkg = 1\n",
      "0x56324cee3860 RooAddPdf::model_Sample2 = 1000.67/1 [Auto,Clean] \n",
      "  0x56324cedfef0/V- RooGaussian::gauss_Sample2 = 0 [Auto,Dirty] \n",
      "    0x56324b8c6180/V- RooRealVar::Energy = 1500\n",
      "    0x56324ced3d80/V- RooRealVar::meanG_Sample2 = 100\n",
      "    0x56324ba5c4e0/V- RooRealVar::sigmaG = 3\n",
      "  0x56324ce83fd0/V- RooFormulaVar::yieldSig_Sample2 = 0.5 [Auto,Clean] \n",
      "    0x56324ccdbc90/V- RooRealVar::M = 1\n",
      "  0x56324bbec8c0/V- RooPolynomial::linear = 1501 [Auto,Dirty] \n",
      "    0x56324b8c6180/V- RooRealVar::Energy = 1500\n",
      "    0x56324bae1d50/V- RooRealVar::pol1 = 1\n",
      "  0x56324bc36150/V- RooRealVar::yieldBkg = 1\n",
      "0x56324ced07e0 RooAddPdf::model_Sample3 = 750.5/1 [Auto,Clean] \n",
      "  0x56324b0bf580/V- RooGaussian::gauss_Sample3 = 0 [Auto,Dirty] \n",
      "    0x56324b8c6180/V- RooRealVar::Energy = 1500\n",
      "    0x56324ce9f190/V- RooRealVar::meanG_Sample3 = 100\n",
      "    0x56324ba5c4e0/V- RooRealVar::sigmaG = 3\n",
      "  0x56324cb1b910/V- RooRealVar::yieldSig_Sample3 = 1\n",
      "  0x56324bbec8c0/V- RooPolynomial::linear = 1501 [Auto,Dirty] \n",
      "    0x56324b8c6180/V- RooRealVar::Energy = 1500\n",
      "    0x56324bae1d50/V- RooRealVar::pol1 = 1\n",
      "  0x56324bc36150/V- RooRealVar::yieldBkg = 1\n",
      "  1) RooRealVar::    meanG_Sample1 = 100\n",
      "  2) RooRealVar::    meanG_Sample2 = 100\n",
      "  3) RooRealVar::    meanG_Sample3 = 100\n",
      "  4) RooRealVar:: yieldSig_Sample3 = 1\n"
     ]
    }
   ],
   "source": [
    "print(\"\\nPDF 1 with a yield depending on M:\\n\")\n",
    "pdf1.Print(\"T\")\n",
    "print(\"\\nPDF 2 with a yield depending on M:\\n\")\n",
    "pdf2.Print(\"T\")\n",
    "print(\"\\nPDF 3 with a free yield:\\n\")\n",
    "pdf3.Print(\"T\")\n",
    "\n",
    "print(\"\\nThe following leaves have been created automatically while customising:\\n\")\n",
    "newLeaves.Print(\"V\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "db53ab94",
   "metadata": {},
   "source": [
    " If we needed to set reasonable values for the means of the gaussians, this could be done as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "b3c23ce7",
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2026-05-19T20:32:50.550586Z",
     "iopub.status.busy": "2026-05-19T20:32:50.550462Z",
     "iopub.status.idle": "2026-05-19T20:32:50.670948Z",
     "shell.execute_reply": "2026-05-19T20:32:50.670379Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "The following leaves have been used while customising\n",
      "\t(partial overlap with the set of automatically created leaves.\n",
      "\ta new customiser for a different PDF could reuse them if necessary.):\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "  1) RooFormulaVar:: yieldSig_Sample1 = 0.29755\n",
      "  2) RooFormulaVar:: yieldSig_Sample2 = 0.5\n",
      "  3) RooRealVar::    meanG_Sample1 = 200\n",
      "  4) RooRealVar::    meanG_Sample2 = 300\n",
      "  5) RooRealVar::    meanG_Sample3 = 100\n",
      "  6) RooRealVar:: yieldSig_Sample3 = 1\n"
     ]
    }
   ],
   "source": [
    "meanG1 = allCustomiserNodes[\"meanG_Sample1\"]\n",
    "meanG1.setVal(200)\n",
    "meanG2 = allCustomiserNodes[\"meanG_Sample2\"]\n",
    "meanG2.setVal(300)\n",
    "\n",
    "print(\n",
    "    \"\\nThe following leaves have been used while customising\\n\\t(partial overlap with the set of automatically created leaves.\\n\\ta new customiser for a different PDF could reuse them if necessary.):\"\n",
    ")\n",
    "allCustomiserNodes.Print(\"V\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
