Skip to content

Commit 9e75275

Browse files
Mesh annotations (#153)
Co-authored-by: Paul Romano <[email protected]>
1 parent 80086ba commit 9e75275

File tree

6 files changed

+137
-20
lines changed

6 files changed

+137
-20
lines changed

openmc_plotter/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '0.4.0'
1+
__version__ = '0.4.1'

openmc_plotter/docks.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,57 @@ def __init__(self, model, font_metric, parent=None):
3333
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding)
3434

3535

36+
class MeshAnnotationDock(PlotterDock):
37+
"""Dock for mesh annotation options"""
38+
39+
def __init__(self, model, font_metric, parent=None):
40+
super().__init__(model, font_metric, parent)
41+
42+
self.treeLayout = QVBoxLayout()
43+
self.meshTree = QTreeWidget()
44+
self.treeExpander = Expander("Meshes:", layout=self.treeLayout)
45+
self.treeExpander.expand() # start with meshes expanded
46+
47+
self.meshTree.setColumnCount(1)
48+
49+
self.mesh_items = []
50+
for mesh_id in self.model.cpp_mesh_ids():
51+
mesh_item = QTreeWidgetItem(self.meshTree, (f'Mesh {mesh_id}',))
52+
mesh_item.setFlags(mesh_item.flags() | QtCore.Qt.ItemIsUserCheckable)
53+
mesh_item.setCheckState(0, QtCore.Qt.Unchecked)
54+
self.mesh_items.append((mesh_id, mesh_item))
55+
self.meshTree.addTopLevelItem(mesh_item)
56+
57+
self.meshTree.setHeaderHidden(True)
58+
59+
# Create submit button
60+
self.applyButton = QPushButton("Apply Changes")
61+
# Mac bug fix
62+
self.applyButton.setMinimumHeight(self.font_metric.height() * 1.6)
63+
self.applyButton.clicked.connect(self.main_window.applyChanges)
64+
65+
label = QLabel("Mesh Annotations")
66+
self.treeLayout.addWidget(label)
67+
self.treeLayout.addWidget(self.meshTree)
68+
self.treeLayout.addWidget(HorizontalLine())
69+
self.treeLayout.addWidget(self.applyButton)
70+
71+
self.optionsWidget = QWidget()
72+
self.optionsWidget.setLayout(self.treeLayout)
73+
self.setWidget(self.optionsWidget)
74+
75+
def get_checked_meshes(self):
76+
return [id for id, item in self.mesh_items if item.checkState(0) == QtCore.Qt.Checked]
77+
78+
def update(self):
79+
pass
80+
81+
def resizeEvent(self, event):
82+
self.main_window.resizeEvent(event)
83+
84+
hideEvent = showEvent = moveEvent = resizeEvent
85+
86+
3687
class DomainDock(PlotterDock):
3788
"""
3889
Domain options dock

openmc_plotter/main_window.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
from .plotmodel import PlotModel, DomainTableModel, hash_model
2424
from .plotgui import PlotImage, ColorDialog
25-
from .docks import DomainDock, TallyDock
25+
from .docks import DomainDock, TallyDock, MeshAnnotationDock
2626
from .overlays import ShortcutsOverlay
2727
from .tools import ExportDataDialog, SourceSitesDialog
2828

@@ -91,6 +91,12 @@ def loadGui(self, use_settings_pkl=True):
9191
self.tallyDock.setObjectName("Tally Options Dock")
9292
self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.tallyDock)
9393

94+
# Mesh Annotation Dock
95+
self.meshAnnotationDock = MeshAnnotationDock(self.model, self.font_metric, self)
96+
self.meshAnnotationDock.update()
97+
self.meshAnnotationDock.setObjectName("Mesh Annotation Dock")
98+
self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.meshAnnotationDock)
99+
94100
# Color DialogtallyDock
95101
self.colorDialog = ColorDialog(self.model, self.font_metric, self)
96102
self.colorDialog.hide()
@@ -386,6 +392,12 @@ def createMenuBar(self):
386392
self.tallyDockAction.setStatusTip('Toggle tally dock visibility')
387393
self.tallyDockAction.triggered.connect(self.toggleTallyDockView)
388394

395+
self.meshAnnotationDockAction = QAction('Mesh &Annotation Dock', self)
396+
self.meshAnnotationDockAction.setShortcut("Ctrl+E")
397+
self.meshAnnotationDockAction.setToolTip('Toggle mesh annotation dock visibility')
398+
self.meshAnnotationDockAction.setStatusTip('Toggle mesh annotation dock visibility')
399+
self.meshAnnotationDockAction.triggered.connect(self.toggleMeshAnnotationDockView)
400+
389401
self.zoomAction = QAction('&Zoom...', self)
390402
self.zoomAction.setShortcut('Alt+Shift+Z')
391403
self.zoomAction.setToolTip('Edit zoom factor')
@@ -395,6 +407,7 @@ def createMenuBar(self):
395407
self.viewMenu = self.mainMenu.addMenu('&View')
396408
self.viewMenu.addAction(self.dockAction)
397409
self.viewMenu.addAction(self.tallyDockAction)
410+
self.viewMenu.addAction(self.meshAnnotationDockAction)
398411
self.viewMenu.addSeparator()
399412
self.viewMenu.addAction(self.zoomAction)
400413
self.viewMenu.aboutToShow.connect(self.updateViewMenu)
@@ -624,6 +637,10 @@ def updateDataMenu(self):
624637
elif hasattr(self, "closeStatePointAction"):
625638
self.dataMenu.removeAction(self.closeStatePointAction)
626639

640+
641+
def updateMeshAnnotations(self):
642+
self.model.activeView.mesh_annotations = self.meshAnnotationDock.get_checked_meshes()
643+
627644
def plotSourceSites(self):
628645
self.sourceSitesDialog.show()
629646
self.sourceSitesDialog.raise_()
@@ -635,6 +652,7 @@ def applyChanges(self):
635652
QApplication.processEvents()
636653
if self.model.activeView.selectedTally is not None:
637654
self.tallyDock.updateModel()
655+
self.updateMeshAnnotations()
638656
self.model.storeCurrent()
639657
self.model.subsequentViews = []
640658
self.plotIm.generatePixmap()
@@ -811,6 +829,18 @@ def toggleTallyDockView(self):
811829
self.resizePixmap()
812830
self.showMainWindow()
813831

832+
def toggleMeshAnnotationDockView(self):
833+
if self.meshAnnotationDock.isVisible():
834+
self.meshAnnotationDock.hide()
835+
if not self.isMaximized() and not self.meshAnnotationDock.isFloating():
836+
self.resize(self.width() - self.meshAnnotationDock.width(), self.height())
837+
else:
838+
self.meshAnnotationDock.setVisible(True)
839+
if not self.isMaximized() and not self.meshAnnotationDock.isFloating():
840+
self.resize(self.width() + self.meshAnnotationDock.width(), self.height())
841+
self.resizePixmap()
842+
self.showMainWindow()
843+
814844
def editZoomAct(self):
815845
percent, ok = QInputDialog.getInt(self, "Edit Zoom", "Zoom Percent:",
816846
self.dock.zoomBox.value(), 25, 2000)

openmc_plotter/overlays.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class ShortcutsOverlay(QWidget):
4040
("Horizontal Scroll", "Alt+Scroll")],
4141
"Menus": [("Hide/Show Geometry Dock", c_key + "+D"),
4242
("Hide/Show Tally Dock", c_key + "+T"),
43+
("Hide/Show Mesh Annotation Dock", c_key + "+E"),
4344
("Reload Model", "Shift+" + c_key + "+R"),
4445
("Quit", c_key + "+Q"),
4546
("Display Shortcuts", "?")],

openmc_plotter/plotgui.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,10 @@ def updatePixmap(self):
641641
self.add_outlines()
642642
self.plotSourceSites()
643643

644+
# annotate mesh boundaries
645+
for mesh_id in cv.mesh_annotations:
646+
self.annotate_mesh(mesh_id)
647+
644648
# always make sure the data bounds are set correctly
645649
self.ax.set_xbound(data_bounds[0], data_bounds[1])
646650
self.ax.set_ybound(data_bounds[2], data_bounds[3])
@@ -652,6 +656,26 @@ def updatePixmap(self):
652656
self.draw()
653657
return "Done"
654658

659+
def current_view_data_bounds(self):
660+
cv = self.model.currentView
661+
return [cv.origin[self.main_window.xBasis] - cv.width/2.,
662+
cv.origin[self.main_window.xBasis] + cv.width/2.,
663+
cv.origin[self.main_window.yBasis] - cv.height/2.,
664+
cv.origin[self.main_window.yBasis] + cv.height/2.]
665+
666+
def annotate_mesh(self, mesh_id):
667+
mesh_bins = self.model.mesh_plot_bins(mesh_id)
668+
669+
data_bounds = self.current_view_data_bounds()
670+
self.mesh_contours = self.ax.contour(
671+
mesh_bins,
672+
origin='upper',
673+
colors='k',
674+
linestyles='solid',
675+
levels=np.unique(mesh_bins),
676+
extent=data_bounds
677+
)
678+
655679
def plotSourceSites(self):
656680
if not self.model.sourceSitesVisible or self.model.sourceSites is None:
657681
return
@@ -681,10 +705,7 @@ def add_outlines(self):
681705
# draw outlines as isocontours
682706
if cv.outlines:
683707
# set data extents for automatic reporting of pointer location
684-
data_bounds = [cv.origin[self.main_window.xBasis] - cv.width/2.,
685-
cv.origin[self.main_window.xBasis] + cv.width/2.,
686-
cv.origin[self.main_window.yBasis] - cv.height/2.,
687-
cv.origin[self.main_window.yBasis] + cv.height/2.]
708+
data_bounds = self.current_view_data_bounds()
688709
levels = np.unique(self.model.ids)
689710
self.contours = self.ax.contour(self.model.ids,
690711
origin='upper',

openmc_plotter/plotmodel.py

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,30 @@ def _create_distribcell_image(self, tally, tally_value, scores, nuclides, cellin
678678

679679
return image_data, None, data_min, data_max
680680

681+
def cpp_mesh_ids(self):
682+
return list(openmc.lib.meshes.keys())
683+
684+
def mesh_plot_bins(self, mesh_id, view: PlotView = None, translation: tuple[float, float, float] = None):
685+
mesh = openmc.lib.meshes[mesh_id]
686+
687+
if view is None:
688+
view = self.currentView
689+
690+
if translation is None:
691+
origin = view.origin
692+
else:
693+
origin = (view.origin[0] - translation[0],
694+
view.origin[1] - translation[1],
695+
view.origin[2] - translation[2])
696+
697+
mesh_bins = mesh.get_plot_bins(
698+
origin=origin,
699+
width=(view.width, view.height),
700+
basis=view.basis,
701+
pixels=(view.h_res, view.v_res),
702+
)
703+
return mesh_bins
704+
681705
def _create_tally_mesh_image(
682706
self, tally: openmc.Tally, tally_value: TallyValueType,
683707
scores: Tuple[str], nuclides: Tuple[str], view: PlotView = None
@@ -737,21 +761,9 @@ def _do_op(array, tally_value, ax=0):
737761
selected_scores.append(idx)
738762
data = _do_op(data[np.array(selected_scores)], tally_value)
739763

740-
# Account for mesh filter translation
741-
if mesh_filter.translation is not None:
742-
t = mesh_filter.translation
743-
origin = (view.origin[0] - t[0], view.origin[1] - t[1], view.origin[2] - t[2])
744-
else:
745-
origin = view.origin
746-
747764
# Get mesh bins from openmc.lib
748765
mesh_cpp = openmc.lib.meshes[mesh.id]
749-
mesh_bins = mesh_cpp.get_plot_bins(
750-
origin=origin,
751-
width=(view.width, view.height),
752-
basis=view.basis,
753-
pixels=(view.h_res, view.v_res),
754-
)
766+
mesh_bins = self.mesh_plot_bins(mesh.id, view, mesh_filter.translation)
755767

756768
# Apply volume normalization
757769
if view.tallyVolumeNorm:
@@ -1017,14 +1029,16 @@ class PlotView:
10171029
Label of the currently selected tally
10181030
"""
10191031

1020-
attrs = ('view_ind', 'view_params', 'cells', 'materials', 'selectedTally')
1032+
attrs = ('view_ind', 'view_params', 'cells', 'materials', 'selectedTally', 'mesh_annotations')
10211033
plotbase_attrs = ('level', 'origin', 'width', 'height',
10221034
'h_res', 'v_res', 'basis', 'llc', 'urc', 'color_overlaps')
10231035

10241036
def __init__(self, origin=(0, 0, 0), width=10, height=10, restore_view=None,
10251037
restore_domains=False, default_res=None):
10261038
"""Initialize PlotView attributes"""
10271039

1040+
self.mesh_annotations = []
1041+
10281042
if restore_view is not None:
10291043
self.view_ind = copy.copy(restore_view.view_ind)
10301044
self.view_params = copy.copy(restore_view.view_params)

0 commit comments

Comments
 (0)