diff --git a/openmc_plotter/__init__.py b/openmc_plotter/__init__.py index 81cfc8c..e1424ed 100644 --- a/openmc_plotter/__init__.py +++ b/openmc_plotter/__init__.py @@ -1,2 +1 @@ - __version__ = '0.3.1' diff --git a/openmc_plotter/__main__.py b/openmc_plotter/__main__.py index 6e8d416..5e95cd0 100644 --- a/openmc_plotter/__main__.py +++ b/openmc_plotter/__main__.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - from argparse import ArgumentParser from pathlib import Path from threading import Thread @@ -13,28 +11,28 @@ from . import __version__ from .main_window import MainWindow, _openmcReload + def main(): ap = ArgumentParser(description='OpenMC Plotter GUI') version_str = f'OpenMC Plotter Version: {__version__}' ap.add_argument('-v', '--version', action='version', version=version_str, help='Display version info.') - ap.add_argument('-d', '--model-directory', default=os.curdir, - help='Location of model dir (default is current dir)') ap.add_argument('-e','--ignore-settings', action='store_false', help='Ignore plot_settings.pkl file if present.') ap.add_argument('-s', '--threads', type=int, default=None, help='If present, number of threads used to generate plots.') + ap.add_argument('model_path', nargs='?', default=os.curdir, + help='Location of model XML file or a directory containing ' + 'XML files (default is current dir)') args = ap.parse_args() - os.chdir(args.model_directory) - run_app(args) def run_app(user_args): - path_icon = str(Path(__file__).parent / 'assets/openmc_logo.png') - path_splash = str(Path(__file__).parent / 'assets/splash.png') + path_icon = str(Path(__file__).parent / 'assets' / 'openmc_logo.png') + path_splash = str(Path(__file__).parent / 'assets' / 'splash.png') app = QApplication(sys.argv) app.setOrganizationName("OpenMC") @@ -53,7 +51,7 @@ def run_app(user_args): QtCore.Qt.AlignHCenter | QtCore.Qt.AlignBottom) app.processEvents() # load OpenMC model on another thread - openmc_args = {'threads': user_args.threads} + openmc_args = {'threads': user_args.threads, 'model_path': user_args.model_path} loader_thread = Thread(target=_openmcReload, kwargs=openmc_args) loader_thread.start() # while thread is working, process app events @@ -67,7 +65,7 @@ def run_app(user_args): font_metric = QtGui.QFontMetrics(app.font()) screen_size = app.primaryScreen().size() - mainWindow = MainWindow(font_metric, screen_size) + mainWindow = MainWindow(font_metric, screen_size, user_args.model_path) # connect splashscreen to main window, close when main window opens mainWindow.loadGui(use_settings_pkl=user_args.ignore_settings) mainWindow.show() diff --git a/openmc_plotter/docks.py b/openmc_plotter/docks.py index ccbdaf5..c348d72 100644 --- a/openmc_plotter/docks.py +++ b/openmc_plotter/docks.py @@ -678,9 +678,9 @@ def update(self): self.tallySelector.addItem("None") for idx, tally in enumerate(self.model.statepoint.tallies.values()): if tally.name == "": - self.tallySelector.addItem('Tally {}'.format(tally.id), userData=tally.id) + self.tallySelector.addItem(f'Tally {tally.id}', userData=tally.id) else: - self.tallySelector.addItem('Tally {} "{}"'.format(tally.id, tally.name), userData=tally.id) + self.tallySelector.addItem(f'Tally {tally.id} "{tally.name}"', userData=tally.id) self.tally_map[idx] = tally self.updateSelectedTally() self.updateMinMax() diff --git a/openmc_plotter/main_window.py b/openmc_plotter/main_window.py index d527678..7514aa3 100755 --- a/openmc_plotter/main_window.py +++ b/openmc_plotter/main_window.py @@ -1,6 +1,6 @@ import copy from functools import partial -import os +from pathlib import Path import pickle from threading import Thread @@ -20,15 +20,14 @@ except ImportError: _HAVE_VTK = False -from .plotmodel import PlotModel, DomainTableModel, hash_file +from .plotmodel import PlotModel, DomainTableModel, hash_model from .plotgui import PlotImage, ColorDialog from .docks import DomainDock, TallyDock from .overlays import ShortcutsOverlay from .tools import ExportDataDialog -_COORD_LEVELS = 0 -def _openmcReload(threads=None): +def _openmcReload(threads=None, model_path='.'): # reset OpenMC memory, instances openmc.lib.reset() openmc.lib.finalize() @@ -37,18 +36,22 @@ def _openmcReload(threads=None): args = ["-c"] if threads is not None: args += ["-s", str(threads)] + args.append(model_path) openmc.lib.init(args) openmc.lib.settings.verbosity = 1 + class MainWindow(QMainWindow): def __init__(self, font=QtGui.QFontMetrics(QtGui.QFont()), - screen_size=QtCore.QSize()): + screen_size=QtCore.QSize(), + model_path='.'): super().__init__() self.screen = screen_size self.font_metric = font self.setWindowTitle('OpenMC Plot Explorer') + self.model_path = Path(model_path) def loadGui(self, use_settings_pkl=True): @@ -451,7 +454,7 @@ def loadModel(self, reload=False, use_settings_pkl=True): if reload: self.resetModels() else: - self.model = PlotModel(use_settings_pkl) + self.model = PlotModel(use_settings_pkl, self.model_path) # update plot and model settings self.updateRelativeBases() @@ -1151,11 +1154,10 @@ def closeEvent(self, event): def saveSettings(self): if self.model.statepoint: - self.model.statepoint.close() + self.model.statepoint.close() - # get hashes for geometry.xml and material.xml at close - mat_xml_hash = hash_file('materials.xml') - geom_xml_hash = hash_file('geometry.xml') + # get hashes for material.xml and geometry.xml at close + mat_xml_hash, geom_xml_hash = hash_model(self.model_path) pickle_data = { 'version': self.model.version, @@ -1164,7 +1166,11 @@ def saveSettings(self): 'mat_xml_hash': mat_xml_hash, 'geom_xml_hash': geom_xml_hash } - with open('plot_settings.pkl', 'wb') as file: + if self.model_path.is_file(): + settings_pkl = self.model_path.with_name('plot_settings.pkl') + else: + settings_pkl = self.model_path / 'plot_settings.pkl' + with settings_pkl.open('wb') as file: pickle.dump(pickle_data, file) def exportTallyData(self): diff --git a/openmc_plotter/plotmodel.py b/openmc_plotter/plotmodel.py index 4d36730..8441407 100644 --- a/openmc_plotter/plotmodel.py +++ b/openmc_plotter/plotmodel.py @@ -1,11 +1,12 @@ from ast import literal_eval from collections import defaultdict import copy +import hashlib import itertools -import threading import os +from pathlib import Path import pickle -import hashlib +import threading from PySide2.QtWidgets import QItemDelegate, QColorDialog, QLineEdit, QMessageBox from PySide2.QtCore import QAbstractTableModel, QModelIndex, Qt, QSize, QEvent @@ -18,7 +19,7 @@ from .statepointmodel import StatePointModel from .plot_colors import random_rgb, reset_seed -ID, NAME, COLOR, COLORLABEL, MASK, HIGHLIGHT = tuple(range(0, 6)) +ID, NAME, COLOR, COLORLABEL, MASK, HIGHLIGHT = range(6) _VOID_REGION = -1 _NOT_FOUND = -2 @@ -28,7 +29,6 @@ _PROPERTY_INDICES = {'temperature': 0, 'density': 1} _REACTION_UNITS = 'Reactions per Source Particle' -_FLUX_UNITS = 'Particle-cm per Source Particle' _PRODUCTION_UNITS = 'Particles Produced per Source Particle' _ENERGY_UNITS = 'eV per Source Particle' @@ -60,10 +60,11 @@ 'Std. Dev.': 'std_dev', 'Rel. Error': 'rel_err'} -def hash_file(filename): + +def hash_file(path): # return the md5 hash of a file h = hashlib.md5() - with open(filename,'rb') as file: + with path.open('rb') as file: chunk = 0 while chunk != b'': # read 32768 bytes at a time @@ -72,50 +73,66 @@ def hash_file(filename): return h.hexdigest() -class PlotModel(): - """ Geometry and plot settings for OpenMC Plot Explorer model +def hash_model(model_path): + """Get hash values for materials.xml and geometry.xml (or model.xml)""" + if model_path.is_file(): + mat_xml_hash = hash_file(model_path) + geom_xml_hash = "" + elif (model_path / 'model.xml').exists(): + mat_xml_hash = hash_file(model_path / 'model.xml') + geom_xml_hash = "" + else: + mat_xml_hash = hash_file(model_path / 'materials.xml') + geom_xml_hash = hash_file(model_path / 'geometry.xml') + return mat_xml_hash, geom_xml_hash - Parameters - ---------- - use_settings_pkl : bool - If True, use plot_settings.pkl file to reload settings - Attributes - ---------- - geom : openmc.Geometry instance - OpenMC Geometry of the model - modelCells : collections.OrderedDict - Dictionary mapping cell IDs to openmc.Cell instances - modelMaterials : collections.OrderedDict - Dictionary mapping material IDs to openmc.Material instances - ids : NumPy int array (v_res, h_res, 1) - Mapping of plot coordinates to cell/material ID by pixel - ids_map : NumPy int32 array (v_res, h_res, 3) - Mapping of cell and material ids - properties : Numpy float array (v_res, h_res, 3) - Mapping of cell temperatures and material densities - image : NumPy int array (v_res, h_res, 3) - The current RGB image data - statepoint : StatePointModel - Simulation data model used to display tally results - applied_filters : tuple of ints - IDs of the applied filters for the displayed tally - previousViews : list of PlotView instances - List of previously created plot view settings used to undo - changes made in plot explorer - subsequentViews : list of PlotView instances - List of undone plot view settings used to redo changes made - in plot explorer - defaultView : PlotView instance - Default settings for given geometry - currentView : PlotView instance - Currently displayed plot settings in plot explorer - activeView : PlotView instance - Active state of settings in plot explorer, which may or may not - have unapplied changes +class PlotModel: + """Geometry and plot settings for OpenMC Plot Explorer model + + Parameters + ---------- + use_settings_pkl : bool + If True, use plot_settings.pkl file to reload settings + model_path : pathlib.Path + Path to model XML file or directory + + Attributes + ---------- + geom : openmc.Geometry + OpenMC Geometry of the model + modelCells : collections.OrderedDict + Dictionary mapping cell IDs to openmc.Cell instances + modelMaterials : collections.OrderedDict + Dictionary mapping material IDs to openmc.Material instances + ids : NumPy int array (v_res, h_res, 1) + Mapping of plot coordinates to cell/material ID by pixel + ids_map : NumPy int32 array (v_res, h_res, 3) + Mapping of cell and material ids + properties : Numpy float array (v_res, h_res, 3) + Mapping of cell temperatures and material densities + image : NumPy int array (v_res, h_res, 3) + The current RGB image data + statepoint : StatePointModel + Simulation data model used to display tally results + applied_filters : tuple of ints + IDs of the applied filters for the displayed tally + previousViews : list of PlotView instances + List of previously created plot view settings used to undo + changes made in plot explorer + subsequentViews : list of PlotView instances + List of undone plot view settings used to redo changes made + in plot explorer + defaultView : PlotView + Default settings for given geometry + currentView : PlotView + Currently displayed plot settings in plot explorer + activeView : PlotView + Active state of settings in plot explorer, which may or may not + have unapplied changes """ - def __init__(self, use_settings_pkl): + def __init__(self, use_settings_pkl, model_path): """ Initialize PlotModel class attributes """ # Retrieve OpenMC Cells/Materials @@ -147,8 +164,13 @@ def __init__(self, use_settings_pkl): self.defaultView = self.getDefaultView() - if use_settings_pkl and os.path.isfile('plot_settings.pkl'): - with open('plot_settings.pkl', 'rb') as file: + if model_path.is_file(): + settings_pkl = model_path.with_name('plot_settings.pkl') + else: + settings_pkl = model_path / 'plot_settings.pkl' + + if use_settings_pkl and settings_pkl.is_file(): + with settings_pkl.open('rb') as file: try: data = pickle.load(file) except AttributeError: @@ -176,8 +198,7 @@ def __init__(self, use_settings_pkl): # get materials.xml and geometry.xml hashes to # restore additional settings if possible - mat_xml_hash = hash_file('materials.xml') - geom_xml_hash = hash_file('geometry.xml') + mat_xml_hash, geom_xml_hash = hash_model(model_path) if mat_xml_hash == data['mat_xml_hash'] and \ geom_xml_hash == data['geom_xml_hash']: restore_domains = True @@ -1071,8 +1092,8 @@ def adopt_plotbase(self, view): self.basis = view.basis -class DomainView(): - """ Represents view settings for OpenMC cell or material. +class DomainView: + """Represents view settings for OpenMC cell or material. Parameters ---------- diff --git a/openmc_plotter/tools.py b/openmc_plotter/tools.py index df431d1..6e29044 100644 --- a/openmc_plotter/tools.py +++ b/openmc_plotter/tools.py @@ -1,13 +1,13 @@ import copy -from time import sleep import numpy as np import openmc -from PySide2 import QtCore, QtGui, QtWidgets +from PySide2 import QtCore, QtWidgets from .custom_widgets import HorizontalLine from .scientific_spin_box import ScientificDoubleSpinBox + class ExportDataDialog(QtWidgets.QDialog): """ A dialog to facilitate generation of VTK files for