Skip to content
Merged
10 changes: 10 additions & 0 deletions include/openmc/tallies/tally.h
Original file line number Diff line number Diff line change
Expand Up @@ -203,11 +203,14 @@ extern vector<unique_ptr<Tally>> tallies;
extern vector<int> active_tallies;
extern vector<int> active_analog_tallies;
extern vector<int> active_tracklength_tallies;
extern vector<int> active_timed_tracklength_tallies;
extern vector<int> active_collision_tallies;
extern vector<int> active_meshsurf_tallies;
extern vector<int> active_surface_tallies;
extern vector<int> active_pulse_height_tallies;
extern vector<int> pulse_height_cells;
extern vector<double> time_grid;

} // namespace model

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

//! Determine distance to next time boundary
//
//! \param time Current time of particle
//! \param speed Speed of particle
//! \return Distance to next time boundary (or INFTY if none)
double distance_to_time_boundary(double time, double speed);

//! Determine which tallies should be active
void setup_active_tallies();

Expand Down
10 changes: 10 additions & 0 deletions include/openmc/tallies/tally_scoring.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,16 @@ void score_analog_tally_mg(Particle& p);
//! \param distance The distance in [cm] traveled by the particle
void score_tracklength_tally(Particle& p, double distance);

//! Score time filtered tallies using a tracklength estimate of the flux.
//
//! This is triggered at every event (surface crossing, lattice crossing, or
//! collision) and thus cannot be done for tallies that require post-collision
//! information.
//
//! \param p The particle being tracked
//! \param total_distance The distance in [cm] traveled by the particle
void score_timed_tracklength_tally(Particle& p, double total_distance);

//! Score surface or mesh-surface tallies for particle currents.
//
//! \param p The particle being tracked
Expand Down
5 changes: 5 additions & 0 deletions src/particle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,11 @@ void Particle::event_advance()
this->time() += dt;
this->lifetime() += dt;

// Score timed track-length tallies
if (!model::active_timed_tracklength_tallies.empty()) {
score_timed_tracklength_tally(*this, distance);
}

