Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 24fdb84

Browse files
jtrammclaudepaulromano
authored
Tally 32-bit Overflow Fix (#3960)
Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]> Co-authored-by: Paul Romano <[email protected]>
1 parent f018524 commit 24fdb84

9 files changed

Lines changed: 67 additions & 28 deletions

File tree

include/openmc/random_ray/source_region.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,10 @@ inline void hash_combine(size_t& seed, const size_t v)
5151
// every iteration.
5252
struct TallyTask {
5353
int tally_idx;
54-
int filter_idx;
54+
int64_t filter_idx;
5555
int score_idx;
5656
int score_type;
57-
TallyTask(int tally_idx, int filter_idx, int score_idx, int score_type)
57+
TallyTask(int tally_idx, int64_t filter_idx, int score_idx, int score_type)
5858
: tally_idx(tally_idx), filter_idx(filter_idx), score_idx(score_idx),
5959
score_type(score_type)
6060
{}
@@ -690,7 +690,7 @@ class SourceRegionContainer {
690690
// Private Methods
691691

692692
// Helper function for indexing
693-
inline int index(int64_t sr, int g) const { return sr * negroups_ + g; }
693+
inline int64_t index(int64_t sr, int g) const { return sr * negroups_ + g; }
694694
};
695695

696696
} // namespace openmc

