• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

openmc-dev / openmc / 18199783194

02 Oct 2025 04:51PM UTC coverage: 85.122% (-0.08%) from 85.199%
18199783194

Pull #3587

github

web-flow
Merge 775bbd9ec into 1dacf4fd2
Pull Request #3587: Ensure weight_windows_file information is read from XML

8 of 9 new or added lines in 1 file covered. (88.89%)

263 existing lines in 10 files now uncovered.

53128 of 62414 relevant lines covered (85.12%)

38859371.13 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.81
/openmc/settings.py
1
from collections.abc import Iterable, Mapping, MutableSequence, Sequence
11✔
2
from enum import Enum
11✔
3
import itertools
11✔
4
from math import ceil
11✔
5
from numbers import Integral, Real
11✔
6
from pathlib import Path
11✔
7

8
import lxml.etree as ET
11✔
9

10
import openmc
11✔
11
import openmc.checkvalue as cv
11✔
12
from openmc.checkvalue import PathLike
11✔
13
from openmc.stats.multivariate import MeshSpatial
11✔
14
from ._xml import clean_indentation, get_elem_list, get_text
11✔
15
from .mesh import _read_meshes, RegularMesh, MeshBase
11✔
16
from .source import SourceBase, MeshSource, IndependentSource
11✔
17
from .utility_funcs import input_path
11✔
18
from .volume import VolumeCalculation
11✔
19
from .weight_windows import WeightWindows, WeightWindowGenerator, WeightWindowsList
11✔
20

21

22
class RunMode(Enum):
11✔
23
    EIGENVALUE = 'eigenvalue'
11✔
24
    FIXED_SOURCE = 'fixed source'
11✔
25
    PLOT = 'plot'
11✔
26
    VOLUME = 'volume'
11✔
27
    PARTICLE_RESTART = 'particle restart'
11✔
28

29

30
_RES_SCAT_METHODS = {'dbrc', 'rvs'}
11✔
31

32

33
class Settings:
11✔
34
    """Settings used for an OpenMC simulation.
35

36
    Parameters
37
    ----------
38
    **kwargs : dict, optional
39
        Any keyword arguments are used to set attributes on the instance.
40

41
    Attributes
42
    ----------
43
    batches : int
44
        Number of batches to simulate
45
    confidence_intervals : bool
46
        If True, uncertainties on tally results will be reported as the
47
        half-width of the 95% two-sided confidence interval. If False,
48
        uncertainties on tally results will be reported as the sample standard
49
        deviation.
50
    create_fission_neutrons : bool
51
        Indicate whether fission neutrons should be created or not.
52
    cutoff : dict
53
        Dictionary defining weight cutoff, energy cutoff and time cutoff. The
54
        dictionary may have the following keys, 'weight', 'weight_avg',
55
        'survival_normalization', 'energy_neutron', 'energy_photon',
56
        'energy_electron', 'energy_positron', 'time_neutron', 'time_photon',
57
        'time_electron', and 'time_positron'. Value for 'weight' should be a
58
        float indicating weight cutoff below which particle undergo Russian
59
        roulette. Value for 'weight_avg' should be a float indicating weight
60
        assigned to particles that are not killed after Russian roulette. Value
61
        of energy should be a float indicating energy in eV below which particle
62
        type will be killed. Value of time should be a float in seconds.
63
        Particles will be killed exactly at the specified time. Value for
64
        'survival_normalization' is a bool indicating whether or not the weight
65
        cutoff parameters will be applied relative to the particle's starting
66
        weight or to its current weight.
67
    delayed_photon_scaling : bool
68
        Indicate whether to scale the fission photon yield by (EGP + EGD)/EGP
69
        where EGP is the energy release of prompt photons and EGD is the energy
70
        release of delayed photons.
71

72
        .. versionadded:: 0.12
73
    electron_treatment : {'led', 'ttb'}
74
        Whether to deposit all energy from electrons locally ('led') or create
75
        secondary bremsstrahlung photons ('ttb').
76
    energy_mode : {'continuous-energy', 'multi-group'}
77
        Set whether the calculation should be continuous-energy or multi-group.
78
    entropy_mesh : openmc.RegularMesh
79
        Mesh to be used to calculate Shannon entropy. If the mesh dimensions are
80
        not specified, OpenMC assigns a mesh such that 20 source sites per mesh
81
        cell are to be expected on average.
82
    event_based : bool
83
        Indicate whether to use event-based parallelism instead of the default
84
        history-based parallelism.
85

86
        .. versionadded:: 0.12
87
    generations_per_batch : int
88
        Number of generations per batch
89
    ifp_n_generation : int
90
        Number of generations to consider for the Iterated Fission Probability
91
        method.
92
    max_lost_particles : int
93
        Maximum number of lost particles
94

95
        .. versionadded:: 0.12
96
    rel_max_lost_particles : float
97
        Maximum number of lost particles, relative to the total number of
98
        particles
99

100
        .. versionadded:: 0.12
101
    inactive : int
102
        Number of inactive batches
103
    keff_trigger : dict
104
        Dictionary defining a trigger on eigenvalue. The dictionary must have
105
        two keys, 'type' and 'threshold'. Acceptable values corresponding to
106
        type are 'variance', 'std_dev', and 'rel_err'. The threshold value
107
        should be a float indicating the variance, standard deviation, or
108
        relative error used.
109
    log_grid_bins : int
110
        Number of bins for logarithmic energy grid search
111
    material_cell_offsets : bool
112
        Generate an "offset table" for material cells by default. These tables
113
        are necessary when a particular instance of a cell needs to be tallied.
114

115
        .. versionadded:: 0.12
116
    max_particles_in_flight : int
117
        Number of neutrons to run concurrently when using event-based
118
        parallelism.
119

120
        .. versionadded:: 0.12
121
    max_particle_events : int
122
        Maximum number of allowed particle events per source particle.
123

124
        .. versionadded:: 0.15.0
125
    max_order : None or int
126
        Maximum scattering order to apply globally when in multi-group mode.
127
    max_history_splits : int
128
        Maximum number of times a particle can split during a history
129

130
        .. versionadded:: 0.13
131
    max_secondaries : int
132
        Maximum secondary bank size
133

134
        .. versionadded:: 0.15.3
135
    max_tracks : int
136
        Maximum number of tracks written to a track file (per MPI process).
137

138
        .. versionadded:: 0.13.1
139
    max_write_lost_particles : int
140
        Maximum number of particle restart files (per MPI process) to write for
141
        lost particles.
142

143
        .. versionadded:: 0.14.0
144
    no_reduce : bool
145
        Indicate that all user-defined and global tallies should not be reduced
146
        across processes in a parallel calculation.
147
    output : dict
148
        Dictionary indicating what files to output. Acceptable keys are:
149

150
        :path: String indicating a directory where output files should be
151
               written
152
        :summary: Whether the 'summary.h5' file should be written (bool)
153
        :tallies: Whether the 'tallies.out' file should be written (bool)
154
    particles : int
155
        Number of particles per generation
156
    photon_transport : bool
157
        Whether to use photon transport.
158
    plot_seed : int
159
       Initial seed for randomly generated plot colors.
160
    ptables : bool
161
        Determine whether probability tables are used.
162
    random_ray : dict
163
        Options for configuring the random ray solver. Acceptable keys are:
164

165
        :distance_inactive:
166
            Indicates the total active distance in [cm] a ray should travel
167
        :distance_active:
168
            Indicates the total active distance in [cm] a ray should travel
169
        :ray_source:
170
            Starting ray distribution (must be uniform in space and angle) as
171
            specified by a :class:`openmc.SourceBase` object.
172
        :volume_estimator:
173
            Choice of volume estimator for the random ray solver. Options are
174
            'naive', 'simulation_averaged', or 'hybrid'.
175
            The default is 'hybrid'.
176
        :source_shape:
177
            Assumed shape of the source distribution within each source region.
178
            Options are 'flat' (default), 'linear', or 'linear_xy'.
179
        :volume_normalized_flux_tallies:
180
            Whether to normalize flux tallies by volume (bool). The default is
181
            'False'. When enabled, flux tallies will be reported in units of
182
            cm/cm^3. When disabled, flux tallies will be reported in units of cm
183
            (i.e., total distance traveled by neutrons in the spatial tally
184
            region).
185
        :adjoint:
186
            Whether to run the random ray solver in adjoint mode (bool). The
187
            default is 'False'.
188
        :sample_method:
189
            Sampling method for the ray starting location and direction of
190
            travel. Options are `prng` (default) or 'halton`.
191
        :source_region_meshes:
192
            List of tuples where each tuple contains a mesh and a list of
193
            domains. Each domain is an instance of openmc.Material, openmc.Cell,
194
            or openmc.Universe. The mesh will be applied to the listed domains
195
            to subdivide source regions so as to improve accuracy and/or conform
196
            with tally meshes.
197
        :diagonal_stabilization_rho:
198
            The rho factor for use with diagonal stabilization. This technique is
199
            applied when negative diagonal (in-group) elements are detected in
200
            the scattering matrix of input MGXS data, which is a common feature
201
            of transport corrected MGXS data. The default is 1.0, which ensures
202
            no negative diagonal elements are present in the iteration matrix and
203
            thus stabilizes the simulation. A value of 0.0 will disable diagonal
204
            stabilization. Values between 0.0 and 1.0 will apply a degree of
205
            stabilization, which may be desirable as stronger diagonal stabilization
206
            also tends to dampen the convergence rate of the solver, thus requiring
207
            more iterations to converge.
208

209
        .. versionadded:: 0.15.0
210
    resonance_scattering : dict
211
        Settings for resonance elastic scattering. Accepted keys are 'enable'
212
        (bool), 'method' (str), 'energy_min' (float), 'energy_max' (float), and
213
        'nuclides' (list). The 'method' can be set to 'dbrc' (Doppler broadening
214
        rejection correction) or 'rvs' (relative velocity sampling). If not
215
        specified, 'rvs' is the default method. The 'energy_min' and
216
        'energy_max' values indicate the minimum and maximum energies above and
217
        below which the resonance elastic scattering method is to be applied.
218
        The 'nuclides' list indicates what nuclides the method should be applied
219
        to. In its absence, the method will be applied to all nuclides with 0 K
220
        elastic scattering data present.
221
    run_mode : {'eigenvalue', 'fixed source', 'plot', 'volume', 'particle restart'}
222
        The type of calculation to perform (default is 'eigenvalue')
223
    seed : int
224
        Seed for the linear congruential pseudorandom number generator
225
    stride : int
226
        Number of random numbers allocated for each source particle history
227
    source : Iterable of openmc.SourceBase
228
        Distribution of source sites in space, angle, and energy
229
    source_rejection_fraction : float
230
        Minimum fraction of source sites that must be accepted when applying
231
        rejection sampling based on constraints. If not specified, the default
232
        value is 0.05.
233
    sourcepoint : dict
234
        Options for writing source points. Acceptable keys are:
235

236
        :batches: list of batches at which to write source
237
        :overwrite: bool indicating whether to overwrite
238
        :separate: bool indicating whether the source should be written as a
239
                   separate file
240
        :write: bool indicating whether or not to write the source
241
        :mcpl: bool indicating whether to write the source as an MCPL file
242
    statepoint : dict
243
        Options for writing state points. Acceptable keys are:
244

245
        :batches: list of batches at which to write statepoint files
246
    surf_source_read : dict
247
        Options for reading surface source points. Acceptable keys are:
248

249
        :path: Path to surface source file (str).
250
    surf_source_write : dict
251
        Options for writing surface source points. Acceptable keys are:
252

253
        :surface_ids: List of surface ids at which crossing particles are to be
254
                   banked (int)
255
        :max_particles: Maximum number of particles to be banked on surfaces per
256
                   process (int)
257
        :max_source_files: Maximum number of surface source files to be created (int)
258
        :mcpl: Output in the form of an MCPL-file (bool)
259
        :cell: Cell ID used to determine if particles crossing identified
260
               surfaces are to be banked. Particles coming from or going to this
261
               declared cell will be banked (int)
262
        :cellfrom: Cell ID used to determine if particles crossing identified
263
                   surfaces are to be banked. Particles coming from this
264
                   declared cell will be banked (int)
265
        :cellto: Cell ID used to determine if particles crossing identified
266
                 surfaces are to be banked. Particles going to this declared
267
                 cell will be banked (int)
268
    survival_biasing : bool
269
        Indicate whether survival biasing is to be used
270
    tabular_legendre : dict
271
        Determines if a multi-group scattering moment kernel expanded via
272
        Legendre polynomials is to be converted to a tabular distribution or
273
        not. Accepted keys are 'enable' and 'num_points'. The value for 'enable'
274
        is a bool stating whether the conversion to tabular is performed; the
275
        value for 'num_points' sets the number of points to use in the tabular
276
        distribution, should 'enable' be True.
277
    temperature : dict
278
        Defines a default temperature and method for treating intermediate
279
        temperatures at which nuclear data doesn't exist. Accepted keys are
280
        'default', 'method', 'range', 'tolerance', and 'multipole'. The value
281
        for 'default' should be a float representing the default temperature in
282
        Kelvin. The value for 'method' should be 'nearest' or 'interpolation'.
283
        If the method is 'nearest', 'tolerance' indicates a range of temperature
284
        within which cross sections may be used. If the method is
285
        'interpolation', 'tolerance' indicates the range of temperatures outside
286
        of the available cross section temperatures where cross sections will
287
        evaluate to the nearer bound. The value for 'range' should be a pair of
288
        minimum and maximum temperatures which are used to indicate that cross
289
        sections be loaded at all temperatures within the range. 'multipole' is
290
        a boolean indicating whether or not the windowed multipole method should
291
        be used to evaluate resolved resonance cross sections.
292
    trace : tuple or list
293
        Show detailed information about a single particle, indicated by three
294
        integers: the batch number, generation number, and particle number
295
    track : tuple or list
296
        Specify particles for which track files should be written. Each particle
297
        is identified by a tuple with the batch number, generation number, and
298
        particle number.
299
    trigger_active : bool
300
        Indicate whether tally triggers are used
301
    trigger_batch_interval : int
302
        Number of batches in between convergence checks
303
    trigger_max_batches : int
304
        Maximum number of batches simulated. If this is set, the number of
305
        batches specified via ``batches`` is interpreted as the minimum number
306
        of batches
307
    uniform_source_sampling : bool
308
        Whether to sampling among multiple sources uniformly, applying their
309
        strengths as weights to sampled particles.
310
    ufs_mesh : openmc.RegularMesh
311
        Mesh to be used for redistributing source sites via the uniform fission
312
        site (UFS) method.
313
    use_decay_photons : bool
314
        Produce decay photons from neutron reactions instead of prompt
315
    verbosity : int
316
        Verbosity during simulation between 1 and 10. Verbosity levels are
317
        described in :ref:`verbosity`.
318
    volume_calculations : VolumeCalculation or iterable of VolumeCalculation
319
        Stochastic volume calculation specifications
320
    weight_windows : WeightWindowsList
321
        Weight windows to use for variance reduction
322

323
        .. versionadded:: 0.13
324
    weight_window_checkpoints : dict
325
        Indicates the checkpoints for weight window split/roulettes. Valid keys
326
        include "collision" and "surface". Values must be of type bool.
327

328
        .. versionadded:: 0.14.0
329
    weight_window_generators : WeightWindowGenerator or iterable of WeightWindowGenerator
330
        Weight windows generation parameters to apply during simulation
331

332
        .. versionadded:: 0.14.0
333

334
    create_delayed_neutrons : bool
335
        Whether delayed neutrons are created in fission.
336

337
        .. versionadded:: 0.13.3
338
    weight_windows_on : bool
339
        Whether weight windows are enabled
340

341
        .. versionadded:: 0.13
342

343
    weight_windows_file: Pathlike
344
        Path to a weight window file to load during simulation initialization
345

346
        .. versionadded::0.14.0
347
    write_initial_source : bool
348
        Indicate whether to write the initial source distribution to file
349
    """
