Collada XML layout ==================== :: import os import lxml.etree as ET parse_ = lambda _:ET.parse(os.path.expandvars(_)).getroot() COLLADA_NS = "http://www.collada.org/2005/11/COLLADASchema" xml = parse_("$LOCAL_BASE/env/geant4/geometry/xdae/g4_01.dae") :: In [249]: len(xml.findall(".//*")) Out[249]: 24776 Element examination --------------------- library_nodes ~~~~~~~~~~~~~ :: libnodes = xml.findall("{%s}library_nodes" % COLLADA_NS )[0] libnodeurl = [node.attrib['id'] for node in libnodes.findall("*")] In [292]: len(libnodeurl) Out[292]: 249 In [293]: len(set(libnodeurl)) Out[293]: 249 instance_node ~~~~~~~~~~~~~~ :: urls = [nn.attrib['url'][1:] for nn in xml.findall(".//{%s}instance_node" % COLLADA_NS )] In [253]: len(urls) Out[253]: 5643 In [281]: len(set(urls)) Out[281]: 249 In [283]: set(urls) Out[283]: set(['__dd__Geometry__AdDetails__lvGasDistributionBoxE0xa904a88', '__dd__Geometry__CalibrationSources__lvGe68AirTop0xa8fee28', '__dd__Geometry__PoolDetails__lvTopCornerCableTray0xa9880d0', '__dd__Geometry__PoolDetails__lvInnShortParCableTray0xa9075d0', '__dd__Geometry__AdDetails__lvGDBTopFlange0xa9048b0', '__dd__Geometry__CalibrationBox__lvGdLSInCalibTubAbvLid0xa9028d8', ... In [294]: set(libnodeurl) == set(urls) # all the 249 urls are used from instance_node referents Out[294]: True Only 249 distinct url references, corresponding to logical volumes. So, for subcopying need to add instance_node referents to library_nodes. node ~~~~~~ :: nodeid = [n.attrib['id'] for n in xml.findall(".//{%s}node" % COLLADA_NS )] In [274]: len(nodeid) Out[274]: 5892 In [282]: len(set(nodeid)) Out[282]: 5892 In [284]: set(nodeid) Out[284]: set(['__dd__Geometry__RPCSupport__lvNearHbeamBigUnit--pvNearThwartLongAIRightDownY60xa8cc388', '__dd__Geometry__Pool__lvNearPoolOWS--pvVetoPmtNearOutFacein--pvNearOutFaceinWall5--pvNearOutFaceinWall5..8--pvVetoPmtUnit--pvPmtMount--pvMountRib1s--pvMountRib1s..2--pvMountRib1unit0xa9c29e8', '__dd__Geometry__RPCSupport__lvNearHbeamSmallUnit--pvNearThwartLongAIUpY20xa8c6928', '__dd__Geometry__Pool__lvNearPoolOWS--pvVetoPmtNearOutFacein--pvNearOutFaceinWall9--pvNearOutFaceinWall9..4--pvVetoPmtUnit--pvPmtMount--pvMountRib3s--pvMountRib3s..2--pvMountRib3unit0xa9ed818', '__dd__Geometry__Pool__lvNearPoolOWS--pvVetoPmtNearOutFacein--pvNearOutFaceinWall3--pvNearOutFaceinWall3..1--pvVetoPmtUnit--pvPmtMount--pvMountRib1s--pvMountRib1s..1--pvMountRib1unit0xa99b208', '__dd__Geometry__Pool__lvNearPoolOWS--pvVetoPmtNearOutFacein--pvNearOutFaceinWall8--pvNearOutFaceinWall8..7--pvVetoPmtUnit--pvPmtMount--pvMountRib3s--pvMountRib3s..1--pvMountRib3unit0xa9dcc58', '__dd__Geometry__Pool__lvNearPoolIWS--pvVetoPmtNearInn--pvNearInnWall7--pvNearInnWall7..10--pvVetoPmtUnit--pvPmtMount--pvMountRib3s--pvMountRib3s..1--pvMountRib3unit0xa958630', ... In [302]: set([len(n.findall("*")) for n in xml.findall(".//{%s}node" % COLLADA_NS )]) Out[302]: set([1, 2, 3, 36, 5, 6, 7, 73, 10, 11, 12, 1620, 179, 9, 521, 55, 4, 2939]) # distinct child counts for every node In [308]: print len([ET.tostring(n) for n in filter(lambda _:len(_.findall("*"))==1, xml.findall(".//{%s}node" % COLLADA_NS ))]) 165 In [307]: print "\n".join([ET.tostring(n) for n in filter(lambda _:len(_.findall("*"))==1, xml.findall(".//{%s}node" % COLLADA_NS ))]) # all 1 child nodes, mostly instance_geometry ... In [311]: print "\n".join([ET.tostring(n) for n in filter(lambda _:len(_.findall("*"))==2, xml.findall(".//{%s}node" % COLLADA_NS ))][0:1]) 6.12303e-17 1 0 -910 -1 6.12303e-17 0 0 0 0 1 0 0.0 0.0 0.0 1.0 In [313]: print "\n".join([ET.tostring(n) for n in filter(lambda _:len(_.findall("*"))==2, xml.findall(".//{%s}node" % COLLADA_NS ))][100:101]) 1 0 0 0 0 1 0 3695 0 0 1 -143 0.0 0.0 0.0 1.0 :: In [325]: print len([ET.tostring(n) for n in filter(lambda _:len(_.findall("*"))==1, xml.findall(".//{%s}node" % COLLADA_NS ))]) 165 In [314]: print len([ET.tostring(n) for n in filter(lambda _:len(_.findall("*"))==2, xml.findall(".//{%s}node" % COLLADA_NS ))]) 5683 In [315]: print len([ET.tostring(n) for n in filter(lambda _:len(_.findall("*"))==3, xml.findall(".//{%s}node" % COLLADA_NS ))]) 9 In [316]: print len([ET.tostring(n) for n in filter(lambda _:len(_.findall("*"))==4, xml.findall(".//{%s}node" % COLLADA_NS ))]) 4 In [317]: print len([ET.tostring(n) for n in filter(lambda _:len(_.findall("*"))==5, xml.findall(".//{%s}node" % COLLADA_NS ))]) 5 In [318]: print len([ET.tostring(n) for n in filter(lambda _:len(_.findall("*"))==6, xml.findall(".//{%s}node" % COLLADA_NS ))]) 6 In [319]: print len([ET.tostring(n) for n in filter(lambda _:len(_.findall("*"))==7, xml.findall(".//{%s}node" % COLLADA_NS ))]) 5 In [320]: print len([ET.tostring(n) for n in filter(lambda _:len(_.findall("*"))==8, xml.findall(".//{%s}node" % COLLADA_NS ))]) 0 In [321]: print len([ET.tostring(n) for n in filter(lambda _:len(_.findall("*"))==9, xml.findall(".//{%s}node" % COLLADA_NS ))]) 2 In [322]: print len([ET.tostring(n) for n in filter(lambda _:len(_.findall("*"))==10, xml.findall(".//{%s}node" % COLLADA_NS ))]) 3 In [323]: print len([ET.tostring(n) for n in filter(lambda _:len(_.findall("*"))==11, xml.findall(".//{%s}node" % COLLADA_NS ))]) 1 In [324]: print len([ET.tostring(n) for n in filter(lambda _:len(_.findall("*"))==12, xml.findall(".//{%s}node" % COLLADA_NS ))]) 2 All nodeid are distinct, corresponding to physical volumes. Eleven child example ~~~~~~~~~~~~~~~~~~~~~~ :: In [326]: print "\n".join([ET.tostring(n) for n in filter(lambda _:len(_.findall("*"))==11, xml.findall(".//{%s}node" % COLLADA_NS ))]) 1 0 0 0 0 1 0 0 0 0 1 150 0.0 0.0 0.0 1.0 1 0 0 8150 0 1 0 0 0 0 1 0 0.0 0.0 0.0 1.0 0.707107 0.707107 0 6603.82 -0.707107 0.707107 0 3603.82 0 0 1 0 0.0 0.0 0.0 1.0 6.12303e-17 1 0 0 -1 6.12303e-17 0 5150 0 0 1 0 0.0 0.0 0.0 1.0 -0.707107 0.707107 0 -6603.82 -0.707107 -0.707107 0 3603.82 0 0 1 0 0.0 0.0 0.0 1.0 -1 1.22461e-16 0 -8150 -1.22461e-16 -1 0 0 0 0 1 0 0.0 0.0 0.0 1.0 -0.707107 -0.707107 0 -6603.82 0.707107 -0.707107 0 -3603.82 0 0 1 0 0.0 0.0 0.0 1.0 6.12303e-17 -1 0 0 1 6.12303e-17 0 -5150 0 0 1 0 0.0 0.0 0.0 1.0 0.707107 -0.707107 0 6603.82 0.707107 0.707107 0 -3603.82 0 0 1 0 0.0 0.0 0.0 1.0 1 0 0 0 0 1 0 0 0 0 1 -5150 0.0 0.0 0.0 1.0