Skip to content

Commit b5a1fd9

Browse files
Adding nascent source site plot capability. (#131)
* Adding nascent source site plot capability. * Polishing source site plotting capability * Plot sites before setting axes extents * Updating README * Clean up interface for source site sampling * Update openmc_plotter/plotgui.py --------- Co-authored-by: Paul Romano <[email protected]>
1 parent 293067c commit b5a1fd9

File tree

6 files changed

+172
-3
lines changed

6 files changed

+172
-3
lines changed

README.md

+9
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,15 @@ user's Matplotlib installation.
9898

9999
- Tally and geometry data (material/cell IDs) can be exported to a VTK file under "File->Export"
100100

101+
### Source Site Plotting
102+
103+
Source locations from an externally defined source can be visualized in the plotter to verify
104+
source definitions. These source sites are gathered as generated by the transport model. A tolerance
105+
can be provided to filter out source sites that are too far from the slice plane, otherwise source
106+
locations are projected onto the slice plane.
107+
108+
![Source plotting](./screenshots/source-sites.png)
109+
101110
# Options/Functionality
102111

103112
## Menu Bar:

openmc_plotter/main_window.py

+16-3
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from .plotgui import PlotImage, ColorDialog
2525
from .docks import DomainDock, TallyDock
2626
from .overlays import ShortcutsOverlay
27-
from .tools import ExportDataDialog
27+
from .tools import ExportDataDialog, SourceSitesDialog
2828

2929

3030
def _openmcReload(threads=None, model_path='.'):
@@ -97,6 +97,7 @@ def loadGui(self, use_settings_pkl=True):
9797

9898
# Tools
9999
self.exportDataDialog = ExportDataDialog(self.model, self.font_metric, self)
100+
self.sourceSitesDialog = SourceSitesDialog(self.model, self.font_metric, self)
100101

101102
# Keyboard overlay
102103
self.shortcutOverlay = ShortcutsOverlay(self)
@@ -206,12 +207,18 @@ def createMenuBar(self):
206207
self.openStatePointAction.setToolTip('Open statepoint file')
207208
self.openStatePointAction.triggered.connect(self.openStatePoint)
208209

210+
self.sourceSitesAction = QAction('&Sample source sites...', self)
211+
self.sourceSitesAction.setToolTip('Add source sites to plot')
212+
self.setStatusTip('Sample and add source sites to the plot')
213+
self.sourceSitesAction.triggered.connect(self.plotSourceSites)
214+
209215
self.importPropertiesAction = QAction("&Import properties...", self)
210216
self.importPropertiesAction.setToolTip("Import properties")
211217
self.importPropertiesAction.triggered.connect(self.importProperties)
212218

213219
self.dataMenu = self.mainMenu.addMenu('D&ata')
214220
self.dataMenu.addAction(self.openStatePointAction)
221+
self.dataMenu.addAction(self.sourceSitesAction)
215222
self.dataMenu.addAction(self.importPropertiesAction)
216223
self.updateDataMenu()
217224

@@ -525,13 +532,14 @@ def loadViewFile(self, filename):
525532
except Exception:
526533
message = 'Error loading plot settings'
527534
saved = {'version': None,
528-
'current': None}
535+
'current': None}
536+
529537
if saved['version'] == self.model.version:
530538
self.model.activeView = saved['current']
531539
self.dock.updateDock()
532540
self.colorDialog.updateDialogValues()
533541
self.applyChanges()
534-
message = '{} settings loaded'.format(filename)
542+
message = '{} loaded'.format(filename)
535543
else:
536544
message = 'Error loading plot settings. Incompatible model.'
537545
self.statusBar().showMessage(message, 5000)
@@ -616,6 +624,11 @@ def updateDataMenu(self):
616624
elif hasattr(self, "closeStatePointAction"):
617625
self.dataMenu.removeAction(self.closeStatePointAction)
618626

627+
def plotSourceSites(self):
628+
self.sourceSitesDialog.show()
629+
self.sourceSitesDialog.raise_()
630+
self.sourceSitesDialog.activateWindow()
631+
619632
def applyChanges(self):
620633
if self.model.activeView != self.model.currentView:
621634
self.statusBar().showMessage('Generating Plot...')

openmc_plotter/plotgui.py

+25
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,7 @@ def updatePixmap(self):
639639

640640
# annotate outlines
641641
self.add_outlines()
642+
self.plotSourceSites()
642643

