diff --git a/docs/source/usersguide/random_ray.rst b/docs/source/usersguide/random_ray.rst index c793cf22ff..5a81495dc8 100644 --- a/docs/source/usersguide/random_ray.rst +++ b/docs/source/usersguide/random_ray.rst @@ -700,23 +700,13 @@ existing MGXS library file, or ``False`` to skip generation and use an existing library file. The continuous energy simulations used to generate the cross section library -can be customized via the ``settings`` parameter. To do so, start from the -default settings returned by :meth:`openmc.Model.mgxs_generation_settings`, -modify them as desired, and pass the result back. For example, the number of -particles per batch (2,000 by default) can be increased to improve the fidelity -of the generated cross section library as:: +can be customized via the ``settings`` parameter. Only the attributes that are +populated on the passed :class:`openmc.Settings` object override the +generation defaults, so a sparse object adjusts just the fields you set. For +example, the number of particles per batch (2,000 by default) can be increased +to improve the fidelity of the generated cross section library as:: - settings = model.mgxs_generation_settings() - settings.particles = 100_000 - model.convert_to_multigroup(settings=settings) - -The settings returned by :meth:`openmc.Model.mgxs_generation_settings` record -the method they were generated for, so a non-default method only needs to be -given once:: - - settings = model.mgxs_generation_settings("stochastic_slab") - settings.particles = 100_000 - model.convert_to_multigroup(settings=settings) + model.convert_to_multigroup(settings=openmc.Settings(particles=100_000)) .. note:: MGXS transport correction (via setting the ``correction`` parameter in the @@ -807,8 +797,7 @@ on the generation settings:: # Then, bootstrap a higher fidelity material-wise library, applying those # weight windows during the continuous energy solve so that particles can # reach materials far from the source. - settings = model.mgxs_generation_settings() - settings.weight_windows_file = "weight_windows.h5" + settings = openmc.Settings(weight_windows_file="weight_windows.h5") model.convert_to_multigroup(settings=settings, overwrite_mgxs_library=True) A weight windows file on the generation settings is only used with the diff --git a/openmc/model/model.py b/openmc/model/model.py index 8728e3aa9c..ac33c246e1 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -2604,66 +2604,9 @@ class Model: mgxs_file.add_xsdata(mgxs_set) mgxs_file.export_to_hdf5(mgxs_path) - def mgxs_generation_settings( - self, method: str = "material_wise" - ) -> openmc.Settings: - """Default settings for the simulations used to generate a MGXS library. - - Returns the :class:`openmc.Settings` object that - :meth:`Model.convert_to_multigroup` would use by default for the given - generation method: a copy of the model's own settings for the - ``"material_wise"`` method, or a fresh Settings object for the - surrogate-geometry methods (``"stochastic_slab"`` and - ``"infinite_medium"``, which also inherit the model's temperature - settings), with the generation defaults for batches, inactive - batches, particles, and output applied. To customize MGXS - generation, modify the returned object and pass it back via the - ``settings`` argument:: - - settings = model.mgxs_generation_settings() - settings.particles = 100_000 - model.convert_to_multigroup(settings=settings) - - The returned object records the method it was generated for, which - :meth:`Model.convert_to_multigroup` uses as its default ``method``, - so a non-default method only needs to be given here. - - .. versionadded:: 0.15.4 - - Parameters - ---------- - method : {"material_wise", "stochastic_slab", "infinite_medium"}, optional - MGXS generation method the settings are intended for. - - Returns - ------- - openmc.Settings - Default settings for the MGXS generation run(s). - """ - check_value('method', method, - ('material_wise', 'stochastic_slab', 'infinite_medium')) - if method == 'material_wise': - settings = copy.deepcopy(self.settings) - else: - settings = openmc.Settings() - settings.temperature = copy.deepcopy(self.settings.temperature) - # The surrogate-geometry methods always run in fixed source mode - # with fission treated as capture (nu-fission is still tallied) - settings.run_mode = 'fixed source' - settings.create_fission_neutrons = False - - settings.batches = 100 if method == 'infinite_medium' else 200 - if method != 'infinite_medium': - settings.inactive = 100 - settings.particles = 2000 - settings.output = {'summary': True, 'tallies': False} - # Record the method so convert_to_multigroup can default to it - settings._mgxs_generation_method = method - return settings - def convert_to_multigroup( self, - method: str | None = None, + method: str = "material_wise", groups: str | Sequence[float] | openmc.mgxs.EnergyGroups = "CASMO-2", nparticles: int | None = None, overwrite_mgxs_library: bool = False, @@ -2682,11 +2625,7 @@ class Model: Parameters ---------- method : {"material_wise", "stochastic_slab", "infinite_medium"}, optional - Method to generate the MGXS. If not given, defaults to the - method that the ``settings`` object was generated for by - :meth:`Model.mgxs_generation_settings`, or ``"material_wise"`` - otherwise. Giving a method here that conflicts with the one the - settings were generated for is an error. + Method to generate the MGXS. groups : openmc.mgxs.EnergyGroups, str, or sequence of float, optional Energy group structure for the MGXS. Can be an :class:`openmc.mgxs.EnergyGroups` object, a string name of a @@ -2739,15 +2678,27 @@ class Model: Set ``temperature`` on the object passed via the ``settings`` argument instead. settings : openmc.Settings, optional - Settings used verbatim for the continuous energy simulation(s) - that generate the MGXS library. If not provided, defaults are - taken from :meth:`Model.mgxs_generation_settings`; to customize - a run, start from those defaults, modify them, and pass the - result here. Note that the ``"stochastic_slab"`` and - ``"infinite_medium"`` methods construct their own fixed source - and set ``run_mode``, ``source``, and - ``create_fission_neutrons`` accordingly. If the settings include - a ``weight_windows_file`` (e.g., ``"weight_windows.h5"``), the + Settings for customizing the continuous energy simulation(s) + used to generate the MGXS library. Only attributes that are + populated override the generation defaults, so a sparse object + may be used to adjust just a few fields, e.g. + ``settings=openmc.Settings(particles=100_000)`` only increases + the particle count. The settings of the generation run are + resolved in three layers, with later layers taking precedence: + (1) the model's own settings for the ``"material_wise"`` method, + or a fresh :class:`openmc.Settings` object for the + surrogate-geometry methods (which also inherit the model's + temperature settings); (2) the generation defaults (200 batches + with 100 inactive for ``"material_wise"`` and + ``"stochastic_slab"``, 100 batches for ``"infinite_medium"``, + 2000 particles, and summary-only output); (3) all populated + attributes of this object (see :meth:`openmc.Settings.update`). + The run mode cannot be set here: ``"material_wise"`` always + takes it from the model, while the ``"stochastic_slab"`` and + ``"infinite_medium"`` methods always run in fixed source mode + with ``create_fission_neutrons`` disabled and construct their + own sources. If the resolved settings include a + ``weight_windows_file`` (e.g., ``"weight_windows.h5"``), the ``"material_wise"`` method loads and applies those weight windows during the continuous energy generation simulation (``weight_windows_on`` is enabled automatically). Applying @@ -2758,35 +2709,20 @@ class Model: first generate weight windows with the ``"stochastic_slab"`` method and the random ray solver, then "bootstrap" a higher-fidelity ``"material_wise"`` library by setting those - weight windows here; a warning is issued and the file is ignored - for the ``"stochastic_slab"`` and ``"infinite_medium"`` methods. - Cannot be combined with the deprecated ``nparticles`` or - ``temperature_settings`` arguments. + weight windows here; a warning is issued and the file is + ignored for the ``"stochastic_slab"`` and ``"infinite_medium"`` + methods. Cannot be combined with the deprecated ``nparticles`` + or ``temperature_settings`` arguments. .. versionadded:: 0.15.4 """ if not isinstance(groups, openmc.mgxs.EnergyGroups): groups = openmc.mgxs.EnergyGroups(groups) - # Resolve the generation method: an explicit argument wins, otherwise - # the method the provided settings were generated for (recorded by - # mgxs_generation_settings) is used, defaulting to "material_wise". - # Conflicting specifications are rejected, since the generation - # defaults differ by method. - if settings is not None: - check_type('settings', settings, openmc.Settings) - settings_method = (settings._mgxs_generation_method - if settings is not None else None) - if method is None: - method = settings_method or 'material_wise' - elif settings_method is not None and method != settings_method: - raise ValueError( - f'The "{method}" generation method conflicts with the ' - 'provided settings, which were generated for the ' - f'"{settings_method}" method by ' - 'Model.mgxs_generation_settings().') check_value('method', method, ('material_wise', 'stochastic_slab', 'infinite_medium')) + if settings is not None: + check_type('settings', settings, openmc.Settings) # The model may reference its materials only through the geometry. # The materials are converted in place and library-wide attributes @@ -2798,46 +2734,61 @@ class Model: self.geometry.get_all_materials().values(), key=lambda mat: mat.id)) - # Resolve the settings for the MGXS generation run(s): the provided - # settings are used verbatim, otherwise the generation defaults are - # used (with the deprecated arguments applied, if given). if nparticles is not None or temperature_settings is not None: warnings.warn( 'The "nparticles" and "temperature_settings" arguments are ' - 'deprecated. Customize MGXS generation by modifying the ' - 'Settings object returned by Model.mgxs_generation_settings()' - ' and passing it via the "settings" argument.', FutureWarning) + 'deprecated. Pass a Settings object with the desired ' + 'attributes via the "settings" argument instead.', + FutureWarning) if settings is not None: raise ValueError( 'The deprecated "nparticles" and "temperature_settings" ' 'arguments cannot be combined with the "settings" ' 'argument.') - if settings is None: - settings = self.mgxs_generation_settings(method) - if nparticles is not None: - settings.particles = nparticles - if temperature_settings is not None: - settings.temperature = temperature_settings + # Resolve the settings for the MGXS generation run(s) in three + # layers, with later layers taking precedence: the model's own + # settings ("material_wise") or a fresh Settings object (surrogate + # methods), then the generation defaults, then any attributes the + # user populated on the provided settings. + user_settings = settings + if method == 'material_wise': + settings = copy.deepcopy(self.settings) else: - # batches and particles are required for any OpenMC transport - # run; catch their absence here so a hand-built settings object - # fails with a pointer to the defaults rather than a mysterious - # error from inside the generation run - if settings.batches is None or settings.particles is None: - raise ValueError( - 'The provided settings are missing required attributes ' - '(batches, particles). Start from the defaults returned ' - 'by Model.mgxs_generation_settings() and modify them ' - 'rather than building a Settings object from scratch.') - settings = copy.deepcopy(settings) + settings = openmc.Settings() + settings.temperature = copy.deepcopy(self.settings.temperature) + # The surrogate-geometry methods treat fission as capture + # (nu-fission is still tallied) + settings.create_fission_neutrons = False + settings.batches = 100 if method == 'infinite_medium' else 200 + if method != 'infinite_medium': + settings.inactive = 100 + settings.particles = 2000 + settings.output = {'summary': True, 'tallies': False} + if nparticles is not None: + settings.particles = nparticles + if temperature_settings is not None: + settings.temperature = temperature_settings + + if user_settings is not None: # The surrogate-geometry methods construct their own sources - if method != "material_wise" and len(settings.source) > 0: + if method != "material_wise" and len(user_settings.source) > 0: warnings.warn( 'The sources defined in "settings" are ignored by the ' f'"{method}" MGXS generation method, which constructs ' 'its own sources.') + settings.update(user_settings) + + # The run mode is the one attribute that cannot be detected as + # user-populated (a fresh Settings object defaults it to + # 'eigenvalue'), so it is owned by the generation method: + # "material_wise" always takes it from the model, while the + # surrogate-geometry methods always run in fixed source mode. + if method == 'material_wise': + settings.run_mode = self.settings.run_mode + else: + settings.run_mode = 'fixed source' # A weight windows file on the generation settings is loaded and # applied during the "material_wise" method's continuous energy diff --git a/openmc/settings.py b/openmc/settings.py index 1ecd483c12..81ade60630 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -1,4 +1,5 @@ from collections.abc import Iterable, Mapping, MutableSequence, Sequence +import copy from enum import Enum import itertools from math import ceil @@ -501,11 +502,6 @@ class Settings: self._random_ray = {} - # MGXS generation method recorded by Model.mgxs_generation_settings() - # and read back by Model.convert_to_multigroup; provenance only, not - # written to XML - self._mgxs_generation_method = None - for key, value in kwargs.items(): setattr(self, key, value) @@ -519,6 +515,51 @@ class Settings: warnings.warn(msg, stacklevel=2) super().__setattr__(name, value) + def update(self, other: 'Settings'): + """Update this object with all populated attributes of another instance. + + Every attribute of `other` that has been populated -- i.e., that is + not None and not an empty collection -- is copied to this object, + overwriting any existing value. Attributes that were never set on + `other` leave the corresponding attribute of this object untouched. + This allows a sparsely-populated Settings object to be applied as a + set of overrides on top of fully-populated defaults. + + Note that :attr:`run_mode` always carries a value, so an explicitly + assigned 'eigenvalue' run mode cannot be distinguished from the + default; the run mode is therefore only copied from `other` when it + differs from 'eigenvalue'. + + .. versionadded:: 0.15.4 + + Parameters + ---------- + other : openmc.Settings + Settings object whose populated attributes are applied to this + object. + + """ + cv.check_type('other', other, Settings) + for name, value in vars(other).items(): + # run_mode defaults to 'eigenvalue' rather than to an "unset" + # state, so only a non-default value is detectable (see note in + # the docstring). + if name == '_run_mode': + if value is not RunMode.EIGENVALUE: + self._run_mode = value + continue + + # None or an empty collection means the attribute was never set + # on `other` -- the same convention to_xml_element() relies on + # to decide which elements to export. + if value is None or (hasattr(value, '__len__') and len(value) == 0): + continue + + # Values on `other` were already validated by its property + # setters; deepcopy so later mutations of `other` cannot leak + # into this object. + setattr(self, name, copy.deepcopy(value)) + @property def run_mode(self) -> str: return self._run_mode.value diff --git a/tests/regression_tests/random_ray_auto_convert/test.py b/tests/regression_tests/random_ray_auto_convert/test.py index a1570c64be..c9df2d6c72 100644 --- a/tests/regression_tests/random_ray_auto_convert/test.py +++ b/tests/regression_tests/random_ray_auto_convert/test.py @@ -26,10 +26,9 @@ def test_random_ray_auto_convert(method): model = pwr_pin_cell() # Convert to a multi-group model - mgxs_settings = model.mgxs_generation_settings(method) - mgxs_settings.particles = 100 model.convert_to_multigroup( - method=method, groups='CASMO-2', settings=mgxs_settings, + method=method, groups='CASMO-2', + settings=openmc.Settings(particles=100), overwrite_mgxs_library=False, mgxs_path="mgxs.h5" ) diff --git a/tests/regression_tests/random_ray_auto_convert_bootstrap/test.py b/tests/regression_tests/random_ray_auto_convert_bootstrap/test.py index 6e43d86592..89b734def3 100644 --- a/tests/regression_tests/random_ray_auto_convert_bootstrap/test.py +++ b/tests/regression_tests/random_ray_auto_convert_bootstrap/test.py @@ -72,18 +72,16 @@ def test_random_ray_auto_convert_bootstrap(): # Generate weight windows covering the whole problem with the # stochastic_slab method and a random ray FW-CADIS solve slab_model = copy.deepcopy(model) - slab_settings = slab_model.mgxs_generation_settings('stochastic_slab') - slab_settings.particles = 50 slab_model.convert_to_multigroup( - groups=GROUPS, settings=slab_settings, + method='stochastic_slab', groups=GROUPS, + settings=openmc.Settings(particles=50), overwrite_mgxs_library=True, mgxs_path='mgxs.h5') generate_weight_windows(slab_model, mesh) # Bootstrap the material-wise MGXS generation with those weight windows, # then regenerate the weight windows from the higher-fidelity library boot_model = copy.deepcopy(model) - boot_settings = boot_model.mgxs_generation_settings() - boot_settings.particles = 1 + boot_settings = openmc.Settings(particles=1) boot_settings.weight_windows_file = Path('weight_windows.h5').resolve() boot_model.convert_to_multigroup( groups=GROUPS, settings=boot_settings, diff --git a/tests/regression_tests/random_ray_auto_convert_kappa_fission/test.py b/tests/regression_tests/random_ray_auto_convert_kappa_fission/test.py index 3f2468532e..b3b8e84c95 100644 --- a/tests/regression_tests/random_ray_auto_convert_kappa_fission/test.py +++ b/tests/regression_tests/random_ray_auto_convert_kappa_fission/test.py @@ -26,10 +26,9 @@ def test_random_ray_auto_convert(method): model = pwr_pin_cell() # Convert to a multi-group model - mgxs_settings = model.mgxs_generation_settings(method) - mgxs_settings.particles = 100 model.convert_to_multigroup( - method=method, groups='CASMO-2', settings=mgxs_settings, + method=method, groups='CASMO-2', + settings=openmc.Settings(particles=100), overwrite_mgxs_library=False, mgxs_path="mgxs.h5" ) diff --git a/tests/regression_tests/random_ray_auto_convert_source_energy/test.py b/tests/regression_tests/random_ray_auto_convert_source_energy/test.py index bfea6ae9f9..8dd95e73ee 100644 --- a/tests/regression_tests/random_ray_auto_convert_source_energy/test.py +++ b/tests/regression_tests/random_ray_auto_convert_source_energy/test.py @@ -37,10 +37,9 @@ def test_random_ray_auto_convert_source_energy(method, source_type): source_energy = openmc.stats.delta_function(1.0e4) # Convert to a multi-group model - mgxs_settings = model.mgxs_generation_settings(method) - mgxs_settings.particles = 100 model.convert_to_multigroup( - method=method, groups='CASMO-8', settings=mgxs_settings, + method=method, groups='CASMO-8', + settings=openmc.Settings(particles=100), overwrite_mgxs_library=False, mgxs_path="mgxs.h5", source_energy=source_energy ) diff --git a/tests/regression_tests/random_ray_auto_convert_temperature/test.py b/tests/regression_tests/random_ray_auto_convert_temperature/test.py index 2cd7133e5c..e6047613be 100644 --- a/tests/regression_tests/random_ray_auto_convert_temperature/test.py +++ b/tests/regression_tests/random_ray_auto_convert_temperature/test.py @@ -33,11 +33,9 @@ def test_random_ray_auto_convert(method): } # Convert to a multi-group model - mgxs_settings = model.mgxs_generation_settings(method) - mgxs_settings.particles = 100 - mgxs_settings.temperature = temp_settings model.convert_to_multigroup( - method=method, groups='CASMO-2', settings=mgxs_settings, + method=method, groups='CASMO-2', + settings=openmc.Settings(particles=100, temperature=temp_settings), overwrite_mgxs_library=False, mgxs_path="mgxs.h5", temperatures=[294.0, 394.0] ) diff --git a/tests/regression_tests/random_ray_diagonal_stabilization/test.py b/tests/regression_tests/random_ray_diagonal_stabilization/test.py index 08d8a50847..76d2c70bd3 100644 --- a/tests/regression_tests/random_ray_diagonal_stabilization/test.py +++ b/tests/regression_tests/random_ray_diagonal_stabilization/test.py @@ -1,5 +1,6 @@ import os +import openmc from openmc.examples import pwr_pin_cell from openmc import RegularMesh @@ -22,10 +23,9 @@ def test_random_ray_diagonal_stabilization(): # and transport correction enabled. This will generate # MGXS data with some negatives on the diagonal, in order # to trigger diagonal correction. - mgxs_settings = model.mgxs_generation_settings('material_wise') - mgxs_settings.particles = 13 model.convert_to_multigroup( - method='material_wise', groups='CASMO-70', settings=mgxs_settings, + method='material_wise', groups='CASMO-70', + settings=openmc.Settings(particles=13), overwrite_mgxs_library=True, mgxs_path="mgxs.h5", correction='P0' ) diff --git a/tests/unit_tests/dagmc/test_convert_to_multigroup.py b/tests/unit_tests/dagmc/test_convert_to_multigroup.py index 4d6243c7cc..d66ebae395 100644 --- a/tests/unit_tests/dagmc/test_convert_to_multigroup.py +++ b/tests/unit_tests/dagmc/test_convert_to_multigroup.py @@ -42,12 +42,10 @@ def test_convert_to_multigroup_without_particles_batches(run_in_tmpdir): # This should work without requiring particles/batches to be set # convert_to_multigroup handles initialization internally using non-transport mode - mgxs_settings = model.mgxs_generation_settings('material_wise') - mgxs_settings.particles = 10 model.convert_to_multigroup( method='material_wise', groups='CASMO-2', - settings=mgxs_settings, + settings=openmc.Settings(particles=10), overwrite_mgxs_library=True ) diff --git a/tests/unit_tests/test_model.py b/tests/unit_tests/test_model.py index abc6a29ffd..53a5b25251 100644 --- a/tests/unit_tests/test_model.py +++ b/tests/unit_tests/test_model.py @@ -1104,72 +1104,44 @@ def _capture_generation_settings(monkeypatch, model, **kwargs): return captured['settings'] -def test_mgxs_generation_settings(): - model = _steel_water_model() - model.settings.run_mode = 'fixed source' - model.settings.photon_transport = True - model.settings.particles = 50 # tuned for the final multigroup solve - model.settings.temperature = {'method': 'interpolation'} - - # material_wise: the model's own settings plus the generation defaults - s = model.mgxs_generation_settings('material_wise') - assert s.batches == 200 - assert s.inactive == 100 - assert s.particles == 2000 - assert s.output == {'summary': True, 'tallies': False} - assert s.run_mode == 'fixed source' - assert s.photon_transport is True - assert s.temperature == {'method': 'interpolation'} - - # The returned object is a copy: modifying it leaves the model untouched - s.particles = 100_000 - assert model.settings.particles == 50 - - # Surrogate methods: fresh settings that only inherit the temperature - for method, batches in (('stochastic_slab', 200), ('infinite_medium', 100)): - s = model.mgxs_generation_settings(method) - assert s.batches == batches - assert s.particles == 2000 - assert s.run_mode == 'fixed source' - assert s.create_fission_neutrons is False - assert s.temperature == {'method': 'interpolation'} - assert s.photon_transport is None - - with pytest.raises(ValueError): - model.mgxs_generation_settings('not_a_method') - - def test_convert_to_multigroup_settings_material_wise(run_in_tmpdir, monkeypatch): model = _steel_water_model() model.settings.run_mode = 'fixed source' model.settings.source = openmc.IndependentSource(space=openmc.stats.Point()) model.settings.photon_transport = True + model.settings.batches = 1200 # tuned for the final multigroup solve - user = model.mgxs_generation_settings('material_wise') - user.particles = 12345 - user.seed = 7 + user = openmc.Settings(particles=12345, seed=7) gen = _capture_generation_settings( monkeypatch, model, method='material_wise', settings=user) - # The provided settings are used verbatim... + # User-populated attributes override the generation defaults... assert gen.particles == 12345 assert gen.seed == 7 + # ...the generation defaults override the model's own settings... assert gen.batches == 200 assert gen.inactive == 100 assert gen.output == {'summary': True, 'tallies': False} - # ...including the model settings baked in by mgxs_generation_settings() + # ...and everything else is inherited from the model assert gen.run_mode == 'fixed source' assert gen.photon_transport is True assert len(gen.source) == 1 + # The caller's object is never mutated + assert user.batches is None + + # The run mode is owned by the generation method: material_wise always + # takes it from the model, even when set on the provided settings + model.settings.run_mode = 'eigenvalue' + gen = _capture_generation_settings( + monkeypatch, model, method='material_wise', + settings=openmc.Settings(run_mode='fixed source')) + assert gen.run_mode == 'eigenvalue' def test_convert_to_multigroup_settings_stochastic_slab(run_in_tmpdir, monkeypatch): model = _steel_water_model() - user = model.mgxs_generation_settings('stochastic_slab') - user.particles = 999 - user.batches = 50 - user.max_history_splits = 42 + user = openmc.Settings(particles=999, batches=50, max_history_splits=42) user.source = openmc.IndependentSource(space=openmc.stats.Point()) with pytest.warns(UserWarning, match='constructs its own'): @@ -1193,8 +1165,7 @@ def test_convert_to_multigroup_settings_weight_windows(run_in_tmpdir, monkeypatc model = _steel_water_model() ww_path = Path('ww.h5').resolve() - user = model.mgxs_generation_settings('material_wise') - user.weight_windows_file = ww_path + user = openmc.Settings(weight_windows_file=ww_path) gen = _capture_generation_settings( monkeypatch, model, method='material_wise', settings=user) @@ -1207,8 +1178,6 @@ def test_convert_to_multigroup_settings_weight_windows(run_in_tmpdir, monkeypatc assert user.weight_windows_on is None # The surrogate-geometry methods ignore the file with a warning - user = model.mgxs_generation_settings('stochastic_slab') - user.weight_windows_file = ww_path with pytest.warns(UserWarning, match='material_wise'): gen = _capture_generation_settings( monkeypatch, model, method='stochastic_slab', settings=user) @@ -1216,46 +1185,25 @@ def test_convert_to_multigroup_settings_weight_windows(run_in_tmpdir, monkeypatc assert user.weight_windows_file == ww_path -def test_convert_to_multigroup_settings_method_recorded(run_in_tmpdir, - monkeypatch): - model = _steel_water_model() - - # Settings record the method they were generated for, so a non-default - # method only needs to be given to mgxs_generation_settings() - user = model.mgxs_generation_settings('stochastic_slab') - gen = _capture_generation_settings(monkeypatch, model, settings=user) - assert gen.run_mode == 'fixed source' - assert gen.create_fission_neutrons is False - # The stochastic slab generation constructs its own source, proving the - # slab method was dispatched - assert len(gen.source) > 0 - - # An explicit method that conflicts with the settings is rejected - with pytest.raises(ValueError, match='stochastic_slab'): - model.convert_to_multigroup(method='material_wise', settings=user) - - def test_convert_to_multigroup_settings_validation(run_in_tmpdir): model = _steel_water_model() with pytest.raises(TypeError): model.convert_to_multigroup(settings={'particles': 100}) - # A hand-built settings object missing attributes required for any - # transport run is rejected with a pointer to the defaults - with pytest.raises(ValueError, match='mgxs_generation_settings'): - model.convert_to_multigroup(settings=openmc.Settings(particles=5000)) + with pytest.raises(ValueError): + model.convert_to_multigroup(method='not_a_method') # The deprecated arguments cannot be combined with settings - settings = model.mgxs_generation_settings() with pytest.raises(ValueError, match='deprecated'), \ pytest.warns(FutureWarning): - model.convert_to_multigroup(nparticles=1000, settings=settings) + model.convert_to_multigroup(nparticles=1000, + settings=openmc.Settings()) with pytest.raises(ValueError, match='deprecated'), \ pytest.warns(FutureWarning): model.convert_to_multigroup( temperature_settings={'method': 'interpolation'}, - settings=settings) + settings=openmc.Settings()) def test_convert_to_multigroup_deprecated_args(run_in_tmpdir, monkeypatch): diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index bdb3ea8fe9..1bbde93c35 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -192,6 +192,35 @@ def test_export_to_xml(run_in_tmpdir): assert s.free_gas_threshold == 800.0 +def test_update(): + base = openmc.Settings(particles=100, batches=10, + output={'tallies': False}) + other = openmc.Settings(particles=500) + other.source = openmc.IndependentSource(space=openmc.stats.Point()) + + base.update(other) + + # Populated attributes of `other` overwrite those of `base`... + assert base.particles == 500 + assert len(base.source) == 1 + # ...while unset attributes leave `base` untouched + assert base.batches == 10 + assert base.output == {'tallies': False} + # Copied values are independent of `other` + assert base.source[0] is not other.source[0] + + # run_mode always carries a value, so only a non-default ('eigenvalue') + # run mode is detectable and copied + base.run_mode = 'fixed source' + base.update(openmc.Settings()) + assert base.run_mode == 'fixed source' + base.update(openmc.Settings(run_mode='volume')) + assert base.run_mode == 'volume' + + with pytest.raises(TypeError): + base.update({'particles': 5}) + + def test_properties_file_load(tmp_path, mpi_intracomm): model = openmc.examples.pwr_assembly() diff --git a/tests/unit_tests/weightwindows/test_ww_gen.py b/tests/unit_tests/weightwindows/test_ww_gen.py index fbb316860b..8209bb8505 100644 --- a/tests/unit_tests/weightwindows/test_ww_gen.py +++ b/tests/unit_tests/weightwindows/test_ww_gen.py @@ -363,12 +363,10 @@ def test_ww_generation_with_dagmc(run_in_tmpdir): rr_model = copy.deepcopy(model) rr_model.settings.inactive = 3 - mgxs_settings = rr_model.mgxs_generation_settings("stochastic_slab") - mgxs_settings.particles = 10 rr_model.convert_to_multigroup( method="stochastic_slab", overwrite_mgxs_library=True, - settings=mgxs_settings, + settings=openmc.Settings(particles=10), groups="CASMO-2" )