{
"cells": [
{
"cell_type": "markdown",
"id": "df9c6f2b",
"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\n",
"of a mass parameter.\n",
"\n",
"\n",
"\n",
"\n",
"**Author:** Stephan Hageboeck, CERN \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:32 PM."
]
},
{
"cell_type": "markdown",
"id": "6a7b01df",
"metadata": {},
"source": [
"Define a proto model that will be used as the template for each category\n",
"---------------------------------------------------------------------------"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "afa692b3",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:32:47.916808Z",
"iopub.status.busy": "2026-05-19T20:32:47.916694Z",
"iopub.status.idle": "2026-05-19T20:32:48.483999Z",
"shell.execute_reply": "2026-05-19T20:32:48.483303Z"
}
},
"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",
"0x7f5d86d75218 RooAddPdf::model = 750.5/1 [Auto,Clean] \n",
" 0x7f5d86d73bc0/V- RooGaussian::gauss = 0 [Auto,Dirty] \n",
" 0x7f5d86d73008/V- RooRealVar::Energy = 1500\n",
" 0x7f5d86d733f0/V- RooRealVar::meanG = 100\n",
" 0x7f5d86d737d8/V- RooRealVar::sigmaG = 3\n",
" 0x7f5d86d74a48/V- RooRealVar::yieldSig = 1\n",
" 0x7f5d86d74500/V- RooPolynomial::linear = 1501 [Auto,Dirty] \n",
" 0x7f5d86d73008/V- RooRealVar::Energy = 1500\n",
" 0x7f5d86d74118/V- RooRealVar::pol1 = 1\n",
" 0x7f5d86d74e30/V- RooRealVar::yieldBkg = 1\n"
]
}
],
"source": [
"RooRealVar E(\"Energy\",\"Energy\",0,3000);\n",
"\n",
"RooRealVar meanG(\"meanG\",\"meanG\", 100., 0., 3000.);\n",
"RooRealVar sigmaG(\"sigmaG\",\"sigmaG\", 3.);\n",
"RooGaussian gauss(\"gauss\", \"gauss\", E, meanG, sigmaG);\n",
"\n",
"RooRealVar pol1(\"pol1\", \"Constant of the polynomial\", 1, -10, 10);\n",
"RooPolynomial linear(\"linear\", \"linear\", E, pol1);\n",
"\n",
"RooRealVar yieldSig(\"yieldSig\", \"yieldSig\", 1, 0, 1.E4);\n",
"RooRealVar yieldBkg(\"yieldBkg\", \"yieldBkg\", 1, 0, 1.E4);\n",
"\n",
"RooAddPdf model(\"model\", \"S + B model\",\n",
" RooArgList(gauss,linear),\n",
" RooArgList(yieldSig, yieldBkg));\n",
"\n",
"std::cout << \"The proto model before customisation:\" << std::endl;\n",
"model.Print(\"T\"); // \"T\" prints the model as a tree"
]
},
{
"cell_type": "markdown",
"id": "c563ce92",
"metadata": {},
"source": [
"Build the categories"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "feebd945",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:32:48.485446Z",
"iopub.status.busy": "2026-05-19T20:32:48.485327Z",
"iopub.status.idle": "2026-05-19T20:32:48.714855Z",
"shell.execute_reply": "2026-05-19T20:32:48.697374Z"
}
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"input_line_53:2:2: warning: 'sample' shadows a declaration with the same name in the 'std' namespace; use '::sample' to reference this declaration\n",
" RooCategory sample(\"sample\",\"sample\");\n",
" ^\n"
]
}
],
"source": [
"RooCategory sample(\"sample\",\"sample\");\n",
"sample[\"Sample1\"] = 1;\n",
"sample[\"Sample2\"] = 2;\n",
"sample[\"Sample3\"] = 3;"
]
},
{
"cell_type": "markdown",
"id": "69baf675",
"metadata": {},
"source": [
"Start to customise the proto model that was defined above.\n",
"---------------------------------------------------------------------------"
]
},