How to properly save custom types to TTree branches in Python

Hi, ROOT experts,

I have some custom types that I want to save to TTree branches, and I have done something similar before in C++ with different custom types but I don’t know how to make it work in Python, could you please enlighten me?

    parser = argparse.ArgumentParser()
    parser.add_argument("inFileName", type=str)
    args = parser.parse_args()

    inFile = TFile(args.inFileName)

    daqstatus = ROOT.mattak.DAQStatus()
    header = ROOT.mattak.Header()
    waveforms = ROOT.mattak.Waveforms()

    inTree = inFile.Get("combined")
    inTree.SetBranchAddress("daqstatus", daqstatus)
    inTree.SetBranchAddress("header", header)
    inTree.SetBranchAddress("waveforms", waveforms)

    outFileName = 'filtered_' + args.inFileName
    outFile = TFile(outFileName, "recreate" )
    outTree = TTree("combined", "combined")
    outTree.Branch("daqstatus", daqstatus)
    outTree.Branch("header", header)
    outTree.Branch("waveforms", waveforms)

    nTotalEvents = inTree.GetEntries()
    nPassedEvents = 0
    hitAll = 4
    HF = HitFilter()

    nChannels = 24
    for i_event in range(nTotalEvents):
        inTree.GetEntry(i_event)
        wfs = []
        for i_channel in range(nChannels):
            if i_channel < 4:
                wfs.append(waveforms.makeGraph(i_channel).GetY())
            else:
                break
        nHits = HF.scanForHits(np.array(wfs))
        if nHits == hitAll:
            nPassedEvents += 1
            outTree.Fill()

    inFile.Close()
    outFile.cd()
    outTree.Write()
    outFile.Close()

Although this script works and the output file can be generated, but when I opened the output file I got errors:

 *** Break *** segmentation violation
[/opt/homebrew/Cellar/root/6.26.06_2/lib/root/libCore.so] TUnixSystem::DispatchSignals(ESignals) (no debug info)
[/usr/lib/system/libsystem_platform.dylib] _sigtramp (no debug info)
[/Users/martin/Desktop/RNO-G/software/mattak/build/libmattak.dylib] TGraph* graphImpl<mattak::Waveforms>(mattak::Waveforms const&, int, bool) /Users/martin/Desktop/RNO-G/software/mattak/src/Waveforms.cc:80
[<unknown binary>] (no debug info)
[/opt/homebrew/Cellar/root/6.26.06_2/lib/root/libcppyy_backend3_10.so] WrapperCall(long, unsigned long, void*, void*, void*) (no debug info)
[/opt/homebrew/Cellar/root/6.26.06_2/lib/root/libcppyy_backend3_10.so] Cppyy::CallR(long, void*, unsigned long, void*) (no debug info)
[/opt/homebrew/Cellar/root/6.26.06_2/lib/root/libcppyy3_10.so] CPyCppyy::(anonymous namespace)::InstancePtrExecutor::Execute(long, void*, CPyCppyy::CallContext*) (no debug info)
[/opt/homebrew/Cellar/root/6.26.06_2/lib/root/libcppyy3_10.so] CPyCppyy::CPPMethod::ExecuteFast(void*, long, CPyCppyy::CallContext*) (no debug info)
[/opt/homebrew/Cellar/root/6.26.06_2/lib/root/libcppyy3_10.so] CPyCppyy::CPPMethod::ExecuteProtected(void*, long, CPyCppyy::CallContext*) (no debug info)
[/opt/homebrew/Cellar/root/6.26.06_2/lib/root/libcppyy3_10.so] CPyCppyy::CPPMethod::Call(CPyCppyy::CPPInstance*&, _object*, _object*, CPyCppyy::CallContext*) (no debug info)
[/opt/homebrew/Cellar/root/6.26.06_2/lib/root/libcppyy3_10.so] CPyCppyy::(anonymous namespace)::mp_call(CPyCppyy::CPPOverload*, _object*, _object*) (no debug info)
[/opt/homebrew/Cellar/python@3.10/3.10.10_1/Frameworks/Python.framework/Versions/3.10/Python] _PyObject_MakeTpCall (no debug info)
[/opt/homebrew/Cellar/python@3.10/3.10.10_1/Frameworks/Python.framework/Versions/3.10/Python] call_function (no debug info)
[/opt/homebrew/Cellar/python@3.10/3.10.10_1/Frameworks/Python.framework/Versions/3.10/Python] _PyEval_EvalFrameDefault (no debug info)
[/opt/homebrew/Cellar/python@3.10/3.10.10_1/Frameworks/Python.framework/Versions/3.10/Python] _PyEval_Vector (no debug info)
[/opt/homebrew/Cellar/python@3.10/3.10.10_1/Frameworks/Python.framework/Versions/3.10/Python] PyEval_EvalCode (no debug info)
[/opt/homebrew/Cellar/python@3.10/3.10.10_1/Frameworks/Python.framework/Versions/3.10/Python] run_eval_code_obj (no debug info)
[/opt/homebrew/Cellar/python@3.10/3.10.10_1/Frameworks/Python.framework/Versions/3.10/Python] run_mod (no debug info)
[/opt/homebrew/Cellar/python@3.10/3.10.10_1/Frameworks/Python.framework/Versions/3.10/Python] pyrun_file (no debug info)
[/opt/homebrew/Cellar/python@3.10/3.10.10_1/Frameworks/Python.framework/Versions/3.10/Python] _PyRun_SimpleFileObject (no debug info)
[/opt/homebrew/Cellar/python@3.10/3.10.10_1/Frameworks/Python.framework/Versions/3.10/Python] _PyRun_AnyFileObject (no debug info)
[/opt/homebrew/Cellar/python@3.10/3.10.10_1/Frameworks/Python.framework/Versions/3.10/Python] pymain_run_file_obj (no debug info)
[/opt/homebrew/Cellar/python@3.10/3.10.10_1/Frameworks/Python.framework/Versions/3.10/Python] pymain_run_file (no debug info)
[/opt/homebrew/Cellar/python@3.10/3.10.10_1/Frameworks/Python.framework/Versions/3.10/Python] Py_RunMain (no debug info)
[/opt/homebrew/Cellar/python@3.10/3.10.10_1/Frameworks/Python.framework/Versions/3.10/Python] Py_BytesMain (no debug info)
[/usr/lib/dyld] start (no debug info)
Traceback (most recent call last):
  File "/Users/martin/Desktop/NuRadioMC/noise/applyHitFilter.py", line 49, in <module>
    wfs.append(waveforms.makeGraph(i_channel).GetY())
