Setting global pointer gGeoManager from PyROOT

Hi,

I am trying to use several TGeoManager objects at the same time with PyROOT. I often need to switch between them, so I need to be able to do it quickly. In ROOT, I would simply load them like this:

TGeoManager *m1 = TGeoManager::Import("geom1.root");
gGeoManager = 0;
TGeoManager *m2 = TGeoManager::Import("geom2.root");

and use them like this:

gGeoManager = m1;
// do stuff with m1
gGeoManager = m2;
// do stuff with m2

However, I can’t efficiently do this in PyROOT. The only working solution that I have found is to use gROOT.ProcessLine() and AddressOf():

// import
from ROOT import TGeoManager, gROOT
m1 = TGeoManager.Import("geom1.root")
gROOT.ProcessLine('gGeoManager = (TGeoManager *) 0;')
m2 = TGeoManager.Import("geom2.root")

// use
gROOT.ProcessLine('gGeoManager = (TGeoManager *) {};'.format(AddressOf(m1)[0]))
// do stuff with m1
gROOT.ProcessLine('gGeoManager = (TGeoManager *) {};'.format(AddressOf(m2)[0]))
// do stuff with m2

It goes without saying that this method is EXTREMELY slow, due to all the parsing involved. Is there any faster way?

For the record, the following solutions do not work:

from ROOT import gGeoManager, MakeNullPointer, TGeoManager
gGeoManager.__assign__(0) # TypeError: TNamed& TNamed::operator=(const TNamed& rhs) => could not convert argument 1
gGeoManager.__assign__(MakeNullPointer(TGeoManager)) # segfault

TL;DR: how do I quickly set gGeoManager from PyROOT?

Thanks in advance,
Davide

Davide,

keep the scope (at least for gGeoManager, otherwise it is local to the importing module):[code]import ROOT
m1 = ROOT.TGeoManager.Import(“geom1.root”)
ROOT.gGeoManager = m1

etc.[/code]Note that ROOT.gGeoManager does not exist until the first TGeoManager is created (as that will load the library in which it lives), but that seems to be fine with the way you’re using it.

Cheers,
Wim

Wim,

Thank you for your reply, I will try that. But how do I set it to NULL?

Cheers,
Davide

Davide,

you need a typed TGeoManager nullptr. Do make sure that gGeoManager is loaded before first setting to null:[code]>>> import ROOT

ROOT.gSystem.Load(‘libGeom’) # brings gGeoManager into memory
0
ROOT.gGeoManager = ROOT.MakeNullPointer(‘TGeoManager’) # sets it to NULL
[/code]
Cheers,
Wim

Wim,

I tried out your suggestions, but I can’t get it to work. Here is a minimal working example illustrating my problem:

import sys
import ROOT

ROOT.gSystem.Load('libGeom')

# first ROOT geometry
mgr1 = ROOT.TGeoManager("geom1", "Sphere in Box")
xx_mat = ROOT.TGeoMaterial("MAT2",
                        mgr1.GetElementTable().GetElement(0),
                        1)
med1 = ROOT.TGeoMedium("FALLTHROUGH", 1, xx_mat)
med2 = ROOT.TGeoMedium("FALLTHROUGH", 1, xx_mat)
box = mgr1.MakeBox("Box", med1, 4, 4, 4)
sphere = mgr1.MakeSphere("Sphere", med2, 0, 2)
box.AddNode(sphere, 1)
mgr1.SetTopVolume(box)
mgr1.CloseGeometry("")
mgr1.SetTopVisible()

ROOT.gGeoManager = ROOT.MakeNullPointer(ROOT.TGeoManager)
print('gGeoManager={}'.format(ROOT.gGeoManager)); sys.stdout.flush()
print('mgr1={}'.format(mgr1)); sys.stdout.flush()

# second ROOT geometry
mgr2 = ROOT.TGeoManager("geom2", "Box in Box")
xx_mat = ROOT.TGeoMaterial("MAT2",
                        mgr2.GetElementTable().GetElement(0),
                        1)
med1 = ROOT.TGeoMedium("Water", 1, xx_mat)
med2 = ROOT.TGeoMedium("FALLTHROUGH", 1, xx_mat)
box = mgr2.MakeBox("BoxOutside", med1, 5, 5, 5)
box2 = mgr2.MakeBox("BoxInside", med2, 3, 3, 3)
box.AddNode(box2, 1)
mgr2.SetTopVolume(box)
mgr2.CloseGeometry("")
mgr2.SetTopVisible()
ROOT.gGeoManager = ROOT.MakeNullPointer(ROOT.TGeoManager)
print('gGeoManager={}'.format(ROOT.gGeoManager)); sys.stdout.flush()

