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

Generated by: LCOV version 1.14