Resolve convert_to_multigroup settings by layering user overrides onto defaults

Replaces the mgxs_generation_settings getter (and its recorded-method
inference) with merge semantics: the user passes a sparse Settings object
and only its populated attributes override the generation defaults, via
the restored Settings.update() helper. The run mode is the one attribute
whose default value makes it undetectable as user-populated, so it is
owned by the generation method: material_wise always takes it from the
model and the surrogate methods always force fixed source mode.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
John Tramm 2026-07-17 20:40:03 +00:00
parent 37d15f655b
commit f50a5a70da
13 changed files with 188 additions and 241 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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"
)

View file

@ -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,

View file

@ -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"
)

View file

@ -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
)

View file

@ -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]
)

View file

@ -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'
)

View file

@ -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
)

View file

@ -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):

View file

@ -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()

View file

@ -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"
)