350

351
    def __init__(self, **kwargs):
11✔
352
        self._run_mode = RunMode.EIGENVALUE
11✔
353
        self._batches = None
11✔
354
        self._generations_per_batch = None
11✔
355
        self._inactive = None
11✔
356
        self._max_lost_particles = None
11✔
357
        self._rel_max_lost_particles = None
11✔
358
        self._max_write_lost_particles = None
11✔
359
        self._particles = None
11✔
360
        self._keff_trigger = None
11✔
361

362
        # Energy mode subelement
363
        self._energy_mode = None
11✔
364
        self._max_order = None
11✔
365

366
        # Source subelement
367
        self._source = cv.CheckedList(SourceBase, 'source distributions')
11✔
368
        self._source_rejection_fraction = None
11✔
369

370
        self._confidence_intervals = None
11✔
371
        self._electron_treatment = None
11✔
372
        self._photon_transport = None
11✔
373
        self._plot_seed = None
11✔
374
        self._ptables = None
11✔
375
        self._uniform_source_sampling = None
11✔
376
        self._seed = None
11✔
377
        self._stride = None
11✔
378
        self._survival_biasing = None
11✔
379

380
        # Shannon entropy mesh
381
        self._entropy_mesh = None
11✔
382

383
        # Trigger subelement
384
        self._trigger_active = None
11✔
385
        self._trigger_max_batches = None
11✔
386
        self._trigger_batch_interval = None
11✔
387

388
        self._output = None
11✔
389

390
        # Iterated Fission Probability
391
        self._ifp_n_generation = None
11✔
392

393
        # Output options
394
        self._statepoint = {}
11✔
395
        self._sourcepoint = {}
11✔
396

397
        self._surf_source_read = {}
11✔
398
        self._surf_source_write = {}
11✔
399

400
        self._no_reduce = None
11✔
401

402
        self._verbosity = None
11✔
403

404
        self._trace = None
11✔
405
        self._track = None
11✔
406

407
        self._tabular_legendre = {}
11✔
408

409
        self._temperature = {}
11✔
410

411
        # Cutoff subelement
412
        self._cutoff = None
11✔
413

414
        # Uniform fission source subelement
415
        self._ufs_mesh = None
11✔
416

417
        self._resonance_scattering = {}
11✔
418
        self._volume_calculations = cv.CheckedList(
11✔
419
            VolumeCalculation, 'volume calculations')
420

421
        self._create_fission_neutrons = None
11✔
422
        self._create_delayed_neutrons = None
11✔
423
        self._delayed_photon_scaling = None
11✔
424
        self._material_cell_offsets = None
11✔
425
        self._log_grid_bins = None
11✔
426

427
        self._event_based = None
11✔
428
        self._max_particles_in_flight = None
11✔
429
        self._max_particle_events = None
11✔
430
        self._write_initial_source = None
11✔
431
        self._weight_windows = WeightWindowsList()
11✔
432
        self._weight_window_generators = cv.CheckedList(WeightWindowGenerator, 'weight window generators')
11✔
433
        self._weight_windows_on = None
11✔
434
        self._weight_windows_file = None
11✔
435
        self._weight_window_checkpoints = {}
11✔
436
        self._max_history_splits = None
11✔
437
        self._max_tracks = None
11✔
438
        self._max_secondaries = None
11✔
439
        self._use_decay_photons = None
11✔
440

441
        self._random_ray = {}
11✔
442

443
        for key, value in kwargs.items():
11✔
444
            setattr(self, key, value)
11✔
445

446
    @property
11✔
447
    def run_mode(self) -> str:
11✔
448
        return self._run_mode.value
11✔
449

450
    @run_mode.setter
11✔
451
    def run_mode(self, run_mode: str):
11✔
452
        cv.check_value('run mode', run_mode, {x.value for x in RunMode})
11✔
453
        for mode in RunMode:
11✔
454
            if mode.value == run_mode:
11✔
455
                self._run_mode = mode
11✔
456

457
    @property
11✔
458
    def batches(self) -> int:
11✔
459
        return self._batches
11✔
460

461
    @batches.setter
11✔
462
    def batches(self, batches: int):
11✔
463
        cv.check_type('batches', batches, Integral)
11✔
464
        cv.check_greater_than('batches', batches, 0)
11✔
465
        self._batches = batches
11✔
466

467
    @property
11✔
468
    def generations_per_batch(self) -> int:
11✔
469
        return self._generations_per_batch
11✔
470

471
    @generations_per_batch.setter
11✔
472
    def generations_per_batch(self, generations_per_batch: int):
11✔
473
        cv.check_type('generations per patch', generations_per_batch, Integral)
11✔
474
        cv.check_greater_than('generations per batch', generations_per_batch, 0)
11✔
475
        self._generations_per_batch = generations_per_batch
11✔
476

477
    @property
11✔
478
    def inactive(self) -> int:
11✔
479
        return self._inactive
11✔
480

481
    @inactive.setter
11✔
482
    def inactive(self, inactive: int):
11✔
483
        cv.check_type('inactive batches', inactive, Integral)
11✔
484
        cv.check_greater_than('inactive batches', inactive, 0, True)
11✔
485
        self._inactive = inactive
11✔
486

487
    @property
11✔
488
    def max_lost_particles(self) -> int:
11✔
489
        return self._max_lost_particles
11✔
490

491
    @max_lost_particles.setter
11✔
492
    def max_lost_particles(self, max_lost_particles: int):
11✔
493
        cv.check_type('max_lost_particles', max_lost_particles, Integral)
11✔
494
        cv.check_greater_than('max_lost_particles', max_lost_particles, 0)
11✔
495
        self._max_lost_particles = max_lost_particles
11✔
496

497
    @property
11✔
498
    def rel_max_lost_particles(self) -> float:
11✔
499
        return self._rel_max_lost_particles
11✔
500

501
    @rel_max_lost_particles.setter
11✔
502
    def rel_max_lost_particles(self, rel_max_lost_particles: float):
11✔
503
        cv.check_type('rel_max_lost_particles', rel_max_lost_particles, Real)
11✔
504
        cv.check_greater_than('rel_max_lost_particles', rel_max_lost_particles, 0)
11✔
505
        cv.check_less_than('rel_max_lost_particles', rel_max_lost_particles, 1)
11✔
506
        self._rel_max_lost_particles = rel_max_lost_particles
11✔
507

508
    @property
11✔
509
    def max_write_lost_particles(self) -> int:
11✔
510
        return self._max_write_lost_particles
11✔
511

512
    @max_write_lost_particles.setter
11✔
513
    def max_write_lost_particles(self, max_write_lost_particles: int):
11✔
514
        cv.check_type('max_write_lost_particles', max_write_lost_particles, Integral)
11✔
515
        cv.check_greater_than('max_write_lost_particles', max_write_lost_particles, 0)
11✔
516
        self._max_write_lost_particles = max_write_lost_particles
11✔
517

518
    @property
11✔
519
    def particles(self) -> int:
11✔
520
        return self._particles
11✔
521

522
    @particles.setter
11✔
523
    def particles(self, particles: int):
11✔
524
        cv.check_type('particles', particles, Integral)
11✔
525
        cv.check_greater_than('particles', particles, 0)
11✔
526
        self._particles = particles
11✔
527

528
    @property
11✔
529
    def keff_trigger(self) -> dict:
11✔
530
        return self._keff_trigger
11✔
531

532
    @keff_trigger.setter
11✔
533
    def keff_trigger(self, keff_trigger: dict):
11✔
534
        if not isinstance(keff_trigger, dict):
11✔
535
            msg = f'Unable to set a trigger on keff from "{keff_trigger}" ' \
×
536
                  'which is not a Python dictionary'
537
            raise ValueError(msg)
×
538

539
        elif 'type' not in keff_trigger:
11✔
540
            msg = f'Unable to set a trigger on keff from "{keff_trigger}" ' \
×
541
                  'which does not have a "type" key'
542
            raise ValueError(msg)
×
543

544
        elif keff_trigger['type'] not in ['variance', 'std_dev', 'rel_err']:
11✔
545
            msg = 'Unable to set a trigger on keff with ' \
×
546
                  'type "{0}"'.format(keff_trigger['type'])
547
            raise ValueError(msg)
×
548

549
        elif 'threshold' not in keff_trigger:
11✔
550
            msg = f'Unable to set a trigger on keff from "{keff_trigger}" ' \
×
551
                  'which does not have a "threshold" key'
552
            raise ValueError(msg)
×
553

554
        elif not isinstance(keff_trigger['threshold'], Real):
11✔
555
            msg = 'Unable to set a trigger on keff with ' \
×
556
                  'threshold "{0}"'.format(keff_trigger['threshold'])
557
            raise ValueError(msg)
×
558

559
        self._keff_trigger = keff_trigger
11✔
560

561
    @property
11✔
562
    def energy_mode(self) -> str:
11✔
563
        return self._energy_mode
11✔
564

565
    @energy_mode.setter
11✔
566
    def energy_mode(self, energy_mode: str):
11✔
567
        cv.check_value('energy mode', energy_mode,
11✔
568
                    ['continuous-energy', 'multi-group'])
569
        self._energy_mode = energy_mode
11✔
570

571
    @property
11✔
572
    def max_order(self) -> int:
11✔
573
        return self._max_order
11✔
574

575
    @max_order.setter
11✔
576
    def max_order(self, max_order: int | None):
11✔
577
        if max_order is not None:
11✔
578
            cv.check_type('maximum scattering order', max_order, Integral)
11✔
579
            cv.check_greater_than('maximum scattering order', max_order, 0,
11✔
580
                                  True)
581
        self._max_order = max_order
11✔
582

583
    @property
11✔
584
    def source(self) -> list[SourceBase]:
11✔
585
        return self._source
11✔
586

587
    @source.setter
11✔
588
    def source(self, source: SourceBase | Iterable[SourceBase]):
11✔
589
        if not isinstance(source, MutableSequence):
11✔
590
            source = [source]
11✔
591
        self._source = cv.CheckedList(SourceBase, 'source distributions', source)
11✔
592

593
    @property
11✔
594
    def confidence_intervals(self) -> bool:
11✔
595
        return self._confidence_intervals
11✔
596

597
    @confidence_intervals.setter
11✔
598
    def confidence_intervals(self, confidence_intervals: bool):
11✔
599
        cv.check_type('confidence interval', confidence_intervals, bool)
11✔
600
        self._confidence_intervals = confidence_intervals
11✔
601

602
    @property
11✔
603
    def electron_treatment(self) -> str:
11✔
604
        return self._electron_treatment
11✔
605

606
    @electron_treatment.setter
11✔
607
    def electron_treatment(self, electron_treatment: str):
11✔
608
        cv.check_value('electron treatment', electron_treatment, ['led', 'ttb'])
11✔
609
        self._electron_treatment = electron_treatment
11✔
610

611
    @property
11✔
612
    def ptables(self) -> bool:
11✔
613
        return self._ptables
11✔
614

615
    @ptables.setter
11✔
616
    def ptables(self, ptables: bool):
11✔
617
        cv.check_type('probability tables', ptables, bool)
11✔
618
        self._ptables = ptables
11✔
619

620
    @property
11✔
621
    def photon_transport(self) -> bool:
11✔
622
        return self._photon_transport
11✔
623

624
    @photon_transport.setter
11✔
625
    def photon_transport(self, photon_transport: bool):
11✔
626
        cv.check_type('photon transport', photon_transport, bool)
11✔
627
        self._photon_transport = photon_transport
11✔
628

629
    @property
11✔
630
    def uniform_source_sampling(self) -> bool:
11✔
631
        return self._uniform_source_sampling
×
632

633
    @uniform_source_sampling.setter
11✔
634
    def uniform_source_sampling(self, uniform_source_sampling: bool):
11✔
635
        cv.check_type('strength as weights', uniform_source_sampling, bool)
11✔
636
        self._uniform_source_sampling = uniform_source_sampling
11✔
637

638
    @property
11✔
639
    def plot_seed(self):
11✔
640
        return self._plot_seed
11✔
641

642
    @plot_seed.setter
11✔
643
    def plot_seed(self, seed):
11✔
644
        cv.check_type('random plot color seed', seed, Integral)
11✔
645
        cv.check_greater_than('random plot color seed', seed, 0)
11✔
646
        self._plot_seed = seed
11✔
647

648
    @property
11✔
649
    def seed(self) -> int:
11✔
650
        return self._seed
11✔
651

652
    @seed.setter
11✔
653
    def seed(self, seed: int):
11✔
654
        cv.check_type('random number generator seed', seed, Integral)
11✔
655
        cv.check_greater_than('random number generator seed', seed, 0)
11✔
656
        self._seed = seed
11✔
657

658
    @property
11✔
659
    def stride(self) -> int:
11✔
660
        return self._stride
