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
4649namespace openmc {
@@ -56,11 +59,13 @@ vector<unique_ptr<Tally>> tallies;
5659vector<int > active_tallies;
5760vector<int > active_analog_tallies;
5861vector<int > active_tracklength_tallies;
62+ vector<int > active_timed_tracklength_tallies;
5963vector<int > active_collision_tallies;
6064vector<int > active_meshsurf_tallies;
6165vector<int > active_surface_tallies;
6266vector<int > active_pulse_height_tallies;
6367vector<int > pulse_height_cells;
68+ vector<double > time_grid;
6469} // namespace model
6570
6671namespace 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
494500void 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+
10671107void 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.
14701522extern " C" int openmc_tally_results (
14711523 int32_t index, double ** results, size_t * shape)
14721524{
0 commit comments