diff --git a/openmc/lib/core.py b/openmc/lib/core.py index 6f08f76b2a..3b691721c6 100644 --- a/openmc/lib/core.py +++ b/openmc/lib/core.py @@ -114,11 +114,8 @@ def global_bounding_box(): def calculate_volumes(output=True): """Run stochastic volume calculation""" - if output: + with quiet_dll(output): _dll.openmc_calculate_volumes() - else: - with quiet_dll(): - _dll.openmc_calculate_volumes() def current_batch(): @@ -152,11 +149,9 @@ def export_properties(filename=None, output=True): """ if filename is not None: filename = c_char_p(filename.encode()) - if output: + + with quiet_dll(output): _dll.openmc_properties_export(filename) - else: - with quiet_dll(): - _dll.openmc_properties_export(filename) def finalize(): @@ -272,11 +267,8 @@ def init(args=None, intracomm=None, output=True): address = MPI._addressof(intracomm) intracomm = c_void_p(address) - if output: + with quiet_dll(output): _dll.openmc_init(argc, argv, intracomm) - else: - with quiet_dll(): - _dll.openmc_init(argc, argv, intracomm) openmc.lib.is_initialized = True @@ -378,11 +370,8 @@ def plot_geometry(output=True): Whether or not to show output. Defaults to showing output """ - if output: + with quiet_dll(output): _dll.openmc_plot_geometry() - else: - with quiet_dll(): - _dll.openmc_plot_geometry() def reset(): @@ -406,11 +395,8 @@ def run(output=True): Whether or not to show output. Defaults to showing output """ - if output: + with quiet_dll(output): _dll.openmc_run() - else: - with quiet_dll(): - _dll.openmc_run() def simulation_init(): @@ -529,27 +515,40 @@ class _FortranObjectWithID(_FortranObject): @contextmanager -def quiet_dll(): - """This context manager allows us to suppress standard output from DLLs""" - sys.stdout.flush() - # Save the initial file descriptor states - initial_stdout = sys.stdout - initial_stdout_fno = os.dup(sys.stdout.fileno()) - # Get a garbage descriptor so we can throw away output - devnull = os.open(os.devnull, os.O_WRONLY) +def quiet_dll(output=True): + """This context manager allows us to suppress standard output from DLLs - # Get the current stdout stream and make a duplicate of it - new_stdout = os.dup(1) - # Copy the garbage output to the stdout stream - os.dup2(devnull, 1) - os.close(devnull) - # Now point stdout to the re-defined stdout - sys.stdout = os.fdopen(new_stdout, 'w') + Parameters + ---------- + output : bool + Denotes whether the output should be displayed (True) or not (False) - try: + .. versionadded:: 0.13.0 + + """ + + if output: yield - finally: - # Now we just clean up after ourselves and reset the streams - sys.stdout = initial_stdout + else: sys.stdout.flush() - os.dup2(initial_stdout_fno, 1) + # Save the initial file descriptor states + initial_stdout = sys.stdout + initial_stdout_fno = os.dup(sys.stdout.fileno()) + # Get a garbage descriptor so we can throw away output + devnull = os.open(os.devnull, os.O_WRONLY) + + # Get the current stdout stream and make a duplicate of it + new_stdout = os.dup(1) + # Copy the garbage output to the stdout stream + os.dup2(devnull, 1) + os.close(devnull) + # Now point stdout to the re-defined stdout + sys.stdout = os.fdopen(new_stdout, 'w') + + try: + yield + finally: + # Now we just clean up after ourselves and reset the streams + sys.stdout = initial_stdout + sys.stdout.flush() + os.dup2(initial_stdout_fno, 1) diff --git a/openmc/model/model.py b/openmc/model/model.py index c65470e3e3..b35f50c87f 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -52,8 +52,6 @@ class Model: Tallies information plots : openmc.Plots, optional Plot information - intracomm : mpi4py.MPI.Intracomm or None, optional - MPI intracommunicator Attributes ---------- @@ -67,8 +65,10 @@ class Model: Tallies information plots : openmc.Plots Plot information - intracomm : mpi4py.MPI.Intracomm or None - MPI intracommunicator + intracomm : mpi4py.MPI.Intracomm or openmc.DummyCommunicator + MPI intracommunicator; this defaults to the mpi4py world communicator + from if present, or a DummyCommunicator otherwise. If an alternative + communicator is desired, this parameter should be modified accordingly. """ @@ -91,7 +91,7 @@ class Model: if plots is not None: self.plots = plots - self.intracomm = intracomm + self.intracomm = openmc.comm # Store dictionaries to the materials and cells by ID and names if materials is None: @@ -191,12 +191,8 @@ class Model: @intracomm.setter def intracomm(self, intracomm): - if intracomm is None: - self._intracomm = openmc.comm - else: - check_type('intracomm', intracomm, - (openmc.MPI.Comm, openmc.DummyCommunicator)) - self._intracomm = intracomm + check_type('intracomm', intracomm, type(openmc.comm)) + self._intracomm = intracomm @classmethod def from_xml(cls, geometry='geometry.xml', materials='materials.xml', @@ -272,7 +268,7 @@ class Model: if isinstance(self.intracomm, openmc.DummyCommunicator): # openmc.lib.init does not accept DummyCommunicator, and importing - # the DummyCommunicator class is overkill. Filter it here + # the DummyCommunicator class there is overkill. Filter it here intracomm = None else: intracomm = self.intracomm @@ -288,7 +284,8 @@ class Model: openmc.lib.finalize() def deplete(self, timesteps, method='cecm', final_step=True, - operator_kwargs=None, directory='.', **integrator_kwargs): + operator_kwargs=None, directory='.', output=True, + **integrator_kwargs): """Deplete model using specified timesteps/power .. versionchanged:: 0.13.0 @@ -310,6 +307,8 @@ class Model: directory : str, optional Directory to write XML files to. If it doesn't exist already, it will be created. Defaults to the current working directory + output : bool + Capture OpenMC output from standard out integrator_kwargs : dict Remaining keyword arguments passed to the depletion Integrator initializer (e.g., :func:`openmc.deplete.integrator.cecm`). @@ -332,8 +331,9 @@ class Model: started_initialized = self.is_initialized with _change_directory(Path(directory)): - depletion_operator = \ - dep.Operator(self.geometry, self.settings, **op_kwargs) + with openmc.lib.quiet_dll(output): + depletion_operator = \ + dep.Operator(self.geometry, self.settings, **op_kwargs) # Tell depletion_operator.finalize NOT to clear C API memory when # it is done @@ -347,8 +347,8 @@ class Model: **integrator_kwargs) # Now perform the depletion - # TODO: add output parameter to integrate - integrator.integrate(final_step) + with openmc.lib.quiet_dll(output): + integrator.integrate(final_step) # Now make the python Materials match the C API material data for mat_id, mat in self._materials_by_id.items(): diff --git a/tests/unit_tests/test_model.py b/tests/unit_tests/test_model.py index 07a356097b..2760eadb46 100644 --- a/tests/unit_tests/test_model.py +++ b/tests/unit_tests/test_model.py @@ -481,7 +481,7 @@ def test_deplete(run_in_tmpdir, pin_model_attributes, mpi_intracomm): # and make sure we get the same answer test_model.deplete([1e6], 'predictor', final_step=False, operator_kwargs=op_kwargs, - power=1.) + power=1., output=False) # Get the new Xe136 and U235 atom densities after_xe = mats[0].get_nuclide_atom_densities()['Xe136'][1] after_u = mats[0].get_nuclide_atom_densities()['U235'][1] @@ -501,7 +501,7 @@ def test_deplete(run_in_tmpdir, pin_model_attributes, mpi_intracomm): test_model.init_lib(output=False) test_model.deplete([1e6], 'predictor', final_step=False, operator_kwargs=op_kwargs, - power=1.) + power=1., output=False) # Get the new Xe136 and U235 atom densities after_lib_xe = mats[0].get_nuclide_atom_densities()['Xe136'][1] after_lib_u = mats[0].get_nuclide_atom_densities()['U235'][1]