×
661

662
    @stride.setter
11✔
663
    def stride(self, stride: int):
11✔
664
        cv.check_type('random number generator stride', stride, Integral)
11✔
665
        cv.check_greater_than('random number generator stride', stride, 0)
11✔
666
        self._stride = stride
11✔
667

668
    @property
11✔
669
    def survival_biasing(self) -> bool:
11✔
670
        return self._survival_biasing
11✔
671

672
    @survival_biasing.setter
11✔
673
    def survival_biasing(self, survival_biasing: bool):
11✔
674
        cv.check_type('survival biasing', survival_biasing, bool)
11✔
675
        self._survival_biasing = survival_biasing
11✔
676

677
    @property
11✔
678
    def entropy_mesh(self) -> RegularMesh:
11✔
679
        return self._entropy_mesh
11✔
680

681
    @entropy_mesh.setter
11✔
682
    def entropy_mesh(self, entropy: RegularMesh):
11✔
683
        cv.check_type('entropy mesh', entropy, RegularMesh)
11✔
684
        self._entropy_mesh = entropy
11✔
685

686
    @property
11✔
687
    def trigger_active(self) -> bool:
11✔
688
        return self._trigger_active
11✔
689

690
    @trigger_active.setter
11✔
691
    def trigger_active(self, trigger_active: bool):
11✔
692
        cv.check_type('trigger active', trigger_active, bool)
11✔
693
        self._trigger_active = trigger_active
11✔
694

695
    @property
11✔
696
    def trigger_max_batches(self) -> int:
11✔
697
        return self._trigger_max_batches
11✔
698

699
    @trigger_max_batches.setter
11✔
700
    def trigger_max_batches(self, trigger_max_batches: int):
11✔
701
        cv.check_type('trigger maximum batches', trigger_max_batches, Integral)
11✔
702
        cv.check_greater_than('trigger maximum batches', trigger_max_batches, 0)
11✔
703
        self._trigger_max_batches = trigger_max_batches
11✔
704

705
    @property
11✔
706
    def trigger_batch_interval(self) -> int:
11✔
707
        return self._trigger_batch_interval
11✔
708

709
    @trigger_batch_interval.setter
11✔
710
    def trigger_batch_interval(self, trigger_batch_interval: int):
11✔
711
        cv.check_type('trigger batch interval', trigger_batch_interval, Integral)
11✔
712
        cv.check_greater_than('trigger batch interval', trigger_batch_interval, 0)
11✔
713
        self._trigger_batch_interval = trigger_batch_interval
11✔
714

715
    @property
11✔
716
    def output(self) -> dict:
11✔
717
        return self._output
11✔
718

719
    @output.setter
11✔
720
    def output(self, output: dict):
11✔
721
        cv.check_type('output', output, Mapping)
11✔
722
        for key, value in output.items():
11✔
723
            cv.check_value('output key', key, ('summary', 'tallies', 'path'))
11✔
724
            if key in ('summary', 'tallies'):
11✔
725
                cv.check_type(f"output['{key}']", value, bool)
11✔
726
            else:
727
                cv.check_type("output['path']", value, str)
11✔
728
        self._output = output
11✔
729

730
    @property
11✔
731
    def sourcepoint(self) -> dict:
11✔
732
        return self._sourcepoint
11✔
733

734
    @sourcepoint.setter
11✔
735
    def sourcepoint(self, sourcepoint: dict):
11✔
736
        cv.check_type('sourcepoint options', sourcepoint, Mapping)
11✔
737
        for key, value in sourcepoint.items():
11✔
738
            if key == 'batches':
11✔
739
                cv.check_type('sourcepoint batches', value, Iterable, Integral)
11✔
740
                for batch in value:
11✔
741
                    cv.check_greater_than('sourcepoint batch', batch, 0)
11✔
742
            elif key == 'separate':
11✔
743
                cv.check_type('sourcepoint separate', value, bool)
11✔
744
            elif key == 'write':
11✔
745
                cv.check_type('sourcepoint write', value, bool)
11✔
746
            elif key == 'overwrite':
11✔
747
                cv.check_type('sourcepoint overwrite', value, bool)
11✔
748
            elif key == 'mcpl':
11✔
749
                cv.check_type('sourcepoint mcpl', value, bool)
11✔
750
            else:
751
                raise ValueError(f"Unknown key '{key}' encountered when "
×
752
                                 "setting sourcepoint options.")
753
        self._sourcepoint = sourcepoint
11✔
754

755
    @property
11✔
756
    def statepoint(self) -> dict:
11✔
757
        return self._statepoint
11✔
758

759
    @statepoint.setter
11✔
760
    def statepoint(self, statepoint: dict):
11✔
761
        cv.check_type('statepoint options', statepoint, Mapping)
11✔
762
        for key, value in statepoint.items():
11✔
763
            if key == 'batches':
11✔
764
                cv.check_type('statepoint batches', value, Iterable, Integral)
11✔
765
                for batch in value:
11✔
766
                    cv.check_greater_than('statepoint batch', batch, 0)
11✔
767
            else:
768
                raise ValueError(f"Unknown key '{key}' encountered when "
×
769
                                 "setting statepoint options.")
770
        self._statepoint = statepoint
11✔
771

772
    @property
11✔
773
    def surf_source_read(self) -> dict:
11✔
774
        return self._surf_source_read
11✔
775

776
    @surf_source_read.setter
11✔
777
    def surf_source_read(self, ssr: dict):
11✔
778
        cv.check_type('surface source reading options', ssr, Mapping)
11✔
779
        for key, value in ssr.items():
11✔
780
            cv.check_value('surface source reading key', key,
11✔
781
                           ('path'))
782
            if key == 'path':
11✔
783
                cv.check_type('path to surface source file', value, PathLike)
11✔
784
        self._surf_source_read = dict(ssr)
11✔
785

786
        # Resolve path to surface source file
787
        if 'path' in ssr:
11✔
788
            self._surf_source_read['path'] = input_path(ssr['path'])
11✔
789

790
    @property
11✔
791
    def surf_source_write(self) -> dict:
11✔
792
        return self._surf_source_write
11✔
793

794
    @surf_source_write.setter
11✔
795
    def surf_source_write(self, surf_source_write: dict):
11✔
796
        cv.check_type("surface source writing options", surf_source_write, Mapping)
11✔
797
        for key, value in surf_source_write.items():
11✔
798
            cv.check_value(
11✔
799
                "surface source writing key",
800
                key,
801
                ("surface_ids", "max_particles", "max_source_files", "mcpl", "cell", "cellfrom", "cellto"),
802
            )
803
            if key == "surface_ids":
11✔
804
                cv.check_type(
11✔
805
                    "surface ids for source banking", value, Iterable, Integral
806
                )
807
                for surf_id in value:
11✔
808
                    cv.check_greater_than("surface id for source banking", surf_id, 0)
11✔
809

810
            elif key == "mcpl":
11✔
811
                cv.check_type("write to an MCPL-format file", value, bool)
11✔
812
            elif key in ("max_particles", "max_source_files", "cell", "cellfrom", "cellto"):
11✔
813
                name = {
11✔
814
                    "max_particles": "maximum particle banks on surfaces per process",
815
                    "max_source_files": "maximun surface source files to be written",
816
                    "cell": "Cell ID for source banking (from or to)",
817
                    "cellfrom": "Cell ID for source banking (from only)",
818
                    "cellto": "Cell ID for source banking (to only)",
819
                }[key]
820
                cv.check_type(name, value, Integral)
11✔
821
                cv.check_greater_than(name, value, 0)
11✔
822

823
        self._surf_source_write = surf_source_write
11✔
824

825
    @property
11✔
826
    def no_reduce(self) -> bool:
11✔
827
        return self._no_reduce
11✔
828

829
    @no_reduce.setter
11✔
830
    def no_reduce(self, no_reduce: bool):
11✔
831
        cv.check_type('no reduction option', no_reduce, bool)
11✔
832
        self._no_reduce = no_reduce
11✔
833

834
    @property
11✔
835
    def verbosity(self) -> int:
11✔
836
        return self._verbosity
11✔
837

838
    @verbosity.setter
11✔
839
    def verbosity(self, verbosity: int):
11✔
840
        cv.check_type('verbosity', verbosity, Integral)
11✔
841
        cv.check_greater_than('verbosity', verbosity, 1, True)
11✔
842
        cv.check_less_than('verbosity', verbosity, 10, True)
11✔
843
        self._verbosity = verbosity
11✔
844

845
    @property
11✔
846
    def ifp_n_generation(self) -> int:
11✔
847
        return self._ifp_n_generation
11✔
848

849
    @ifp_n_generation.setter
11✔
850
    def ifp_n_generation(self, ifp_n_generation: int):
11✔
851
        if ifp_n_generation is not None:
11✔
852
            cv.check_type("number of generations", ifp_n_generation, Integral)
11✔
853
            cv.check_greater_than("number of generations", ifp_n_generation, 0)
11✔
854
        self._ifp_n_generation = ifp_n_generation
11✔
855

856
    @property
11✔
857
    def tabular_legendre(self) -> dict:
11✔
858
        return self._tabular_legendre
11✔
859

860
    @tabular_legendre.setter
11✔
861
    def tabular_legendre(self, tabular_legendre: dict):
11✔
862
        cv.check_type('tabular_legendre settings', tabular_legendre, Mapping)
11✔
863
        for key, value in tabular_legendre.items():
11✔
864
            cv.check_value('tabular_legendre key', key,
11✔
865
                           ['enable', 'num_points'])
866
            if key == 'enable':
11✔
867
                cv.check_type('enable tabular_legendre', value, bool)
11✔
868
            elif key == 'num_points':
11✔
869
                cv.check_type('num_points tabular_legendre', value, Integral)
11✔
870
                cv.check_greater_than('num_points tabular_legendre', value, 0)
11✔
871
        self._tabular_legendre = tabular_legendre
11✔
872

873
    @property
11✔
874
    def temperature(self) -> dict:
11✔
875
        return self._temperature
11✔
876

877
    @temperature.setter
11✔
878
    def temperature(self, temperature: dict):
11✔
879

880
        cv.check_type('temperature settings', temperature, Mapping)
11✔
881
        for key, value in temperature.items():
11✔
882
            cv.check_value('temperature key', key,
11✔
883
                           ['default', 'method', 'tolerance', 'multipole',
884
                            'range'])
885
            if key == 'default':
11✔
886
                cv.check_type('default temperature', value, Real)
11✔
887
            elif key == 'method':
11✔
888
                cv.check_value('temperature method', value,
11✔
889
                               ['nearest', 'interpolation'])
890
            elif key == 'tolerance':
11✔
891
                cv.check_type('temperature tolerance', value, Real)
11✔
892
            elif key == 'multipole':
11✔
893
                cv.check_type('temperature multipole', value, bool)
11✔
894
            elif key == 'range':
11✔
895
                cv.check_length('temperature range', value, 2)
11✔
896
                for T in value:
11✔
897
                    cv.check_type('temperature', T, Real)
11✔
898

899
        self._temperature = temperature
11✔
900

901
    @property
11✔
902
    def trace(self) -> Iterable:
11✔
903
        return self._trace
11✔
904

905
    @trace.setter
11✔
906
    def trace(self, trace: Iterable):
11✔
907
        cv.check_type('trace', trace, Iterable, Integral)
11✔
908
        cv.check_length('trace', trace, 3)
11✔
909
        cv.check_greater_than('trace batch', trace[0], 0)
11✔
910
        cv.check_greater_than('trace generation', trace[1], 0)
11✔
911
        cv.check_greater_than('trace particle', trace[2], 0)
11✔
912
        self._trace = trace
11✔
913

914
    @property
11✔
915
    def track(self) -> Iterable[Iterable[int]]:
11✔
916
        return self._track
11✔
917

918
    @track.setter
11✔
919
    def track(self, track: Iterable[Iterable[int]]):
11✔
920
        cv.check_type('track', track, Sequence)
11✔
921
        for t in track:
11✔
922
            if len(t) != 3:
11✔
923
                msg = f'Unable to set the track to "{t}" since its length is not 3'
×
924
                raise ValueError(msg)
×
925
            cv.check_greater_than('track batch', t[0], 0)
11✔
926
            cv.check_greater_than('track generation', t[1], 0)
11✔
927
            cv.check_greater_than('track particle', t[2], 0)
11✔
928
            cv.check_type('track batch', t[0], Integral)
11✔
929
            cv.check_type('track generation', t[1], Integral)
11✔
930
            cv.check_type('track particle', t[2], Integral)
11✔
931
        self._track = track
11✔
932

933
    @property
11✔
934
    def cutoff(self) -> dict:
11✔
935
        return self._cutoff
11✔
936

937
    @cutoff.setter
11✔
938
    def cutoff(self, cutoff: dict):
11✔
939
        if not isinstance(cutoff, Mapping):
11✔
940
            msg = f'Unable to set cutoff from "{cutoff}" which is not a '\
×
941
                  'Python dictionary'
942
            raise ValueError(msg)
×
943
        for key in cutoff:
11✔
944
            if key == 'weight':
11✔
945
                cv.check_type('weight cutoff', cutoff[key], Real)
11✔
946
                cv.check_greater_than('weight cutoff', cutoff[key], 0.0)
11✔
947
            elif key == 'weight_avg':
11✔
948
                cv.check_type('average survival weight', cutoff[key], Real)
11✔
949
                cv.check_greater_than('average survival weight',
11✔
950
                                      cutoff[key], 0.0)
951
            elif key == 'survival_normalization':
11✔
952
                cv.check_type('survival normalization', cutoff[key], bool)
11✔
953
            elif key in ['energy_neutron', 'energy_photon', 'energy_electron',
11✔
954
                         'energy_positron']:
955
                cv.check_type('energy cutoff', cutoff[key], Real)
11✔
956
                cv.check_greater_than('energy cutoff', cutoff[key], 0.0)
11✔
957
            else:
958
                msg = f'Unable to set cutoff to "{key}" which is unsupported ' \
11✔
959
                      'by OpenMC'
960

961
        self._cutoff = cutoff
11✔
962

963
    @property
11✔
964
    def ufs_mesh(self) -> RegularMesh:
11✔
965
        return self._ufs_mesh
11✔
966

967
    @ufs_mesh.setter
11✔
968
    def ufs_mesh(self, ufs_mesh: RegularMesh):
