diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index 7091757f23b..dfab96131df 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -626,7 +626,11 @@ found in the :ref:`random ray user guide `. :volume_estimator: Specifies choice of volume estimator for the random ray solver. Options - are 'naive', 'simulation_averaged', or 'hybrid'. The default is 'hybrid'. + are 'naive', 'simulation_averaged', 'hybrid', 'adaptive', + 'strict_adaptive', or 'auto'. The default is 'auto', which selects + 'adaptive' for standard solves and 'strict_adaptive' for solves whose + results feed variance reduction (weight window generation and adjoint + workflows). *Default*: None @@ -1648,6 +1652,7 @@ and 10. The verbosity levels are defined as follows: :5: all of the above + file I/O :6: all of the above + timing statistics and initialization messages :7: all of the above + :math:`k` by generation + :8: all of the above + random ray volume-estimator diagnostics :9: all of the above + indicate when each particle starts :10: all of the above + event information diff --git a/docs/source/methods/random_ray.rst b/docs/source/methods/random_ray.rst index 8bc2a0a1bf5..537930096d8 100644 --- a/docs/source/methods/random_ray.rst +++ b/docs/source/methods/random_ray.rst @@ -511,18 +511,53 @@ when using the naive estimator, though at the cost of a notable increase in variance. Empirical testing reveals that on most eigenvalue problems, the simulation averaged estimator does win out overall in numerical performance, as a much coarser quadrature can be used resulting in faster runtimes overall. -Thus, OpenMC uses the simulation averaged estimator as default in its random ray -mode for eigenvalue solves. +Thus, the simulation averaged estimator is generally preferred over the naive +estimator for eigenvalue solves. OpenMC also features a "hybrid" volume estimator that uses the naive estimator for all regions containing an external (fixed) source term. For all other source regions, the "simulation averaged" estimator is used. This typically achieves -a best of both worlds result, with the benefits of the low bias simulation averaged +a best of both worlds result, with the benefits of the unbiased simulation averaged estimator in most regions, while preventing instability and/or large biases in regions -with external source terms via use of the naive estimator. In general, it is -recommended to use the "hybrid" estimator, which is the default method used -in OpenMC. If instability is encountered despite high ray densities, then -the naive estimator may be preferable. +with external source terms via use of the naive estimator. If instability is +encountered despite high ray densities, then the naive estimator may be +preferable. + +OpenMC also features an "adaptive" volume estimator that generalizes the +hybrid estimator. It uses the simulation averaged estimator by default and +automatically demotes individual cells to the naive treatment (the naive +volume and the previous-flux miss treatment) when they show signs of +instability. The most important case this adds over the hybrid estimator is +a cell fed almost entirely by in-scatter from other groups, as in the +optically thin air regions common to shielding problems, where the reduced +source dwarfs the flux even though no external source is present. + +A cell is demoted when it is hit-starved or when its reduced source is +negative. During the inactive batches, a cell whose reduced source is much +larger than its scalar flux is also demoted. Finally, beginning at the end +of the inactive batches and re-evaluated throughout the active phase, a +cell is demoted permanently if its flux accumulated over the simulation is +negative or is dominated by sources that do not derive from its own flux. +Because these last decisions are made from accumulated statistics and are +never reversed, the estimator choice does not churn with iteration noise +during the tallied batches. When a linear source shape is in use, demoted +cells also revert to a flat source representation. + +A "strict adaptive" variant is provided for solves whose results feed +variance reduction, where even a small number of slightly negative flux +estimates can degrade the adjoint solve and the quality of generated weight +windows. It runs the same machinery and additionally repairs any negative +flux estimate each batch, first by recomputing it with the batch's own +volume and then, if it is still negative, by falling back on the previous +iterate. A cell that needs the repair repeatedly is demoted outright, which +keeps the one-sided repair from biasing its flux upward. The repair +introduces a small conservative bias overall (several hundred pcm on +typical eigenvalue problems), so the strict variant is not used for +standard solves. + +By default, OpenMC selects the volume estimator automatically ("auto"). +Weight window generation and adjoint solves receive the strict adaptive +estimator, and all other solves receive the adaptive estimator. A table that summarizes the pros and cons, as well as recommendations for different use cases, is given in the :ref:`volume diff --git a/docs/source/usersguide/random_ray.rst b/docs/source/usersguide/random_ray.rst index 81db61ae062..88b5c8fab1c 100644 --- a/docs/source/usersguide/random_ray.rst +++ b/docs/source/usersguide/random_ray.rst @@ -1080,6 +1080,12 @@ following methods are currently available in OpenMC: - Description - Pros - Cons + * - ``auto`` (default) + - Automatically selects an appropriate estimator for the type of + simulation being performed. Most users do not need to consider this + setting further. + - * No user input needed + - * N/A * - ``simulation_averaged`` - Accumulates total active ray lengths in each FSR over all iterations, improving the estimate of the volume in each cell each iteration. @@ -1099,15 +1105,44 @@ following methods are currently available in OpenMC: unstable - * Biased estimator * Requires more rays or longer active ray length to mitigate bias - * - ``hybrid`` (default) + * - ``hybrid`` - Applies the naive estimator to all cells that contain an external (fixed) source contribution. Applies the simulation averaged estimator to all other cells. - - * High accuracy/low bias of the simulation averaged estimator in most + - * Accuracy of the unbiased simulation averaged estimator in most cells * Stability of the naive estimator in cells with fixed sources - * Can lead to slightly negative fluxes in cells where the simulation averaged estimator is used + * - ``adaptive`` + - Generalizes the hybrid estimator. Uses the simulation averaged + estimator by default, but automatically (and permanently) demotes + individual cells to the naive treatment when their accumulated + statistics indicate the simulation averaged estimator is unstable + there (e.g., cells dominated by external or in-scatter sources, and + hit-starved cells). + - * Accuracy of the simulation averaged estimator in most cells + * Stable in cases where the simulation averaged and hybrid estimators + are not + * No parameters to tune + - * Benefits from a longer inactive phase to inform the demotion + decisions + * - ``strict_adaptive`` + - As ``adaptive``, but additionally applies a per-batch fixup to any + negative flux estimate (recomputing it with the batch's own volume, + then falling back on the previous iterate) and demotes chronically + affected cells to the naive treatment. + - * Suppresses the negative flux estimates other estimators can produce + in pathological cases + * Improves the quality of generated weight windows + - * The one-sided fixup introduces a small conservative bias, so it is + not recommended where unbiased results are the priority + +By default, the ``volume_estimator`` field is set to ``auto``, which selects +``strict_adaptive`` for solves whose results feed variance reduction (weight +window generation and adjoint workflows) and ``adaptive`` for all other +solves. The end-of-simulation output reports which estimator was selected, +and explicitly setting any other value overrides the automatic selection. These estimators can be selected by setting the ``volume_estimator`` field in the :attr:`openmc.Settings.random_ray` dictionary. For example, to use the naive @@ -1117,6 +1152,22 @@ estimator, the following code would be used: settings.random_ray['volume_estimator'] = 'naive' +The ``auto`` setting is the default, as it gives reliable behavior out of +the box across problem types. The adaptive estimator is especially valuable +for fixed source and shielding problems, where optically thin, scattering- +or streaming-dominated regions (for example, the air- or void-filled regions +of a shielding model) can destabilize the ``hybrid`` and +``simulation_averaged`` estimators. It detects and stabilizes the affected +cells automatically while leaving the rest of the problem on the unbiased +simulation averaged estimator. Because demotions are decided from each +cell's accumulated statistics rather than from single-iteration values, the +estimator choice does not churn with iteration noise and avoids the bias +that per-iteration selection can introduce. Solves that feed variance +reduction are routed to ``strict_adaptive`` instead, as even a small number +of slightly negative flux estimates in the near-void regions of pathological +problems can otherwise degrade the adjoint solve and the quality of +generated weight windows. + ----------------- Adjoint Flux Mode ----------------- diff --git a/include/openmc/constants.h b/include/openmc/constants.h index a1d94e5819e..b1573446156 100644 --- a/include/openmc/constants.h +++ b/include/openmc/constants.h @@ -61,20 +61,6 @@ constexpr double RADIAL_MESH_TOL {1e-10}; // Maximum number of random samples per history constexpr int MAX_SAMPLE {100000}; -// Avg. number of hits per batch to be defined as a "small" -// source region in the random ray solver -constexpr double MIN_HITS_PER_BATCH {1.5}; - -// The minimum flux value to be considered non-zero when computing adjoint -// sources. Positive values below this cutoff will be treated as zero, so as to -// prevent extremely large adjoint source terms from being generated. -constexpr double ZERO_FLUX_CUTOFF {1e-22}; - -// The minimum macroscopic cross section value considered non-void for the -// random ray solver. Materials with any group with a cross section below this -// value will be converted to pure void. -constexpr double MINIMUM_MACRO_XS {1e-6}; - // Relative dead band applied to weight window comparisons: particles split // only above upper * (1 + tol) and roulette only below lower * (1 - tol). // Weight window arithmetic can land a particle's weight exactly back on a @@ -378,7 +364,14 @@ enum class RunMode { enum class SolverType { MONTE_CARLO, RANDOM_RAY }; -enum class RandomRayVolumeEstimator { NAIVE, SIMULATION_AVERAGED, HYBRID }; +enum class RandomRayVolumeEstimator { + NAIVE, + SIMULATION_AVERAGED, + HYBRID, + ADAPTIVE, + STRICT_ADAPTIVE, + AUTO +}; enum class RandomRaySourceShape { FLAT, LINEAR, LINEAR_XY }; enum class RandomRaySampleMethod { PRNG, HALTON, S2 }; enum class RandomRaySolve { FORWARD, FORWARD_FOR_ADJOINT, ADJOINT }; diff --git a/include/openmc/random_ray/flat_source_domain.h b/include/openmc/random_ray/flat_source_domain.h index 09414fd4465..c87ff48f301 100644 --- a/include/openmc/random_ray/flat_source_domain.h +++ b/include/openmc/random_ray/flat_source_domain.h @@ -12,6 +12,50 @@ namespace openmc { +// Avg. number of hits per batch to be defined as a "small" source region. +constexpr double MIN_HITS_PER_BATCH {1.5}; + +// Strong-source ratio threshold for the adaptive volume estimator. A source +// region is treated as having a "strong" inhomogeneous source in any group +// where the reduced source q/Sigma_t exceeds this multiple of the region's +// scalar flux, indicating a source sustained by an external or in-scatter +// contribution rather than by the local flux. Such regions are given the +// naive volume and previous-flux miss treatment. The value sits well inside +// the range over which benign problems remain untriggered while pathological +// cells are still caught. +constexpr double ADAPTIVE_VOLUME_KAPPA {4.0}; + +// Chronic-negativity demotion thresholds for the strict adaptive volume +// estimator. A region whose flux has gone negative (before the fixup) in at +// least max(MIN_COUNT, RATE * current_batch) batches is demoted to the naive +// volume and previous-flux miss treatment. Without this channel the +// non-negativity floor would mask the accumulated-flux sign signal that the +// adaptive demotion relies on, leaving noisy regions to be clipped every +// batch and biasing their fluxes upward. Demotion instead moves such regions +// onto an estimator that does not need clipping. +constexpr int NEGATIVE_FLUX_DEMOTION_MIN_COUNT {3}; +constexpr double NEGATIVE_FLUX_DEMOTION_RATE {0.005}; + +// The minimum flux value to be considered non-zero when computing adjoint +// sources. Positive values below this cutoff will be treated as zero, so as to +// prevent extremely large adjoint source terms from being generated. +constexpr double ZERO_FLUX_CUTOFF {1e-22}; + +// The minimum macroscopic cross section value considered non-void for the +// random ray solver. Materials with any group with a cross section below this +// value will be converted to pure void. +constexpr double MINIMUM_MACRO_XS {1e-6}; + +// True for the members of the adaptive volume estimator family: the +// adaptive estimator, and the strict adaptive estimator, which runs the +// same machinery plus a per-batch non-negativity enforcement on the flux +// iterates. +inline bool is_adaptive_family(RandomRayVolumeEstimator e) +{ + return e == RandomRayVolumeEstimator::ADAPTIVE || + e == RandomRayVolumeEstimator::STRICT_ADAPTIVE; +} + /* * The FlatSourceDomain class encompasses data and methods for storing * scalar flux and source region for all flat source regions in a @@ -39,6 +83,7 @@ class FlatSourceDomain { void reset_tally_volumes(); void random_ray_tally(); virtual void accumulate_iteration_flux(); + void demotion_step(); void output_to_vtk() const; void convert_external_sources(bool use_adjoint_sources); void count_external_source_regions(); @@ -59,7 +104,6 @@ class FlatSourceDomain { SourceRegionHandle get_subdivided_source_region_handle( SourceRegionKey sr_key, Position r, Direction u); void finalize_discovered_source_regions(); - void apply_transport_stabilization(); int64_t n_source_regions() const { return source_regions_.n_source_regions(); @@ -93,7 +137,14 @@ class FlatSourceDomain { //---------------------------------------------------------------------------- // Static data members + // The volume estimator as configured ("auto" by default). This is set when + // the settings are read and is never modified by the solver. static RandomRayVolumeEstimator volume_estimator_; + // The concrete estimator the solver runs with, assigned at the start of + // every random ray solve. It holds the configured value, or for "auto" the + // estimator selected for the type of simulation being performed. All + // solver code reads this member rather than volume_estimator_. + static RandomRayVolumeEstimator resolved_volume_estimator_; //---------------------------------------------------------------------------- // Public Data members @@ -103,6 +154,27 @@ class FlatSourceDomain { int64_t n_external_source_regions_ {0}; // Total number of source regions with // non-zero external source terms + // Final-batch snapshot of the naive volume treatment, partitioned by + // mutually exclusive cause (the cause counts sum to n_final_naive_), for + // end-of-simulation reporting. The two demote-only decisions made from the + // running accumulated flux (a strong accumulated feed and a negative + // accumulated flux) are counted with first priority, so their counts equal + // the decisions settled by the final batch. The per-batch strong-source + // test and hit-starved causes count the remainder. + int64_t n_final_naive_ {0}; + int64_t n_final_latch_ {0}; // strong source, from the accumulated feed + int64_t n_final_strong_ {0}; // strong source, from the per-batch test + int64_t n_final_sign_ {0}; // negative accumulated flux + int64_t n_final_small_ {0}; // hit-starved + int64_t n_final_chronic_ {0}; // chronic negativity (strict adaptive) + // Final-batch counts of the strict adaptive estimator's non-negativity + // enforcement: regions whose negative batch flux was recomputed with the + // batch volume (rescued), and regions floored at the previous iterate + // after the rescue was insufficient or unavailable. + int64_t n_final_rescued_ {0}; + int64_t n_final_floored_ {0}; + bool final_stats_valid_ {false}; + // 1D array representing source region starting offset for each OpenMC Cell // in model::cells vector source_region_offsets_; @@ -170,6 +242,20 @@ class FlatSourceDomain { virtual void set_flux_to_flux_plus_source(int64_t sr, double volume, int g); void set_flux_to_source(int64_t sr, int g); virtual void set_flux_to_old_flux(int64_t sr, int g); + double flux_additive_term(int64_t sr, int g) const; + double stabilized_flux(int64_t sr, int g, double phi_new) const; + + //! Adaptive-estimator "strong source" test. Returns true if, in any group, + //! the region's reduced source q/Sigma_t is negative (with a non-negative + //! previous-iteration flux) or exceeds ADAPTIVE_VOLUME_KAPPA times the + //! (non-negative) previous-iteration scalar flux. The ratio condition is + //! only checked when include_ratio is set, which callers do only during + //! the inactive batches. The test is shared by the flat volume switch + //! (add_source_to_scalar_flux) and the linear gradient fallback + //! (update_single_neutron_source), with the region's per-group + //! reduced-source and previous-flux arrays passed directly. + bool region_has_strong_source(const float* reduced_source, + const double* flux_old, bool include_ratio) const; //---------------------------------------------------------------------------- // Private data members diff --git a/include/openmc/random_ray/source_region.h b/include/openmc/random_ray/source_region.h index 1d2bbe1e8dc..f488aa293aa 100644 --- a/include/openmc/random_ray/source_region.h +++ b/include/openmc/random_ray/source_region.h @@ -149,6 +149,8 @@ class SourceRegionHandle { int* temperature_idx_; double* density_mult_; int* is_small_; + int* n_negative_batches_; + int* converged_negative_; int* n_hits_; int* birthday_; OpenMPMutex* lock_; @@ -204,7 +206,10 @@ class SourceRegionHandle { const int temperature_idx() const { return *temperature_idx_; } int& is_small() { return *is_small_; } + int& n_negative_batches() { return *n_negative_batches_; } const int is_small() const { return *is_small_; } + int& converged_negative() { return *converged_negative_; } + const int converged_negative() const { return *converged_negative_; } int& n_hits() { return *n_hits_; } const int n_hits() const { return *n_hits_; } @@ -337,8 +342,19 @@ class SourceRegion { double volume_naive_ {0.0}; //!< Volume as integrated from this iteration only int position_recorded_ {0}; //!< Has the position been recorded yet? int external_source_present_ { - 0}; //!< Is an external source present in this region? - int is_small_ {0}; //!< Is it "small", receiving < 1.5 hits per iteration? + 0}; //!< Is an external source present in this region? + int is_small_ {0}; //!< Is it "small", receiving < 1.5 hits per iteration? + int n_negative_batches_ { + 0}; //!< Number of batches in which this region's flux went negative + //!< before the strict adaptive estimator's non-negativity + //!< enforcement (drives the chronic-negativity demotion) + int converged_negative_ { + 0}; //!< Demote-only flag (adaptive estimator only), evaluated from the + //!< running accumulated flux at the inactive->active transition and + //!< re-evaluated every active batch. 1 = accumulated flux negative + //!< in some group, 2 = strong accumulated feed (latch). Any value + //!< > 0 demotes the region to the naive volume estimator, and once + //!< set the flag is never released. int n_hits_ {0}; //!< Number of total hits (ray crossings) // Mesh that subdivides this source region int mesh_ {C_NONE}; //!< Index in openmc::model::meshes array that subdivides @@ -395,8 +411,10 @@ class SourceRegionContainer { public: //---------------------------------------------------------------------------- // Constructors - SourceRegionContainer(int negroups, bool is_linear) - : negroups_(negroups), is_linear_(is_linear) + SourceRegionContainer( + int negroups, bool is_linear, bool is_adaptive, bool is_strict_adaptive) + : negroups_(negroups), is_linear_(is_linear), is_adaptive_(is_adaptive), + is_strict_adaptive_(is_strict_adaptive) {} SourceRegionContainer() = default; @@ -412,7 +430,13 @@ class SourceRegionContainer { const double density_mult(int64_t sr) const { return density_mult_[sr]; } int& is_small(int64_t sr) { return is_small_[sr]; } + int& n_negative_batches(int64_t sr) { return n_negative_batches_[sr]; } const int is_small(int64_t sr) const { return is_small_[sr]; } + int& converged_negative(int64_t sr) { return converged_negative_[sr]; } + const int converged_negative(int64_t sr) const + { + return converged_negative_[sr]; + } int& n_hits(int64_t sr) { return n_hits_[sr]; } const int n_hits(int64_t sr) const { return n_hits_[sr]; } @@ -572,6 +596,17 @@ class SourceRegionContainer { return scalar_flux_final_[se]; } + double& scalar_flux_t(int64_t sr, int g) + { + return scalar_flux_t_[index(sr, g)]; + } + const double scalar_flux_t(int64_t sr, int g) const + { + return scalar_flux_t_[index(sr, g)]; + } + double& scalar_flux_t(int64_t se) { return scalar_flux_t_[se]; } + const double scalar_flux_t(int64_t se) const { return scalar_flux_t_[se]; } + float& source(int64_t sr, int g) { return source_[index(sr, g)]; } const float source(int64_t sr, int g) const { return source_[index(sr, g)]; } float& source(int64_t se) { return source_[se]; } @@ -639,12 +674,16 @@ class SourceRegionContainer { int64_t n_source_regions_ {0}; int negroups_ {0}; bool is_linear_ {false}; + bool is_adaptive_ {false}; + bool is_strict_adaptive_ {false}; // SoA storage for scalar fields (one item per source region) vector material_; vector temperature_idx_; vector density_mult_; vector is_small_; + vector n_negative_batches_; + vector converged_negative_; vector n_hits_; vector mesh_; vector parent_sr_; @@ -671,6 +710,12 @@ class SourceRegionContainer { vector scalar_flux_old_; vector scalar_flux_new_; vector scalar_flux_final_; + // Running sum of the scalar flux over every batch of the current solve, + // inactive and active. Unlike scalar_flux_final, which holds only the + // active-batch accumulation used for tallies, it is never reset within a + // solve. Allocated only for the adaptive volume estimator, which makes its + // demotion decisions from it. + vector scalar_flux_t_; vector source_; vector external_source_; diff --git a/openmc/settings.py b/openmc/settings.py index 3d75ee80dc2..78bed32b8ff 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -202,8 +202,11 @@ class Settings: specified by a :class:`openmc.SourceBase` object. :volume_estimator: Choice of volume estimator for the random ray solver. Options are - 'naive', 'simulation_averaged', or 'hybrid'. - The default is 'hybrid'. + 'naive', 'simulation_averaged', 'hybrid', 'adaptive', + 'strict_adaptive', or 'auto'. The default is 'auto', which + selects 'adaptive' for standard solves and 'strict_adaptive' for + solves whose results feed variance reduction (weight window + generation and adjoint workflows). :source_shape: Assumed shape of the source distribution within each source region. Options are 'flat' (default), 'linear', or 'linear_xy'. @@ -1416,7 +1419,8 @@ def random_ray(self, random_ray: dict): elif key == 'volume_estimator': cv.check_value('volume estimator', value, ('naive', 'simulation_averaged', - 'hybrid')) + 'hybrid', 'adaptive', 'strict_adaptive', + 'auto')) elif key == 'source_shape': cv.check_value('source shape', value, ('flat', 'linear', 'linear_xy')) diff --git a/src/random_ray/flat_source_domain.cpp b/src/random_ray/flat_source_domain.cpp index a2434a901dc..f3567e6f9db 100644 --- a/src/random_ray/flat_source_domain.cpp +++ b/src/random_ray/flat_source_domain.cpp @@ -28,7 +28,9 @@ namespace openmc { // Static Variable Declarations RandomRayVolumeEstimator FlatSourceDomain::volume_estimator_ { - RandomRayVolumeEstimator::HYBRID}; + RandomRayVolumeEstimator::AUTO}; +RandomRayVolumeEstimator FlatSourceDomain::resolved_volume_estimator_ { + RandomRayVolumeEstimator::AUTO}; bool FlatSourceDomain::volume_normalized_flux_tallies_ {false}; bool FlatSourceDomain::adjoint_requested_ {false}; RandomRaySolve FlatSourceDomain::solve_ {RandomRaySolve::FORWARD}; @@ -56,7 +58,11 @@ FlatSourceDomain::FlatSourceDomain() : negroups_(data::mg.num_energy_groups_) // Initialize source regions. bool is_linear = RandomRay::source_shape_ != RandomRaySourceShape::FLAT; - source_regions_ = SourceRegionContainer(negroups_, is_linear); + bool is_adaptive = is_adaptive_family(resolved_volume_estimator_); + bool is_strict_adaptive = + resolved_volume_estimator_ == RandomRayVolumeEstimator::STRICT_ADAPTIVE; + source_regions_ = SourceRegionContainer( + negroups_, is_linear, is_adaptive, is_strict_adaptive); // Initialize tally volumes if (volume_normalized_flux_tallies_) { @@ -103,6 +109,148 @@ void FlatSourceDomain::accumulate_iteration_flux() } } +// Demotion step for the adaptive volume estimator (no-op for the +// others). Rather than reacting to per-iteration negatives, this estimator +// runs the unmodified simulation-averaged update and accumulates every +// batch's flux into a running sum (scalar_flux_t, kept separate from the +// active-only tally accumulator), from which it makes two demotion +// decisions: +// +// 1. Accumulated-negative (sign): any region whose accumulated flux is +// negative in any group is demoted to the naive (iteration) volume +// estimator, a positively weighted estimator that cannot go negative +// with a non-negative source. During the active phase the test also +// watches the active-only tally accumulation (scalar_flux_final), the +// quantity tally means are actually computed from. Because the decision +// is made on accumulated means rather than on individual fluctuations, +// the lower tail of the noise distribution is not clipped, so regions +// that are merely noisy (and average non-negative) are left unbiased. +// +// 2. Strong-feed latch: any region whose flux-independent feed (cross-group +// in-scatter, fission, and external source), evaluated from the same +// accumulated flux, exceeds ADAPTIVE_VOLUME_KAPPA times its own +// accumulated flux in any group is likewise demoted. This is the same +// physical condition the per-iteration strong-source test targets, +// decided from accumulated data: the per-iteration test, evaluated on +// noisy iterates, cannot fire in the joint excursion where a bad +// iteration drags a region's source and flux negative together, so a +// strong region would otherwise ride such excursions on unprotected +// simulation-averaged updates and can accumulate a negative window +// average. The latch removes the whole strong-feed class. A region with +// no cross-group or external feed can never latch, so sign-locked (e.g. +// one-group) noise cannot cause demotion through this path. +// +// Both conditions are first decided at the inactive->active transition and +// then re-evaluated every active batch as the accumulation keeps growing. +// Decisions are demote-only: a set flag is never released, so the estimator +// choice cannot churn with active-batch noise (a release/re-demote cycle +// conditioned on tallied iterations would bias the tallies), and a marginal +// region whose accumulated ratio converges below the threshold is never +// eroded into demotion by continued re-evaluation. The active-phase +// re-evaluation exists for solves whose inactive phase is too short to +// converge deep regions: there the transition-time decision alone misses +// chronically unstable regions whose accumulated flux only turns negative +// (or whose feed ratio only crosses the threshold) after active batches +// begin, and an escapee left on unprotected simulation-averaged updates can +// corrupt the solution far beyond its own boundary through scattering +// feedback. +// +// The decisions are recorded in converged_negative (1 = sign, 2 = latch; +// > 0 == demoted), consumed by the volume switch and miss treatment in +// add_source_to_scalar_flux and by the linear-source gradient fallback. +void FlatSourceDomain::demotion_step() +{ + if (!is_adaptive_family(resolved_volume_estimator_)) + return; + +#pragma omp parallel for + for (int64_t se = 0; se < n_source_elements(); se++) { + source_regions_.scalar_flux_t(se) += source_regions_.scalar_flux_new(se); + } + + // Decisions start on the last inactive batch and continue every active + // batch thereafter. + if (simulation::current_batch < settings::n_inactive) + return; + +#pragma omp parallel for + for (int64_t sr = 0; sr < n_source_regions(); sr++) { + // Demote-only: settled regions are never re-evaluated or released. + if (source_regions_.converged_negative(sr) > 0) + continue; + bool negative = false; + for (int g = 0; g < negroups_; g++) { + if (source_regions_.scalar_flux_t(sr, g) < 0.0) { + negative = true; + break; + } + } + // During the active phase, a negative accumulated tally flux + // (scalar_flux_final, the active-only sum that tally means are computed + // from) also demotes, as a region with a strong positive inactive + // accumulation can hold the running sum positive while the active-only + // sum that is actually reported goes negative. This branch fires only + // when the reported mean has already lost positivity, so it clips + // realized-negative outcomes rather than one tail of a healthy region's + // noise. + if (!negative && simulation::current_batch > settings::n_inactive) { + for (int g = 0; g < negroups_; g++) { + if (source_regions_.scalar_flux_final(sr, g) < 0.0) { + negative = true; + break; + } + } + } + bool latched = false; + int material = source_regions_.material(sr); + if (!negative && material != MATERIAL_VOID) { + int temp = source_regions_.temperature_idx(sr); + const int material_offset = (material * ntemperature_ + temp) * negroups_; + const int scatter_offset = + (material * ntemperature_ + temp) * negroups_ * negroups_; + double inverse_k_eff = 1.0 / k_eff_; + for (int g = 0; g < negroups_ && !latched; g++) { + double feed = 0.0; + double chi = chi_[material_offset + g]; + for (int gp = 0; gp < negroups_; gp++) { + double phi = std::max(source_regions_.scalar_flux_t(sr, gp), 0.0); + if (gp != g) { + feed += sigma_s_[scatter_offset + g * negroups_ + gp] * phi; + } + if (settings::create_fission_neutrons) { + feed += + chi * nu_sigma_f_[material_offset + gp] * phi * inverse_k_eff; + } + } + double sigma_t = sigma_t_[material_offset + g]; + double q_indep = feed / sigma_t; + // The external source arrays are only allocated in fixed source + // mode, so the external term must not be read in an eigenvalue + // solve (where no external sources exist). The external source is a + // per-batch quantity while the flux is an accumulated one, so the + // term is scaled by the number of accumulated batches. + if (settings::run_mode == RunMode::FIXED_SOURCE) { + q_indep += + simulation::current_batch * source_regions_.external_source(sr, g); + } + if (q_indep > ADAPTIVE_VOLUME_KAPPA * + std::max(source_regions_.scalar_flux_t(sr, g), 0.0)) { + latched = true; + } + } + } + if (negative) { + source_regions_.converged_negative(sr) = 1; + } else if (latched) { + source_regions_.converged_negative(sr) = 2; + } + } + // No separate decision-count bookkeeping is needed here: demote-only flags + // can only accumulate, so the final-batch by-cause snapshot in + // add_source_to_scalar_flux (which counts them with first priority) + // reports the settled decisions exactly. +} + void FlatSourceDomain::update_single_neutron_source(SourceRegionHandle& srh) { // Reset all source regions to zero (important for void regions) @@ -194,6 +342,24 @@ void FlatSourceDomain::normalize_scalar_flux_and_volumes( } } +// The additive term of the flux update for a source region and group. A +// material region adds its reduced source q/Sigma_t. A void region has no +// such term and instead adds a bounded contribution from its external +// source, which is nonzero only in fixed source mode. The same term is used +// by the strict estimator's rescue, which rescales only the transport part +// of an update, so the two cannot drift apart. +double FlatSourceDomain::flux_additive_term(int64_t sr, int g) const +{ + if (source_regions_.material(sr) == MATERIAL_VOID) { + if (settings::run_mode == RunMode::FIXED_SOURCE) { + return 0.5f * source_regions_.external_source(sr, g) * + source_regions_.volume_sq(sr); + } + return 0.0; + } + return source_regions_.source(sr, g); +} + void FlatSourceDomain::set_flux_to_flux_plus_source( int64_t sr, double volume, int g) { @@ -201,18 +367,72 @@ void FlatSourceDomain::set_flux_to_flux_plus_source( int temp = source_regions_.temperature_idx(sr); if (material == MATERIAL_VOID) { source_regions_.scalar_flux_new(sr, g) /= volume; - if (settings::run_mode == RunMode::FIXED_SOURCE) { - source_regions_.scalar_flux_new(sr, g) += - 0.5f * source_regions_.external_source(sr, g) * - source_regions_.volume_sq(sr); - } } else { double sigma_t = sigma_t_[(material * ntemperature_ + temp) * negroups_ + g] * source_regions_.density_mult(sr); source_regions_.scalar_flux_new(sr, g) /= (sigma_t * volume); - source_regions_.scalar_flux_new(sr, g) += source_regions_.source(sr, g); } + source_regions_.scalar_flux_new(sr, g) += flux_additive_term(sr, g); +} + +// Applies the "diagonal stabilization" technique developed by Gunow et al. +// to one flux iterate: +// +// Geoffrey Gunow, Benoit Forget, Kord Smith, Stabilization of multi-group +// neutron transport with transport-corrected cross-sections, Annals of Nuclear +// Energy, Volume 126, 2019, Pages 211-219, ISSN 0306-4549, +// https://doi.org/10.1016/j.anucene.2018.10.036. +// +// Returns the given iterate unchanged unless the region's within-group +// scattering cross section for the group is negative, in which case the +// stabilized iterate is returned. The stabilization is part of the flux +// update rather than a separate pass, so that every candidate value the +// update considers, including the strict estimator's rescued and floored +// candidates, is assessed in stabilized form. With transport-corrected +// cross sections a raw iterate can legitimately be negative and stabilize +// to a positive value, and a positivity fixup applied to the raw value +// would instead freeze the iteration, since the previous iterate is a +// fixed point of the stabilization. +double FlatSourceDomain::stabilized_flux( + int64_t sr, int g, double phi_new) const +{ + // Nothing to do if all in-group scattering cross sections are positive + if (!is_transport_stabilization_needed_) { + return phi_new; + } + int material = source_regions_.material(sr); + if (material == MATERIAL_VOID) { + return phi_new; + } + int temp = source_regions_.temperature_idx(sr); + double density_mult = source_regions_.density_mult(sr); + + // Only apply stabilization if the diagonal (in-group) scattering XS is + // negative + double sigma_s = + sigma_s_[((material * ntemperature_ + temp) * negroups_ + g) * negroups_ + + g] * + density_mult; + if (sigma_s >= 0.0) { + return phi_new; + } + double sigma_t = + sigma_t_[(material * ntemperature_ + temp) * negroups_ + g] * density_mult; + double phi_old = source_regions_.scalar_flux_old(sr, g); + + // Equation 18 in the above Gunow et al. 2019 paper. For a default + // rho of 1.0, this ensures there are no negative diagonal elements + // in the iteration matrix. A lesser rho could be used (or exposed + // as a user input parameter) to reduce the negative impact on + // convergence rate though would need to be experimentally tested to see + // if it doesn't become unstable. rho = 1.0 is good as it gives the + // highest assurance of stability, and the impacts on convergence rate + // are pretty mild. + double D = diagonal_stabilization_rho_ * sigma_s / sigma_t; + + // Equation 16 in the above Gunow et al. 2019 paper + return (phi_new - D * phi_old) / (1.0 - D); } void FlatSourceDomain::set_flux_to_old_flux(int64_t sr, int g) @@ -226,14 +446,67 @@ void FlatSourceDomain::set_flux_to_source(int64_t sr, int g) source_regions_.scalar_flux_new(sr, g) = source_regions_.source(sr, g); } +bool FlatSourceDomain::region_has_strong_source( + const float* reduced_source, const double* flux_old, bool include_ratio) const +{ + for (int g = 0; g < negroups_; g++) { + double src = reduced_source[g]; + // A negative reduced source counts as strong only when the region's own + // previous flux is non-negative. That is the transport-corrected (TCP0) + // signature, where negative within-group scattering drives the source + // negative independently of the flux. When the previous flux is itself + // negative, a negative source is just the sign-locked image of that + // fluctuation (exactly so in one-group problems, where q = c*phi + + // q_external), and reacting to it iteration-by-iteration would condition + // the estimator choice on the sign of the noise, which is the bias the + // converged-negative demotion exists to avoid. Chronically negative + // regions are handled by that demotion instead. + if (src < 0.0 && flux_old[g] >= 0.0) { + return true; + } + // The ratio condition is consulted only while the source is converging + // (include_ratio is false in the active batches): evaluated on noisy + // single-batch iterates, it demotes a churning population of regions + // whose converged ratios are below kappa, and that noise-conditioned, + // one-sided treatment biases the accumulated tallies. Once the + // transition decisions are made from the converged flux, the stable + // classifications (the strong-feed latch, the converged-negative sign + // demotion, and the hit-starved treatment) govern the tallied batches. + if (include_ratio && + src > ADAPTIVE_VOLUME_KAPPA * std::max(flux_old[g], 0.0)) { + return true; + } + } + return false; +} + // Combine transport flux contributions and flat source contributions from the // previous iteration to generate this iteration's estimate of scalar flux. int64_t FlatSourceDomain::add_source_to_scalar_flux() { int64_t n_hits = 0; double inverse_batch = 1.0 / simulation::current_batch; - -#pragma omp parallel for reduction(+ : n_hits) + int64_t n_naive = 0; + int64_t n_latch = 0; + int64_t n_strong = 0; + int64_t n_sign = 0; + int64_t n_small = 0; + bool final_iteration = (simulation::current_batch == settings::n_batches); + // The adaptive estimator uses the proactive strong-source (kappa) test, the + // demote-to-naive volume switch, and the previous-flux miss treatment, with + // demote-only decisions made from the running accumulated flux (recorded + // as a flag in converged_negative by demotion_step). + const bool is_adaptive = is_adaptive_family(resolved_volume_estimator_); + // The strict adaptive estimator additionally enforces non-negativity on + // the flux iterates each batch (see the enforcement step below). + const bool is_strict = + resolved_volume_estimator_ == RandomRayVolumeEstimator::STRICT_ADAPTIVE; + int64_t n_rescued = 0; + int64_t n_floored = 0; + int64_t n_chronic = 0; + +#pragma omp parallel for reduction(+ : n_hits, n_naive, n_latch, n_strong, \ + n_sign, n_small, n_rescued, n_floored, n_chronic) for (int64_t sr = 0; sr < n_source_regions(); sr++) { double volume_simulation_avg = source_regions_.volume(sr); @@ -252,54 +525,174 @@ int64_t FlatSourceDomain::add_source_to_scalar_flux() source_regions_.is_small(sr) = 0; } - // The volume treatment depends on the volume estimator type - // and whether or not an external source is present in the cell. - double volume; - switch (volume_estimator_) { + // Determine if the source region has a "strong" inhomogeneous source, + // defined as any group whose reduced source greatly exceeds the previous + // iteration's scalar flux. In that condition the cell sits far below its + // own infinite-medium flux (q/Sigma_t), which arises when an optically + // thin cell holds a source that does not derive from its own local flux + // (an external source, or in-scatter from other groups). The + // flux update in such cells is a near-cancellation of the transport term + // against q/Sigma_t, which is only exact when the volumes used by the two + // terms are consistent. These cells therefore require the naive + // (iteration) volume estimator and the previous-flux miss treatment to + // avoid error + // terms proportional to (q/Sigma_t) * (1 - V_iteration/V_average) that + // can greatly exceed the physical flux. + // + // A reduced source that is itself negative is also treated as strong. This + // arises under transport-corrected (e.g. TCP0) cross sections, whose + // within-group scattering term can be negative, driving q/Sigma_t below + // zero even for a non-negative flux. It can also arise transiently from a + // negative previous-iteration flux, which the estimator permits by design + // (individual iterations are never modified). The diagonal (Gunow) + // stabilization keeps the TCP0 iteration convergent but acts on the flux, + // not on the source sign, so such regions still need the consistent + // (naive) volume and previous-flux miss treatment to keep a negative + // source from depositing negative flux through the miss path. In a normal + // slowing-down spectrum the positive in-scatter from faster groups + // dominates the negative within-group term, so in practice this condition + // rarely fires. + // + // Only the adaptive estimator consults the strong-source flag, so the + // other estimators skip the test (and its end-of-run report) entirely. + // Void (and effectively-void, sub-MINIMUM_MACRO_XS) regions are also + // excluded. They carry no q/Sigma_t term, as their flux is the streaming + // tally plus a bounded external contribution, so the near-cancellation + // the test guards against cannot occur and demoting them to the naive + // volume would only add ratio bias. This matches the linear domain, which + // already gates its strong-source gradient fallback on MATERIAL_VOID. + bool strong_source = + is_adaptive && source_regions_.material(sr) != MATERIAL_VOID && + region_has_strong_source(&source_regions_.source(sr, 0), + &source_regions_.scalar_flux_old(sr, 0), + simulation::current_batch <= settings::n_inactive); + // Per-region demotion reasons, all g-independent. The hit-starved + // (small) flag is re-evaluated every iteration. The strong-source flag + // is also re-evaluated every iteration, though in the active batches it + // reduces to the negative-source (TCP0) condition, as the stable + // accumulated-flux decisions govern there instead. converged_neg is the + // demote-only flag set from the running accumulated flux by + // demotion_step. The external-source flag drives only the hybrid policy + // and the default miss treatment, since the adaptive estimator catches a + // low-cross-section external region through the kappa strong-source test + // (during the inactive batches) and the strong-feed latch (thereafter), + // its external term being folded into q/Sigma_t. + bool external = source_regions_.external_source_present(sr); + bool small = source_regions_.is_small(sr); + int conv_flag = is_adaptive ? source_regions_.converged_negative(sr) : 0; + bool converged_neg = conv_flag > 0; + + // Every estimator reduces to two g-independent per-region decisions: + // 1. which volume to use on a hit (the simulation-averaged volume, + // unless the region is demoted to the naive (iteration) volume) + // 2. what to substitute on a miss (the reduced source by default, or + // the previous iterate) + // The previous-flux miss treatment is needed wherever assigning the bare + // reduced source q/Sigma_t to a missed region would bias it, as a low + // cross section region would otherwise deposit its full infinite-medium + // flux every time it is missed. Hybrid keys this on the external-source + // flag. The adaptive estimator instead extends the previous-flux + // treatment to every region it demotes, which (through the kappa test) + // already covers any region whose q/Sigma_t greatly exceeds its flux, + // external or not. Both decisions are made once here so the per-group + // loop stays estimator-agnostic. + bool use_naive_volume = false; + bool use_old_flux_on_miss = external; + switch (resolved_volume_estimator_) { case RandomRayVolumeEstimator::NAIVE: - volume = volume_iteration; + use_naive_volume = true; break; case RandomRayVolumeEstimator::SIMULATION_AVERAGED: - volume = volume_simulation_avg; break; case RandomRayVolumeEstimator::HYBRID: - if (source_regions_.external_source_present(sr) || - source_regions_.is_small(sr)) { - volume = volume_iteration; - } else { - volume = volume_simulation_avg; - } + use_naive_volume = external || small; + break; + case RandomRayVolumeEstimator::ADAPTIVE: + case RandomRayVolumeEstimator::STRICT_ADAPTIVE: + use_naive_volume = small || strong_source || converged_neg; + use_old_flux_on_miss = use_naive_volume; break; default: fatal_error("Invalid volume estimator type"); } + double volume = use_naive_volume ? volume_iteration : volume_simulation_avg; + + // On the final iteration, classify the demoted (naive-volume) regions by + // cause for the end-of-simulation report. The causes are mutually + // exclusive and assigned in priority order, so they sum to the total. + // The accumulated-flux demotions are counted first, as their demote-only + // flags can only accumulate and so equal the decisions settled by the + // final batch. The per-batch causes count only the remainder. + if (final_iteration && is_adaptive && use_naive_volume) { + n_naive++; + if (conv_flag == 2) { + n_latch++; + } else if (conv_flag == 1) { + n_sign++; + } else if (conv_flag == 3) { + n_chronic++; + } else if (strong_source) { + n_strong++; + } else if (small) { + n_small++; + } + } + bool region_rescued = false; + bool region_floored = false; for (int g = 0; g < negroups_; g++) { - // There are three scenarios we need to consider: if (volume_iteration > 0.0) { - // 1. If the FSR was hit this iteration, then the new flux is equal to - // the flat source from the previous iteration plus the contributions - // from rays passing through the source region (computed during the - // transport sweep) + // Hit this iteration: the flat source from the previous iteration plus + // this iteration's transport contribution, normalized by the chosen + // volume, then stabilized. set_flux_to_flux_plus_source(sr, volume, g); + double raw = source_regions_.scalar_flux_new(sr, g); + double phi = stabilized_flux(sr, g, raw); + // The strict adaptive estimator applies a per-batch fixup to + // negative flux iterates, assessed on the stabilized value. First + // the flux is rescued by rescaling the transport term from the + // volume used to the batch's own volume, algebraically reproducing + // the naive-volume update, with the rescued candidate stabilized in + // turn. If it is still negative (or the region already used the + // batch volume), it is floored at the previous iterate, which the + // stabilization leaves unchanged. A value-level fixup is needed + // here because demotion alone cannot prevent a region from + // inheriting a negative excursion through in-scatter from + // not-yet-demoted neighbors. The price is a small conservative + // (one-sided clip) bias, which is why the strict estimator is not + // the standard-solve default. Linear-source flux moments are left + // untouched, as demoted and hit-starved regions already fall back to + // flat shapes. + if (is_strict && phi < 0.0) { + if (volume != volume_iteration) { + double additive = flux_additive_term(sr, g); + double rescued = + (raw - additive) * (volume / volume_iteration) + additive; + phi = stabilized_flux(sr, g, rescued); + region_rescued = true; + } + if (phi < 0.0) { + phi = source_regions_.scalar_flux_old(sr, g); + region_floored = true; + } + } + source_regions_.scalar_flux_new(sr, g) = phi; } else if (volume_simulation_avg > 0.0) { - // 2. If the FSR was not hit this iteration, but has been hit some - // previous iteration, then we need to make a choice about what - // to do. Naively we will usually want to set the flux to be equal - // to the reduced source. However, in fixed source problems where - // there is a strong external source present in the cell, and where - // the cell has a very low cross section, this approximation will - // cause a huge upward bias in the flux estimate of the cell (in these - // conditions, the flux estimate can be orders of magnitude too large). - // Thus, to avoid this bias, if any external source is present - // in the cell we will use the previous iteration's flux estimate. This - // injects a small degree of correlation into the simulation, but this - // is going to be trivial when the miss rate is a few percent or less. - if (source_regions_.external_source_present(sr)) { + // Missed this iteration but hit previously: substitute per the miss + // policy decided above (the previous iterate, or the reduced source), + // then stabilize. + if (use_old_flux_on_miss) { set_flux_to_old_flux(sr, g); } else { set_flux_to_source(sr, g); } + source_regions_.scalar_flux_new(sr, g) = + stabilized_flux(sr, g, source_regions_.scalar_flux_new(sr, g)); + } else { + // Never hit: the iterate stays at its reset value, stabilized like + // every other element. + source_regions_.scalar_flux_new(sr, g) = + stabilized_flux(sr, g, source_regions_.scalar_flux_new(sr, g)); } // Halt if NaN implosion is detected if (!std::isfinite(source_regions_.scalar_flux_new(sr, g))) { @@ -309,6 +702,42 @@ int64_t FlatSourceDomain::add_source_to_scalar_flux() "the source region mesh."); } } + // Chronic-negativity demotion (strict adaptive only). The + // non-negativity floor prevents a chronically noisy region's + // accumulated flux from ever going negative, masking the very signal + // the accumulated sign demotion detects. Left alone, such a region + // would be clipped every batch, biasing its flux upward. Counting the + // batches that needed the fixup restores the escape, as after a few + // events the region is demoted to the naive volume and previous-flux + // miss treatment, where clipping + // is no longer needed. + if (is_strict && (region_rescued || region_floored)) { + int n = ++source_regions_.n_negative_batches(sr); + double threshold = + std::max(static_cast(NEGATIVE_FLUX_DEMOTION_MIN_COUNT), + NEGATIVE_FLUX_DEMOTION_RATE * simulation::current_batch); + if (n >= threshold && source_regions_.converged_negative(sr) == 0) { + source_regions_.converged_negative(sr) = 3; + } + } + if (final_iteration) { + n_rescued += region_rescued; + n_floored += region_floored; + } + } + + // Store the final-iteration treatment snapshot for reporting (adaptive only; + // the other estimators do not produce a by-cause naive-treatment breakdown) + if (final_iteration && is_adaptive) { + n_final_naive_ = n_naive; + n_final_latch_ = n_latch; + n_final_strong_ = n_strong; + n_final_sign_ = n_sign; + n_final_small_ = n_small; + n_final_chronic_ = n_chronic; + n_final_rescued_ = n_rescued; + n_final_floored_ = n_floored; + final_stats_valid_ = true; } // Return the number of source regions that were hit this iteration @@ -1751,62 +2180,6 @@ void FlatSourceDomain::finalize_discovered_source_regions() discovered_source_regions_.clear(); } -// This is the "diagonal stabilization" technique developed by Gunow et al. in: -// -// Geoffrey Gunow, Benoit Forget, Kord Smith, Stabilization of multi-group -// neutron transport with transport-corrected cross-sections, Annals of Nuclear -// Energy, Volume 126, 2019, Pages 211-219, ISSN 0306-4549, -// https://doi.org/10.1016/j.anucene.2018.10.036. -void FlatSourceDomain::apply_transport_stabilization() -{ - // Don't do anything if all in-group scattering - // cross sections are positive - if (!is_transport_stabilization_needed_) { - return; - } - - // Apply the stabilization factor to all source elements -#pragma omp parallel for - for (int64_t sr = 0; sr < n_source_regions(); sr++) { - int material = source_regions_.material(sr); - int temp = source_regions_.temperature_idx(sr); - double density_mult = source_regions_.density_mult(sr); - if (material == MATERIAL_VOID) { - continue; - } - for (int g = 0; g < negroups_; g++) { - // Only apply stabilization if the diagonal (in-group) scattering XS is - // negative - double sigma_s = - sigma_s_[((material * ntemperature_ + temp) * negroups_ + g) * - negroups_ + - g] * - density_mult; - if (sigma_s < 0.0) { - double sigma_t = - sigma_t_[(material * ntemperature_ + temp) * negroups_ + g] * - density_mult; - double phi_new = source_regions_.scalar_flux_new(sr, g); - double phi_old = source_regions_.scalar_flux_old(sr, g); - - // Equation 18 in the above Gunow et al. 2019 paper. For a default - // rho of 1.0, this ensures there are no negative diagonal elements - // in the iteration matrix. A lesser rho could be used (or exposed - // as a user input parameter) to reduce the negative impact on - // convergence rate though would need to be experimentally tested to see - // if it doesn't become unstable. rho = 1.0 is good as it gives the - // highest assurance of stability, and the impacts on convergence rate - // are pretty mild. - double D = diagonal_stabilization_rho_ * sigma_s / sigma_t; - - // Equation 16 in the above Gunow et al. 2019 paper - source_regions_.scalar_flux_new(sr, g) = - (phi_new - D * phi_old) / (1.0 - D); - } - } - } -} - // Determines the base source region index (i.e., a material filled cell // instance) that corresponds to a particular location in the geometry. Requires // that the "gs" object passed in has already been initialized and has called diff --git a/src/random_ray/linear_source_domain.cpp b/src/random_ray/linear_source_domain.cpp index b4701ed1fa9..229752ca7f9 100644 --- a/src/random_ray/linear_source_domain.cpp +++ b/src/random_ray/linear_source_domain.cpp @@ -111,6 +111,28 @@ void LinearSourceDomain::update_single_neutron_source(SourceRegionHandle& srh) srh.source(g) += srh.external_source(g); } } + + // Under the adaptive volume estimator, demoted regions also fall back to a + // flat source representation, extending the flat-source fallback already + // applied to hit-starved (small) regions so that demotion is uniform in + // effect. For strong-source regions the reduced source greatly exceeds the + // scalar flux, so the flat-source cancellation must be exact, while the + // gradient terms attenuate segments against the local rather than the flat + // source, introducing per-iteration noise at the gradient scale that the + // volume choice cannot cancel. The same reasoning applies to regions + // demoted from the accumulated flux (converged_negative > 0). A negative + // accumulated flux means the fitted gradients carry no meaningful shape + // information, and a latched strong feed is the gradient-scale noise + // hazard the strong-source fallback above exists for. + if (is_adaptive_family(resolved_volume_estimator_) && + material != MATERIAL_VOID && + (srh.converged_negative() > 0 || + region_has_strong_source(&srh.source(0), &srh.scalar_flux_old(0), + simulation::current_batch <= settings::n_inactive))) { + for (int g = 0; g < negroups_; g++) { + srh.source_gradients(g) = {0.0, 0.0, 0.0}; + } + } } void LinearSourceDomain::normalize_scalar_flux_and_volumes( diff --git a/src/random_ray/random_ray_simulation.cpp b/src/random_ray/random_ray_simulation.cpp index 0a1ed0381d9..29d98bf1dbd 100644 --- a/src/random_ray/random_ray_simulation.cpp +++ b/src/random_ray/random_ray_simulation.cpp @@ -287,7 +287,8 @@ void validate_random_ray_inputs() void openmc_finalize_random_ray() { - FlatSourceDomain::volume_estimator_ = RandomRayVolumeEstimator::HYBRID; + FlatSourceDomain::volume_estimator_ = RandomRayVolumeEstimator::AUTO; + FlatSourceDomain::resolved_volume_estimator_ = RandomRayVolumeEstimator::AUTO; FlatSourceDomain::volume_normalized_flux_tallies_ = false; FlatSourceDomain::adjoint_requested_ = false; FlatSourceDomain::solve_ = RandomRaySolve::FORWARD; @@ -346,7 +347,19 @@ void RandomRaySimulation::prepare_fw_fixed_sources_adjoint() // Prepare adjoint fixed sources using forward flux domain_->source_regions_.adjoint_reset(); if (settings::run_mode == RunMode::FIXED_SOURCE) { + // Consumes the accumulated forward flux (and zeroes it as it goes), so + // the adjoint solve starts from a clean accumulator. domain_->set_fw_adjoint_sources(); + } else { + // In eigenvalue mode there are no fixed adjoint sources to derive from + // the forward flux, but the accumulated forward flux must still be + // cleared so that the adjoint solve's active accumulation starts from a + // clean array. Otherwise any consumer of the final flux would mix + // forward and adjoint modes. +#pragma omp parallel for + for (int64_t se = 0; se < domain_->n_source_elements(); se++) { + domain_->source_regions_.scalar_flux_final(se) = 0.0; + } } } @@ -448,12 +461,10 @@ void RandomRaySimulation::simulate() domain_->normalize_scalar_flux_and_volumes( settings::n_particles * RandomRay::distance_active_); - // Add source to scalar flux, compute number of FSR hits + // Add source to scalar flux (applying any transport stabilization + // factors), compute number of FSR hits int64_t n_hits = domain_->add_source_to_scalar_flux(); - // Apply transport stabilization factors - domain_->apply_transport_stabilization(); - if (settings::run_mode == RunMode::EIGENVALUE) { // Compute random ray k-eff domain_->compute_k_eff(); @@ -474,6 +485,11 @@ void RandomRaySimulation::simulate() domain_->random_ray_tally(); } + // For the adaptive estimator, accumulate this batch's flux into the + // running sum and update (demote-only) which regions use the naive + // volume estimator (no-op for the other estimators). + domain_->demotion_step(); + // Set phi_old = phi_new domain_->flux_swap(); @@ -594,7 +610,7 @@ void RandomRaySimulation::print_results_random_ray( total_integrations / settings::n_batches); std::string estimator; - switch (domain_->volume_estimator_) { + switch (FlatSourceDomain::resolved_volume_estimator_) { case RandomRayVolumeEstimator::SIMULATION_AVERAGED: estimator = "Simulation Averaged"; break; @@ -604,10 +620,59 @@ void RandomRaySimulation::print_results_random_ray( case RandomRayVolumeEstimator::HYBRID: estimator = "Hybrid"; break; + case RandomRayVolumeEstimator::ADAPTIVE: + estimator = "Adaptive"; + break; + case RandomRayVolumeEstimator::STRICT_ADAPTIVE: + estimator = "Strict Adaptive"; + break; default: fatal_error("Invalid volume estimator type"); } + if (FlatSourceDomain::volume_estimator_ == RandomRayVolumeEstimator::AUTO) { + estimator += " (auto)"; + } fmt::print(" Volume Estimator Type = {}\n", estimator); + if (domain_->final_stats_valid_) { + double inv = 100.0 / domain_->n_source_regions(); + // Single summary at default verbosity: every source region that + // received the naive volume treatment in the final batch, for any + // reason (the demote-only decisions made from the accumulated flux + // plus that batch's per-iteration demotions). + fmt::print(" Number of Naive Demotions = {} SRs ({:.4f}%)\n", + domain_->n_final_naive_, domain_->n_final_naive_ * inv); + // The per-cause diagnostic breakdown is developer-facing, so it is + // printed at verbosity 8, above the default (7) but below the + // per-particle output (9). + // The causes are mutually exclusive and sum to the total above: + // "accumulated" causes are the demote-only decisions made from the + // running accumulated flux (from the inactive->active transition + // onward), "per batch" causes are re-evaluated each batch and reported + // for the final batch. + if (settings::verbosity >= 8) { + fmt::print(" Strong source (accumulated) = {} SRs ({:.4f}%)\n", + domain_->n_final_latch_, domain_->n_final_latch_ * inv); + fmt::print(" Strong source (per batch) = {} SRs ({:.4f}%)\n", + domain_->n_final_strong_, domain_->n_final_strong_ * inv); + fmt::print(" Negative flux (accumulated) = {} SRs ({:.4f}%)\n", + domain_->n_final_sign_, domain_->n_final_sign_ * inv); + fmt::print(" Hit-starved (per batch) = {} SRs ({:.4f}%)\n", + domain_->n_final_small_, domain_->n_final_small_ * inv); + // The strict adaptive estimator's per-batch non-negativity + // enforcement, reported for the final batch. These overlap the + // partition above rather than extending it: a rescued or floored + // region may or may not also carry the naive treatment. + if (FlatSourceDomain::resolved_volume_estimator_ == + RandomRayVolumeEstimator::STRICT_ADAPTIVE) { + fmt::print(" Chronic negative (per batch) = {} SRs ({:.4f}%)\n", + domain_->n_final_chronic_, domain_->n_final_chronic_ * inv); + fmt::print(" Rescued (batch volume) = {} SRs ({:.4f}%)\n", + domain_->n_final_rescued_, domain_->n_final_rescued_ * inv); + fmt::print(" Floored (previous flux) = {} SRs ({:.4f}%)\n", + domain_->n_final_floored_, domain_->n_final_floored_ * inv); + } + } + } std::string adjoint_true = (FlatSourceDomain::solve_ == RandomRaySolve::ADJOINT) ? "ON" : "OFF"; @@ -684,6 +749,26 @@ void openmc_run_random_ray() { using namespace openmc; + // Resolve the volume estimator for this solve, leaving the configured + // setting untouched. "Auto" (the default) maps to a concrete estimator + // based on the type of simulation being performed. Solves whose results + // feed variance reduction (weight window generation, and any adjoint + // workflow, including the forward solve an adjoint source is derived + // from) receive the strict adaptive estimator, whose per-batch fixup of + // negative flux iterates benefits those workflows. All other solves + // receive the unbiased adaptive estimator. + if (FlatSourceDomain::volume_estimator_ == RandomRayVolumeEstimator::AUTO) { + bool positivity_needed = + FlatSourceDomain::adjoint_requested_ || + !variance_reduction::weight_windows_generators.empty(); + FlatSourceDomain::resolved_volume_estimator_ = + positivity_needed ? RandomRayVolumeEstimator::STRICT_ADAPTIVE + : RandomRayVolumeEstimator::ADAPTIVE; + } else { + FlatSourceDomain::resolved_volume_estimator_ = + FlatSourceDomain::volume_estimator_; + } + // Determine which solves to run. If adjoint results are requested and no // user-defined adjoint source is present, an initial forward solve is needed // to construct the adjoint source from the forward flux (FW-CADIS). If the diff --git a/src/random_ray/source_region.cpp b/src/random_ray/source_region.cpp index 78543c5ab53..ab8126f1804 100644 --- a/src/random_ray/source_region.cpp +++ b/src/random_ray/source_region.cpp @@ -12,7 +12,8 @@ namespace openmc { SourceRegionHandle::SourceRegionHandle(SourceRegion& sr) : negroups_(sr.scalar_flux_old_.size()), material_(&sr.material_), temperature_idx_(&sr.temperature_idx_), density_mult_(&sr.density_mult_), - is_small_(&sr.is_small_), n_hits_(&sr.n_hits_), + is_small_(&sr.is_small_), n_negative_batches_(&sr.n_negative_batches_), + converged_negative_(&sr.converged_negative_), n_hits_(&sr.n_hits_), is_linear_(sr.source_gradients_.size() > 0), lock_(&sr.lock_), volume_(&sr.volume_), volume_t_(&sr.volume_t_), volume_sq_(&sr.volume_sq_), volume_sq_t_(&sr.volume_sq_t_), volume_naive_(&sr.volume_naive_), @@ -74,6 +75,12 @@ void SourceRegionContainer::push_back(const SourceRegion& sr) temperature_idx_.push_back(sr.temperature_idx_); density_mult_.push_back(sr.density_mult_); is_small_.push_back(sr.is_small_); + if (is_strict_adaptive_) { + n_negative_batches_.push_back(sr.n_negative_batches_); + } + if (is_adaptive_) { + converged_negative_.push_back(sr.converged_negative_); + } n_hits_.push_back(sr.n_hits_); lock_.push_back(sr.lock_); volume_.push_back(sr.volume_); @@ -102,6 +109,10 @@ void SourceRegionContainer::push_back(const SourceRegion& sr) scalar_flux_old_.push_back(sr.scalar_flux_old_[g]); scalar_flux_new_.push_back(sr.scalar_flux_new_[g]); scalar_flux_final_.push_back(sr.scalar_flux_final_[g]); + // A newly discovered region starts with nothing accumulated + if (is_adaptive_) { + scalar_flux_t_.push_back(0.0); + } source_.push_back(sr.source_[g]); if (settings::run_mode == RunMode::FIXED_SOURCE) { external_source_.push_back(sr.external_source_[g]); @@ -129,6 +140,8 @@ void SourceRegionContainer::assign( temperature_idx_.clear(); density_mult_.clear(); is_small_.clear(); + n_negative_batches_.clear(); + converged_negative_.clear(); n_hits_.clear(); lock_.clear(); volume_.clear(); @@ -153,6 +166,7 @@ void SourceRegionContainer::assign( scalar_flux_old_.clear(); scalar_flux_new_.clear(); scalar_flux_final_.clear(); + scalar_flux_t_.clear(); source_.clear(); external_source_.clear(); @@ -188,6 +202,9 @@ SourceRegionHandle SourceRegionContainer::get_source_region_handle(int64_t sr) handle.temperature_idx_ = &temperature_idx(sr); handle.density_mult_ = &density_mult(sr); handle.is_small_ = &is_small(sr); + handle.n_negative_batches_ = + is_strict_adaptive_ ? &n_negative_batches(sr) : nullptr; + handle.converged_negative_ = is_adaptive_ ? &converged_negative(sr) : nullptr; handle.n_hits_ = &n_hits(sr); handle.is_linear_ = is_linear(); handle.lock_ = &lock(sr); @@ -231,6 +248,8 @@ SourceRegionHandle SourceRegionContainer::get_source_region_handle(int64_t sr) void SourceRegionContainer::adjoint_reset() { std::fill(n_hits_.begin(), n_hits_.end(), 0); + std::fill(converged_negative_.begin(), converged_negative_.end(), 0); + std::fill(n_negative_batches_.begin(), n_negative_batches_.end(), 0); std::fill(volume_.begin(), volume_.end(), 0.0); std::fill(volume_t_.begin(), volume_t_.end(), 0.0); std::fill(volume_sq_.begin(), volume_sq_.end(), 0.0); @@ -253,6 +272,7 @@ void SourceRegionContainer::adjoint_reset() std::fill(scalar_flux_old_.begin(), scalar_flux_old_.end(), 1.0); } std::fill(scalar_flux_new_.begin(), scalar_flux_new_.end(), 0.0); + std::fill(scalar_flux_t_.begin(), scalar_flux_t_.end(), 0.0); std::fill(source_.begin(), source_.end(), 0.0f); std::fill(external_source_.begin(), external_source_.end(), 0.0f); std::fill(source_gradients_.begin(), source_gradients_.end(), diff --git a/src/settings.cpp b/src/settings.cpp index 3e20144f1bd..85d1f428b2c 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -309,6 +309,14 @@ void get_run_parameters(pugi::xml_node node_base) FlatSourceDomain::volume_estimator_ = RandomRayVolumeEstimator::NAIVE; } else if (temp_str == "hybrid") { FlatSourceDomain::volume_estimator_ = RandomRayVolumeEstimator::HYBRID; + } else if (temp_str == "adaptive") { + FlatSourceDomain::volume_estimator_ = + RandomRayVolumeEstimator::ADAPTIVE; + } else if (temp_str == "strict_adaptive") { + FlatSourceDomain::volume_estimator_ = + RandomRayVolumeEstimator::STRICT_ADAPTIVE; + } else if (temp_str == "auto") { + FlatSourceDomain::volume_estimator_ = RandomRayVolumeEstimator::AUTO; } else { fatal_error("Unrecognized volume estimator: " + temp_str); } diff --git a/tests/regression_tests/random_ray_adjoint_fixed_source/adaptive_starved/inputs_true.dat b/tests/regression_tests/random_ray_adjoint_fixed_source/adaptive_starved/inputs_true.dat new file mode 100644 index 00000000000..55f004b680b --- /dev/null +++ b/tests/regression_tests/random_ray_adjoint_fixed_source/adaptive_starved/inputs_true.dat @@ -0,0 +1,248 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 10 + 40 + 20 + + + 100.0 1.0 + + + universe + 1 + + + multi-group + + 500.0 + 100.0 + + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + + true + true + adaptive + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/random_ray_adjoint_fixed_source/adaptive_starved/results_true.dat b/tests/regression_tests/random_ray_adjoint_fixed_source/adaptive_starved/results_true.dat new file mode 100644 index 00000000000..a8b8824e6b9 --- /dev/null +++ b/tests/regression_tests/random_ray_adjoint_fixed_source/adaptive_starved/results_true.dat @@ -0,0 +1,9 @@ +tally 1: +3.923616E+06 +1.276954E+12 +tally 2: +5.338607E+06 +1.444114E+12 +tally 3: +1.722575E+07 +1.493106E+13 diff --git a/tests/regression_tests/random_ray_adjoint_fixed_source/test.py b/tests/regression_tests/random_ray_adjoint_fixed_source/test.py index 6c2790fa093..489d3a70516 100644 --- a/tests/regression_tests/random_ray_adjoint_fixed_source/test.py +++ b/tests/regression_tests/random_ray_adjoint_fixed_source/test.py @@ -1,5 +1,7 @@ import os +import openmc +from openmc.utility_funcs import change_directory from openmc.examples import random_ray_three_region_cube from tests.testing_harness import TolerantPyAPITestHarness @@ -20,3 +22,21 @@ def test_random_ray_adjoint_fixed_source(): model.settings.particles = 500 harness = MGXSTestHarness('statepoint.10.h5', model) harness.main() + + +def test_random_ray_adjoint_fixed_source_adaptive_starved(): + # Ray-starved adaptive case (~20% forward / ~40% adjoint miss rate). The + # adjoint (second) solve makes real end-of-inactive demotion decisions on + # its own accumulated flux, guarding both the adaptive machinery in + # adjoint mode and the clearing of the forward solve's accumulated flux + # between the two solves (which would otherwise swamp the demotion test). + with change_directory('adaptive_starved'): + openmc.reset_auto_ids() + model = random_ray_three_region_cube() + model.settings.random_ray['adjoint'] = True + model.settings.random_ray['volume_estimator'] = 'adaptive' + model.settings.particles = 10 + model.settings.inactive = 20 + model.settings.batches = 40 + harness = MGXSTestHarness('statepoint.40.h5', model) + harness.main() diff --git a/tests/regression_tests/random_ray_cell_density/fs/inputs_true.dat b/tests/regression_tests/random_ray_cell_density/fs/inputs_true.dat index f369bae89f3..9a578087213 100644 --- a/tests/regression_tests/random_ray_cell_density/fs/inputs_true.dat +++ b/tests/regression_tests/random_ray_cell_density/fs/inputs_true.dat @@ -215,6 +215,7 @@ true + hybrid diff --git a/tests/regression_tests/random_ray_cell_density/test.py b/tests/regression_tests/random_ray_cell_density/test.py index 48ebe0baaa2..9e77b4c8d35 100644 --- a/tests/regression_tests/random_ray_cell_density/test.py +++ b/tests/regression_tests/random_ray_cell_density/test.py @@ -39,5 +39,6 @@ def test_random_ray_basic(run_mode): cell.density = 1e3 # Gold file was generated with manually scaled source cross sections. + model.settings.random_ray['volume_estimator'] = 'hybrid' harness = MGXSTestHarness('statepoint.10.h5', model) harness.main() diff --git a/tests/regression_tests/random_ray_diagonal_stabilization/adaptive/inputs_true.dat b/tests/regression_tests/random_ray_diagonal_stabilization/adaptive/inputs_true.dat new file mode 100644 index 00000000000..8012b6c76b5 --- /dev/null +++ b/tests/regression_tests/random_ray_diagonal_stabilization/adaptive/inputs_true.dat @@ -0,0 +1,68 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 20 + 15 + + + -0.63 -0.63 -1 0.63 0.63 1 + + + true + + + multi-group + + + + + -0.63 -0.63 -1.0 0.63 0.63 1.0 + + + + 30.0 + 150.0 + + + + + + linear + 0.5 + adaptive + + + 2 2 + -0.63 -0.63 + 0.63 0.63 + + + diff --git a/tests/regression_tests/random_ray_diagonal_stabilization/adaptive/results_true.dat b/tests/regression_tests/random_ray_diagonal_stabilization/adaptive/results_true.dat new file mode 100644 index 00000000000..57f8c47bbc5 --- /dev/null +++ b/tests/regression_tests/random_ray_diagonal_stabilization/adaptive/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +7.165325E-01 1.371333E-02 diff --git a/tests/regression_tests/random_ray_diagonal_stabilization/homogeneous_adaptive/inputs_true.dat b/tests/regression_tests/random_ray_diagonal_stabilization/homogeneous_adaptive/inputs_true.dat new file mode 100644 index 00000000000..30c71486a91 --- /dev/null +++ b/tests/regression_tests/random_ray_diagonal_stabilization/homogeneous_adaptive/inputs_true.dat @@ -0,0 +1,48 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + eigenvalue + 100 + 8 + 3 + multi-group + + 30.0 + 200.0 + + + + 0.0 0.0 0.0 10.0 10.0 10.0 + + + + + + + + + adaptive + + + 4 4 4 + 0.0 0.0 0.0 + 10.0 10.0 10.0 + + + diff --git a/tests/regression_tests/random_ray_diagonal_stabilization/homogeneous_adaptive/results_true.dat b/tests/regression_tests/random_ray_diagonal_stabilization/homogeneous_adaptive/results_true.dat new file mode 100644 index 00000000000..530d8b49018 --- /dev/null +++ b/tests/regression_tests/random_ray_diagonal_stabilization/homogeneous_adaptive/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +5.484375E-01 2.130966E-02 diff --git a/tests/regression_tests/random_ray_diagonal_stabilization/homogeneous_hybrid/inputs_true.dat b/tests/regression_tests/random_ray_diagonal_stabilization/homogeneous_hybrid/inputs_true.dat new file mode 100644 index 00000000000..1cf9b55110a --- /dev/null +++ b/tests/regression_tests/random_ray_diagonal_stabilization/homogeneous_hybrid/inputs_true.dat @@ -0,0 +1,48 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + eigenvalue + 100 + 8 + 3 + multi-group + + 30.0 + 200.0 + + + + 0.0 0.0 0.0 10.0 10.0 10.0 + + + + + + + + + hybrid + + + 4 4 4 + 0.0 0.0 0.0 + 10.0 10.0 10.0 + + + diff --git a/tests/regression_tests/random_ray_diagonal_stabilization/homogeneous_hybrid/results_true.dat b/tests/regression_tests/random_ray_diagonal_stabilization/homogeneous_hybrid/results_true.dat new file mode 100644 index 00000000000..530d8b49018 --- /dev/null +++ b/tests/regression_tests/random_ray_diagonal_stabilization/homogeneous_hybrid/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +5.484375E-01 2.130966E-02 diff --git a/tests/regression_tests/random_ray_diagonal_stabilization/homogeneous_strict_adaptive/inputs_true.dat b/tests/regression_tests/random_ray_diagonal_stabilization/homogeneous_strict_adaptive/inputs_true.dat new file mode 100644 index 00000000000..59d650d0526 --- /dev/null +++ b/tests/regression_tests/random_ray_diagonal_stabilization/homogeneous_strict_adaptive/inputs_true.dat @@ -0,0 +1,48 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + eigenvalue + 100 + 8 + 3 + multi-group + + 30.0 + 200.0 + + + + 0.0 0.0 0.0 10.0 10.0 10.0 + + + + + + + + + strict_adaptive + + + 4 4 4 + 0.0 0.0 0.0 + 10.0 10.0 10.0 + + + diff --git a/tests/regression_tests/random_ray_diagonal_stabilization/homogeneous_strict_adaptive/results_true.dat b/tests/regression_tests/random_ray_diagonal_stabilization/homogeneous_strict_adaptive/results_true.dat new file mode 100644 index 00000000000..530d8b49018 --- /dev/null +++ b/tests/regression_tests/random_ray_diagonal_stabilization/homogeneous_strict_adaptive/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +5.484375E-01 2.130966E-02 diff --git a/tests/regression_tests/random_ray_diagonal_stabilization/inputs_true.dat b/tests/regression_tests/random_ray_diagonal_stabilization/hybrid/inputs_true.dat similarity index 98% rename from tests/regression_tests/random_ray_diagonal_stabilization/inputs_true.dat rename to tests/regression_tests/random_ray_diagonal_stabilization/hybrid/inputs_true.dat index 0ea8c017760..4d48f151a51 100644 --- a/tests/regression_tests/random_ray_diagonal_stabilization/inputs_true.dat +++ b/tests/regression_tests/random_ray_diagonal_stabilization/hybrid/inputs_true.dat @@ -57,6 +57,7 @@ linear 0.5 + hybrid 2 2 diff --git a/tests/regression_tests/random_ray_diagonal_stabilization/results_true.dat b/tests/regression_tests/random_ray_diagonal_stabilization/hybrid/results_true.dat similarity index 100% rename from tests/regression_tests/random_ray_diagonal_stabilization/results_true.dat rename to tests/regression_tests/random_ray_diagonal_stabilization/hybrid/results_true.dat diff --git a/tests/regression_tests/random_ray_diagonal_stabilization/test.py b/tests/regression_tests/random_ray_diagonal_stabilization/test.py index 4ea21ae1a72..22fabc56f27 100644 --- a/tests/regression_tests/random_ray_diagonal_stabilization/test.py +++ b/tests/regression_tests/random_ray_diagonal_stabilization/test.py @@ -1,8 +1,12 @@ import os +import numpy as np import openmc +import openmc.mgxs from openmc.examples import pwr_pin_cell +from openmc.utility_funcs import change_directory from openmc import RegularMesh +import pytest from tests.testing_harness import TolerantPyAPITestHarness @@ -15,7 +19,7 @@ def _cleanup(self): os.remove(f) -def test_random_ray_diagonal_stabilization(): +def _build_model(): # Start with a normal continuous energy model model = pwr_pin_cell() @@ -59,5 +63,92 @@ def test_random_ray_diagonal_stabilization(): model.settings.inactive = 15 model.settings.batches = 20 - harness = MGXSTestHarness('statepoint.20.h5', model) - harness.main() + return model + + +def _build_homogeneous_model(): + # A homogeneous two-group eigenvalue problem with a negative within-group + # scattering cross section in the fast group, as a transport correction + # produces. With chi = (1, 0) and no upscatter the infinite-medium + # eigenvalue is + # k = nu_sigma_f2 * sigma_s12 / ((sigma_t2 - sigma_s22) * + # (sigma_t1 - sigma_s11)) + # = 0.375 * 1.0 / (0.5 * 1.5) = 0.5 + # exactly, and the iteration approaches it geometrically from above + # (1.5, 1.0, 0.75, 0.625, ...). Only a few batches are run, enough for + # the stored reference to pin that direction. + groups = openmc.mgxs.EnergyGroups(group_edges=[1e-5, 1.0e3, 20.0e6]) + xs = openmc.XSdata('mat', groups) + xs.order = 0 + xs.set_total([1.0, 1.0]) + xs.set_absorption([0.5, 0.5]) + xs.set_scatter_matrix(np.array([[[-0.5], [1.0]], + [[0.0], [0.5]]])) + xs.set_fission([0.0, 0.15]) + xs.set_nu_fission([0.0, 0.375]) + xs.set_chi([1.0, 0.0]) + lib = openmc.MGXSLibrary(groups) + lib.add_xsdatas([xs]) + lib.export_to_hdf5('mgxs.h5') + + mat = openmc.Material(name='mat') + mat.set_density('macro', 1.0) + mat.add_macroscopic(openmc.Macroscopic('mat')) + model = openmc.Model() + model.materials = openmc.Materials([mat]) + model.materials.cross_sections = 'mgxs.h5' + box = openmc.model.RectangularParallelepiped( + 0.0, 10.0, 0.0, 10.0, 0.0, 10.0, boundary_type='reflective') + cell = openmc.Cell(fill=mat, region=-box) + model.geometry = openmc.Geometry([cell]) + + mesh = RegularMesh() + mesh.lower_left = (0.0, 0.0, 0.0) + mesh.upper_right = (10.0, 10.0, 10.0) + mesh.dimension = (4, 4, 4) + + settings = model.settings + settings.energy_mode = 'multi-group' + settings.run_mode = 'eigenvalue' + settings.particles = 100 + settings.inactive = 3 + settings.batches = 8 + settings.random_ray = { + 'distance_inactive': 30.0, + 'distance_active': 200.0, + 'ray_source': openmc.IndependentSource( + space=openmc.stats.Box((0.0, 0.0, 0.0), (10.0, 10.0, 10.0))), + 'source_region_meshes': [(mesh, [model.geometry.root_universe])], + } + return model + + +# The transport-corrected (P0) library's negative within-group scattering +# drives some reduced sources negative, which the adaptive estimator must +# handle through its negative-source (strong) treatment and its +# end-of-inactive demotion. The adaptive case pins that interplay. +@pytest.mark.parametrize("estimator", ["hybrid", "adaptive"]) +def test_random_ray_diagonal_stabilization(estimator): + with change_directory(estimator): + openmc.reset_auto_ids() + model = _build_model() + model.settings.random_ray['volume_estimator'] = estimator + harness = MGXSTestHarness('statepoint.20.h5', model) + harness.main() + + +# Every estimator must descend toward the analytic eigenvalue of the +# homogeneous problem. The strict estimator's non-negativity fixup must +# assess the stabilized flux iterate: the negative within-group scattering +# drives the raw fast-group iterate negative early on, which the +# stabilization maps to a positive value. Flooring the raw value first would +# freeze the iteration at the previous iterate, and this problem then climbs +# toward a spurious k of 2.0 instead. +@pytest.mark.parametrize("estimator", ["hybrid", "adaptive", "strict_adaptive"]) +def test_random_ray_diagonal_stabilization_homogeneous(estimator): + with change_directory(f'homogeneous_{estimator}'): + openmc.reset_auto_ids() + model = _build_homogeneous_model() + model.settings.random_ray['volume_estimator'] = estimator + harness = MGXSTestHarness('statepoint.8.h5', model) + harness.main() diff --git a/tests/regression_tests/random_ray_fixed_source_domain/cell/inputs_true.dat b/tests/regression_tests/random_ray_fixed_source_domain/cell/inputs_true.dat index d650bbaf95c..d2fbd6d2247 100644 --- a/tests/regression_tests/random_ray_fixed_source_domain/cell/inputs_true.dat +++ b/tests/regression_tests/random_ray_fixed_source_domain/cell/inputs_true.dat @@ -215,6 +215,7 @@ true + hybrid diff --git a/tests/regression_tests/random_ray_fixed_source_domain/material/inputs_true.dat b/tests/regression_tests/random_ray_fixed_source_domain/material/inputs_true.dat index 98a51add1f3..bad8edb0ceb 100644 --- a/tests/regression_tests/random_ray_fixed_source_domain/material/inputs_true.dat +++ b/tests/regression_tests/random_ray_fixed_source_domain/material/inputs_true.dat @@ -215,6 +215,7 @@ true + hybrid diff --git a/tests/regression_tests/random_ray_fixed_source_domain/test.py b/tests/regression_tests/random_ray_fixed_source_domain/test.py index 5885a92009a..5b54871b780 100644 --- a/tests/regression_tests/random_ray_fixed_source_domain/test.py +++ b/tests/regression_tests/random_ray_fixed_source_domain/test.py @@ -47,5 +47,6 @@ def test_random_ray_fixed_source(domain_type): constraints['domain_type'] = 'universe' constraints['domain_ids'] = [universe.id] + model.settings.random_ray['volume_estimator'] = 'hybrid' harness = MGXSTestHarness('statepoint.10.h5', model) harness.main() diff --git a/tests/regression_tests/random_ray_fixed_source_domain/universe/inputs_true.dat b/tests/regression_tests/random_ray_fixed_source_domain/universe/inputs_true.dat index 20deba664bd..4d1af46b121 100644 --- a/tests/regression_tests/random_ray_fixed_source_domain/universe/inputs_true.dat +++ b/tests/regression_tests/random_ray_fixed_source_domain/universe/inputs_true.dat @@ -215,6 +215,7 @@ true + hybrid diff --git a/tests/regression_tests/random_ray_fixed_source_linear/linear/inputs_true.dat b/tests/regression_tests/random_ray_fixed_source_linear/linear/inputs_true.dat index 2268d82c391..dd11567f69d 100644 --- a/tests/regression_tests/random_ray_fixed_source_linear/linear/inputs_true.dat +++ b/tests/regression_tests/random_ray_fixed_source_linear/linear/inputs_true.dat @@ -216,6 +216,7 @@ true linear + hybrid diff --git a/tests/regression_tests/random_ray_fixed_source_linear/linear_xy/inputs_true.dat b/tests/regression_tests/random_ray_fixed_source_linear/linear_xy/inputs_true.dat index fe95baa7bb5..74a7a0b7f4d 100644 --- a/tests/regression_tests/random_ray_fixed_source_linear/linear_xy/inputs_true.dat +++ b/tests/regression_tests/random_ray_fixed_source_linear/linear_xy/inputs_true.dat @@ -216,6 +216,7 @@ true linear_xy + hybrid diff --git a/tests/regression_tests/random_ray_fixed_source_linear/test.py b/tests/regression_tests/random_ray_fixed_source_linear/test.py index 99211024e6e..b0532a458bb 100644 --- a/tests/regression_tests/random_ray_fixed_source_linear/test.py +++ b/tests/regression_tests/random_ray_fixed_source_linear/test.py @@ -25,5 +25,6 @@ def test_random_ray_fixed_source_linear(shape): model.settings.random_ray['source_shape'] = shape model.settings.inactive = 20 model.settings.batches = 40 + model.settings.random_ray['volume_estimator'] = 'hybrid' harness = MGXSTestHarness('statepoint.40.h5', model) harness.main() diff --git a/tests/regression_tests/random_ray_fixed_source_mesh/flat/inputs_true.dat b/tests/regression_tests/random_ray_fixed_source_mesh/flat/inputs_true.dat index a5632ece960..6b0eaaab6c7 100644 --- a/tests/regression_tests/random_ray_fixed_source_mesh/flat/inputs_true.dat +++ b/tests/regression_tests/random_ray_fixed_source_mesh/flat/inputs_true.dat @@ -227,6 +227,7 @@ flat + hybrid 24 24 24 diff --git a/tests/regression_tests/random_ray_fixed_source_mesh/linear/inputs_true.dat b/tests/regression_tests/random_ray_fixed_source_mesh/linear/inputs_true.dat index 9d22603c63c..d7a7d4f418b 100644 --- a/tests/regression_tests/random_ray_fixed_source_mesh/linear/inputs_true.dat +++ b/tests/regression_tests/random_ray_fixed_source_mesh/linear/inputs_true.dat @@ -227,6 +227,7 @@ linear + hybrid 24 24 24 diff --git a/tests/regression_tests/random_ray_fixed_source_mesh/test.py b/tests/regression_tests/random_ray_fixed_source_mesh/test.py index 0b93b2a7a6a..9e09959e904 100644 --- a/tests/regression_tests/random_ray_fixed_source_mesh/test.py +++ b/tests/regression_tests/random_ray_fixed_source_mesh/test.py @@ -49,5 +49,6 @@ def test_random_ray_fixed_source_mesh(shape): model.settings.inactive = 15 model.settings.batches = 30 + model.settings.random_ray['volume_estimator'] = 'hybrid' harness = MGXSTestHarness('statepoint.30.h5', model) harness.main() diff --git a/tests/regression_tests/random_ray_fixed_source_normalization/False/inputs_true.dat b/tests/regression_tests/random_ray_fixed_source_normalization/False/inputs_true.dat index de941f10fbb..ffe38772386 100644 --- a/tests/regression_tests/random_ray_fixed_source_normalization/False/inputs_true.dat +++ b/tests/regression_tests/random_ray_fixed_source_normalization/False/inputs_true.dat @@ -215,6 +215,7 @@ false + hybrid diff --git a/tests/regression_tests/random_ray_fixed_source_normalization/True/inputs_true.dat b/tests/regression_tests/random_ray_fixed_source_normalization/True/inputs_true.dat index 20deba664bd..4d1af46b121 100644 --- a/tests/regression_tests/random_ray_fixed_source_normalization/True/inputs_true.dat +++ b/tests/regression_tests/random_ray_fixed_source_normalization/True/inputs_true.dat @@ -215,6 +215,7 @@ true + hybrid diff --git a/tests/regression_tests/random_ray_fixed_source_normalization/test.py b/tests/regression_tests/random_ray_fixed_source_normalization/test.py index 3fa4ba2a63f..aa9823d0e27 100644 --- a/tests/regression_tests/random_ray_fixed_source_normalization/test.py +++ b/tests/regression_tests/random_ray_fixed_source_normalization/test.py @@ -23,5 +23,6 @@ def test_random_ray_fixed_source(normalize): model = random_ray_three_region_cube() model.settings.random_ray['volume_normalized_flux_tallies'] = normalize + model.settings.random_ray['volume_estimator'] = 'hybrid' harness = MGXSTestHarness('statepoint.10.h5', model) harness.main() diff --git a/tests/regression_tests/random_ray_fixed_source_subcritical/flat/inputs_true.dat b/tests/regression_tests/random_ray_fixed_source_subcritical/flat/inputs_true.dat index 943468a1095..8a992399806 100644 --- a/tests/regression_tests/random_ray_fixed_source_subcritical/flat/inputs_true.dat +++ b/tests/regression_tests/random_ray_fixed_source_subcritical/flat/inputs_true.dat @@ -119,6 +119,7 @@ false flat + hybrid diff --git a/tests/regression_tests/random_ray_fixed_source_subcritical/linear_xy/inputs_true.dat b/tests/regression_tests/random_ray_fixed_source_subcritical/linear_xy/inputs_true.dat index 650953c4b06..20e5095f8f2 100644 --- a/tests/regression_tests/random_ray_fixed_source_subcritical/linear_xy/inputs_true.dat +++ b/tests/regression_tests/random_ray_fixed_source_subcritical/linear_xy/inputs_true.dat @@ -119,6 +119,7 @@ false linear_xy + hybrid diff --git a/tests/regression_tests/random_ray_fixed_source_subcritical/test.py b/tests/regression_tests/random_ray_fixed_source_subcritical/test.py index e2f3cf17582..b8f08e63696 100644 --- a/tests/regression_tests/random_ray_fixed_source_subcritical/test.py +++ b/tests/regression_tests/random_ray_fixed_source_subcritical/test.py @@ -129,5 +129,6 @@ def test_random_ray_fixed_source_subcritical(shape): ######################################## # Run test + model.settings.random_ray['volume_estimator'] = 'hybrid' harness = MGXSTestHarness('statepoint.125.h5', model) harness.main() diff --git a/tests/regression_tests/random_ray_k_eff_mesh/adaptive_starved/inputs_true.dat b/tests/regression_tests/random_ray_k_eff_mesh/adaptive_starved/inputs_true.dat new file mode 100644 index 00000000000..0935ab4c9c0 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_mesh/adaptive_starved/inputs_true.dat @@ -0,0 +1,122 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.126 0.126 + 10 10 + -0.63 -0.63 + +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 + + + 1.26 1.26 + 2 2 + -1.26 -1.26 + +2 2 +2 5 + + + + + + + + + + + + + + + + + + + + + eigenvalue + 25 + 40 + 20 + multi-group + + 100.0 + 20.0 + + + + -1.26 -1.26 -1 1.26 1.26 1 + + + + true + + + + + + adaptive + + + 40 40 + -1.26 -1.26 + 1.26 1.26 + + + + + 2 2 + -1.26 -1.26 + 1.26 1.26 + + + 1 + + + 1e-05 0.0635 10.0 100.0 1000.0 500000.0 1000000.0 20000000.0 + + + 1 2 + flux fission nu-fission + analog + + + diff --git a/tests/regression_tests/random_ray_k_eff_mesh/adaptive_starved/results_true.dat b/tests/regression_tests/random_ray_k_eff_mesh/adaptive_starved/results_true.dat new file mode 100644 index 00000000000..038d14115e4 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_mesh/adaptive_starved/results_true.dat @@ -0,0 +1,171 @@ +k-combined: +1.107191E+00 1.650659E-02 +tally 1: +5.382339E+00 +1.458425E+00 +1.990201E+00 +1.996805E-01 +4.843752E+00 +1.182784E+00 +3.851449E+00 +7.437451E-01 +5.695747E-01 +1.627384E-02 +1.386231E+00 +9.639619E-02 +2.905354E+00 +4.222530E-01 +9.355436E-02 +4.378740E-04 +2.276926E-01 +2.593694E-03 +3.728133E+00 +6.951656E-01 +1.234731E-01 +7.626219E-04 +3.005089E-01 +4.517301E-03 +9.697786E+00 +4.702912E+00 +1.132007E-01 +6.408188E-04 +2.755113E-01 +3.795908E-03 +2.110307E+01 +2.227083E+01 +3.196285E-02 +5.109814E-05 +7.908983E-02 +3.128639E-04 +1.114013E+01 +6.210744E+00 +1.495081E-01 +1.120059E-03 +4.158491E-01 +8.665291E-03 +8.789910E+00 +3.891597E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.704870E+00 +1.109865E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.067040E+00 +4.705142E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.067399E+00 +8.273897E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.781891E+00 +4.784770E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.891493E+01 +1.789205E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.664789E+00 +4.679805E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.684700E+00 +1.105730E+00 +1.748804E+00 +1.543531E-01 +4.256239E+00 +9.142921E-01 +3.633542E+00 +6.620806E-01 +5.450623E-01 +1.490410E-02 +1.326573E+00 +8.828271E-02 +2.859797E+00 +4.091356E-01 +9.368855E-02 +4.391008E-04 +2.280192E-01 +2.600962E-03 +3.656296E+00 +6.686608E-01 +1.230360E-01 +7.572462E-04 +2.994450E-01 +4.485459E-03 +9.692731E+00 +4.698026E+00 +1.151164E-01 +6.626605E-04 +2.801737E-01 +3.925288E-03 +2.161344E+01 +2.335940E+01 +3.339064E-02 +5.576419E-05 +8.262280E-02 +3.414332E-04 +1.149109E+01 +6.606633E+00 +1.577263E-01 +1.246078E-03 +4.387076E-01 +9.640234E-03 +5.394231E+00 +1.465973E+00 +2.033778E+00 +2.084693E-01 +4.949809E+00 +1.234843E+00 +3.854239E+00 +7.449142E-01 +5.821861E-01 +1.699800E-02 +1.416925E+00 +1.006856E-01 +2.904495E+00 +4.219991E-01 +9.568897E-02 +4.580415E-04 +2.328878E-01 +2.713155E-03 +3.721089E+00 +6.925391E-01 +1.259809E-01 +7.939042E-04 +3.066124E-01 +4.702598E-03 +9.683004E+00 +4.688502E+00 +1.156990E-01 +6.693965E-04 +2.815917E-01 +3.965188E-03 +2.116801E+01 +2.240891E+01 +3.293516E-02 +5.425827E-05 +8.149575E-02 +3.322128E-04 +1.127270E+01 +6.359090E+00 +1.560609E-01 +1.220736E-03 +4.340755E-01 +9.444178E-03 diff --git a/tests/regression_tests/random_ray_k_eff_mesh/inputs_true.dat b/tests/regression_tests/random_ray_k_eff_mesh/inputs_true.dat index f6e9c8e3e71..2dc2577e393 100644 --- a/tests/regression_tests/random_ray_k_eff_mesh/inputs_true.dat +++ b/tests/regression_tests/random_ray_k_eff_mesh/inputs_true.dat @@ -93,6 +93,7 @@ + hybrid 40 40 diff --git a/tests/regression_tests/random_ray_k_eff_mesh/test.py b/tests/regression_tests/random_ray_k_eff_mesh/test.py index cffdaf8bb4c..1f3b212d173 100644 --- a/tests/regression_tests/random_ray_k_eff_mesh/test.py +++ b/tests/regression_tests/random_ray_k_eff_mesh/test.py @@ -1,6 +1,7 @@ import os import openmc +from openmc.utility_funcs import change_directory from openmc.examples import random_ray_lattice from tests.testing_harness import TolerantPyAPITestHarness @@ -27,10 +28,35 @@ def test_random_ray_k_eff_mesh(): mesh.dimension = (dim, dim) mesh.lower_left = (-pitch, -pitch) mesh.upper_right = (pitch, pitch) - + root = model.geometry.root_universe - + model.settings.random_ray['source_region_meshes'] = [(mesh, [root])] + model.settings.random_ray['volume_estimator'] = 'hybrid' harness = MGXSTestHarness('statepoint.10.h5', model) harness.main() + + +def test_random_ray_k_eff_mesh_adaptive_starved(): + # Ray-starved adaptive eigenvalue case (~1.2% miss rate). The subdivision + # mesh plus a low ray count engages the adaptive demotion machinery + # (strong-source and hit-starved regions, plus the end-of-inactive + # demotion decision and the previous-flux miss treatment) in eigenvalue + # mode, which the nominal 0%-miss eigenvalue tests never exercise. + with change_directory('adaptive_starved'): + openmc.reset_auto_ids() + model = random_ray_lattice() + pitch = 1.26 + mesh = openmc.RegularMesh() + mesh.dimension = (40, 40) + mesh.lower_left = (-pitch, -pitch) + mesh.upper_right = (pitch, pitch) + root = model.geometry.root_universe + model.settings.random_ray['source_region_meshes'] = [(mesh, [root])] + model.settings.random_ray['volume_estimator'] = 'adaptive' + model.settings.particles = 25 + model.settings.inactive = 20 + model.settings.batches = 40 + harness = MGXSTestHarness('statepoint.40.h5', model) + harness.main() diff --git a/tests/regression_tests/random_ray_low_density/inputs_true.dat b/tests/regression_tests/random_ray_low_density/inputs_true.dat index 20deba664bd..4d1af46b121 100644 --- a/tests/regression_tests/random_ray_low_density/inputs_true.dat +++ b/tests/regression_tests/random_ray_low_density/inputs_true.dat @@ -215,6 +215,7 @@ true + hybrid diff --git a/tests/regression_tests/random_ray_low_density/test.py b/tests/regression_tests/random_ray_low_density/test.py index 1b4ffb78183..22f68cca291 100644 --- a/tests/regression_tests/random_ray_low_density/test.py +++ b/tests/regression_tests/random_ray_low_density/test.py @@ -56,5 +56,6 @@ def test_random_ray_low_density(): [source_mat_data, void_mat_data, absorber_mat_data]) mg_cross_sections_file.export_to_hdf5() + model.settings.random_ray['volume_estimator'] = 'hybrid' harness = MGXSTestHarness('statepoint.10.h5', model) harness.main() diff --git a/tests/regression_tests/random_ray_point_source_locator/inputs_true.dat b/tests/regression_tests/random_ray_point_source_locator/inputs_true.dat index b4bd263f5ac..a66cd835c8b 100644 --- a/tests/regression_tests/random_ray_point_source_locator/inputs_true.dat +++ b/tests/regression_tests/random_ray_point_source_locator/inputs_true.dat @@ -219,6 +219,7 @@ + hybrid 30 30 30 diff --git a/tests/regression_tests/random_ray_point_source_locator/test.py b/tests/regression_tests/random_ray_point_source_locator/test.py index fd3d8a18fe9..c72e2b85699 100644 --- a/tests/regression_tests/random_ray_point_source_locator/test.py +++ b/tests/regression_tests/random_ray_point_source_locator/test.py @@ -40,5 +40,6 @@ def test_random_ray_point_source_locator(): model.settings.inactive = 15 model.settings.batches = 30 + model.settings.random_ray['volume_estimator'] = 'hybrid' harness = MGXSTestHarness('statepoint.30.h5', model) harness.main() diff --git a/tests/regression_tests/random_ray_void/flat/inputs_true.dat b/tests/regression_tests/random_ray_void/flat/inputs_true.dat index 66390c76661..39533ab0dd6 100644 --- a/tests/regression_tests/random_ray_void/flat/inputs_true.dat +++ b/tests/regression_tests/random_ray_void/flat/inputs_true.dat @@ -216,6 +216,7 @@ true flat + hybrid diff --git a/tests/regression_tests/random_ray_void/linear/inputs_true.dat b/tests/regression_tests/random_ray_void/linear/inputs_true.dat index 45228a03955..83d960a441c 100644 --- a/tests/regression_tests/random_ray_void/linear/inputs_true.dat +++ b/tests/regression_tests/random_ray_void/linear/inputs_true.dat @@ -216,6 +216,7 @@ true linear + hybrid diff --git a/tests/regression_tests/random_ray_void/test.py b/tests/regression_tests/random_ray_void/test.py index b48a7794d7e..cc45650175e 100644 --- a/tests/regression_tests/random_ray_void/test.py +++ b/tests/regression_tests/random_ray_void/test.py @@ -68,5 +68,6 @@ def test_random_ray_void(shape): tallies = openmc.Tallies([source_tally, void_tally, absorber_tally]) model.tallies = tallies + model.settings.random_ray['volume_estimator'] = 'hybrid' harness = MGXSTestHarness('statepoint.40.h5', model) harness.main() diff --git a/tests/regression_tests/random_ray_volume_estimator/adaptive/inputs_true.dat b/tests/regression_tests/random_ray_volume_estimator/adaptive/inputs_true.dat new file mode 100644 index 00000000000..33ab014d4c6 --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator/adaptive/inputs_true.dat @@ -0,0 +1,247 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 10 + 40 + 20 + + + 100.0 1.0 + + + universe + 1 + + + multi-group + + 500.0 + 100.0 + + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + + true + adaptive + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/random_ray_volume_estimator/adaptive/results_true.dat b/tests/regression_tests/random_ray_volume_estimator/adaptive/results_true.dat new file mode 100644 index 00000000000..ac9fcd45390 --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator/adaptive/results_true.dat @@ -0,0 +1,9 @@ +tally 1: +2.259425E+00 +2.675738E-01 +tally 2: +1.018431E-01 +6.713412E-04 +tally 3: +7.288312E-03 +3.488945E-06 diff --git a/tests/regression_tests/random_ray_volume_estimator/hybrid/inputs_true.dat b/tests/regression_tests/random_ray_volume_estimator/hybrid/inputs_true.dat index 4d1af46b121..505d4c7dfed 100644 --- a/tests/regression_tests/random_ray_volume_estimator/hybrid/inputs_true.dat +++ b/tests/regression_tests/random_ray_volume_estimator/hybrid/inputs_true.dat @@ -191,9 +191,9 @@ fixed source - 90 - 10 - 5 + 10 + 40 + 20 100.0 1.0 diff --git a/tests/regression_tests/random_ray_volume_estimator/hybrid/results_true.dat b/tests/regression_tests/random_ray_volume_estimator/hybrid/results_true.dat index 6da51a711bf..16adfa6d81a 100644 --- a/tests/regression_tests/random_ray_volume_estimator/hybrid/results_true.dat +++ b/tests/regression_tests/random_ray_volume_estimator/hybrid/results_true.dat @@ -1,9 +1,9 @@ tally 1: -5.934460E-01 -7.058894E-02 +2.261079E+00 +2.678641E-01 tally 2: -3.206214E-02 -2.063370E-04 +1.043325E-01 +7.552784E-04 tally 3: -2.096411E-03 -8.804924E-07 +6.475295E-03 +2.740906E-06 diff --git a/tests/regression_tests/random_ray_volume_estimator/naive/inputs_true.dat b/tests/regression_tests/random_ray_volume_estimator/naive/inputs_true.dat index a268d55d04a..b408131f815 100644 --- a/tests/regression_tests/random_ray_volume_estimator/naive/inputs_true.dat +++ b/tests/regression_tests/random_ray_volume_estimator/naive/inputs_true.dat @@ -191,9 +191,9 @@ fixed source - 90 - 10 - 5 + 10 + 40 + 20 100.0 1.0 diff --git a/tests/regression_tests/random_ray_volume_estimator/naive/results_true.dat b/tests/regression_tests/random_ray_volume_estimator/naive/results_true.dat index f8d6d10b001..6afcb204a05 100644 --- a/tests/regression_tests/random_ray_volume_estimator/naive/results_true.dat +++ b/tests/regression_tests/random_ray_volume_estimator/naive/results_true.dat @@ -1,9 +1,9 @@ tally 1: -5.935538E-01 -7.061433E-02 +2.258260E+00 +2.673097E-01 tally 2: -3.263210E-02 -2.134164E-04 +1.100975E-01 +7.432009E-04 tally 3: -2.107977E-03 -8.905227E-07 +6.385176E-03 +2.616155E-06 diff --git a/tests/regression_tests/random_ray_volume_estimator/simulation_averaged/inputs_true.dat b/tests/regression_tests/random_ray_volume_estimator/simulation_averaged/inputs_true.dat index 777ccaea510..789d280f166 100644 --- a/tests/regression_tests/random_ray_volume_estimator/simulation_averaged/inputs_true.dat +++ b/tests/regression_tests/random_ray_volume_estimator/simulation_averaged/inputs_true.dat @@ -191,9 +191,9 @@ fixed source - 90 - 10 - 5 + 10 + 40 + 20 100.0 1.0 diff --git a/tests/regression_tests/random_ray_volume_estimator/simulation_averaged/results_true.dat b/tests/regression_tests/random_ray_volume_estimator/simulation_averaged/results_true.dat index 5f297586075..3466720b9f5 100644 --- a/tests/regression_tests/random_ray_volume_estimator/simulation_averaged/results_true.dat +++ b/tests/regression_tests/random_ray_volume_estimator/simulation_averaged/results_true.dat @@ -1,9 +1,9 @@ tally 1: --5.745886E+02 -9.758367E+04 +-5.372158E+03 +9.133802E+06 tally 2: -2.971927E-02 -1.827222E-04 +8.960408E-02 +1.520820E-03 tally 3: -1.978393E-03 -7.951531E-07 +4.634379E-03 +2.696798E-06 diff --git a/tests/regression_tests/random_ray_volume_estimator/strict_adaptive/inputs_true.dat b/tests/regression_tests/random_ray_volume_estimator/strict_adaptive/inputs_true.dat new file mode 100644 index 00000000000..f7aa1795cfe --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator/strict_adaptive/inputs_true.dat @@ -0,0 +1,247 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 10 + 40 + 20 + + + 100.0 1.0 + + + universe + 1 + + + multi-group + + 500.0 + 100.0 + + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + + true + strict_adaptive + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/random_ray_volume_estimator/strict_adaptive/results_true.dat b/tests/regression_tests/random_ray_volume_estimator/strict_adaptive/results_true.dat new file mode 100644 index 00000000000..ea66b9cc72b --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator/strict_adaptive/results_true.dat @@ -0,0 +1,9 @@ +tally 1: +2.274462E+00 +2.703615E-01 +tally 2: +1.222058E-01 +9.217159E-04 +tally 3: +7.435355E-03 +3.594692E-06 diff --git a/tests/regression_tests/random_ray_volume_estimator/test.py b/tests/regression_tests/random_ray_volume_estimator/test.py index fba4bbbbe6d..db711adc90f 100644 --- a/tests/regression_tests/random_ray_volume_estimator/test.py +++ b/tests/regression_tests/random_ray_volume_estimator/test.py @@ -16,15 +16,27 @@ def _cleanup(self): os.remove(f) +# A deliberately ray-starved configuration (~20% source region miss rate). +# The volume estimators only differ meaningfully when regions are missed or +# sparsely hit, so a starved run exercises every estimator code path. At +# this density the per-estimator volume choices, the miss treatments, and +# for the adaptive estimator the strong-source (kappa) demotion, the +# hit-starved demotion, the end-of-inactive converged-negative demotion, +# and the previous-flux miss treatment all fire. @pytest.mark.parametrize("estimator", ["hybrid", "simulation_averaged", - "naive" + "naive", + "adaptive", + "strict_adaptive" ]) def test_random_ray_volume_estimator(estimator): with change_directory(estimator): openmc.reset_auto_ids() model = random_ray_three_region_cube() model.settings.random_ray['volume_estimator'] = estimator + model.settings.particles = 10 + model.settings.inactive = 20 + model.settings.batches = 40 - harness = MGXSTestHarness('statepoint.10.h5', model) + harness = MGXSTestHarness('statepoint.40.h5', model) harness.main() diff --git a/tests/regression_tests/random_ray_volume_estimator_auto/__init__.py b/tests/regression_tests/random_ray_volume_estimator_auto/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/regression_tests/random_ray_volume_estimator_auto/adjoint/inputs_true.dat b/tests/regression_tests/random_ray_volume_estimator_auto/adjoint/inputs_true.dat new file mode 100644 index 00000000000..66d297ada61 --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator_auto/adjoint/inputs_true.dat @@ -0,0 +1,247 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 10 + 40 + 20 + + + 100.0 1.0 + + + universe + 1 + + + multi-group + + 500.0 + 100.0 + + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + + true + true + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/random_ray_volume_estimator_auto/adjoint/results_true.dat b/tests/regression_tests/random_ray_volume_estimator_auto/adjoint/results_true.dat new file mode 100644 index 00000000000..7b6c664d32f --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator_auto/adjoint/results_true.dat @@ -0,0 +1,9 @@ +tally 1: +3.511731E+06 +7.366501E+11 +tally 2: +4.076663E+06 +8.358445E+11 +tally 3: +1.531116E+07 +1.175684E+13 diff --git a/tests/regression_tests/random_ray_volume_estimator_auto/forward/inputs_true.dat b/tests/regression_tests/random_ray_volume_estimator_auto/forward/inputs_true.dat new file mode 100644 index 00000000000..dca77cb4edb --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator_auto/forward/inputs_true.dat @@ -0,0 +1,246 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 10 + 40 + 20 + + + 100.0 1.0 + + + universe + 1 + + + multi-group + + 500.0 + 100.0 + + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + + true + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/random_ray_volume_estimator_auto/forward/results_true.dat b/tests/regression_tests/random_ray_volume_estimator_auto/forward/results_true.dat new file mode 100644 index 00000000000..ac9fcd45390 --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator_auto/forward/results_true.dat @@ -0,0 +1,9 @@ +tally 1: +2.259425E+00 +2.675738E-01 +tally 2: +1.018431E-01 +6.713412E-04 +tally 3: +7.288312E-03 +3.488945E-06 diff --git a/tests/regression_tests/random_ray_volume_estimator_auto/test.py b/tests/regression_tests/random_ray_volume_estimator_auto/test.py new file mode 100644 index 00000000000..3a144e3055f --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator_auto/test.py @@ -0,0 +1,47 @@ +import os + +import openmc +from openmc.utility_funcs import change_directory +from openmc.examples import random_ray_three_region_cube +import pytest + +from tests.testing_harness import TolerantPyAPITestHarness + + +class MGXSTestHarness(TolerantPyAPITestHarness): + def _cleanup(self): + super()._cleanup() + for f in ('mgxs.h5', 'weight_windows.h5'): + if os.path.exists(f): + os.remove(f) + + +# The default "auto" volume estimator resolves by solve type. Standard +# solves receive the adaptive estimator, while solves whose results feed +# variance reduction (any adjoint workflow, and weight window generation) +# receive the strict adaptive estimator. No case sets an estimator +# explicitly, so these golds pin the routing itself, and a regression in +# the resolution policy shifts the affected case's results. The forward and +# adjoint cases pin the adjoint-flag trigger, while the weight_windows case +# pins the generator-presence trigger. +@pytest.mark.parametrize("solve", ["forward", "adjoint", "weight_windows"]) +def test_random_ray_volume_estimator_auto(solve): + with change_directory(solve): + openmc.reset_auto_ids() + model = random_ray_three_region_cube() + if solve == "adjoint": + model.settings.random_ray['adjoint'] = True + elif solve == "weight_windows": + ww_mesh = openmc.RegularMesh() + ww_mesh.dimension = (6, 6, 6) + ww_mesh.lower_left = (0.0, 0.0, 0.0) + ww_mesh.upper_right = (30.0, 30.0, 30.0) + model.settings.weight_window_generators = \ + openmc.WeightWindowGenerator( + method="fw_cadis", mesh=ww_mesh, max_realizations=40) + model.settings.particles = 10 + model.settings.inactive = 20 + model.settings.batches = 40 + + harness = MGXSTestHarness('statepoint.40.h5', model) + harness.main() diff --git a/tests/regression_tests/random_ray_volume_estimator_auto/weight_windows/inputs_true.dat b/tests/regression_tests/random_ray_volume_estimator_auto/weight_windows/inputs_true.dat new file mode 100644 index 00000000000..edb3f4834ff --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator_auto/weight_windows/inputs_true.dat @@ -0,0 +1,261 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 10 + 40 + 20 + + + 100.0 1.0 + + + universe + 1 + + + multi-group + + + 1 + neutron + 40 + 1 + true + fw_cadis + + + + 6 6 6 + 0.0 0.0 0.0 + 30.0 30.0 30.0 + + + 500.0 + 100.0 + + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + + true + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/random_ray_volume_estimator_auto/weight_windows/results_true.dat b/tests/regression_tests/random_ray_volume_estimator_auto/weight_windows/results_true.dat new file mode 100644 index 00000000000..f9eb7e003ae --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator_auto/weight_windows/results_true.dat @@ -0,0 +1,442 @@ +tally 1: +3.511731E+06 +7.366501E+11 +tally 2: +4.076663E+06 +8.358445E+11 +tally 3: +1.531116E+07 +1.175684E+13 +tally 4: +3.511731E+06 +7.366501E+11 +3.188749E+06 +5.374186E+11 +3.650230E+06 +7.551441E+11 +3.754951E+06 +7.778043E+11 +3.468497E+06 +7.956805E+11 +1.004161E+06 +5.103314E+10 +4.647621E+06 +1.334221E+12 +4.017974E+06 +8.770192E+11 +4.029574E+06 +8.966119E+11 +3.953177E+06 +8.652758E+11 +3.957070E+06 +8.892706E+11 +7.152463E+05 +2.836055E+10 +4.148038E+06 +9.672359E+11 +3.750889E+06 +7.648028E+11 +3.290958E+06 +6.160634E+11 +3.956685E+06 +8.356989E+11 +4.787057E+06 +1.189227E+12 +3.535766E+06 +6.427457E+11 +4.149550E+06 +1.006099E+12 +4.658888E+06 +1.149165E+12 +4.145351E+06 +9.728065E+11 +4.606747E+06 +1.205466E+12 +5.017471E+06 +1.401316E+12 +1.000641E+07 +5.175932E+12 +5.085297E+06 +1.542497E+12 +3.579261E+06 +7.951955E+11 +3.760606E+06 +8.413000E+11 +4.280654E+06 +9.893678E+11 +4.550993E+06 +1.153381E+12 +2.043152E+07 +2.134729E+13 +2.364753E+06 +2.880193E+11 +1.134082E+06 +6.713813E+10 +1.364444E+06 +9.760639E+10 +1.019639E+06 +5.340783E+10 +4.621795E+06 +1.181955E+12 +2.583832E+07 +3.849646E+13 +3.981869E+06 +1.086257E+12 +3.294522E+06 +6.029500E+11 +3.744218E+06 +7.967128E+11 +4.110475E+06 +8.967747E+11 +3.749926E+06 +8.162320E+11 +1.261849E+06 +8.335011E+10 +3.949725E+06 +9.014090E+11 +3.356913E+06 +5.874923E+11 +4.228265E+06 +1.026989E+12 +7.370925E+06 +3.037283E+12 +4.288574E+06 +1.000915E+12 +1.697752E+06 +1.488605E+11 +4.490552E+06 +1.208635E+12 +3.871653E+06 +8.072918E+11 +3.750247E+06 +7.870138E+11 +4.226575E+06 +9.572865E+11 +4.278271E+06 +9.839734E+11 +2.853690E+06 +4.295371E+11 +5.041098E+06 +1.477825E+12 +4.617993E+06 +1.225844E+12 +4.666636E+06 +1.204811E+12 +4.480502E+06 +1.062325E+12 +5.129911E+06 +1.423279E+12 +1.018553E+07 +5.448004E+12 +4.645548E+06 +1.225328E+12 +3.414316E+06 +7.183967E+11 +3.299665E+06 +6.152288E+11 +4.676476E+06 +1.226497E+12 +5.993866E+06 +1.916331E+12 +2.273457E+07 +2.634530E+13 +1.024619E+06 +5.418869E+10 +8.671716E+05 +4.105630E+10 +9.781613E+05 +4.922450E+10 +8.982715E+05 +4.632745E+10 +8.214506E+06 +3.502364E+12 +1.892745E+08 +1.817735E+15 +4.395286E+06 +1.140780E+12 +3.785742E+06 +8.767900E+11 +4.009253E+06 +9.466143E+11 +4.485226E+06 +1.135228E+12 +3.926414E+06 +8.341972E+11 +7.427310E+06 +2.962614E+12 +3.218770E+06 +5.613005E+11 +3.606096E+06 +6.835904E+11 +4.143995E+06 +8.943078E+11 +4.162951E+06 +9.119008E+11 +3.546854E+06 +6.595082E+11 +3.688159E+06 +7.104847E+11 +3.295761E+06 +6.129546E+11 +3.351777E+06 +6.027285E+11 +3.842937E+06 +8.073173E+11 +4.364532E+06 +1.012091E+12 +4.387931E+06 +1.025643E+12 +6.201362E+06 +1.994758E+12 +4.134093E+06 +9.194351E+11 +3.853368E+06 +9.429980E+11 +4.133933E+06 +1.051933E+12 +4.587503E+06 +1.204363E+12 +4.775310E+06 +1.220055E+12 +2.193940E+07 +2.484892E+13 +3.781856E+06 +8.523019E+11 +3.501634E+06 +7.260762E+11 +3.742681E+06 +7.892108E+11 +4.463231E+06 +1.270897E+12 +5.608885E+06 +1.688139E+12 +2.975687E+07 +4.750545E+13 +9.838028E+06 +5.129536E+12 +2.924712E+06 +4.804874E+11 +3.236877E+06 +5.427566E+11 +7.509008E+05 +3.294363E+10 +2.171245E+06 +2.478454E+11 +4.602123E+07 +1.190969E+14 +3.884936E+06 +8.360487E+11 +3.405745E+06 +6.871056E+11 +4.778259E+06 +1.368089E+12 +4.374198E+06 +1.117471E+12 +3.974206E+06 +8.501475E+11 +2.411812E+07 +2.928116E+13 +3.691588E+06 +7.339252E+11 +3.820362E+06 +8.680514E+11 +3.691492E+06 +7.170432E+11 +3.796136E+06 +7.551591E+11 +4.074632E+06 +8.975806E+11 +8.125921E+06 +3.353014E+12 +3.176230E+06 +5.742728E+11 +3.757790E+06 +7.535155E+11 +4.115031E+06 +8.998277E+11 +3.914108E+06 +7.994967E+11 +4.154937E+06 +9.090614E+11 +1.468863E+07 +1.108387E+13 +4.261661E+06 +1.221398E+12 +5.131497E+06 +1.479243E+12 +4.169697E+06 +9.408782E+11 +4.247139E+06 +9.734525E+11 +3.723081E+06 +8.021019E+11 +3.284082E+06 +6.716747E+11 +4.209247E+06 +1.033962E+12 +4.319313E+06 +1.026540E+12 +4.494468E+06 +1.068931E+12 +4.215369E+06 +9.851685E+11 +4.378681E+06 +1.054765E+12 +4.718820E+06 +1.455518E+12 +4.447193E+06 +1.021099E+12 +2.294114E+06 +2.729155E+11 +4.377327E+06 +9.788008E+11 +2.081229E+06 +2.307087E+11 +2.935037E+06 +5.750307E+11 +3.558339E+07 +6.512040E+13 +3.718008E+06 +7.878636E+11 +3.225813E+06 +6.619645E+11 +3.154046E+06 +5.675161E+11 +3.142328E+06 +5.655317E+11 +3.396229E+06 +6.808510E+11 +1.389886E+06 +1.101227E+11 +3.835349E+06 +7.871905E+11 +3.311217E+06 +5.752228E+11 +2.914361E+06 +4.489648E+11 +3.354950E+06 +6.075447E+11 +3.044035E+06 +5.047191E+11 +1.616458E+06 +1.372809E+11 +3.838041E+06 +7.978651E+11 +3.284366E+06 +6.000267E+11 +3.415545E+06 +6.273188E+11 +3.664195E+06 +7.879427E+11 +3.587293E+06 +9.920550E+11 +7.555655E+06 +2.958614E+12 +3.136367E+06 +5.698810E+11 +4.425221E+06 +1.090182E+12 +5.349680E+06 +1.601384E+12 +4.571635E+06 +1.116531E+12 +3.480027E+06 +6.946927E+11 +3.410988E+06 +6.135500E+11 +2.956921E+06 +5.456226E+11 +3.849969E+06 +8.590042E+11 +5.848881E+06 +1.842619E+12 +4.992325E+06 +1.491634E+12 +5.217210E+06 +1.450929E+12 +1.159970E+07 +7.155993E+12 +7.849803E+05 +3.676798E+10 +1.486403E+06 +1.171380E+11 +6.162755E+06 +1.936067E+12 +2.581138E+06 +3.504055E+11 +8.777419E+06 +5.124225E+12 +3.867422E+07 +8.940284E+13 +6.252498E+05 +2.797823E+10 +1.386801E+06 +9.832696E+10 +1.219518E+06 +7.524495E+10 +1.377031E+06 +9.892865E+10 +2.572278E+06 +3.346946E+11 +5.503366E+06 +1.544794E+12 +5.508395E+06 +1.568296E+12 +8.937474E+05 +4.416202E+10 +1.315355E+06 +1.010789E+11 +3.927717E+06 +8.017487E+11 +2.631941E+06 +3.604918E+11 +4.973776E+06 +1.320408E+12 +9.384108E+06 +4.487679E+12 +7.096218E+05 +2.913956E+10 +1.105527E+07 +6.197818E+12 +8.303532E+06 +3.557135E+12 +1.393782E+06 +1.059622E+11 +1.252992E+07 +8.452169E+12 +7.598952E+06 +2.951110E+12 +5.548974E+05 +1.868332E+10 +6.795833E+06 +2.499745E+12 +1.276300E+07 +8.359363E+12 +3.473792E+06 +6.366007E+11 +4.292349E+06 +1.051648E+12 +1.040833E+06 +6.564349E+10 +6.971116E+05 +3.020991E+10 +2.218284E+07 +2.522320E+13 +1.296505E+07 +8.891396E+12 +6.897286E+06 +2.768700E+12 +6.825276E+07 +2.402817E+14 +2.800386E+06 +4.295429E+11 +1.784838E+06 +1.678569E+11 +8.425260E+06 +3.647005E+12 +9.344687E+06 +4.541404E+12 +2.352474E+07 +5.039598E+13 +4.715877E+08 +1.133465E+16 diff --git a/tests/regression_tests/random_ray_volume_estimator_linear/adaptive/inputs_true.dat b/tests/regression_tests/random_ray_volume_estimator_linear/adaptive/inputs_true.dat new file mode 100644 index 00000000000..82587334ecb --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator_linear/adaptive/inputs_true.dat @@ -0,0 +1,248 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 10 + 40 + 20 + + + 100.0 1.0 + + + universe + 1 + + + multi-group + + 500.0 + 100.0 + + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + + true + adaptive + linear + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/random_ray_volume_estimator_linear/adaptive/results_true.dat b/tests/regression_tests/random_ray_volume_estimator_linear/adaptive/results_true.dat new file mode 100644 index 00000000000..c80049b6427 --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator_linear/adaptive/results_true.dat @@ -0,0 +1,9 @@ +tally 1: +2.285865E+00 +2.736206E-01 +tally 2: +1.030651E-01 +8.460405E-04 +tally 3: +7.895001E-03 +4.249732E-06 diff --git a/tests/regression_tests/random_ray_volume_estimator_linear/hybrid/inputs_true.dat b/tests/regression_tests/random_ray_volume_estimator_linear/hybrid/inputs_true.dat index dd11567f69d..53d7bbac171 100644 --- a/tests/regression_tests/random_ray_volume_estimator_linear/hybrid/inputs_true.dat +++ b/tests/regression_tests/random_ray_volume_estimator_linear/hybrid/inputs_true.dat @@ -191,7 +191,7 @@ fixed source - 90 + 10 40 20 @@ -215,8 +215,8 @@ true - linear hybrid + linear diff --git a/tests/regression_tests/random_ray_volume_estimator_linear/hybrid/results_true.dat b/tests/regression_tests/random_ray_volume_estimator_linear/hybrid/results_true.dat index e90d6bfdcb8..e1937ae6510 100644 --- a/tests/regression_tests/random_ray_volume_estimator_linear/hybrid/results_true.dat +++ b/tests/regression_tests/random_ray_volume_estimator_linear/hybrid/results_true.dat @@ -1,9 +1,9 @@ tally 1: -2.339086E+00 -2.747305E-01 +2.334964E+00 +3.212860E-01 tally 2: -1.089827E-01 -6.069324E-04 +8.558657E-02 +5.274716E-03 tally 3: -7.300831E-03 -2.715940E-06 +6.549250E-03 +2.816575E-06 diff --git a/tests/regression_tests/random_ray_volume_estimator_linear/naive/inputs_true.dat b/tests/regression_tests/random_ray_volume_estimator_linear/naive/inputs_true.dat index 6933fba435e..31620e93ee8 100644 --- a/tests/regression_tests/random_ray_volume_estimator_linear/naive/inputs_true.dat +++ b/tests/regression_tests/random_ray_volume_estimator_linear/naive/inputs_true.dat @@ -191,7 +191,7 @@ fixed source - 90 + 10 40 20 @@ -215,8 +215,8 @@ true - linear naive + linear diff --git a/tests/regression_tests/random_ray_volume_estimator_linear/naive/results_true.dat b/tests/regression_tests/random_ray_volume_estimator_linear/naive/results_true.dat index 5258ffd9c84..991fc3e9abe 100644 --- a/tests/regression_tests/random_ray_volume_estimator_linear/naive/results_true.dat +++ b/tests/regression_tests/random_ray_volume_estimator_linear/naive/results_true.dat @@ -1,9 +1,9 @@ tally 1: -2.339567E+00 -2.748423E-01 +2.313040E+00 +3.180880E-01 tally 2: -1.085878E-01 -6.024509E-04 +1.040963E-01 +6.664367E-04 tally 3: -7.299803E-03 -2.741867E-06 +6.583311E-03 +2.750085E-06 diff --git a/tests/regression_tests/random_ray_volume_estimator_linear/simulation_averaged/inputs_true.dat b/tests/regression_tests/random_ray_volume_estimator_linear/simulation_averaged/inputs_true.dat index 3ccab1d21b7..b681d2618a5 100644 --- a/tests/regression_tests/random_ray_volume_estimator_linear/simulation_averaged/inputs_true.dat +++ b/tests/regression_tests/random_ray_volume_estimator_linear/simulation_averaged/inputs_true.dat @@ -191,7 +191,7 @@ fixed source - 90 + 10 40 20 @@ -215,8 +215,8 @@ true - linear simulation_averaged + linear diff --git a/tests/regression_tests/random_ray_volume_estimator_linear/simulation_averaged/results_true.dat b/tests/regression_tests/random_ray_volume_estimator_linear/simulation_averaged/results_true.dat index 1e8aa9fb75f..0ea3a88bd83 100644 --- a/tests/regression_tests/random_ray_volume_estimator_linear/simulation_averaged/results_true.dat +++ b/tests/regression_tests/random_ray_volume_estimator_linear/simulation_averaged/results_true.dat @@ -1,9 +1,9 @@ tally 1: -2.670850E+02 -4.432939E+05 +-5.372146E+03 +9.133101E+06 tally 2: -1.116994E-01 -6.491358E-04 +7.428803E-02 +3.870266E-03 tally 3: -7.564527E-03 -2.947794E-06 +4.615173E-03 +2.683571E-06 diff --git a/tests/regression_tests/random_ray_volume_estimator_linear/strict_adaptive/inputs_true.dat b/tests/regression_tests/random_ray_volume_estimator_linear/strict_adaptive/inputs_true.dat new file mode 100644 index 00000000000..4d0d5f1f7a6 --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator_linear/strict_adaptive/inputs_true.dat @@ -0,0 +1,248 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 10 + 40 + 20 + + + 100.0 1.0 + + + universe + 1 + + + multi-group + + 500.0 + 100.0 + + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + + true + strict_adaptive + linear + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/random_ray_volume_estimator_linear/strict_adaptive/results_true.dat b/tests/regression_tests/random_ray_volume_estimator_linear/strict_adaptive/results_true.dat new file mode 100644 index 00000000000..4e0dbef63cd --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator_linear/strict_adaptive/results_true.dat @@ -0,0 +1,9 @@ +tally 1: +2.282556E+00 +2.728257E-01 +tally 2: +1.337071E-01 +1.094397E-03 +tally 3: +8.725872E-03 +5.015646E-06 diff --git a/tests/regression_tests/random_ray_volume_estimator_linear/test.py b/tests/regression_tests/random_ray_volume_estimator_linear/test.py index 94a14f3ad3b..5be5b79b04c 100644 --- a/tests/regression_tests/random_ray_volume_estimator_linear/test.py +++ b/tests/regression_tests/random_ray_volume_estimator_linear/test.py @@ -16,17 +16,28 @@ def _cleanup(self): os.remove(f) +# A deliberately ray-starved configuration (~20% source region miss rate). +# The volume estimators only differ meaningfully when regions are missed or +# sparsely hit, so a starved run exercises every estimator code path. At +# this density the per-estimator volume choices, the miss treatments, and +# for the adaptive estimator the strong-source (kappa) demotion, the +# hit-starved demotion, the end-of-inactive converged-negative demotion, +# and the previous-flux miss treatment all fire. @pytest.mark.parametrize("estimator", ["hybrid", "simulation_averaged", - "naive" + "naive", + "adaptive", + "strict_adaptive" ]) def test_random_ray_volume_estimator_linear(estimator): with change_directory(estimator): openmc.reset_auto_ids() model = random_ray_three_region_cube() - model.settings.random_ray['source_shape'] = 'linear' model.settings.random_ray['volume_estimator'] = estimator + model.settings.random_ray['source_shape'] = 'linear' + model.settings.particles = 10 model.settings.inactive = 20 model.settings.batches = 40 + harness = MGXSTestHarness('statepoint.40.h5', model) harness.main() diff --git a/tests/regression_tests/weightwindows_fw_cadis_mesh/flat/inputs_true.dat b/tests/regression_tests/weightwindows_fw_cadis_mesh/flat/inputs_true.dat index a0d84257a8d..ed14e0fbf6d 100644 --- a/tests/regression_tests/weightwindows_fw_cadis_mesh/flat/inputs_true.dat +++ b/tests/regression_tests/weightwindows_fw_cadis_mesh/flat/inputs_true.dat @@ -236,6 +236,7 @@ flat + hybrid diff --git a/tests/regression_tests/weightwindows_fw_cadis_mesh/linear/inputs_true.dat b/tests/regression_tests/weightwindows_fw_cadis_mesh/linear/inputs_true.dat index 62f8478586b..33c75bb271d 100644 --- a/tests/regression_tests/weightwindows_fw_cadis_mesh/linear/inputs_true.dat +++ b/tests/regression_tests/weightwindows_fw_cadis_mesh/linear/inputs_true.dat @@ -236,6 +236,7 @@ linear + hybrid diff --git a/tests/regression_tests/weightwindows_fw_cadis_mesh/test.py b/tests/regression_tests/weightwindows_fw_cadis_mesh/test.py index 680e9dc6df7..40caf1700f6 100644 --- a/tests/regression_tests/weightwindows_fw_cadis_mesh/test.py +++ b/tests/regression_tests/weightwindows_fw_cadis_mesh/test.py @@ -44,6 +44,7 @@ def test_weight_windows_fw_cadis_mesh(shape): model.settings.inactive = 20 model.settings.random_ray['source_shape'] = shape + model.settings.random_ray['volume_estimator'] = 'hybrid' harness = MGXSTestHarness('statepoint.30.h5', model) harness.main() diff --git a/tests/unit_tests/test_random_ray_default_persistence.py b/tests/unit_tests/test_random_ray_default_persistence.py new file mode 100644 index 00000000000..d261917a6c9 --- /dev/null +++ b/tests/unit_tests/test_random_ray_default_persistence.py @@ -0,0 +1,46 @@ +"""The random ray volume-estimator setting must not leak across an +in-process finalize/re-initialize cycle (openmc.lib workflows such as +iterative weight window generation). The solver never modifies the +configured setting, as "auto" is resolved into a separate run-scoped value. +However, the configured setting is a static that XML parsing only assigns +when the element is present, so openmc_finalize_random_ray() must restore +the "auto" default between runs. Running an explicit estimator first and a +default model second makes a missed restore visible, as the second run +would report the first run's estimator instead of resolving "auto". The +default second run is an adjoint solve, which also pins the auto routing +under openmc.lib (it must resolve to the strict adaptive estimator, once +per solve).""" + +import openmc +import openmc.lib +from openmc.examples import random_ray_three_region_cube + + +def test_random_ray_default_estimator_persistence(run_in_tmpdir, capfd): + openmc.reset_auto_ids() + model = random_ray_three_region_cube() + model.settings.particles = 10 + model.settings.inactive = 2 + model.settings.batches = 4 + + reported = [] + for explicit in (True, False): + if explicit: + model.settings.random_ray['volume_estimator'] = 'naive' + else: + del model.settings.random_ray['volume_estimator'] + model.settings.random_ray['adjoint'] = True + model.export_to_model_xml() + openmc.lib.init() + openmc.lib.run_random_ray() + openmc.lib.finalize() + out = capfd.readouterr().out + for line in out.splitlines(): + if 'Volume Estimator Type' in line: + reported.append(line.split('=')[-1].strip()) + + # The forward run reports once, while the adjoint run reports once per + # solve (forward-for-adjoint, adjoint). + assert reported == ['Naive', 'Strict Adaptive (auto)', + 'Strict Adaptive (auto)'], ( + f"volume estimator leaked across in-process reruns: {reported}")