643644
# always make sure the data bounds are set correctly
644645
self.ax.set_xbound(data_bounds[0], data_bounds[1])
@@ -651,6 +652,30 @@ def updatePixmap(self):
651652
self.draw()
652653
return "Done"
653654

655+
def plotSourceSites(self):
656+
if not self.model.sourceSitesVisible or self.model.sourceSites is None:
657+
return
658+
659+
cv = self.model.currentView
660+
basis = cv.view_params.basis
661+
662+
h_idx = 'xyz'.index(basis[0])
663+
v_idx = 'xyz'.index(basis[1])
664+
665+
sites = self.model.sourceSites
666+
667+
slice_ax = cv.view_params.slice_axis
668+
669+
if self.model.sourceSitesApplyTolerance:
670+
sites_to_plot = sites[np.abs(sites[:, slice_ax] - cv.origin[slice_ax]) <= self.model.sourceSitesTolerance]
671+
else:
672+
sites_to_plot = sites
673+
674+
self.ax.scatter([s[h_idx] for s in sites_to_plot],
675+
[s[v_idx] for s in sites_to_plot],
676+
marker='o',
677+
color=rgb_normalize(self.model.sourceSitesColor))
678+
654679
def add_outlines(self):
655680
cv = self.model.currentView
656681
# draw outlines as isocontours

openmc_plotter/plotmodel.py

+35
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,14 @@ class PlotModel:
140140
subsequentViews : list of PlotView instances
141141
List of undone plot view settings used to redo changes made
142142
in plot explorer
143+
sourceSitesTolerance : float
144+
Tolerance for source site plotting (default 0.1 cm)
145+
sourceSitesColor : tuple of 3 int
146+
RGB color for source site plotting (default blue)
147+
sourceSitesVisible : bool
148+
Whether to plot source sites (default True)
149+
sourceSites : Source sites to plot
150+
Set of source locations to plot
143151
defaultView : PlotView
144152
Default settings for given geometry
145153
currentView : PlotView
@@ -178,6 +186,13 @@ def __init__(self, use_settings_pkl, model_path, default_res):
178186

179187
self.defaultView = self.getDefaultView(default_res)
180188

189+
# Source site defaults
190+
self.sourceSitesApplyTolerance = False
191+
self.sourceSitesTolerance = 0.1 # cm
192+
self.sourceSitesColor = (0, 0, 255)
193+
self.sourceSitesVisible = True
194+
self.sourceSites = None
195+
181196
if model_path.is_file():
182197
settings_pkl = model_path.with_name('plot_settings.pkl')
183198
else:
@@ -399,6 +414,15 @@ def redo(self):
399414
self.activeView = self.subsequentViews.pop()
400415
self.generatePlot()
401416

417+
def getExternalSourceSites(self, n=100):
418+
"""Plot source sites from a source file
419+
"""
420+
if n == 0:
421+
self.source_sites = None
422+
return
423+
sites = openmc.lib.sample_external_source(n)
424+
self.sourceSites = np.array([s.r for s in sites[:n]], dtype=float)
425+
402426
def storeCurrent(self):
403427
""" Add current view to previousViews list """
404428
self.previousViews.append(copy.deepcopy(self.currentView))
@@ -786,6 +810,8 @@ class ViewParam(openmc.lib.plot._PlotBase):
786810
Vertical resolution of plot image
787811
basis : {'xy', 'xz', 'yz'}
788812
The basis directions for the plot
813+
slice_axis : int
814+
The axis along which the plot is sliced
789815
color_overlaps : bool
790816
Indicator of whether or not overlaps will be shown
791817
level : int
@@ -806,6 +832,15 @@ def __init__(self, origin=(0, 0, 0), width=10, height=10, default_res=1000):
806832
self.basis = 'xy'
807833
self.color_overlaps = False
808834

835+
@property
836+
def slice_axis(self):
837+
if self.basis == 'xy':
838+
return 2
839+
elif self.basis == 'yz':
840+
return 0
841+
else:
842+
return 1
843+
809844
@property
810845
def llc(self):
811846
if self.basis == 'xy':

openmc_plotter/tools.py

+87
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,93 @@
88
from .scientific_spin_box import ScientificDoubleSpinBox
99

1010