11✔
969
        cv.check_type('UFS mesh', ufs_mesh, RegularMesh)
11✔
970
        cv.check_length('UFS mesh dimension', ufs_mesh.dimension, 3)
11✔
971
        cv.check_length('UFS mesh lower-left corner', ufs_mesh.lower_left, 3)
11✔
972
        cv.check_length('UFS mesh upper-right corner', ufs_mesh.upper_right, 3)
11✔
973
        self._ufs_mesh = ufs_mesh
11✔
974

975
    @property
11✔
976
    def resonance_scattering(self) -> dict:
11✔
977
        return self._resonance_scattering
11✔
978

979
    @resonance_scattering.setter
11✔
980
    def resonance_scattering(self, res: dict):
11✔
981
        cv.check_type('resonance scattering settings', res, Mapping)
11✔
982
        keys = ('enable', 'method', 'energy_min', 'energy_max', 'nuclides')
11✔
983
        for key, value in res.items():
11✔
984
            cv.check_value('resonance scattering dictionary key', key, keys)
11✔
985
            if key == 'enable':
11✔
986
                cv.check_type('resonance scattering enable', value, bool)
11✔
987
            elif key == 'method':
11✔
988
                cv.check_value('resonance scattering method', value,
11✔
989
                               _RES_SCAT_METHODS)
990
            elif key == 'energy_min':
11✔
991
                name = 'resonance scattering minimum energy'
11✔
992
                cv.check_type(name, value, Real)
11✔
993
                cv.check_greater_than(name, value, 0)
11✔
994
            elif key == 'energy_max':
11✔
995
                name = 'resonance scattering minimum energy'
11✔
996
                cv.check_type(name, value, Real)
11✔
997
                cv.check_greater_than(name, value, 0)
11✔
998
            elif key == 'nuclides':
11✔
999
                cv.check_type('resonance scattering nuclides', value,
11✔
1000
                              Iterable, str)
1001
        self._resonance_scattering = res
11✔
1002

1003
    @property
11✔
1004
    def volume_calculations(self) -> list[VolumeCalculation]:
11✔
1005
        return self._volume_calculations
11✔
1006

1007
    @volume_calculations.setter
11✔
1008
    def volume_calculations(
11✔
1009
        self, vol_calcs: VolumeCalculation | Iterable[VolumeCalculation]
1010
    ):
1011
        if not isinstance(vol_calcs, MutableSequence):
11✔
1012
            vol_calcs = [vol_calcs]
11✔
1013
        self._volume_calculations = cv.CheckedList(
11✔
1014
            VolumeCalculation, 'stochastic volume calculations', vol_calcs)
1015

1016
    @property
11✔
1017
    def create_fission_neutrons(self) -> bool:
11✔
1018
        return self._create_fission_neutrons
11✔
1019

1020
    @create_fission_neutrons.setter
11✔
1021
    def create_fission_neutrons(self, create_fission_neutrons: bool):
11✔
1022
        cv.check_type('Whether create fission neutrons',
11✔
1023
                      create_fission_neutrons, bool)
1024
        self._create_fission_neutrons = create_fission_neutrons
11✔
1025

1026
    @property
11✔
1027
    def create_delayed_neutrons(self) -> bool:
11✔
1028
        return self._create_delayed_neutrons
11✔
1029

1030
    @create_delayed_neutrons.setter
11✔
1031
    def create_delayed_neutrons(self, create_delayed_neutrons: bool):
11✔
1032
        cv.check_type('Whether create only prompt neutrons',
11✔
1033
                      create_delayed_neutrons, bool)
1034
        self._create_delayed_neutrons = create_delayed_neutrons
11✔
1035

1036
    @property
11✔
1037
    def delayed_photon_scaling(self) -> bool:
11✔
1038
        return self._delayed_photon_scaling
×
1039

1040
    @delayed_photon_scaling.setter
11✔
1041
    def delayed_photon_scaling(self, value: bool):
11✔
1042
        cv.check_type('delayed photon scaling', value, bool)
×
1043
        self._delayed_photon_scaling = value
×
1044

1045
    @property
11✔
1046
    def material_cell_offsets(self) -> bool:
11✔
1047
        return self._material_cell_offsets
×
1048

1049
    @material_cell_offsets.setter
11✔
1050
    def material_cell_offsets(self, value: bool):
11✔
1051
        cv.check_type('material cell offsets', value, bool)
×
1052
        self._material_cell_offsets = value
×
1053

1054
    @property
11✔
1055
    def log_grid_bins(self) -> int:
11✔
1056
        return self._log_grid_bins
11✔
1057

1058
    @log_grid_bins.setter
11✔
1059
    def log_grid_bins(self, log_grid_bins: int):
11✔
1060
        cv.check_type('log grid bins', log_grid_bins, Real)
11✔
1061
        cv.check_greater_than('log grid bins', log_grid_bins, 0)
11✔
1062
        self._log_grid_bins = log_grid_bins
11✔
1063

1064
    @property
11✔
1065
    def event_based(self) -> bool:
11✔
1066
        return self._event_based
×
1067

1068
    @event_based.setter
11✔
1069
    def event_based(self, value: bool):
11✔
1070
        cv.check_type('event based', value, bool)
×
1071
        self._event_based = value
×
1072

1073
    @property
11✔
1074
    def max_particles_in_flight(self) -> int:
11✔
1075
        return self._max_particles_in_flight
×
1076

1077
    @max_particles_in_flight.setter
11✔
1078
    def max_particles_in_flight(self, value: int):
11✔
1079
        cv.check_type('max particles in flight', value, Integral)
×
1080
        cv.check_greater_than('max particles in flight', value, 0)
×
1081
        self._max_particles_in_flight = value
×
1082

1083
    @property
11✔
1084
    def max_particle_events(self) -> int:
11✔
1085
        return self._max_particle_events
11✔
1086

1087
    @max_particle_events.setter
11✔
1088
    def max_particle_events(self, value: int):
11✔
1089
        cv.check_type('max particle events', value, Integral)
11✔
1090
        cv.check_greater_than('max particle events', value, 0)
11✔
1091
        self._max_particle_events = value
11✔
1092

1093
    @property
11✔
1094
    def write_initial_source(self) -> bool:
11✔
1095
        return self._write_initial_source
11✔
1096

1097
    @write_initial_source.setter
11✔
1098
    def write_initial_source(self, value: bool):
11✔
1099
        cv.check_type('write initial source', value, bool)
11✔
1100
        self._write_initial_source = value
11✔
1101

1102
    @property
11✔
1103
    def weight_windows(self) -> WeightWindowsList:
11✔
1104
        return self._weight_windows
11✔
1105

1106
    @weight_windows.setter
11✔
1107
    def weight_windows(self, value: WeightWindows | Sequence[WeightWindows]):
11✔
1108
        if not isinstance(value, Sequence):
11✔
1109
            value = [value]
11✔
1110
        self._weight_windows = WeightWindowsList(value)
11✔
1111

1112
    @property
11✔
1113
    def weight_windows_on(self) -> bool:
11✔
1114
        return self._weight_windows_on
×
1115

1116
    @weight_windows_on.setter
11✔
1117
    def weight_windows_on(self, value: bool):
11✔
1118
        cv.check_type('weight windows on', value, bool)
11✔
1119
        self._weight_windows_on = value
11✔
1120

1121
    @property
11✔
1122
    def weight_window_checkpoints(self) -> dict:
11✔
1123
        return self._weight_window_checkpoints
11✔
1124

1125
    @weight_window_checkpoints.setter
11✔
1126
    def weight_window_checkpoints(self, weight_window_checkpoints: dict):
11✔
1127
        for key in weight_window_checkpoints.keys():
11✔
1128
            cv.check_value('weight_window_checkpoints', key, ('collision', 'surface'))
11✔
1129
        self._weight_window_checkpoints = weight_window_checkpoints
11✔
1130

1131
    @property
11✔
1132
    def max_splits(self):
11✔
1133
        raise AttributeError('max_splits has been deprecated. Please use max_history_splits instead')
×
1134

1135
    @property
11✔
1136
    def max_history_splits(self) -> int:
11✔
1137
        return self._max_history_splits
×
1138

1139
    @max_history_splits.setter
11✔
1140
    def max_history_splits(self, value: int):
11✔
1141
        cv.check_type('maximum particle splits', value, Integral)
11✔
1142
        cv.check_greater_than('max particle splits', value, 0)
11✔
1143
        self._max_history_splits = value
11✔
1144

1145
    @property
11✔
1146
    def max_secondaries(self) -> int:
11✔
1147
        return self._max_secondaries
11✔
1148

1149
    @max_secondaries.setter
11✔
1150
    def max_secondaries(self, value: int):
11✔
1151
        cv.check_type('maximum secondary bank size', value, Integral)
11✔
1152
        cv.check_greater_than('max secondary bank size', value, 0)
11✔
1153
        self._max_secondaries = value
11✔
1154

1155
    @property
11✔
1156
    def max_tracks(self) -> int:
11✔
1157
        return self._max_tracks
11✔
1158

1159
    @max_tracks.setter
11✔
1160
    def max_tracks(self, value: int):
11✔
1161
        cv.check_type('maximum particle tracks', value, Integral)
11✔
1162
        cv.check_greater_than('maximum particle tracks', value, 0, True)
11✔
1163
        self._max_tracks = value
11✔
1164

1165
    @property
11✔
1166
    def weight_windows_file(self) -> PathLike | None:
11✔
1167
        return self._weight_windows_file
11✔
1168

1169
    @weight_windows_file.setter
11✔
1170
    def weight_windows_file(self, value: PathLike):
11✔
1171
        cv.check_type('weight windows file', value, PathLike)
×
1172
        self._weight_windows_file = input_path(value)
×
1173

1174
    @property
11✔
1175
    def weight_window_generators(self) -> list[WeightWindowGenerator]:
11✔
1176
        return self._weight_window_generators
11✔
1177

1178
    @weight_window_generators.setter
11✔
1179
    def weight_window_generators(self, wwgs):
11✔
1180
        if not isinstance(wwgs, MutableSequence):
11✔
1181
            wwgs = [wwgs]
11✔
1182
        self._weight_window_generators = cv.CheckedList(WeightWindowGenerator, 'weight window generators', wwgs)
11✔
1183

1184
    @property
11✔
1185
    def random_ray(self) -> dict:
11✔
1186
        return self._random_ray
11✔
1187

1188
    @random_ray.setter
11✔
1189
    def random_ray(self, random_ray: dict):
11✔
1190
        if not isinstance(random_ray, Mapping):
11✔
1191
            raise ValueError(f'Unable to set random_ray from "{random_ray}" '
×
1192
                             'which is not a dict.')
1193
        for key, value in random_ray.items():
11✔
1194
            if key == 'distance_active':
11✔
1195
                cv.check_type('active ray length', value, Real)
11✔
1196
                cv.check_greater_than('active ray length', value, 0.0)
11✔
1197
            elif key == 'distance_inactive':
11✔
1198
                cv.check_type('inactive ray length', value, Real)
11✔
1199
                cv.check_greater_than('inactive ray length',
11✔
1200
                                      value, 0.0, True)
1201
            elif key == 'ray_source':
11✔
1202
                cv.check_type('random ray source', value, SourceBase)
11✔
1203
            elif key == 'volume_estimator':
×
1204
                cv.check_value('volume estimator', value,
×
1205
                               ('naive', 'simulation_averaged',
1206
                                'hybrid'))
1207
            elif key == 'source_shape':
×
1208
                cv.check_value('source shape', value,
×
1209
                               ('flat', 'linear', 'linear_xy'))
1210
            elif key == 'volume_normalized_flux_tallies':
×
1211
                cv.check_type('volume normalized flux tallies', value, bool)
×
1212
            elif key == 'adjoint':
×
1213
                cv.check_type('adjoint', value, bool)
×
1214
            elif key == 'source_region_meshes':
×
1215
                cv.check_type('source region meshes', value, Iterable)
×
1216
                for mesh, domains in value:
×
1217
                    cv.check_type('mesh', mesh, MeshBase)
×
1218
                    cv.check_type('domains', domains, Iterable)
×
1219
                    valid_types = (openmc.Material, openmc.Cell, openmc.Universe)
×
1220
                    for domain in domains:
×
1221
                        if not isinstance(domain, valid_types):
×
1222
                            raise ValueError(
×
1223
                                f'Invalid domain type: {type(domain)}. Expected '
1224
                                'openmc.Material, openmc.Cell, or openmc.Universe.')
1225
            elif key == 'sample_method':
×
1226
                cv.check_value('sample method', value,
×
1227
                               ('prng', 'halton'))
1228
            elif key == 'diagonal_stabilization_rho':
×
1229
                cv.check_type('diagonal stabilization rho', value, Real)
×
1230
                cv.check_greater_than('diagonal stabilization rho',
×
1231
                                      value, 0.0, True)
1232
            else:
1233
                raise ValueError(f'Unable to set random ray to "{key}" which is '
×
1234
                                 'unsupported by OpenMC')
1235

1236
        self._random_ray = random_ray
11✔
1237

1238
    @property
11✔
1239
    def use_decay_photons(self) -> bool:
11✔
1240
        return self._use_decay_photons
×
1241

1242
    @use_decay_photons.setter
11✔
1243
    def use_decay_photons(self, value):
11✔
1244
        cv.check_type('use decay photons', value, bool)
11✔
1245
        self._use_decay_photons = value
11✔
1246

1247
    @property
11✔
1248
    def source_rejection_fraction(self) -> float:
11✔
1249
        return self._source_rejection_fraction
11✔
1250

1251
    @source_rejection_fraction.setter
11✔
1252
    def source_rejection_fraction(self, source_rejection_fraction: float):
11✔
1253
        cv.check_type('source_rejection_fraction', source_rejection_fraction, Real)
11✔
1254
        cv.check_greater_than('source_rejection_fraction', source_rejection_fraction, 0)
11✔
1255
        cv.check_less_than('source_rejection_fraction', source_rejection_fraction, 1)
11✔
1256
        self._source_rejection_fraction = source_rejection_fraction
11✔
1257

1258
    def _create_run_mode_subelement(self, root):
11✔
1259
        elem = ET.SubElement(root, "run_mode")
11✔
1260
        elem.text = self._run_mode.value
11✔
1261

1262
    def _create_batches_subelement(self, root):
11✔
1263
        if self._batches is not None:
11✔
1264
            element = ET.SubElement(root, "batches")
