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 3696 : CellTally::validParams() 26 : { 27 3696 : auto params = TallyBase::validParams(); 28 3696 : params.addClassDescription("A class which implements distributed cell tallies."); 29 7392 : params.addParam<std::vector<SubdomainName>>( 30 : "block", 31 : "Subdomains for which to add tallies in OpenMC. If not provided, cell " 32 : "tallies will be applied over the entire mesh."); 33 7392 : params.addParam<std::vector<SubdomainName>>("blocks", 34 : "This parameter is deprecated, use 'block' instead!"); 35 : 36 7392 : params.addParam<bool>( 37 : "check_equal_mapped_tally_volumes", 38 7392 : false, 39 : "Whether to check if the tallied cells map to regions in the mesh of equal volume. " 40 : "This can be helpful to ensure that the volume normalization of OpenMC's tallies doesn't " 41 : "introduce any unintentional distortion just because the mapped volumes are different. " 42 : "You should only set this to true if your OpenMC tally cells are all the same volume!"); 43 11088 : params.addRangeCheckedParam<Real>("equal_tally_volume_abs_tol", 44 7392 : 1e-8, 45 : "equal_tally_volume_abs_tol > 0", 46 : "Absolute tolerance for comparing tally volumes"); 47 : 48 3696 : return params; 49 0 : } 50 : 51 1814 : CellTally::CellTally(const InputParameters & parameters) 52 : : TallyBase(parameters), 53 3572 : _check_equal_mapped_tally_volumes(getParam<bool>("check_equal_mapped_tally_volumes")), 54 5386 : _equal_tally_volume_abs_tol(getParam<Real>("equal_tally_volume_abs_tol")) 55 : { 56 3572 : if (isParamSetByUser("blocks")) 57 0 : mooseError("This parameter is deprecated, use 'block' instead!"); 58 : 59 3572 : if (isParamValid("block")) 60 : { 61 4758 : auto block_names = getParam<std::vector<SubdomainName>>("block"); 62 1586 : if (block_names.empty()) 63 0 : paramError("block", "Subdomain names must be provided if using 'block'!"); 64 : 65 1586 : auto block_ids = _openmc_problem.getMooseMesh().getSubdomainIDs(block_names); 66 1586 : std::copy( 67 : block_ids.begin(), block_ids.end(), std::inserter(_tally_blocks, _tally_blocks.end())); 68 : 69 : // Check to make sure all of the blocks are in the mesh. 70 1586 : const auto & subdomains = _openmc_problem.getMooseMesh().meshSubdomains(); 71 4327 : for (std::size_t b = 0; b < block_names.size(); ++b) 72 2743 : if (subdomains.find(block_ids[b]) == subdomains.end()) 73 4 : paramError("block", 74 2 : "Block '" + block_names[b] + "' specified in 'block' not found in mesh!"); 75 1584 : } 76 : else 77 : { 78 : // Tally over all mesh blocks if no blocks are provided. 79 400 : for (const auto & s : _openmc_problem.getMooseMesh().meshSubdomains()) 80 : _tally_blocks.insert(s); 81 : } 82 1784 : } 83 : 84 : std::pair<unsigned int, openmc::Filter *> 85 1764 : CellTally::spatialFilter() 86 : { 87 : // Check to make sure we can map tallies to the mesh subdomains requested in tally_blocks. 88 1764 : checkCellMappedSubdomains(); 89 : 90 1762 : if (_openmc_problem.cellToElem().size() == 0) 91 0 : mooseError("Did not find any overlap between MOOSE elements and OpenMC cells for " 92 : "the specified blocks!"); 93 : 94 1762 : auto tally_cells = getTallyCells(); 95 : std::vector<openmc::CellInstance> cells; 96 64154 : for (const auto & c : tally_cells) 97 62394 : cells.push_back({c.first, c.second}); 98 : 99 1760 : _cell_filter = dynamic_cast<openmc::CellInstanceFilter *>(openmc::Filter::create("cellinstance")); 100 1760 : _cell_filter->set_cell_instances(cells); 101 : 102 3520 : return std::make_pair(openmc::model::tally_filters.size() - 1, _cell_filter); 103 : } 104 : 105 : Real 106 2557 : CellTally::storeResultsInner(const std::vector<unsigned int> & var_numbers, 107 : unsigned int local_score, 108 : unsigned int global_score, 109 : std::vector<xt::xtensor<double, 1>> tally_vals, 110 : bool norm_by_src_rate) 111 : { 112 : Real total = 0.0; 113 : 114 6188 : for (unsigned int ext_bin = 0; ext_bin < _num_ext_filter_bins; ++ext_bin) 115 : { 116 : int i = 0; 117 239521 : for (const auto & c : _openmc_problem.cellToElem()) 118 : { 119 235890 : auto cell_info = c.first; 120 : 121 : // if this cell doesn't have any tallies, skip it 122 235890 : if (!_cell_has_tally[cell_info]) 123 32608 : continue; 124 : 125 203282 : Real unnormalized_tally = tally_vals[local_score](ext_bin * _cell_filter->n_bins() + i++); 126 : 127 : // divide each tally value by the volume that it corresponds to in MOOSE 128 : // because we will apply it as a volumetric tally 129 203282 : Real volumetric_tally = unnormalized_tally; 130 406532 : volumetric_tally *= norm_by_src_rate ? _openmc_problem.tallyMultiplier(global_score) / 131 203250 : _openmc_problem.cellMappedVolume(cell_info) 132 : : 1.0; 133 203282 : total += _ext_bins_to_skip[ext_bin] ? 0.0 : unnormalized_tally; 134 : 135 203282 : auto var = var_numbers[_num_ext_filter_bins * local_score + ext_bin]; 136 203282 : fillElementalAuxVariable(var, c.second, volumetric_tally); 137 : } 138 : } 139 : 140 2557 : return total; 141 : } 142 : 143 : void 144 1764 : CellTally::checkCellMappedSubdomains() 145 : { 146 : _cell_has_tally.clear(); 147 : 148 : // If the OpenMC cell maps to multiple subdomains that _also_ have different 149 : // tally settings, we need to error because we are unsure of whether to add tallies or not; 150 : // both of these need to be true to error 151 76566 : for (const auto & c : _openmc_problem.cellToElem()) 152 : { 153 : bool at_least_one_in_tallies = false; 154 : bool at_least_one_not_in_tallies = false; 155 : int block_in_tallies, block_not_in_tallies; 156 : 157 74804 : auto cell_info = c.first; 158 74804 : auto cell_subdomains = _openmc_problem.getCellToElementSub(cell_info); 159 179508 : for (const auto & s : cell_subdomains) 160 : { 161 104706 : if (!at_least_one_in_tallies) 162 : { 163 74830 : at_least_one_in_tallies = _tally_blocks.count(s) != 0; 164 74830 : block_in_tallies = s; 165 : } 166 : 167 104706 : if (!at_least_one_not_in_tallies) 168 : { 169 104680 : at_least_one_not_in_tallies = _tally_blocks.count(s) == 0; 170 104680 : block_not_in_tallies = s; 171 : } 172 : 173 : // can cut the search early if we've already hit multiple tally settings 174 104706 : if (at_least_one_in_tallies && at_least_one_not_in_tallies) 175 : break; 176 : } 177 : 178 74804 : if (at_least_one_in_tallies && at_least_one_not_in_tallies) 179 6 : mooseError("cell " + _openmc_problem.printCell(cell_info) + 180 : " maps to blocks with different tally settings!\n" 181 2 : "Block " + 182 0 : Moose::stringify(block_in_tallies) + 183 : " is in 'block', but " 184 2 : "block " + 185 0 : Moose::stringify(block_not_in_tallies) + " is not."); 186 : 187 74802 : _cell_has_tally[cell_info] = at_least_one_in_tallies; 188 : } 189 1762 : } 190 : 191 : std::vector<OpenMCCellAverageProblem::cellInfo> 192 1762 : CellTally::getTallyCells() const 193 : { 194 : bool is_first_tally_cell = true; 195 1762 : OpenMCCellAverageProblem::cellInfo first_tally_cell; 196 : Real mapped_tally_volume; 197 : 198 : std::vector<OpenMCCellAverageProblem::cellInfo> tally_cells; 199 : 200 76554 : for (const auto & c : _openmc_problem.cellToElem()) 201 : { 202 74794 : auto cell_info = c.first; 203 : 204 74794 : if (_cell_has_tally.at(cell_info)) 205 : { 206 62398 : tally_cells.push_back(cell_info); 207 : 208 62398 : if (is_first_tally_cell) 209 : { 210 : is_first_tally_cell = false; 211 : first_tally_cell = cell_info; 212 1762 : mapped_tally_volume = _openmc_problem.cellMappedVolume(first_tally_cell); 213 : } 214 : 215 62398 : if (_check_equal_mapped_tally_volumes) 216 : { 217 6270 : Real diff = std::abs(mapped_tally_volume - _openmc_problem.cellMappedVolume(cell_info)); 218 6270 : bool absolute_diff = diff > _equal_tally_volume_abs_tol; 219 6270 : bool relative_diff = diff / mapped_tally_volume > 1e-3; 220 6270 : if (absolute_diff && relative_diff) 221 : { 222 2 : std::stringstream msg; 223 : msg << "Detected un-equal mapped tally volumes!\n cell " 224 2 : << _openmc_problem.printCell(first_tally_cell) << " maps to a volume of " 225 4 : << Moose::stringify(_openmc_problem.cellMappedVolume(first_tally_cell)) 226 4 : << " (cm3)\n cell " << _openmc_problem.printCell(cell_info) << " maps to a volume of " 227 4 : << Moose::stringify(_openmc_problem.cellMappedVolume(cell_info)) 228 : << " (cm3).\n\n" 229 : "If the tallied cells in your OpenMC model are of identical volumes, this means " 230 : "that you can get\n" 231 : "distortion of the volumetric tally output. For instance, suppose you have " 232 : "two equal-size OpenMC\n" 233 : "cells which have the same volume - but each OpenMC cell maps to a MOOSE region " 234 : "of different volume\n" 235 : "just due to the nature of the centroid mapping scheme. Even if those two tallies " 236 : "do actually have the\n" 237 : "same value, the volumetric tally will be different because you'll be " 238 : "dividing each tally by a\n" 239 10 : "different mapped MOOSE volume.\n\n"; 240 : 241 2 : if (_openmc_problem.hasPointTransformations()) 242 : msg << "NOTE: You have imposed symmetry, which means that you'll hit this error if any " 243 : "of your tally\n" 244 : "cells are cut by symmetry planes. If your tally cells would otherwise be the " 245 : "same volume if NOT\n" 246 : "imposing symmetry, or if your tally cells are not the same volume regardless, " 247 : "you need to set\n" 248 0 : "'check_equal_mapped_tally_volumes = false'."; 249 : else 250 : msg << "We recommend re-creating the mesh mirror to have an equal volume mapping of " 251 : "MOOSE elements to each\n" 252 : "OpenMC cell. Or, you can disable this check by setting " 253 2 : "'check_equal_mapped_tally_volumes = false'."; 254 : 255 2 : mooseError(msg.str()); 256 0 : } 257 : } 258 : } 259 : } 260 : 261 1760 : return tally_cells; 262 : } 263 : #endif