
{
"cell_type": "markdown",
"id": "67b0afa5",
"metadata": {},
"source": [
"We need two sets for bookkeeping of PDF nodes:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "0e030827",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:32:48.716620Z",
"iopub.status.busy": "2026-05-19T20:32:48.716480Z",
"iopub.status.idle": "2026-05-19T20:32:48.922356Z",
"shell.execute_reply": "2026-05-19T20:32:48.921751Z"
}
},
"outputs": [],
"source": [
"RooArgSet newLeaves; // This set collects leaves that are created in the process.\n",
"RooArgSet allCustomiserNodes; // This set lists leaves that have been used in a replacement operation."
]
},
{
"cell_type": "markdown",
"id": "0480a786",
"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": "d4fec911",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:32:48.923989Z",
"iopub.status.busy": "2026-05-19T20:32:48.923870Z",
"iopub.status.idle": "2026-05-19T20:32:49.133197Z",
"shell.execute_reply": "2026-05-19T20:32:49.132623Z"
}
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"input_line_55:2:28: error: reference to 'sample' is ambiguous\n",
" RooCustomizer cust(model, sample, newLeaves, &allCustomiserNodes);\n",
" ^\n",
"input_line_53:2:14: note: candidate found by name lookup is 'sample'\n",
" RooCategory sample(\"sample\",\"sample\");\n",
" ^\n",
"/usr/lib/gcc/x86_64-redhat-linux/14/../../../../include/c++/14/bits/stl_algo.h:5826:5: note: candidate found by name lookup is 'std::sample'\n",
" sample(_PopulationIterator __first, _PopulationIterator __last,\n",
" ^\n",
"input_line_55:3:22: error: reference to 'sample' is ambiguous\n",
"cust.splitArg(meanG, sample);\n",
" ^\n",
"input_line_53:2:14: note: candidate found by name lookup is 'sample'\n",
" RooCategory sample(\"sample\",\"sample\");\n",
" ^\n",
"/usr/lib/gcc/x86_64-redhat-linux/14/../../../../include/c++/14/bits/stl_algo.h:5826:5: note: candidate found by name lookup is 'std::sample'\n",
" sample(_PopulationIterator __first, _PopulationIterator __last,\n",
" ^\n"
]
}
],
"source": [
"RooCustomizer cust(model, sample, newLeaves, &allCustomiserNodes);\n",
"cust.splitArg(meanG, sample);"
]
},
{
"cell_type": "markdown",
"id": "7408cad4",
"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, \"_\", 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": "8ceb1cf2",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:32:49.134599Z",
"iopub.status.busy": "2026-05-19T20:32:49.134483Z",
"iopub.status.idle": "2026-05-19T20:32:49.343130Z",
"shell.execute_reply": "2026-05-19T20:32:49.342497Z"
}
},
"outputs": [],
"source": [
"RooRealVar mass(\"M\", \"M\", 1, 0, 12000);\n",
"RooFormulaVar yield1(\"yieldSig_Sample1\", \"Signal yield in the first sample\", \"M/3.360779\", mass);\n",
"RooFormulaVar yield2(\"yieldSig_Sample2\", \"Signal yield in the second sample\", \"M/2\", mass);\n",
"allCustomiserNodes.add(yield1);\n",
"allCustomiserNodes.add(yield2);"
]
},
{
"cell_type": "markdown",
"id": "48bb7e63",
"metadata": {},
"source": [
"Instruct the customiser to replace all yieldSig nodes for each sample:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "7cc75a96",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:32:49.344955Z",
"iopub.status.busy": "2026-05-19T20:32:49.344837Z",
"iopub.status.idle": "2026-05-19T20:32:49.553700Z",
"shell.execute_reply": "2026-05-19T20:32:49.553137Z"
}
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"input_line_69:2:26: error: reference to 'sample' is ambiguous\n",
" cust.splitArg(yieldSig, sample);\n",
" ^\n",
"input_line_53:2:14: note: candidate found by name lookup is 'sample'\n",
" RooCategory sample(\"sample\",\"sample\");\n",
" ^\n",
"/usr/lib/gcc/x86_64-redhat-linux/14/../../../../include/c++/14/bits/stl_algo.h:5826:5: note: candidate found by name lookup is 'std::sample'\n",
" sample(_PopulationIterator __first, _PopulationIterator __last,\n",
" ^\n"
]
}
],
"source": [
"cust.splitArg(yieldSig, sample);"
]
},