11✔
1265
            element.text = str(self._batches)
11✔
1266

1267
    def _create_generations_per_batch_subelement(self, root):
11✔
1268
        if self._generations_per_batch is not None:
11✔
1269
            element = ET.SubElement(root, "generations_per_batch")
11✔
1270
            element.text = str(self._generations_per_batch)
11✔
1271

1272
    def _create_inactive_subelement(self, root):
11✔
1273
        if self._inactive is not None:
11✔
1274
            element = ET.SubElement(root, "inactive")
11✔
1275
            element.text = str(self._inactive)
11✔
1276

1277
    def _create_max_lost_particles_subelement(self, root):
11✔
1278
        if self._max_lost_particles is not None:
11✔
1279
            element = ET.SubElement(root, "max_lost_particles")
11✔
1280
            element.text = str(self._max_lost_particles)
11✔
1281

1282
    def _create_rel_max_lost_particles_subelement(self, root):
11✔
1283
        if self._rel_max_lost_particles is not None:
11✔
1284
            element = ET.SubElement(root, "rel_max_lost_particles")
11✔
1285
            element.text = str(self._rel_max_lost_particles)
11✔
1286

1287
    def _create_max_write_lost_particles_subelement(self, root):
11✔
1288
        if self._max_write_lost_particles is not None:
11✔
1289
            element = ET.SubElement(root, "max_write_lost_particles")
11✔
1290
            element.text = str(self._max_write_lost_particles)
11✔
1291

1292
    def _create_particles_subelement(self, root):
11✔
1293
        if self._particles is not None:
11✔
1294
            element = ET.SubElement(root, "particles")
11✔
1295
            element.text = str(self._particles)
11✔
1296

1297
    def _create_keff_trigger_subelement(self, root):
11✔
1298
        if self._keff_trigger is not None:
11✔
1299
            element = ET.SubElement(root, "keff_trigger")
11✔
1300
            for key, value in sorted(self._keff_trigger.items()):
11✔
1301
                subelement = ET.SubElement(element, key)
11✔
1302
                subelement.text = str(value).lower()
11✔
1303

1304
    def _create_energy_mode_subelement(self, root):
11✔
1305
        if self._energy_mode is not None:
11✔
1306
            element = ET.SubElement(root, "energy_mode")
11✔
1307
            element.text = str(self._energy_mode)
11✔
1308

1309
    def _create_max_order_subelement(self, root):
11✔
1310
        if self._max_order is not None:
11✔
1311
            element = ET.SubElement(root, "max_order")
11✔
1312
            element.text = str(self._max_order)
11✔
1313

1314
    def _create_source_subelement(self, root, mesh_memo=None):
11✔
1315
        for source in self.source:
11✔
1316
            root.append(source.to_xml_element())
11✔
1317
            if isinstance(source, IndependentSource) and isinstance(source.space, MeshSpatial):
11✔
1318
                path = f"./mesh[@id='{source.space.mesh.id}']"
11✔
1319
                if root.find(path) is None:
11✔
1320
                    root.append(source.space.mesh.to_xml_element())
11✔
1321
            if isinstance(source, MeshSource):
11✔
1322
                path = f"./mesh[@id='{source.mesh.id}']"
11✔
1323
                if root.find(path) is None:
11✔
1324
                    root.append(source.mesh.to_xml_element())
11✔
1325
                    if mesh_memo is not None:
11✔
1326
                        mesh_memo.add(source.mesh.id)
11✔
1327

1328
    def _create_volume_calcs_subelement(self, root):
11✔
1329
        for calc in self.volume_calculations:
11✔
1330
            root.append(calc.to_xml_element())
11✔
1331

1332
    def _create_output_subelement(self, root):
11✔
1333
        if self._output is not None:
11✔
1334
            element = ET.SubElement(root, "output")
11✔
1335
            for key, value in sorted(self._output.items()):
11✔
1336
                subelement = ET.SubElement(element, key)
11✔
1337
                if key in ('summary', 'tallies'):
11✔
1338
                    subelement.text = str(value).lower()
11✔
1339
                else:
1340
                    subelement.text = value
11✔
1341

1342
    def _create_verbosity_subelement(self, root):
11✔
1343
        if self._verbosity is not None:
11✔
1344
            element = ET.SubElement(root, "verbosity")
11✔
1345
            element.text = str(self._verbosity)
11✔
1346

1347
    def _create_statepoint_subelement(self, root):
11✔
1348
        if self._statepoint:
11✔
1349
            element = ET.SubElement(root, "state_point")
11✔
1350
            if 'batches' in self._statepoint:
11✔
1351
                subelement = ET.SubElement(element, "batches")
11✔
1352
                subelement.text = ' '.join(
11✔
1353
                    str(x) for x in self._statepoint['batches'])
1354

1355
    def _create_uniform_source_sampling_subelement(self, root):
11✔
1356
        if self._uniform_source_sampling is not None:
11✔
1357
            element = ET.SubElement(root, "uniform_source_sampling")
11✔
1358
            element.text = str(self._uniform_source_sampling).lower()
11✔
1359

1360
    def _create_sourcepoint_subelement(self, root):
11✔
1361
        if self._sourcepoint:
11✔
1362
            element = ET.SubElement(root, "source_point")
11✔
1363

1364
            if 'batches' in self._sourcepoint:
11✔
1365
                subelement = ET.SubElement(element, "batches")
11✔
1366
                subelement.text = ' '.join(
11✔
1367
                    str(x) for x in self._sourcepoint['batches'])
1368

1369
            if 'separate' in self._sourcepoint:
11✔
1370
                subelement = ET.SubElement(element, "separate")
11✔
1371
                subelement.text = str(self._sourcepoint['separate']).lower()
11✔
1372

1373
            if 'write' in self._sourcepoint:
11✔
1374
                subelement = ET.SubElement(element, "write")
11✔
1375
                subelement.text = str(self._sourcepoint['write']).lower()
11✔
1376

1377
            # Overwrite latest subelement
1378
            if 'overwrite' in self._sourcepoint:
11✔
1379
                subelement = ET.SubElement(element, "overwrite_latest")
11✔
1380
                subelement.text = str(self._sourcepoint['overwrite']).lower()
11✔
1381

1382
            if 'mcpl' in self._sourcepoint:
11✔
1383
                subelement = ET.SubElement(element, "mcpl")
11✔
1384
                subelement.text = str(self._sourcepoint['mcpl']).lower()
11✔
1385

1386
    def _create_surf_source_read_subelement(self, root):
11✔
1387
        if self._surf_source_read:
11✔
1388
            element = ET.SubElement(root, "surf_source_read")
11✔
1389
            if 'path' in self._surf_source_read:
11✔
1390
                subelement = ET.SubElement(element, "path")
11✔
1391
                subelement.text = str(self._surf_source_read['path'])
11✔
1392

1393
    def _create_surf_source_write_subelement(self, root):
11✔
1394
        if self._surf_source_write:
11✔
1395
            element = ET.SubElement(root, "surf_source_write")
11✔
1396
            if "surface_ids" in self._surf_source_write:
11✔
1397
                subelement = ET.SubElement(element, "surface_ids")
11✔
1398
                subelement.text = " ".join(
11✔
1399
                    str(x) for x in self._surf_source_write["surface_ids"]
1400
                )
1401
            if "mcpl" in self._surf_source_write:
11✔
1402
                subelement = ET.SubElement(element, "mcpl")
11✔
1403
                subelement.text = str(self._surf_source_write["mcpl"]).lower()
11✔
1404
            for key in ("max_particles", "max_source_files", "cell", "cellfrom", "cellto"):
11✔
1405
                if key in self._surf_source_write:
11✔
1406
                    subelement = ET.SubElement(element, key)
11✔
1407
                    subelement.text = str(self._surf_source_write[key])
11✔
1408

1409
    def _create_confidence_intervals(self, root):
11✔
1410
        if self._confidence_intervals is not None:
11✔
1411
            element = ET.SubElement(root, "confidence_intervals")
11✔
1412
            element.text = str(self._confidence_intervals).lower()
11✔
1413

1414
    def _create_electron_treatment_subelement(self, root):
11✔
1415
        if self._electron_treatment is not None:
11✔
1416
            element = ET.SubElement(root, "electron_treatment")
11✔
1417
            element.text = str(self._electron_treatment)
11✔
1418

1419
    def _create_photon_transport_subelement(self, root):
11✔
1420
        if self._photon_transport is not None:
11✔
1421
            element = ET.SubElement(root, "photon_transport")
11✔
1422
            element.text = str(self._photon_transport).lower()
11✔
1423

1424
    def _create_plot_seed_subelement(self, root):
11✔
1425
        if self._plot_seed is not None:
11✔
1426
            element = ET.SubElement(root, "plot_seed")
11✔
1427
            element.text = str(self._plot_seed)
11✔
1428

1429
    def _create_ptables_subelement(self, root):
11✔
1430
        if self._ptables is not None:
11✔
1431
            element = ET.SubElement(root, "ptables")
11✔
1432
            element.text = str(self._ptables).lower()
11✔
1433

1434
    def _create_seed_subelement(self, root):
11✔
1435
        if self._seed is not None:
11✔
1436
            element = ET.SubElement(root, "seed")
11✔
1437
            element.text = str(self._seed)
11✔
1438

1439
    def _create_stride_subelement(self, root):
11✔
1440
        if self._stride is not None:
11✔
1441
            element = ET.SubElement(root, "stride")
11✔
1442
            element.text = str(self._stride)
11✔
1443

1444
    def _create_survival_biasing_subelement(self, root):
11✔
1445
        if self._survival_biasing is not None:
11✔
1446
            element = ET.SubElement(root, "survival_biasing")
11✔
1447
            element.text = str(self._survival_biasing).lower()
11✔
1448

1449
    def _create_cutoff_subelement(self, root):
11✔
1450
        if self._cutoff is not None:
11✔
1451
            element = ET.SubElement(root, "cutoff")
11✔
1452
            for key, value in self._cutoff.items():
11✔
1453
                subelement = ET.SubElement(element, key)
11✔
1454
                subelement.text = str(value) if key != 'survival_normalization' \
11✔
1455
                    else str(value).lower()
1456

1457
    def _create_entropy_mesh_subelement(self, root, mesh_memo=None):
11✔
1458
        if self.entropy_mesh is None:
11✔
1459
            return
11✔
1460

1461
        # use default heuristic for entropy mesh if not set by user
1462
        if self.entropy_mesh.dimension is None:
11✔
1463
            if self.particles is None:
×
1464
                raise RuntimeError("Number of particles must be set in order to " \
×
1465
                    "use entropy mesh dimension heuristic")
1466
            else:
1467
                n = ceil((self.particles / 20.0)**(1.0 / 3.0))
×
1468
                d = len(self.entropy_mesh.lower_left)
×
1469
                self.entropy_mesh.dimension = (n,)*d
×
1470

1471
        # add mesh ID to this element
1472
        subelement = ET.SubElement(root, "entropy_mesh")
11✔
1473
        subelement.text = str(self.entropy_mesh.id)
11✔
1474

1475
        # If this mesh has already been written outside the
1476
        # settings element, skip writing it again
1477
        if mesh_memo and self.entropy_mesh.id in mesh_memo:
11✔
1478
            return
×
1479

1480
        # See if a <mesh> element already exists -- if not, add it
1481
        path = f"./mesh[@id='{self.entropy_mesh.id}']"
11✔
1482
        if root.find(path) is None:
11✔
1483
            root.append(self.entropy_mesh.to_xml_element())
11✔
1484
            if mesh_memo is not None:
11✔
1485
                mesh_memo.add(self.entropy_mesh.id)
11✔
1486

1487
    def _create_trigger_subelement(self, root):
11✔
1488
        if self._trigger_active is not None:
11✔
1489
            trigger_element = ET.SubElement(root, "trigger")
11✔
1490
            element = ET.SubElement(trigger_element, "active")
11✔
1491
            element.text = str(self._trigger_active).lower()
11✔
1492

1493
            if self._trigger_max_batches is not None:
11✔
1494
                element = ET.SubElement(trigger_element, "max_batches")
11✔
1495
                element.text = str(self._trigger_max_batches)
11✔
1496

1497
            if self._trigger_batch_interval is not None:
11✔
1498
                element = ET.SubElement(trigger_element, "batch_interval")
11✔
1499
                element.text = str(self._trigger_batch_interval)
11✔
1500

1501
    def _create_no_reduce_subelement(self, root):
11✔
1502
        if self._no_reduce is not None:
11✔
1503
            element = ET.SubElement(root, "no_reduce")
11✔
1504
            element.text = str(self._no_reduce).lower()
11✔
1505

1506
    def _create_ifp_n_generation_subelement(self, root):
11✔
1507
        if self._ifp_n_generation is not None:
11✔
1508
            element = ET.SubElement(root, "ifp_n_generation")
11✔
1509
            element.text = str(self._ifp_n_generation)
11✔
1510

1511
    def _create_tabular_legendre_subelements(self, root):
11✔
1512
        if self.tabular_legendre:
11✔
1513
            element = ET.SubElement(root, "tabular_legendre")
11✔
1514
            subelement = ET.SubElement(element, "enable")
11✔
1515
            subelement.text = str(self._tabular_legendre['enable']).lower()
11✔
1516
            if 'num_points' in self._tabular_legendre:
11✔
1517
                subelement = ET.SubElement(element, "num_points")
11✔
1518
                subelement.text = str(self._tabular_legendre['num_points'])
11✔
1519

1520
    def _create_temperature_subelements(self, root):
11✔
1521
        if self.temperature:
11✔
1522
            for key, value in sorted(self.temperature.items()):
11✔
1523
                element = ET.SubElement(root, f"temperature_{key}")
11✔
1524
                if isinstance(value, bool):
11✔
1525
                    element.text = str(value).lower()
11✔
1526
                elif key == 'range':
11✔
1527
                    element.text = ' '.join(str(T) for T in value)
11✔
1528
                else:
1529
                    element.text = str(value)
11✔
1530

1531
    def _create_trace_subelement(self, root):
11✔
1532
        if self._trace is not None:
11✔
1533
            element = ET.SubElement(root, "trace")
11✔
1534
            element.text = ' '.join(map(str, self._trace))
11✔
1535

1536
    def _create_track_subelement(self, root):
11✔
1537
        if self._track is not None:
11✔
1538
            element = ET.SubElement(root, "track")
