diff --git a/include/openmc/plot.h b/include/openmc/plot.h index fc78c250bf..225242c702 100644 --- a/include/openmc/plot.h +++ b/include/openmc/plot.h @@ -295,6 +295,7 @@ private: * to mean not having matching intersection lengths, but rather having * a matching sequence of surface/cell/material intersections. */ + struct TrackSegment; bool trackstack_equivalent(const vector& track1, const vector& track2) const; diff --git a/openmc/plots.py b/openmc/plots.py index 508fb6c237..2e6e9bcb92 100644 --- a/openmc/plots.py +++ b/openmc/plots.py @@ -194,7 +194,7 @@ class PlotBase(IDManagerMixin): name : str Name of the plot pixels : Iterable of int - Number of pixels to use in each basis direction + Number of pixels to use in each direction filename : Path to write the plot to color_by : {'cell', 'material'} @@ -358,6 +358,12 @@ class PlotBase(IDManagerMixin): cv.check_greater_than('RGB component', rgb, 0, True) cv.check_less_than('RGB component', rgb, 256) + # Helper function that returns the domain ID given either a + # Cell/Material object or the domain ID itself + @staticmethod + def _get_id(domain): + return domain if isinstance(domain, Integral) else domain.id + def colorize(self, geometry, seed=1): """Generate a color scheme for each domain in the plot. @@ -401,7 +407,6 @@ class PlotBase(IDManagerMixin): """ element = ET.Element("plot") - element.set("id", str(self._id)) if self._filename is not None: element.set("filename", self._filename) element.set("color_by", self._color_by) @@ -419,7 +424,7 @@ class PlotBase(IDManagerMixin): if self._mask_components is not None: subelement = ET.SubElement(element, "mask") subelement.set("components", ' '.join( - str(get_id(d)) for d in self._mask_components)) + str(PlotBase._get_id(d)) for d in self._mask_components)) color = self._mask_background if color is not None: if isinstance(color, str): @@ -505,7 +510,7 @@ class Plot(PlotBase): @type.setter def type(self, plottype): - cv.check_value('plot type', plottype, ['slice', 'voxel']) + cv.check_value('plot type', plottype, ['slice', 'voxel', 'projection']) self._type = plottype @basis.setter @@ -674,6 +679,7 @@ class Plot(PlotBase): """ element = super().to_xml_element() + element.set("id", str(self._id)) element.set("type", self._type) if self._type == 'slice': @@ -685,16 +691,11 @@ class Plot(PlotBase): subelement = ET.SubElement(element, "width") subelement.text = ' '.join(map(str, self._width)) - # Helper function that returns the domain ID given either a - # Cell/Material object or the domain ID itself - def get_id(domain): - return domain if isinstance(domain, Integral) else domain.id - if self._colors: for domain, color in sorted(self._colors.items(), - key=lambda x: get_id(x[0])): + key=lambda x: PlotBase._get_id(x[0])): subelement = ET.SubElement(element, "color") - subelement.set("id", str(get_id(domain))) + subelement.set("id", str(PlotBase._get_id(domain))) if isinstance(color, str): color = _SVG_COLORS[color.lower()] subelement.set("rgb", ' '.join(str(x) for x in color)) @@ -1029,6 +1030,7 @@ class ProjectionPlot(PlotBase): """ element = super().to_xml_element() + element.set("id", str(self._id)) element.set("type", "projection") subelement = ET.SubElement(element, "camera_position") @@ -1094,9 +1096,12 @@ class ProjectionPlot(PlotBase): plot.filename = elem.get("filename") plot.color_by = elem.get("color_by") plot.type = "projection" - plot.horizontal_field_of_view = elem.get("horizontal_field_of_view") - tmp = elem.get("horizontal_field_of_view") + horizontal_fov = elem.find("horizontal_field_of_view") + if horizontal_fov is not None: + plot.horizontal_field_of_view = float(horizontal_fov.text) + + tmp = elem.find("orthographic_width") if tmp is not None: self.orthographic_width = float(tmp) @@ -1300,11 +1305,11 @@ class Plots(cv.CheckedList): # Generate each plot plots = cls() for e in elem.findall('plot'): - plot_type = elem.get('type') + plot_type = e.get('type') if plot_type == 'projection': - plots.append(ProjectionPlot.from_xml_element(elem)) + plots.append(ProjectionPlot.from_xml_element(e)) else: - plots.append(Plot.from_xml_element(elem)) + plots.append(Plot.from_xml_element(e)) return plots @classmethod diff --git a/tests/regression_tests/distribmat/inputs_true.dat b/tests/regression_tests/distribmat/inputs_true.dat index f6c43c19a5..adc9bf3fbe 100644 --- a/tests/regression_tests/distribmat/inputs_true.dat +++ b/tests/regression_tests/distribmat/inputs_true.dat @@ -48,14 +48,14 @@ + 400 400 0 0 0 7 7 - 400 400 + 400 400 0 0 0 7 7 - 400 400 diff --git a/tests/regression_tests/plot/test.py b/tests/regression_tests/plot/test.py index 2fd6a5b795..30d9ab0d08 100644 --- a/tests/regression_tests/plot/test.py +++ b/tests/regression_tests/plot/test.py @@ -1,60 +1,7 @@ -import glob -import hashlib -import os - -import h5py -import openmc - -from tests.testing_harness import TestHarness +from tests.testing_harness import PlotTestHarness from tests.regression_tests import config -class PlotTestHarness(TestHarness): - """Specialized TestHarness for running OpenMC plotting tests.""" - def __init__(self, plot_names): - super().__init__(None) - self._plot_names = plot_names - - def _run_openmc(self): - openmc.plot_geometry(openmc_exec=config['exe']) - - def _test_output_created(self): - """Make sure *.png has been created.""" - for fname in self._plot_names: - assert os.path.exists(fname), 'Plot output file does not exist.' - - def _cleanup(self): - super()._cleanup() - for fname in self._plot_names: - if os.path.exists(fname): - os.remove(fname) - - def _get_results(self): - """Return a string hash of the plot files.""" - outstr = bytes() - - for fname in self._plot_names: - if fname.endswith('.png'): - # Add PNG output to results - with open(fname, 'rb') as fh: - outstr += fh.read() - elif fname.endswith('.h5'): - # Add voxel data to results - with h5py.File(fname, 'r') as fh: - outstr += fh.attrs['filetype'] - outstr += fh.attrs['num_voxels'].tobytes() - outstr += fh.attrs['lower_left'].tobytes() - outstr += fh.attrs['voxel_width'].tobytes() - outstr += fh['data'][()].tobytes() - - # Hash the information and return. - sha512 = hashlib.sha512() - sha512.update(outstr) - outstr = sha512.hexdigest() - - return outstr - - def test_plot(): harness = PlotTestHarness(('plot_1.png', 'plot_2.png', 'plot_3.png', 'plot_4.h5')) diff --git a/tests/regression_tests/plot_overlaps/test.py b/tests/regression_tests/plot_overlaps/test.py index cf23abc27f..0828c6e255 100644 --- a/tests/regression_tests/plot_overlaps/test.py +++ b/tests/regression_tests/plot_overlaps/test.py @@ -1,60 +1,7 @@ -import glob -import hashlib -import os - -import h5py -import openmc - -from tests.testing_harness import TestHarness +from tests.testing_harness import PlotTestHarness from tests.regression_tests import config -class PlotTestHarness(TestHarness): - """Specialized TestHarness for running OpenMC plotting tests.""" - def __init__(self, plot_names): - super().__init__(None) - self._plot_names = plot_names - - def _run_openmc(self): - openmc.plot_geometry(openmc_exec=config['exe']) - - def _test_output_created(self): - """Make sure *.png has been created.""" - for fname in self._plot_names: - assert os.path.exists(fname), 'Plot output file does not exist.' - - def _cleanup(self): - super()._cleanup() - for fname in self._plot_names: - if os.path.exists(fname): - os.remove(fname) - - def _get_results(self): - """Return a string hash of the plot files.""" - outstr = bytes() - - for fname in self._plot_names: - if fname.endswith('.png'): - # Add PNG output to results - with open(fname, 'rb') as fh: - outstr += fh.read() - elif fname.endswith('.h5'): - # Add voxel data to results - with h5py.File(fname, 'r') as fh: - outstr += fh.attrs['filetype'] - outstr += fh.attrs['num_voxels'].tobytes() - outstr += fh.attrs['lower_left'].tobytes() - outstr += fh.attrs['voxel_width'].tobytes() - outstr += fh['data'][()].tobytes() - - # Hash the information and return. - sha512 = hashlib.sha512() - sha512.update(outstr) - outstr = sha512.hexdigest() - - return outstr - - def test_plot_overlap(): harness = PlotTestHarness(('plot_1.png', 'plot_2.png', 'plot_3.png', 'plot_4.h5')) diff --git a/tests/regression_tests/plot_projections/test.py b/tests/regression_tests/plot_projections/test.py index c390a06ab2..cf18503f4b 100644 --- a/tests/regression_tests/plot_projections/test.py +++ b/tests/regression_tests/plot_projections/test.py @@ -1,60 +1,6 @@ -import glob -import hashlib -import os - -import h5py -import openmc - -from tests.testing_harness import TestHarness +from tests.testing_harness import PlotTestHarness from tests.regression_tests import config - -class PlotTestHarness(TestHarness): - """Specialized TestHarness for running OpenMC plotting tests.""" - def __init__(self, plot_names): - super().__init__(None) - self._plot_names = plot_names - - def _run_openmc(self): - openmc.plot_geometry(openmc_exec=config['exe']) - - def _test_output_created(self): - """Make sure *.png has been created.""" - for fname in self._plot_names: - assert os.path.exists(fname), 'Plot output file does not exist.' - - def _cleanup(self): - super()._cleanup() - for fname in self._plot_names: - if os.path.exists(fname): - os.remove(fname) - - def _get_results(self): - """Return a string hash of the plot files.""" - outstr = bytes() - - for fname in self._plot_names: - if fname.endswith('.png'): - # Add PNG output to results - with open(fname, 'rb') as fh: - outstr += fh.read() - elif fname.endswith('.h5'): - # Add voxel data to results - with h5py.File(fname, 'r') as fh: - outstr += fh.attrs['filetype'] - outstr += fh.attrs['num_voxels'].tobytes() - outstr += fh.attrs['lower_left'].tobytes() - outstr += fh.attrs['voxel_width'].tobytes() - outstr += fh['data'][()].tobytes() - - # Hash the information and return. - sha512 = hashlib.sha512() - sha512.update(outstr) - outstr = sha512.hexdigest() - - return outstr - - def test_plot(): harness = PlotTestHarness(('plot_1.png', 'example1.png', 'example2.png', 'example3.png', 'orthographic_example1.png')) harness.main() diff --git a/tests/regression_tests/plot_voxel/test.py b/tests/regression_tests/plot_voxel/test.py index c4fa20c80f..e034decf79 100644 --- a/tests/regression_tests/plot_voxel/test.py +++ b/tests/regression_tests/plot_voxel/test.py @@ -1,62 +1,11 @@ -import glob -import hashlib -import os -from subprocess import check_call - -import h5py -import openmc import pytest -from tests.testing_harness import TestHarness +from tests.testing_harness import PlotTestHarness from tests.regression_tests import config vtk = pytest.importorskip('vtk') -class PlotVoxelTestHarness(TestHarness): - """Specialized TestHarness for running OpenMC voxel plot tests.""" - def __init__(self, plot_names): - super().__init__(None) - self._plot_names = plot_names - - def _run_openmc(self): - openmc.plot_geometry(openmc_exec=config['exe']) - - check_call(['../../../scripts/openmc-voxel-to-vtk'] + - glob.glob('plot_4.h5')) - - def _test_output_created(self): - """Make sure plots have been created.""" - for fname in self._plot_names: - assert os.path.exists(fname), 'Plot output file does not exist.' - - def _cleanup(self): - super()._cleanup() - for fname in self._plot_names: - if os.path.exists(fname): - os.remove(fname) - - def _get_results(self): - """Return a string hash of the plot files.""" - outstr = bytes() - - for fname in self._plot_names: - if fname.endswith('.h5'): - # Add voxel data to results - with h5py.File(fname, 'r') as fh: - outstr += fh.attrs['filetype'] - outstr += fh.attrs['num_voxels'].tobytes() - outstr += fh.attrs['lower_left'].tobytes() - outstr += fh.attrs['voxel_width'].tobytes() - outstr += fh['data'][()].tobytes() - - # Hash the information and return. - sha512 = hashlib.sha512() - sha512.update(outstr) - outstr = sha512.hexdigest() - - return outstr - def test_plot_voxel(): - harness = PlotVoxelTestHarness(('plot_4.h5', 'plot.vti')) + harness = PlotTestHarness(('plot_4.h5', 'plot.vti'), voxel_convert_checks=['plot_4.h5']) harness.main() diff --git a/tests/testing_harness.py b/tests/testing_harness.py index 157f6e6402..f0edf4ea44 100644 --- a/tests/testing_harness.py +++ b/tests/testing_harness.py @@ -1,6 +1,8 @@ from difflib import unified_diff +from subprocess import check_call import filecmp import glob +import h5py import hashlib import os import shutil @@ -380,3 +382,55 @@ class HashedPyAPITestHarness(PyAPITestHarness): def _get_results(self): """Digest info in the statepoint and return as a string.""" return super()._get_results(True) + + +class PlotTestHarness(TestHarness): + """Specialized TestHarness for running OpenMC plotting tests.""" + def __init__(self, plot_names, voxel_convert_checks=[]): + super().__init__(None) + self._plot_names = plot_names + self._voxel_convert_checks = voxel_convert_checks + + def _run_openmc(self): + openmc.plot_geometry(openmc_exec=config['exe']) + + # 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)) + + def _test_output_created(self): + """Make sure *.png has been created.""" + for fname in self._plot_names: + assert os.path.exists(fname), 'Plot output file does not exist.' + + def _cleanup(self): + super()._cleanup() + for fname in self._plot_names: + if os.path.exists(fname): + os.remove(fname) + + def _get_results(self): + """Return a string hash of the plot files.""" + outstr = bytes() + + for fname in self._plot_names: + if fname.endswith('.png'): + # Add PNG output to results + with open(fname, 'rb') as fh: + outstr += fh.read() + elif fname.endswith('.h5'): + # Add voxel data to results + with h5py.File(fname, 'r') as fh: + outstr += fh.attrs['filetype'] + outstr += fh.attrs['num_voxels'].tobytes() + outstr += fh.attrs['lower_left'].tobytes() + outstr += fh.attrs['voxel_width'].tobytes() + outstr += fh['data'][()].tobytes() + + # Hash the information and return. + sha512 = hashlib.sha512() + sha512.update(outstr) + outstr = sha512.hexdigest() + + return outstr