diff --git a/docs/source/_images/plotmeshtally.png b/docs/source/_images/plotmeshtally.png deleted file mode 100644 index d874b4906..000000000 Binary files a/docs/source/_images/plotmeshtally.png and /dev/null differ diff --git a/docs/source/io_formats/plots.rst b/docs/source/io_formats/plots.rst index 04d5720c8..c1fa78330 100644 --- a/docs/source/io_formats/plots.rst +++ b/docs/source/io_formats/plots.rst @@ -71,9 +71,9 @@ sub-elements: the PNG file format. The "voxel" plot type produces a binary datafile containing voxel grid positioning and the cell or material (specified by the ``color`` tag) at the center of each voxel. Voxel plot files can be - processed into VTK files using the :ref:`scripts_voxel` script provided with - OpenMC and subsequently viewed with a 3D viewer such as VISIT or Paraview. - See the :ref:`io_voxel` for information about the datafile structure. + processed into VTK files using the :func:`openmc.voxel_to_vtk` function and + subsequently viewed with a 3D viewer such as VISIT or Paraview. See the + :ref:`io_voxel` for information about the datafile structure. .. note:: High-resolution voxel files produced by OpenMC can be quite large, but the equivalent VTK files will be significantly smaller. @@ -152,17 +152,17 @@ attributes or sub-elements. These are not used in "voxel" plots: *Default*: 255 255 255 (white) :show_overlaps: - Indicates whether overlapping regions of different cells are shown. + Indicates whether overlapping regions of different cells are shown. *Default*: None :overlap_color: - Specifies the RGB color of overlapping regions of different cells. Does not - do anything if ``show_overlaps`` is "false" or not specified. Should be 3 + Specifies the RGB color of overlapping regions of different cells. Does not + do anything if ``show_overlaps`` is "false" or not specified. Should be 3 integers separated by spaces. *Default*: 255 0 0 (red) - + :meshlines: The ``meshlines`` sub-element allows for plotting the boundaries of a regular mesh on top of a plot. Only one ``meshlines`` element is allowed per diff --git a/docs/source/usersguide/plots.rst b/docs/source/usersguide/plots.rst index 2d588519c..d453ea453 100644 --- a/docs/source/usersguide/plots.rst +++ b/docs/source/usersguide/plots.rst @@ -111,11 +111,11 @@ The voxel plot data is written to an :ref:`HDF5 file `. The voxel file can subsequently be converted into a standard mesh format that can be viewed in `ParaView `_, `VisIt `_, etc. This typically -will compress the size of the file significantly. The provided -:ref:`scripts_voxel` script can convert the HDF5 voxel file to VTK formats. Once -processed into a standard 3D file format, colors and masks can be defined using -the stored ID numbers to better explore the geometry. The process for doing this -will depend on the 3D viewer, but should be straightforward. +will compress the size of the file significantly. The +:func:`openmc.voxel_to_vtk` function can convert the HDF5 voxel file to VTK +formats. Once processed into a standard 3D file format, colors and masks can be +defined using the stored ID numbers to better explore the geometry. The process +for doing this will depend on the 3D viewer, but should be straightforward. .. note:: 3D voxel plotting can be very computer intensive for the viewing program (Visit, ParaView, etc.) if the number of voxels is large (>10 diff --git a/docs/source/usersguide/processing.rst b/docs/source/usersguide/processing.rst index 3103b7b7c..10944b5e2 100644 --- a/docs/source/usersguide/processing.rst +++ b/docs/source/usersguide/processing.rst @@ -41,12 +41,9 @@ Plotting in 2D -------------- The `example notebook`_ also demonstrates how to plot a structured mesh tally in -two dimensions using the Python API. One can also use the :ref:`scripts_plot` -script which provides an interactive GUI to explore and plot structured mesh -tallies for any scores and filter bins. - -.. image:: ../_images/plotmeshtally.png - :width: 400px +two dimensions using the Python API. One can also use the `openmc-plotter +`_ application that provides an +interactive GUI to explore and plot a much wider variety of tallies. .. _usersguide_track: @@ -81,7 +78,7 @@ of three, e.g., if we wanted particles 3 and 4 from batch 1 and generation 2:: After running OpenMC, the working directory will contain a file of the form "track_(batch #)_(generation #)_(particle #).h5" for each particle tracked. These track files can be converted into VTK poly data files with the -:ref:`scripts_track` script. +:class:`openmc.Tracks` class. ---------------------- Source Site Processing diff --git a/docs/source/usersguide/scripts.rst b/docs/source/usersguide/scripts.rst index 78ee6f775..0879d63ef 100644 --- a/docs/source/usersguide/scripts.rst +++ b/docs/source/usersguide/scripts.rst @@ -53,142 +53,3 @@ flags: .. note:: If you're using the Python API, :func:`openmc.run` is equivalent to running ``openmc`` from the command line. - -.. _scripts_ace: - ----------------------- -``openmc-ace-to-hdf5`` ----------------------- - -This script can be used to create HDF5 nuclear data libraries used by OpenMC if -you have existing ACE files. There are four different ways you can specify ACE -libraries that are to be converted: - -1. List each ACE library as a positional argument. This is very useful in - conjunction with the usual shell utilities (``ls``, ``find``, etc.). -2. Use the ``--xml`` option to specify a pre-v0.9 cross_sections.xml file. -3. Use the ``--xsdir`` option to specify a MCNP xsdir file. -4. Use the ``--xsdata`` option to specify a Serpent xsdata file. - -The script does not use any extra information from cross_sections.xml/ xsdir/ -xsdata files to determine whether the nuclide is metastable. Instead, the -``--metastable`` argument can be used to specify whether the ZAID naming convention -follows the NNDC data convention (1000*Z + A + 300 + 100*m), or the MCNP data -convention (essentially the same as NNDC, except that the first metastable state -of Am242 is 95242 and the ground state is 95642). - -The optional ``--fission_energy_release`` argument will accept an HDF5 file -containing a library of fission energy release (ENDF MF=1 MT=458) data. A -library built from ENDF/B-VII.1 data is released with OpenMC and can be found at -openmc/data/fission_Q_data_endb71.h5. This data is necessary for -'fission-q-prompt' and 'fission-q-recoverable' tallies, but is not needed -otherwise. - --h, --help show help message and exit - --d DESTINATION, --destination DESTINATION - Directory to create new library in - --m META, --metastable META - How to interpret ZAIDs for metastable nuclides. META - can be either 'nndc' or 'mcnp'. (default: nndc) - ---xml XML Old-style cross_sections.xml that lists ACE libraries - ---xsdir XSDIR MCNP xsdir file that lists ACE libraries - ---xsdata XSDATA Serpent xsdata file that lists ACE libraries - ---fission_energy_release FISSION_ENERGY_RELEASE - HDF5 file containing fission energy release data - -.. _scripts_plot: - --------------------------- -``openmc-plot-mesh-tally`` --------------------------- - -``openmc-plot-mesh-tally`` provides a graphical user interface for plotting mesh -tallies. The path to the statepoint file can be provided as an optional arugment -(if omitted, a file dialog will be presented). - -.. _scripts_track_combine: - ------------------------- -``openmc-track-combine`` ------------------------- - -This script combines multiple HDF5 :ref:`particle track files -` into a single HDF5 particle track file. The filenames of the -particle track files should be given as posititional arguments. The output -filename can also be changed with the ``-o`` flag: - --o OUT, --out OUT Output HDF5 particle track file - -.. _scripts_track: - ------------------------ -``openmc-track-to-vtk`` ------------------------ - -This script converts HDF5 :ref:`particle track files ` to VTK -poly data that can be viewed with ParaView or VisIt. The filenames of the -particle track files should be given as posititional arguments. The output -filename can also be changed with the ``-o`` flag: - --o OUT, --out OUT Output VTK poly filename - ------------------------- -``openmc-update-inputs`` ------------------------- - -If you have existing XML files that worked in a previous version of OpenMC that -no longer work with the current version, you can try to update these files using -``openmc-update-inputs``. If any of the given files do not match the most -up-to-date formatting, then they will be automatically rewritten. The old -out-of-date files will not be deleted; they will be moved to a new file with -'.original' appended to their name. - -Formatting changes that will be made: - -geometry.xml - Lattices containing 'outside' attributes/tags will be replaced with lattices - containing 'outer' attributes, and the appropriate cells/universes will be - added. Any 'surfaces' attributes/elements on a cell will be renamed 'region'. - -materials.xml - Nuclide names will be changed from ACE aliases (e.g., Am-242m) to HDF5/GNDS - names (e.g., Am242_m1). Thermal scattering table names will be changed from - ACE aliases (e.g., HH2O) to HDF5/GNDS names (e.g., c_H_in_H2O). - ----------------------- -``openmc-update-mgxs`` ----------------------- - -This script updates OpenMC's deprecated multi-group cross section XML files to -the latest HDF5-based format. - --i IN, --input IN Input XML file --o OUT, --output OUT Output file in HDF5 format - -.. _scripts_voxel: - ---------------------------- -``openmc-voxel-to-vtk`` ---------------------------- - -When OpenMC generates :ref:`voxel plots `, they are in an -:ref:`HDF5 format ` that is not terribly useful by itself. The -``openmc-voxel-to-vtk`` script converts a voxel HDF5 file to a `VTK -`_ file. To run this script, you will need to have the VTK -Python bindings installed. To convert a voxel file, simply provide the path to -the file: - -.. code-block:: sh - - openmc-voxel-to-vtk voxel_1.h5 - -The ``openmc-voxel-to-vtk`` script also takes the following optional -command-line arguments: - --o, --output Path to output VTK file diff --git a/docs/source/usersguide/settings.rst b/docs/source/usersguide/settings.rst index ca11d6487..1b2d4bc1a 100644 --- a/docs/source/usersguide/settings.rst +++ b/docs/source/usersguide/settings.rst @@ -751,11 +751,10 @@ instance, whereas the :meth:`openmc.Track.filter` method returns a new with more than one process, a separate track file will be written for each MPI process with the filename ``tracks_p#.h5`` where # is the rank of the corresponding process. Multiple track files can be - combined with the :ref:`scripts_track_combine` script: + combined with the :meth:`openmc.Tracks.combine` method:: - .. code-block:: sh - - openmc-track-combine tracks_p*.h5 --out tracks.h5 + track_files = [f"tracks_p{rank}.h5" for rank in range(32)] + openmc.Tracks.combine(track_files, "tracks.h5") ----------------------- Restarting a Simulation diff --git a/pyproject.toml b/pyproject.toml index e4e8472f1..1f8de10e3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -59,19 +59,10 @@ Repository = "https://github.com/openmc-dev/openmc" Issues = "https://github.com/openmc-dev/openmc/issues" [tool.setuptools.packages.find] -include = ['openmc*', 'scripts*'] +include = ['openmc*'] exclude = ['tests*'] [tool.setuptools.package-data] "openmc.data.effective_dose" = ["**/*.txt"] "openmc.data" = ["*.txt", "*.DAT", "*.json", "*.h5"] "openmc.lib" = ["libopenmc.dylib", "libopenmc.so"] - -[project.scripts] -openmc-ace-to-hdf5 = "scripts.openmc_ace_to_hdf5:main" -openmc-plot-mesh-tally = "scripts.openmc_plot_mesh_tally:main" -openmc-track-combine = "scripts.openmc_track_combine:main" -openmc-track-to-vtk = "scripts.openmc_track_to_vtk:main" -openmc-update-inputs = "scripts.openmc_update_inputs:main" -openmc-update-mgxs = "scripts.openmc_update_mgxs:main" -openmc-voxel-to-vtk = "scripts.openmc_voxel_to_vtk:main" diff --git a/scripts/openmc-ace-to-hdf5 b/scripts/openmc-ace-to-hdf5 deleted file mode 100755 index 66ae7e2a9..000000000 --- a/scripts/openmc-ace-to-hdf5 +++ /dev/null @@ -1,156 +0,0 @@ -#!/usr/bin/env python3 - -"""This script can be used to create HDF5 nuclear data libraries used by -OpenMC. There are four different ways you can specify ACE libraries that are to -be converted: - -1. List each ACE library as a positional argument. This is very useful in - conjunction with the usual shell utilities (ls, find, etc.). -2. Use the --xsdir option to specify a MCNP xsdir file. -3. Use the --xsdata option to specify a Serpent xsdata file. - -The script does not use any extra information from xsdir/xsdata files to -determine whether the nuclide is metastable. Instead, the --metastable argument -can be used to specify whether the ZAID naming convention follows the NNDC data -convention (1000*Z + A + 300 + 100*m), or the MCNP data convention (essentially -the same as NNDC, except that the first metastable state of Am242 is 95242 and -the ground state is 95642). - -""" - -import argparse -from functools import partial -import os -from pathlib import Path -import warnings - -import openmc.data -from openmc.data.ace import TableType - - -def ace_to_hdf5(destination, xsdir, xsdata, libraries, metastable, libver): - - if not destination.is_dir(): - destination.mkdir(parents=True, exist_ok=True) - - ace_libraries = [] - if xsdir is not None: - ace_libraries.extend(openmc.data.ace.get_libraries_from_xsdir(xsdir)) - elif xsdata is not None: - ace_libraries.extend(openmc.data.ace.get_libraries_from_xsdata(xsdata)) - else: - ace_libraries = [Path(lib) for lib in libraries] - - converted = {} - library = openmc.data.DataLibrary() - - for path in ace_libraries: - # Check that ACE library exists - if not os.path.exists(path): - warnings.warn(f"ACE library '{path}' does not exist.") - continue - - lib = openmc.data.ace.Library(path) - for table in lib.tables: - # Check type of the ACE table and determine appropriate class / - # conversion function - if table.data_type == TableType.NEUTRON_CONTINUOUS: - name = table.zaid - cls = openmc.data.IncidentNeutron - converter = partial(cls.from_ace, metastable_scheme=metastable) - elif table.data_type == TableType.THERMAL_SCATTERING: - # Adjust name to be the new thermal scattering name - name = openmc.data.get_thermal_name(table.zaid) - cls = openmc.data.ThermalScattering - converter = cls.from_ace - else: - print(f"Can't convert ACE table {table.name}") - continue - - if name not in converted: - try: - data = converter(table) - except Exception as e: - print(f"Failed to convert {table.name}: {e}") - continue - - print(f"Converting {table.name} (ACE) to {data.name} (HDF5)") - - # Determine output filename - outfile = destination / (data.name.replace(".", "_") + ".h5") - data.export_to_hdf5(outfile, "w", libver=libver) - - # Register with library - library.register_file(outfile) - - # Add nuclide to list - converted[name] = outfile - else: - # Read existing HDF5 file - data = cls.from_hdf5(converted[name]) - - # Add data for new temperature - try: - print(f"Converting {table.name} (ACE) to {data.name} (HDF5)") - if table.data_type == TableType.NEUTRON_CONTINUOUS: - data.add_temperature_from_ace(table, metastable) - else: - data.add_temperature_from_ace(table) - except Exception as e: - print(f"Failed to convert {table.name}: {e}") - continue - - # Re-export - data.export_to_hdf5(converted[name], "w", libver=libver) - - # Write cross_sections.xml - library.export_to_xml(destination / "cross_sections.xml") - - -if __name__ == "__main__": - - class CustomFormatter( - argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter - ): - pass - - parser = argparse.ArgumentParser( - description=__doc__, formatter_class=CustomFormatter - ) - parser.add_argument("libraries", nargs="*", help="ACE libraries to convert to HDF5") - parser.add_argument( - "-d", - "--destination", - type=Path, - default=Path.cwd(), - help="Directory to create new library in", - ) - parser.add_argument( - "-m", - "--metastable", - choices=["mcnp", "nndc"], - default="nndc", - help="How to interpret ZAIDs for metastable nuclides", - ) - parser.add_argument("--xsdir", help="MCNP xsdir file that lists " "ACE libraries") - parser.add_argument( - "--xsdata", help="Serpent xsdata file that lists " "ACE libraries" - ) - parser.add_argument( - "--libver", - choices=["earliest", "latest"], - default="earliest", - help="Output HDF5 versioning. Use " - "'earliest' for backwards compatibility or 'latest' " - "for performance", - ) - args = parser.parse_args() - - ace_to_hdf5( - destination=args.destination, - xsdir=args.xsdir, - xsdata=args.xsdata, - libraries=args.libraries, - metastable=args.metastable, - libver=args.libver, - ) diff --git a/scripts/openmc-plot-mesh-tally b/scripts/openmc-plot-mesh-tally deleted file mode 100755 index 2a4fcd743..000000000 --- a/scripts/openmc-plot-mesh-tally +++ /dev/null @@ -1,344 +0,0 @@ -#!/usr/bin/env python3 - -"""Python script to plot tally data generated by OpenMC.""" - - -import os -import sys -import argparse -import tkinter as tk -import tkinter.filedialog as filedialog -import tkinter.font as font -import tkinter.messagebox as messagebox -import tkinter.ttk as ttk - -import matplotlib -matplotlib.use("TkAgg") -from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg -from matplotlib.backends.backend_tkagg import NavigationToolbar2Tk -from matplotlib.figure import Figure -import matplotlib.pyplot as plt -import numpy as np - -from openmc import StatePoint, MeshFilter, UnstructuredMesh - -_COMBOBOX_SELECTED = '<>' - - -def mesh_filter_check(filter): - """ - Check that the filter is a usable mesh filter - """ - return isinstance(filter, MeshFilter) and not isinstance(filter.mesh, UnstructuredMesh) - - -class MeshPlotter(tk.Frame): - def __init__(self, parent, filename): - super().__init__(parent) - - self.labels = { - 'Cell': 'Cell:', - 'Cellborn': 'Cell born:', - 'Surface': 'Surface:', - 'Material': 'Material:', - 'Universe': 'Universe:', - 'Energy': 'Energy in:', - 'Energyout': 'Energy out:' - } - - self.filterBoxes = {} - - # Read data from source or leakage fraction file - self.get_file_data(filename) - - # Set up top-level window - top = self.winfo_toplevel() - top.title('Mesh Tally Plotter: ' + filename) - top.rowconfigure(0, weight=1) - top.columnconfigure(0, weight=1) - self.grid(sticky=tk.W+tk.N) - - # Create widgets and draw to screen - self.create_widgets() - self.update() - - def create_widgets(self): - figureFrame = tk.Frame(self) - figureFrame.grid(row=0, column=0) - - # Create the Figure and Canvas - self.dpi = 100 - self.fig = Figure((5.0, 5.0), dpi=self.dpi) - self.canvas = FigureCanvasTkAgg(self.fig, master=figureFrame) - self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1) - - # Create the navigation toolbar, tied to the canvas - self.mpl_toolbar = NavigationToolbar2Tk(self.canvas, figureFrame) - self.mpl_toolbar.update() - self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1) - - # Create frame for comboboxes - self.selectFrame = tk.Frame(self) - self.selectFrame.grid(row=1, column=0, sticky=tk.W+tk.E) - - # Tally selection - labelTally = tk.Label(self.selectFrame, text='Tally:') - labelTally.grid(row=0, column=0, sticky=tk.W) - self.tallyBox = ttk.Combobox(self.selectFrame, state='readonly') - self.tallyBox['values'] = [self.datafile.tallies[i].id - for i in self.meshTallies] - self.tallyBox.current(0) - self.tallyBox.grid(row=0, column=1, sticky=tk.W+tk.E) - self.tallyBox.bind(_COMBOBOX_SELECTED, self.update) - - # Planar basis selection - labelBasis = tk.Label(self.selectFrame, text='Basis:') - labelBasis.grid(row=1, column=0, sticky=tk.W) - self.basisBox = ttk.Combobox(self.selectFrame, state='readonly') - self.basisBox['values'] = ('xy', 'yz', 'xz') - self.basisBox.current(0) - self.basisBox.grid(row=1, column=1, sticky=tk.W+tk.E) - self.basisBox.bind(_COMBOBOX_SELECTED, self.update) - - # Axial level - labelAxial = tk.Label(self.selectFrame, text='Axial level:') - labelAxial.grid(row=2, column=0, sticky=tk.W) - self.axialBox = ttk.Combobox(self.selectFrame, state='readonly') - self.axialBox.grid(row=2, column=1, sticky=tk.W+tk.E) - self.axialBox.bind(_COMBOBOX_SELECTED, self.redraw) - - # Option for mean/uncertainty - labelMean = tk.Label(self.selectFrame, text='Mean/Uncertainty:') - labelMean.grid(row=3, column=0, sticky=tk.W) - self.meanBox = ttk.Combobox(self.selectFrame, state='readonly') - self.meanBox['values'] = ('Mean', 'Absolute uncertainty', - 'Relative uncertainty') - self.meanBox.current(0) - self.meanBox.grid(row=3, column=1, sticky=tk.W+tk.E) - self.meanBox.bind(_COMBOBOX_SELECTED, self.update) - - # Scores - labelScore = tk.Label(self.selectFrame, text='Score:') - labelScore.grid(row=4, column=0, sticky=tk.W) - self.scoreBox = ttk.Combobox(self.selectFrame, state='readonly') - self.scoreBox.grid(row=4, column=1, sticky=tk.W+tk.E) - self.scoreBox.bind(_COMBOBOX_SELECTED, self.redraw) - - # Filter label - boldfont = font.Font(weight='bold') - labelFilters = tk.Label(self.selectFrame, text='Filters:', - font=boldfont) - labelFilters.grid(row=5, column=0, sticky=tk.W) - - def update(self, event=None): - widget = event.widget if event else None - - tally_id = self.meshTallies[self.tallyBox.current()] - selectedTally = self.datafile.tallies[tally_id] - - # Get mesh for selected tally - self.mesh = selectedTally.find_filter(MeshFilter).mesh - - # Get mesh dimensions - if len(self.mesh.dimension) == 2: - self.nx, self.ny = self.mesh.dimension - self.nz = 1 - else: - self.nx, self.ny, self.nz = self.mesh.dimension - - # Repopulate comboboxes baesd on current basis selection - text = self.basisBox.get() - if text == 'xy': - self.axialBox['values'] = [str(i+1) for i in range(self.nz)] - elif text == 'yz': - self.axialBox['values'] = [str(i+1) for i in range(self.nx)] - else: - self.axialBox['values'] = [str(i+1) for i in range(self.ny)] - self.axialBox.current(0) - - # If update() was called by a change in the basis combobox, we don't - # need to repopulate the filters - if widget == self.basisBox: - self.redraw() - return - - # Update scores - self.scoreBox['values'] = selectedTally.scores - self.scoreBox.current(0) - - # Remove any filter labels/comboboxes that exist - for row in range(6, self.selectFrame.grid_size()[1]): - for w in self.selectFrame.grid_slaves(row=row): - w.grid_forget() - w.destroy() - - # create a label/combobox for each filter in selected tally - count = 0 - for f in selectedTally.filters: - filterType = f.short_name - if filterType == 'Mesh': - continue - count += 1 - - # Create label and combobox for this filter - label = tk.Label(self.selectFrame, text=self.labels[filterType]) - label.grid(row=count+6, column=0, sticky=tk.W) - combobox = ttk.Combobox(self.selectFrame, state='readonly') - self.filterBoxes[filterType] = combobox - - # Set combobox items - if filterType in ['Energy', 'Energyout']: - combobox['values'] = ['{} to {}'.format(*ebin) - for ebin in f.bins] - else: - combobox['values'] = [str(i) for i in f.bins] - - combobox.current(0) - combobox.grid(row=count+6, column=1, sticky=tk.W+tk.E) - combobox.bind(_COMBOBOX_SELECTED, self.redraw) - - # If There are no filters, leave a 'None available' message - if count == 0: - count += 1 - label = tk.Label(self.selectFrame, text="None Available") - label.grid(row=count+6, column=0, sticky=tk.W) - - self.redraw() - - def redraw(self, event=None): - basis = self.basisBox.current() + 1 - axial_level = self.axialBox.current() + 1 - mbvalue = self.meanBox.get() - - # Get selected tally - tally_id = self.meshTallies[self.tallyBox.current()] - selectedTally = self.datafile.tallies[tally_id] - - # Create spec_list - spec_list = [] - for f in selectedTally.filters: - if f.short_name == 'Mesh': - mesh_filter = f - continue - elif f.short_name in ['Energy', 'Energyout']: - index = self.filterBoxes[f.short_name].current() - ebin = f.bins[index] - spec_list.append((type(f), (ebin,))) - else: - index = self.filterBoxes[f.short_name].current() - spec_list.append((type(f), (index,))) - - dims = (self.nx, self.ny, self.nz) - - text = self.basisBox.get() - if text == 'xy': - h_ind = 0 - v_ind = 1 - elif text == 'yz': - h_ind = 1 - v_ind = 2 - else: - h_ind = 0 - v_ind = 2 - - axial_ind = 3 - (h_ind + v_ind) - dims = (dims[h_ind], dims[v_ind]) - - mesh_dim = len(self.mesh.dimension) - if mesh_dim == 3: - mesh_indices = [0,0,0] - else: - mesh_indices = [0,0] - - matrix = np.zeros(dims) - for i in range(dims[0]): - for j in range(dims[1]): - if mesh_dim == 3: - mesh_indices[h_ind] = i + 1 - mesh_indices[v_ind] = j + 1 - mesh_indices[axial_ind] = axial_level - else: - mesh_indices[0] = i + 1 - mesh_indices[1] = j + 1 - filters, filter_bins = zip(*spec_list + [ - (type(mesh_filter), (tuple(mesh_indices),))]) - mean = selectedTally.get_values( - [self.scoreBox.get()], filters, filter_bins) - stdev = selectedTally.get_values( - [self.scoreBox.get()], filters, filter_bins, - value='std_dev') - if mbvalue == 'Mean': - matrix[i, j] = mean - elif mbvalue == 'Absolute uncertainty': - matrix[i, j] = stdev - else: - if mean > 0.: - matrix[i, j] = stdev/mean - else: - matrix[i, j] = 0. - - # Clear the figure - self.fig.clear() - - # Make figure, set up color bar - self.axes = self.fig.add_subplot(111) - cax = self.axes.imshow(matrix.transpose(), vmin=0.0, vmax=matrix.max(), - interpolation='none', origin='lower') - self.fig.colorbar(cax) - - self.axes.set_xticks([]) - self.axes.set_yticks([]) - self.axes.set_aspect('equal') - - # Draw canvas - self.canvas.draw() - - def get_file_data(self, filename): - # Create StatePoint object and read in data - self.datafile = StatePoint(filename) - - meshes = self.datafile.meshes - if any(isinstance(m, UnstructuredMesh) for m in meshes.values()): - warn_msg = "Unstructured meshes are present in the" \ - " statepoint file but are not currently" \ - " supported by this script" - messagebox.showwarning("Unstructured Meshes", message=warn_msg) - - # Find which tallies are mesh tallies - self.meshTallies = [] - for itally, tally in self.datafile.tallies.items(): - if any([mesh_filter_check(f) for f in tally.filters]): - self.meshTallies.append(itally) - - if not self.meshTallies: - messagebox.showerror("Invalid StatePoint File", - "File does not contain mesh tallies!") - sys.exit(1) - - -if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument('statepoint', nargs='?', help='Statepoint file') - args = parser.parse_args() - - # Hide root window - root = tk.Tk() - root.withdraw() - - # If no filename given as command-line argument, open file dialog - if args.statepoint is None: - filename = filedialog.askopenfilename(title='Select statepoint file', - initialdir='.') - else: - filename = args.statepoint - - if filename: - # Check to make sure file exists - if not os.path.isfile(filename): - messagebox.showerror("File not found", - "Could not find regular file: " + filename) - sys.exit(1) - - app = MeshPlotter(root, filename) - root.deiconify() - root.mainloop() diff --git a/scripts/openmc-track-combine b/scripts/openmc-track-combine deleted file mode 100755 index f69f2151c..000000000 --- a/scripts/openmc-track-combine +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python3 - -"""Combine multiple HDF5 particle track files.""" - -import argparse - -import openmc - - -def main(): - # Parse command-line arguments - parser = argparse.ArgumentParser( - description='Combine particle track files into a single .h5 file.') - parser.add_argument('input', metavar='IN', nargs='+', - help='Input HDF5 particle track filename(s).') - parser.add_argument('-o', '--out', metavar='OUT', default='tracks.h5', - help='Output HDF5 particle track file.') - - args = parser.parse_args() - openmc.Tracks.combine(args.input, args.out) - - -if __name__ == '__main__': - main() diff --git a/scripts/openmc-track-to-vtk b/scripts/openmc-track-to-vtk deleted file mode 100755 index 82439bea7..000000000 --- a/scripts/openmc-track-to-vtk +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python3 - -"""Convert HDF5 particle track to VTK poly data. - -""" - -import argparse - -import openmc -import vtk - - -def _parse_args(): - # Create argument parser. - parser = argparse.ArgumentParser( - description='Convert particle track file(s) to a .pvtp file.') - parser.add_argument('input', metavar='IN', type=str, - help='Input particle track data filename.') - parser.add_argument('-o', '--out', metavar='OUT', type=str, dest='out', - help='Output VTK poly data filename.') - - # Parse and return commandline arguments. - return parser.parse_args() - - -def main(): - # Parse commandline arguments. - args = _parse_args() - - # Make sure that the output filename ends with '.pvtp'. - if not args.out: - args.out = 'tracks.pvtp' - elif not args.out.endswith('.pvtp'): - args.out += '.pvtp' - - # Write coordinate values to points array. - track_file = openmc.Tracks(args.input) - track_file.write_to_vtk(args.out) - -if __name__ == '__main__': - main() diff --git a/scripts/openmc-update-inputs b/scripts/openmc-update-inputs deleted file mode 100755 index 5ee1f1ca0..000000000 --- a/scripts/openmc-update-inputs +++ /dev/null @@ -1,307 +0,0 @@ -#!/usr/bin/env python3 -"""Update OpenMC's input XML files to the latest format.""" - -import argparse -from itertools import chain -from random import randint -from shutil import move -import xml.etree.ElementTree as ET - -import openmc.data - - -description = "Update OpenMC's input XML files to the latest format." -epilog = """\ -If any of the given files do not match the most up-to-date formatting, then they -will be automatically rewritten. The old out-of-date files will not be deleted; -they will be moved to a new file with '.original' appended to their name. - -Formatting changes that will be made: - -geometry.xml: Lattices containing 'outside' attributes/tags will be replaced - with lattices containing 'outer' attributes, and the appropriate - cells/universes will be added. Any 'surfaces' attributes/elements on a cell - will be renamed 'region'. - -materials.xml: Nuclide names will be changed from ACE aliases (e.g., Am-242m) to - HDF5/GNDS names (e.g., Am242_m1). Thermal scattering table names will be - changed from ACE aliases (e.g., HH2O) to HDF5/GNDS names (e.g., c_H_in_H2O). - -""" - - -def parse_args(): - """Read the input files from the commandline.""" - # Create argument parser. - parser = argparse.ArgumentParser( - description=description, - epilog=epilog, - formatter_class=argparse.RawTextHelpFormatter) - parser.add_argument('input', metavar='IN', type=str, nargs='+', - help='Input XML file(s).') - - # Parse and return commandline arguments. - return parser.parse_args() - - -def get_universe_ids(geometry_root): - """Return a set of universe id numbers.""" - root = geometry_root - out = set() - - # Get the ids of universes defined by cells. - for cell in root.iter('cell'): - # Get universe attributes/elements - if 'universe' in cell.attrib: - uid = cell.attrib['universe'] - out.add(int(uid)) - elif cell.find('universe') is not None: - elem = cell.find('universe') - uid = elem.text - out.add(int(uid)) - else: - # Default to universe 0 - out.add(0) - - # Get the ids of universes defined by lattices. - for lat in root.iter('lattice'): - # Get id attributes. - if 'id' in lat.attrib: - uid = lat.attrib['id'] - out.add(int(uid)) - - # Get id elements. - elif lat.find('id') is not None: - elem = lat.find('id') - uid = elem.text - out.add(int(uid)) - - return out - - -def get_cell_ids(geometry_root): - """Return a set of cell id numbers.""" - root = geometry_root - out = set() - - # Get the ids of universes defined by cells. - for cell in root.iter('cell'): - # Get id attributes. - if 'id' in cell.attrib: - cid = cell.attrib['id'] - out.add(int(cid)) - - # Get id elements. - elif cell.find('id') is not None: - elem = cell.find('id') - cid = elem.text - out.add(int(cid)) - - return out - - -def find_new_id(current_ids, preferred=None): - """Return a new id that is not already present in current_ids.""" - distance_from_preferred = 21 - max_random_attempts = 10000 - - # First, try to find an id near the preferred number. - if preferred is not None: - assert isinstance(preferred, int) - for i in range(1, distance_from_preferred): - if (preferred - i not in current_ids) and (preferred - i > 0): - return preferred - i - if (preferred + i not in current_ids) and (preferred + i > 0): - return preferred + i - - # If that was unsuccessful, attempt to randomly guess a new id number. - for i in range(max_random_attempts): - num = randint(1, 2147483647) - if num not in current_inds: - return num - - # Raise an error if an id was not found. - raise RuntimeError('Could not find a unique id number for a new universe.') - - -def get_lat_id(lattice_element): - """Return the id integer of the lattice_element.""" - assert isinstance(lattice_element, ET.Element) - if 'id' in lattice_element.attrib: - return int(lattice_element.attrib['id'].strip()) - elif any([child.tag == 'id' for child in lattice_element]): - elem = lattice_element.find('id') - return int(elem.text.strip()) - else: - raise RuntimeError('Could not find the id for a lattice.') - - -def pop_lat_outside(lattice_element): - """Return lattice's outside material and remove from attributes/elements.""" - assert isinstance(lattice_element, ET.Element) - - # Check attributes. - if 'outside' in lattice_element.attrib: - material = lattice_element.attrib['outside'].strip() - del lattice_element.attrib['outside'] - - # Check subelements. - elif any([child.tag == 'outside' for child in lattice_element]): - elem = lattice_element.find('outside') - material = elem.text.strip() - lattice_element.remove(elem) - - # No 'outside' specified. This means the outside is a void. - else: - material = 'void' - - return material - - -def update_geometry(geometry_root): - """Update the given XML geometry tree. Return True if changes were made.""" - root = geometry_root - was_updated = False - - # Get a set of already-used universe and cell ids. - uids = get_universe_ids(root) - cids = get_cell_ids(root) - taken_ids = uids.union(cids) - - # Replace 'outside' with 'outer' in lattices. - for lat in chain(root.iter('lattice'), root.iter('hex_lattice')): - # Get the lattice's id. - lat_id = get_lat_id(lat) - - # Ignore lattices that have 'outer' specified. - if any([child.tag == 'outer' for child in lat]): continue - if 'outer' in lat.attrib: continue - - # Pop the 'outside' material. - material = pop_lat_outside(lat) - - # Get an id number for a new outer universe. Ideally, the id should - # be close to the lattice's id. - new_uid = find_new_id(taken_ids, preferred=lat_id) - assert new_uid not in taken_ids - - # Add the new universe filled with the old 'outside' material to the - # geometry. - new_cell = ET.Element('cell') - new_cell.attrib['id'] = str(new_uid) - new_cell.attrib['universe'] = str(new_uid) - new_cell.attrib['material'] = material - root.append(new_cell) - taken_ids.add(new_uid) - - # Add the new universe to the lattice's 'outer' attribute. - lat.attrib['outer'] = str(new_uid) - - was_updated = True - - for surface in root.findall('surface'): - for attribute in ['type', 'coeffs', 'boundary']: - if surface.find(attribute) is not None: - value = surface.find(attribute).text - surface.attrib[attribute] = value - - child_element = surface.find(attribute) - surface.remove(child_element) - was_updated = True - - # Remove 'type' from lattice definitions. - for lat in root.iter('lattice'): - elem = lat.find('type') - if elem is not None: - lat.remove(elem) - was_updated = True - if 'type' in lat.attrib: - del lat.attrib['type'] - was_updated = True - - # Change 'width' to 'pitch' in lattice definitions. - for lat in root.iter('lattice'): - elem = lat.find('width') - if elem is not None: - elem.tag = 'pitch' - was_updated = True - if 'width' in lat.attrib: - lat.attrib['pitch'] = lat.attrib['width'] - del lat.attrib['width'] - was_updated = True - - # Change 'surfaces' to 'region' in cell definitions - for cell in root.iter('cell'): - elem = cell.find('surfaces') - if elem is not None: - elem.tag = 'region' - was_updated = True - if 'surfaces' in cell.attrib: - cell.attrib['region'] = cell.attrib['surfaces'] - del cell.attrib['surfaces'] - was_updated = True - - return was_updated - -def update_materials(root): - """Update the given XML materials tree. Return True if changes were made.""" - was_updated = False - - for material in root.findall('material'): - for nuclide in material.findall('nuclide'): - if 'name' in nuclide.attrib: - nucname = nuclide.attrib['name'].replace('-', '') - # If a nuclide name is in the ZAID notation (e.g., a number), - # convert it to the proper nuclide name. - if nucname.strip().isnumeric(): - nucname = openmc.data.ace.get_metadata(int(nucname))[0] - nucname = nucname.replace('Nat', '0') - if nucname.endswith('m'): - nucname = nucname[:-1] + '_m1' - nuclide.set('name', nucname) - was_updated = True - - elif nuclide.find('name') is not None: - name_elem = nuclide.find('name') - nucname = name_elem.text - nucname = nucname.replace('-', '') - nucname = nucname.replace('Nat', '0') - if nucname.endswith('m'): - nucname = nucname[:-1] + '_m1' - name_elem.text = nucname - was_updated = True - - for sab in material.findall('sab'): - if 'name' in sab.attrib: - sabname = sab.attrib['name'] - sab.set('name', openmc.data.get_thermal_name(sabname)) - was_updated = True - - elif sab.find('name') is not None: - name_elem = sab.find('name') - sabname = name_elem.text - name_elem.text = openmc.data.get_thermal_name(sabname) - was_updated = True - - return was_updated - - -if __name__ == '__main__': - args = parse_args() - for fname in args.input: - # Parse the XML data. - tree = ET.parse(fname) - root = tree.getroot() - was_updated = False - - if root.tag == 'geometry': - was_updated = update_geometry(root) - elif root.tag == 'materials': - was_updated = update_materials(root) - - if was_updated: - # Move the original geometry file to preserve it. - move(fname, fname + '.original') - - # Write a new geometry file. - tree.write(fname, xml_declaration=True) \ No newline at end of file diff --git a/scripts/openmc-update-mgxs b/scripts/openmc-update-mgxs deleted file mode 100755 index aac6959b7..000000000 --- a/scripts/openmc-update-mgxs +++ /dev/null @@ -1,212 +0,0 @@ -#!/usr/bin/env python3 -"""Update OpenMC's deprecated multi-group cross section XML files to the latest -HDF5-based format. - -""" - -import os -import warnings -import xml.etree.ElementTree as ET - -import argparse -import numpy as np - -import openmc.mgxs_library - - -def parse_args(): - """Read the input files from the commandline.""" - # Create argument parser - parser = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) - parser.add_argument('-i', '--input', type=argparse.FileType('r'), - help='input XML file') - parser.add_argument('-o', '--output', nargs='?', default='', - help='output file, in HDF5 format') - args = vars(parser.parse_args()) - - if args['output'] == '': - filename = args['input'].name - extension = os.path.splitext(filename) - if extension == '.xml': - filename = filename[:filename.rfind('.')] + '.h5' - args['output'] = filename - - # Parse and return commandline arguments. - return args - - -def get_data(element, entry): - value = element.find(entry) - if value is not None: - value = value.text.strip() - elif entry in element.attrib: - value = element.attrib[entry].strip() - else: - value = None - - return value - - -def main(): - args = parse_args() - - # Parse the XML data. - tree = ET.parse(args['input']) - root = tree.getroot() - - # Get old metadata - group_structure = tree.find('group_structure').text.strip() - group_structure = np.array(group_structure.split(), dtype=float) - # Convert from MeV to eV - group_structure *= 1.e6 - energy_groups = openmc.mgxs.EnergyGroups(group_structure) - - inverse_velocity = tree.find('inverse-velocity') - if inverse_velocity is not None: - inverse_velocity = inverse_velocity.text.split() - inverse_velocity = np.array(inverse_velocity, dtype=float) - else: - inverse_velocity = None - - xsd = [] - names = [] - - # Now move on to the cross section data itself - for xsdata_elem in root.iter('xsdata'): - name = get_data(xsdata_elem, 'name') - - temperature = get_data(xsdata_elem, 'kT') - if temperature is not None: - temperature = float(temperature) / openmc.data.K_BOLTZMANN * 1.E6 - else: - temperature = 294. - temperatures = [temperature] - - awr = get_data(xsdata_elem, 'awr') - if awr is not None: - awr = float(awr) - - representation = get_data(xsdata_elem, 'representation') - if representation is None: - representation = 'isotropic' - if representation == 'angle': - n_azi = int(get_data(xsdata_elem, 'num_azimuthal')) - n_pol = int(get_data(xsdata_elem, 'num_polar')) - - scatter_format = get_data(xsdata_elem, 'scatt_type') - if scatter_format is None: - scatter_format = 'legendre' - - order = int(get_data(xsdata_elem, 'order')) - - tab_leg = get_data(xsdata_elem, 'tabular_legendre') - if tab_leg is not None: - warnings.warn('The tabular_legendre option has moved to the ' - 'settings.xml file and must be added manually') - - # Either add the data to a previously existing xsdata (if it is - # for the same 'name' but a different temperature), or create a - # new one. - try: - # It is in our list, so store that entry - i = names.index(name) - except ValueError: - # It is not in our list, so add it - i = -1 - xsd.append(openmc.XSdata(name, energy_groups, - temperatures=temperatures, - representation=representation)) - if awr is not None: - xsd[-1].atomic_weight_ratio = awr - if representation == 'angle': - xsd[-1].num_azimuthal = n_azi - xsd[-1].num_polar = n_pol - xsd[-1].scatter_format = scatter_format - xsd[-1].order = order - names.append(name) - - if scatter_format == 'legendre': - order_dim = order + 1 - else: - order_dim = order - - if i != -1: - xsd[i].add_temperature(temperature) - - total = get_data(xsdata_elem, 'total') - if total is not None: - total = np.array(total.split(), dtype=float) - total.shape = xsd[i].xs_shapes['[G]'] - xsd[i].set_total(total, temperature) - - if inverse_velocity is not None: - xsd[i].set_inverse_velocity(inverse_velocity, temperature) - - absorption = get_data(xsdata_elem, 'absorption') - absorption = np.array(absorption.split(), dtype=float) - absorption.shape = xsd[i].xs_shapes['[G]'] - xsd[i].set_absorption(absorption, temperature) - - scatter = get_data(xsdata_elem, 'scatter') - scatter = np.array(scatter.split(), dtype=float) - # This is now a flattened-array of something that started with a - # shape of [Order][G][G']; we need to unflatten and then switch the - # ordering - in_shape = (order_dim, energy_groups.num_groups, - energy_groups.num_groups) - if representation == 'angle': - in_shape = (n_pol, n_azi) + in_shape - scatter.shape = in_shape - scatter = np.swapaxes(scatter, 2, 3) - scatter = np.swapaxes(scatter, 3, 4) - else: - scatter.shape = in_shape - scatter = np.swapaxes(scatter, 0, 1) - scatter = np.swapaxes(scatter, 1, 2) - - xsd[i].set_scatter_matrix(scatter, temperature) - - multiplicity = get_data(xsdata_elem, 'multiplicity') - if multiplicity is not None: - multiplicity = np.array(multiplicity.split(), dtype=float) - multiplicity.shape = xsd[i].xs_shapes["[G][G']"] - xsd[i].set_multiplicity_matrix(multiplicity, temperature) - - fission = get_data(xsdata_elem, 'fission') - if fission is not None: - fission = np.array(fission.split(), dtype=float) - fission.shape = xsd[i].xs_shapes['[G]'] - xsd[i].set_fission(fission, temperature) - - kappa_fission = get_data(xsdata_elem, 'kappa_fission') - if kappa_fission is not None: - kappa_fission = np.array(kappa_fission.split(), dtype=float) - kappa_fission.shape = xsd[i].xs_shapes['[G]'] - xsd[i].set_kappa_fission(kappa_fission, temperature) - - chi = get_data(xsdata_elem, 'chi') - if chi is not None: - chi = np.array(chi.split(), dtype=float) - chi.shape = xsd[i].xs_shapes['[G]'] - xsd[i].set_chi(chi, temperature) - else: - chi = None - - nu_fission = get_data(xsdata_elem, 'nu_fission') - if nu_fission is not None: - nu_fission = np.array(nu_fission.split(), dtype=float) - if chi is not None: - nu_fission.shape = xsd[i].xs_shapes['[G]'] - else: - nu_fission.shape = xsd[i].xs_shapes["[G][G']"] - xsd[i].set_nu_fission(nu_fission, temperature) - - # Build library as we go, but first we have enough to initialize it - lib = openmc.MGXSLibrary(energy_groups) - lib.add_xsdatas(xsd) - lib.export_to_hdf5(args['output']) - - -if __name__ == '__main__': - main() diff --git a/scripts/openmc-voxel-to-vtk b/scripts/openmc-voxel-to-vtk deleted file mode 100755 index 3f59ffd42..000000000 --- a/scripts/openmc-voxel-to-vtk +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python3 - -from argparse import ArgumentParser - -import openmc - - -if __name__ == "__main__": - # Process command line arguments - parser = ArgumentParser('Converts a voxel HDF5 file to a VTK file') - parser.add_argument("voxel_file", help="Path to voxel h5 file") - parser.add_argument( - "-o", - "--output", - action="store", - default="plot", - help="Path to output VTK file.", - ) - args = parser.parse_args() - print("Reading and translating data...") - openmc.voxel_to_vtk(args.voxel_file, args.output) - print(f"Written VTK file {args.output}...") diff --git a/tests/regression_tests/track_output/test.py b/tests/regression_tests/track_output/test.py index 62526117d..299e1aa34 100644 --- a/tests/regression_tests/track_output/test.py +++ b/tests/regression_tests/track_output/test.py @@ -1,7 +1,6 @@ import glob import os from pathlib import Path -from subprocess import call import numpy as np import openmc @@ -26,8 +25,8 @@ class TrackTestHarness(TestHarness): # For MPI mode, combine track files if config['mpi']: - call(['../../../scripts/openmc-track-combine', '-o', 'tracks.h5'] + - glob.glob('tracks_p*.h5')) + track_files = list(glob.glob('tracks_p*.h5')) + openmc.Tracks.combine(track_files, 'tracks.h5') # Get string of track file information outstr = '' diff --git a/tests/testing_harness.py b/tests/testing_harness.py index ddae89e02..6de475e6e 100644 --- a/tests/testing_harness.py +++ b/tests/testing_harness.py @@ -1,5 +1,4 @@ from difflib import unified_diff -from subprocess import check_call import filecmp import glob import h5py @@ -487,8 +486,7 @@ class PlotTestHarness(TestHarness): # Check that voxel h5 can be converted to vtk for voxel_h5_filename in self._voxel_convert_checks: - check_call(['../../../scripts/openmc-voxel-to-vtk'] + - glob.glob(voxel_h5_filename)) + openmc.voxel_to_vtk(voxel_h5_filename) def _test_output_created(self): """Make sure *.png has been created."""