11+
class SourceSitesDialog(QtWidgets.QDialog):
12+
def __init__(self, model, font_metric, parent=None):
13+
super().__init__(parent)
14+
15+
self.setWindowTitle('Sample Source Sites')
16+
self.model = model
17+
self.font_metric = font_metric
18+
self.parent = parent
19+
20+
self.layout = QtWidgets.QFormLayout()
21+
self.setLayout(self.layout)
22+
23+
self.populate()
24+
25+
def populate(self):
26+
self.nSitesBox = QtWidgets.QSpinBox(self)
27+
self.nSitesBox.setMaximum(1_000_000)
28+
self.nSitesBox.setMinimum(0)
29+
self.nSitesBox.setValue(1000)
30+
self.nSitesBox.setToolTip('Number of source sites to sample from the OpenMC source')
31+
32+
self.sites_visible = QtWidgets.QCheckBox(self)
33+
self.sites_visible.setChecked(self.model.sourceSitesVisible)
34+
self.sites_visible.setToolTip('Toggle visibility of source sites on the slice plane')
35+
self.sites_visible.stateChanged.connect(self._toggle_source_sites)
36+
37+
self.colorButton = QtWidgets.QPushButton(self)
38+
self.colorButton.setToolTip('Select color for displaying source sites on the slice plane')
39+
self.colorButton.setCursor(QtCore.Qt.PointingHandCursor)
40+
self.colorButton.setFixedHeight(self.font_metric.height() * 1.5)
41+
self.colorButton.clicked.connect(self._select_source_site_color)
42+
rgb = self.model.sourceSitesColor
43+
self.colorButton.setStyleSheet(
44+
f"border-radius: 8px; background-color: rgb{rgb}")
45+
46+
self.toleranceBox = ScientificDoubleSpinBox()
47+
self.toleranceBox.setToolTip('Slice axis tolerance for displaying source sites on the slice plane')
48+
self.toleranceBox.setValue(self.model.sourceSitesTolerance)
49+
self.toleranceBox.valueChanged.connect(self._set_source_site_tolerance)
50+
self.toleranceBox.setEnabled(self.model.sourceSitesApplyTolerance)
51+
52+
self.toleranceToggle = QtWidgets.QCheckBox(self)
53+
self.toleranceToggle.setChecked(self.model.sourceSitesApplyTolerance)
54+
self.toleranceToggle.stateChanged.connect(self._toggle_tolerance)
55+
56+
self.sampleButton = QtWidgets.QPushButton("Sample New Sites")
57+
self.sampleButton.setToolTip('Sample new source sites from the OpenMC source')
58+
self.sampleButton.clicked.connect(self._sample_sites)
59+
60+
self.closeButton = QtWidgets.QPushButton("Close")
61+
self.closeButton.clicked.connect(self.close)
62+
63+
self.layout.addRow("Source Sites:", self.nSitesBox)
64+
self.layout.addRow("Visible:", self.sites_visible)
65+
self.layout.addRow("Color:", self.colorButton)
66+
self.layout.addRow('Tolerance:', self.toleranceBox)
67+
self.layout.addRow('Apply tolerance:', self.toleranceToggle)
68+
self.layout.addRow(HorizontalLine())
69+
self.layout.addRow(self.sampleButton)
70+
self.layout.addRow(self.closeButton)
71+
72+
def _sample_sites(self):
73+
self.model.getExternalSourceSites(self.nSitesBox.value())
74+
self.parent.applyChanges()
75+
76+
def _toggle_source_sites(self):
77+
self.model.sourceSitesVisible = self.sites_visible.isChecked()
78+
self.parent.applyChanges()
79+
80+
def _select_source_site_color(self):
81+
color = QtWidgets.QColorDialog.getColor()
82+
if color.isValid():
83+
rgb = self.model.sourceSitesColor = color.getRgb()[:3]
84+
self.colorButton.setStyleSheet(
85+
f"border-radius: 8px; background-color: rgb{rgb}")
86+
self.parent.applyChanges()
87+
88+
def _toggle_tolerance(self):
89+
self.model.sourceSitesApplyTolerance = self.toleranceToggle.isChecked()
90+
self.toleranceBox.setEnabled(self.toleranceToggle.isChecked())
91+
self.parent.applyChanges()
92+
93+
def _set_source_site_tolerance(self):
94+
self.model.sourceSitesTolerance = self.toleranceBox.value()
95+
self.parent.applyChanges()
96+
97+
1198
class ExportDataDialog(QtWidgets.QDialog):
1299
"""
13100
A dialog to facilitate generation of VTK files for

screenshots/source-sites.png

241 KB
Loading

0 commit comments

Comments
 (0)