Skip to content

Commit afd9d06

Browse files
GuyStenpaulromano
andauthored
Fixed a bug when combining TimeFilter, MeshFilter, and tracklength estimator (#3525)
Co-authored-by: Paul Romano <paul.k.romano@gmail.com>
1 parent c717528 commit afd9d06

6 files changed

Lines changed: 227 additions & 20 deletions

File tree

include/openmc/tallies/tally.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,11 +203,14 @@ extern vector<unique_ptr<Tally>> tallies;
203203
extern vector<int> active_tallies;
204204
extern vector<int> active_analog_tallies;
205205
extern vector<int> active_tracklength_tallies;
206+
extern vector<int> active_timed_tracklength_tallies;
206207
extern vector<int> active_collision_tallies;
207208
extern vector<int> active_meshsurf_tallies;
208209
extern vector<int> active_surface_tallies;
209210
extern vector<int> active_pulse_height_tallies;
210211
extern vector<int> pulse_height_cells;
212+
extern vector<double> time_grid;
213+
211214
} // namespace model
212215

213216
namespace simulation {
@@ -239,6 +242,13 @@ void read_tallies_xml(pugi::xml_node root);
239242
//! batch to a new random variable
240243
void accumulate_tallies();
241244

245+
//! Determine distance to next time boundary
246+
//
247+
//! \param time Current time of particle
248+
//! \param speed Speed of particle
249+
//! \return Distance to next time boundary (or INFTY if none)
250+
double distance_to_time_boundary(double time, double speed);
251+
242252
//! Determine which tallies should be active
243253
void setup_active_tallies();
244254

include/openmc/tallies/tally_scoring.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,16 @@ void score_analog_tally_mg(Particle& p);
9191
//! \param distance The distance in [cm] traveled by the particle
9292
void score_tracklength_tally(Particle& p, double distance);
9393

94+
//! Score time filtered tallies using a tracklength estimate of the flux.
95+
//
96+
//! This is triggered at every event (surface crossing, lattice crossing, or
97+
//! collision) and thus cannot be done for tallies that require post-collision
98+
//! information.
99+
//
100+
//! \param p The particle being tracked
101+
//! \param total_distance The distance in [cm] traveled by the particle
102+
void score_timed_tracklength_tally(Particle& p, double total_distance);
103+
94104
//! Score surface or mesh-surface tallies for particle currents.
95105
//
96106
//! \param p The particle being tracked

src/particle.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,11 @@ void Particle::event_advance()
252252
this->time() += dt;
253253
this->lifetime() += dt;
254254

255+
// Score timed track-length tallies
256+
if (!model::active_timed_tracklength_tallies.empty()) {
257+
score_timed_tracklength_tally(*this, distance);
258+
}
259+
255260
// Score track-length tallies
256261
if (!model::active_tracklength_tallies.empty()) {
257262
score_tracklength_tally(*this, distance);

src/tallies/tally.cpp

Lines changed: 66 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,23 @@
2727
#include "openmc/tallies/filter_legendre.h"
2828
#include "openmc/tallies/filter_mesh.h"
2929
#include "openmc/tallies/filter_meshborn.h"
30+
#include "openmc/tallies/filter_meshmaterial.h"
3031
#include "openmc/tallies/filter_meshsurface.h"
3132
#include "openmc/tallies/filter_particle.h"
3233
#include "openmc/tallies/filter_sph_harm.h"
3334
#include "openmc/tallies/filter_surface.h"
35+
#include "openmc/tallies/filter_time.h"
3436
#include "openmc/xml_interface.h"
3537

3638
#include "xtensor/xadapt.hpp"
3739
#include "xtensor/xbuilder.hpp" // for empty_like
3840
#include "xtensor/xview.hpp"
3941
#include <fmt/core.h>
4042

41-
#include <algorithm> // for max
43+
#include <algorithm> // for max, set_union
4244
#include <cassert>
43-
#include <cstddef> // for size_t
45+
#include <cstddef> // for size_t
46+
#include <iterator> // for back_inserter
4447
#include <string>
4548

4649
namespace openmc {
@@ -56,11 +59,13 @@ vector<unique_ptr<Tally>> tallies;
5659
vector<int> active_tallies;
5760
vector<int> active_analog_tallies;
5861
vector<int> active_tracklength_tallies;
62+
vector<int> active_timed_tracklength_tallies;
5963
vector<int> active_collision_tallies;
6064
vector<int> active_meshsurf_tallies;
6165
vector<int> active_surface_tallies;
6266
vector<int> active_pulse_height_tallies;
6367
vector<int> pulse_height_cells;
68+
vector<double> time_grid;
6469
} // namespace model
6570

6671
namespace simulation {
@@ -243,8 +248,8 @@ Tally::Tally(pugi::xml_node node)
243248
for (int score : scores_) {
244249
switch (score) {
245250
case SCORE_PULSE_HEIGHT:
246-
fatal_error(
247-
"For pulse-height tallies, photon transport needs to be activated.");
251+
fatal_error("For pulse-height tallies, photon transport needs to be "
252+
"activated.");
248253
break;
249254
}
250255
}
@@ -318,7 +323,8 @@ Tally::Tally(pugi::xml_node node)
318323
if (has_energyout && i_nuc == -1) {
319324
fatal_error(fmt::format(
320325
"Error on tally {}: Cannot use a "
321-
"'nuclide_density' or 'temperature' derivative on a tally with an "
326+
"'nuclide_density' or 'temperature' derivative on a tally with "
327+
"an "
322328
"outgoing energy filter and 'total' nuclide rate. Instead, tally "
323329
"each nuclide in the material individually.",
324330
id_));
@@ -493,9 +499,9 @@ void Tally::add_filter(Filter* filter)
493499

494500
void Tally::set_strides()
495501
{
496-
// Set the strides. Filters are traversed in reverse so that the last filter
497-
// has the shortest stride in memory and the first filter has the longest
498-
// stride.
502+
// Set the strides. Filters are traversed in reverse so that the last
503+
// filter has the shortest stride in memory and the first filter has the
504+
// longest stride.
499505
auto n = filters_.size();
500506
strides_.resize(n, 0);
501507
int stride = 1;
@@ -551,7 +557,8 @@ void Tally::set_scores(const vector<std::string>& scores)
551557

552558
// Iterate over the given scores.
553559
for (auto score_str : scores) {
554-
// Make sure a delayed group filter wasn't used with an incompatible score.
560+
// Make sure a delayed group filter wasn't used with an incompatible
561+
// score.
555562
if (delayedgroup_filter_ != C_NONE) {
556563
if (score_str != "delayed-nu-fission" && score_str != "decay-rate")
557564
fatal_error("Cannot tally " + score_str + "with a delayedgroup filter");
@@ -984,8 +991,8 @@ void reduce_tally_results()
984991
}
985992
}
986993

987-
// Note that global tallies are *always* reduced even when no_reduce option is
988-
// on.
994+
// Note that global tallies are *always* reduced even when no_reduce option
995+
// is on.
989996

990997
// Get view of global tally values
991998
auto& gt = simulation::global_tallies;
@@ -1064,21 +1071,59 @@ void accumulate_tallies()
10641071
}
10651072
}
10661073

1074+
double distance_to_time_boundary(double time, double speed)
1075+
{
1076+
if (model::time_grid.empty()) {
1077+
return INFTY;
1078+
} else if (time >= model::time_grid.back()) {
1079+
return INFTY;
1080+
} else {
1081+
double next_time =
1082+
*std::upper_bound(model::time_grid.begin(), model::time_grid.end(), time);
1083+
return (next_time - time) * speed;
1084+
}
1085+
}
1086+
1087+
//! Add new points to the global time grid
1088+
//
1089+
//! \param grid Vector of new time points to add
1090+
void add_to_time_grid(vector<double> grid)
1091+
{
1092+
if (grid.empty())
1093+
return;
1094+
1095+
// Create new vector with enough space to hold old and new grid points
1096+
vector<double> merged;
1097+
merged.reserve(model::time_grid.size() + grid.size());
1098+
1099+
// Merge and remove duplicates
1100+
std::set_union(model::time_grid.begin(), model::time_grid.end(), grid.begin(),
1101+
grid.end(), std::back_inserter(merged));
1102+
1103+
// Swap in the new grid
1104+
model::time_grid.swap(merged);
1105+
}
1106+
10671107
void setup_active_tallies()
10681108
{
10691109
model::active_tallies.clear();
10701110
model::active_analog_tallies.clear();
10711111
model::active_tracklength_tallies.clear();
1112+
model::active_timed_tracklength_tallies.clear();
10721113
model::active_collision_tallies.clear();
10731114
model::active_meshsurf_tallies.clear();
10741115
model::active_surface_tallies.clear();
10751116
model::active_pulse_height_tallies.clear();
1117+
model::time_grid.clear();
10761118

10771119
for (auto i = 0; i < model::tallies.size(); ++i) {
10781120
const auto& tally {*model::tallies[i]};
10791121

10801122
if (tally.active_) {
10811123
model::active_tallies.push_back(i);
1124+
bool mesh_present = (tally.get_filter<MeshFilter>() ||
1125+
tally.get_filter<MeshMaterialFilter>());
1126+
auto time_filter = tally.get_filter<TimeFilter>();
10821127
switch (tally.type_) {
10831128

10841129
case TallyType::VOLUME:
@@ -1087,7 +1132,12 @@ void setup_active_tallies()
10871132
model::active_analog_tallies.push_back(i);
10881133
break;
10891134
case TallyEstimator::TRACKLENGTH:
1090-
model::active_tracklength_tallies.push_back(i);
1135+
if (time_filter && mesh_present) {
1136+
model::active_timed_tracklength_tallies.push_back(i);
1137+
add_to_time_grid(time_filter->bins());
1138+
} else {
1139+
model::active_tracklength_tallies.push_back(i);
1140+
}
10911141
break;
10921142
case TallyEstimator::COLLISION:
10931143
model::active_collision_tallies.push_back(i);
@@ -1123,10 +1173,12 @@ void free_memory_tally()
11231173
model::active_tallies.clear();
11241174
model::active_analog_tallies.clear();
11251175
model::active_tracklength_tallies.clear();
1176+
model::active_timed_tracklength_tallies.clear();
11261177
model::active_collision_tallies.clear();
11271178
model::active_meshsurf_tallies.clear();
11281179
model::active_surface_tallies.clear();
11291180
model::active_pulse_height_tallies.clear();
1181+
model::time_grid.clear();
11301182

11311183
model::tally_map.clear();
11321184
}
@@ -1465,8 +1517,8 @@ extern "C" int openmc_tally_get_n_realizations(int32_t index, int32_t* n)
14651517
return 0;
14661518
}
14671519

1468-
//! \brief Returns a pointer to a tally results array along with its shape. This
1469-
//! allows a user to obtain in-memory tally results from Python directly.
1520+
//! \brief Returns a pointer to a tally results array along with its shape.
1521+
//! This allows a user to obtain in-memory tally results from Python directly.
14701522
extern "C" int openmc_tally_results(
14711523
int32_t index, double** results, size_t* shape)
14721524
{

src/tallies/tally_scoring.cpp

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2404,15 +2404,13 @@ void score_analog_tally_mg(Particle& p)
24042404
match.bins_present_ = false;
24052405
}
24062406

2407-
void score_tracklength_tally(Particle& p, double distance)
2407+
void score_tracklength_tally_general(
2408+
Particle& p, double flux, const vector<int>& tallies)
24082409
{
2409-
// Determine the tracklength estimate of the flux
2410-
double flux = p.wgt() * distance;
2411-
24122410
// Set 'none' value for log union grid index
24132411
int i_log_union = C_NONE;
24142412

2415-
for (auto i_tally : model::active_tracklength_tallies) {
2413+
for (auto i_tally : tallies) {
24162414
const Tally& tally {*model::tallies[i_tally]};
24172415

24182416
// Initialize an iterator over valid filter bin combinations. If there are
@@ -2481,6 +2479,57 @@ void score_tracklength_tally(Particle& p, double distance)
24812479
match.bins_present_ = false;
24822480
}
24832481

2482+
void score_timed_tracklength_tally(Particle& p, double total_distance)
2483+
{
2484+
double speed = p.speed();
2485+
double total_dt = total_distance / speed;
2486+
2487+
// save particle last state
2488+
auto time_last = p.time_last();
2489+
auto r_last = p.r_last();
2490+
2491+
// move particle back
2492+
p.move_distance(-total_distance);
2493+
p.time() -= total_dt;
2494+
p.lifetime() -= total_dt;
2495+
2496+
double distance_traveled = 0.0;
2497+
while (distance_traveled < total_distance) {
2498+
2499+
double distance = std::min(distance_to_time_boundary(p.time(), speed),
2500+
total_distance - distance_traveled);
2501+
double dt = distance / speed;
2502+
2503+
// Save particle last state for tracklength tallies
2504+
p.time_last() = p.time();
2505+
p.r_last() = p.r();
2506+
2507+
// Advance particle in space and time
2508+
p.move_distance(distance);
2509+
p.time() += dt;
2510+
p.lifetime() += dt;
2511+
2512+
// Determine the tracklength estimate of the flux
2513+
double flux = p.wgt() * distance;
2514+
2515+
score_tracklength_tally_general(
2516+
p, flux, model::active_timed_tracklength_tallies);
2517+
distance_traveled += distance;
2518+
}
2519+
2520+
p.time_last() = time_last;
2521+
p.r_last() = r_last;
2522+
}
2523+
2524+
void score_tracklength_tally(Particle& p, double distance)
2525+
{
2526+
2527+
// Determine the tracklength estimate of the flux
2528+
double flux = p.wgt() * distance;
2529+
2530+
score_tracklength_tally_general(p, flux, model::active_tracklength_tallies);
2531+
}
2532+
24842533
void score_collision_tally(Particle& p)
24852534
{
24862535
// Determine the collision estimate of the flux

0 commit comments

Comments
 (0)