LCOV - code coverage report
Current view: top level - src/tallies - TallyBase.C (source / functions) Hit Total Coverage
Test: neams-th-coe/cardinal: cf347a Lines: 339 355 95.5 %
Date: 2026-06-03 12:57:15 Functions: 23 23 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /********************************************************************/
       2             : /*                  SOFTWARE COPYRIGHT NOTIFICATION                 */
       3             : /*                             Cardinal                             */
       4             : /*                                                                  */
       5             : /*                  (c) 2021 UChicago Argonne, LLC                  */
       6             : /*                        ALL RIGHTS RESERVED                       */
       7             : /*                                                                  */
       8             : /*                 Prepared by UChicago Argonne, LLC                */
       9             : /*               Under Contract No. DE-AC02-06CH11357               */
      10             : /*                With the U. S. Department of Energy               */
      11             : /*                                                                  */
      12             : /*             Prepared by Battelle Energy Alliance, LLC            */
      13             : /*               Under Contract No. DE-AC07-05ID14517               */
      14             : /*                With the U. S. Department of Energy               */
      15             : /*                                                                  */
      16             : /*                 See LICENSE for full restrictions                */
      17             : /********************************************************************/
      18             : 
      19             : #ifdef ENABLE_OPENMC_COUPLING
      20             : #include "TallyBase.h"
      21             : 
      22             : #include "OpenMCCellAverageProblem.h"
      23             : #include "UserErrorChecking.h"
      24             : #include "AuxiliarySystem.h"
      25             : #include "FilterBase.h"
      26             : 
      27             : #include "AngularLegendreFilter.h"
      28             : #include "EnergyOutFilter.h"
      29             : #include "DelayedGroupFilter.h"
      30             : 
      31             : #include "openmc/settings.h"
      32             : 
      33             : InputParameters
      34        5094 : TallyBase::validParams()
      35             : {
      36        5094 :   auto params = MooseObject::validParams();
      37       10188 :   params.addParam<MultiMooseEnum>(
      38             :       "score",
      39       10188 :       getTallyScoreEnum(),
      40             :       "Score(s) to use in the OpenMC tallies. If not specified, defaults to 'kappa_fission'");
      41       10188 :   params.addParam<MooseEnum>(
      42       10188 :       "estimator", getTallyEstimatorEnum(), "Type of tally estimator to use in OpenMC");
      43       10188 :   params.addParam<std::vector<std::string>>(
      44             :       "name",
      45             :       "Auxiliary variable name(s) to use for OpenMC tallies. "
      46             :       "If not specified, defaults to the names of the scores");
      47             : 
      48       10188 :   params.addParam<std::vector<SubdomainName>>(
      49             :       "block",
      50             :       "Subdomains for which to add tallies in OpenMC. If not provided, "
      51             :       "tallies will be applied over the entire domain corresponding to the [Mesh] block.");
      52       10188 :   params.addParam<std::vector<SubdomainName>>("blocks",
      53             :                                               "This parameter is deprecated, use 'block' instead!");
      54             : 
      55        5094 :   MultiMooseEnum tally_trigger("rel_err none");
      56       10188 :   params.addParam<MultiMooseEnum>(
      57             :       "trigger",
      58             :       tally_trigger,
      59             :       "Trigger criterion to determine when OpenMC simulation is complete "
      60             :       "based on tallies. If multiple scores are specified in 'score, "
      61             :       "this same trigger is applied to all scores.");
      62       10188 :   params.addRangeCheckedParam<std::vector<Real>>(
      63             :       "trigger_threshold", "trigger_threshold > 0", "Threshold for the tally trigger");
      64       10188 :   params.addParam<std::vector<bool>>(
      65             :       "trigger_ignore_zeros",
      66             :       {false},
      67             :       "Whether tally bins with zero scores are ignored when computing the tally trigger. If only "
      68             :       "one "
      69             :       "value of 'trigger_ignore_zeros' is provided, that value is applied to all tally scores.");
      70             : 
      71             :   MultiMooseEnum openmc_outputs(
      72        5094 :       "unrelaxed_tally_std_dev unrelaxed_tally_rel_error unrelaxed_tally");
      73       10188 :   params.addParam<MultiMooseEnum>(
      74             :       "output",
      75             :       openmc_outputs,
      76             :       "UNRELAXED field(s) to output from OpenMC for each tally score. "
      77             :       "unrelaxed_tally_std_dev will write the standard deviation of "
      78             :       "each tally into auxiliary variables "
      79             :       "named *_std_dev. unrelaxed_tally_rel_error will write the "
      80             :       "relative standard deviation (unrelaxed_tally_std_dev / unrelaxed_tally) "
      81             :       "of each tally into auxiliary variables named *_rel_error. "
      82             :       "unrelaxed_tally will write the raw unrelaxed tally into auxiliary "
      83             :       "variables named *_raw (replace * with 'name').");
      84             : 
      85       10188 :   params.addParam<std::vector<std::string>>("filters", "External filters to add to this tally.");
      86             : 
      87       10188 :   params.addParam<bool>("check_tally_sum",
      88             :                         "Whether to check consistency between the local tallies "
      89             :                         "with a global tally sum. This will require that the "
      90             :                         "integral of the local tally matches a tally with no filters "
      91             :                         "(defined over the entire phase space).");
      92       10188 :   params.addParam<bool>(
      93             :       "normalize_by_global_tally",
      94       10188 :       true,
      95             :       "Whether to normalize local tallies by a global tally (true) or else by the sum "
      96             :       "of the local tally (false)");
      97             : 
      98        5094 :   params.addPrivateParam<OpenMCCellAverageProblem *>("_openmc_problem");
      99             : 
     100        5094 :   params.registerBase("Tally");
     101        5094 :   params.registerSystemAttributeName("Tally");
     102             : 
     103        5094 :   return params;
     104        5094 : }
     105             : 
     106        2701 : TallyBase::TallyBase(const InputParameters & parameters)
     107             :   : MooseObject(parameters),
     108        2701 :     _openmc_problem(*getParam<OpenMCCellAverageProblem *>("_openmc_problem")),
     109        2701 :     _mesh(_openmc_problem.mesh()),
     110        2701 :     _aux(_openmc_problem.getAuxiliarySystem()),
     111        2929 :     _tally_trigger(isParamValid("trigger") ? &getParam<MultiMooseEnum>("trigger") : nullptr),
     112        8103 :     _trigger_ignore_zeros(getParam<std::vector<bool>>("trigger_ignore_zeros")),
     113        2701 :     _normalize_by_global(_openmc_problem.runMode() == openmc::RunMode::FIXED_SOURCE
     114        2701 :                              ? false
     115        5296 :                              : getParam<bool>("normalize_by_global_tally")),
     116        2701 :     _check_tally_sum(isParamValid("check_tally_sum")
     117        6013 :                          ? getParam<bool>("check_tally_sum")
     118        2090 :                          : (_openmc_problem.runMode() == openmc::RunMode::FIXED_SOURCE
     119        2090 :                                 ? true
     120        2012 :                                 : _normalize_by_global)),
     121        2701 :     _needs_global_tally(_check_tally_sum || _normalize_by_global),
     122        5402 :     _renames_tally_vars(isParamValid("name")),
     123        5402 :     _has_outputs(isParamValid("output")),
     124        8103 :     _is_adaptive(_openmc_problem.hasAdaptivity())
     125             : {
     126        5402 :   if (isParamValid("score"))
     127             :   {
     128        3226 :     const auto & scores = getParam<MultiMooseEnum>("score");
     129        3642 :     for (const auto & score : scores)
     130        4058 :       _tally_score.push_back(_openmc_problem.enumToTallyScore(score));
     131             :   }
     132             :   else
     133        2176 :     _tally_score = {"kappa-fission"};
     134             : 
     135             :   const bool heating =
     136        2701 :       std::find(_tally_score.begin(), _tally_score.end(), "heating") != _tally_score.end();
     137             :   const bool nu_scatter =
     138        2701 :       std::find(_tally_score.begin(), _tally_score.end(), "nu-scatter") != _tally_score.end();
     139             : 
     140        5402 :   if (isParamValid("estimator"))
     141             :   {
     142         310 :     auto estimator = getParam<MooseEnum>("estimator").getEnum<tally::TallyEstimatorEnum>();
     143             : 
     144             :     // Photon heating tallies cannot use tracklength estimators.
     145         310 :     if (estimator == tally::tracklength && openmc::settings::photon_transport && heating)
     146           2 :       paramError("estimator",
     147             :                  "Tracklength estimators are currently incompatible with photon transport and "
     148             :                  "heating scores! For more information: https://tinyurl.com/3wre3kwt");
     149             : 
     150         308 :     if (estimator != tally::analog && nu_scatter)
     151           2 :       paramError("estimator", "Non-analog estimators are not supported for nu_scatter scores!");
     152             : 
     153         306 :     _estimator = _openmc_problem.tallyEstimator(estimator);
     154             :   }
     155             :   else
     156             :   {
     157             :     /**
     158             :      * Set a default of tracklength for all tallies other then heating tallies in photon transport
     159             :      * and nu_scatter tallies. This behavior must be overridden in derived tallies that implement
     160             :      * mesh filters.
     161             :      */
     162        2391 :     _estimator = openmc::TallyEstimator::TRACKLENGTH;
     163             : 
     164        2391 :     if (nu_scatter && !(heating && openmc::settings::photon_transport))
     165           8 :       _estimator = openmc::TallyEstimator::ANALOG;
     166        2383 :     else if (nu_scatter && heating && openmc::settings::photon_transport)
     167           2 :       paramError(
     168             :           "estimator",
     169             :           "A single tally cannot score both nu_scatter and heating when photon transport is "
     170             :           "enabled, as both scores require different estimators. Consider adding one tally "
     171             :           "which scores nu_scatter (with an analog estimator), and a second tally that scores "
     172             :           "heating (with a collision estimator).");
     173             : 
     174        2389 :     if (heating && openmc::settings::photon_transport)
     175           8 :       _estimator = openmc::TallyEstimator::COLLISION;
     176             :   }
     177             : 
     178        2695 :   if (heating && !openmc::settings::photon_transport)
     179         170 :     mooseWarning(
     180             :         "When using the 'heating' score with photon transport disabled, energy deposition\n"
     181             :         "from photons is neglected unless you specifically ran NJOY to produce MT=301 with\n"
     182             :         "photon energy deposited locally (not true for any pre-packaged OpenMC data libraries\n"
     183             :         "on openmc.org).\n\n"
     184             :         "If you did NOT specifically run NJOY yourself with this customization, we recommend\n"
     185             :         "using the 'heating_local' score instead, which will capture photon energy deposition.\n"
     186             :         "Otherwise, you will underpredict the true energy deposition.");
     187             : 
     188        8085 :   if (isParamValid("trigger") != isParamValid("trigger_threshold"))
     189           2 :     paramError("trigger",
     190             :                "You must either specify none or both of 'trigger' and "
     191             :                "'trigger_threshold'. You have specified only one.");
     192             : 
     193        2693 :   if (_tally_trigger)
     194             :   {
     195         224 :     checkRequiredParam(parameters, "trigger_threshold", "using tally triggers");
     196         224 :     _tally_trigger_threshold = getParam<std::vector<Real>>("trigger_threshold");
     197             : 
     198         112 :     if (_tally_trigger->size() != _tally_score.size())
     199           2 :       paramError("trigger",
     200           2 :                  "'trigger' (size " + std::to_string(_tally_trigger->size()) +
     201           2 :                      ") must have the same length as 'score' (size " +
     202           2 :                      std::to_string(_tally_score.size()) + ")");
     203             : 
     204         110 :     if (_tally_trigger_threshold.size() != _tally_score.size())
     205           2 :       paramError("trigger_threshold",
     206           2 :                  "'trigger_threshold' (size " + std::to_string(_tally_trigger_threshold.size()) +
     207           2 :                      ") must have the same length as 'score' (size " +
     208           2 :                      std::to_string(_tally_score.size()) + ")");
     209             : 
     210         108 :     if (_trigger_ignore_zeros.size() > 1)
     211             :     {
     212           2 :       if (_tally_score.size() != _trigger_ignore_zeros.size())
     213           2 :         paramError("trigger_ignore_zeros",
     214           2 :                    "'trigger_ignore_zeros' (size " + std::to_string(_trigger_ignore_zeros.size()) +
     215           2 :                        ") must have the same length as 'score' (size " +
     216           2 :                        std::to_string(_tally_score.size()) + ")");
     217             :     }
     218         106 :     else if (_trigger_ignore_zeros.size() == 1)
     219         104 :       _trigger_ignore_zeros.resize(_tally_score.size(), _trigger_ignore_zeros[0]);
     220             : 
     221         210 :     _openmc_problem.checkEmptyVector(_trigger_ignore_zeros, "trigger_ignore_zeros");
     222             :   }
     223             : 
     224             :   // Fetch the filters required by this tally. Error if the filter hasn't been added yet.
     225        5370 :   if (isParamValid("filters"))
     226             :   {
     227        2292 :     for (const auto & filter_name : getParam<std::vector<std::string>>("filters"))
     228             :     {
     229        2042 :       if (!_openmc_problem.hasFilter(filter_name))
     230           4 :         paramError("filters", "Filter with the name " + filter_name + " does not exist!");
     231             : 
     232        1020 :       _ext_filters.push_back(_openmc_problem.getFilter(filter_name));
     233             :     }
     234             :   }
     235             : 
     236             :   // Check the estimator to make sure it doesn't conflict with certain filters.
     237        3697 :   for (auto & f : _ext_filters)
     238             :   {
     239        1020 :     if ((dynamic_cast<AngularLegendreFilter *>(f.get()) ||
     240        1020 :          dynamic_cast<EnergyOutFilter *>(f.get())) &&
     241         186 :         _estimator != openmc::TallyEstimator::ANALOG)
     242           8 :       paramError("estimator",
     243           4 :                  "The filter " + f->name() +
     244             :                      " requires an analog estimator! Please ensure 'estimator' is set to analog.");
     245             : 
     246        1016 :     if (dynamic_cast<DelayedGroupFilter *>(f.get()))
     247          26 :       for (const auto & s : _tally_score)
     248          18 :         if (s != "delayed-nu-fission" && s != "decay-rate")
     249           4 :           paramError("score",
     250           2 :                      "The filter " + f->name() +
     251             :                          " can only be used with delayed_nu_fission and decay_rate scores!");
     252             :   }
     253             : 
     254        5354 :   if (isParamValid("name"))
     255        1746 :     _tally_name = getParam<std::vector<std::string>>("name");
     256             :   else
     257             :   {
     258        4462 :     for (auto score : _tally_score)
     259             :     {
     260             :       std::replace(score.begin(), score.end(), '-', '_');
     261        2367 :       _tally_name.push_back(score);
     262             :     }
     263             :   }
     264             : 
     265        2677 :   if (_has_outputs)
     266             :   {
     267             :     // names of output are appended to ends of 'name'
     268         509 :     for (const auto & o : getParam<MultiMooseEnum>("output"))
     269             :     {
     270             :       std::string name = o;
     271             : 
     272         175 :       if (o == "UNRELAXED_TALLY_STD_DEV")
     273         132 :         _output_name.push_back("std_dev");
     274         109 :       else if (o == "UNRELAXED_TALLY_REL_ERROR")
     275         136 :         _output_name.push_back("rel_error");
     276          41 :       else if (o == "UNRELAXED_TALLY")
     277          82 :         _output_name.push_back("raw");
     278             :       else
     279           0 :         mooseError("Unhandled OutputEnum in OpenMCCellAverageProblem!");
     280             :     }
     281             :   }
     282             : 
     283        2677 :   if (_tally_name.size() != _tally_score.size())
     284           2 :     paramError("name", "'name' must be the same length as 'score'!");
     285             : 
     286             :   // Modify the variable names so they take into account the bins in the external filters.
     287        2675 :   auto all_var_names = _tally_name;
     288        3689 :   for (const auto & filter : _ext_filters)
     289             :   {
     290             :     std::vector<std::string> n;
     291        2832 :     for (unsigned int i = 0; i < all_var_names.size(); ++i)
     292        4938 :       for (unsigned int j = 0; j < filter->numBins(); ++j)
     293        6240 :         n.push_back(all_var_names[i] + "_" + filter->binName(j));
     294             : 
     295        1014 :     all_var_names = n;
     296             : 
     297        1014 :     _num_ext_filter_bins *= filter->numBins();
     298        1014 :   }
     299        2675 :   _tally_name = all_var_names;
     300             : 
     301             :   // A map of external filter bins to skip when computing sums and means for normalization.
     302        2675 :   std::vector<bool> skip{false};
     303        3689 :   for (const auto & filter : _ext_filters)
     304             :   {
     305             :     std::vector<bool> s;
     306        2610 :     for (unsigned int i = 0; i < skip.size(); ++i)
     307        4356 :       for (unsigned int j = 0; j < filter->numBins(); ++j)
     308        3184 :         s.push_back(skip[i] || filter->skipBin(j));
     309             : 
     310        1014 :     skip = s;
     311             :   }
     312        2675 :   _ext_bins_to_skip = skip;
     313             : 
     314        5350 :   if (isParamSetByUser("blocks"))
     315           0 :     mooseError("This parameter is deprecated, use 'block' instead!");
     316             : 
     317        5350 :   if (isParamValid("block"))
     318             :   {
     319        4866 :     auto block_names = getParam<std::vector<SubdomainName>>("block");
     320        1622 :     if (block_names.empty())
     321           2 :       paramError("block", "Subdomain names must be provided if using 'block'!");
     322             : 
     323        1620 :     auto block_ids = _openmc_problem.getMooseMesh().getSubdomainIDs(block_names);
     324        1620 :     std::copy(
     325        1620 :         block_ids.begin(), block_ids.end(), std::inserter(_tally_blocks, _tally_blocks.end()));
     326             : 
     327             :     // Check to make sure all of the blocks are in the mesh.
     328        1620 :     const auto & subdomains = _openmc_problem.getMooseMesh().meshSubdomains();
     329        4133 :     for (std::size_t b = 0; b < block_names.size(); ++b)
     330        2515 :       if (subdomains.find(block_ids[b]) == subdomains.end())
     331           4 :         paramError("block",
     332           2 :                    "Block '" + block_names[b] + "' specified in 'block' not found in mesh!");
     333        1618 :   }
     334             :   else
     335             :   {
     336             :     // Tally over all mesh blocks if no blocks are provided.
     337        2280 :     for (const auto & s : _openmc_problem.getMooseMesh().meshSubdomains())
     338        1227 :       _tally_blocks.insert(s);
     339             :   }
     340             : 
     341        2671 :   _openmc_problem.checkDuplicateEntries(_tally_name, "name");
     342        2669 :   _openmc_problem.checkDuplicateEntries(_tally_score, "score");
     343             : 
     344        2667 :   _local_sum_tally.resize(_tally_score.size(), 0.0);
     345        2667 :   _local_mean_tally.resize(_tally_score.size(), 0.0);
     346             : 
     347        2667 :   _current_tally.resize(_tally_score.size());
     348        2667 :   _current_raw_tally.resize(_tally_score.size());
     349        2667 :   _current_raw_tally_rel_error.resize(_tally_score.size());
     350        2667 :   _current_raw_tally_std_dev.resize(_tally_score.size());
     351        2667 :   _previous_tally.resize(_tally_score.size());
     352        4843 : }
     353             : 
     354             : void
     355        2792 : TallyBase::initializeTally()
     356             : {
     357             :   // Clear cached results.
     358        2792 :   _local_sum_tally.clear();
     359        2792 :   _local_sum_tally.resize(_tally_score.size(), 0.0);
     360        2792 :   _local_mean_tally.clear();
     361        2792 :   _local_mean_tally.resize(_tally_score.size(), 0.0);
     362             : 
     363        2792 :   if (_linked_tallies.size() > 0)
     364             :   {
     365         532 :     _linked_local_sum_tally.clear();
     366         532 :     _linked_local_sum_tally.resize(_tally_score.size(), 0.0);
     367             :   }
     368             : 
     369        2792 :   _current_tally.resize(_tally_score.size());
     370        2792 :   _current_raw_tally.resize(_tally_score.size());
     371        2792 :   _current_raw_tally_rel_error.resize(_tally_score.size());
     372        2792 :   _current_raw_tally_std_dev.resize(_tally_score.size());
     373        2792 :   _previous_tally.resize(_tally_score.size());
     374             : 
     375        2792 :   if (_needs_global_tally)
     376             :   {
     377        1588 :     _global_sum_tally.clear();
     378        1588 :     _global_sum_tally.resize(_tally_score.size(), 0.0);
     379             :   }
     380             : 
     381             :   // create the global tally for normalization; we make sure to use the
     382             :   // same estimator as the local tally
     383        2792 :   if (addingGlobalTally())
     384             :   {
     385        1458 :     _global_tally_index = openmc::model::tallies.size();
     386        1458 :     _global_tally = openmc::Tally::create();
     387        1458 :     _global_tally->set_scores(_tally_score);
     388        1458 :     _global_tally->estimator_ = _estimator;
     389             :   }
     390             : 
     391        2792 :   auto [index, spatial_filter] = spatialFilter();
     392        2782 :   _filter_index = index;
     393             : 
     394             :   std::vector<openmc::Filter *> filters;
     395        3796 :   for (auto & filter : _ext_filters)
     396        1014 :     filters.push_back(filter->getWrappedFilter());
     397             :   // We add the spatial filter last to minimize the number of cache
     398             :   // misses during the OpenMC -> Cardinal transfer.
     399        2782 :   filters.push_back(spatial_filter);
     400             : 
     401             :   // Create the tally, assign the required filters and apply the triggers.
     402        2782 :   _local_tally_index = openmc::model::tallies.size();
     403        2782 :   _local_tally = openmc::Tally::create();
     404        2782 :   _local_tally->set_scores(_tally_score);
     405        2782 :   _local_tally->estimator_ = _estimator;
     406        2782 :   _local_tally->set_filters(filters);
     407        2782 :   applyTriggersToLocalTally(_local_tally);
     408        2782 : }
     409             : 
     410             : void
     411         278 : TallyBase::resetTally()
     412             : {
     413             :   // Erase the tally.
     414         278 :   openmc::model::tallies.erase(openmc::model::tallies.begin() + _local_tally_index);
     415             : 
     416             :   // Erase global tally.
     417         278 :   if (addingGlobalTally())
     418         150 :     openmc::model::tallies.erase(openmc::model::tallies.begin() + _global_tally_index);
     419             : 
     420             :   // Erase the filter(s).
     421         278 :   openmc::model::tally_filters.erase(openmc::model::tally_filters.begin() + _filter_index);
     422         278 : }
     423             : 
     424             : Real
     425        4066 : TallyBase::storeResults(const std::vector<unsigned int> & var_numbers,
     426             :                         unsigned int local_score,
     427             :                         const std::string & output_type)
     428             : {
     429        4066 :   Real total = 0.0;
     430             : 
     431        4066 :   if (output_type == "relaxed")
     432        3770 :     total += storeResultsInner(var_numbers, local_score, _current_tally);
     433         296 :   else if (output_type == "rel_error")
     434          78 :     storeResultsInner(var_numbers, local_score, _current_raw_tally_rel_error, false);
     435         218 :   else if (output_type == "std_dev")
     436         112 :     storeResultsInner(var_numbers, local_score, _current_raw_tally_std_dev);
     437         106 :   else if (output_type == "raw")
     438         106 :     storeResultsInner(var_numbers, local_score, _current_raw_tally);
     439             :   else
     440           0 :     mooseError("Unknown external output " + output_type);
     441             : 
     442             :   // Check the normalization.
     443        4066 :   if (output_type == "relaxed")
     444        3770 :     checkNormalization(total, local_score);
     445             : 
     446        4066 :   return total;
     447             : }
     448             : 
     449             : void
     450          16 : TallyBase::addScore(const std::string & score)
     451             : {
     452          16 :   _tally_score.push_back(score);
     453             : 
     454          48 :   std::vector<std::string> score_names({score});
     455             :   std::replace(score_names.back().begin(), score_names.back().end(), '-', '_');
     456             : 
     457             :   // Modify the variable name and add extra names for the external filter bins.
     458          24 :   for (const auto & filter : _ext_filters)
     459             :   {
     460             :     std::vector<std::string> n;
     461          16 :     for (unsigned int i = 0; i < score_names.size(); ++i)
     462          24 :       for (unsigned int j = 0; j < filter->numBins(); ++j)
     463          32 :         n.push_back(score_names[i] + "_" + filter->binName(j));
     464             : 
     465           8 :     score_names = n;
     466           8 :   }
     467          16 :   std::copy(score_names.begin(), score_names.end(), std::back_inserter(_tally_name));
     468             : 
     469          16 :   _local_sum_tally.resize(_tally_score.size(), 0.0);
     470          16 :   _local_mean_tally.resize(_tally_score.size(), 0.0);
     471             : 
     472          16 :   _current_tally.resize(_tally_score.size());
     473          16 :   _current_raw_tally.resize(_tally_score.size());
     474          16 :   _current_raw_tally_rel_error.resize(_tally_score.size());
     475          16 :   _current_raw_tally_std_dev.resize(_tally_score.size());
     476          16 :   _previous_tally.resize(_tally_score.size());
     477          32 : }
     478             : 
     479             : void
     480        2657 : TallyBase::setRelaxation(relaxation::RelaxationEnum relaxation_type, const Real & relaxation_factor)
     481             : {
     482        2657 :   _relaxation_type = relaxation_type;
     483        2657 :   _relaxation_factor = relaxation_factor;
     484        2657 : }
     485             : 
     486             : void
     487        3328 : TallyBase::computeSumAndMean()
     488             : {
     489        7100 :   for (unsigned int score = 0; score < _tally_score.size(); ++score)
     490             :   {
     491        3772 :     _local_sum_tally[score] = 0.0;
     492             : 
     493        3772 :     const unsigned int mapped_bins = _local_tally->n_filter_bins() / _num_ext_filter_bins;
     494        8854 :     for (unsigned int ext = 0; ext < _num_ext_filter_bins; ++ext)
     495      452402 :       for (unsigned int m = 0; m < mapped_bins; ++m)
     496      447320 :         if (!_ext_bins_to_skip[ext])
     497      889056 :           _local_sum_tally[score] += _local_tally->results_.slice(
     498             :               openmc::tensor::all,
     499             :               score,
     500      444528 :               static_cast<int>(openmc::TallyResult::SUM))[ext * mapped_bins + m];
     501             : 
     502        3772 :     _local_mean_tally[score] = _local_sum_tally[score] / _local_tally->n_realizations_;
     503        3772 :     if (addingGlobalTally())
     504        1914 :       _global_sum_tally[score] = _openmc_problem.tallySumAcrossBins({_global_tally}, score);
     505             : 
     506        3772 :     if (_linked_tallies.size() > 0)
     507         864 :       _linked_local_sum_tally[score] = _local_sum_tally[score];
     508             :   }
     509        3328 : }
     510             : 
     511             : void
     512        2287 : TallyBase::gatherLinkedSum()
     513             : {
     514        2287 :   if (_linked_tallies.size() == 0)
     515             :     return;
     516             : 
     517           0 :   for (const auto & other : _linked_tallies)
     518           0 :     for (unsigned int score = 0; score < _tally_score.size(); ++score)
     519           0 :       _linked_local_sum_tally[score] += other->getSum(score);
     520             : }
     521             : 
     522             : void
     523        3328 : TallyBase::renormalizeLinkedTallies()
     524             : {
     525        3328 :   if (_linked_tallies.size() == 0)
     526             :     return;
     527             : 
     528        1680 :   for (unsigned int score = 0; score < _tally_score.size(); ++score)
     529             :   {
     530         864 :     _local_sum_tally[score] = _linked_local_sum_tally[score];
     531         864 :     _local_mean_tally[score] = _local_sum_tally[score] / _local_tally->n_realizations_;
     532             :   }
     533             : }
     534             : 
     535             : void
     536        3328 : TallyBase::relaxAndNormalizeTally()
     537             : {
     538             :   Real alpha;
     539        3328 :   switch (_relaxation_type)
     540             :   {
     541             :     case relaxation::none:
     542             :     {
     543             :       alpha = 1.0;
     544             :       break;
     545             :     }
     546         734 :     case relaxation::constant:
     547             :     {
     548         734 :       alpha = _relaxation_factor;
     549         734 :       break;
     550             :     }
     551          24 :     case relaxation::robbins_monro:
     552             :     {
     553          24 :       alpha = 1.0 / (_openmc_problem.fixedPointIteration() + 1);
     554          24 :       break;
     555             :     }
     556          48 :     case relaxation::dufek_gudowski:
     557             :     {
     558          48 :       alpha = static_cast<float>(_openmc_problem.nParticles()) /
     559          48 :               static_cast<float>(_openmc_problem.nTotalParticles());
     560          48 :       break;
     561             :     }
     562           0 :     default:
     563           0 :       mooseError("Unhandled RelaxationEnum in TallyBase!");
     564             :   }
     565             : 
     566        7098 :   for (unsigned int score = 0; score < _tally_score.size(); ++score)
     567             :   {
     568        3772 :     if (_check_tally_sum && _needs_global_tally)
     569        1442 :       checkTallySum(score);
     570             : 
     571        3770 :     const Real norm = tallyNormalization(score);
     572             : 
     573        3770 :     auto & current = _current_tally[score];
     574             :     auto & previous = _previous_tally[score];
     575             :     auto & current_raw = _current_raw_tally[score];
     576             :     auto & current_raw_rel_error = _current_raw_tally_rel_error[score];
     577             :     auto & current_raw_std_dev = _current_raw_tally_std_dev[score];
     578             : 
     579        3770 :     auto mean_tally = _openmc_problem.tallySum(_local_tally, score);
     580             :     /**
     581             :      * If the value over the whole domain is zero, then the values in the individual bins must be
     582             :      * zero. We need to avoid divide-by-zeros.
     583             :      */
     584        3770 :     current_raw = mean_tally;
     585        3770 :     current_raw *= std::abs(norm) < ZERO_TALLY_THRESHOLD ? 0.0 : (1.0 / norm);
     586             : 
     587        3770 :     auto sum_sq = OMCTensor(_local_tally->results_.slice(
     588        3770 :         openmc::tensor::all, score, static_cast<int>(openmc::TallyResult::SUM_SQ)));
     589             :     current_raw_rel_error =
     590        3770 :         _openmc_problem.relativeError(mean_tally, sum_sq, _local_tally->n_realizations_);
     591        3770 :     current_raw_std_dev = current_raw_rel_error * current_raw;
     592             : 
     593        7167 :     if (_openmc_problem.fixedPointIteration() == 0 || alpha == 1.0)
     594             :     {
     595        3397 :       current = current_raw;
     596        3397 :       previous = current_raw;
     597             :       continue;
     598             :     }
     599             : 
     600             :     // Save the current tally (from the previous iteration) into the previous one.
     601             :     std::copy(current.cbegin(), current.cend(), previous.begin());
     602             : 
     603             :     // Relax the tallies by alpha. TODO: skip relaxation when alpha is one.
     604        1119 :     auto relaxed_tally = (1.0 - alpha) * previous + alpha * current_raw;
     605             :     std::copy(relaxed_tally.cbegin(), relaxed_tally.cend(), current.begin());
     606             :   }
     607        3326 : }
     608             : 
     609             : void
     610        1104 : TallyBase::addLinkedTally(const TallyBase * other)
     611             : {
     612        1104 :   if (this != other)
     613        1104 :     _linked_tallies.push_back(other);
     614             :   else
     615           0 :     mooseError("Internal error: cannot link a tally with itself!");
     616        1104 : }
     617             : 
     618             : const openmc::Tally *
     619        3160 : TallyBase::getWrappedTally() const
     620             : {
     621        3160 :   if (!_local_tally)
     622           0 :     mooseError("Internal error: this tally has not been initialized!");
     623             : 
     624        3160 :   return _local_tally;
     625             : }
     626             : 
     627             : const openmc::Tally *
     628        1720 : TallyBase::getWrappedGlobalTally() const
     629             : {
     630        1720 :   if (!_global_tally)
     631           0 :     mooseError("Internal error: this tally has not been initialized!");
     632             : 
     633        1720 :   return _global_tally;
     634             : }
     635             : 
     636             : int32_t
     637        2780 : TallyBase::getTallyID() const
     638             : {
     639        2780 :   return getWrappedTally()->id();
     640             : }
     641             : 
     642             : int32_t
     643        1448 : TallyBase::getGlobalTallyID() const
     644             : {
     645        1448 :   return getWrappedGlobalTally()->id();
     646             : }
     647             : 
     648             : int
     649        1256 : TallyBase::scoreIndex(const std::string & score) const
     650             : {
     651        1256 :   if (!hasScore(score))
     652           0 :     mooseError("Internal error: tally " + name() + " does not contain the score " + score);
     653             : 
     654        1256 :   return std::find(_tally_score.begin(), _tally_score.end(), score) - _tally_score.begin();
     655             : }
     656             : 
     657             : std::vector<std::string>
     658         152 : TallyBase::getScoreVars(const std::string & score) const
     659             : {
     660             :   std::vector<std::string> score_vars;
     661         152 :   if (!hasScore(score))
     662             :     return score_vars;
     663             : 
     664             :   unsigned int idx =
     665         152 :       std::find(_tally_score.begin(), _tally_score.end(), score) - _tally_score.begin();
     666         152 :   std::copy(_tally_name.begin() + idx * _num_ext_filter_bins,
     667         152 :             _tally_name.begin() + (idx + 1) * _num_ext_filter_bins,
     668             :             std::back_inserter(score_vars));
     669             : 
     670             :   return score_vars;
     671           0 : }
     672             : 
     673             : void
     674      485486 : TallyBase::fillElementalAuxVariable(const unsigned int & var_num,
     675             :                                     const std::vector<unsigned int> & elem_ids,
     676             :                                     const Real & value)
     677             : {
     678      485486 :   auto & solution = _aux.solution();
     679      485486 :   auto sys_number = _aux.number();
     680             : 
     681             :   // loop over all the elements and set the specified variable to the specified value
     682     7961342 :   for (const auto & e : elem_ids)
     683             :   {
     684     7475856 :     auto elem_ptr = _openmc_problem.getMooseMesh().queryElemPtr(e);
     685             : 
     686     7475856 :     if (!_openmc_problem.isLocalElem(elem_ptr))
     687     3722360 :       continue;
     688             : 
     689     3753496 :     auto dof_idx = elem_ptr->dof_number(sys_number, var_num, 0);
     690     3753496 :     solution.set(dof_idx, value);
     691             :   }
     692      485486 : }
     693             : 
     694             : void
     695        2782 : TallyBase::applyTriggersToLocalTally(openmc::Tally * tally)
     696             : {
     697        2782 :   if (_tally_trigger)
     698         232 :     for (int score = 0; score < _tally_score.size(); ++score)
     699         256 :       tally->triggers_.push_back({_openmc_problem.triggerMetric((*_tally_trigger)[score]),
     700             :                                   _tally_trigger_threshold[score],
     701             :                                   _trigger_ignore_zeros[score],
     702             :                                   score});
     703        2782 : }
     704             : 
     705             : Real
     706        7540 : TallyBase::tallyNormalization(unsigned int score) const
     707             : {
     708        7540 :   return _normalize_by_global ? _global_sum_tally[score] : _local_sum_tally[score];
     709             : }
     710             : 
     711             : void
     712        1442 : TallyBase::checkTallySum(const unsigned int & score) const
     713             : {
     714        1442 :   if (std::abs(_global_sum_tally[score] - _local_sum_tally[score]) / _global_sum_tally[score] >
     715             :       1e-6)
     716             :   {
     717           2 :     std::stringstream msg;
     718           4 :     msg << _tally_score[score] << " tallies do not match the global " << _tally_score[score]
     719             :         << " tally:\n"
     720           2 :         << " Global value: " << Moose::stringify(_global_sum_tally[score])
     721           4 :         << "\n Tally sum:    " << Moose::stringify(_local_sum_tally[score])
     722           8 :         << "\n Difference:   " << _global_sum_tally[score] - _local_sum_tally[score]
     723             :         << "\n\nThis means that the tallies created by Cardinal are missing some hits over the "
     724             :            "domain.\n"
     725           2 :         << "You can turn off this check by setting 'check_tally_sum' to false.";
     726             : 
     727           2 :     mooseError(msg.str());
     728           0 :   }
     729        1440 : }
     730             : 
     731             : void
     732        3770 : TallyBase::checkNormalization(const Real & sum, unsigned int score) const
     733             : {
     734        3770 :   if (tallyNormalization(score) > ZERO_TALLY_THRESHOLD)
     735        3762 :     if (_check_tally_sum && std::abs(sum - 1.0) > 1e-6)
     736           0 :       mooseError("Tally normalization process failed for " + _tally_score[score] +
     737           0 :                  " score! Total fraction of " + Moose::stringify(sum) + " does not match 1.0!");
     738        3770 : }
     739             : #endif

Generated by: LCOV version 1.14