
{
"cell_type": "markdown",
"id": "053bb15a",
"metadata": {},
"source": [
"Now we can start building the PDFs for all categories:"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "e35eb14e",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:32:49.555257Z",
"iopub.status.busy": "2026-05-19T20:32:49.555147Z",
"iopub.status.idle": "2026-05-19T20:32:49.760831Z",
"shell.execute_reply": "2026-05-19T20:32:49.760277Z"
}
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"input_line_70:2:14: error: cannot deduce 'auto' from unknown expression\n",
" auto pdf1 = cust.build(\"Sample1\");\n",
" ^\n"
]
}
],
"source": [
"auto pdf1 = cust.build(\"Sample1\");\n",
"auto pdf2 = cust.build(\"Sample2\");\n",
"auto pdf3 = cust.build(\"Sample3\");"
]
},
{
"cell_type": "markdown",
"id": "58fb3d42",
"metadata": {},
"source": [
"And we inspect the two PDFs"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "a99ae4d1",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:32:49.762243Z",
"iopub.status.busy": "2026-05-19T20:32:49.762129Z",
"iopub.status.idle": "2026-05-19T20:32:49.967975Z",
"shell.execute_reply": "2026-05-19T20:32:49.967433Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"PDF 1 with a yield depending on M:\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"input_line_72:2:3: error: use of undeclared identifier 'pdf1'\n",
" (pdf1->Print(\"T\"))\n",
" ^\n",
"Error in : Error evaluating expression (pdf1->Print(\"T\"))\n",
"Execution of your code was aborted.\n"
]
}
],
"source": [
"std::cout << \"\\nPDF 1 with a yield depending on M:\" << std::endl;\n",
"pdf1->Print(\"T\");\n",
"std::cout << \"\\nPDF 2 with a yield depending on M:\" << std::endl;\n",
"pdf2->Print(\"T\");\n",
"std::cout << \"\\nPDF 3 with a free yield:\" << std::endl;\n",
"pdf3->Print(\"T\");\n",
"\n",
"std::cout << \"\\nThe following leaves have been created automatically while customising:\" << std::endl;\n",
"newLeaves.Print(\"V\");"
]
},
{
"cell_type": "markdown",
"id": "0ec1f19d",
"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": "ac9587ec",
"metadata": {
"collapsed": false,
"execution": {
"iopub.execute_input": "2026-05-19T20:32:49.969466Z",
"iopub.status.busy": "2026-05-19T20:32:49.969354Z",
"iopub.status.idle": "2026-05-19T20:32:50.174977Z",
"shell.execute_reply": "2026-05-19T20:32:50.174493Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[#0] ERROR:InputArguments -- RooArgSet::operator[]() ERROR: no element named meanG_Sample1 in set\n"
]
}
],
"source": [
"auto& meanG1 = static_cast(allCustomiserNodes[\"meanG_Sample1\"]);\n",
"meanG1.setVal(200);\n",
"auto& meanG2 = static_cast(allCustomiserNodes[\"meanG_Sample2\"]);\n",
"meanG2.setVal(300);\n",
"\n",
"std::cout << \"\\nThe following leaves have been used while customising\"\n",
" << \"\\n\\t(partial overlap with the set of automatically created leaves.\"\n",
" << \"\\n\\ta new customiser for a different PDF could reuse them if necessary.):\" << std::endl;\n",
"allCustomiserNodes.Print(\"V\");"
]
}
],
"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
}