LCOV - code coverage report
Current view: top level - src/tallies - CellTally.C (source / functions) Hit Total Coverage
Test: neams-th-coe/cardinal: ddd5f2 Lines: 83 90 92.2 %
Date: 2026-06-07 19:35:24 Functions: 7 7 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        4001 : CellTally::validParams()
      26             : {
      27        4001 :   auto params = TallyBase::validParams();
      28        4001 :   params.addClassDescription("A class which implements distributed cell tallies.");
      29             : 
      30        8002 :   params.addParam<bool>(
      31             :       "check_equal_mapped_tally_volumes",
      32        8002 :       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       12003 :   params.addRangeCheckedParam<Real>("equal_tally_volume_abs_tol",
      38        8002 :                                     1e-8,
      39             :                                     "equal_tally_volume_abs_tol > 0",
      40             :                                     "Absolute tolerance for comparing tally volumes");
      41             : 
      42        4001 :   return params;
      43           0 : }
      44             : 
      45        1968 : CellTally::CellTally(const InputParameters & parameters)
      46             :   : TallyBase(parameters),
      47        3872 :     _check_equal_mapped_tally_volumes(getParam<bool>("check_equal_mapped_tally_volumes")),
      48        5840 :     _equal_tally_volume_abs_tol(getParam<Real>("equal_tally_volume_abs_tol"))
      49             : {
      50        1936 : }
      51             : 
      52             : std::pair<unsigned int, openmc::Filter *>
      53        2391 : CellTally::spatialFilter()
      54             : {
      55             :   // Check to make sure we can map tallies to the mesh subdomains requested in tally_blocks.
      56        2391 :   checkCellMappedSubdomains();
      57             : 
      58        2389 :   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        2389 :   auto tally_cells = getTallyCells();
      63             :   std::vector<openmc::CellInstance> cells;
      64       12685 :   for (const auto & c : tally_cells)
      65       10298 :     cells.push_back({c.first, c.second});
      66             : 
      67        2387 :   _cell_filter = dynamic_cast<openmc::CellInstanceFilter *>(openmc::Filter::create("cellinstance"));
      68        2387 :   _cell_filter->set_cell_instances(cells);
      69             : 
      70        4774 :   return std::make_pair(openmc::model::tally_filters.size() - 1, _cell_filter);
      71        2387 : }
      72             : 
      73             : void
      74        1936 : CellTally::setRelaxation(relaxation::RelaxationEnum relaxation_type, const Real & relaxation_factor)
      75             : {
      76        1936 :   if (relaxation_type != relaxation::RelaxationEnum::none && _openmc_problem.hasSkinner())
      77           1 :     mooseError("Relaxation is not supported when using cell tallies with "
      78             :                "the MoabSkinner! The skinning operation changes cells, "
      79             :                "and therefore tally bins, between Picard iterations. "
      80             :                "Please set 'relaxation' to 'none' in your "
      81             :                "OpenMCCellAverageProblem.");
      82             : 
      83        1935 :   TallyBase::setRelaxation(relaxation_type, relaxation_factor);
      84        1935 : }
      85             : 
      86             : Real
      87        2853 : CellTally::storeResultsInner(const std::vector<unsigned int> & var_numbers,
      88             :                              unsigned int local_score,
      89             :                              const std::vector<OMCTensor> & tally_vals,
      90             :                              bool norm_by_src_rate)
      91             : {
      92             :   Real total = 0.0;
      93             : 
      94        6770 :   for (unsigned int ext_bin = 0; ext_bin < _num_ext_filter_bins; ++ext_bin)
      95             :   {
      96             :     int i = 0;
      97       24435 :     for (const auto & c : _openmc_problem.cellToElem())
      98             :     {
      99       20518 :       auto cell_info = c.first;
     100             : 
     101             :       // if this cell doesn't have any tallies, skip it
     102       20518 :       if (!_cell_has_tally[cell_info])
     103        3448 :         continue;
     104             : 
     105       17070 :       Real unnormalized_tally = tally_vals[local_score](ext_bin * _cell_filter->n_bins() + i++);
     106             : 
     107             :       // divide each tally value by the volume that it corresponds to in MOOSE
     108             :       // because we will apply it as a volumetric tally
     109       17070 :       Real volumetric_tally = unnormalized_tally;
     110       17070 :       volumetric_tally *= norm_by_src_rate
     111       34060 :                               ? _openmc_problem.tallyMultiplier(_tally_score[local_score],
     112             :                                                                 _local_mean_tally[local_score]) /
     113       16990 :                                     _openmc_problem.cellMappedVolume(cell_info)
     114             :                               : 1.0;
     115       17070 :       total += _ext_bins_to_skip[ext_bin] ? 0.0 : unnormalized_tally;
     116             : 
     117       17070 :       auto var = var_numbers[_num_ext_filter_bins * local_score + ext_bin];
     118       17070 :       fillElementalAuxVariable(var, c.second, volumetric_tally);
     119             :     }
     120             :   }
     121             : 
     122        2853 :   return total;
     123             : }
     124             : 
     125             : void
     126        2391 : CellTally::checkCellMappedSubdomains()
     127             : {
     128             :   _cell_has_tally.clear();
     129             : 
     130             :   // If the OpenMC cell maps to multiple subdomains that _also_ have different
     131             :   // tally settings, we need to error because we are unsure of whether to add tallies or not;
     132             :   // both of these need to be true to error
     133       15997 :   for (const auto & c : _openmc_problem.cellToElem())
     134             :   {
     135             :     bool at_least_one_in_tallies = false;
     136             :     bool at_least_one_not_in_tallies = false;
     137             :     int block_in_tallies, block_not_in_tallies;
     138             : 
     139       13608 :     auto cell_info = c.first;
     140       13608 :     auto cell_subdomains = _openmc_problem.getCellToElementSub(cell_info);
     141       27357 :     for (const auto & s : cell_subdomains)
     142             :     {
     143       13751 :       if (!at_least_one_in_tallies)
     144             :       {
     145       13634 :         at_least_one_in_tallies = _tally_blocks.count(s) != 0;
     146       13634 :         block_in_tallies = s;
     147             :       }
     148             : 
     149       13751 :       if (!at_least_one_not_in_tallies)
     150             :       {
     151       13725 :         at_least_one_not_in_tallies = _tally_blocks.count(s) == 0;
     152       13725 :         block_not_in_tallies = s;
     153             :       }
     154             : 
     155             :       // can cut the search early if we've already hit multiple tally settings
     156       13751 :       if (at_least_one_in_tallies && at_least_one_not_in_tallies)
     157             :         break;
     158             :     }
     159             : 
     160       13608 :     if (at_least_one_in_tallies && at_least_one_not_in_tallies)
     161           6 :       mooseError("cell " + _openmc_problem.printCell(cell_info) +
     162             :                  " maps to blocks with different tally settings!\n"
     163           2 :                  "Block " +
     164           0 :                  Moose::stringify(block_in_tallies) +
     165             :                  " is in 'block', but "
     166           2 :                  "block " +
     167           0 :                  Moose::stringify(block_not_in_tallies) + " is not.");
     168             : 
     169       13606 :     _cell_has_tally[cell_info] = at_least_one_in_tallies;
     170             :   }
     171        2389 : }
     172             : 
     173             : std::vector<OpenMCCellAverageProblem::cellInfo>
     174        2389 : CellTally::getTallyCells() const
     175             : {
     176             :   bool is_first_tally_cell = true;
     177        2389 :   OpenMCCellAverageProblem::cellInfo first_tally_cell;
     178             :   Real mapped_tally_volume;
     179             : 
     180             :   std::vector<OpenMCCellAverageProblem::cellInfo> tally_cells;
     181             : 
     182       15985 :   for (const auto & c : _openmc_problem.cellToElem())
     183             :   {
     184       13598 :     auto cell_info = c.first;
     185             : 
     186       13598 :     if (_cell_has_tally.at(cell_info))
     187             :     {
     188       10302 :       tally_cells.push_back(cell_info);
     189             : 
     190       10302 :       if (is_first_tally_cell)
     191             :       {
     192             :         is_first_tally_cell = false;
     193             :         first_tally_cell = cell_info;
     194        2389 :         mapped_tally_volume = _openmc_problem.cellMappedVolume(first_tally_cell);
     195             :       }
     196             : 
     197       10302 :       if (_check_equal_mapped_tally_volumes)
     198             :       {
     199         220 :         Real diff = std::abs(mapped_tally_volume - _openmc_problem.cellMappedVolume(cell_info));
     200         220 :         bool absolute_diff = diff > _equal_tally_volume_abs_tol;
     201         220 :         bool relative_diff = diff / mapped_tally_volume > 1e-3;
     202         220 :         if (absolute_diff && relative_diff)
     203             :         {
     204           2 :           std::stringstream msg;
     205             :           msg << "Detected un-equal mapped tally volumes!\n cell "
     206           2 :               << _openmc_problem.printCell(first_tally_cell) << " maps to a volume of "
     207           4 :               << Moose::stringify(_openmc_problem.cellMappedVolume(first_tally_cell))
     208           4 :               << " (cm3)\n cell " << _openmc_problem.printCell(cell_info) << " maps to a volume of "
     209           4 :               << Moose::stringify(_openmc_problem.cellMappedVolume(cell_info))
     210             :               << " (cm3).\n\n"
     211             :                  "If the tallied cells in your OpenMC model are of identical volumes, this means "
     212             :                  "that you can get\n"
     213             :                  "distortion of the volumetric tally output. For instance, suppose you have "
     214             :                  "two equal-size OpenMC\n"
     215             :                  "cells which have the same volume - but each OpenMC cell maps to a MOOSE region "
     216             :                  "of different volume\n"
     217             :                  "just due to the nature of the centroid mapping scheme. Even if those two tallies "
     218             :                  "do actually have the\n"
     219             :                  "same value, the volumetric tally will be different because you'll be "
     220             :                  "dividing each tally by a\n"
     221             :                  "different mapped MOOSE volume. This can occur if the CellTally maps to different "
     222             :                  "regions in\n"
     223             :                  "your OpenMC geometry while computing the same score over each region. A common "
     224             :                  "example is\n"
     225             :                  "tallying fluxes over multiple regions (e.g. fuel + moderator) instead of a "
     226             :                  "single region\n"
     227          10 :                  "(e.g. fuel).\n\n";
     228             : 
     229           2 :           if (_openmc_problem.hasPointTransformations())
     230             :             msg << "NOTE: You have imposed symmetry, which means that you'll hit this error if any "
     231             :                    "of your tally\n"
     232             :                    "cells are cut by symmetry planes. If your tally cells would otherwise be the "
     233             :                    "same volume if NOT\n"
     234             :                    "imposing symmetry, or if your tally cells are not the same volume regardless, "
     235             :                    "you need to set\n"
     236           0 :                    "'check_equal_mapped_tally_volumes = false'.";
     237             :           else
     238             :             msg << "We recommend re-creating the mesh mirror to have an equal volume mapping of "
     239             :                    "MOOSE elements to each\n"
     240             :                    "OpenMC cell instance if this tally is intended to compute scores over a single "
     241             :                    "OpenMC cell. If this\n"
     242             :                    "tally computes scores over multiple unique cells, you will need to disable "
     243             :                    "this check by setting\n"
     244           2 :                    "'check_equal_mapped_tally_volumes = false'.";
     245             : 
     246           2 :           mooseError(msg.str());
     247           0 :         }
     248             :       }
     249             :     }
     250             :   }
     251             : 
     252        2387 :   return tally_cells;
     253           0 : }
     254             : #endif

Generated by: LCOV version 1.14