From e62ba9b85af208d15cb959cd0c7d83b3724f15d4 Mon Sep 17 00:00:00 2001 From: agnelson Date: Tue, 28 Sep 2021 08:44:31 -0500 Subject: [PATCH] Upgrading tests of model, and adding better file management, Model.calculate_volumes method and Model.plot_geometry method. These last two still need to be tested as well as Model.deplete --- openmc/executor.py | 15 ++ openmc/lib/core.py | 2 +- openmc/model/model.py | 282 ++++++++++++++++++++++++++------- tests/unit_tests/conftest.py | 7 +- tests/unit_tests/test_model.py | 177 ++++++++++++++++++--- 5 files changed, 396 insertions(+), 87 deletions(-) diff --git a/openmc/executor.py b/openmc/executor.py index e8f0de2c7e..de5604c9da 100644 --- a/openmc/executor.py +++ b/openmc/executor.py @@ -1,10 +1,25 @@ from collections.abc import Iterable from numbers import Integral import subprocess +from contextlib import contextmanager +from pathlib import Path +import os import openmc +@contextmanager +def change_directory(working_dir): + """A context manager for executing in a provided working directory""" + start_dir = Path().absolute() + try: + Path.mkdir(working_dir, exist_ok=True) + os.chdir(working_dir) + yield + finally: + os.chdir(start_dir) + + def _run(args, output, cwd): # Launch a subprocess p = subprocess.Popen(args, cwd=cwd, stdout=subprocess.PIPE, diff --git a/openmc/lib/core.py b/openmc/lib/core.py index e9601c3320..03c6c41fcb 100644 --- a/openmc/lib/core.py +++ b/openmc/lib/core.py @@ -215,7 +215,7 @@ def import_properties(filename): See Also -------- openmc.lib.export_properties - +mat """ _dll.openmc_properties_import(filename.encode()) diff --git a/openmc/model/model.py b/openmc/model/model.py index 84174eb5ed..e32876a6c2 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -4,6 +4,7 @@ from pathlib import Path from numbers import Integral import time import warnings +import subprocess import h5py import numpy as np @@ -450,8 +451,8 @@ class Model: mats_group = fh['materials'] n_cells = mats_group.attrs['n_materials'] if n_cells != len(materials): - raise ValueError("Number of materials in properties file doesn't " - "match current model.") + raise ValueError("Number of materials in properties file " + "doesn't match current model.") # Update material densities for name, group in mats_group.items(): @@ -518,65 +519,208 @@ class Model: tstart = time.time() last_statepoint = None - if self.C_init: - # Handle the openmc.run kwargs - # First dont allow ones that must be set via init - for arg_name, arg, default in zip( - ['threads', 'geometry_debug', 'restart_file', 'tracks', 'cwd'], - [threads, geometry_debug, restart_file, tracks, cwd], - [None, False, None, False, '.']): - if arg != default: - msg = "{} must be set via Model.c_init(...)".format( - arg_name) + # Operate in the provided working directory + with openmc.change_directory(Path(cwd)): + if self.C_init: + # Handle the run options as applicable + # First dont allow ones that must be set via init + for arg_name, arg, default in zip( + ['threads', 'geometry_debug', 'restart_file', 'tracks'], + [threads, geometry_debug, restart_file, tracks], + [None, False, None, False]): + if arg != default: + msg = "{} must be set via Model.c_init(...)".format( + arg_name) + raise ValueError(msg) + + if particles is not None: + init_particles = openmc.lib.settings.particles + if isinstance(particles, Integral) and particles > 0: + openmc.lib.settings.particles = particles + + # If we dont want output, make the verbosity quiet + if not output: + init_verbosity = openmc.lib.settings.verbosity + if not output: + openmc.lib.settings.verbosity = 1 + + # Event-based can be set at init-time or on a case-basis. + # Handle the argument here. + # TODO This will be dealt with in a future change to the C-API + + # Then run using the C-API + openmc.lib.run() + + # Reset changes for the openmc.run kwargs handling + if particles is not None: + openmc.lib.settings.particles = init_particles + if not output: + # Then re-set the initial verbosity + openmc.lib.settings.verbosity = init_verbosity + + else: + # Then run via the command line + self.export_to_xml() + openmc.run(particles, threads, geometry_debug, restart_file, + tracks, output, Path('.'), openmc_exec, mpi_args, + event_based) + + # Get output directory and return the last statepoint written + if self.settings.output and 'path' in self.settings.output: + output_dir = Path(self.settings.output['path']) + else: + output_dir = Path.cwd() + for sp in output_dir.glob('statepoint.*.h5'): + mtime = sp.stat().st_mtime + if mtime >= tstart: # >= allows for poor clock resolution + tstart = mtime + last_statepoint = sp + return last_statepoint + + def calculate_volume(self, threads=None, output=True, cwd='.', + openmc_exec='openmc', mpi_args=None, + apply_volumes=True): + """Runs an OpenMC stochastic volume calculation and, if requested, + applies volumes to the model + + Parameters + ---------- + threads : int, optional + Number of OpenMP threads. If OpenMC is compiled with OpenMP + threading enabled, the default is implementation-dependent but is + usually equal to the number of hardware threads available (or a + value set by the :envvar:`OMP_NUM_THREADS` environment variable). + This currenty only applies to the case when not using the C-API. + output : bool, optional + Capture OpenMC output from standard out + openmc_exec : str, optional + Path to OpenMC executable. Defaults to 'openmc'. + This only applies to the case when not using the C-API. + mpi_args : list of str, optional + MPI execute command and any additional MPI arguments to pass, + e.g. ['mpiexec', '-n', '8']. + This only applies to the case when not using the C-API. + cwd : str, optional + Path to working directory to run in. Defaults to the current + working directory. + apply_volumes : bool, optional + Whether apply the volume calculation results from this calculation + to the model. Defaults to applying the volumes. + """ + + if len(self.settings.volume_calculation) == 0: + # Then there is no volume calculation specified + raise ValueError("The Settings.volume_calculation attribute must" + " be specified before executing this method!") + + with openmc.change_directory(Path(cwd)): + if self.C_init: + if threads is not None: + msg = "Threads must be set via Model.c_init(...)" + raise ValueError(msg) + if mpi_args is not None: + msg = "The MPI environment must be set otherwise such as" \ + "with the call to mpi4py" raise ValueError(msg) - if particles is not None: - init_particles = openmc.lib.settings.particles - if isinstance(particles, Integral) and particles > 0: - openmc.lib.settings.particles = particles - - # Now lets handle the ones we can handle - if not output: - init_verbosity = openmc.lib.settings.verbosity + # Apply the output settings if not output: - openmc.lib.settings.verbosity = 1 + init_verbosity = openmc.lib.settings.verbosity + if not output: + openmc.lib.settings.verbosity = 1 - # Event-based can be set at init-time or on a case-basis. Handle - # the case-basis here. - # TODO This will be dealt with in a future change to the C-API + # Compute the volumes + openmc.lib.calculate_volumes() - # Then run using the C-API - openmc.lib.run() + # Reset the output verbosity + if not output: + openmc.lib.settings.verbosity = init_verbosity + else: + openmc.calculate_volumes(threads=threads, output=output, + openmc_exec=openmc_exec, + mpi_args=mpi_args) - # Reset changes for the openmc.run kwargs handling - if particles is not None: - openmc.lib.settings.particles = init_particles - if output is not None: - openmc.lib.settings.verbosity = init_verbosity + # Now we apply the volumes + if apply_volumes: + # Load the results + f_names = \ + ["volume_{}.h5".format(i + 1) + for i in range(len(self.settings.volume_calculations))] + vol_calcs = [openmc.VolumeCalculation.load_results(f_name) + for f_name in f_names] + # And now we can add them to the model + for vol_calc in vol_calcs: + # First add them to the Python side + self.geometry.add_volume_information(vol_calc) - else: - # Then run via the command line - self.export_to_xml() - openmc.run(particles, threads, geometry_debug, restart_file, - tracks, output, cwd, openmc_exec, mpi_args, event_based) + # And now repeat for the C-API + if vol_calc.domain == 'material': + # Then we can do this in the C-API + for domain_id in vol_calc.domains: + self.update_material_volumes( + [domain_id], vol_calc.volumes[domain_id]) - # Get output directory and return the last statepoint written this run - if self.settings.output and 'path' in self.settings.output: - output_dir = Path(self.settings.output['path']) - else: - output_dir = Path.cwd() - for sp in output_dir.glob('statepoint.*.h5'): - mtime = sp.stat().st_mtime - if mtime >= tstart: # >= allows for poor clock resolution - tstart = mtime - last_statepoint = sp - return last_statepoint + def plot_geometry(self, output=True, cwd='.', openmc_exec='openmc', + convert=True, convert_exec='convert'): + """Creates plot images as specified by the Model.plots attribute + + If convert is True, this function requires that a program is installed + to convert PPM files to PNG files. Typically, that would be + `ImageMagick `_ which includes a + `convert` command. + + Parameters + ---------- + output : bool, optional + Capture OpenMC output from standard out + cwd : str, optional + Path to working directory to run in. Defaults to the current + working directory. + openmc_exec : str, optional + Path to OpenMC executable. Defaults to 'openmc'. + This only applies to the case when not using the C-API. + convert : bool, optional + Whether or not to attempt to convert from PPM to PNG + convert_exec : str, optional + Command that can convert PPM files into PNG files + """ + + if len(self.plots) == 0: + # Then there is no volume calculation specified + raise ValueError("The Model.plots attribute must be specified " + "before executing this method!") + + with openmc.change_directory(Path(cwd)): + if self.C_init: + # Apply the output settings + if not output: + init_verbosity = openmc.lib.settings.verbosity + if not output: + openmc.lib.settings.verbosity = 1 + + # Compute the volumes + openmc.lib.plot_geometry() + + # Reset the output verbosity + if not output: + openmc.lib.settings.verbosity = init_verbosity + else: + openmc.plot_geometry(output=output, openmc_exec=openmc_exec) + + if convert: + for p in self.plots: + if p.filename is not None: + ppm_file = f'{p.filename}.ppm' + else: + ppm_file = f'plot_{p.id}.ppm' + png_file = ppm_file.replace('.ppm', '.png') + subprocess.check_call([convert_exec, ppm_file, png_file]) def _change_py_C_attribs(self, names_or_ids, value, obj_type, attrib_name, density_units='atom/b-cm'): # Method to do the same work whether it is a cell or material and # a temperature or volume - check_type('names_or_ids', names_or_ids, Iterable, (np.int, int, str)) + check_type('names_or_ids', names_or_ids, Iterable, (Integral, str)) check_type('obj_type', obj_type, str) obj_type = obj_type.lower() check_value('obj_type', obj_type, ('material', 'cell')) @@ -588,7 +732,11 @@ class Model: # The C-API has no way to set cell volume so lets raise an exception if obj_type == 'cell' and attrib_name == 'volume': raise NotImplementedError( - 'Setting a Cell volume is not yet supported!') + 'Setting a Cell volume is not supported!') + # Same with setting temperatures, TODO: update C-API for this + if obj_type == 'material' and attrib_name == 'temperature': + raise NotImplementedError( + 'Setting a Material temperature is not yet supported!') # And some items just dont make sense if obj_type == 'cell' and attrib_name == 'density': raise ValueError('Cannot set a Cell density!') @@ -610,12 +758,13 @@ class Model: # only values that have actual ids ids = [None] * len(names_or_ids) for i, name_or_id in enumerate(names_or_ids): - if isinstance(name_or_id, (int, np.int)): + if isinstance(name_or_id, Integral): if name_or_id in by_id: ids[i] = int(name_or_id) - msg = '{} ID {} is not present in the model!'.format( - obj_type.capitalize(), name_or_id) - raise InvalidIDError(msg) + else: + msg = '{} ID {} is not present in the model!'.format( + obj_type.capitalize(), name_or_id) + raise InvalidIDError(msg) elif isinstance(name_or_id, str): if name_or_id in by_name: ids[i] = by_name[name_or_id].id @@ -636,7 +785,7 @@ class Model: elif attrib_name == 'temperature': obj.temperature = value elif attrib_name == 'density': - obj.set_density(value, density_units) + obj.set_density(density_units, value) # Next lets keep what is in C-API memory up to date as well if self.C_init: C_obj = C_by_id[id_] @@ -645,9 +794,9 @@ class Model: elif attrib_name == 'translation': C_obj.translation = value elif attrib_name == 'volume': - C_obj.set_volume = value + C_obj.volume = value elif attrib_name == 'temperature': - C_obj.set_temperature = value + C_obj.set_temperature(value) elif attrib_name == 'density': C_obj.set_density(value, density_units) @@ -685,7 +834,7 @@ class Model: self._change_py_C_attribs(names_or_ids, vector, 'cell', 'translation') - def update_densities(self, names_or_ids, density, density_units): + def update_densities(self, names_or_ids, density, density_units='atom/b-cm'): """Update the density of a given set of materials to a new value Parameters @@ -695,8 +844,8 @@ class Model: This parameter can include a mix of names and ids. density : float The density to apply in the units specified by `density_units` - density_units : {'atom/b-cm', 'g/cm3'} - Units for `density` + density_units : {'atom/b-cm', 'g/cm3'}, optional + Units for `density`. Defaults to 'atom/b-cm' """ @@ -734,3 +883,18 @@ class Model: self._change_py_C_attribs(names_or_ids, temperature, 'material', 'temperature') + + def update_material_volumes(self, names_or_ids, volume): + """Update the volume of a set of materials to the given value + + Parameters + ---------- + names_or_ids : Iterable of str or int + The material names (if str) or id (if int) that are to be updated. + This parameter can include a mix of names and ids. + volume : float + The volume to apply in units of cm^3 + + """ + + self._change_py_C_attribs(names_or_ids, volume, 'material', 'volume') diff --git a/tests/unit_tests/conftest.py b/tests/unit_tests/conftest.py index a1c4a24dab..d6f94e82e1 100644 --- a/tests/unit_tests/conftest.py +++ b/tests/unit_tests/conftest.py @@ -28,10 +28,13 @@ def pin_model_attributes(): pitch = 1.25984 fuel_or = openmc.ZCylinder(r=0.39218, name='Fuel OR') clad_or = openmc.ZCylinder(r=0.45720, name='Clad OR') - box = openmc.model.rectangular_prism(pitch, pitch, boundary_type='reflective') + box = openmc.model.rectangular_prism(pitch, pitch, + boundary_type='reflective') # Define cells - fuel = openmc.Cell(name='fuel', fill=uo2, region=-fuel_or) + fuel_inf_cell = openmc.Cell(name='inf fuel', fill=uo2) + fuel_inf_univ = openmc.Universe(cells=[fuel_inf_cell]) + fuel = openmc.Cell(name='fuel', fill=fuel_inf_univ, region=-fuel_or) clad = openmc.Cell(fill=zirc, region=+fuel_or & -clad_or) water = openmc.Cell(fill=borated_water, region=+clad_or & box) diff --git a/tests/unit_tests/test_model.py b/tests/unit_tests/test_model.py index a068ef7816..0ee8568e37 100644 --- a/tests/unit_tests/test_model.py +++ b/tests/unit_tests/test_model.py @@ -1,4 +1,4 @@ -from typing import Type +import numpy as np import pytest from pathlib import Path import openmc @@ -6,7 +6,7 @@ import openmc.lib def test_init(run_in_tmpdir, pin_model_attributes): - mats, geom, settings, tals, plots, chain, fission_q, _ = \ + mats, geom, settings, tals, plots, chain, fission_q, _ = \ pin_model_attributes openmc.reset_auto_ids() @@ -44,13 +44,16 @@ def test_init(run_in_tmpdir, pin_model_attributes): assert test_model._materials_by_id == {1: mats[0], 2: mats[1], 3: mats[2]} assert test_model._materials_by_name == {'UO2': mats[0], 'Zirc': mats[1], 'Borated water': mats[2]} - assert test_model._cells_by_id == {1: geom.root_universe.cells[1], - 2: geom.root_universe.cells[2], - 3: geom.root_universe.cells[3]} + # The last cell is the one that contains the infinite fuel + assert test_model._cells_by_id == \ + {2: geom.root_universe.cells[2], 3: geom.root_universe.cells[3], + 4: geom.root_universe.cells[4], + 1: geom.root_universe.cells[2].fill.cells[1]} # No cell name for 2 and 3, so we expect a blank name to be assigned to # cell 3 due to overwriting assert test_model._cells_by_name == { - 'fuel': geom.root_universe.cells[1], '': geom.root_universe.cells[3]} + 'fuel': geom.root_universe.cells[2], '': geom.root_universe.cells[4], + 'inf fuel': geom.root_universe.cells[2].fill.cells[1]} assert test_model.C_init is False # Finally test the parameter type checking by passing bad types and @@ -79,13 +82,16 @@ def test_from_xml(run_in_tmpdir, pin_model_attributes): test_model = openmc.Model.from_xml() assert test_model.geometry.root_universe.cells.keys() == \ geom.root_universe.cells.keys() - assert [c.fill.name for c in test_model.geometry.root_universe.cells.values()] == \ + assert [c.fill.name for c in + test_model.geometry.root_universe.cells.values()] == \ [c.fill.name for c in geom.root_universe.cells.values()] - assert [mat.name for mat in test_model.materials] == [mat.name for mat in mats] + assert [mat.name for mat in test_model.materials] == \ + [mat.name for mat in mats] # We will assume the attributes of settings that are custom objects are # OK if the others are so we dotn need to implement explicit comparisons no_test = ['_source', '_entropy_mesh'] - assert sorted(k for k in test_model.settings.__dict__.keys() if k not in no_test) == \ + assert sorted(k for k in test_model.settings.__dict__.keys() + if k not in no_test) == \ sorted(k for k in settings.__dict__.keys() if k not in no_test) keys = sorted(k for k in settings.__dict__.keys() if k not in no_test) for ref_k in keys: @@ -100,15 +106,17 @@ def test_from_xml(run_in_tmpdir, pin_model_attributes): assert test_model._materials_by_name == { 'UO2': test_model.materials[0], 'Zirc': test_model.materials[1], 'Borated water': test_model.materials[2]} - assert test_model._cells_by_id == {\ - 1: test_model.geometry.root_universe.cells[1], + assert test_model._cells_by_id == { 2: test_model.geometry.root_universe.cells[2], - 3: test_model.geometry.root_universe.cells[3]} + 3: test_model.geometry.root_universe.cells[3], + 4: test_model.geometry.root_universe.cells[4], + 1: test_model.geometry.root_universe.cells[2].fill.cells[1]} # No cell name for 2 and 3, so we expect a blank name to be assigned to # cell 3 due to overwriting assert test_model._cells_by_name == { - 'fuel': test_model.geometry.root_universe.cells[1], - '': test_model.geometry.root_universe.cells[3]} + 'fuel': test_model.geometry.root_universe.cells[2], + '': test_model.geometry.root_universe.cells[4], + 'inf fuel': test_model.geometry.root_universe.cells[2].fill.cells[1]} assert test_model.C_init is False @@ -163,13 +171,26 @@ def test_import_properties(run_in_tmpdir, mpi_intracomm): cell.set_temperature(600.0) cell.fill.set_density(5.0, 'g/cm3') openmc.lib.export_properties() + + # Import properties to existing model + model.import_properties("properties.h5") + + # Check to see that values are assigned to the C and python representations + # First python + cell = model.geometry.get_all_cells()[1] + assert cell.temperature == [600.0] + assert cell.fill.get_mass_density() == pytest.approx(5.0) + # Now C + assert openmc.lib.cells[1].get_temperature() == 600. + assert openmc.lib.materials[1].get_density('g/cm3') == pytest.approx(5.0) + + # Clear the C-API openmc.lib.finalize() - # Import properties to existing model and re-export to new directory - model.import_properties("properties.h5") + # Verify the attributes survived by exporting to XML and re-creating model.export_to_xml("with_properties") - # Load model with properties and confirm temperature/density has been changed + # Load model with properties and confirm temperature/density changed model_with_properties = openmc.Model.from_xml( 'with_properties/geometry.xml', 'with_properties/materials.xml', @@ -183,10 +204,6 @@ def test_import_properties(run_in_tmpdir, mpi_intracomm): def test_run(run_in_tmpdir, pin_model_attributes, mpi_intracomm): mats, geom, settings, tals, plots, _, _, _ = pin_model_attributes test_model = openmc.Model(geom, mats, settings, tals, plots) - # Set the depletion module to use the test intracomm as the depletion mods - # intracomm is the one that init uses. We will not perturb system state - # outside of this test by re-setting the openmc.deplete.comm to what it was - # before when we exist # This case will run by getting the k-eff and tallies for command-line and # C-API execution modes and ensuring they give the same result. @@ -196,6 +213,10 @@ def test_run(run_in_tmpdir, pin_model_attributes, mpi_intracomm): cli_flux = sp.get_tally(id=1).get_values()[0, 0, 0] if mpi_intracomm is not None: + # Set the depletion module to use the test intracomm as the depletion + # module's intracomm is the one that init uses. We will not perturb + # system state outside of this test by re-setting the + # openmc.deplete.comm to what it was before when we exist orig_comm = openmc.deplete.comm openmc.deplete.comm = mpi_intracomm test_model.init_C_api() @@ -205,8 +226,8 @@ def test_run(run_in_tmpdir, pin_model_attributes, mpi_intracomm): C_flux = sp.get_tally(id=1).get_values()[0, 0, 0] # and lets compare results - assert (C_keff - cli_keff) < 1e-15 - assert (C_flux - cli_flux) < 1e-13 + assert abs(C_keff - cli_keff) < 1e-15 + assert abs(C_flux - cli_flux) < 1e-13 # Now we should make sure that the flags for items which should be handled # by init are properly set @@ -218,8 +239,6 @@ def test_run(run_in_tmpdir, pin_model_attributes, mpi_intracomm): test_model.run(restart_file='1.h5') with pytest.raises(ValueError): test_model.run(tracks=True) - with pytest.raises(ValueError): - test_model.run(cwd='hi') test_model.clear_C_api() @@ -228,3 +247,111 @@ def test_run(run_in_tmpdir, pin_model_attributes, mpi_intracomm): openmc.deplete.comm = orig_comm +def test_py_C_attributes(run_in_tmpdir, pin_model_attributes, mpi_intracomm): + mats, geom, settings, tals, plots, _, _, _ = pin_model_attributes + test_model = openmc.Model(geom, mats, settings, tals, plots) + + if mpi_intracomm is not None: + # Set the depletion module to use the test intracomm as the depletion + # module's intracomm is the one that init uses. We will not perturb + # system state outside of this test by re-setting the + # openmc.deplete.comm to what it was before when we exist + orig_comm = openmc.deplete.comm + openmc.deplete.comm = mpi_intracomm + test_model.init_C_api() + + # Now we can call rotate_cells, translate_cells, update_densities, + # update_cell_temperatures, and update_material_temperatures and make sure + # the changes have taken hold. + # For each we will first try bad inputs to make sure we get the right + # errors and then we do a good one which calls the material by name and + # then id to make sure it worked + + # The rotate_cells and translate_cells will work on the cell named fill, as + # it is filled with a universe and thus the operation will be valid + + # First rotate_cells + with pytest.raises(TypeError): + # Make sure it tells us we have a bad names_or_ids type + test_model.rotate_cells(None, (0, 0, 90)) + with pytest.raises(TypeError): + test_model.rotate_cells([None], (0, 0, 90)) + with pytest.raises(openmc.exceptions.InvalidIDError): + # Make sure it tells us we had a bad id + test_model.rotate_cells([7200], (0, 0, 90)) + with pytest.raises(openmc.exceptions.InvalidIDError): + # Make sure it tells us we had a bad id + test_model.rotate_cells(['bad_name'], (0, 0, 90)) + # Now a good one + assert np.all(openmc.lib.cells[2].rotation == (0., 0., 0.)) + test_model.rotate_cells([2], (0, 0, 90)) + assert np.all(openmc.lib.cells[2].rotation == (0., 0., 90.)) + + # And same thing by name + test_model.rotate_cells(['fuel'], (0, 0, 180)) + + # Now translate_cells. We dont need to re-check the TypeErrors/bad ids, + # because the other functions use the same hidden method as rotate_cells + assert np.all(openmc.lib.cells[2].translation == (0., 0., 0.)) + test_model.translate_cells([2], (0, 0, 10)) + assert np.all(openmc.lib.cells[2].translation == (0., 0., 10.)) + + # Now lets do the density updates. + # Check initial conditions + assert abs(openmc.lib.materials[1].get_density( + 'atom/b-cm') - 0.06891296988603757) < 1e-13 + mat_a_dens = np.sum( + [v[1] for v in test_model.materials[0]. + get_nuclide_atom_densities().values()]) + assert abs(mat_a_dens - 0.06891296988603757) < 1e-8 + # Change the density + test_model.update_densities(['UO2'], 2.) + assert abs(openmc.lib.materials[1].get_density('atom/b-cm') - 2.) < 1e-13 + mat_a_dens = np.sum( + [v[1] for v in test_model.materials[0]. + get_nuclide_atom_densities().values()]) + assert abs(mat_a_dens - 2.) < 1e-8 + + # Now lets do the cell temperature updates. + # Check initial conditions + + assert test_model._cells_by_id == \ + {2: geom.root_universe.cells[2], 3: geom.root_universe.cells[3], + 4: geom.root_universe.cells[4], + 1: geom.root_universe.cells[2].fill.cells[1]} + assert abs(openmc.lib.cells[3].get_temperature() - 293.6) < 1e-13 + assert test_model.geometry.root_universe.cells[3].temperature is None + # Change the temperature + test_model.update_cell_temperatures([3], 600.) + assert abs(openmc.lib.cells[3].get_temperature() - 600.) < 1e-13 + assert abs(test_model.geometry.root_universe.cells[3].temperature - + 600.) < 1e-13 + + # Now lets do the material temperature updates. + # Check initial conditions + with pytest.raises(NotImplementedError): + test_model.update_material_temperatures(['UO2'], 600.) + # TODO: When C-API material.set_temperature is implemented, uncomment below + # assert abs(openmc.lib.materials[1].temperature - 293.6) < 1e-13 + # # The temperature on the material will be None because its just the + # # default assert test_model.materials[0].temperature is None + # # Change the temperature + # test_model.update_material_temperatures(['UO2'], 600.) + # assert abs(openmc.lib.materials[1].temperature - 600.) < 1e-13 + # assert abs(test_model.materials[0].temperature - 600.) < 1e-13 + + # And finally material volume + # import pdb; pdb.set_trace() + assert abs(openmc.lib.materials[1].volume - 0.4831931368640985) < 1e-13 + # The temperature on the material will be None because its just the default + assert abs(test_model.materials[0].volume - 0.4831931368640985) < 1e-13 + # Change the temperature + test_model.update_material_volumes(['UO2'], 2.) + assert abs(openmc.lib.materials[1].volume - 2.) < 1e-13 + assert abs(test_model.materials[0].volume - 2.) < 1e-13 + + test_model.clear_C_api() + + # And before done, reset the deplete communicator + if mpi_intracomm is not None: + openmc.deplete.comm = orig_comm