LCOV - code coverage report
Current view: top level - src/tallies - CellTally.C (source / functions) Hit Total Coverage
Test: neams-th-coe/cardinal: f3518d Lines: 77 84 91.7 %
Date: 2025-10-01 10:06:53 Functions: 6 6 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 "CellTally.h"
      21             : 
      22             : registerMooseObject("CardinalApp", CellTally);
      23             : 
      24             : InputParameters
      25        3748 : CellTally::validParams()
      26             : {
      27        3748 :   auto params = TallyBase::validParams();
      28        3748 :   params.addClassDescription("A class which implements distributed cell tallies.");
      29             : 
      30        7496 :   params.addParam<bool>(
      31             :       "check_equal_mapped_tally_volumes",
      32        7496 :       false,
      33             :       "Whether to check if the tallied cells map to regions in the mesh of equal volume. "
      34             :       "This can be helpful to ensure that the volume normalization of OpenMC's tallies doesn't "
      35             :       "introduce any unintentional distortion just because the mapped volumes are different. "
      36             :       "You should only set this to true if your OpenMC tally cells are all the same volume!");
      37       11244 :   params.addRangeCheckedParam<Real>("equal_tally_volume_abs_tol",
      38        7496 :                                     1e-8,
      39             :                                     "equal_tally_volume_abs_tol > 0",
      40             :                                     "Absolute tolerance for comparing tally volumes");
      41             : 
      42        3748 :   return params;
      43           0 : }
      44             : 
      45        1841 : CellTally::CellTally(const InputParameters & parameters)
      46             :   : TallyBase(parameters),
      47        3618 :     _check_equal_mapped_tally_volumes(getParam<bool>("check_equal_mapped_tally_volumes")),
      48        5459 :     _equal_tally_volume_abs_tol(getParam<Real>("equal_tally_volume_abs_tol"))
      49             : {
      50        1809 : }
      51             : 
      52             : std::pair<unsigned int, openmc::Filter *>
      53        1799 : CellTally::spatialFilter()
      54             : {
      55             :   // Check to make sure we can map tallies to the mesh subdomains requested in tally_blocks.
      56        1799 :   checkCellMappedSubdomains();
      57             : 
      58        1797 :   if (_openmc_problem.cellToElem().size() == 0)
      59           0 :     mooseError("Did not find any overlap between MOOSE elements and OpenMC cells for "
      60             :                "the specified blocks!");
      61             : 
      62        1797 :   auto tally_cells = getTallyCells();
      63             :   std::vector<openmc::CellInstance> cells;
      64       65275 :   for (const auto & c : tally_cells)
      65       63480 :     cells.push_back({c.first, c.second});
      66             : 
      67        1795 :   _cell_filter = dynamic_cast<openmc::CellInstanceFilter *>(openmc::Filter::create("cellinstance"));
      68        1795 :   _cell_filter->set_cell_instances(cells);
      69             : 
      70        3590 :   return std::make_pair(openmc::model::tally_filters.size() - 1, _cell_filter);
      71        1795 : }
      72             : 
      73             : Real
      74        2615 : CellTally::storeResultsInner(const std::vector<unsigned int> & var_numbers,
      75             :                              unsigned int local_score,
      76             :                              unsigned int global_score,
      77             :                              std::vector<xt::xtensor<double, 1>> tally_vals,
      78             :                              bool norm_by_src_rate)
      79             : {
      80             :   Real total = 0.0;
      81             : 
      82        6384 :   for (unsigned int ext_bin = 0; ext_bin < _num_ext_filter_bins; ++ext_bin)
      83             :   {
      84             :     int i = 0;
      85      240199 :     for (const auto & c : _openmc_problem.cellToElem())
      86             :     {
      87      236430 :       auto cell_info = c.first;
      88             : 
      89             :       // if this cell doesn't have any tallies, skip it
      90      236430 :       if (!_cell_has_tally[cell_info])
      91       30688 :         continue;
      92             : 
      93      205742 :       Real unnormalized_tally = tally_vals[local_score](ext_bin * _cell_filter->n_bins() + i++);
      94             : 
      95             :       // divide each tally value by the volume that it corresponds to in MOOSE
      96             :       // because we will apply it as a volumetric tally
      97      205742 :       Real volumetric_tally = unnormalized_tally;
      98      411452 :       volumetric_tally *= norm_by_src_rate ? _openmc_problem.tallyMultiplier(global_score) /
      99      205710 :                                                  _openmc_problem.cellMappedVolume(cell_info)
     100             :                                            : 1.0;
     101      205742 :       total += _ext_bins_to_skip[ext_bin] ? 0.0 : unnormalized_tally;
     102             : 
     103      205742 :       auto var = var_numbers[_num_ext_filter_bins * local_score + ext_bin];
     104      205742 :       fillElementalAuxVariable(var, c.second, volumetric_tally);
     105             :     }
     106             :   }
     107             : 
     108        2615 :   return total;
     109             : }
     110             : 
     111             : void
     112        1799 : CellTally::checkCellMappedSubdomains()
     113             : {
     114             :   _cell_has_tally.clear();
     115             : 
     116             :   // If the OpenMC cell maps to multiple subdomains that _also_ have different
     117             :   // tally settings, we need to error because we are unsure of whether to add tallies or not;
     118             :   // both of these need to be true to error
     119       76727 :   for (const auto & c : _openmc_problem.cellToElem())
     120             :   {
     121             :     bool at_least_one_in_tallies = false;
     122             :     bool at_least_one_not_in_tallies = false;
     123             :     int block_in_tallies, block_not_in_tallies;
     124             : 
     125       74930 :     auto cell_info = c.first;
     126       74930 :     auto cell_subdomains = _openmc_problem.getCellToElementSub(cell_info);
     127      179760 :     for (const auto & s : cell_subdomains)
     128             :     {
     129      104832 :       if (!at_least_one_in_tallies)
     130             :       {
     131       74956 :         at_least_one_in_tallies = _tally_blocks.count(s) != 0;
     132       74956 :         block_in_tallies = s;
     133             :       }
     134             : 
     135      104832 :       if (!at_least_one_not_in_tallies)
     136             :       {
     137      104806 :         at_least_one_not_in_tallies = _tally_blocks.count(s) == 0;
     138      104806 :         block_not_in_tallies = s;
     139             :       }
     140             : 
     141             :       // can cut the search early if we've already hit multiple tally settings
     142      104832 :       if (at_least_one_in_tallies && at_least_one_not_in_tallies)
     143             :         break;
     144             :     }
     145             : 
     146       74930 :     if (at_least_one_in_tallies && at_least_one_not_in_tallies)
     147           6 :       mooseError("cell " + _openmc_problem.printCell(cell_info) +
     148             :                  " maps to blocks with different tally settings!\n"
     149           2 :                  "Block " +
     150           0 :                  Moose::stringify(block_in_tallies) +
     151             :                  " is in 'block', but "
     152           2 :                  "block " +
     153           0 :                  Moose::stringify(block_not_in_tallies) + " is not.");
     154             : 
     155       74928 :     _cell_has_tally[cell_info] = at_least_one_in_tallies;
     156             :   }
     157        1797 : }
     158             : 
     159             : std::vector<OpenMCCellAverageProblem::cellInfo>
     160        1797 : CellTally::getTallyCells() const
     161             : {
     162             :   bool is_first_tally_cell = true;
     163        1797 :   OpenMCCellAverageProblem::cellInfo first_tally_cell;
     164             :   Real mapped_tally_volume;
     165             : 
     166             :   std::vector<OpenMCCellAverageProblem::cellInfo> tally_cells;
     167             : 
     168       76715 :   for (const auto & c : _openmc_problem.cellToElem())
     169             :   {
     170       74920 :     auto cell_info = c.first;
     171             : 
     172       74920 :     if (_cell_has_tally.at(cell_info))
     173             :     {
     174       63484 :       tally_cells.push_back(cell_info);
     175             : 
     176       63484 :       if (is_first_tally_cell)
     177             :       {
     178             :         is_first_tally_cell = false;
     179             :         first_tally_cell = cell_info;
     180        1797 :         mapped_tally_volume = _openmc_problem.cellMappedVolume(first_tally_cell);
     181             :       }
     182             : 
     183       63484 :       if (_check_equal_mapped_tally_volumes)
     184             :       {
     185        6270 :         Real diff = std::abs(mapped_tally_volume - _openmc_problem.cellMappedVolume(cell_info));
     186        6270 :         bool absolute_diff = diff > _equal_tally_volume_abs_tol;
     187        6270 :         bool relative_diff = diff / mapped_tally_volume > 1e-3;
     188        6270 :         if (absolute_diff && relative_diff)
     189             :         {
     190           2 :           std::stringstream msg;
     191             :           msg << "Detected un-equal mapped tally volumes!\n cell "
     192           2 :               << _openmc_problem.printCell(first_tally_cell) << " maps to a volume of "
     193           4 :               << Moose::stringify(_openmc_problem.cellMappedVolume(first_tally_cell))
     194           4 :               << " (cm3)\n cell " << _openmc_problem.printCell(cell_info) << " maps to a volume of "
     195           4 :               << Moose::stringify(_openmc_problem.cellMappedVolume(cell_info))
     196             :               << " (cm3).\n\n"
     197             :                  "If the tallied cells in your OpenMC model are of identical volumes, this means "
     198             :                  "that you can get\n"
     199             :                  "distortion of the volumetric tally output. For instance, suppose you have "
     200             :                  "two equal-size OpenMC\n"
     201             :                  "cells which have the same volume - but each OpenMC cell maps to a MOOSE region "
     202             :                  "of different volume\n"
     203             :                  "just due to the nature of the centroid mapping scheme. Even if those two tallies "
     204             :                  "do actually have the\n"
     205             :                  "same value, the volumetric tally will be different because you'll be "
     206             :                  "dividing each tally by a\n"
     207          10 :                  "different mapped MOOSE volume.\n\n";
     208             : 
     209           2 :           if (_openmc_problem.hasPointTransformations())
     210             :             msg << "NOTE: You have imposed symmetry, which means that you'll hit this error if any "
     211             :                    "of your tally\n"
     212             :                    "cells are cut by symmetry planes. If your tally cells would otherwise be the "
     213             :                    "same volume if NOT\n"
     214             :                    "imposing symmetry, or if your tally cells are not the same volume regardless, "
     215             :                    "you need to set\n"
     216           0 :                    "'check_equal_mapped_tally_volumes = false'.";
     217             :           else
     218             :             msg << "We recommend re-creating the mesh mirror to have an equal volume mapping of "
     219             :                    "MOOSE elements to each\n"
     220             :                    "OpenMC cell. Or, you can disable this check by setting "
     221           2 :                    "'check_equal_mapped_tally_volumes = false'.";
     222             : 
     223           2 :           mooseError(msg.str());
     224           0 :         }
     225             :       }
     226             :     }
     227             :   }
     228             : 
     229        1795 :   return tally_cells;
     230           0 : }
     231             : #endif

Generated by: LCOV version 1.14