def viewResults(r, scenIdall, detector): """ Opens Results table """ # need a correspondence table in order to display results! session = Session() settings = RaseSettings() default_corr_table = session.query(CorrespondenceTable).filter_by( is_default=True).one_or_none() if not default_corr_table: msgbox = QMessageBox( QMessageBox.Question, 'No Correspondence Table set!', 'No correspondence table set! Would you like to set a correspondence table now?' ) msgbox.addButton(QMessageBox.Yes) msgbox.addButton(QMessageBox.No) answer = msgbox.exec() if answer == QMessageBox.Yes: CorrespondenceTableDialog().exec_() settings.setIsAfterCorrespondenceTableCall(True) else: return selected_scenarios = scenIdall r.calculateScenarioStats(1, selected_scenarios, detector) ViewResultsDialog(r, selected_scenarios, detector).exec_()
def export_scenarios(scenarios_ids, file_path): """ Export scenarios from database to xml file """ session = Session() scenarios = [session.query(Scenario).filter_by(id=scenid).first() for scenid in scenarios_ids] scen_io = ScenariosIO() xml_str = scen_io.scenario_export(scenarios) Path(file_path).write_text(xml_str)
def delete_scenario(scenario_ids, sample_root_dir): """ Delete scenarios from database and cleanup sample folders """ session = Session() for id in scenario_ids: scenDelete = session.query(Scenario).filter(Scenario.id == id) matDelete = session.query(ScenarioMaterial).filter(ScenarioMaterial.scenario_id == id) backgMatDelete = session.query(ScenarioBackgroundMaterial).filter(ScenarioBackgroundMaterial.scenario_id == id) # folders folders = [name for name in glob.glob(os.path.join(sample_root_dir, "*" + id + "*"))] for folder in folders: shutil.rmtree(folder) # database scenObj = scenDelete.first() scenObj.scenario_groups.clear() matDelete.delete() backgMatDelete.delete() scenDelete.delete() session.commit() session.close()
def cleanup_scenarios(rangefind_rep, scenIds): """Remove scenarios from the database that were rangefinders, i.e.: low replication scenarios""" settings = RaseSettings() session = Session() scenarios = [] for scen in scenIds: scenarios.append(session.query(Scenario).filter_by(id=scen).first()) scens_to_delete = [] for scen in scenarios: if scen.replication == rangefind_rep: scens_to_delete.append(scen.id) delete_scenario(scens_to_delete, settings.getSampleDirectory()) session.commit()
class ScenarioDialog(ui_create_scenario_dialog.Ui_ScenarioDialog, QDialog): def __init__(self, rase_gui, id=None, duplicate=[], auto_s=False): QDialog.__init__(self) self.setupUi(self) self.rase_gui = rase_gui self.tblMaterial.setContextMenuPolicy(Qt.CustomContextMenu) self.tblBackground.setContextMenuPolicy(Qt.CustomContextMenu) self.id = id self.auto_s = auto_s self.settings = RaseSettings() self.scenarioHasChanged = False self.groupHasChanged = False self.duplicate = duplicate self.session = Session() self.txtAcqTime.setText('30') self.txtAcqTime.setToolTip( "Enter comma-separated values OR range as min-max:step OR range followed by comma-separated values" ) self.txtReplication_2.setText('100') int_validator = QIntValidator() self.txtAcqTime.setValidator(RegExpSetValidator()) self.txtReplication_2.setValidator(int_validator) self.tblMaterial.setHorizontalHeaderItem(INTENSITY, QTableWidgetItem('Intensity')) self.tblBackground.setHorizontalHeaderItem( INTENSITY, QTableWidgetItem('Intensity')) self.tblMaterial.customContextMenuRequested.connect( lambda x, table=self.tblMaterial: self.context_auto_range( x, self.tblMaterial)) self.tblBackground.customContextMenuRequested.connect( lambda x, table=self.tblBackground: self.context_auto_range( x, self.tblBackground)) # set material table self.tblMaterial.setItemDelegate( MaterialDoseDelegate(self.tblMaterial, unitsCol=UNITS, materialCol=MATERIAL, intensityCol=INTENSITY)) self.tblMaterial.setRowCount(10) for row in range(self.tblMaterial.rowCount()): self.tblMaterial.setItem(row, UNITS, QTableWidgetItem()) self.tblMaterial.setItem(row, INTENSITY, QTableWidgetItem()) self.tblMaterial.setItem(row, MATERIAL, QTableWidgetItem()) self.tblMaterial.item(row, INTENSITY).setToolTip( "Enter comma-separated values OR range as min-max:step OR range followed by comma-separated values" ) self.tblMaterial.setRowHeight(row, 22) # set background table self.tblBackground.setItemDelegate( MaterialDoseDelegate(self.tblBackground, unitsCol=UNITS, materialCol=MATERIAL, intensityCol=INTENSITY, auto_s=self.auto_s)) self.tblBackground.setRowCount(10) for row in range(self.tblBackground.rowCount()): self.tblBackground.setItem(row, UNITS, QTableWidgetItem()) self.tblBackground.setItem(row, INTENSITY, QTableWidgetItem()) self.tblBackground.setItem(row, MATERIAL, QTableWidgetItem()) self.tblBackground.item(row, INTENSITY).setToolTip( "Enter comma-separated values OR range as min-max:step OR range followed by comma-separated values" ) self.tblBackground.setRowHeight(row, 22) # fill influence list for influence in self.session.query(Influence): self.lstInfluences.addItem(QListWidgetItem(influence.name)) self.comboDetectorSelect.addItems( ["all detectors"] + [s.name for s in self.session.query(Detector).all()]) self.comboDetectorSelect.currentIndexChanged.connect( self.updateTableDelegate) # display a previous scenario if defined if self.id: if not self.duplicate: self.setWindowTitle("Scenario Edit") scenario = self.session.query(Scenario).get(id) materials = scenario.scen_materials bckg_materials = scenario.scen_bckg_materials influences = scenario.influences for table, mat_list in zip( (self.tblMaterial, self.tblBackground), (materials, bckg_materials)): for rowCount, mat in enumerate(mat_list): item = QTableWidgetItem(units_labels[mat.fd_mode]) item.setData(Qt.UserRole, mat.fd_mode) table.setItem(rowCount, 0, item) table.setItem(rowCount, 1, QTableWidgetItem(mat.material_name)) table.setItem(rowCount, 2, QTableWidgetItem(str(mat.dose))) self.txtAcqTime.setText(str(scenario.acq_time)) self.txtReplication_2.setText(str(scenario.replication)) for influence in influences: lst = self.lstInfluences.findItems(influence.name, Qt.MatchExactly)[0] lst.setSelected(True) self.groups = self.getGroups() else: self.setWindowTitle("Build Scenario from Other Scenario") scens = [ self.session.query(Scenario).filter_by(id=scen).first() for scen in self.duplicate ] scenario = scens[0] materials = scenario.scen_materials back_materials = scenario.scen_bckg_materials influences = scenario.influences mat_dict = {} back_dict = {} mat_fd = [] back_fd = [] for mat in materials: mat_fd.append((mat.material_name, mat.fd_mode)) mat_dict[mat.material_name] = set([mat.dose]) for back in back_materials: back_fd.append((back.material_name, back.fd_mode)) back_dict[back.material_name] = set([back.dose]) if len(scens) > 1: for scen in scens[1:]: mat_dict = self.make_matDict(scen.scen_materials, mat_dict) back_dict = self.make_matDict(scen.scen_bckg_materials, back_dict) if influences: influences.append[scen.influences] else: influences = scen.influences for table, material_dictionary, fd_list in \ zip((self.tblMaterial, self.tblBackground), (mat_dict, back_dict), (mat_fd, back_fd)): mat_list_tup = [(k, v) for k, v in material_dictionary.items()] for rowCount, (mat, fd_mode) in enumerate( zip(mat_list_tup, fd_list)): doses = [str(d) for d in sorted(mat[1])] item = QTableWidgetItem(units_labels[fd_mode[1]]) item.setData(Qt.UserRole, fd_mode[1]) table.setItem(rowCount, 0, item) table.setItem(rowCount, 1, QTableWidgetItem(str(mat[0]))) table.setItem(rowCount, 2, QTableWidgetItem(str(','.join(doses)))) self.txtAcqTime.setText(str(scenario.acq_time)) for influence in influences: lst = self.lstInfluences.findItems(influence.name, Qt.MatchExactly)[0] lst.setSelected(True) self.groups = self.getGroups() else: self.groups = [] if self.auto_s and self.rase_gui.static_background: for rowCount, mat in enumerate(self.rase_gui.static_background): mat = mat[0] item = QTableWidgetItem(units_labels[mat[0]]) item.setData(Qt.UserRole, mat[0]) self.tblBackground.setItem(rowCount, 0, item) self.tblBackground.setItem(rowCount, 1, QTableWidgetItem(mat[1].name)) self.tblBackground.setItem(rowCount, 2, QTableWidgetItem(str(mat[2]))) # signal/slot connections (this has to be done after_ the previous scenario is loaded) self.tblMaterial.cellChanged.connect(self.scenarioChanged) self.tblBackground.cellChanged.connect(self.scenarioChanged) self.tblMaterial.cellChanged.connect(self.updateScenariosList) self.tblBackground.cellChanged.connect(self.updateScenariosList) self.lstInfluences.itemSelectionChanged.connect(self.scenarioChanged) self.txtAcqTime.textChanged.connect(self.scenarioChanged) self.txtReplication_2.textChanged.connect(self.scenarioChanged) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) def make_matDict(self, mats, m_dict): for mat in mats: m_dict[mat.material_name].update([mat.dose]) return m_dict def getGroups(self): if self.id: if not self.duplicate: scen_edit = self.session.query(Scenario).filter_by( id=self.id).first() return [grp.name for grp in scen_edit.scenario_groups] else: scens = [ self.session.query(Scenario).filter_by(id=scen).first() for scen in self.duplicate ] grps = set() for scen in scens: grps.update([grp.name for grp in scen.scenario_groups]) return grps else: return [] @pyqtSlot() def scenarioChanged(self): """ Listens for Scenario changed """ self.scenarioHasChanged = True @pyqtSlot() def groupChanged(self): """ Listens for group changed """ self.groupHasChanged = True @pyqtSlot(int) def updateTableDelegate(self, index): if index == 0: selected_detname = None else: selected_detname = self.comboDetectorSelect.currentText() self.tblMaterial.setItemDelegate( MaterialDoseDelegate(self.tblMaterial, unitsCol=UNITS, materialCol=MATERIAL, intensityCol=INTENSITY, selected_detname=selected_detname)) self.tblBackground.setItemDelegate( MaterialDoseDelegate(self.tblBackground, unitsCol=UNITS, materialCol=MATERIAL, intensityCol=INTENSITY, selected_detname=selected_detname, auto_s=self.auto_s)) @pyqtSlot(QPoint) def context_auto_range(self, point, table): current_cell = table.itemAt(point) # show the context menu only if on an a valid part of the table if current_cell: column = current_cell.column() if column == 2: autorangeAction = QAction('Auto-Define Range', self) menu = QMenu(table) menu.addAction(autorangeAction) action = menu.exec_(table.mapToGlobal(point)) if action == autorangeAction: auto_list = self.auto_range() if auto_list: current_cell.setText(','.join(auto_list)) @pyqtSlot(bool) def auto_range(self): dialog = ScenarioRange(self) dialog.setWindowModality(Qt.WindowModal) if dialog.exec_(): if dialog.points: return dialog.points @pyqtSlot() def updateScenariosList(self): materialStr = "" for row in range(self.tblMaterial.rowCount()): untStr = self.tblMaterial.item(row, UNITS).text() matStr = self.tblMaterial.item(row, MATERIAL).text() intStr = self.tblMaterial.item(row, INTENSITY).text() if matStr and untStr and intStr: if (len(materialStr) > 0): materialStr = materialStr + "\n " materialStr = materialStr + '{}({})'.format(matStr, ', '.join("{:.5f}".format(float(dose)) for dose in self.getSet(self.tblMaterial.item(row, INTENSITY)))) + \ ', Units: ' + self.tblMaterial.item(row, UNITS).data(Qt.UserRole).title() backgroundStr = "" for row in range(self.tblBackground.rowCount()): untStr = self.tblBackground.item(row, UNITS).text() matStr = self.tblBackground.item(row, MATERIAL).text() intStr = self.tblBackground.item(row, INTENSITY).text() if matStr and untStr and intStr: if (len(backgroundStr) > 0): backgroundStr = backgroundStr + "\n " backgroundStr = backgroundStr + '{}({})'.format(matStr, ', '.join("{:.5f}".format(float(dose)) for dose in self.getSet(self.tblBackground.item(row, INTENSITY)))) + \ ', Units: ' + self.tblBackground.item(row, UNITS).data(Qt.UserRole).title() self.txtScenariosList_2.setText( f"Source materials:\n {materialStr} \n\nBackground materials:\n {backgroundStr}" ) @pyqtSlot(bool) def on_btnGroups_clicked(self, checked): dialog = GroupSettings(self, groups=self.groups) dialog.setWindowModality(Qt.WindowModal) if dialog.exec_(): self.groups = dialog.n_groups @pyqtSlot() def accept(self): if self.auto_s: materials_doses = [] for matsT in [self.tblBackground]: for row in range(matsT.rowCount()): matName = matsT.item(row, MATERIAL).text() if matName: matArr = [] for dose in self.getSet(matsT.item(row, 2)): mat = self.session.query(Material).filter_by( name=matName).first() fd_mat = matsT.item(row, UNITS).data(Qt.UserRole) matArr.append((fd_mat, mat, dose)) materials_doses.append(matArr) self.rase_gui.static_background = materials_doses return QDialog.accept(self) self.tblMaterial.setCurrentIndex(QModelIndex( )) # so that if table item is being edited it will commit the data # if this is edit rather than create, need to treat differently: if self.id and not self.duplicate: # check if the scenario has been changed by the user # Note that this approach considers a change even if # the user rewrites the previous entry identically if not self.scenarioHasChanged: # update just the group for the same scenario # so as not to change the scenario ID scen = self.session.query(Scenario).get(self.id) self.provide_message_new_groups = False self.add_groups_to_scen(scen, self.groups, add_groups=True) self.session.commit() return QDialog.accept(self) else: # clear the existing scenario first self.scenario_delete() # replication and influences replication = int(self.txtReplication_2.text()) influences = [] # element type: Influence for index in self.lstInfluences.selectedIndexes(): influences.append( self.session.query(Influence).filter_by( name=self.lstInfluences.itemFromIndex( index).text()).first()) materials_doses = [[], []] for i, matsT in enumerate([self.tblMaterial, self.tblBackground]): for row in range(matsT.rowCount()): matName = matsT.item(row, MATERIAL).text() if matName: matArr = [] for dose in self.getSet(matsT.item(row, 2)): mat = self.session.query(Material).filter_by( name=matName).first() fd_mat = matsT.item(row, UNITS).data(Qt.UserRole) matArr.append((fd_mat, mat, dose)) materials_doses[i].append(matArr) # cartesian product to break out scenarios from scenario group integrity_fail = False duplicate = False self.provide_message_new_groups = True for acqTime in self.getSet(self.txtAcqTime): mm = product(*materials_doses[0]) bb = product(*materials_doses[1]) for mat_dose_arr, bckg_mat_dose_arr in product(mm, bb): scenMaterials = [ ScenarioMaterial(material=m, dose=float(d), fd_mode=u) for u, m, d in mat_dose_arr ] bcgkScenMaterials = [ ScenarioBackgroundMaterial(material=m, dose=float(d), fd_mode=u) for u, m, d in bckg_mat_dose_arr ] scen_groups = [] try: for groupname in self.groups: scen_groups.append( self.session.query(ScenarioGroup).filter_by( name=groupname).first()) if not scen_groups: scen_groups.append( self.session.query(ScenarioGroup).filter_by( name='default_group').first()) # if just changing groups, add to new group without creating a new scenario scen_hash = Scenario.scenario_hash(float(acqTime), scenMaterials, bcgkScenMaterials, influences) scen_exists = self.session.query(Scenario).filter_by( id=scen_hash).first() add_groups = False if scen_exists: # and (sorted([g.name for g in scen_exists.scenario_groups]) != # sorted([g.name for g in scen_groups])): for group in scen_groups: if group not in scen_exists.scenario_groups: add_groups = True break all_groups = set(g.name for g in scen_exists.scenario_groups + scen_groups) all_groups.update(self.groups) # don't allow duplicate scenarios, unless there are other scenarios in the group that are # simply having their group changed. In which case, pass by those groups without impact. duplicate = self.add_groups_to_scen( scen_exists, all_groups, add_groups=add_groups) else: Scenario(float(acqTime), replication, scenMaterials, bcgkScenMaterials, influences, scen_groups) # if inputting multiple scenarios with at least one preexisting scenario (i.e.: this only happens when # the loop traverses more than once and the database is accessed again) except (IntegrityError, FlushError): self.integrity_message(materials_doses) integrity_fail = True break # if inputting a single scenario that already exists if not integrity_fail: if duplicate: self.integrity_message(materials_doses) else: try: self.session.commit() return QDialog.accept(self) except (IntegrityError, FlushError): self.integrity_message(materials_doses) def add_groups_to_scen(self, scen, all_groups, add_groups=False): """ Clear groups associated with a scenario and append new ones """ if add_groups: scen.scenario_groups.clear() for groupname in all_groups: scen.scenario_groups.append( self.session.query(ScenarioGroup).filter_by( name=groupname).first()) if self.provide_message_new_groups: QMessageBox.information( self, 'Record Exists', 'At least one defined scenario is already in the database; ' 'adding scenario to additional groups.') self.provide_message_new_groups = False elif self.provide_message_new_groups: return True return False def scenario_delete(self): """ Clear existing scenario before adding the modified version """ scenDelete = self.session.query(Scenario).filter( Scenario.id == self.id) matDelete = self.session.query(ScenarioMaterial).filter( ScenarioMaterial.scenario_id == self.id) bckgMatDelete = self.session.query(ScenarioBackgroundMaterial).filter( ScenarioBackgroundMaterial.scenario_id == self.id) scenTableAssocDelete = scenDelete.first() scenTableAssocDelete.scenario_groups.clear() scenTableAssocDelete.influences.clear() matDelete.delete() bckgMatDelete.delete() scenDelete.delete() def integrity_message(self, materials_doses): if (materials_doses[0] and len(list(product(*materials_doses[0]))) > 1) or \ (materials_doses[1] and len(list(product(*materials_doses[1]))) > 1): QMessageBox.critical( self, 'Record Exists', 'At least one defined scenario is already in the database! ' 'Please change scenarios.') else: QMessageBox.critical( self, 'Record Exists', 'This scenario is already in the database! Please change scenario.' ) self.session.rollback() # TODO: combine these two methods using a cellChanged.connect() @pyqtSlot(int, int) def on_tblMaterial_cellChanged(self, row, col): """ Listens for Material table cell changed """ if col == UNITS: if self.tblMaterial.item(row, MATERIAL) and self.tblMaterial.item( row, INTENSITY): if not self.tblMaterial.item(row, UNITS).data(Qt.UserRole): self.tblMaterial.item(row, MATERIAL).setText('') self.tblMaterial.item(row, INTENSITY).setText('') elif self.tblMaterial.item(row, MATERIAL): units = self.tblMaterial.item(row, UNITS) matName = self.tblMaterial.item(row, MATERIAL) doseItem = self.tblMaterial.item(row, INTENSITY) self.set_otherCols_fromUnit(units, matName, doseItem) if col == MATERIAL: units = self.tblMaterial.item(row, UNITS) matName = self.tblMaterial.item(row, MATERIAL).text() doseItem = self.tblMaterial.item(row, INTENSITY) self.set_otherCols_fromMat(units, matName, doseItem) @pyqtSlot(int, int) def on_tblBackground_cellChanged(self, row, col): """ Listens for Material table cell changed """ if col == UNITS: if self.tblBackground.item( row, MATERIAL) and self.tblBackground.item(row, INTENSITY): if not self.tblBackground.item(row, UNITS).data(Qt.UserRole): self.tblBackground.item(row, MATERIAL).setText('') self.tblBackground.item(row, INTENSITY).setText('') elif self.tblBackground.item(row, MATERIAL): units = self.tblBackground.item(row, UNITS) matName = self.tblBackground.item(row, MATERIAL) doseItem = self.tblBackground.item(row, INTENSITY) self.set_otherCols_fromUnit(units, matName, doseItem) if col == MATERIAL: units = self.tblBackground.item(row, UNITS) matName = self.tblBackground.item(row, MATERIAL).text() doseItem = self.tblBackground.item(row, INTENSITY) self.set_otherCols_fromMat(units, matName, doseItem) def set_otherCols_fromUnit(self, units, matName, doseItem): textKeep = False if self.comboDetectorSelect.currentIndex() == 0: detector_list = [ detector for detector in Session().query(Detector) ] else: detector_list = [ Session().query(Detector).filter_by( name=self.comboDetectorSelect.currentText()).first() ] for detector in detector_list: for baseSpectrum in detector.base_spectra: if baseSpectrum.material.name == matName.text( ) and not textKeep: if (units.data(Qt.UserRole) == 'DOSE' and isinstance(baseSpectrum.rase_sensitivity, float)) or \ (units.data(Qt.UserRole) == 'FLUX' and isinstance(baseSpectrum.flux_sensitivity, float)): textKeep = True if not textKeep: matName.setText('') def set_otherCols_fromMat(self, units, matName, doseItem): if matName: if not doseItem.text(): doseItem.setText('0.1') if not units.text(): textSet = False if self.comboDetectorSelect.currentIndex() == 0: detector_list = [ detector for detector in Session().query(Detector) ] else: detector_list = [ Session().query(Detector).filter_by( name=self.comboDetectorSelect.currentText()).first( ) ] for detector in detector_list: for baseSpectrum in detector.base_spectra: if baseSpectrum.material.name == matName and not textSet: units.tableWidget().blockSignals(True) if isinstance(baseSpectrum.rase_sensitivity, float): units.setText(units_labels['DOSE']) units.setData(Qt.UserRole, 'DOSE') textSet = True else: units.setText(units_labels['FLUX']) units.setData(Qt.UserRole, 'FLUX') units.tableWidget().blockSignals(False) def getSet(self, dialogField): values = [] groups = dialogField.text().split(',') for group in groups: group = group.strip() if '-' in group and ':' in group: start, stop, step = [ float(x) for x in re.match(r'([^-]+)-([^:]+):(.*)', group).groups() ] if step == 0: values.append(start) values.append(stop) else: while start <= stop: values.append(start) start += step else: values.append(group) return values
def _getCountsDoseAndSensitivity(scenario, detector, degradations=None): """ :param scenario: :param detector: :return: """ session = Session() # distortion: distort ecal with influence factors ecal = [detector.ecal3, detector.ecal2, detector.ecal1, detector.ecal0] energies = np.polyval(ecal, np.arange(detector.chan_count)) new_influences = [] bin_widths = np.zeros([len(scenario.influences), len(energies)]) for index, influence in enumerate(scenario.influences): detInfl = session.query(DetectorInfluence).filter_by(influence_name=influence.name).first() new_infl = [detInfl.infl_0, detInfl.infl_1, detInfl.infl_2, detInfl.fixed_smear, detInfl.linear_smear] if degradations: new_infl = [infl + deg for infl, deg in zip(new_infl, degradations[index])] # deal with potential negative values for position, n_inf in enumerate(new_infl): if n_inf < 0: if not position == 1: new_infl[position] = 0 else: new_infl[position] = 0.0001 new_influences.append(new_infl) if new_infl[0] != 0 or new_infl[2] != 0 or new_infl[1] != 1: energies = np.polyval([new_infl[2], (new_infl[1]), new_infl[0]], energies) # convert fixed energy smear distortion from energy to bins if new_infl[3] != 0: e_width = new_infl[3] / 2 for sub_index, energy in enumerate(energies): b0 = np.roots([new_infl[2], new_infl[1], new_infl[0] - (energy - e_width)]) b1 = np.roots([new_infl[2], new_infl[1], new_infl[0] - (energy + e_width)]) bin_widths[index][sub_index] = max(b1) - max(b0) # get dose, counts and sensitivity for each material countsDoseAndSensitivity = [] for scenMaterial in scenario.scen_materials + scenario.scen_bckg_materials: baseSpectrum = (session.query(BaseSpectrum) .filter_by(detector_name=detector.name, material_name=scenMaterial.material_name) ).first() counts = baseSpectrum.get_counts_as_np() if scenario.influences: for index, infl in enumerate(new_influences): counts = apply_distortions(infl, counts, bin_widths[index], energies, ecal) if scenMaterial.fd_mode == 'FLUX': countsDoseAndSensitivity.append((counts, scenMaterial.dose, baseSpectrum.flux_sensitivity)) else: countsDoseAndSensitivity.append((counts, scenMaterial.dose, baseSpectrum.rase_sensitivity)) # if the detector has an internal calibration source, it needs to be added with special treatment if detector.includeSecondarySpectrum and detector.secondary_type == secondary_type['internal']: secondary_spectrum = (session.query(BackgroundSpectrum).filter_by(detector_name=detector.name)).first() counts = secondary_spectrum.get_counts_as_np() # apply distortion on counts if scenario.influences: for index, infl in enumerate(new_influences): counts = apply_distortions(infl, counts, bin_widths[index], energies, ecal) # extract counts per second cps = sum(counts)/secondary_spectrum.livetime # the internal calibration spectrum is scaled only by time # so the sensitivity parameter is set to the cps and the dose to 1 countsDoseAndSensitivity.append((counts, 1.0, cps)) return countsDoseAndSensitivity
def generate_curve(r, input_data, advanced): """Primary function where the points that make up the S-curve are determined""" session = Session() if advanced['custom_name'] == '[Default]': group_name = ("AutoScurve_" + input_data['instrument'] + '_' + input_data['source']) else: suffix = 0 while True: if session.query(ScenarioGroup).filter_by( name=advanced['custom_name']).first(): if not session.query(ScenarioGroup).filter_by( name=advanced['custom_name']).first().scenarios: group_name = (advanced['custom_name']) break else: suffix += 1 advanced['custom_name'] = advanced[ 'custom_name'] + '_' + str(suffix) else: group_name = (advanced['custom_name']) break detName = [input_data['instrument']] detector = session.query(Detector).filter_by(name=detName[0]).first() condition = False already_done = False expand = 0 first_run = True # All scenarios within the group that have the same source/backgrounds make_scen_group(session, group_name, input_data) scenIdall = make_scenIdall(session, input_data) # scenarios that will be rerun in this run scenIds_no_persist = [] maxback = 0 if input_data['background']: for bmat in input_data['background']: bmat = bmat[0] if bmat[0] == input_data['source_fd']: if maxback == 0: maxback = float(bmat[2]) else: maxback = max(maxback, float(bmat[2])) if not maxback: maxback = 0.1 while not condition: # newly generated scenIds, and all scenIds with source/backgrounds as defined in the auto s-curve gui scenIds, scenIds_no_persist, scenIdall = gen_scens( input_data, advanced, session, scenIdall, scenIds_no_persist, group_name, advanced['repetitions']) abort = run_scenarios(r, scenIds, detector, input_data['replay'], condition, expand, first_run) first_run = False if abort: cleanup_scenarios(advanced['repetitions'], scenIds_no_persist) return r.calculateScenarioStats(1, scenIdall, detName) if input_data['results_type'] == 'C&C': results = r.scenario_stats_df['C&C'] elif input_data['results_type'] == 'TP': results = r.scenario_stats_df['TP'] elif input_data['results_type'] == 'Precision': results = r.scenario_stats_df['Precision'] elif input_data['results_type'] == 'Recall': results = r.scenario_stats_df['Recall'] elif input_data['results_type'] == 'Fscore': results = r.scenario_stats_df['F_Score'] else: # to add more later results = r.scenario_stats_df['PID'] if not input_data['invert_curve']: results = results.sort_values() else: results = results.sort_values(ascending=False) if max(results) >= advanced['upper_bound'] and min( results) <= advanced['lower_bound']: """If there are values surrounding the rising edge""" # find scenarios in for cases ids_on_edge = [] start_list = [] end_list = [] prev_point = False for index, result in enumerate(results): if (not input_data['invert_curve'] and result <= advanced['lower_bound']) or \ (input_data['invert_curve'] and result >= advanced['upper_bound']): start_list.append(results.index[index].split('*')[0]) if prev_point: ids_on_edge = [ ] # rose and then dropped back down (i.e.: fluctuations) prev_point = False elif advanced['lower_bound'] <= result <= advanced[ 'upper_bound']: ids_on_edge.append(results.index[index].split('*')[0]) prev_point = True elif (not input_data['invert_curve'] and result >= advanced['upper_bound']) or \ (input_data['invert_curve'] and result <= advanced['lower_bound']): end_list.append(results.index[index].split('*')[0]) # Grab doses for scenarios on the edges of the S-curve. The first value in each list is # the value that is the second closest to the rising edge, and the second is the closest start_val = [-1, -1] end_val = [-1, -1] for scenid in scenIdall: scen = session.query(Scenario).filter_by(id=scenid).first() if scenid in start_list: if start_val[1] == -1 or scen.scen_materials[ 0].dose >= start_val[1]: start_val = set_bounds(start_val, scen.scen_materials[0].dose) if scen.id in end_list: if end_val[1] == -1 or scen.scen_materials[ 0].dose < end_val[1]: end_val = set_bounds(end_val, scen.scen_materials[0].dose) # check if there are enough points on the rising edge if len(ids_on_edge) >= advanced['rise_points']: condition = True # to avoid persistence errors by reusing a value with the same ID but different replications edge_count, bound_scens_start, bound_scens_end = check_edge_ids( session, input_data['input_reps'], start_list, end_list, ids_on_edge, detector) if edge_count < advanced[ 'rise_points'] or bound_scens_start < 2 or bound_scens_end < 2: advanced['min_guess'] = start_val[0] * 0.9 advanced['max_guess'] = end_val[0] * 1.1 advanced['num_points'] = advanced['rise_points'] + 4 else: already_done = True else: # avoid infinite loop due to being stuck on edge cases. Moves slightly inward to better populate edge if start_val[1] == advanced['min_guess']: advanced['min_guess'] = start_val[1] + ( end_val[1] - start_val[1]) * 0.01 else: advanced['min_guess'] = start_val[1] if end_val[1] == advanced['max_guess']: advanced['max_guess'] = end_val[1] - (end_val[1] - start_val[1]) * 0.01 else: advanced['max_guess'] = end_val[1] advanced['num_points'] = advanced['rise_points'] # + 4 elif min(results) >= advanced['lower_bound']: """If the quoted results aren't small enough yet""" expand += 1 dose_list = [] for scenId in scenIdall: scen = session.query(Scenario).filter_by(id=scenId).first() dose_list.append(scen.scen_materials[0].dose) dose_list.sort() if not input_data['invert_curve']: dose_bound = dose_list[0] if (0 < dose_bound <= 1E-9 * maxback and len(scenIdall) >= 9) or 0 < dose_bound <= 1E-12: fail_never(r, scenIdall, [input_data['instrument']]) return if len(dose_list) > 1: step_ratio = dose_list[0] / dose_list[1] else: step_ratio = dose_list[0] * 0.9 advanced['min_guess'] = advanced['min_guess'] * step_ratio advanced['max_guess'] = advanced['min_guess'] advanced['num_points'] = 1 else: dose_bound = dose_list[-1] if dose_bound >= 40 * maxback and len(scenIdall) >= 9: fail_never(r, scenIdall, [input_data['instrument']]) return if len(dose_list) > 1: step_ratio = dose_list[-1] / dose_list[-2] else: step_ratio = dose_list[0] * 1.1 advanced['min_guess'] = advanced['max_guess'] * step_ratio advanced['max_guess'] = advanced['min_guess'] advanced['num_points'] = 1 elif max(results) <= advanced['upper_bound']: """If the quoted results aren't large enough yet""" expand += 1 dose_list = [] for scenId in scenIdall: scen = session.query(Scenario).filter_by(id=scenId).first() # for scen in session.query(ScenarioGroup).filter_by(name=group_name).first().scenarios: dose_list.append(scen.scen_materials[0].dose) dose_list.sort() if not input_data['invert_curve']: dose_bound = dose_list[-1] if dose_bound >= 40 * maxback and len(scenIdall) >= 9: fail_always(r, scenIdall, [input_data['instrument']]) return if len(dose_list) > 1: step_ratio = dose_list[-1] / dose_list[-2] else: step_ratio = dose_list[0] * 1.1 advanced['min_guess'] = advanced['max_guess'] * step_ratio advanced['max_guess'] = advanced['min_guess'] advanced['num_points'] = 1 else: dose_bound = dose_list[0] if (0 < dose_bound <= 1E-9 * maxback and len(scenIdall) >= 9) or 0 < dose_bound <= 1E-12: fail_always(r, scenIdall, [input_data['instrument']]) return if len(dose_list) > 1: step_ratio = dose_list[0] / dose_list[1] else: step_ratio = dose_list[0] * 0.9 advanced['min_guess'] = advanced['min_guess'] * step_ratio advanced['max_guess'] = advanced['min_guess'] advanced['num_points'] = 1 if condition: if not already_done: scenIds, _, _ = gen_scens(input_data, advanced, session, scenIdall, scenIds_no_persist, group_name, input_data['input_reps'], condition) detector = session.query(Detector).filter_by( name=detName[0]).first() abort = run_scenarios(r, scenIds, detector, input_data['replay'], condition) if abort: cleanup_scenarios(advanced['repetitions'], scenIds_no_persist) cleanup_scenarios(input_data['input_reps'], scenIds) return if advanced['cleanup']: cleanup_scenarios(advanced['repetitions'], scenIds_no_persist) r.populateScenarios() msgbox = QMessageBox(QMessageBox.Question, 'S-Curve generation complete!', 'Would you like to view the results?') msgbox.addButton(QMessageBox.Yes) msgbox.addButton(QMessageBox.No) answer = msgbox.exec() if answer == QMessageBox.Yes: viewResults(r, scenIds, [input_data['instrument']])
class AutomatedSCurve(ui_auto_scurve.Ui_AutoSCurveDialog, QDialog): def __init__(self, parent): QDialog.__init__(self, parent) self.setWindowTitle('Automated S-Curve Generation') self.Rase = parent self.settings = RaseSettings() self.setupUi(self) self.session = Session() # setting default states self.detName = None self.detReplay = None self.static_background = [] self.setInstrumentItems() self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False) # Validators self.line_rep.setValidator( QRegularExpressionValidator(QRegularExpression("[0-9]{0,9}"))) self.line_initrep.setValidator( QRegularExpressionValidator(QRegularExpression("[0-9]{0,9}"))) self.line_edge.setValidator( QRegularExpressionValidator(QRegularExpression("[0-9]{0,9}"))) self.line_dwell.setValidator( QRegularExpressionValidator( QRegularExpression("((\d*\.\d*)|(\d*))"))) self.line_minx.setValidator( QRegularExpressionValidator( QRegularExpression("((\d*\.\d*)|(\d*))"))) self.line_maxx.setValidator( QRegularExpressionValidator( QRegularExpression("((\d*\.\d*)|(\d*))"))) self.line_addpoints.setValidator( QRegularExpressionValidator( QRegularExpression( r"((((\d+\.\d*)|(\d*\.\d+))|(\d+))((((,\d*\.\d+)|(,\d+\.\d*))|(,\d+))*)(,|,\.)?)" ))) self.line_lowerbound.setValidator( QRegularExpressionValidator( QRegularExpression("((\d*\.\d*)|(\d*))"))) self.line_upperbound.setValidator( QRegularExpressionValidator( QRegularExpression("((\d*\.\d*)|(\d*))"))) # connections self.combo_inst.currentTextChanged.connect(self.updateMaterials) self.combo_mat.currentTextChanged[str].connect( lambda mat: self.updateUnits(mat, self.combo_matdose)) self.btn_bgnd.clicked.connect(self.defineBackground) # Confirm enables self.combo_matdose.currentTextChanged.connect(self.enableOk) # Set values of various things based on user inputs self.line_rep.editingFinished.connect( lambda: self.setminval(self.line_rep, '2')) self.line_initrep.editingFinished.connect( lambda: self.setminval(self.line_initrep, '1')) self.line_edge.editingFinished.connect( lambda: self.setminval(self.line_edge, '1')) self.line_dwell.editingFinished.connect( lambda: self.setminval(self.line_dwell, '1', '0.00000000001')) self.line_minx.editingFinished.connect(lambda: self.setminval( self.line_minx, '0.00000001', '0.00000000001')) self.line_maxx.editingFinished.connect( lambda: self.setminval(self.line_maxx, '0.001', '0.00000000001')) self.line_lowerbound.editingFinished.connect( lambda: self.setminval(self.line_lowerbound, '0')) self.line_lowerbound.editingFinished.connect( lambda: self.setmaxval(self.line_lowerbound, '.99')) self.line_upperbound.editingFinished.connect( lambda: self.setmaxval(self.line_upperbound, '1')) self.line_minx.editingFinished.connect(self.checkMaxX) self.line_maxx.editingFinished.connect(self.checkMaxX) self.line_lowerbound.editingFinished.connect(self.checkMaxY) self.line_upperbound.editingFinished.connect(self.checkMaxY) self.line_addpoints.editingFinished.connect(self.removeZeroPoint) self.check_minx.stateChanged.connect(self.setDefaultMin) self.check_maxx.stateChanged.connect(self.setDefaultMax) self.check_addpoints.stateChanged.connect(self.setAddPoints) self.check_name.stateChanged.connect(self.setDefaultName) def setInstrumentItems(self): for det in self.session.query(Detector): if det.replay and det.replay.is_cmd_line: self.combo_inst.addItem(det.name) @pyqtSlot(str) def updateMaterials(self, detName): """ Updates the possible material selection based on the selected instrument. Also identify the name of the replay associated with the chosen detector and set it for S-curve processing """ self.combo_mat.clear() self.combo_mat.addItem('') if not detName.strip(): self.combo_mat.setCurrentIndex(0) self.combo_mat.setEnabled(False) self.btn_bgnd.setEnabled(False) self.detName = None self.detReplay = None else: self.detName = detName self.combo_mat.setEnabled(True) self.btn_bgnd.setEnabled(True) det = self.session.query(Detector).filter_by(name=detName).first() self.detReplay = det.replay.name for baseSpectrum in det.base_spectra: self.combo_mat.addItem(baseSpectrum.material.name) def updateUnits(self, matName, combobox): """ General function call for updating the flux/dose setting in the combobox after material has been selected """ combobox.clear() combobox.addItem('') if not matName.strip(): combobox.setCurrentIndex(0) combobox.setEnabled(False) else: combobox.setEnabled(True) det = self.session.query(Detector).filter_by( name=self.detName).first() for baseSpectrum in det.base_spectra: if baseSpectrum.material_name == matName: if baseSpectrum.rase_sensitivity: combobox.addItem('DOSE (\u00B5Sv/h)') combobox.setCurrentIndex(1) if baseSpectrum.flux_sensitivity: combobox.addItem('FLUX (\u03B3/(cm\u00B2s))') @pyqtSlot(str) def enableOk(self, intensity): """Only enable the okay button if all the relevant points are selected""" if self.combo_matdose.currentText() and self.line_dwell.text() and \ self.line_maxx.text() and self.line_minx.text(): self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(True) else: self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False) def changeMinMax(self): """Change the min and max guess based on the background intensity""" if not self.check_minx.isChecked(): minv = str(1E-7) minv = self.checkSciNote(minv) self.line_minx.setText(minv) self.checkMaxX() self.setAddPoints() if not self.check_maxx.isChecked(): maxv = str(1E-2) maxv = self.checkSciNote(maxv) self.line_maxx.setText(maxv) self.checkMaxX() def checkSciNote(self, val): """If there is scientific notation, remove it so as to not break regex""" if 'E' in val.upper(): sci = val.upper().split('E') if int(sci[1]) < 0: val = '0.' + ''.zfill(abs(int(sci[1])) - 1) + sci[0].replace( '.', '') else: val = str(float(sci[0]) * 10**int(sci[1])) return val def setminval(self, line, setval='0.00000001', minval=None): if not minval: minval = setval try: if float(line.text()) <= float(minval): line.setText(setval) except: # if the user enters a decimal point or something nonsensical line.setText(setval) def setmaxval(self, line, setval='1', maxval=None): if not maxval: maxval = setval try: if float(line.text()) >= float(maxval): line.setText(setval) except: # if the user enters a decimal point or something nonsensical line.setText(setval) def checkMaxX(self): """Make sure the maximum x value is larger than the minimum x value""" if self.line_maxx.text() and self.line_minx.text(): if float(self.line_maxx.text()) <= float(self.line_minx.text()): self.line_maxx.setText(str(float(self.line_minx.text()) * 1E5)) def checkMaxY(self): """Make sure that the bounds don't overlap each other""" if self.line_upperbound.text() and self.line_lowerbound.text(): if float(self.line_upperbound.text()) <= float( self.line_lowerbound.text()): self.line_upperbound.setText( str(max([0.9, float(self.line_lowerbound.text()) * 1.01]))) def setDefaultMin(self): """Set the default minimum x value if it has been unchecked""" if not self.check_minx.isChecked(): self.line_minx.setText('0.00000001') self.setAddPoints() self.checkMaxX() def setDefaultMax(self): """Set the default max x value if it has been unchecked""" if not self.check_maxx.isChecked(): self.line_maxx.setText('0.001') self.checkMaxX() def setAddPoints(self): """Set default user-added points and clears them if the box is unchecked""" if self.check_addpoints.isChecked(): if not self.line_addpoints.text(): self.line_addpoints.setText(self.line_minx.text()) else: self.line_addpoints.setText('') def setDefaultName(self): """Set name back to [Default] if the checkbox is unchecked""" if not self.check_name.isChecked(): self.line_name.setText('[Default]') def removeZeroPoint(self): """Disallow the user to add a point with 0 dose/flux""" if self.line_addpoints.text(): self.line_addpoints.setText( self.endRecurse(self.line_addpoints.text())) addpoints = [ float(i) for i in self.line_addpoints.text().split(',') ] if 0 in addpoints: addpoints = [i for i in addpoints if i != 0] addpoints = list(dict.fromkeys(addpoints)) addpoints.sort() addpoints = [self.checkSciNote(str(i)) for i in addpoints] addpoints = str(addpoints)[1:-1].replace('\'', '').replace(' ', '') self.line_addpoints.setText(addpoints) def endRecurse(self, line): """Remove commas/periods at the end of the uesr-adde points list, recursively""" if (line[-1] == ',') or (line[-1] == '.'): return self.endRecurse(line[:-1]) else: return line def defineBackground(self): dialog = ScenarioDialog(self, auto_s=True) dialog.comboDetectorSelect.setCurrentText( self.combo_inst.currentText()) remove_layouts = [dialog.horizontalLayout, dialog.horizontalLayout_4] for layout in remove_layouts: for i in reversed(range(layout.count())): item = layout.itemAt(i) if isinstance(item, QWidgetItem): item.widget().hide() dialog.pushButton.hide() dialog.tblMaterial.hide() dialog.label_scenlist.hide() dialog.txtScenariosList_2.hide() dialog.label_influences.hide() dialog.lstInfluences.hide() dialog.resize(440, 340) dialog.setWindowTitle('Set static background for the ' + self.combo_inst.currentText() + '.') dialog.exec_() @pyqtSlot() def accept(self): if self.line_addpoints.text(): addpoints = [ float(i) for i in self.line_addpoints.text().split(',') ] else: addpoints = [] self.input_d = { "instrument": self.detName, "replay": self.detReplay, "source": self.combo_mat.currentText(), "source_fd": self.combo_matdose.currentText().split()[0], "background": self.static_background, "dwell_time": float(self.line_dwell.text()), "results_type": self.combo_resulttype.currentText(), "input_reps": int(self.line_rep.text()), "invert_curve": self.check_invert.isChecked() } self.input_advanced = { "rise_points": int(self.line_edge.text()), "min_guess": float(self.line_minx.text()), "max_guess": float(self.line_maxx.text()), "repetitions": int(self.line_initrep.text()), "add_points": addpoints, "cleanup": self.check_cleanup.isChecked(), "custom_name": self.line_name.text(), "num_points": 6, # hardcode for now "lower_bound": float(self.line_lowerbound.text()), "upper_bound": float(self.line_upperbound.text()) } return QDialog.accept(self)
class GroupSettings(QDialog): """Simple Dialog to allow the user to select which groups a scenario is in. This information is stored in the scenario_groups table value for each scenario. :param parent: the parent dialog """ def __init__(self, parent=None, groups=[], scens=None, del_groups=False): QDialog.__init__(self, parent) self.groups = groups self.scens = scens self.del_groups = del_groups self.session = Session() self.makeLayout() def makeLayout(self): cols_list = [grp.name for grp in self.session.query(ScenarioGroup)] cols_list.insert(0, cols_list.pop(cols_list.index('default_group'))) cb_list = [QCheckBox(v.replace('&', '&&')) for v in cols_list] self.layout = QVBoxLayout() self.checklayout = QVBoxLayout() self.buttonlayout = QVBoxLayout() for cb in cb_list: self.checklayout.addWidget(cb) if cb.text() in self.groups: cb.setChecked(True) if cb.text() == 'default_group' and self.del_groups: cb.setEnabled(False) self.btn_newgroup = QPushButton('Add New Group') self.buttonlayout.addWidget(self.btn_newgroup) self.btn_newgroup.clicked.connect(self.addCheckbox) self.buttonBox = QDialogButtonBox(self) if self.del_groups: self.btn_deletegroup = QPushButton('Remove Group(s)') self.buttonlayout.addWidget(self.btn_deletegroup) self.btn_deletegroup.clicked.connect(self.delGroup) self.buttonBox.setStandardButtons(QDialogButtonBox.Close) self.buttonBox.rejected.connect(self.reject) else: self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) self.buttonlayout.addWidget(self.buttonBox) self.layout.addLayout(self.checklayout) self.layout.addLayout(self.buttonlayout) if not self.del_groups and self.scens and len(self.scens) > 1: self.info = QLabel( 'NOTE: A group box is checked if\nany one of the selected scenarios\nis in that ' 'group. Pressing OK will\nadd all selected scenarios to the\nselected groups.' ) self.layout.addWidget(self.info) self.setLayout(self.layout) def _cb_list(self): return [ self.checklayout.itemAt(i).widget() for i in range(self.checklayout.count()) ] def addCheckbox(self): newgroup, okPressed = QInputDialog.getText(self, "Add New Scenario Group", "Scenario Group name:", QLineEdit.Normal, "") collist = [grp.name for grp in self.session.query(ScenarioGroup)] if okPressed and (newgroup != '' and newgroup not in collist): self.checklayout.addWidget(QCheckBox(newgroup)) self.setLayout(self.layout) self.add_groups(self.session, newgroup) def delGroup(self): del_groups = [cb.text() for cb in self._cb_list() if cb.isChecked()] if len(del_groups) > 1: answer = QMessageBox( QMessageBox.Question, 'Delete Scenario Groups', 'Are you sure you want to delete these scenario groups? ' 'Scenarios in these groups will not be deleted') else: answer = QMessageBox( QMessageBox.Question, 'Delete Scenario Group', 'Are you sure you want to delete this scenario group? ' 'Scenarios in this group will not be deleted') answer.addButton(QMessageBox.Yes) answer.addButton(QMessageBox.No) ans_hold = answer.exec() if ans_hold == QMessageBox.Yes: for group in del_groups: self.delete_groups(self.session, group) for cb in self._cb_list(): if cb.text() in del_groups: self.checklayout.removeWidget(cb) cb.deleteLater() @staticmethod def delete_groups(session, group): group_delete = session.query(ScenarioGroup).filter( ScenarioGroup.name == group) group_delete.delete() session.commit() @staticmethod def add_groups(session, group): session.add(ScenarioGroup(name=group)) session.commit() @pyqtSlot() def accept(self): """ Adds the scenario to the checked groups """ self.n_groups = [cb.text() for cb in self._cb_list() if cb.isChecked()] if self.scens: for scen in self.scens: scen.scenario_groups.clear() for groupname in self.n_groups: scen.scenario_groups.append( self.session.query(ScenarioGroup).filter_by( name=groupname).first()) self.session.commit() return QDialog.accept(self)