print('mgrs={}, {}'.format(mgr1, mgr2)); sys.stdout.flush()

And here is the output on my machine (Python 3.4.3, ROOT 5.34.30):

Info in <TGeoManager::TGeoManager>: Geometry geom1, Sphere in Box created
Info in <TGeoManager::SetTopVolume>: Top volume is Box. Master volume is Box
Info in <TGeoNavigator::BuildCache>: --- Maximum geometry depth set to 100
Info in <TGeoManager::CheckGeometry>: Fixing runtime shapes...
Info in <TGeoManager::CheckGeometry>: ...Nothing to fix
Info in <TGeoManager::CloseGeometry>: Counting nodes...
Info in <TGeoManager::Voxelize>: Voxelizing...
Info in <TGeoManager::CloseGeometry>: Building cache...
Info in <TGeoManager::CountLevels>: max level = 1, max placements = 1
Info in <TGeoManager::CloseGeometry>: 2 nodes/ 2 volume UID's in Sphere in Box
Info in <TGeoManager::CloseGeometry>: ----------------modeler ready----------------
gGeoManager=<ROOT.TGeoManager object at 0x(nil)>
mgr1=<ROOT.TGeoManager object ("geom1") at 0x2511a00>
TGeoManager::Init:0: RuntimeWarning: Deleting previous geometry: geom1/Sphere in Box
Info in <TGeoManager::TGeoManager>: Geometry geom2, Box in Box created
Info in <TGeoManager::SetTopVolume>: Top volume is BoxOutside. Master volume is BoxOutside
Info in <TGeoManager::CheckGeometry>: Fixing runtime shapes...
Info in <TGeoManager::CheckGeometry>: ...Nothing to fix
Info in <TGeoManager::CloseGeometry>: Counting nodes...
Info in <TGeoManager::Voxelize>: Voxelizing...
Info in <TGeoManager::CloseGeometry>: Building cache...
Info in <TGeoManager::CountLevels>: max level = 1, max placements = 1
Info in <TGeoManager::CloseGeometry>: 2 nodes/ 2 volume UID's in Box in Box
Info in <TGeoManager::CloseGeometry>: ----------------modeler ready----------------
gGeoManager=<ROOT.TGeoManager object at 0x(nil)>
mgrs=None, <ROOT.TGeoManager object ("geom2") at 0x27e6640>

As you can see, there is a warning about the first geometry being deleted, and mgr1=None on the last line.

Regards,
Davide

Hi,

two things: before using it the first time, you need to bring it into existence (i.e. just a “ROOT.gGeomanager”). And I didn’t realize that this is ROOT5.

I can probably backport some of the changes.

(Aside, even with ROOT6, there appears to be an ownership issue on application exit.)

Cheers,
Wim

Hi,

the crash is in the redefinition of “MAT2”, so I don’t think it’s a python issue. Other than that, it works fine on ROOT6. I’ll look into a backport …

Cheers,
Wim

OK, thank you. If I understand correctly, it looks like a bug, doesn’t it? Would you like me to submit a bug report in the tracker?

Cheers,
Davide

Davide,

it’s a missing feature not a bug (it doesn’t break anything, after all). ROOT5.34 is closed for further development, with only “blocker” bugs being fixed. There are many, many more features that are only available in ROOT6, some of which are impossible to backport (b/c of Cling v.s. CINT) and the code bases have massively diverged.

Cheers,
Wim

Well, I would argue that it breaks the possibility to use two geometries simultaneously, which is possible in vanilla ROOT (without PyROOT). If it is a limitation of the Python wrappers, it should be at least documented IMO.

Cheers,
Davide

Davide,

[quote=“arekfu”]If it is a limitation of the Python wrappers, it should be at least documented IMO.[/quote]Oh, sure, in an ideal world with infinite developer resources … Of course, in such a world, all user code would long have moved through ROOT6 and be done with ROOT5.

I could not do a backport; too many changes. I’ve made it work in a different way. We’ll see from the nightlies if it breaks anything. If so, I’ll revert. Otherwise, it should show up at some coming patch release.

Cheers,
Wim