11✔
1539
            element.text = ' '.join(map(str, itertools.chain(*self._track)))
11✔
1540

1541
    def _create_ufs_mesh_subelement(self, root, mesh_memo=None):
11✔
1542
        if self.ufs_mesh is None:
11✔
1543
            return
11✔
1544

1545
        subelement = ET.SubElement(root, "ufs_mesh")
11✔
1546
        subelement.text = str(self.ufs_mesh.id)
11✔
1547

1548
        if mesh_memo and self.ufs_mesh.id in mesh_memo:
11✔
1549
            return
×
1550

1551
        # See if a <mesh> element already exists -- if not, add it
1552
        path = f"./mesh[@id='{self.ufs_mesh.id}']"
11✔
1553
        if root.find(path) is None:
11✔
1554
            root.append(self.ufs_mesh.to_xml_element())
×
1555
            if mesh_memo is not None: mesh_memo.add(self.ufs_mesh.id)
×
1556

1557
    def _create_use_decay_photons_subelement(self, root):
11✔
1558
        if self._use_decay_photons is not None:
11✔
1559
            element = ET.SubElement(root, "use_decay_photons")
11✔
1560
            element.text = str(self._use_decay_photons).lower()
11✔
1561

1562
    def _create_resonance_scattering_subelement(self, root):
11✔
1563
        res = self.resonance_scattering
11✔
1564
        if res:
11✔
1565
            elem = ET.SubElement(root, 'resonance_scattering')
11✔
1566
            if 'enable' in res:
11✔
1567
                subelem = ET.SubElement(elem, 'enable')
11✔
1568
                subelem.text = str(res['enable']).lower()
11✔
1569
            if 'method' in res:
11✔
1570
                subelem = ET.SubElement(elem, 'method')
11✔
1571
                subelem.text = res['method']
11✔
1572
            if 'energy_min' in res:
11✔
1573
                subelem = ET.SubElement(elem, 'energy_min')
11✔
1574
                subelem.text = str(res['energy_min'])
11✔
1575
            if 'energy_max' in res:
11✔
1576
                subelem = ET.SubElement(elem, 'energy_max')
11✔
1577
                subelem.text = str(res['energy_max'])
11✔
1578
            if 'nuclides' in res:
11✔
1579
                subelem = ET.SubElement(elem, 'nuclides')
11✔
1580
                subelem.text = ' '.join(res['nuclides'])
11✔
1581

1582
    def _create_create_fission_neutrons_subelement(self, root):
11✔
1583
        if self._create_fission_neutrons is not None:
11✔
1584
            elem = ET.SubElement(root, "create_fission_neutrons")
11✔
1585
            elem.text = str(self._create_fission_neutrons).lower()
11✔
1586

1587
    def _create_create_delayed_neutrons_subelement(self, root):
11✔
1588
        if self._create_delayed_neutrons is not None:
11✔
1589
            elem = ET.SubElement(root, "create_delayed_neutrons")
11✔
1590
            elem.text = str(self._create_delayed_neutrons).lower()
11✔
1591

1592
    def _create_delayed_photon_scaling_subelement(self, root):
11✔
1593
        if self._delayed_photon_scaling is not None:
11✔
1594
            elem = ET.SubElement(root, "delayed_photon_scaling")
×
1595
            elem.text = str(self._delayed_photon_scaling).lower()
×
1596

1597
    def _create_event_based_subelement(self, root):
11✔
1598
        if self._event_based is not None:
11✔
1599
            elem = ET.SubElement(root, "event_based")
×
1600
            elem.text = str(self._event_based).lower()
×
1601

1602
    def _create_max_particles_in_flight_subelement(self, root):
11✔
1603
        if self._max_particles_in_flight is not None:
11✔
1604
            elem = ET.SubElement(root, "max_particles_in_flight")
×
1605
            elem.text = str(self._max_particles_in_flight).lower()
×
1606

1607
    def _create_max_events_subelement(self, root):
11✔
1608
        if self._max_particle_events is not None:
11✔
1609
            elem = ET.SubElement(root, "max_particle_events")
11✔
1610
            elem.text = str(self._max_particle_events).lower()
11✔
1611

1612
    def _create_material_cell_offsets_subelement(self, root):
11✔
1613
        if self._material_cell_offsets is not None:
11✔
1614
            elem = ET.SubElement(root, "material_cell_offsets")
×
1615
            elem.text = str(self._material_cell_offsets).lower()
×
1616

1617
    def _create_log_grid_bins_subelement(self, root):
11✔
1618
        if self._log_grid_bins is not None:
11✔
1619
            elem = ET.SubElement(root, "log_grid_bins")
11✔
1620
            elem.text = str(self._log_grid_bins)
11✔
1621

1622
    def _create_write_initial_source_subelement(self, root):
11✔
1623
        if self._write_initial_source is not None:
11✔
1624
            elem = ET.SubElement(root, "write_initial_source")
11✔
1625
            elem.text = str(self._write_initial_source).lower()
11✔
1626

1627
    def _create_weight_windows_subelement(self, root, mesh_memo=None):
11✔
1628
        for ww in self._weight_windows:
11✔
1629
            # Add weight window information
1630
            root.append(ww.to_xml_element())
11✔
1631

1632
            # if this mesh has already been written,
1633
            # skip writing the mesh element
1634
            if mesh_memo and ww.mesh.id in mesh_memo:
11✔
1635
                continue
11✔
1636

1637
            # See if a <mesh> element already exists -- if not, add it
1638
            path = f"./mesh[@id='{ww.mesh.id}']"
11✔
1639
            if root.find(path) is None:
11✔
1640
                root.append(ww.mesh.to_xml_element())
11✔
1641
                if mesh_memo is not None:
11✔
1642
                    mesh_memo.add(ww.mesh.id)
11✔
1643

1644
    def _create_weight_windows_on_subelement(self, root):
11✔
1645
        if self._weight_windows_on is not None:
11✔
1646
            elem = ET.SubElement(root, "weight_windows_on")
11✔
1647
            elem.text = str(self._weight_windows_on).lower()
11✔
1648

1649
    def _create_weight_window_generators_subelement(self, root, mesh_memo=None):
11✔
1650
        if not self.weight_window_generators:
11✔
1651
            return
11✔
1652
        elem = ET.SubElement(root, 'weight_window_generators')
11✔
1653
        for wwg in self.weight_window_generators:
11✔
1654
            elem.append(wwg.to_xml_element())
11✔
1655

1656
        # ensure that mesh elements are created if needed
1657
        for wwg in self.weight_window_generators:
11✔
1658
            if mesh_memo is not None and wwg.mesh.id in mesh_memo:
11✔
1659
                continue
×
1660

1661
            # See if a <mesh> element already exists -- if not, add it
1662
            path = f"./mesh[@id='{wwg.mesh.id}']"
11✔
1663
            if root.find(path) is None:
11✔
1664
                root.append(wwg.mesh.to_xml_element())
11✔
1665
                if mesh_memo is not None:
11✔
1666
                    mesh_memo.add(wwg.mesh.id)
11✔
1667

1668
    def _create_weight_windows_file_element(self, root):
11✔
1669
        if self.weight_windows_file is not None:
11✔
1670
            element = ET.Element("weight_windows_file")
×
1671
            element.text = str(self.weight_windows_file)
×
1672
            root.append(element)
×
1673

1674
    def _create_weight_window_checkpoints_subelement(self, root):
11✔
1675
        if not self._weight_window_checkpoints:
11✔
1676
            return
11✔
1677
        element = ET.SubElement(root, "weight_window_checkpoints")
11✔
1678

1679
        if 'collision' in self._weight_window_checkpoints:
11✔
1680
            subelement = ET.SubElement(element, "collision")
11✔
1681
            subelement.text = str(self._weight_window_checkpoints['collision']).lower()
11✔
1682

1683
        if 'surface' in self._weight_window_checkpoints:
11✔
1684
            subelement = ET.SubElement(element, "surface")
11✔
1685
            subelement.text = str(self._weight_window_checkpoints['surface']).lower()
11✔
1686

1687
    def _create_max_history_splits_subelement(self, root):
11✔
1688
        if self._max_history_splits is not None:
11✔
1689
            elem = ET.SubElement(root, "max_history_splits")
11✔
1690
            elem.text = str(self._max_history_splits)
11✔
1691

1692
    def _create_max_secondaries_subelement(self, root):
11✔
1693
        if self._max_secondaries is not None:
11✔
1694
            elem = ET.SubElement(root, "max_secondaries")
11✔
1695
            elem.text = str(self._max_secondaries)
11✔
1696

1697
    def _create_max_tracks_subelement(self, root):
11✔
1698
        if self._max_tracks is not None:
11✔
1699
            elem = ET.SubElement(root, "max_tracks")
11✔
1700
            elem.text = str(self._max_tracks)
11✔
1701

1702
    def _create_random_ray_subelement(self, root, mesh_memo=None):
11✔
1703
        if self._random_ray:
11✔
1704
            element = ET.SubElement(root, "random_ray")
11✔
1705
            for key, value in self._random_ray.items():
11✔
1706
                if key == 'ray_source' and isinstance(value, SourceBase):
11✔
1707
                    source_element = value.to_xml_element()
11✔
1708
                    element.append(source_element)
11✔
1709
                elif key == 'source_region_meshes':
11✔
1710
                    subelement = ET.SubElement(element, 'source_region_meshes')
11✔
1711
                    for mesh, domains in value:
11✔
1712
                        mesh_elem = ET.SubElement(subelement, 'mesh')
11✔
1713
                        mesh_elem.set('id', str(mesh.id))
11✔
1714
                        for domain in domains:
11✔
1715
                            domain_elem = ET.SubElement(mesh_elem, 'domain')
11✔
1716
                            domain_elem.set('id', str(domain.id))
11✔
1717
                            domain_elem.set('type', domain.__class__.__name__.lower())
11✔
1718
                        if mesh_memo is not None and mesh.id not in mesh_memo:
11✔
1719
                            root.append(mesh.to_xml_element())
11✔
1720
                            mesh_memo.add(mesh.id)
11✔
1721
                else:
1722
                    subelement = ET.SubElement(element, key)
11✔
1723
                    subelement.text = str(value)
11✔
1724

1725
    def _create_source_rejection_fraction_subelement(self, root):
11✔
1726
        if self._source_rejection_fraction is not None:
11✔
1727
            element = ET.SubElement(root, "source_rejection_fraction")
11✔
1728
            element.text = str(self._source_rejection_fraction)
11✔
1729

1730
    def _eigenvalue_from_xml_element(self, root):
11✔
1731
        elem = root.find('eigenvalue')
11✔
1732
        if elem is not None:
11✔
1733
            self._run_mode_from_xml_element(elem)
×
1734
            self._particles_from_xml_element(elem)
×
1735
            self._batches_from_xml_element(elem)
×
1736
            self._inactive_from_xml_element(elem)
×
1737
            self._max_lost_particles_from_xml_element(elem)
×
1738
            self._rel_max_lost_particles_from_xml_element(elem)
×
1739
            self._max_write_lost_particles_from_xml_element(elem)
×
1740
            self._generations_per_batch_from_xml_element(elem)
×
1741

1742
    def _run_mode_from_xml_element(self, root):
11✔
1743
        text = get_text(root, 'run_mode')
11✔
1744
        if text is not None:
11✔
1745
            self.run_mode = text
11✔
1746

1747
    def _particles_from_xml_element(self, root):
11✔
1748
        text = get_text(root, 'particles')
11✔
1749
        if text is not None:
11✔
1750
            self.particles = int(text)
11✔
1751

1752
    def _batches_from_xml_element(self, root):
11✔
1753
        text = get_text(root, 'batches')
11✔
1754
        if text is not None:
11✔
1755
            self.batches = int(text)
11✔
1756

1757
    def _inactive_from_xml_element(self, root):
11✔
1758
        text = get_text(root, 'inactive')
11✔
1759
        if text is not None:
11✔
1760
            self.inactive = int(text)
11✔
1761

1762
    def _max_lost_particles_from_xml_element(self, root):
11✔
1763
        text = get_text(root, 'max_lost_particles')
11✔
1764
        if text is not None:
11✔
1765
            self.max_lost_particles = int(text)
11✔
1766

1767
    def _rel_max_lost_particles_from_xml_element(self, root):
11✔
1768
        text = get_text(root, 'rel_max_lost_particles')
11✔
1769
        if text is not None:
11✔
1770
            self.rel_max_lost_particles = float(text)
11✔
1771

1772
    def _max_write_lost_particles_from_xml_element(self, root):
11✔
1773
        text = get_text(root, 'max_write_lost_particles')
11✔
1774
        if text is not None:
11✔
1775
            self.max_write_lost_particles = int(text)
×
1776

1777
    def _generations_per_batch_from_xml_element(self, root):
11✔
1778
        text = get_text(root, 'generations_per_batch')
11✔
1779
        if text is not None:
11✔
1780
            self.generations_per_batch = int(text)
11✔
1781

1782
    def _keff_trigger_from_xml_element(self, root):
11✔
1783
        elem = root.find('keff_trigger')
11✔
1784
        if elem is not None:
11✔
1785
            trigger = get_text(elem, 'type')
11✔
1786
            threshold = float(get_text(elem, 'threshold'))
11✔
1787
            self.keff_trigger = {'type': trigger, 'threshold': threshold}
11✔
1788

1789
    def _source_from_xml_element(self, root, meshes=None):
11✔
1790
        for elem in root.findall('source'):
11✔
1791
            src = SourceBase.from_xml_element(elem, meshes)
11✔
1792
            # add newly constructed source object to the list
1793
            self.source.append(src)
11✔
1794

1795
    def _volume_calcs_from_xml_element(self, root):
11✔
1796
        volume_elems = root.findall("volume_calc")
11✔
1797
        if volume_elems:
11✔
1798
            self.volume_calculations = [VolumeCalculation.from_xml_element(elem)
11✔
1799
                                        for elem in volume_elems]
1800

1801
    def _output_from_xml_element(self, root):
11✔
1802
        elem = root.find('output')
11✔
1803
        if elem is not None:
11✔
1804
            self.output = {}
11✔
1805
            for key in ('summary', 'tallies', 'path'):
11✔
1806
                value = get_text(elem, key)
11✔
1807
                if value is not None:
11✔
1808
                    if key in ('summary', 'tallies'):
11✔
1809
                        value = value in ('true', '1')