// Score track-length tallies
if (!model::active_tracklength_tallies.empty()) {
score_tracklength_tally(*this, distance);
Expand Down
80 changes: 66 additions & 14 deletions src/tallies/tally.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,23 @@
#include "openmc/tallies/filter_legendre.h"
#include "openmc/tallies/filter_mesh.h"
#include "openmc/tallies/filter_meshborn.h"
#include "openmc/tallies/filter_meshmaterial.h"
#include "openmc/tallies/filter_meshsurface.h"
#include "openmc/tallies/filter_particle.h"
#include "openmc/tallies/filter_sph_harm.h"
#include "openmc/tallies/filter_surface.h"
#include "openmc/tallies/filter_time.h"
#include "openmc/xml_interface.h"

#include "xtensor/xadapt.hpp"
#include "xtensor/xbuilder.hpp" // for empty_like
#include "xtensor/xview.hpp"
#include <fmt/core.h>

#include <algorithm> // for max
#include <algorithm> // for max, set_union
#include <cassert>
#include <cstddef> // for size_t
#include <cstddef> // for size_t
#include <iterator> // for back_inserter
#include <string>

namespace openmc {
Expand All @@ -56,11 +59,13 @@ vector<unique_ptr<Tally>> tallies;
vector<int> active_tallies;
vector<int> active_analog_tallies;
vector<int> active_tracklength_tallies;
vector<int> active_timed_tracklength_tallies;
vector<int> active_collision_tallies;
vector<int> active_meshsurf_tallies;
vector<int> active_surface_tallies;
vector<int> active_pulse_height_tallies;
vector<int> pulse_height_cells;
vector<double> time_grid;
} // namespace model

namespace simulation {
Expand Down Expand Up @@ -243,8 +248,8 @@ Tally::Tally(pugi::xml_node node)
for (int score : scores_) {
switch (score) {
case SCORE_PULSE_HEIGHT:
fatal_error(
"For pulse-height tallies, photon transport needs to be activated.");
fatal_error("For pulse-height tallies, photon transport needs to be "
"activated.");
break;
}
}
Expand Down Expand Up @@ -318,7 +323,8 @@ Tally::Tally(pugi::xml_node node)
if (has_energyout && i_nuc == -1) {
fatal_error(fmt::format(
"Error on tally {}: Cannot use a "
"'nuclide_density' or 'temperature' derivative on a tally with an "
"'nuclide_density' or 'temperature' derivative on a tally with "
"an "
"outgoing energy filter and 'total' nuclide rate. Instead, tally "
"each nuclide in the material individually.",
id_));
Expand Down Expand Up @@ -493,9 +499,9 @@ void Tally::add_filter(Filter* filter)

void Tally::set_strides()
{
// Set the strides. Filters are traversed in reverse so that the last filter
// has the shortest stride in memory and the first filter has the longest
// stride.
// Set the strides. Filters are traversed in reverse so that the last
// filter has the shortest stride in memory and the first filter has the
// longest stride.
auto n = filters_.size();
strides_.resize(n, 0);
int stride = 1;
Expand Down Expand Up @@ -551,7 +557,8 @@ void Tally::set_scores(const vector<std::string>& scores)

// Iterate over the given scores.
for (auto score_str : scores) {
// Make sure a delayed group filter wasn't used with an incompatible score.
// Make sure a delayed group filter wasn't used with an incompatible
// score.
if (delayedgroup_filter_ != C_NONE) {
if (score_str != "delayed-nu-fission" && score_str != "decay-rate")
fatal_error("Cannot tally " + score_str + "with a delayedgroup filter");
Expand Down Expand Up @@ -984,8 +991,8 @@ void reduce_tally_results()
}
}

// Note that global tallies are *always* reduced even when no_reduce option is
// on.
// Note that global tallies are *always* reduced even when no_reduce option
// is on.

// Get view of global tally values
auto& gt = simulation::global_tallies;
Expand Down Expand Up @@ -1064,21 +1071,59 @@ void accumulate_tallies()
}
}

double distance_to_time_boundary(double time, double speed)
{
if (model::time_grid.empty()) {
return INFTY;
} else if (time >= model::time_grid.back()) {
return INFTY;
} else {
double next_time =
*std::upper_bound(model::time_grid.begin(), model::time_grid.end(), time);
return (next_time - time) * speed;
}
}

//! Add new points to the global time grid
//
//! \param grid Vector of new time points to add
void add_to_time_grid(vector<double> grid)
{
if (grid.empty())
return;

// Create new vector with enough space to hold old and new grid points
vector<double> merged;
merged.reserve(model::time_grid.size() + grid.size());

// Merge and remove duplicates
std::set_union(model::time_grid.begin(), model::time_grid.end(), grid.begin(),
grid.end(), std::back_inserter(merged));

// Swap in the new grid
model::time_grid.swap(merged);
}

void setup_active_tallies()
{
model::active_tallies.clear();
model::active_analog_tallies.clear();
model::active_tracklength_tallies.clear();
model::active_timed_tracklength_tallies.clear();
model::active_collision_tallies.clear();
model::active_meshsurf_tallies.clear();
model::active_surface_tallies.clear();
model::active_pulse_height_tallies.clear();
model::time_grid.clear();

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

if (tally.active_) {
model::active_tallies.push_back(i);
bool mesh_present = (tally.get_filter<MeshFilter>() ||
tally.get_filter<MeshMaterialFilter>());
auto time_filter = tally.get_filter<TimeFilter>();
switch (tally.type_) {

case TallyType::VOLUME:
Expand All @@ -1087,7 +1132,12 @@ void setup_active_tallies()
model::active_analog_tallies.push_back(i);
break;
case TallyEstimator::TRACKLENGTH:
model::active_tracklength_tallies.push_back(i);
if (time_filter && mesh_present) {
model::active_timed_tracklength_tallies.push_back(i);
add_to_time_grid(time_filter->bins());
} else {
model::active_tracklength_tallies.push_back(i);
}
break;
case TallyEstimator::COLLISION:
model::active_collision_tallies.push_back(i);
Expand Down Expand Up @@ -1123,10 +1173,12 @@ void free_memory_tally()
model::active_tallies.clear();
model::active_analog_tallies.clear();
model::active_tracklength_tallies.clear();
model::active_timed_tracklength_tallies.clear();
model::active_collision_tallies.clear();
model::active_meshsurf_tallies.clear();
model::active_surface_tallies.clear();
model::active_pulse_height_tallies.clear();
model::time_grid.clear();

model::tally_map.clear();
}
Expand Down Expand Up @@ -1465,8 +1517,8 @@ extern "C" int openmc_tally_get_n_realizations(int32_t index, int32_t* n)
return 0;
}

//! \brief Returns a pointer to a tally results array along with its shape. This
//! allows a user to obtain in-memory tally results from Python directly.
//! \brief Returns a pointer to a tally results array along with its shape.
//! This allows a user to obtain in-memory tally results from Python directly.
extern "C" int openmc_tally_results(
int32_t index, double** results, size_t* shape)
{
Expand Down
59 changes: 54 additions & 5 deletions src/tallies/tally_scoring.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2404,15 +2404,13 @@ void score_analog_tally_mg(Particle& p)
match.bins_present_ = false;
}

void score_tracklength_tally(Particle& p, double distance)
void score_tracklength_tally_general(
Particle& p, double flux, const vector<int>& tallies)
{
// Determine the tracklength estimate of the flux
double flux = p.wgt() * distance;

// Set 'none' value for log union grid index
int i_log_union = C_NONE;

for (auto i_tally : model::active_tracklength_tallies) {
for (auto i_tally : tallies) {
const Tally& tally {*model::tallies[i_tally]};

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

void score_timed_tracklength_tally(Particle& p, double total_distance)
{
double speed = p.speed();
double total_dt = total_distance / speed;

// save particle last state
auto time_last = p.time_last();
auto r_last = p.r_last();

// move particle back
p.move_distance(-total_distance);
p.time() -= total_dt;
p.lifetime() -= total_dt;

double distance_traveled = 0.0;
while (distance_traveled < total_distance) {

double distance = std::min(distance_to_time_boundary(p.time(), speed),
total_distance - distance_traveled);
double dt = distance / speed;

// Save particle last state for tracklength tallies
p.time_last() = p.time();
p.r_last() = p.r();

// Advance particle in space and time
p.move_distance(distance);
p.time() += dt;
p.lifetime() += dt;

// Determine the tracklength estimate of the flux
double flux = p.wgt() * distance;

score_tracklength_tally_general(
p, flux, model::active_timed_tracklength_tallies);
distance_traveled += distance;
}

p.time_last() = time_last;
p.r_last() = r_last;
}

void score_tracklength_tally(Particle& p, double distance)
{

// Determine the tracklength estimate of the flux
double flux = p.wgt() * distance;

score_tracklength_tally_general(p, flux, model::active_tracklength_tallies);
}

void score_collision_tally(Particle& p)
{
// Determine the collision estimate of the flux
Expand Down
Loading