File indexing completed on 2026-04-09 07:48:45
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021 """
0022 ddbase.py : Detdesc parsing into wrapped Elem tree
0023 ====================================================
0024
0025 Questions
0026 -----------
0027
0028 Fly in ointment is that there is detdesc xml generation
0029 which means cannot understand whats going on from sources
0030 alone.
0031
0032 ABANDONED : Detdesc Cross File Referencing
0033 ----------------------------------------------
0034
0035 Catalog declares things that can be referenced from other files and
0036 defines the references for them eg /dd/Geometry
0037
0038 DDDB/geometry.xml::
0039
0040 04 <DDDB>
0041 5
0042 6 <catalog name="Geometry">
0043 7
0044 ..
0045 30
0046 31 <catalogref href="PMT/geometry.xml#PMT" />
0047 ..
0048 41 </catalog>
0049 42
0050 43 </DDDB>
0051
0052
0053 DDDB/PMT/geometry.xml::
0054
0055 06 <DDDB>
0056 7
0057 8 <catalog name="PMT">
0058 9
0059 10 <logvolref href="hemi-pmt.xml#lvPmtHemiFrame"/>
0060 11 <logvolref href="hemi-pmt.xml#lvPmtHemi"/>
0061 12 <logvolref href="hemi-pmt.xml#lvPmtHemiwPmtHolder"/>
0062 13 <logvolref href="hemi-pmt.xml#lvAdPmtCollar"/>
0063
0064
0065 ::
0066
0067 simon:DDDB blyth$ pmt-dfind /dd/Geometry/PMT/lvPmtHemi\"
0068 ./PMT/hemi-pmt.xml: <physvol name="pvPmtHemi" logvol="/dd/Geometry/PMT/lvPmtHemi" />
0069 ./PMT/hemi-pmt.xml: <physvol name="pvAdPmt" logvol="/dd/Geometry/PMT/lvPmtHemi" />
0070 ./PMT/pmt.xml: volsecond="/dd/Geometry/PMT/lvPmtHemi">
0071 ./PmtBox/geometry.xml: <physvol name="pvPmtBoxPmt" logvol="/dd/Geometry/PMT/lvPmtHemi">
0072 ./PmtPanel/properties.xml: volfirst="/dd/Geometry/PMT/lvPmtHemi"
0073 ./PmtPanel/properties.xml: volfirst="/dd/Geometry/PMT/lvPmtHemi"
0074 simon:DDDB blyth$
0075
0076 simon:DDDB blyth$ pmt-dfind /dd/Geometry/PMT/lvPmtHemiFrame\"
0077 ./PmtPanel/vetopmt.xml: <physvol name="pvVetoPmtUnit" logvol="/dd/Geometry/PMT/lvPmtHemiFrame">
0078 simon:DDDB blyth$
0079
0080 simon:DDDB blyth$ pmt-dfind /dd/Geometry/PMT/lvPmtHemiwPmtHolder\"
0081 ./AdPmts/geometry.xml: <physvol name="pvAdPmtUnit" logvol="/dd/Geometry/PMT/lvPmtHemiwPmtHolder">
0082 simon:DDDB blyth$
0083
0084
0085
0086
0087 * Relies on all paramters being defined within the file
0088 (XML entity inclusion is regarded as being withn the same file)
0089
0090
0091 Classes
0092 --------
0093
0094 Att(object)
0095 holds string expression "expr" and top level global "g"
0096 allowing the expression to be evaluated within the global context
0097 via the "value" property
0098
0099 For simple properties this is not needed, only need for expression
0100 properties.
0101
0102
0103 Elem(object)
0104 ctor simply holds raw lxml elem and global "g",
0105
0106 Primary functionality invoked via findall_ which takes the
0107 lxml elements returned by lxml findall and wraps them into
0108 Elem subclasses appropriate to the lxml tag
0109
0110 Also provides introspection.
0111
0112
0113 Dddb(Elem)
0114 top level global g that holds the context within which txt expr
0115 are evaluated via Att "value" properties
0116
0117 Parameter(Elem)
0118 prop: expr
0119
0120 PosXYZ(Elem)
0121 att: x,y,z
0122
0123 att rather than simple props are needed as they are expressions
0124 that must be evaluated within the global context
0125
0126 Primitive(Elem)
0127 att: outerRadius, innerRadius
0128
0129 Sphere(Primitive)
0130 att: startThetaAngle, deltaThetaAngle
0131
0132 Tubs(Primitive)
0133 att: sizeZ
0134
0135 Logvol(Elem)
0136 prop: material, sensdet
0137
0138 Physvol(Elem)
0139 prop: logvolref
0140
0141 Union(Elem)
0142 Intersection(Elem)
0143 Subtraction(Elem)
0144 naming types
0145
0146
0147
0148
0149 """
0150 import os, re, logging, math
0151 log = logging.getLogger(__name__)
0152
0153 from opticks.ana.main import opticks_main
0154
0155 import numpy as np
0156 import lxml.etree as ET
0157 import lxml.html as HT
0158 from math import acos
0159
0160 tostring_ = lambda _:ET.tostring(_)
0161 exists_ = lambda _:os.path.exists(os.path.expandvars(_))
0162 parse_ = lambda _:ET.parse(os.path.expandvars(_)).getroot()
0163 fparse_ = lambda _:HT.fragments_fromstring(file(os.path.expandvars(_)).read())
0164 pp_ = lambda d:"\n".join([" %30s : %f " % (k,d[k]) for k in sorted(d.keys())])
0165
0166 X,Y,Z = 0,1,2
0167
0168
0169 class Att(object):
0170 def __init__(self, expr, g, evaluate=True):
0171 self.expr = expr
0172 self.g = g
0173 self.evaluate = evaluate
0174
0175 value = property(lambda self:self.g.ctx.evaluate(self.expr) if self.evaluate else self.expr)
0176
0177 def __repr__(self):
0178 return "%s : %s " % (self.expr, self.value)
0179
0180
0181 class E(object):
0182 """
0183 Element base type, providing lxml elem lookup and wrapping
0184 """
0185 lvtype = 'Logvol'
0186 pvtype = 'Physvol'
0187 postype = 'posXYZ'
0188
0189 typ = property(lambda self:self.__class__.__name__)
0190 name = property(lambda self:self.elem.attrib.get('name',None))
0191 shortname = property(lambda self:self.name)
0192
0193 def __init__(self, elem, g=None):
0194 """
0195 :param elem: lxml parsed DetDesc XML element
0196 :param g: registry object
0197
0198 The registry object (Dddb class) is planted at top level
0199 and passed along to all E instances.
0200 """
0201 self.elem = elem
0202 self.g = g
0203
0204 def att(self, k, dflt=None):
0205 v = self.elem.attrib.get(k, None)
0206 return Att(v, self.g) if v is not None else Att(dflt, self.g, evaluate=False)
0207
0208 def findall_(self, expr):
0209 """
0210 lxml findall result elements are wrapped in the class appropriate to their tags,
0211 note the global g gets passed into all Elem
0212
0213 g.kls is a dict associating tag names to classes, Elem
0214 is fallback, all the classes have signature of elem-instance, g
0215
0216 """
0217 wrap_ = lambda e:self.g.kls.get(e.tag,E)(e,self.g)
0218 fa = map(wrap_, self.elem.findall(expr) )
0219 kln = self.__class__.__name__
0220 name = self.name
0221 log.debug("findall_ from %s:%s expr:%s returned %s " % (kln, name, expr, len(fa)))
0222 return fa
0223
0224 def findone_(self, expr):
0225 all_ = self.findall_(expr)
0226 assert len(all_) == 1
0227 return all_[0]
0228
0229 def find1_(self, expr):
0230 all_ = self.findall_(expr)
0231 assert len(all_) in [0,1]
0232 return all_[0] if len(all_) == 1 else None
0233
0234 def find_(self, expr):
0235 e = self.elem.find(expr)
0236 wrap_ = lambda e:self.g.kls.get(e.tag,Elem)(e,self.g)
0237 return wrap_(e) if e is not None else None
0238
0239 def __repr__(self):
0240 return "%15s : %s " % ( self.elem.tag, repr(self.elem.attrib) )
0241
0242
0243
0244 class Elem(E):
0245 posXYZ = None
0246 is_rev = False
0247
0248
0249 is_primitive = property(lambda self:type(self) in self.g.primitive)
0250 is_composite = property(lambda self:type(self) in self.g.composite)
0251 is_intersection = property(lambda self:type(self) in self.g.intersection)
0252 is_difference = property(lambda self:type(self) in self.g.difference)
0253 is_operator = property(lambda self:type(self) in self.g.operator)
0254 is_tubs = property(lambda self:type(self) in self.g.tubs)
0255 is_sphere = property(lambda self:type(self) in self.g.sphere)
0256 is_union = property(lambda self:type(self) in self.g.union)
0257 is_posXYZ = property(lambda self:type(self) in self.g.posXYZ)
0258 is_geometry = property(lambda self:type(self) in self.g.geometry)
0259 is_logvol = property(lambda self:type(self) in self.g.logvol)
0260
0261 @classmethod
0262 def link_posXYZ(cls, ls, posXYZ):
0263 """
0264 Attach *posXYZ* attribute to all primitives in the list
0265 """
0266 for i in range(len(ls)):
0267 if ls[i].is_primitive:
0268 ls[i].posXYZ = posXYZ
0269 log.debug("linking %s to %s " % (posXYZ, ls[i]))
0270
0271 @classmethod
0272 def combine_posXYZ(cls, prim, plus):
0273 assert 0, "not yet implemented"
0274
0275 @classmethod
0276 def link_prior_posXYZ(cls, ls, base=None):
0277 """
0278 Attach any *posXYZ* instances in the list to preceeding primitives
0279
0280 Singular base linking is needed to honour posXYZ applied on a pv
0281 which gets hooked onto the referenced lv and where the lv yields a primitive.
0282
0283 ::
0284
0285 105
0286 106 <physvol name="pvPmtHemiBottom"
0287 107 logvol="/dd/Geometry/PMT/lvPmtHemiBottom">
0288 108 <posXYZ z="PmtHemiFaceOff+PmtHemiBellyOff"/>
0289 109 </physvol>
0290 110
0291 111 <physvol name="pvPmtHemiDynode"
0292 112 logvol="/dd/Geometry/PMT/lvPmtHemiDynode">
0293 113 <posXYZ z="-0.5*PmtHemiGlassBaseLength+PmtHemiGlassThickness"/>
0294 114 </physvol>
0295
0296
0297 """
0298 log.info("link_prior_posXYZ lls %d base %r " % (len(ls), base))
0299 if len(ls) > 1:
0300 for i in range(1,len(ls)):
0301 if base is not None and ls[i-1].is_primitive:
0302 ls[i-1].posXYZ = base
0303 pass
0304 if ls[i].is_posXYZ and ls[i-1].is_primitive:
0305 if ls[i-1].posXYZ is not None:
0306 cls.combine_posXYZ(ls[i-1], ls[i])
0307 else:
0308 ls[i-1].posXYZ = ls[i]
0309 log.debug("linking %s to %s " % (ls[i], ls[i-1]))
0310 pass
0311 pass
0312 elif len(ls) == 1:
0313 if base is not None and ls[0].is_primitive:
0314 log.info(" link_prior_posXYZ doing singular base link " )
0315 ls[0].posXYZ = base
0316 pass
0317 else:
0318 pass
0319
0320
0321 def _get_desc(self):
0322 return "%10s %15s %s " % (type(self).__name__, self.xyz, self.name )
0323 desc = property(_get_desc)
0324
0325 def _get_xyz(self):
0326 x = y = z = 0
0327 if self.posXYZ is not None:
0328 x = self.posXYZ.x.value
0329 y = self.posXYZ.y.value
0330 z = self.posXYZ.z.value
0331 pass
0332 return [x,y,z]
0333 xyz = property(_get_xyz)
0334
0335 def _get_z(self):
0336 """
0337 z value from any linked *posXYZ* or 0
0338 """
0339 z = 0
0340 if self.posXYZ is not None:
0341 z = self.posXYZ.z.value
0342 return z
0343 z = property(_get_z)
0344
0345 def _get_children(self):
0346 """
0347 Defines the nature of the tree.
0348
0349 * for Physvol returns single item list containing the referenced Logvol
0350 * for Logvol returns list of all contained Physvol
0351 * otherwise returns empty list
0352
0353 NB bits of geometry of a Logvol are not regarded as children,
0354 but rather are constitutent to it.
0355
0356 Fixed Issue of physvol offsets not being honoured::
0357
0358 105
0359 106 <physvol name="pvPmtHemiBottom"
0360 107 logvol="/dd/Geometry/PMT/lvPmtHemiBottom">
0361 108 <posXYZ z="PmtHemiFaceOff+PmtHemiBellyOff"/>
0362 109 </physvol>
0363 110
0364 111 <physvol name="pvPmtHemiDynode"
0365 112 logvol="/dd/Geometry/PMT/lvPmtHemiDynode">
0366 113 <posXYZ z="-0.5*PmtHemiGlassBaseLength+PmtHemiGlassThickness"/>
0367 114 </physvol>
0368
0369 """
0370 if type(self) is Physvol:
0371 posXYZ = self.find_("./posXYZ")
0372 lvn = self.logvolref.split("/")[-1]
0373 lv = self.g.logvol_(lvn)
0374
0375 assert getattr(lv,'posXYZ',None) == None, ("_get_children stomping on posXYZ ", lv)
0376 lv.posXYZ = posXYZ
0377
0378
0379
0380
0381
0382
0383
0384
0385
0386
0387
0388
0389
0390
0391
0392
0393
0394 if posXYZ is not None:
0395 log.info("children... posXYZ %s " % (repr(posXYZ)))
0396 log.info("children... %s passing pv posXYZ to lv %s " % (self.name, repr(lv)))
0397 pass
0398
0399 return [lv]
0400
0401 elif type(self) is Logvol:
0402 pvs = self.findall_("./physvol")
0403 return pvs
0404 else:
0405 return []
0406 pass
0407
0408 children = property(_get_children)
0409
0410 def comps(self):
0411 """
0412 :return comps: immediate constituents of an Elem, not recursive
0413 """
0414 comps = self.findall_("./*")
0415 self.link_prior_posXYZ(comps)
0416 return comps
0417
0418 def geometry(self):
0419 return filter(lambda c:c.is_geometry, self.comps())
0420
0421
0422 class Logvol(Elem):
0423 material = property(lambda self:self.elem.attrib.get('material', None))
0424 sensdet = property(lambda self:self.elem.attrib.get('sensdet', None))
0425 def __repr__(self):
0426 return "LV %-20s %20s %s : %s " % (self.name, self.material, self.sensdet, repr(self.posXYZ))
0427
0428 class Physvol(Elem):
0429 logvolref = property(lambda self:self.elem.attrib.get('logvol', None))
0430 def __repr__(self):
0431 return "PV %-20s %s " % (self.name, self.logvolref)
0432
0433 class Union(Elem):
0434 def __repr__(self):
0435 return "Union %20s " % (self.name)
0436
0437 class Intersection(Elem):
0438 def __repr__(self):
0439 return "Intersection %20s " % (self.name)
0440
0441 class Subtraction(Elem):
0442 def __repr__(self):
0443 return "Subtraction %20s " % (self.name)
0444
0445
0446
0447 class Parameter(Elem):
0448 expr = property(lambda self:self.elem.attrib['value'])
0449
0450 def hasprefix(self, prefix):
0451 return self.name.startswith(prefix)
0452
0453 def __repr__(self):
0454 return "%30s : %s " % ( self.name, self.expr )
0455
0456
0457 class PosXYZ(Elem):
0458 x = property(lambda self:self.att('x',0))
0459 y = property(lambda self:self.att('y',0))
0460 z = property(lambda self:self.att('z',0))
0461 def __repr__(self):
0462 return "PosXYZ %s " % (repr(self.z))
0463
0464
0465 class Primitive(Elem):
0466 is_rev = True
0467 outerRadius = property(lambda self:self.att('outerRadius'))
0468 innerRadius = property(lambda self:self.att('innerRadius'))
0469
0470 def bbox(self, zl, zr, yn, yp ):
0471 assert yn < 0 and yp > 0 and zr > zl
0472 return BBox([yn,yn,zl], [yp,yp,zr])
0473
0474
0475 class Sphere(Primitive):
0476 startThetaAngle = property(lambda self:self.att('startThetaAngle'))
0477 deltaThetaAngle = property(lambda self:self.att('deltaThetaAngle'))
0478
0479 def has_inner(self):
0480 return self.innerRadius.value is not None
0481
0482 def has_innerRadius(self):
0483 return self.innerRadius.value is not None
0484
0485 def has_startThetaAngle(self):
0486 return self.startThetaAngle.value is not None
0487
0488 def has_deltaThetaAngle(self):
0489 return self.deltaThetaAngle.value is not None
0490
0491 def __repr__(self):
0492 return "sphere %20s : %s : %s " % (self.name, self.outerRadius, self.posXYZ)
0493
0494 class Tubs(Primitive):
0495 sizeZ = property(lambda self:self.att('sizeZ'))
0496 def __repr__(self):
0497 return "Tubs %20s : outerRadius %s sizeZ %s : %s " % (self.name, self.outerRadius, self.sizeZ, self.posXYZ)
0498
0499
0500
0501
0502 class Dddb(Elem):
0503 kls = {
0504 "parameter":Parameter,
0505 "sphere":Sphere,
0506 "tubs":Tubs,
0507 "logvol":Logvol,
0508 "physvol":Physvol,
0509 "posXYZ":PosXYZ,
0510 "intersection":Intersection,
0511 "union":Union,
0512 "subtraction":Subtraction,
0513 }
0514
0515
0516 primitive = [Sphere, Tubs]
0517 tubs = [Tubs]
0518 sphere = [Sphere]
0519
0520 intersection = [Intersection]
0521 union = [Union]
0522 subtraction = [Subtraction]
0523 operator = [Union, Intersection, Subtraction]
0524
0525 composite = [Union, Intersection, Subtraction, Physvol]
0526
0527 geometry = [Sphere, Tubs, Union, Intersection, Subtraction]
0528
0529 posXYZ= [PosXYZ]
0530 logvol = [Logvol]
0531
0532 expand = {
0533 "(PmtHemiFaceROCvac^2-PmtHemiBellyROCvac^2-(PmtHemiFaceOff-PmtHemiBellyOff)^2)/(2*(PmtHemiFaceOff-PmtHemiBellyOff))":
0534 "(PmtHemiFaceROCvac*PmtHemiFaceROCvac-PmtHemiBellyROCvac*PmtHemiBellyROCvac-(PmtHemiFaceOff-PmtHemiBellyOff)*(PmtHemiFaceOff-PmtHemiBellyOff))/(2*(PmtHemiFaceOff-PmtHemiBellyOff))"
0535
0536 }
0537
0538 @classmethod
0539 def parse(cls, path, analytic_version=0):
0540 log.info("Dddb parsing %s " % path )
0541 g = Dddb(parse_(path))
0542 g.init(analytic_version=analytic_version)
0543 return g
0544
0545 def __call__(self, expr):
0546 return self.ctx.evaluate(expr)
0547
0548 def init(self, analytic_version):
0549 self.g = self
0550 self.analytic_version = analytic_version
0551
0552 pctx = {}
0553 pctx["mm"] = 1.0
0554 pctx["cm"] = 10.0
0555 pctx["m"] = 1000.0
0556 pctx["degree"] = 1.0
0557 pctx["radian"] = 180./math.pi
0558
0559 self.ctx = Context(pctx, self.expand)
0560 self.ctx.build_context(self.params_())
0561
0562 def logvol_(self, name):
0563 """
0564 Hmm this referencing is single file only,
0565 need to build registry of logvol from all files
0566 """
0567 log.info("logvol_ %s " % name)
0568
0569 if name[0] == '/':
0570 name = name.split("/")[-1]
0571 return self.find_(".//logvol[@name='%s']"%name)
0572
0573 def logvols_(self):
0574 return self.findall_(".//logvol")
0575
0576 def params_(self, prefix=None):
0577 pp = self.findall_(".//parameter")
0578 if prefix is not None:
0579 pp = filter(lambda p:p.hasprefix(prefix), pp)
0580 return pp
0581
0582 def context_(self, prefix=None):
0583 dd = {}
0584 for k,v in self.ctx.d.items():
0585 if k.startswith(prefix) or prefix is None:
0586 dd[k] = v
0587 return dd
0588
0589 def dump_context(self, prefix=None):
0590 print(pp_(self.context_(prefix)))
0591
0592
0593
0594
0595
0596 class Context(object):
0597 def __init__(self, d, expand):
0598 """
0599 :param d: context dict pre-populated with units
0600 :param expand: manual expansion dict, workaround for detdesc
0601 expressions that are not valid python
0602 """
0603 self.d = d
0604 self.expand = expand
0605
0606 def build_context(self, params):
0607 """
0608 :param params: XML parameter elements
0609
0610 Three wave context building avoids having to work out dependencies,
0611 just repeat and rely on simpler expressions getting set into
0612 context on prior waves.
0613 """
0614 name_error = params
0615 type_error = []
0616 for wave in range(3):
0617 name_error, type_error = self._build_context(name_error, wave)
0618 log.debug("after wave %s remaining name_error %s type_error %s " % (wave, len(name_error), len(type_error)))
0619 pass
0620 assert len(name_error) == 0
0621 assert len(type_error) == 0
0622
0623 def evaluate(self, expr):
0624 txt = "float(%s)" % expr
0625 try:
0626 val = eval(txt, globals(), self.d)
0627 except NameError:
0628 log.fatal("failed to evaluate expr %s " % (expr))
0629 val = None
0630 pass
0631 return val
0632
0633 def _build_context(self, params, wave):
0634 """
0635 :param params: to be evaluated into the context
0636 """
0637 name_error = []
0638 type_error = []
0639 for p in params:
0640 if p.expr in self.expand:
0641 expr = self.expand[p.expr]
0642 log.debug("using manual expansion of %s to %s " % (p.expr, expr))
0643 else:
0644 expr = p.expr
0645
0646 txt = "float(%s)" % expr
0647 try:
0648 val = eval(txt, globals(), self.d)
0649 pass
0650 self.d[p.name] = float(val)
0651 except NameError:
0652 name_error.append(p)
0653 log.debug("NameError %s %s " % (p.name, txt ))
0654 except TypeError:
0655 type_error.append(p)
0656 log.debug("TypeError %s %s " % (p.name, txt ))
0657 pass
0658 return name_error, type_error
0659
0660 def dump_context(self, prefix):
0661 log.info("dump_context %s* " % prefix )
0662 return "\n".join(["%25s : %s " % (k,v) for k,v in filter(lambda kv:kv[0].startswith(prefix),self.d.items())])
0663
0664 def __repr__(self):
0665 return "\n".join(["%25s : %s " % (k,v) for k,v in self.d.items()])
0666
0667
0668
0669
0670 class Ref(E):
0671 href = property(lambda self:self.elem.attrib['href'])
0672
0673 xmlname = property(lambda self:self.href.split("#")[0])
0674 anchor = property(lambda self:self.href.split("#")[1])
0675
0676 xmlpath = property(lambda self:os.path.join(self.parent.xmldir, self.xmlname))
0677 ddpath = property(lambda self:os.path.join(self.parent.ddpath, self.anchor))
0678
0679 def __repr__(self):
0680 return "%20s %20s %s %s " % (self.typ, self.anchor, self.xmlpath, self.ddpath )
0681
0682 class CatalogRef(Ref):
0683 pass
0684
0685 class LogvolRef(Ref):
0686 pass
0687
0688
0689 class Catalog(E):
0690 """
0691 Role of catalog is to provide easy access to
0692 logvol via simple path access... so this
0693 needs to maintain the connection between
0694
0695 * filesystem paths
0696 * dd paths
0697 * in memory elements
0698
0699 """
0700 name = property(lambda self:self.elem.attrib['name'])
0701 parent = None
0702
0703 def _get_ddpath(self):
0704 lev = [""]
0705 if self.parent is not None:
0706 lev.append( self.parent.ddpath )
0707 pass
0708 lev.append(self.name)
0709 return "/".join(lev)
0710
0711 ddpath = property(_get_ddpath)
0712
0713
0714 def __repr__(self):
0715 return "%20s %20s %s " % (self.typ, self.name, self.ddpath)
0716
0717 def refs(self):
0718 """
0719 Catalogs often contain catalogref, logvolref but they
0720 can also directly contain logvol.
0721
0722 Hmm splitting into separate files is an
0723 implementation detail, should not impact the
0724 tree being built.
0725 """
0726 refs = self.findall_("./catalogref")
0727 for ref in refs:
0728 ref.parent = self
0729 if not exists_(ref.xmlpath):
0730 log.warning("ref.xmlpath doesnt exist %s " % ref.xmlpath)
0731 pass
0732 return refs
0733
0734
0735
0736 class DD(E):
0737 """
0738 CAUTION
0739 This was an initial foray into parsing
0740 multi-file detdesc that was abandoned.
0741 Jumped ship to single file GDML parsing, that turned out to be much easier.
0742
0743 """
0744 ddr = {}
0745
0746 kls = {
0747 "catalog":Catalog,
0748 "catalogref":CatalogRef,
0749 "logvolref":LogvolRef,
0750 }
0751
0752 xmldir = property(lambda self:os.path.dirname(self.xmlpath))
0753
0754 @classmethod
0755 def parse(cls, path):
0756 log.info("DD parsing %s " % path )
0757 g = cls
0758 dd = DD(parse_(path), g)
0759 dd.xmlpath = path
0760 dd.init()
0761 return dd
0762
0763 def init(self):
0764 cat = self.find1_("./catalog")
0765 if cat is not None:
0766 print(cat)
0767 cat.xmldir = self.xmldir
0768 for ref in cat.refs():
0769 print("%s %s " % (ref, ref.ddpath))
0770 self.g.ddr[ref.ddpath] = DD.parse(ref.xmlpath)
0771
0772 def __repr__(self):
0773 return "%20s %s %s " % (self.typ, self.xmlpath, self.xmldir)
0774
0775
0776
0777
0778
0779
0780
0781
0782
0783 if __name__ == '__main__':
0784 args = opticks_main(apmtidx=2)
0785
0786
0787
0788 xmlpath = args.apmtddpath
0789 log.info("parsing %s -> %s " % (xmlpath, os.path.expandvars(xmlpath)))
0790
0791 g = Dddb.parse(xmlpath)
0792
0793 lv = g.logvol_("lvPmtHemi")
0794
0795
0796 dd = DD.parse(args.addpath)
0797
0798
0799 print(dd)
0800
0801
0802