Import Binded RooAbsPdf into a RooWorkspace


I am trying to export a RooAbsPdf constructed via BindPdf() function, which creates a pdf from a custom-defined template IBaseFunctionMultiDimTempl. But it seems the internal compiler did not manage to interoperate the pdf with compilation error and segfault when I try to dump the workspace. Is there any documentation how I can achieve this goal?

Here is a mini example to reproduce the problem:

#include "SumFunction.h"
#include "RooWorkspace.h"
#include "RooRealVar.h"
#include "TMath.h"
#include "Math/DistFunc.h"
#include "RooTFnBinding.h"
#include "RooFunctorBinding.h"

using namespace RooFit;

int main(int argc, char *argv[]) {
  RooRealVar a("a", "a", 0, -10, 10);
  RooRealVar b("b", "b", 0, -10, 10);
  RooArgList vars(a, b);

  // SumFunction is just a simple template with take the summation of two varialbes
  SumFunction*  test = new SumFunction();
  // creating the pdf
  RooAbsPdf *testpdf = bindPdf("test", *test, vars);
  // creating worksapce
  RooWorkspace *ws = new RooWorkspace("w");

  ws->writeToFile("workspace.root", kTRUE);

I also adde the SumFunction.cpp and SumFunction.h here.

#include "SumFunction.h"

ROOT::Math::IBaseFunctionMultiDimTempl<double>* SumFunction::Clone() const {
   return new SumFunction(*this);

unsigned int SumFunction::NDim() const {
  return 2;

double SumFunction::DoEval(const double *x) const {
  return x[0] + x[1];
#include <Math/IFunction.h>

class SumFunction : public ROOT::Math::IBaseFunctionMultiDimTempl<double> {
    SumFunction() = default;

    bool HasGradient() const override{
      return false;

    // Clone method for copying functions
    virtual ROOT::Math:: IBaseFunctionMultiDimTempl<double>* Clone() const override;
    virtual unsigned int NDim() const override;
    // Destructor
    ~SumFunction() = default;

    virtual double DoEval(const double *x) const override;

Many thanks,

PS: when I run this code I got warnings like:

Warning in <TBufferFile::WriteObjectAny>: An object of type 11SumFunction (from type_info) passed through a ROOT::Math::IBaseFunctionMultiDimTempl<double> pointer was truncated (due a missing dictionary)!!!
Warning in <TStreamerInfo::WriteBuffer>: The actual class of RooFunctorPdfBinding::func is not available. Only the "ROOT::Math::IBaseFunctionMultiDimTempl<double>" part will be written

_ROOT Version: 6.30.02
Platform: linuxx8664gcc
Compiler: GCC 13.1.0

Hi @huachengcai92, welcome to the ROOT forum and thanks for the interesting post!

You are encountering exactly the kind of problem that made me want to deprecate the PDF binding: they are not persistable, and there are better alternatives.

The better alternative is to use RooGenericPdf, where you can use any C++ function, also if it’s from a custom header. I have written down and example on how to do this here. With the RooGenericPdf, only the declaring string is written to the workspace, so that’s not a problem.

Would that be a solution for you?


Hi @jonas ,

Many thanks for the prompt reply!

I am reading your example, and I am afraid with

gInterpreter->ProcessLine("#include \"customPdfs.h\"");

it would be difficult for us to adapt… In our case, we build our custom class N2LL outside root, using many external libraries such as Eigen to calculate the covariance matrix. I have my test customPdf.h as

#ifndef customPdfs_h
#define customPdfs_h
#include <cppfit/MorphingFunction.h>
#include <cppfit/N2LL.h>

inline double evaluateNLL(double a, double b, double c, double d, double e, double f)
  N2LL* n2ll = new N2LL();
  n2ll->initialize("/eos/atlas/atlascerngroupdisk/phys-top/singletop/fourAngleDatabases/v34v7v1/json/asimov.json", true);
  double values[] = {a, b, c, d, e, f};
  Eigen::VectorXd pars = Eigen::Map<Eigen::VectorXd>(values, 6);
  return n2ll->operator()(pars);

#endif // customPdfs_h

and the external library cannot be found by the inline compiler:

/eos/user/h/hcai/top/fourAngleAnalysis/build/pdfInterface/customPdfs.h:3:10: fatal error: 'cppfit/MorphingFunction.h' file not found
#include <cppfit/MorphingFunction.h>
input_line_9:2:65: error: use of undeclared identifier 'evaluateNLL'
Double_t TFormula____id8854526937451671553(Double_t *x){ return evaluateNLL(x[0],x[1],x[2],x[3],x[4],x[5]) ; }
input_line_10:2:65: error: use of undeclared identifier 'evaluateNLL'
Double_t TFormula____id8854526937451671553(Double_t *x){ return evaluateNLL(x[0],x[1],x[2],x[3],x[4],x[5]) ; }

I guess we must generate a dictionary but don’t know how to do it cleanly…

And I have a followup question about how the RooWorkspace actually works. In our approach, initializing the N2LL object takes a long time. So we want to save the workspace as an intermediate product to share among the analysis team. But if we use the inline header, I wonder how RooWorkspace will handle this class… does it just store the uncompiled object and recompile, then redo the initializing? Or it store the CINT object including all the data, stack and heap and we can directly call the initialized object?

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.