cppyy.ll.SegmentationViolation: TGraph* mattak::Waveforms::makeGraph(int chan, bool ns = true) =>
    SegmentationViolation: segfault in C++; program state was reset

ROOT Version: 6.26/06
Python Version: 3.10.10


Hi,
To store custom types in a ROOT tree you need fist to generate the dictionary and then you can also use them from Python. See for example Fill Branch from Vector of Vectors - #2 by moneta

Lorenzo

Hi, Lorenzo,

I am still running into some issues:

root [0] .L saveCustomTypesToTree.C+
Info in <TMacOSXSystem::ACLiC>: creating shared library /Users/martin/Desktop/NuRadioMC/noise/./saveCustomTypesToTree_C.so
Warning in <TClassTable::Add>: class mattak::Header already in TClassTable
Warning in <TClassTable::Add>: class mattak::VoltageCalibration already in TClassTable
Warning in <TClassTable::Add>: class mattak::Waveforms already in TClassTable
root [1] saveCustomTypesToTree()
Error in <TBranchElement::InitializeOffsets>: Could not find the real data member '__elems_[4096]' when constructing the branch 'scan_result' [Likely missing ShowMember].

 *** Break *** segmentation violation
[/usr/lib/system/libsystem_platform.dylib] _sigtramp (no debug info)
[/opt/homebrew/Cellar/root/6.26.06_2/lib/root/libTree.so] TTree::BranchImp(char const*, char const*, TClass*, void*, int, int) (no debug info)
[/Users/martin/Desktop/NuRadioMC/noise/saveCustomTypesToTree_C.so] saveCustomTypesToTree() (no debug info)
[/opt/homebrew/Cellar/root/6.26.06_2/lib/root/libCling.so] cling::IncrementalExecutor::executeWrapper(llvm::StringRef, cling::Value*) const (no debug info)
[/opt/homebrew/Cellar/root/6.26.06_2/lib/root/libCling.so] cling::Interpreter::RunFunction(clang::FunctionDecl const*, cling::Value*) (no debug info)
[/opt/homebrew/Cellar/root/6.26.06_2/lib/root/libCling.so] cling::Interpreter::EvaluateInternal(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, cling::CompilationOptions, cling::Value*, cling::Transaction**, unsigned long) (no debug info)
[/opt/homebrew/Cellar/root/6.26.06_2/lib/root/libCling.so] cling::Interpreter::process(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, cling::Value*, cling::Transaction**, bool) (no debug info)
[/opt/homebrew/Cellar/root/6.26.06_2/lib/root/libCling.so] cling::MetaProcessor::process(llvm::StringRef, cling::Interpreter::CompilationResult&, cling::Value*, bool) (no debug info)
[/opt/homebrew/Cellar/root/6.26.06_2/lib/root/libCling.so] HandleInterpreterException(cling::MetaProcessor*, char const*, cling::Interpreter::CompilationResult&, cling::Value*) (no debug info)
[/opt/homebrew/Cellar/root/6.26.06_2/lib/root/libCling.so] TCling::ProcessLine(char const*, TInterpreter::EErrorCode*) (no debug info)
[/opt/homebrew/Cellar/root/6.26.06_2/lib/root/libRint.so] TRint::ProcessLineNr(char const*, char const*, int*) (no debug info)
[/opt/homebrew/Cellar/root/6.26.06_2/lib/root/libRint.so] TRint::HandleTermInput() (no debug info)
[/opt/homebrew/Cellar/root/6.26.06_2/lib/root/libCore.so] TUnixSystem::CheckDescriptors() (no debug info)
[/opt/homebrew/Cellar/root/6.26.06_2/lib/root/libCore.so] TMacOSXSystem::DispatchOneEvent(bool) (no debug info)
[/opt/homebrew/Cellar/root/6.26.06_2/lib/root/libCore.so] TSystem::InnerLoop() (no debug info)
[/opt/homebrew/Cellar/root/6.26.06_2/lib/root/libCore.so] TSystem::Run() (no debug info)
[/opt/homebrew/Cellar/root/6.26.06_2/lib/root/libCore.so] TApplication::Run(bool) (no debug info)
[/opt/homebrew/Cellar/root/6.26.06_2/lib/root/libRint.so] TRint::Run(bool) (no debug info)
[/opt/homebrew/Cellar/root/6.26.06_2/bin/root.exe] main (no debug info)
[/usr/lib/dyld] start (no debug info)

