LCOV - code coverage report
Current view: top level - src/base - OpenMCProblemBase.C (source / functions) Hit Total Coverage
Test: neams-th-coe/cardinal: f3518d Lines: 347 408 85.0 %
Date: 2025-10-01 10:06:53 Functions: 46 53 86.8 %
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             : 
      21             : #include "OpenMCProblemBase.h"
      22             : 
      23             : #include "CardinalAppTypes.h"
      24             : #include "AddTallyAction.h"
      25             : #include "SetupMGXSAction.h"
      26             : 
      27             : #include "OpenMCNuclideDensities.h"
      28             : #include "OpenMCDomainFilterEditor.h"
      29             : #include "OpenMCTallyEditor.h"
      30             : 
      31             : #include "openmc/random_lcg.h"
      32             : 
      33             : InputParameters
      34        3918 : OpenMCProblemBase::validParams()
      35             : {
      36        3918 :   InputParameters params = CardinalProblem::validParams();
      37        7836 :   params.addParam<PostprocessorName>(
      38             :       "power", "Power (Watts) to normalize the OpenMC tallies; only used for k-eigenvalue mode");
      39        7836 :   params.addParam<PostprocessorName>(
      40             :       "source_strength",
      41             :       "Neutrons/second to normalize the OpenMC tallies; only used for fixed source mode");
      42        7836 :   params.addParam<bool>("verbose", false, "Whether to print diagnostic information");
      43             : 
      44        7836 :   params.addParam<MooseEnum>("tally_type", getTallyTypeEnum(), "Type of tally to use in OpenMC");
      45             : 
      46       11754 :   params.addRangeCheckedParam<Real>(
      47             :       "scaling",
      48        7836 :       1.0,
      49             :       "scaling > 0.0",
      50             :       "Scaling factor to apply to [Mesh] to get to units of centimeters that OpenMC expects; "
      51             :       "setting 'scaling = 100.0', for instance, indicates that the [Mesh] is in units of meters");
      52             : 
      53             :   // interfaces to directly set some OpenMC parameters
      54        7836 :   params.addRangeCheckedParam<unsigned int>(
      55             :       "openmc_verbosity",
      56             :       "openmc_verbosity >= 1 & openmc_verbosity <= 10",
      57             :       "OpenMC verbosity level; this overrides the setting in the XML files. Note that we cannot "
      58             :       "influence the verbosity of OpenMC's initialization routines, since these are run before "
      59             :       "Cardinal is initialized.");
      60        7836 :   params.addRangeCheckedParam<unsigned int>(
      61             :       "inactive_batches",
      62             :       "inactive_batches >= 0",
      63             :       "Number of inactive batches to run in OpenMC; this overrides the setting in the XML files.");
      64        7836 :   params.addRangeCheckedParam<unsigned int>("particles",
      65             :                                             "particles > 0 ",
      66             :                                             "Number of particles to run in each OpenMC batch; this "
      67             :                                             "overrides the setting in the XML files.");
      68        7836 :   params.addRangeCheckedParam<unsigned int>(
      69             :       "batches",
      70             :       "batches > 0",
      71             :       "Number of batches to run in OpenMC; this overrides the setting in the XML files.");
      72             : 
      73        7836 :   params.addParam<bool>("reuse_source",
      74        7836 :                         false,
      75             :                         "Whether to take the initial fission source "
      76             :                         "for interation n to be the converged source bank from iteration n-1");
      77        7836 :   params.addParam<bool>(
      78             :       "skip_statepoint",
      79        7836 :       false,
      80             :       "Whether to skip writing any statepoint files from OpenMC; this is a performance "
      81             :       "optimization for scenarios where you may not want the statepoint files anyways");
      82        7836 :   params.addParam<bool>(
      83             :       "reset_seed",
      84        7836 :       false,
      85             :       "Whether to reset OpenMC's seed to the initial starting seed before each OpenMC solve");
      86             : 
      87             :   // Kinetics parameters.
      88        7836 :   params.addParam<bool>("calc_kinetics_params",
      89        7836 :                         false,
      90             :                         "Whether or not Cardinal should enable the calculation of kinetics "
      91             :                         "parameters (Lambda effective and beta effective).");
      92        7836 :   params.addParam<unsigned int>(
      93             :       "ifp_generations",
      94             :       openmc::DEFAULT_IFP_N_GENERATION,
      95             :       "The number of generations to use with the method of iterated fission probabilities.");
      96        7836 :   params.addParam<FileName>(
      97             :       "xml_directory", "./", "The directory in which to look for OpenMC XML files.");
      98        3918 :   return params;
      99           0 : }
     100             : 
     101        1976 : OpenMCProblemBase::OpenMCProblemBase(const InputParameters & params)
     102             :   : CardinalProblem(params),
     103             :     PostprocessorInterface(this),
     104        1976 :     _verbose(getParam<bool>("verbose")),
     105        3952 :     _reuse_source(getParam<bool>("reuse_source")),
     106        1976 :     _specified_scaling(params.isParamSetByUser("scaling")),
     107        3952 :     _scaling(getParam<Real>("scaling")),
     108        3952 :     _skip_statepoint(getParam<bool>("skip_statepoint")),
     109        1976 :     _fixed_point_iteration(-1),
     110        1976 :     _total_n_particles(0),
     111        1976 :     _has_adaptivity(getMooseApp().actionWarehouse().hasActions("set_adaptivity_options")),
     112        1976 :     _run_on_adaptivity_cycle(true),
     113        3952 :     _calc_kinetics_params(getParam<bool>("calc_kinetics_params")),
     114        3952 :     _reset_seed(getParam<bool>("reset_seed")),
     115        1976 :     _initial_seed(openmc::openmc_get_seed()),
     116        5928 :     _xml_directory(getParam<FileName>("xml_directory"))
     117             : {
     118        3952 :   if (isParamValid("tally_type"))
     119           0 :     mooseError("The tally system used by OpenMCProblemBase derived classes has been deprecated. "
     120             :                "Please add tallies with the [Tallies] block instead.");
     121             : 
     122        5928 :   std::vector<std::string> argv_vec = {"openmc", _xml_directory};
     123             : 
     124             :   std::vector<char *> argv;
     125             : 
     126        5928 :   for (const auto & arg : argv_vec)
     127             :   {
     128        3952 :     argv.push_back(const_cast<char *>(arg.data()));
     129             :   }
     130             :   // Add terminating nullptr
     131        1976 :   argv.push_back(nullptr);
     132             : 
     133        1976 :   openmc_init(argv.size() - 1, argv.data(), &_communicator.get());
     134             : 
     135             :   // ensure that any mapped cells have their distribcell indices generated in OpenMC
     136        1976 :   if (!openmc::settings::material_cell_offsets)
     137             :   {
     138           0 :     mooseWarning("Distributed properties for material cells are disabled "
     139             :                  "in the OpenMC settings. Enabling...");
     140           0 :     openmc::settings::material_cell_offsets = true;
     141           0 :     openmc::prepare_distribcell();
     142             :   }
     143             : 
     144             :   // ensure that unsupported run modes are not used, while also checking for
     145             :   // necessary/unused input parameters for the valid run modes
     146        1976 :   _run_mode = openmc::settings::run_mode;
     147        1976 :   const auto & tally_actions = getMooseApp().actionWarehouse().getActions<AddTallyAction>();
     148        1976 :   const auto & mgxs_actions = getMooseApp().actionWarehouse().getActions<SetupMGXSAction>();
     149        1976 :   switch (_run_mode)
     150             :   {
     151             :     case openmc::RunMode::EIGENVALUE:
     152             :     {
     153             :       // Jumping through hoops to see if we're going to add tallies down the line.
     154        1870 :       if (tally_actions.size() > 0 || mgxs_actions.size() > 0)
     155             :       {
     156        3206 :         checkRequiredParam(params, "power", "running in k-eigenvalue mode");
     157        1603 :         _power = &getPostprocessorValue("power");
     158             :       }
     159             :       else
     160         534 :         checkUnusedParam(params, "power", "no tallies have been added");
     161             : 
     162        3740 :       checkUnusedParam(params, "source_strength", "running in k-eigenvalue mode");
     163        1870 :       break;
     164             :     }
     165             :     case openmc::RunMode::FIXED_SOURCE:
     166             :     {
     167         100 :       if (tally_actions.size() > 0 || mgxs_actions.size() > 0)
     168             :       {
     169         184 :         checkRequiredParam(params, "source_strength", "running in fixed source mode");
     170          92 :         _source_strength = &getPostprocessorValue("source_strength");
     171             :       }
     172             :       else
     173          16 :         checkUnusedParam(params, "source_strength", "no tallies have been added");
     174             : 
     175         200 :       checkUnusedParam(params, "inactive_batches", "running in fixed source mode");
     176         200 :       checkUnusedParam(params, "reuse_source", "running in fixed source mode");
     177         200 :       checkUnusedParam(params, "power", "running in fixed source mode");
     178         100 :       _reuse_source = false;
     179         100 :       break;
     180             :     }
     181           6 :     case openmc::RunMode::PLOTTING:
     182             :     case openmc::RunMode::PARTICLE:
     183             :     case openmc::RunMode::VOLUME:
     184           6 :       mooseError("Running OpenMC in plotting, particle, and volume modes is not supported through "
     185             :                  "Cardinal! Please just run using the OpenMC executable (e.g., openmc --plot for "
     186             :                  "plot mode).");
     187           0 :     default:
     188           0 :       mooseError("Unhandled openmc::RunMode enum in OpenMCInitAction!");
     189             :   }
     190             : 
     191        1970 :   _n_cell_digits = std::to_string(openmc::model::cells.size()).length();
     192             : 
     193        1970 :   if (openmc::settings::libmesh_comm)
     194           0 :     mooseWarning("libMesh communicator already set in OpenMC.");
     195             : 
     196        1970 :   openmc::settings::libmesh_comm = &_mesh.comm();
     197             : 
     198        3940 :   if (isParamValid("openmc_verbosity"))
     199           0 :     openmc::settings::verbosity = getParam<unsigned int>("openmc_verbosity");
     200             : 
     201        3940 :   if (isParamValid("inactive_batches"))
     202         200 :     openmc::settings::n_inactive = getParam<unsigned int>("inactive_batches");
     203             : 
     204        3940 :   if (isParamValid("particles"))
     205         248 :     openmc::settings::n_particles = getParam<unsigned int>("particles");
     206             : 
     207        3940 :   if (isParamValid("batches"))
     208             :   {
     209         130 :     auto xml_n_batches = openmc::settings::n_batches;
     210             : 
     211         260 :     int err = openmc_set_n_batches(getParam<unsigned int>("batches"),
     212             :                                    true /* set the max batches */,
     213         130 :                                    true /* add the last batch for statepoint writing */);
     214         258 :     catchOpenMCError(err, "set the number of batches");
     215             : 
     216             :     // if we set the batches from Cardinal, remove whatever statepoint file was
     217             :     // created for the #batches set in the XML files; this is just to reduce the
     218             :     // number of statepoint files by removing an unnecessary point
     219             :     openmc::settings::statepoint_batch.erase(xml_n_batches);
     220             :   }
     221             : 
     222             :   // The OpenMC wrapping doesn't require material properties itself, but we might
     223             :   // define them on some blocks of the domain for other auxiliary kernel purposes
     224             :   setMaterialCoverageCheck(false);
     225             : 
     226             :   // If the user requests kinetics parameters, make sure it's enabled in OpenMC.
     227        1968 :   if (_calc_kinetics_params)
     228             :   {
     229          12 :     if (_run_mode != openmc::RunMode::EIGENVALUE)
     230           2 :       paramError("calc_kinetics_params",
     231             :                  "Kinetic parameters can only be calculated in k-eigenvalue mode!");
     232             : 
     233          10 :     openmc::settings::ifp_on = true;
     234          10 :     openmc::settings::ifp_parameter = openmc::IFPParameter::Both;
     235             : 
     236          20 :     openmc::settings::ifp_n_generation = getParam<unsigned int>("ifp_generations");
     237          10 :     if (openmc::settings::ifp_n_generation > openmc::settings::n_inactive)
     238           2 :       paramError("ifp_generations",
     239             :                  "'ifp_generations' must be less than or equal to the number of inactive batches!");
     240             :   }
     241        5916 : }
     242             : 
     243        1709 : OpenMCProblemBase::~OpenMCProblemBase() { openmc_finalize(); }
     244             : 
     245             : void
     246   227834083 : OpenMCProblemBase::catchOpenMCError(const int & err, const std::string descriptor) const
     247             : {
     248   227834083 :   if (err)
     249           8 :     mooseError(
     250           8 :         "In attempting to ", descriptor, ", OpenMC reported:\n\n", std::string(openmc_err_msg));
     251   227834075 : }
     252             : 
     253             : void
     254           0 : OpenMCProblemBase::fillElementalAuxVariable(const unsigned int & var_num,
     255             :                                             const std::vector<unsigned int> & elem_ids,
     256             :                                             const Real & value)
     257             : {
     258           0 :   auto & solution = _aux->solution();
     259           0 :   auto sys_number = _aux->number();
     260             : 
     261             :   // loop over all the elements and set the specified variable to the specified value
     262           0 :   for (const auto & e : elem_ids)
     263             :   {
     264           0 :     auto elem_ptr = _mesh.queryElemPtr(e);
     265             : 
     266           0 :     if (!isLocalElem(elem_ptr))
     267           0 :       continue;
     268             : 
     269           0 :     auto dof_idx = elem_ptr->dof_number(sys_number, var_num, 0);
     270           0 :     solution.set(dof_idx, value);
     271             :   }
     272           0 : }
     273             : 
     274             : int
     275        6591 : OpenMCProblemBase::nParticles() const
     276             : {
     277        6591 :   return openmc::settings::n_particles;
     278             : }
     279             : 
     280             : std::string
     281        6855 : OpenMCProblemBase::materialName(const int32_t index) const
     282             : {
     283             :   // OpenMC uses -1 to indicate void materials, which don't have a name. So we return
     284             :   // one ourselves, or else openmc_material_get_name will throw an error.
     285        6855 :   if (index == -1)
     286          16 :     return "VOID";
     287             : 
     288             :   const char * name;
     289        6839 :   int err = openmc_material_get_name(index, &name);
     290        6839 :   catchOpenMCError(err, "get material name for material with index " + std::to_string(index));
     291             : 
     292        6839 :   std::string n = name;
     293             : 
     294             :   // if the material does not have a name, just return the ID instead
     295        6839 :   if (n.empty())
     296        6974 :     n = std::to_string(materialID(index));
     297             : 
     298        6839 :   return n;
     299             : }
     300             : 
     301             : int32_t
     302   130215152 : OpenMCProblemBase::cellID(const int32_t index) const
     303             : {
     304             :   int32_t id;
     305   130215152 :   int err = openmc_cell_get_id(index, &id);
     306   130215152 :   catchOpenMCError(err, "get ID for cell with index " + std::to_string(index));
     307   130215152 :   return id;
     308             : }
     309             : 
     310             : int32_t
     311     2850909 : OpenMCProblemBase::materialID(const int32_t index) const
     312             : {
     313     2850909 :   if (index == openmc::MATERIAL_VOID)
     314             :     return -1;
     315             : 
     316             :   int32_t id;
     317     2850837 :   int err = openmc_material_get_id(index, &id);
     318     2850837 :   catchOpenMCError(err, "get ID for material with index " + std::to_string(index));
     319     2850837 :   return id;
     320             : }
     321             : 
     322             : std::string
     323           0 : OpenMCProblemBase::printMaterial(const int32_t & index) const
     324             : {
     325           0 :   int32_t id = materialID(index);
     326           0 :   std::stringstream msg;
     327           0 :   msg << "material " << id;
     328           0 :   return msg.str();
     329           0 : }
     330             : 
     331             : std::string
     332          10 : OpenMCProblemBase::printPoint(const Point & p) const
     333             : {
     334          10 :   std::stringstream msg;
     335          10 :   msg << "(" << std::setprecision(6) << std::setw(7) << p(0) << ", " << std::setprecision(6)
     336          10 :       << std::setw(7) << p(1) << ", " << std::setprecision(6) << std::setw(7) << p(2) << ")";
     337          10 :   return msg.str();
     338          10 : }
     339             : 
     340             : bool
     341          72 : OpenMCProblemBase::firstSolve() const
     342             : {
     343          72 :   return _fixed_point_iteration < 0;
     344             : }
     345             : 
     346             : void
     347        2332 : OpenMCProblemBase::externalSolve()
     348             : {
     349        4664 :   TIME_SECTION("solveOpenMC", 1, "Solving OpenMC", false);
     350             : 
     351             :   // Check to see if this is a steady solve. If so, we can skip extra OpenMC runs
     352             :   // once the mesh stops getting adapted.
     353        2332 :   if (_has_adaptivity && !_run_on_adaptivity_cycle)
     354             :   {
     355          30 :     _console << " Skipping running OpenMC as the mesh has not changed!" << std::endl;
     356             :     return;
     357             :   }
     358             : 
     359        2302 :   _console << " Running OpenMC with " << nParticles() << " particles per batch..." << std::endl;
     360             : 
     361             :   // apply a new starting fission source
     362        2302 :   if (_reuse_source && !firstSolve())
     363             :   {
     364          16 :     openmc::free_memory_source();
     365          16 :     openmc::model::external_sources.push_back(
     366          48 :         std::make_unique<openmc::FileSource>(sourceBankFileName()));
     367             :   }
     368             : 
     369             :   // update tallies as needed before starting the OpenMC run
     370        2302 :   executeEditors();
     371             : 
     372        2296 :   if (_reset_seed)
     373             :   {
     374          24 :     openmc_hard_reset();
     375          24 :     openmc_set_seed(_initial_seed);
     376             :   }
     377             : 
     378        2296 :   int err = openmc_run();
     379        2296 :   if (err)
     380           0 :     mooseError(openmc_err_msg);
     381             : 
     382        2296 :   _total_n_particles += nParticles();
     383             : 
     384        2296 :   err = openmc_reset_timers();
     385        2296 :   if (err)
     386           0 :     mooseError(openmc_err_msg);
     387             : 
     388        2296 :   _fixed_point_iteration++;
     389             : 
     390             :   // save the latest fission source for re-use in the next iteration
     391        2296 :   if (_reuse_source)
     392          48 :     writeSourceBank(sourceBankFileName());
     393             : }
     394             : 
     395             : void
     396        1797 : OpenMCProblemBase::initialSetup()
     397             : {
     398        1797 :   ExternalProblem::initialSetup();
     399             : 
     400             :   // Initialize the IFP parameters tally.
     401        1797 :   if (_calc_kinetics_params)
     402             :   {
     403           8 :     _ifp_tally_index = openmc::model::tallies.size();
     404           8 :     _ifp_tally = openmc::Tally::create();
     405           8 :     _ifp_tally->set_scores({"ifp-time-numerator", "ifp-beta-numerator", "ifp-denominator"});
     406           8 :     _ifp_tally->estimator_ = openmc::TallyEstimator::COLLISION;
     407             :   }
     408        1797 : }
     409             : 
     410             : void
     411        4674 : OpenMCProblemBase::syncSolutions(ExternalProblem::Direction direction)
     412             : {
     413             :   // Always run OpenMC on the first timestep in a steady solve with adaptivity. This
     414             :   // ensures that OpenMC runs at least once during each Picard iteration.
     415        4674 :   _run_on_adaptivity_cycle |= (timeStep() == 1 && !isTransient());
     416        4674 : }
     417             : 
     418             : bool
     419         605 : OpenMCProblemBase::adaptMesh()
     420             : {
     421         605 :   _run_on_adaptivity_cycle = CardinalProblem::adaptMesh() || isTransient();
     422         605 :   return _run_on_adaptivity_cycle;
     423             : }
     424             : 
     425             : void
     426          24 : OpenMCProblemBase::writeSourceBank(const std::string & filename)
     427             : {
     428          24 :   hid_t file_id = openmc::file_open(filename, 'w', true);
     429             :   openmc::write_attribute(file_id, "filetype", "source");
     430          24 :   openmc::write_source_bank(
     431             :       file_id, openmc::simulation::source_bank, openmc::simulation::work_index);
     432          24 :   openmc::file_close(file_id);
     433          24 : }
     434             : 
     435             : unsigned int
     436        2507 : OpenMCProblemBase::numElemsInSubdomain(const SubdomainID & id) const
     437             : {
     438        2507 :   unsigned int n = 0;
     439     9217023 :   for (unsigned int e = 0; e < _mesh.nElem(); ++e)
     440             :   {
     441     9214516 :     const auto * elem = _mesh.queryElemPtr(e);
     442             : 
     443     9214516 :     if (!isLocalElem(elem) || !elem->active())
     444     4442804 :       continue;
     445             : 
     446             :     const auto subdomain_id = elem->subdomain_id();
     447     4771712 :     if (id == subdomain_id)
     448     1863372 :       n += 1;
     449             :   }
     450             : 
     451        2507 :   _communicator.sum(n);
     452             : 
     453        2507 :   return n;
     454             : }
     455             : 
     456             : bool
     457   136448189 : OpenMCProblemBase::isLocalElem(const Elem * elem) const
     458             : {
     459   136448189 :   if (!elem)
     460             :   {
     461             :     // we should only not be able to find an element if the mesh is distributed
     462             :     libmesh_assert(!_mesh.getMesh().is_serial());
     463             :     return false;
     464             :   }
     465             : 
     466    96952347 :   if (elem->processor_id() == _communicator.rank())
     467    57794289 :     return true;
     468             : 
     469             :   return false;
     470             : }
     471             : 
     472             : bool
     473           4 : OpenMCProblemBase::cellHasZeroInstances(const cellInfo & cell_info) const
     474             : {
     475           4 :   auto n = openmc::model::cells.at(cell_info.first)->n_instances();
     476           4 :   return !n;
     477             : }
     478             : 
     479             : void
     480   396634508 : OpenMCProblemBase::setCellTemperature(const int32_t & index,
     481             :                                       const int32_t & instance,
     482             :                                       const Real & T,
     483             :                                       const cellInfo & cell_info) const
     484             : {
     485   396634508 :   int err = openmc_cell_set_temperature(index, T, &instance, false);
     486   396634508 :   if (err)
     487             :   {
     488             :     std::string descriptor =
     489          12 :         "set cell " + printCell(cell_info) + " to temperature " + Moose::stringify(T) + " (K)";
     490             : 
     491             :     // special error message if cell has zero instances
     492           4 :     if (cellHasZeroInstances(cell_info))
     493           0 :       mooseError("Failed to set the temperature for cell " + printCell(cell_info) +
     494             :                  " with zero instances.");
     495             : 
     496          12 :     mooseError("In attempting to set cell " + printCell(cell_info) + " to temperature " +
     497           4 :                    Moose::stringify(T) + " (K), OpenMC reported:\n\n",
     498           4 :                std::string(openmc_err_msg) + "\n\n" +
     499             :                    "If you are trying to debug a model setup, you can set 'initial_properties = "
     500             :                    "xml' to use the initial temperature and density in the OpenMC XML files for "
     501             :                    "OpenMC's first run.");
     502             :   }
     503   396634504 : }
     504             : 
     505             : std::vector<int32_t>
     506    86681791 : OpenMCProblemBase::cellFill(const cellInfo & cell_info, int & fill_type) const
     507             : {
     508    86681791 :   int32_t * materials = nullptr;
     509    86681791 :   int n_materials = 0;
     510             : 
     511    86681791 :   int err = openmc_cell_get_fill(cell_info.first, &fill_type, &materials, &n_materials);
     512   173363582 :   catchOpenMCError(err, "get fill of cell " + printCell(cell_info));
     513             : 
     514             :   std::vector<int32_t> material_indices;
     515    86681791 :   material_indices.assign(materials, materials + n_materials);
     516    86681791 :   return material_indices;
     517           0 : }
     518             : 
     519             : bool
     520    86681791 : OpenMCProblemBase::materialFill(const cellInfo & cell_info, int32_t & material_index) const
     521             : {
     522             :   int fill_type;
     523    86681791 :   auto material_indices = cellFill(cell_info, fill_type);
     524             : 
     525    86681791 :   if (fill_type != static_cast<int>(openmc::Fill::MATERIAL))
     526             :     return false;
     527             : 
     528             :   // The number of materials in a cell is either 1, or equal to the number of instances
     529             :   // (if distributed materials were used).
     530    86681789 :   if (material_indices.size() == 1)
     531    86678861 :     material_index = material_indices[0];
     532             :   else
     533        2928 :     material_index = material_indices[cell_info.second];
     534             : 
     535             :   return true;
     536    86681791 : }
     537             : 
     538             : void
     539        1964 : OpenMCProblemBase::setCellDensity(const Real & density, const cellInfo & cell_info) const
     540             : {
     541             :   // OpenMC technically allows a density of >= 0.0, but we can impose a tighter
     542             :   // check here with a better error message than the Excepts() in material->set_density
     543             :   // because it could be a very common mistake to forget to set an initial condition
     544             :   // for density if OpenMC runs first
     545        1964 :   if (density <= 0.0)
     546           4 :     mooseError("Densities less than or equal to zero cannot be set in the OpenMC model!\n\n cell " +
     547           4 :                printCell(cell_info) + " set to density " + Moose::stringify(density) + " (kg/m3)");
     548             : 
     549             :   int32_t material_index;
     550        1962 :   auto is_material_cell = materialFill(cell_info, material_index);
     551             : 
     552        1962 :   if (!is_material_cell)
     553           0 :     mooseError(
     554             :         "Density transfer does not currently support cells filled with universes or lattices!");
     555             : 
     556             :   // throw a special error if the cell is void, because the OpenMC error isn't very
     557             :   // clear what the mistake is
     558        1962 :   if (material_index == MATERIAL_VOID)
     559             :   {
     560          12 :     mooseWarning("Skipping setting density for cell " + printCell(cell_info) +
     561             :                  " because this cell is void (vacuum)");
     562           4 :     return;
     563             :   }
     564             : 
     565             :   // Compute the density. We multiply density by 0.001 to convert from kg/m3
     566             :   // (the units assumed in the 'density' auxvariable as well as the MOOSE fluid
     567             :   // properties module) to g/cm3
     568        1956 :   int err = openmc_cell_set_density(
     569        1956 :       cell_info.first, _density_conversion_factor * density, &cell_info.second, false);
     570             : 
     571        1956 :   if (err)
     572             :   {
     573             :     // special error message if cell has zero instances
     574           0 :     if (cellHasZeroInstances(cell_info))
     575           0 :       mooseError("Failed to set the density for cell " + printCell(cell_info) +
     576             :                  " with zero instances.");
     577             : 
     578           0 :     mooseError("In attempting to set cell " + printCell(cell_info) + " to density " +
     579           0 :                    Moose::stringify(density) + " (kg/m3), OpenMC reported:\n\n",
     580           0 :                std::string(openmc_err_msg) + "\n\n" +
     581             :                    "If you are trying to debug a model setup, you can set 'initial_properties = "
     582             :                    "xml' to use the initial temperature and density in the OpenMC XML files for "
     583             :                    "OpenMC's first run.");
     584             :   }
     585             : }
     586             : 
     587             : std::string
     588    94811146 : OpenMCProblemBase::printCell(const cellInfo & cell_info, const bool brief) const
     589             : {
     590    94811146 :   int32_t id = cellID(cell_info.first);
     591             : 
     592    94811146 :   std::stringstream msg;
     593    94811146 :   if (!brief)
     594    94772141 :     msg << "id ";
     595             : 
     596   189622292 :   msg << std::setw(_n_cell_digits) << Moose::stringify(id) << ", instance "
     597   189622292 :       << std::setw(_n_cell_digits) << Moose::stringify(cell_info.second) << " (of "
     598    94811146 :       << std::setw(_n_cell_digits)
     599   379244584 :       << Moose::stringify(openmc::model::cells.at(cell_info.first)->n_instances()) << ")";
     600             : 
     601    94811146 :   return msg.str();
     602    94811146 : }
     603             : 
     604             : void
     605           2 : OpenMCProblemBase::importProperties() const
     606             : {
     607           2 :   _console << "Reading temperature and density from properties.h5" << std::endl;
     608             : 
     609           2 :   int err = openmc_properties_import("properties.h5");
     610           2 :   catchOpenMCError(err, "load temperature and density from a properties.h5 file");
     611           0 : }
     612             : 
     613             : xt::xtensor<double, 1>
     614        4228 : OpenMCProblemBase::relativeError(const xt::xtensor<double, 1> & sum,
     615             :                                  const xt::xtensor<double, 1> & sum_sq,
     616             :                                  const int & n_realizations) const
     617             : {
     618        4228 :   xt::xtensor<double, 1> rel_err = xt::zeros<double>({sum.size()});
     619             : 
     620     1705846 :   for (unsigned int i = 0; i < sum.size(); ++i)
     621             :   {
     622     1701618 :     auto mean = sum(i) / n_realizations;
     623     1701618 :     auto std_dev = std::sqrt((sum_sq(i) / n_realizations - mean * mean) / (n_realizations - 1));
     624     1701618 :     rel_err[i] = mean != 0.0 ? std_dev / std::abs(mean) : 0.0;
     625             :   }
     626             : 
     627        4228 :   return rel_err;
     628             : }
     629             : 
     630             : xt::xtensor<double, 1>
     631        5482 : OpenMCProblemBase::tallySum(openmc::Tally * tally, const unsigned int & score) const
     632             : {
     633       10964 :   return xt::view(tally->results_, xt::all(), score, static_cast<int>(openmc::TallyResult::SUM));
     634             : }
     635             : 
     636             : double
     637        1876 : OpenMCProblemBase::tallySumAcrossBins(std::vector<openmc::Tally *> tally,
     638             :                                       const unsigned int & score) const
     639             : {
     640             :   double sum = 0.0;
     641             : 
     642        3752 :   for (const auto & t : tally)
     643             :   {
     644        1876 :     auto mean = tallySum(t, score);
     645        3752 :     sum += xt::sum(mean)();
     646        1876 :   }
     647             : 
     648        1876 :   return sum;
     649             : }
     650             : 
     651             : double
     652           0 : OpenMCProblemBase::tallyMeanAcrossBins(std::vector<openmc::Tally *> tally,
     653             :                                        const unsigned int & score) const
     654             : {
     655             :   int n = 0;
     656           0 :   for (const auto & t : tally)
     657           0 :     n += t->n_realizations_;
     658             : 
     659           0 :   return tallySumAcrossBins(tally, score) / n;
     660             : }
     661             : 
     662             : std::string
     663        2624 : OpenMCProblemBase::enumToTallyScore(const std::string & score) const
     664             : {
     665             :   // the MultiMooseEnum is all caps, but the MooseEnum is already the correct case,
     666             :   // so we need to treat these as separate
     667        2624 :   std::string s = score;
     668        2624 :   if (std::all_of(
     669       17468 :           s.begin(), s.end(), [](unsigned char c) { return !std::isalpha(c) || std::isupper(c); }))
     670             :   {
     671       18544 :     std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); });
     672             : 
     673             :     // we need to revert back to some letters being uppercase for certain scores
     674        1874 :     if (s == "h3_production")
     675             :       s = "H3_production";
     676             :   }
     677             : 
     678             :   // MOOSE enums use underscores, OpenMC uses dashes
     679             :   std::replace(s.begin(), s.end(), '_', '-');
     680        2624 :   return s;
     681             : }
     682             : 
     683             : std::string
     684           0 : OpenMCProblemBase::tallyScoreToEnum(const std::string & score) const
     685             : {
     686             :   // MOOSE enums use underscores, OpenMC uses dashes
     687           0 :   std::string s = score;
     688             :   std::replace(s.begin(), s.end(), '-', '_');
     689           0 :   return s;
     690             : }
     691             : 
     692             : openmc::TallyEstimator
     693         346 : OpenMCProblemBase::tallyEstimator(tally::TallyEstimatorEnum estimator) const
     694             : {
     695             :   switch (estimator)
     696             :   {
     697             :     case tally::tracklength:
     698             :       return openmc::TallyEstimator::TRACKLENGTH;
     699             :     case tally::collision:
     700             :       return openmc::TallyEstimator::COLLISION;
     701             :     case tally::analog:
     702             :       return openmc::TallyEstimator::ANALOG;
     703           0 :     default:
     704           0 :       mooseError("Unhandled TallyEstimatorEnum!");
     705             :   }
     706             : }
     707             : 
     708             : std::string
     709           0 : OpenMCProblemBase::estimatorToString(openmc::TallyEstimator estimator) const
     710             : {
     711           0 :   switch (estimator)
     712             :   {
     713             :     case openmc::TallyEstimator::TRACKLENGTH:
     714           0 :       return "tracklength";
     715             :     case openmc::TallyEstimator::COLLISION:
     716           0 :       return "collision";
     717             :     case openmc::TallyEstimator::ANALOG:
     718           0 :       return "analog";
     719           0 :     default:
     720           0 :       mooseError("Unhandled TallyEstimatorEnum!");
     721             :   }
     722             : }
     723             : 
     724             : openmc::TriggerMetric
     725         138 : OpenMCProblemBase::triggerMetric(std::string trigger) const
     726             : {
     727         138 :   if (trigger == "variance")
     728             :     return openmc::TriggerMetric::variance;
     729         138 :   else if (trigger == "std_dev")
     730             :     return openmc::TriggerMetric::standard_deviation;
     731         138 :   else if (trigger == "rel_err")
     732             :     return openmc::TriggerMetric::relative_error;
     733           8 :   else if (trigger == "none")
     734             :     return openmc::TriggerMetric::not_active;
     735             :   else
     736           0 :     mooseError("Unhandled TallyTriggerTypeEnum: ", trigger);
     737             : }
     738             : 
     739             : openmc::TriggerMetric
     740        1858 : OpenMCProblemBase::triggerMetric(trigger::TallyTriggerTypeEnum trigger) const
     741             : {
     742             :   switch (trigger)
     743             :   {
     744             :     case trigger::variance:
     745             :       return openmc::TriggerMetric::variance;
     746             :     case trigger::std_dev:
     747             :       return openmc::TriggerMetric::standard_deviation;
     748             :     case trigger::rel_err:
     749             :       return openmc::TriggerMetric::relative_error;
     750             :     case trigger::none:
     751             :       return openmc::TriggerMetric::not_active;
     752           0 :     default:
     753           0 :       mooseError("Unhandled TallyTriggerTypeEnum!");
     754             :   }
     755             : }
     756             : 
     757             : bool
     758           0 : OpenMCProblemBase::cellIsVoid(const cellInfo & cell_info) const
     759             : {
     760             :   // material_index will be unchanged if the cell is filled by a universe or lattice.
     761             :   // Otherwise, this will get set to the material index in the cell.
     762           0 :   int32_t material_index = 0;
     763           0 :   materialFill(cell_info, material_index);
     764           0 :   return material_index == MATERIAL_VOID;
     765             : }
     766             : 
     767             : void
     768        1001 : OpenMCProblemBase::geometryType(bool & has_csg_universe, bool & has_dag_universe) const
     769             : {
     770        1001 :   has_csg_universe = false;
     771        1001 :   has_dag_universe = false;
     772             : 
     773             :   // Loop over universes and check if type is DAGMC
     774       13900 :   for (const auto & universe : openmc::model::universes)
     775             :   {
     776       12899 :     if (universe->geom_type() == openmc::GeometryType::DAG)
     777          50 :       has_dag_universe = true;
     778       12849 :     else if (universe->geom_type() == openmc::GeometryType::CSG)
     779       12849 :       has_csg_universe = true;
     780             :     else
     781           0 :       mooseError("Unhandled GeometryType enum!");
     782             :   }
     783        1001 : }
     784             : 
     785             : long unsigned int
     786        1893 : OpenMCProblemBase::numCells() const
     787             : {
     788             :   long unsigned int n_openmc_cells = 0;
     789      289568 :   for (const auto & c : openmc::model::cells)
     790      287675 :     n_openmc_cells += c->n_instances();
     791             : 
     792        1893 :   return n_openmc_cells;
     793             : }
     794             : 
     795             : const openmc::Tally &
     796          48 : OpenMCProblemBase::getKineticsParamTally()
     797             : {
     798          48 :   if (!_ifp_tally)
     799           0 :     mooseError("Internal error: kinetics parameters have not been enabled.");
     800             : 
     801          48 :   return *_ifp_tally;
     802             : }
     803             : 
     804             : bool
     805      628150 : OpenMCProblemBase::isReactionRateScore(const std::string & score) const
     806             : {
     807             :   const std::set<std::string> viable_scores = {"H3-production",
     808             :                                                "total",
     809             :                                                "absorption",
     810             :                                                "scatter",
     811             :                                                "nu-scatter",
     812             :                                                "fission",
     813             :                                                "nu-fission",
     814             :                                                "prompt-nu-fission",
     815      628150 :                                                "delayed-nu-fission"};
     816      628150 :   return viable_scores.count(score);
     817             : }
     818             : 
     819             : bool
     820     1159348 : OpenMCProblemBase::isHeatingScore(const std::string & score) const
     821             : {
     822             :   const std::set<std::string> viable_scores = {
     823     1159348 :       "heating", "heating-local", "kappa-fission", "fission-q-prompt", "fission-q-recoverable"};
     824     1159348 :   return viable_scores.count(score);
     825             : }
     826             : 
     827             : unsigned int
     828        9091 : OpenMCProblemBase::addExternalVariable(const std::string & name,
     829             :                                        const std::vector<SubdomainName> * block)
     830             : {
     831        9091 :   auto var_params = _factory.getValidParams("MooseVariable");
     832       18182 :   var_params.set<MooseEnum>("family") = "MONOMIAL";
     833       18182 :   var_params.set<MooseEnum>("order") = "CONSTANT";
     834             : 
     835        9091 :   if (block)
     836       11890 :     var_params.set<std::vector<SubdomainName>>("block") = *block;
     837             : 
     838        9091 :   checkDuplicateVariableName(name);
     839       18178 :   addAuxVariable("MooseVariable", name, var_params);
     840       18178 :   return _aux->getFieldVariable<Real>(0, name).number();
     841        9089 : }
     842             : 
     843             : std::string
     844        5328 : OpenMCProblemBase::subdomainName(const SubdomainID & id) const
     845             : {
     846        5328 :   std::string name = _mesh.getSubdomainName(id);
     847        5328 :   if (name.empty())
     848        9620 :     name = std::to_string(id);
     849        5328 :   return name;
     850             : }
     851             : 
     852             : void
     853        1797 : OpenMCProblemBase::getOpenMCUserObjects()
     854             : {
     855        1797 :   TheWarehouse::Query uo_query = theWarehouse().query().condition<AttribSystem>("UserObject");
     856             :   std::vector<UserObject *> userobjs;
     857             :   uo_query.queryInto(userobjs);
     858             : 
     859        8695 :   for (const auto & u : userobjs)
     860             :   {
     861        6898 :     OpenMCNuclideDensities * c = dynamic_cast<OpenMCNuclideDensities *>(u);
     862        6898 :     if (c)
     863          44 :       _nuclide_densities_uos.push_back(c);
     864             : 
     865        6898 :     OpenMCTallyEditor * e = dynamic_cast<OpenMCTallyEditor *>(u);
     866        6898 :     if (e)
     867          60 :       _tally_editor_uos.push_back(e);
     868             : 
     869        6898 :     OpenMCDomainFilterEditor * f = dynamic_cast<OpenMCDomainFilterEditor *>(u);
     870        6898 :     if (f)
     871          28 :       _filter_editor_uos.push_back(f);
     872             :   }
     873             : 
     874        1797 :   checkOpenMCUserObjectIDs();
     875        1793 : }
     876             : 
     877             : void
     878        1797 : OpenMCProblemBase::checkOpenMCUserObjectIDs() const
     879             : {
     880             :   std::set<int32_t> tally_ids;
     881        1855 :   for (const auto & te : _tally_editor_uos)
     882             :   {
     883          60 :     int32_t tally_id = te->tallyId();
     884             :     if (tally_ids.count(tally_id) != 0)
     885           2 :       te->duplicateTallyError(tally_id);
     886          58 :     tally_ids.insert(tally_id);
     887             :   }
     888             : 
     889             :   std::set<int32_t> filter_ids;
     890        1821 :   for (const auto & fe : _filter_editor_uos)
     891             :   {
     892          28 :     int32_t filter_id = fe->filterId();
     893             :     if (filter_ids.count(filter_id) != 0)
     894           2 :       fe->duplicateFilterError(filter_id);
     895          26 :     filter_ids.insert(filter_id);
     896             :   }
     897        1793 : }
     898             : 
     899             : void
     900        1848 : OpenMCProblemBase::checkTallyEditorIDs() const
     901             : {
     902        1848 :   std::vector<int32_t> mapped_tally_ids = getMappedTallyIDs();
     903             : 
     904        1902 :   for (const auto & te : _tally_editor_uos)
     905             :   {
     906          56 :     int32_t tally_id = te->tallyId();
     907             : 
     908             :     // ensure that the TallyEditor IDs don't apply to any mapped tally objects
     909          56 :     if (std::find(mapped_tally_ids.begin(), mapped_tally_ids.end(), tally_id) !=
     910             :         mapped_tally_ids.end())
     911           2 :       te->mappedTallyError(tally_id);
     912             :   }
     913        1846 : }
     914             : 
     915             : void
     916        2302 : OpenMCProblemBase::executeFilterEditors()
     917             : {
     918        2302 :   executeControls(EXEC_FILTER_EDITORS);
     919        2302 :   _console << "Executing filter editors..." << std::endl;
     920        2326 :   for (const auto & fe : _filter_editor_uos)
     921          24 :     fe->execute();
     922        2302 : }
     923             : 
     924             : void
     925        2302 : OpenMCProblemBase::executeTallyEditors()
     926             : {
     927        2302 :   executeControls(EXEC_TALLY_EDITORS);
     928        2302 :   _console << "Executing tally editors..." << std::endl;
     929        2350 :   for (const auto & te : _tally_editor_uos)
     930          54 :     te->execute();
     931        2296 : }
     932             : 
     933             : void
     934        2302 : OpenMCProblemBase::executeEditors()
     935             : {
     936        2302 :   executeFilterEditors();
     937        2302 :   executeTallyEditors();
     938        2296 : }
     939             : 
     940             : void
     941        2318 : OpenMCProblemBase::sendNuclideDensitiesToOpenMC()
     942             : {
     943        2318 :   if (_nuclide_densities_uos.size() == 0)
     944             :     return;
     945             : 
     946             :   // We could probably put this somewhere better, but it's good for now
     947          44 :   executeControls(EXEC_SEND_OPENMC_DENSITIES);
     948             : 
     949          44 :   _console << "Sending nuclide compositions to OpenMC... ";
     950          84 :   for (const auto & uo : _nuclide_densities_uos)
     951          44 :     uo->setValue();
     952             : }
     953             : 
     954             : #endif

Generated by: LCOV version 1.14