diff --git a/openmc_plotter/main_window.py b/openmc_plotter/main_window.py index 73a53f4..e0b47f1 100755 --- a/openmc_plotter/main_window.py +++ b/openmc_plotter/main_window.py @@ -378,6 +378,16 @@ def createMenuBar(self): self.overlapAct.toggled.connect(overlap_connector) self.editMenu.addAction(self.overlapAct) + self.undefinedAct = QAction('Enable Undefined Region Coloring', self) + self.undefinedAct.setShortcut('Ctrl+I') + self.undefinedAct.setCheckable(True) + self.undefinedAct.setToolTip('Toggle undefined regions') + self.undefinedAct.setStatusTip('Toggle display of undefined ' + '(not found) regions when enabled') + undefined_connector = partial(self.toggleUndefined, apply=True) + self.undefinedAct.toggled.connect(undefined_connector) + self.editMenu.addAction(self.undefinedAct) + self.outlineAct = QAction('Enable Domain Outlines', self) self.outlineAct.setShortcut('Ctrl+U') self.outlineAct.setCheckable(True) @@ -438,7 +448,7 @@ def updateEditMenu(self): self.restoreAction.setDisabled(not changed) toggle_actions = (self.maskingAction, self.highlightingAct, - self.outlineAct, self.overlapAct) + self.outlineAct, self.overlapAct, self.undefinedAct) # Temporarily block signals to avoid triggering plot update for action in toggle_actions: action.blockSignals(True) @@ -446,6 +456,7 @@ def updateEditMenu(self): self.highlightingAct.setChecked(self.model.currentView.highlighting) self.outlineAct.setChecked(self.model.currentView.outlinesCell) self.overlapAct.setChecked(self.model.currentView.color_overlaps) + self.undefinedAct.setChecked(self.model.currentView.color_undefined) for action in toggle_actions: action.blockSignals(False) @@ -742,6 +753,12 @@ def toggleOverlaps(self, state, apply=False): if apply: self.applyChanges() + def toggleUndefined(self, state, apply=False): + self.model.activeView.color_undefined = bool(state) + self.colorDialog.updateUndefined() + if apply: + self.applyChanges() + def editColorMap(self, colormap_name, property_type, apply=False): self.model.activeView.colormaps[property_type] = colormap_name self.plotIm.updateColorMap(colormap_name, property_type) @@ -935,6 +952,18 @@ def editOverlapColor(self, apply=False): if apply: self.applyChanges() + def editUndefinedColor(self, apply=False): + current_color = self.model.activeView.undefined_color + dlg = QColorDialog(self) + dlg.setCurrentColor(QtGui.QColor.fromRgb(*current_color)) + if dlg.exec(): + new_color = dlg.currentColor().getRgb()[:3] + self.model.activeView.undefined_color = new_color + self.colorDialog.updateUndefinedColor() + + if apply: + self.applyChanges() + def editBackgroundColor(self, apply=False): current_color = self.model.activeView.domainBackground dlg = QColorDialog(self) diff --git a/openmc_plotter/overlays.py b/openmc_plotter/overlays.py index a87b453..31b08f7 100644 --- a/openmc_plotter/overlays.py +++ b/openmc_plotter/overlays.py @@ -29,6 +29,7 @@ class ShortcutsOverlay(QWidget): ("Toggle Masking", c_key + "+M"), ("Toggle Highlighting", c_key + "+L"), ("Toggle Overlap Coloring", c_key + "+P"), + ("Toggle Undefined Coloring", c_key + "+I"), ("Toggle Domain Outlines", c_key + "+U"), ("Set XY Basis", "Alt+X"), ("Set YZ Basis", "Alt+Y"), diff --git a/openmc_plotter/plotgui.py b/openmc_plotter/plotgui.py index 1e080b2..6cb5a34 100644 --- a/openmc_plotter/plotgui.py +++ b/openmc_plotter/plotgui.py @@ -361,6 +361,22 @@ def _overlapInfo(self, id): label2 = self._cellLabel(cell2) return f"OVERLAP: Universe {universe}, Cells {label1} and {label2}" + def _isUndefinedInternal(self, event): + """Whether the pixel under the event is an internal undefined region.""" + if self.model.undefined_internal is None: + return False + xPos, yPos = self.getDataIndices(event) + cv = self.model.currentView + if not (0 <= yPos < cv.v_res and 0 <= xPos < cv.h_res): + return False + return bool(self.model.undefined_internal[yPos, xPos]) + + def _undefinedInfo(self, event): + """Status-bar text for an undefined (not-found) pixel, if classified.""" + if self._isUndefinedInternal(event): + return "WARNING: undefined internal region" + return "" + def mouseDoubleClickEvent(self, event): xCenter, yCenter = self.getPlotCoords(event.pos()) self.main_window.editPlotOrigin(xCenter, yCenter, apply=True) @@ -414,7 +430,7 @@ def mouseMoveEvent(self, event): density, temperature)) else: - domainInfo = "" + domainInfo = self._undefinedInfo(event) if self.model.tally_data is not None: tid, value = self.getTallyInfo(event) @@ -568,7 +584,16 @@ def contextMenuEvent(self, event): if cv.colorby not in _MODEL_PROPERTIES: self.menu.addSeparator() - if int(id) == _NOT_FOUND: + if int(id) == _NOT_FOUND and self._isUndefinedInternal(event): + undefColorAction = self.menu.addAction( + 'Edit Undefined Color...') + undefColorAction.setToolTip('Edit undefined color') + undefColorAction.setStatusTip( + 'Edit plot undefined region color') + connector = partial(self.main_window.editUndefinedColor, + apply=True) + undefColorAction.triggered.connect(connector) + elif int(id) == _NOT_FOUND: bgColorAction = self.menu.addAction( 'Edit Background Color...') bgColorAction.setToolTip('Edit background color') @@ -598,6 +623,7 @@ def contextMenuEvent(self, event): self.menu.addAction(self.main_window.maskingAction) self.menu.addAction(self.main_window.highlightingAct) self.menu.addAction(self.main_window.overlapAct) + self.menu.addAction(self.main_window.undefinedAct) self.menu.addSeparator() self.menu.addAction(self.main_window.dockAction) @@ -1125,6 +1151,17 @@ def createGeneralTab(self): self.overlapColorButton.setFixedHeight(self.font_metric.height() * 1.5) self.overlapColorButton.clicked.connect(main_window.editOverlapColor) + # Undefined (not-found) region plotting + self.undefinedCheck = QCheckBox('', self) + undefined_connector = partial(main_window.toggleUndefined) + self.undefinedCheck.stateChanged.connect(undefined_connector) + + self.undefinedColorButton = QPushButton() + self.undefinedColorButton.setCursor(QtCore.Qt.PointingHandCursor) + self.undefinedColorButton.setFixedWidth(button_width) + self.undefinedColorButton.setFixedHeight(self.font_metric.height() * 1.5) + self.undefinedColorButton.clicked.connect(main_window.editUndefinedColor) + self.colorResetButton = QPushButton("&Reset Colors") self.colorResetButton.setCursor(QtCore.Qt.PointingHandCursor) self.colorResetButton.clicked.connect(main_window.resetColors) @@ -1147,6 +1184,9 @@ def createGeneralTab(self): formLayout.addRow('Show Overlaps:', self.overlapCheck) formLayout.addRow('Overlap Color:', self.overlapColorButton) formLayout.addRow(HorizontalLine()) + formLayout.addRow('Show Undefined:', self.undefinedCheck) + formLayout.addRow('Undefined Color:', self.undefinedColorButton) + formLayout.addRow(HorizontalLine()) formLayout.addRow('Color Plot By:', self.colorbyBox) formLayout.addRow('Universe Level:', self.universeLevelBox) formLayout.addRow(self.colorResetButton, None) @@ -1314,6 +1354,8 @@ def updateDialogValues(self): self.updateDomainTabs() self.updateOverlap() self.updateOverlapColor() + self.updateUndefined() + self.updateUndefinedColor() def updateMasking(self): masking = self.model.activeView.masking @@ -1383,10 +1425,22 @@ def updateOverlap(self): if colorby in ('cell', 'material'): self.overlapCheck.setChecked(overlap_val) + def updateUndefinedColor(self): + color = self.model.activeView.undefined_color + self.undefinedColorButton.setStyleSheet("border-radius: 8px;" + "background-color: rgb%s" % (str(color))) + + def updateUndefined(self): + colorby = self.model.activeView.colorby + undefined_val = self.model.activeView.color_undefined + if colorby in ('cell', 'material'): + self.undefinedCheck.setChecked(undefined_val) + def updateColorBy(self): colorby = self.model.activeView.colorby self.colorbyBox.setCurrentText(colorby) self.overlapCheck.setEnabled(colorby in ("cell", "material")) + self.undefinedCheck.setEnabled(colorby in ("cell", "material")) self.universeLevelBox.setEnabled(colorby == 'cell') def updateUniverseLevel(self): diff --git a/openmc_plotter/plotmodel.py b/openmc_plotter/plotmodel.py index 6faf2d8..d77125f 100644 --- a/openmc_plotter/plotmodel.py +++ b/openmc_plotter/plotmodel.py @@ -327,6 +327,12 @@ def __init__(self, use_settings_pkl, model_path, default_res): # Map to be populated by overlap functions self.overlap_info = None + # Boolean mask of internal undefined (not-found) regions, populated by + # makePlot when undefined-region coloring is enabled. Internal regions + # are enclosed by defined geometry (potentially unsafe); external + # (boundary-connected) undefined regions are intentionally ignored. + self.undefined_internal = None + self.version = __version__ # default statepoint value @@ -585,6 +591,18 @@ def makePlot(self, view: Optional["PlotView"] = None, if dom.highlight: image[self.ids == int(id)] = cv.highlightBackground + # Classify and color undefined (not-found) regions. Only internal + # regions (holes enclosed by defined geometry) are flagged as a + # warning; external regions connect to the slice boundary and are + # left untouched. + self.undefined_internal = None + if cv.color_undefined: + _, _, internal = \ + openmc.Model._classify_undefined_regions(self.cell_ids) + if internal is not None: + self.undefined_internal = internal + image[internal] = cv.undefined_color + # set model image self.image = image @@ -1228,6 +1246,11 @@ class PlotViewIndependent: RGB color to apply to plot background overlap_color : 3-tuple of int RGB color to apply for cell overlap regions + color_undefined : bool + Indicator of whether or not internal undefined (not-found) regions + will be shown + undefined_color : 3-tuple of int + RGB color to apply for internal (enclosed) undefined regions domainAlpha : float between 0 and 1 Alpha value of the geometry plot plotVisibile : bool @@ -1277,6 +1300,9 @@ def __init__(self): self.highlightSeed = 1 self.domainBackground = (50, 50, 50) self.overlap_color = (255, 0, 0) + # Undefined (not-found) region coloring (internal regions only) + self.color_undefined = False + self.undefined_color = (255, 165, 0) self.domainAlpha = 1.0 self.domainVisible = True self.outlinesCell = False @@ -1314,6 +1340,11 @@ def __setstate__(self, state): self.outlinesCell = False if not hasattr(self, 'outlinesMat'): self.outlinesMat = False + # Undefined region coloring (added in a later version) + if not hasattr(self, 'color_undefined'): + self.color_undefined = False + if not hasattr(self, 'undefined_color'): + self.undefined_color = (255, 165, 0) # Migrate old boolean attributes to new tallyDataMinMaxType if not hasattr(self, 'tallyDataMinMaxType'): if getattr(self, 'tallyDataUserMinMax', False):