Hi,
It looks like there are still some problem with the generated dictionaries. Some class definitions seem meeting. Maybe @pcanal can help you more on this

This indicates that the dictionary is loaded twice (and/or there is 2 dictionary for the same classes).

For example this could happens if the type are defined in saveCustomTypesToTree.C or saveCustomTypesToTree.h and also in a shared library that is automatic loaded.

@moneta and @pcanal,

Thank you for your replies! I am still confused about what I should do to indicate the custom types in the tree branches in Python:

    daqstatus = ROOT.mattak.DAQStatus()
    header = ROOT.mattak.Header()
    waveforms = ROOT.mattak.Waveforms()

    inTree = inFile.Get("combined")
    inTree.SetBranchAddress("daqstatus", daqstatus)
    inTree.SetBranchAddress("header", header)
    inTree.SetBranchAddress("waveforms", waveforms)

    outFileName = 'filtered_' + args.inFileName
    outFile = TFile(outFileName, "recreate" )
    outTree = TTree("combined", "combined")
    outTree.Branch("daqstatus", daqstatus)
    outTree.Branch("header", header)
    outTree.Branch("waveforms", waveforms)

Could you please tell me what I need to change here?

Thanks!

@moneta and @pcanal,

I’ve figured out how to do that, thanks for your previous responses, which have inspired me.

Don’t hesitate to post the answer/solution here for other to see when finding your post when they have a similar question.

What I’ve done is to indicate the custom types in the same way as what we do in C++, though it might look funny in a Python script but it works:

    parser = argparse.ArgumentParser()
    parser.add_argument("inFileName", type=str)
    args = parser.parse_args()

    inFile = TFile(args.inFileName)

    daqstatus = ROOT.mattak.DAQStatus()
    header = ROOT.mattak.Header()
    waveforms = ROOT.mattak.Waveforms()

    inTree = inFile.Get("combined")
    inTree.SetBranchAddress("daqstatus", ROOT.AddressOf(daqstatus))
    inTree.SetBranchAddress("header", ROOT.AddressOf(header))
    inTree.SetBranchAddress("waveforms", ROOT.AddressOf(waveforms))

    outFileName = 'filtered_' + args.inFileName
    outFile = TFile(outFileName, "recreate" )
    outTree = TTree("combined", "combined")
    outTree.Branch("daqstatus", "mattak::DAQStatus", ROOT.AddressOf(daqstatus))
    outTree.Branch("header", "mattak::Header", ROOT.AddressOf(header))
    outTree.Branch("waveforms", "mattak::Waveforms", ROOT.AddressOf(waveforms))

    nTotalEvents = inTree.GetEntries()
    nPassedEvents = 0
    hitAll = 4
    HF = HitFilter()

    nChannels = 24
    for i_event in range(nTotalEvents):
        inTree.GetEntry(i_event)
        wfs = []
        for i_channel in range(nChannels):
            if i_channel < 4:
                wfs.append(waveforms.makeGraph(i_channel).GetY())
            else:
                break
        nHits = HF.scanForHits(np.array(wfs))
        if nHits == hitAll:
            nPassedEvents += 1
            outTree.Fill()

    inFile.Close()
    outFile.cd()
    outTree.Write()
    outFile.Close()

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