11✔
1810
                    self.output[key] = value
11✔
1811

1812
    def _statepoint_from_xml_element(self, root):
11✔
1813
        elem = root.find('state_point')
11✔
1814
        if elem is not None:
11✔
1815
            batches = get_elem_list(elem, "batches", int)
11✔
1816
            if batches is not None:
11✔
1817
                self.statepoint['batches'] = batches
11✔
1818

1819
    def _sourcepoint_from_xml_element(self, root):
11✔
1820
        elem = root.find('source_point')
11✔
1821
        if elem is not None:
11✔
1822
            for key in ('separate', 'write', 'overwrite_latest', 'batches', 'mcpl'):
11✔
1823
                if key in ('separate', 'write', 'mcpl', 'overwrite_latest'):
11✔
1824
                    value = get_text(elem, key) in ('true', '1')
11✔
1825
                    if key == 'overwrite_latest':
11✔
1826
                        key = 'overwrite'
11✔
1827
                else:
1828
                    value = get_elem_list(elem, key, int)
11✔
1829
                if value is not None:
11✔
1830
                    self.sourcepoint[key] = value
11✔
1831

1832
    def _surf_source_read_from_xml_element(self, root):
11✔
1833
        elem = root.find('surf_source_read')
11✔
1834
        if elem is not None:
11✔
1835
            ssr = {}
11✔
1836
            value = get_text(elem, 'path')
11✔
1837
            if value is not None:
11✔
1838
                ssr['path'] = value
11✔
1839
            self.surf_source_read = ssr
11✔
1840

1841
    def _surf_source_write_from_xml_element(self, root):
11✔
1842
        elem = root.find('surf_source_write')
11✔
1843
        if elem is None:
11✔
1844
            return
11✔
1845
        for key in ('surface_ids', 'max_particles', 'max_source_files', 'mcpl', 'cell', 'cellto', 'cellfrom'):
11✔
1846
            if key == 'surface_ids':
11✔
1847
                value = get_elem_list(elem, key, int)
11✔
1848
            else:
1849
                value = get_text(elem, key)
11✔
1850
            if value is not None:
11✔
1851
                if key == 'mcpl':
11✔
1852
                    value = value in ('true', '1')
×
1853
                elif key in ('max_particles', 'max_source_files', 'cell', 'cellfrom', 'cellto'):
11✔
1854
                    value = int(value)
11✔
1855
                self.surf_source_write[key] = value
11✔
1856

1857
    def _confidence_intervals_from_xml_element(self, root):
11✔
1858
        text = get_text(root, 'confidence_intervals')
11✔
1859
        if text is not None:
11✔
1860
            self.confidence_intervals = text in ('true', '1')
11✔
1861

1862
    def _electron_treatment_from_xml_element(self, root):
11✔
1863
        text = get_text(root, 'electron_treatment')
11✔
1864
        if text is not None:
11✔
1865
            self.electron_treatment = text
11✔
1866

1867
    def _energy_mode_from_xml_element(self, root):
11✔
1868
        text = get_text(root, 'energy_mode')
11✔
1869
        if text is not None:
11✔
1870
            self.energy_mode = text
11✔
1871

1872
    def _max_order_from_xml_element(self, root):
11✔
1873
        text = get_text(root, 'max_order')
11✔
1874
        if text is not None:
11✔
1875
            self.max_order = int(text)
11✔
1876

1877
    def _photon_transport_from_xml_element(self, root):
11✔
1878
        text = get_text(root, 'photon_transport')
11✔
1879
        if text is not None:
11✔
1880
            self.photon_transport = text in ('true', '1')
11✔
1881

1882
    def _uniform_source_sampling_from_xml_element(self, root):
11✔
1883
        text = get_text(root, 'uniform_source_sampling')
11✔
1884
        if text is not None:
11✔
1885
            self.uniform_source_sampling = text in ('true', '1')
×
1886

1887
    def _plot_seed_from_xml_element(self, root):
11✔
1888
        text = get_text(root, 'plot_seed')
11✔
1889
        if text is not None:
11✔
1890
            self.plot_seed = int(text)
11✔
1891

1892
    def _ptables_from_xml_element(self, root):
11✔
1893
        text = get_text(root, 'ptables')
11✔
1894
        if text is not None:
11✔
1895
            self.ptables = text in ('true', '1')
11✔
1896

1897
    def _seed_from_xml_element(self, root):
11✔
1898
        text = get_text(root, 'seed')
11✔
1899
        if text is not None:
11✔
1900
            self.seed = int(text)
11✔
1901

1902
    def _stride_from_xml_element(self, root):
11✔
1903
        text = get_text(root, 'stride')
11✔
1904
        if text is not None:
11✔
1905
            self.stride = int(text)
×
1906

1907
    def _survival_biasing_from_xml_element(self, root):
11✔
1908
        text = get_text(root, 'survival_biasing')
11✔
1909
        if text is not None:
11✔
1910
            self.survival_biasing = text in ('true', '1')
11✔
1911

1912
    def _cutoff_from_xml_element(self, root):
11✔
1913
        elem = root.find('cutoff')
11✔
1914
        if elem is not None:
11✔
1915
            self.cutoff = {}
11✔
1916
            for key in ('energy_neutron', 'energy_photon', 'energy_electron',
11✔
1917
                        'energy_positron', 'weight', 'weight_avg', 'time_neutron',
1918
                        'time_photon', 'time_electron', 'time_positron',
1919
                        'survival_normalization'):
1920
                value = get_text(elem, key)
11✔
1921
                if value is not None:
11✔
1922
                    if key == 'survival_normalization':
11✔
1923
                        self.cutoff[key] = value in ('true', '1')
11✔
1924
                    else:
1925
                        self.cutoff[key] = float(value)
11✔
1926

1927
    def _entropy_mesh_from_xml_element(self, root, meshes):
11✔
1928
        text = get_text(root, 'entropy_mesh')
11✔
1929
        if text is None:
11✔
1930
            return
11✔
1931
        mesh_id = int(text)
11✔
1932
        if mesh_id not in meshes:
11✔
1933
            raise ValueError(f'Could not locate mesh with ID "{mesh_id}"')
×
1934
        self.entropy_mesh = meshes[mesh_id]
11✔
1935

1936
    def _trigger_from_xml_element(self, root):
11✔
1937
        elem = root.find('trigger')
11✔
1938
        if elem is not None:
11✔
1939
            self.trigger_active = get_text(elem, 'active') in ('true', '1')
11✔
1940
            text = get_text(elem, 'max_batches')
11✔
1941
            if text is not None:
11✔
1942
                self.trigger_max_batches = int(text)
11✔
1943
            text = get_text(elem, 'batch_interval')
11✔
1944
            if text is not None:
11✔
1945
                self.trigger_batch_interval = int(text)
11✔
1946

1947
    def _no_reduce_from_xml_element(self, root):
11✔
1948
        text = get_text(root, 'no_reduce')
11✔
1949
        if text is not None:
11✔
1950
            self.no_reduce = text in ('true', '1')
11✔
1951

1952
    def _verbosity_from_xml_element(self, root):
11✔
1953
        text = get_text(root, 'verbosity')
11✔
1954
        if text is not None:
11✔
1955
            self.verbosity = int(text)
11✔
1956

1957
    def _ifp_n_generation_from_xml_element(self, root):
11✔
1958
        text = get_text(root, 'ifp_n_generation')
11✔
1959
        if text is not None:
11✔
1960
            self.ifp_n_generation = int(text)
11✔
1961

1962
    def _tabular_legendre_from_xml_element(self, root):
11✔
1963
        elem = root.find('tabular_legendre')
11✔
1964
        if elem is not None:
11✔
1965
            text = get_text(elem, 'enable')
11✔
1966
            self.tabular_legendre['enable'] = text in ('true', '1')
11✔
1967
            text = get_text(elem, 'num_points')
11✔
1968
            if text is not None:
11✔
1969
                self.tabular_legendre['num_points'] = int(text)
11✔
1970

1971
    def _temperature_from_xml_element(self, root):
11✔
1972
        text = get_text(root, 'temperature_default')
11✔
1973
        if text is not None:
11✔
1974
            self.temperature['default'] = float(text)
11✔
1975
        text = get_text(root, 'temperature_tolerance')
11✔
1976
        if text is not None:
11✔
1977
            self.temperature['tolerance'] = float(text)
×
1978
        text = get_text(root, 'temperature_method')
11✔
1979
        if text is not None:
11✔
1980
            self.temperature['method'] = text
11✔
1981
        text = get_elem_list(root, "temperature_range", float)
11✔
1982
        if text is not None:
11✔
1983
            self.temperature['range'] = text
11✔
1984
        text = get_text(root, 'temperature_multipole')
11✔
1985
        if text is not None:
11✔
1986
            self.temperature['multipole'] = text in ('true', '1')
11✔
1987

1988
    def _trace_from_xml_element(self, root):
11✔
1989
        text = get_elem_list(root, "trace", int)
11✔
1990
        if text is not None:
11✔
1991
            self.trace = text
11✔
1992

1993
    def _track_from_xml_element(self, root):
11✔
1994
        values = get_elem_list(root, "track", int)
11✔
1995
        if values is not None:
11✔
1996
            self.track = list(zip(values[::3], values[1::3], values[2::3]))
11✔
1997

1998
    def _ufs_mesh_from_xml_element(self, root, meshes):
11✔
1999
        text = get_text(root, 'ufs_mesh')
11✔
2000
        if text is None:
11✔
2001
            return
11✔
2002
        mesh_id = int(text)
11✔
2003
        if mesh_id not in meshes:
11✔
2004
            raise ValueError(f'Could not locate mesh with ID "{mesh_id}"')
×
2005
        self.ufs_mesh = meshes[mesh_id]
11✔
2006

2007
    def _resonance_scattering_from_xml_element(self, root):
11✔
2008
        elem = root.find('resonance_scattering')
11✔
2009
        if elem is not None:
11✔
2010
            keys = ('enable', 'method', 'energy_min', 'energy_max', 'nuclides')
11✔
2011
            for key in keys:
11✔
2012
                if key == 'nuclides':
11✔
2013
                    value = get_elem_list(elem, key, str)
11✔
2014
                else:
2015
                    value = get_text(elem, key)
11✔
2016
                if value is not None:
11✔
2017
                    if key == 'enable':
11✔
2018
                        value = value in ('true', '1')
11✔
2019
                    elif key in ('energy_min', 'energy_max'):
11✔
2020
                        value = float(value)
11✔
2021
                    self.resonance_scattering[key] = value
11✔
2022

2023
    def _create_fission_neutrons_from_xml_element(self, root):
11✔
2024
        text = get_text(root, 'create_fission_neutrons')
11✔
2025
        if text is not None:
11✔
2026
            self.create_fission_neutrons = text in ('true', '1')
11✔
2027

2028
    def _create_delayed_neutrons_from_xml_element(self, root):
11✔
2029
        text = get_text(root, 'create_delayed_neutrons')
11✔
2030
        if text is not None:
11✔
2031
            self.create_delayed_neutrons = text in ('true', '1')
11✔
2032

2033
    def _delayed_photon_scaling_from_xml_element(self, root):
11✔
2034
        text = get_text(root, 'delayed_photon_scaling')
11✔
2035
        if text is not None:
11✔
2036
            self.delayed_photon_scaling = text in ('true', '1')
×
2037

2038
    def _event_based_from_xml_element(self, root):
11✔
2039
        text = get_text(root, 'event_based')
11✔
2040
        if text is not None:
11✔
2041
            self.event_based = text in ('true', '1')
×
2042

2043
    def _max_particles_in_flight_from_xml_element(self, root):
11✔
2044
        text = get_text(root, 'max_particles_in_flight')
11✔
2045
        if text is not None:
11✔
2046
            self.max_particles_in_flight = int(text)
×
2047

2048
    def _max_particle_events_from_xml_element(self, root):
11✔
2049
        text = get_text(root, 'max_particle_events')
11✔
2050
        if text is not None:
11✔
2051
            self.max_particle_events = int(text)
11✔
2052

2053
    def _material_cell_offsets_from_xml_element(self, root):
11✔
2054
        text = get_text(root, 'material_cell_offsets')
11✔
2055
        if text is not None:
11✔
2056
            self.material_cell_offsets = text in ('true', '1')
×
2057

2058
    def _log_grid_bins_from_xml_element(self, root):
11✔
2059
        text = get_text(root, 'log_grid_bins')
11✔
2060
        if text is not None:
11✔
2061
            self.log_grid_bins = int(text)
11✔
2062

2063
    def _write_initial_source_from_xml_element(self, root):
11✔
2064
        text = get_text(root, 'write_initial_source')
11✔
2065
        if text is not None:
11✔
2066
            self.write_initial_source = text in ('true', '1')
11✔
2067

2068
    def _weight_window_generators_from_xml_element(self, root, meshes=None):
11✔
2069
        for elem in root.iter('weight_windows_generator'):
11✔
2070
            wwg = WeightWindowGenerator.from_xml_element(elem, meshes)
11✔
2071
            self.weight_window_generators.append(wwg)
11✔
2072

2073
    def _weight_windows_from_xml_element(self, root, meshes=None):
11✔
2074
        for elem in root.findall('weight_windows'):
11✔
2075
            ww = WeightWindows.from_xml_element(elem, meshes)
11✔
2076
            self.weight_windows.append(ww)
11✔
2077

2078
    def _weight_windows_on_from_xml_element(self, root):
11✔
2079
        text = get_text(root, 'weight_windows_on')
11✔
2080
        if text is not None:
11✔
2081
            self.weight_windows_on = text in ('true', '1')
×
2082

2083
    def _weight_windows_file_from_xml_element(self, root):
11✔
2084
        text = get_text(root, 'weight_windows_file')
11✔
2085
        if text is not None:
11✔
NEW
2086
            self.weight_windows_file = text
×
2087

2088
    def _weight_window_checkpoints_from_xml_element(self, root):
11✔
2089
        elem = root.find('weight_window_checkpoints')
11✔
2090
        if elem is None:
11✔
2091
            return
11✔
2092
        for key in ('collision', 'surface'):
11✔
2093
            value = get_text(elem, key)
11✔
2094
            if value is not None:
11✔
2095
                value = value in ('true', '1')
11✔
2096
                self.weight_window_checkpoints[key] = value
11✔
2097

2098
    def _max_history_splits_from_xml_element(self, root):