include/openmc/tallies/tally.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,9 @@ class Tally {
9797
//! Given already-set filters, set the stride lengths
9898
void set_strides();
9999

100-
int32_t strides(int i) const { return strides_[i]; }
100+
int64_t strides(int i) const { return strides_[i]; }
101101

102-
int32_t n_filter_bins() const { return n_filter_bins_; }
102+
int64_t n_filter_bins() const { return n_filter_bins_; }
103103

104104
bool multiply_density() const { return multiply_density_; }
105105

@@ -184,9 +184,9 @@ class Tally {
184184
vector<int32_t> filters_; //!< Filter indices in global filters array
185185

186186
//! Index strides assigned to each filter to support 1D indexing.
187-
vector<int32_t> strides_;
187+
vector<int64_t> strides_;
188188

189-
int32_t n_filter_bins_ {0};
189+
int64_t n_filter_bins_ {0};
190190

191191
//! Whether to multiply by atom density for reaction rates
192192
bool multiply_density_ {true};

include/openmc/tallies/tally_scoring.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class FilterBinIter {
4141

4242
FilterBinIter& operator++();
4343

44-
int index_ {1};
44+
int64_t index_ {1};
4545
double weight_ {1.};
4646

4747
vector<FilterMatch>& filter_matches_;

src/mesh.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
#define _USE_MATH_DEFINES // to make M_PI declared in Intel and MSVC compilers
77
#include <cmath> // for ceil
88
#include <cstddef> // for size_t
9-
#include <numeric> // for accumulate
9+
#include <limits>
10+
#include <numeric> // for accumulate
1011
#include <string>
1112

1213
#ifdef _MSC_VER
@@ -1080,13 +1081,26 @@ int StructuredMesh::get_bin(Position r) const
10801081

10811082
int StructuredMesh::n_bins() const
10821083
{
1083-
return std::accumulate(
1084-
shape_.begin(), shape_.begin() + n_dimension_, 1, std::multiplies<>());
1084+
// Bin indices are stored as 32-bit ints in the tally system.
1085+
int64_t n = 1;
1086+
for (int i = 0; i < n_dimension_; ++i)
1087+
n *= shape_[i];
1088+
if (n > std::numeric_limits<int>::max()) {
1089+
fatal_error(fmt::format(
1090+
"Mesh {} has too many bins ({}) for 32-bit tally indexing", id_, n));
1091+
}
1092+
return static_cast<int>(n);
10851093
}
10861094

10871095
int StructuredMesh::n_surface_bins() const
10881096
{
1089-
return 4 * n_dimension_ * n_bins();
1097+
// Surface bin indices are stored as 32-bit ints in the tally system.
1098+
int64_t n = static_cast<int64_t>(n_bins()) * 4 * n_dimension_;
1099+
if (n > std::numeric_limits<int>::max()) {
1100+
fatal_error(fmt::format(
1101+
"Mesh {} has too many surface bins ({}) for tally indexing", id_, n));
1102+
}
1103+
return static_cast<int>(n);
10901104
}
10911105

10921106
tensor::Tensor<double> StructuredMesh::count_sites(

src/random_ray/flat_source_domain.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -725,7 +725,7 @@ void FlatSourceDomain::random_ray_tally()
725725
for (int i = 0; i < model::tallies.size(); i++) {
726726
Tally& tally {*model::tallies[i]};
727727
#pragma omp parallel for
728-
for (int bin = 0; bin < tally.n_filter_bins(); bin++) {
728+
for (int64_t bin = 0; bin < tally.n_filter_bins(); bin++) {
729729
for (int score_idx = 0; score_idx < tally.n_scores(); score_idx++) {
730730
auto score_type = tally.scores_[score_idx];
731731
if (score_type == SCORE_FLUX) {

src/tallies/tally.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,7 @@ void Tally::set_strides()
512512
// longest stride.
513513
auto n = filters_.size();
514514
strides_.resize(n, 0);
515-
int stride = 1;
515+
int64_t stride = 1;
516516
for (int i = n - 1; i >= 0; --i) {
517517
strides_[i] = stride;
518518
stride *= model::tally_filters[filters_[i]]->n_bins();
@@ -887,7 +887,7 @@ void Tally::accumulate()
887887
if (higher_moments_) {
888888
#pragma omp parallel for
889889
// filter bins (specific cell, energy bins)
890-
for (int i = 0; i < results_.shape(0); ++i) {
890+
for (int64_t i = 0; i < results_.shape(0); ++i) {
891891
// score bins (flux, total reaction rate, fission reaction rate, etc.)
892892
for (int j = 0; j < results_.shape(1); ++j) {
893893
double val = results_(i, j, TallyResult::VALUE) * norm;
@@ -902,7 +902,7 @@ void Tally::accumulate()
902902
} else {
903903
#pragma omp parallel for
904904
// filter bins (specific cell, energy bins)
905-
for (int i = 0; i < results_.shape(0); ++i) {
905+
for (int64_t i = 0; i < results_.shape(0); ++i) {
906906
// score bins (flux, total reaction rate, fission reaction rate, etc.)
907907
for (int j = 0; j < results_.shape(1); ++j) {
908908
double val = results_(i, j, TallyResult::VALUE) * norm;

src/tallies/tally_scoring.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ void score_fission_delayed_dg(int i_tally, int d_bin, double score,
158158
dg_match.bins_[i_bin] = d_bin;
159159

160160
// Determine the filter scoring index
161-
auto filter_index = 0;
161+
int64_t filter_index = 0;
162162
double filter_weight = 1.;
163163
for (auto i = 0; i < tally.filters().size(); ++i) {
164164
auto i_filt = tally.filters(i);
@@ -449,7 +449,7 @@ void score_fission_eout(Particle& p, int i_tally, int i_score, int score_bin)
449449
(score_bin == SCORE_PROMPT_NU_FISSION && g == 0)) {
450450

451451
// Find the filter scoring index for this filter combination
452-
int filter_index = 0;
452+
int64_t filter_index = 0;
453453
double filter_weight = 1.0;
454454
for (auto j = 0; j < tally.filters().size(); ++j) {
455455
auto i_filt = tally.filters(j);
@@ -497,7 +497,7 @@ void score_fission_eout(Particle& p, int i_tally, int i_score, int score_bin)
497497
} else {
498498

499499
// Find the filter index and weight for this filter combination
500-
int filter_index = 0;
500+
int64_t filter_index = 0;
501501
double filter_weight = 1.;
502502
for (auto j = 0; j < tally.filters().size(); ++j) {
503503
auto i_filt = tally.filters(j);
@@ -578,8 +578,8 @@ double get_nuclide_xs(const Particle& p, int i_nuclide, int score_bin)
578578
//! collision estimator.
579579

580580
void score_general_ce_nonanalog(Particle& p, int i_tally, int start_index,
581-
int filter_index, double filter_weight, int i_nuclide, double atom_density,
582-
double flux)
581+
int64_t filter_index, double filter_weight, int i_nuclide,
582+
double atom_density, double flux)
583583
{
584584
Tally& tally {*model::tallies[i_tally]};
585585

@@ -1112,8 +1112,8 @@ void score_general_ce_nonanalog(Particle& p, int i_tally, int start_index,
11121112
//! is not used for analog tallies.
11131113

11141114
void score_general_ce_analog(Particle& p, int i_tally, int start_index,
1115-
int filter_index, double filter_weight, int i_nuclide, double atom_density,
1116-
double flux)
1115+
int64_t filter_index, double filter_weight, int i_nuclide,
1116+
double atom_density, double flux)
11171117
{
11181118
Tally& tally {*model::tallies[i_tally]};
11191119

@@ -1615,8 +1615,8 @@ void score_general_ce_analog(Particle& p, int i_tally, int start_index,
16151615
//! argument is really just used for filter weights.
16161616

16171617
void score_general_mg(Particle& p, int i_tally, int start_index,
1618-
int filter_index, double filter_weight, int i_nuclide, double atom_density,
1619-
double flux)
1618+
int64_t filter_index, double filter_weight, int i_nuclide,
1619+
double atom_density, double flux)
16201620
{
16211621
auto& tally {*model::tallies[i_tally]};
16221622

src/tallies/trigger.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ KTrigger keff_trigger;
2828
//==============================================================================
2929

3030
std::pair<double, double> get_tally_uncertainty(
31-
int i_tally, int score_index, int filter_index)
31+
int i_tally, int score_index, int64_t filter_index)
3232
{
3333
const auto& tally {model::tallies[i_tally]};
3434

@@ -71,7 +71,7 @@ void check_tally_triggers(double& ratio, int& tally_id, int& score)
7171
continue;
7272

7373
const auto& results = t.results_;
74-
for (auto filter_index = 0; filter_index < results.shape(0);
74+
for (int64_t filter_index = 0; filter_index < results.shape(0);
7575
++filter_index) {
7676
// Compute the tally uncertainty metrics.
7777
auto uncert_pair =

tests/cpp_unit_tests/test_tally.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include "openmc/tallies/filter_energy.h"
12
#include "openmc/tallies/tally.h"
23
#include <catch2/catch_test_macros.hpp>
34

@@ -46,10 +47,34 @@ TEST_CASE("Test add/set_filter")
4647
REQUIRE(model::filter_map[cell_filter->id()] == tally->filters(0));
4748
REQUIRE(model::filter_map[particle_filter->id()] == tally->filters(1));
4849

49-
// set filters with a duplicate filter, should only add the filter to the tally once
50+
// set filters with a duplicate filter, should only add the filter to the
51+
// tally once
5052
filters = {cell_filter, cell_filter};
5153
tally->set_filters(filters);
5254
REQUIRE(tally->filters().size() == 1);
5355
REQUIRE(model::filter_map[cell_filter->id()] == tally->filters(0));
56+
}
5457

58+
// Regression test for 64-bit tally filter-bin counts (mesh x groups > 2^31).
59+
TEST_CASE("Tally filter-bin count does not overflow 32 bits")
60+
{
61+
// Two energy filters whose bin counts multiply to 2.5e9, above INT32_MAX.
62+
constexpr int64_t bins_per_filter = 50000;
63+
64+
// Only the bin count matters here, so the edge values are an arbitrary ramp.
65+
std::vector<double> edges(bins_per_filter + 1);
66+
for (int64_t i = 0; i < bins_per_filter + 1; ++i)
67+
edges[i] = static_cast<double>(i);
68+
69+
Tally* tally = Tally::create();
70+
for (int i = 0; i < 2; ++i) {
71+
Filter* filter = Filter::create("energy");
72+
dynamic_cast<EnergyFilter*>(filter)->set_bins(edges);
73+
tally->add_filter(filter);
74+
}
75+
tally->set_strides();
76+
77+
// set_strides() previously accumulated this product in a 32-bit int.
78+
REQUIRE(tally->n_filter_bins() == bins_per_filter * bins_per_filter);
79+
REQUIRE(tally->n_filter_bins() > 2147483647);
5580
}

0 commit comments

Comments
 (0)