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