11✔
2099
        text = get_text(root, 'max_history_splits')
11✔
2100
        if text is not None:
11✔
2101
            self.max_history_splits = int(text)
11✔
2102

2103
    def _max_secondaries_from_xml_element(self, root):
11✔
2104
        text = get_text(root, 'max_secondaries')
11✔
2105
        if text is not None:
11✔
2106
            self.max_secondaries = int(text)
11✔
2107

2108
    def _max_tracks_from_xml_element(self, root):
11✔
2109
        text = get_text(root, 'max_tracks')
11✔
2110
        if text is not None:
11✔
2111
            self.max_tracks = int(text)
11✔
2112

2113
    def _random_ray_from_xml_element(self, root):
11✔
2114
        elem = root.find('random_ray')
11✔
2115
        if elem is not None:
11✔
2116
            self.random_ray = {}
11✔
2117
            for child in elem:
11✔
2118
                if child.tag in ('distance_inactive', 'distance_active', 'diagonal_stabilization_rho'):
11✔
2119
                    self.random_ray[child.tag] = float(child.text)
11✔
2120
                elif child.tag == 'source':
11✔
2121
                    source = SourceBase.from_xml_element(child)
11✔
2122
                    self.random_ray['ray_source'] = source
11✔
2123
                elif child.tag == 'volume_estimator':
×
2124
                    self.random_ray['volume_estimator'] = child.text
×
2125
                elif child.tag == 'source_shape':
×
2126
                    self.random_ray['source_shape'] = child.text
×
2127
                elif child.tag == 'volume_normalized_flux_tallies':
×
2128
                    self.random_ray['volume_normalized_flux_tallies'] = (
×
2129
                        child.text in ('true', '1')
2130
                    )
2131
                elif child.tag == 'adjoint':
×
2132
                    self.random_ray['adjoint'] = (
×
2133
                        child.text in ('true', '1')
2134
                    )
2135
                elif child.tag == 'sample_method':
×
2136
                    self.random_ray['sample_method'] = child.text
×
2137
                elif child.tag == 'source_region_meshes':
×
2138
                    self.random_ray['source_region_meshes'] = []
×
2139
                    for mesh_elem in child.findall('mesh'):
×
2140
                        mesh = MeshBase.from_xml_element(mesh_elem)
×
2141
                        domains = []
×
2142
                        for domain_elem in mesh_elem.findall('domain'):
×
2143
                            domain_id = int(get_text(domain_elem, "id"))
×
2144
                            domain_type = get_text(domain_elem, "type")
×
2145
                            if domain_type == 'material':
×
2146
                                domain = openmc.Material(domain_id)
×
2147
                            elif domain_type == 'cell':
×
2148
                                domain = openmc.Cell(domain_id)
×
2149
                            elif domain_type == 'universe':
×
2150
                                domain = openmc.Universe(domain_id)
×
2151
                            domains.append(domain)
×
2152
                        self.random_ray['source_region_meshes'].append((mesh, domains))
×
2153

2154
    def _use_decay_photons_from_xml_element(self, root):
11✔
2155
        text = get_text(root, 'use_decay_photons')
11✔
2156
        if text is not None:
11✔
2157
            self.use_decay_photons = text in ('true', '1')
×
2158

2159
    def _source_rejection_fraction_from_xml_element(self, root):
11✔
2160
        text = get_text(root, 'source_rejection_fraction')
11✔
2161
        if text is not None:
11✔
2162
            self.source_rejection_fraction = float(text)
11✔
2163

2164
    def to_xml_element(self, mesh_memo=None):
11✔
2165
        """Create a 'settings' element to be written to an XML file.
2166

2167
        Parameters
2168
        ----------
2169
        mesh_memo : set of ints
2170
            A set of mesh IDs to keep track of whether a mesh has already been written.
2171
        """
2172
        # Reset xml element tree
2173
        element = ET.Element("settings")
11✔
2174

2175
        self._create_run_mode_subelement(element)
11✔
2176
        self._create_particles_subelement(element)
11✔
2177
        self._create_batches_subelement(element)
11✔
2178
        self._create_inactive_subelement(element)
11✔
2179
        self._create_max_lost_particles_subelement(element)
11✔
2180
        self._create_rel_max_lost_particles_subelement(element)
11✔
2181
        self._create_max_write_lost_particles_subelement(element)
11✔
2182
        self._create_generations_per_batch_subelement(element)
11✔
2183
        self._create_keff_trigger_subelement(element)
11✔
2184
        self._create_source_subelement(element, mesh_memo)
11✔
2185
        self._create_output_subelement(element)
11✔
2186
        self._create_statepoint_subelement(element)
11✔
2187
        self._create_sourcepoint_subelement(element)
11✔
2188
        self._create_surf_source_read_subelement(element)
11✔
2189
        self._create_surf_source_write_subelement(element)
11✔
2190
        self._create_confidence_intervals(element)
11✔
2191
        self._create_electron_treatment_subelement(element)
11✔
2192
        self._create_energy_mode_subelement(element)
11✔
2193
        self._create_max_order_subelement(element)
11✔
2194
        self._create_photon_transport_subelement(element)
11✔
2195
        self._create_uniform_source_sampling_subelement(element)
11✔
2196
        self._create_plot_seed_subelement(element)
11✔
2197
        self._create_ptables_subelement(element)
11✔
2198
        self._create_seed_subelement(element)
11✔
2199
        self._create_stride_subelement(element)
11✔
2200
        self._create_survival_biasing_subelement(element)
11✔
2201
        self._create_cutoff_subelement(element)
11✔
2202
        self._create_entropy_mesh_subelement(element, mesh_memo)
11✔
2203
        self._create_trigger_subelement(element)
11✔
2204
        self._create_no_reduce_subelement(element)
11✔
2205
        self._create_verbosity_subelement(element)
11✔
2206
        self._create_ifp_n_generation_subelement(element)
11✔
2207
        self._create_tabular_legendre_subelements(element)
11✔
2208
        self._create_temperature_subelements(element)
11✔
2209
        self._create_trace_subelement(element)
11✔
2210
        self._create_track_subelement(element)
11✔
2211
        self._create_ufs_mesh_subelement(element, mesh_memo)
11✔
2212
        self._create_resonance_scattering_subelement(element)
11✔
2213
        self._create_volume_calcs_subelement(element)
11✔
2214
        self._create_create_fission_neutrons_subelement(element)
11✔
2215
        self._create_create_delayed_neutrons_subelement(element)
11✔
2216
        self._create_delayed_photon_scaling_subelement(element)
11✔
2217
        self._create_event_based_subelement(element)
11✔
2218
        self._create_max_particles_in_flight_subelement(element)
11✔
2219
        self._create_max_events_subelement(element)
11✔
2220
        self._create_material_cell_offsets_subelement(element)
11✔
2221
        self._create_log_grid_bins_subelement(element)
11✔
2222
        self._create_write_initial_source_subelement(element)
11✔
2223
        self._create_weight_windows_subelement(element, mesh_memo)
11✔
2224
        self._create_weight_windows_on_subelement(element)
11✔
2225
        self._create_weight_window_generators_subelement(element, mesh_memo)
11✔
2226
        self._create_weight_windows_file_element(element)
11✔
2227
        self._create_weight_window_checkpoints_subelement(element)
11✔
2228
        self._create_max_history_splits_subelement(element)
11✔
2229
        self._create_max_tracks_subelement(element)
11✔
2230
        self._create_max_secondaries_subelement(element)
11✔
2231
        self._create_random_ray_subelement(element, mesh_memo)
11✔
2232
        self._create_use_decay_photons_subelement(element)
11✔
2233
        self._create_source_rejection_fraction_subelement(element)
11✔
2234

2235
        # Clean the indentation in the file to be user-readable
2236
        clean_indentation(element)
11✔
2237

2238
        return element
11✔
2239

2240
    def export_to_xml(self, path: PathLike = 'settings.xml'):
11✔
2241
        """Export simulation settings to an XML file.
2242

2243
        Parameters
2244
        ----------
2245
        path : str
2246
            Path to file to write. Defaults to 'settings.xml'.
2247

2248
        """
2249
        root_element = self.to_xml_element()
11✔
2250

2251
        # Check if path is a directory
2252
        p = Path(path)
11✔
2253
        if p.is_dir():
11✔
2254
            p /= 'settings.xml'
11✔
2255

2256
        # Write the XML Tree to the settings.xml file
2257
        tree = ET.ElementTree(root_element)
11✔
2258
        tree.write(str(p), xml_declaration=True, encoding='utf-8')
11✔
2259

2260
    @classmethod
11✔
2261
    def from_xml_element(cls, elem, meshes=None):
11✔
2262
        """Generate settings from XML element
2263

2264
        Parameters
2265
        ----------
2266
        elem : lxml.etree._Element
2267
            XML element
2268
        meshes : dict or None
2269
            A dictionary with mesh IDs as keys and mesh instances as values that
2270
            have already been read from XML. Pre-existing meshes are used
2271
            and new meshes are added to when creating tally objects.
2272

2273
        Returns
2274
        -------
2275
        openmc.Settings
2276
            Settings object
2277

2278
        """
2279
        # read all meshes under the settings node and update
2280
        settings_meshes = _read_meshes(elem)
11✔
2281
        meshes = {} if meshes is None else meshes
11✔
2282
        meshes.update(settings_meshes)
11✔
2283

2284
        settings = cls()
11✔
2285
        settings._eigenvalue_from_xml_element(elem)
11✔
2286
        settings._run_mode_from_xml_element(elem)
11✔
2287
        settings._particles_from_xml_element(elem)
11✔
2288
        settings._batches_from_xml_element(elem)
11✔
2289
        settings._inactive_from_xml_element(elem)
11✔
2290
        settings._max_lost_particles_from_xml_element(elem)
11✔
2291
        settings._rel_max_lost_particles_from_xml_element(elem)
11✔
2292
        settings._max_write_lost_particles_from_xml_element(elem)
11✔
2293
        settings._generations_per_batch_from_xml_element(elem)
11✔
2294
        settings._keff_trigger_from_xml_element(elem)
11✔
2295
        settings._source_from_xml_element(elem, meshes)
11✔
2296
        settings._volume_calcs_from_xml_element(elem)
11✔
2297
        settings._output_from_xml_element(elem)
11✔
2298
        settings._statepoint_from_xml_element(elem)
11✔
2299
        settings._sourcepoint_from_xml_element(elem)
11✔
2300
        settings._surf_source_read_from_xml_element(elem)
11✔
2301
        settings._surf_source_write_from_xml_element(elem)
11✔
2302
        settings._confidence_intervals_from_xml_element(elem)
11✔
2303
        settings._electron_treatment_from_xml_element(elem)
11✔
2304
        settings._energy_mode_from_xml_element(elem)
11✔
2305
        settings._max_order_from_xml_element(elem)
11✔
2306
        settings._photon_transport_from_xml_element(elem)
11✔
2307
        settings._uniform_source_sampling_from_xml_element(elem)
11✔
2308
        settings._plot_seed_from_xml_element(elem)
11✔
2309
        settings._ptables_from_xml_element(elem)
11✔
2310
        settings._seed_from_xml_element(elem)
11✔
2311
        settings._stride_from_xml_element(elem)
11✔
2312
        settings._survival_biasing_from_xml_element(elem)
11✔
2313
        settings._cutoff_from_xml_element(elem)
11✔
2314
        settings._entropy_mesh_from_xml_element(elem, meshes)
11✔
2315
        settings._trigger_from_xml_element(elem)
11✔
2316
        settings._no_reduce_from_xml_element(elem)
11✔
2317
        settings._verbosity_from_xml_element(elem)
11✔
2318
        settings._ifp_n_generation_from_xml_element(elem)
11✔
2319
        settings._tabular_legendre_from_xml_element(elem)
11✔
2320
        settings._temperature_from_xml_element(elem)
11✔
2321
        settings._trace_from_xml_element(elem)
11✔
2322
        settings._track_from_xml_element(elem)
11✔
2323
        settings._ufs_mesh_from_xml_element(elem, meshes)
11✔
2324
        settings._resonance_scattering_from_xml_element(elem)
11✔
2325
        settings._create_fission_neutrons_from_xml_element(elem)
11✔
2326
        settings._create_delayed_neutrons_from_xml_element(elem)
11✔
2327
        settings._delayed_photon_scaling_from_xml_element(elem)
11✔
2328
        settings._event_based_from_xml_element(elem)
11✔
2329
        settings._max_particles_in_flight_from_xml_element(elem)
11✔
2330
        settings._max_particle_events_from_xml_element(elem)
11✔
2331
        settings._material_cell_offsets_from_xml_element(elem)
11✔
2332
        settings._log_grid_bins_from_xml_element(elem)
11✔
2333
        settings._write_initial_source_from_xml_element(elem)
11✔
2334
        settings._weight_windows_from_xml_element(elem, meshes)
11✔
2335
        settings._weight_windows_on_from_xml_element(elem)
11✔
2336
        settings._weight_windows_file_from_xml_element(elem)
11✔
2337
        settings._weight_window_generators_from_xml_element(elem, meshes)
11✔
2338
        settings._weight_window_checkpoints_from_xml_element(elem)
11✔
2339
        settings._max_history_splits_from_xml_element(elem)
11✔
2340
        settings._max_tracks_from_xml_element(elem)
11✔
2341
        settings._max_secondaries_from_xml_element(elem)
11✔
2342
        settings._random_ray_from_xml_element(elem)
11✔
2343
        settings._use_decay_photons_from_xml_element(elem)
11✔
2344
        settings._source_rejection_fraction_from_xml_element(elem)
11✔
2345

2346
        return settings
11✔
2347

2348
    @classmethod
11✔
2349
    def from_xml(cls, path: PathLike = 'settings.xml'):
11✔
2350
        """Generate settings from XML file
2351

2352
        .. versionadded:: 0.13.0
2353

2354
        Parameters
2355
        ----------
2356
        path : str, optional
2357
            Path to settings XML file
2358

2359
        Returns
2360
        -------
2361
        openmc.Settings
2362
            Settings object
2363

2364
        """
2365
        parser = ET.XMLParser(huge_tree=True)
11✔
2366
        tree = ET.parse(path, parser=parser)
11✔
2367
        root = tree.getroot()
11✔
2368
        meshes = _read_meshes(root)
11✔
2369
        return cls.from_xml_element(root, meshes)
11✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc