""" M. Littlefield """ # This file is part of MAUS: http://micewww.pp.rl.ac.uk:8080/projects/maus # # MAUS is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # MAUS is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with MAUS. If not, see . import shutil import os import libxml2 from xml.dom import minidom from geometry.ConfigReader import Configreader SCHEMA_PATH = os.environ["MAUS_ROOT_DIR"]+\ "/src/common_py/geometry/GDML_3_0_0/schema/gdml.xsd" class Formatter: #pylint: disable = R0902, R0912, R0914, R0915, C0103 """ @Class formatter this class formats the raw outputted fastrad files so their schema material file paths are correct This class opens the files which need the correct paths added and edits the file in situ. """ def __init__(self, path_in, path_out): """ @Method Class Constructor This is the class constructor which sets up the variables so they point at the correct files ready to edit. @Param Path The path to the directory which contains the fastrad outputted GDML files """ self.g4_step_max = Configreader().g4_step_max self.tof_0_file_number = Configreader().tof_0_file_number self.tof_1_file_number = Configreader().tof_1_file_number self.tof_2_file_number = Configreader().tof_2_file_number self.ckov1_file_number = Configreader().ckov1_file_number self.ckov2_file_number = Configreader().ckov2_file_number self.kl_file_number = Configreader().kl_file_number self.emr_file_number = Configreader().emr_file_number self.tracker0_file_number = Configreader().tracker0_file_number self.tracker1_file_number = Configreader().tracker1_file_number self.absorber0_file_number = Configreader().absorber0_file_number self.absorber1_file_number = Configreader().absorber1_file_number self.absorber2_file_number = Configreader().absorber2_file_number self.path_in = path_in self.path_out = path_out.rstrip('/') + '/' self.beamline_file = None self.coolingChannel_file = None self.maus_information_file = None self.configuration_file = None self.material_file = None self.tracker_file = None self.detmodulefiles = [] self.modulefiles = [] self.stepfiles = [] self.formatted = False self.txt_file = "" self.schema = SCHEMA_PATH self.usegdml = False gdmls = os.listdir(self.path_in) #if self.path_in == self.path_out: # paths_equal = True for fname in gdmls: print fname if fname[-5:] != '.gdml' and fname[-4:] != '.xml' and \ fname.find('.table') < 0: print fname+' not a gdml or xml file - ignored' elif fname.find('materials') >= 0: self.material_file = fname shutil.copy(os.path.join(self.path_in, fname), os.path.join(self.path_out, fname) ) elif fname.find('fastrad') >= 0 or \ fname.find('Fastrad') >= 0 or \ fname.find('Step_IV')>=0: self.configuration_file = fname elif fname.find('Cooling_Channel') >= 0: self.tracker_file = fname elif fname.find('Maus_Information') >= 0 \ or fname.find('maus_information') >= 0 \ or fname.find('Field') >= 0 \ or fname.find('field') >= 0: print 'Found ', fname self.maus_information_file = fname elif fname.find('Beamline') >= 0: self.beamline_file = fname elif fname.find('CoolingChannelInfo') >= 0: self.coolingChannel_file = fname print 'Found ', fname elif fname.find('Quad') >= 0 \ or fname.find('Dipole') >= 0 \ or fname.find('Solenoid') >= 0 \ or fname.find('AFC') >= 0: self.modulefiles.append(fname) elif fname.find('KL') >= 0 \ or fname.find('Tracker') >= 0: self.detmodulefiles.append(fname) else: self.stepfiles.append(fname) if self.maus_information_file == None: self.maus_information_file = 'Maus_Information.gdml' maus_information_location = os.environ['MAUS_ROOT_DIR'] + \ '/tests/py_unit/test_geometry/testCases/mausModuleSource' shutil.copy(os.path.join(maus_information_location, \ self.maus_information_file), os.path.join(self.path_in, self.maus_information_file) ) print 'Maus_Information file not found' + \ 'Maus_Information.gdml has been copied from ' + \ maus_information_location else: info = libxml2.parseFile(os.path.join(self.path_in, self.maus_information_file)) if len(info.xpathEval(\ "MICE_Information/Other_Information/Module")) > 0: self.usegdml = True def format_schema_location(self, gdmlfile): """ @method format_schema_location This method parses the GDML file into memory and alters the location of the schema location. It then rewrites the GDML with the valid schema. @param gdmlfile absolute path to the file which will have its schema location altered. """ # ignore the field maps. if gdmlfile.find('.table') < 0: xmldoc = minidom.parse(os.path.join(self.path_in, gdmlfile)) for node in xmldoc.getElementsByTagName("gdml"): if node.hasAttribute("xsi:noNamespaceSchemaLocation"): node.attributes['xsi:noNamespaceSchemaLocation'] = \ self.schema # print "Schema location corrected for ",gdmlfile fout = open(os.path.join(self.path_out, gdmlfile), 'w') xmldoc.writexml(fout) fout.close() else: shutil.copy(os.path.join(self.path_in, gdmlfile), \ os.path.join(self.path_out, gdmlfile)) def format_gdml_locations(self, gdmlfile): """ @method format_gdml_locations This method parses the GDML file into memoty and alters the location of the file names to absolute paths. """ docfile = os.path.join(self.path_out, gdmlfile) print "Reviewing content of ", docfile xmldoc = libxml2.parseFile(docfile) for vol in xmldoc.xpathEval("gdml/structure/volume/physvol"): if len(vol.xpathEval("file")) > 0: filenode = vol.xpathEval("file")[0] filename = filenode.prop("name") if filename.find(self.path_out) == -1: # add the path to the name newname = os.path.join(self.path_out, filename) # print "Replacing ", filename, " with ", newname filenode.setProp("name", newname) gfile = open(os.path.join(self.path_out, gdmlfile),'w') xmldoc.saveTo(gfile) gfile.close() xmldoc.freeDoc() def correct_gdml_locations(self, gdmlfile): """ @method format_gdml_locations This method parses the GDML file into memoty and alters the location of the file names to absolute paths. """ docfile = os.path.join(self.path_out, gdmlfile) # print "Reviewing content of ",docfile xmldoc = libxml2.parseFile(docfile) for vol in xmldoc.xpathEval("gdml/structure/volume/physvol"): if len(vol.xpathEval("file")) > 0: filenode = vol.xpathEval("file")[0] filename = filenode.prop("name") if filename.find(self.path_in) == -1: # add the path to the name newname = os.path.join(self.path_out, filename) # print "Replacing ",filename," with ",newname filenode.setProp("name", newname) # print "Saving to",os.path.join(self.path_out, gdmlfile) f = open(os.path.join(self.path_out, gdmlfile),'w') xmldoc.saveTo(f) f.close() xmldoc.freeDoc() def add_other_info(self): #pylint: disable = R0914, R0915, C0301 """ @method add_other_information THis methods adds other information needed by MAUS. This is mainly so we can add computer specific environment variables into the translated gdml files. """ maus_information = minidom.parse(os.path.join(self.path_out, \ self.maus_information_file)) file_numbers = None for node in maus_information.getElementsByTagName("FileNumbers"): file_numbers = node doc = minidom.Document() top_node = doc.createElement("Other_Information") doc.appendChild(top_node) maus_dir = doc.createElement("GDML_Files") path = self.path_out maus_dir.setAttribute("location", path) maus_dir.setAttribute("basefile", self.configuration_file) top_node.appendChild(maus_dir) g4_step = doc.createElement("G4StepMax") g4_step.setAttribute("Value", str(self.g4_step_max)) top_node.appendChild(g4_step) file_numbers = doc.createElement("FileNumbers") top_node.appendChild(file_numbers) tof_0_file_number = doc.createElement("Tof0FileNumber") tof_0_file_number.setAttribute("number", str(self.tof_0_file_number)) file_numbers.appendChild(tof_0_file_number) tof_1_file_number = doc.createElement("Tof1FileNumber") tof_1_file_number.setAttribute("number", str(self.tof_1_file_number)) file_numbers.appendChild(tof_1_file_number) tof_2_file_number = doc.createElement("Tof2FileNumber") tof_2_file_number.setAttribute("number", str(self.tof_2_file_number)) file_numbers.appendChild(tof_2_file_number) ckov1_file_number = doc.createElement("Ckov1FileNumber") ckov1_file_number.setAttribute("number", str(self.ckov1_file_number)) file_numbers.appendChild(ckov1_file_number) ckov2_file_number = doc.createElement("Ckov2FileNumber") ckov2_file_number.setAttribute("number", str(self.ckov2_file_number)) file_numbers.appendChild(ckov2_file_number) kl_file_number = doc.createElement("KLFileNumber") kl_file_number.setAttribute("number", str(self.kl_file_number)) file_numbers.appendChild(kl_file_number) emr_file_number = doc.createElement("EMRFileNumber") emr_file_number.setAttribute("number", str(self.emr_file_number)) file_numbers.appendChild(emr_file_number) tracker0_file_number = doc.createElement("Tracker0FileNumber") tracker0_file_number.setAttribute("number", str(self.tracker0_file_number)) file_numbers.appendChild(tracker0_file_number) tracker1_file_number = doc.createElement("Tracker1FileNumber") tracker1_file_number.setAttribute("number", str(self.tracker1_file_number)) file_numbers.appendChild(tracker1_file_number) absorber0_file_number = doc.createElement("Absorber0FileNumber") absorber0_file_number.setAttribute("number", str(self.absorber0_file_number)) file_numbers.appendChild(absorber0_file_number) absorber1_file_number = doc.createElement("Absorber1FileNumber") absorber1_file_number.setAttribute("number", str(self.absorber1_file_number)) file_numbers.appendChild(absorber1_file_number) absorber2_file_number = doc.createElement("Absorber2FileNumber") absorber2_file_number.setAttribute("number", str(self.absorber2_file_number)) file_numbers.appendChild(absorber2_file_number) if file_numbers != None: top_node.appendChild(file_numbers) maus_information = minidom.parse(os.path.join(self.path_out, \ self.maus_information_file)) old_node = maus_information.childNodes[0].childNodes[7] new_node = doc.childNodes[0] base_node = maus_information.childNodes[0] base_node.replaceChild(new_node, old_node) fout = open(os.path.join(self.path_out, self.maus_information_file),'w') maus_information.writexml(fout) fout.close() def merge_maus_info(self, gdmlfile): """ @method merge_maus_info This method adds the maus_information information to the configuration GDML. """ self.add_other_info() config = minidom.parse(os.path.join(self.path_out, gdmlfile)) # print "config file ", os.path.join(self.path_out, gdmlfile) # print "information file ", os.path.join(self.path_out, \ # self.maus_information_file) maus_information = minidom.parse(os.path.join(self.path_out, \ self.maus_information_file)) for node in maus_information.getElementsByTagName("MICE_Information"): maus_info = node # print str(node) # print str(config) root_node = config.childNodes[2] root_node.insertBefore(maus_info, root_node.childNodes[9]) fout = open(os.path.join(self.path_out, gdmlfile), 'w') root_node.writexml(fout) fout.close() def merge_run_info(self, gdmlfile): """ @method merge_run_info This method adds the run information to the maus_information_file GDML. """ run_info = False fin = open(os.path.join(self.path_in, gdmlfile)) for lines in fin.readlines(): if lines.find('run') >= 0 or lines.find('runs') >= 0: run_info = True fin.close() if run_info == False: maus_information = \ minidom.parse(os.path.join(self.path_in, gdmlfile)) beamline_path = os.path.join(self.path_in, self.beamline_file) beamline = minidom.parse(beamline_path) for node in beamline.getElementsByTagName("run"): run_info = node if type(run_info) == bool: raise IOError("Run number you have selected is not on the CDB") else: root_node = maus_information.childNodes[0].childNodes[1] root_node.insertBefore(run_info, root_node.childNodes[0]) fout = open(os.path.join(self.path_out, gdmlfile), 'w') maus_information.writexml(fout) fout.close() print 'Run information merged!' def merge_cooling_channel_info(self, gdmlfile): """ @method merge_maus_info This method adds the run information to the maus_information_file GDML. """ run_info = False fin = open(os.path.join(self.path_out, gdmlfile)) #for lines in fin.readlines(): # if lines.find('run') >= 0 or lines.find('runs') >= 0: # run_info = True fin.close() if run_info == False: maus_information = \ minidom.parse(os.path.join(self.path_out, \ gdmlfile)) coolingChannel_path = os.path.join(self.path_in, \ self.coolingChannel_file) # print coolingChannel_path coolingChannel = minidom.parse(coolingChannel_path) for node in coolingChannel.getElementsByTagName("coolingchannel"): cc_info = node if type(cc_info) == bool: raise IOError("Run number you have selected is not on the CDB") else: root_node = maus_information.childNodes[0].childNodes[1] root_node.insertBefore(cc_info, root_node.childNodes[0]) fout = open(os.path.join(self.path_out, gdmlfile), 'w') maus_information.writexml(fout) fout.close() print 'Cooling channel information merged!' def format_materials(self, gdmlfile): """ @method Format Materials This method opens a new GDML file in memory and creates a new Document Type Definition (DTD) which contains the correct location of the materials reference file produced by fastRad. @param GDMLFile The name of the file which will have its materials reference file location altered. """ impl = minidom.getDOMImplementation() docstr = 'gdml []' doctype = impl.createDocumentType(docstr, None, None) newdoc = impl.createDocument(None, "MAUS", doctype) config = minidom.parse(os.path.join(self.path_out, gdmlfile)) for node in config.getElementsByTagName("gdml"): rootelement = node for node2 in newdoc.getElementsByTagName("MAUS"): oldelement = node2 newdoc.replaceChild(rootelement, oldelement) self.txt_file = os.path.join(self.path_out, gdmlfile[:-5] + '.txt') fout = open(self.txt_file, 'w') newdoc.writexml(fout) fout.close() def insert_materials_ref(self, inputfile): #pylint: disable = R0201 """ @method Insert Materials Reference This method opens the GDML file and replaces the materials file reference call which is lost during the parsing of the GDML's in other methods. @param GDMLFile The name of the file which will have its materials reference replaced. """ fin = open(inputfile, 'r') gdmlfile = inputfile[:-4] + '.gdml' fout = open(gdmlfile, 'w') for line in fin.readlines(): if line.find('')>=0: matdef = '' new_line = line.replace(matdef, matdef+'&materials;') print >> fout, new_line else: print >> fout, line print >> fout, '' fin.close() fout.close() os.remove(inputfile) def format_check(self, gdmlfile): """ @method Format Check This method checks to see whether the file passed to it has already been formatted but looking for the string which will have been put in the file if it had already been formatted. This is to stop multiple materials reference calls being added as it cannot be added by parsing the GDML. @param GDML File The file to be checked. """ self.formatted = False fin = open(os.path.join(self.path_in, gdmlfile)) for lines in fin.readlines(): if lines.find('') >= 0: print gdmlfile + ' already formatted!' self.formatted = True fin.close() def format(self): """ @method Format This method calls all the other methods to format the necessary parts of the file. It will run through all of the fastRad outputted files within the folder location given to the class constructor. """ self.format_check(self.configuration_file) if self.beamline_file != None: self.merge_run_info(self.maus_information_file) else: shutil.copy(os.path.join(self.path_in, self.maus_information_file), os.path.join(self.path_out, self.maus_information_file)) if self.coolingChannel_file != None: self.merge_cooling_channel_info(self.maus_information_file) if self.formatted == False: print self.configuration_file self.format_schema_location(self.configuration_file) self.merge_maus_info(self.configuration_file) self.format_materials(self.configuration_file) self.format_gdml_locations(self.configuration_file) self.insert_materials_ref(self.txt_file) print "Formatted Configuration File" if self.tracker_file != None: self.format_check(self.tracker_file) if self.formatted == False: print self.tracker_file self.format_schema_location(self.tracker_file) self.merge_maus_info(self.tracker_file) self.format_materials(self.tracker_file) self.format_gdml_locations(self.tracker_file) self.insert_materials_ref(self.txt_file) print "Formatted cooling channel and trackers" for module in self.modulefiles: self.format_check(module) if self.formatted == False: self.format_schema_location(module) self.merge_maus_info(module) self.format_materials(module) self.format_gdml_locations(module) self.insert_materials_ref(self.txt_file) print "Formatted module files" self.stepfiles.extend(self.detmodulefiles) noofstepfiles = len(self.stepfiles) for num in range(0, noofstepfiles): self.format_check(self.stepfiles[num]) if self.formatted == False: self.format_schema_location(self.stepfiles[num]) self.format_materials(self.stepfiles[num]) self.insert_materials_ref(self.txt_file) print "Formatted " + str(num+1) + \ " of " + str(noofstepfiles) + " Geometry Files" print "Format Complete" def addDiffuserIrises(self): """ @ Module addDiffuserIrises Add the irises to the output gdml file """ mausInfo = libxml2.parseFile(os.path.join(self.path_out, \ self.maus_information_file)) mausInfoIn = libxml2.parseFile(os.path.join(self.path_in, \ self.maus_information_file)) runInfo = mausInfo.xpathEval(\ "MICE_Information/Configuration_Information/run") print runInfo diffuserSetting = [] if len(runInfo) > 0: diffuserSetting.append(runInfo[0].prop("diffuserThickness")) print "Will use diffuser setting ", diffuserSetting[0] else: print "Will use the default diffuser." # check the possible keys for suspected modulekeys = mausInfoIn.xpathEval(\ "MICE_Information/Other_Information/Module") keynode = next(x for x in \ modulekeys if x.prop('name').find('SolenoidUS') >= 0 \ or x.prop('name').find('Cooling_Channel') >= 0) key = keynode.prop('name') modrange = [float(keynode.prop('zmin')), float(keynode.prop('zmax'))] diffuser = mausInfo.xpathEval(\ "MICE_Information/Detector_Information/Diffuser") targetname = os.path.join(self.path_in, key+".gdml") print targetname target = libxml2.parseFile(targetname) vol = next(x for x in target.xpathEval("gdml/structure/volume") if x.prop("name").find(key + "_structvol") >= 0) for iris in diffuser: # extract the iris number, position and rotation inum = int(iris.xpathEval("Iris")[0].prop("name")) pos = [float(iris.xpathEval("Position")[0].prop("x")), \ float(iris.xpathEval("Position")[0].prop("y")), \ float(iris.xpathEval("Position")[0].prop("z"))] print key, pos[2], modrange pos[2] = pos[2] - (modrange[1] + modrange[0])/2. print key, pos[2], modrange posUnit = iris.xpathEval("Position")[0].prop("unit") rot = [float(iris.xpathEval("Rotation")[0].prop("x")), \ float(iris.xpathEval("Rotation")[0].prop("y")), \ float(iris.xpathEval("Rotation")[0].prop("z"))] rotUnit = iris.xpathEval("Rotation")[0].prop("unit") newNode = libxml2.newNode("physvol") fileNode = libxml2.newNode("file") if len(diffuserSetting) > 0: # test of whether the statement exists. print "Using diffuser setting ", int(diffuserSetting[0]) if (inum == 1 and int(diffuserSetting[0]) % 2 == 0) or \ (inum == 2 and (int(diffuserSetting[0]) % 4 == 0 or \ int(diffuserSetting[0]) % 4 == 1)) or \ (inum == 3 and (int(diffuserSetting[0]) < 4 or (int(diffuserSetting[0]) >= 8 and \ int(diffuserSetting[0]) < 12))) or \ (inum == 4 and int(diffuserSetting[0]) < 8): newNode.setProp("name", "iris" + str(inum)) fileNode.setProp("name", "iris" + str(inum) + "_open.gdml") else: newNode.setProp("name", "iris" + str(inum)) fileNode.setProp("name", "iris" + str(inum) + \ "_closed.gdml") else: if inum == 2 or inum == 3: print "Iris ", inum, " default closed introduced." newNode.setProp("name", "iris" + str(inum)) fileNode.setProp("name", "iris" + str(inum) + \ "_closed.gdml") else: print "Iris ", inum, " default open introduced." newNode.setProp("name", "iris" + str(inum)) fileNode.setProp("name", "iris" + str(inum) + "_open.gdml") newNode.addChild(fileNode) posNode = libxml2.newNode("position") posNode.setProp("name", "posRef_iris_" + str(inum)) posNode.setProp("x", str(pos[0])) posNode.setProp("y", str(pos[1])) posNode.setProp("z", str(pos[2])) posNode.setProp("unit", posUnit) newNode.addChild(posNode) rotNode = libxml2.newNode("rotation") rotNode.setProp("name", "rotRef_iris_"+str(inum)) rotNode.setProp("x", str(rot[0])) rotNode.setProp("y", str(rot[1])) rotNode.setProp("z", str(rot[2])) rotNode.setProp("unit", rotUnit) newNode.addChild(rotNode) vol.addChild(newNode) tgtfile = open(targetname, 'w') target.saveTo(tgtfile) tgtfile.close() target.freeDoc() mausInfo.freeDoc() def maus_info_to_gdml(self): """ To be used when synthesising the gdml files for direct use to define the geometry. Reformats the information file to use gdml headers. """ self.add_other_info() infofile = os.path.join(self.path_out, self.maus_information_file) info = libxml2.parseFile(infofile) doc = libxml2.parseDoc(""" """) root = doc.xpathEval("gdml")[0] root.addChildList(info.xpathEval("MICE_Information")[0].copyNode(1)) f = open(infofile,"w") doc.saveTo(f) f.close() doc.freeDoc() info.freeDoc() def formatForGDML(self): """ @method FormatForGDML This method calls all the other methods to format the necessary parts of the file. It will run through all of the fastRad outputted files within the folder location given to the class constructor. """ # self.format_check(self.configuration_file) if self.beamline_file != None: self.merge_run_info(self.maus_information_file) else: shutil.copy(os.path.join(self.path_in, self.maus_information_file), os.path.join(self.path_out, self.maus_information_file)) self.add_other_info() if self.coolingChannel_file != None: self.merge_cooling_channel_info(self.maus_information_file) # if self.formatted == False: print self.configuration_file self.format_schema_location(self.configuration_file) self.correct_gdml_locations(self.configuration_file) self.addDiffuserIrises() # self.merge_maus_info(self.configuration_file) print "Formatted Configuration File" self.maus_info_to_gdml() if self.tracker_file != None: self.format_check(self.tracker_file) if self.formatted == False: print self.tracker_file self.format_schema_location(self.tracker_file) self.correct_gdml_locations(self.tracker_file) # print "Formatted cooling channel and trackers" for module in self.modulefiles: self.format_check(module) if self.formatted == False: self.format_schema_location(module) self.correct_gdml_locations(module) # print "Formatted module file ",module for module in self.detmodulefiles: # self.format_check(module) if self.formatted == False: self.format_schema_location(module) self.correct_gdml_locations(module) # print "Formatted detector module file ",module noofstepfiles = len(self.stepfiles) for num in range(0, noofstepfiles): self.format_schema_location(self.stepfiles[num]) # print "Formatted step file ",self.stepfiles[num] # self.correct_gdml_locations(self.stepfiles[num]) print "Format Complete" def main(): """ Main Function. """ if __name__ == "__main__": main()