LCOV - code coverage report
Current view: top level - src/userobjects - MoabSkinner.C (source / functions) Hit Total Coverage
Test: neams-th-coe/cardinal: 93e9c4 Lines: 518 528 98.1 %
Date: 2026-07-16 12:06:10 Functions: 41 41 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : #ifdef ENABLE_DAGMC
       2             : 
       3             : #include "MoabSkinner.h"
       4             : #include "VariadicTable.h"
       5             : #include "AuxiliarySystem.h"
       6             : #include "BinUtility.h"
       7             : #include "GeometryUtils.h"
       8             : #include "UserErrorChecking.h"
       9             : #include "DisplacedProblem.h"
      10             : #include "MooseMeshElementConversionUtils.h"
      11             : 
      12             : #include "libmesh/elem.h"
      13             : #include "libmesh/enum_io_package.h"
      14             : #include "libmesh/enum_order.h"
      15             : #include "libmesh/enum_fe_family.h"
      16             : #include "libmesh/equation_systems.h"
      17             : #include "libmesh/system.h"
      18             : #include "libmesh/mesh_tools.h"
      19             : 
      20             : registerMooseObject("CardinalApp", MoabSkinner);
      21             : 
      22             : InputParameters
      23          96 : MoabSkinner::validParams()
      24             : {
      25          96 :   InputParameters params = GeneralUserObject::validParams();
      26         192 :   params.addParam<bool>("verbose", false, "Whether to print diagnostic information");
      27             : 
      28             :   // temperature binning
      29         192 :   params.addRequiredParam<std::string>("temperature",
      30             :                                        "Temperature variable by which to bin elements");
      31         288 :   params.addRangeCheckedParam<Real>(
      32         192 :       "temperature_min", 0.0, "temperature_min >= 0.0", "Lower bound of temperature bins");
      33         192 :   params.addRequiredParam<Real>("temperature_max", "Upper bound of temperature bins");
      34         192 :   params.addRequiredRangeCheckedParam<unsigned int>(
      35             :       "n_temperature_bins", "n_temperature_bins > 0", "Number of temperature bins");
      36             : 
      37             :   // density binning
      38         192 :   params.addParam<std::string>("density", "Density variable by which to bin elements");
      39         288 :   params.addRangeCheckedParam<Real>(
      40         192 :       "density_min", 0.0, "density_min >= 0.0", "Lower bound of density bins");
      41         192 :   params.addParam<Real>("density_max", "Upper bound of density bins");
      42         192 :   params.addRangeCheckedParam<unsigned int>(
      43             :       "n_density_bins", "n_density_bins > 0", "Number of density bins");
      44             : 
      45         192 :   params.addParam<std::vector<std::string>>(
      46             :       "material_names",
      47             :       "List of names for each subdomain to use for naming the new volumes created in MOAB. "
      48             :       "You only need to set this if using this skinner independent of OpenMC; otherwise, "
      49             :       "these names are auto-deduced from OpenMC");
      50             : 
      51         288 :   params.addRangeCheckedParam<Real>(
      52         192 :       "faceting_tol", 1e-4, "faceting_tol > 0", "Faceting tolerance for DagMC");
      53         288 :   params.addRangeCheckedParam<Real>(
      54         192 :       "geom_tol", 1e-6, "geom_tol > 0", "Geometry tolerance for DagMC");
      55             : 
      56         192 :   params.addParam<bool>(
      57         192 :       "build_graveyard", false, "Whether to build a graveyard around the geometry");
      58         288 :   params.addRangeCheckedParam<Real>(
      59             :       "graveyard_scale_inner",
      60         192 :       1.01,
      61             :       "graveyard_scale_inner > 1",
      62             :       "Multiplier on mesh bounding box to form inner graveyard surface");
      63         192 :   params.addParam<Real>("graveyard_scale_outer",
      64         192 :                         1.10,
      65             :                         "Multiplier on mesh bounding box to form outer graveyard surface");
      66         192 :   params.addParam<std::string>("implicit_complement_material",
      67             :                                "Assigns OpenMC material name or ID to the implicit complement "
      68             :                                "region. If not provided, void material is assigned by default.");
      69             :   // TODO: would be nice to support other file formats as well, like exodus
      70         192 :   params.addParam<bool>(
      71             :       "output_skins",
      72         192 :       false,
      73             :       "Whether the skinned MOAB mesh (skins generated from the "
      74             :       "libMesh [Mesh]) should be written to a file. The files will be named moab_skins_<n>.h5m, "
      75             :       "where <n> is the time step index. You can then visualize these files by running "
      76             :       "'mbconvert'.");
      77         192 :   params.addParam<bool>("output_full",
      78         192 :                         false,
      79             :                         "Whether the MOAB mesh (copied from the libMesh [Mesh]) should "
      80             :                         "be written to a file. The files will be named moab_full_<n>.h5m, where "
      81             :                         "<n> is the time step index. "
      82             :                         "You can then visualize these files by running 'mbconvert'.");
      83         192 :   params.addParam<bool>("use_displaced_mesh",
      84         192 :                         false,
      85             :                         "Whether the skinned mesh should be generated from a displaced mesh ");
      86          96 :   params.addClassDescription("Re-generate the OpenMC geometry on-the-fly according to changes in "
      87             :                              "the mesh geometry and/or contours in temperature and density");
      88          96 :   return params;
      89           0 : }
      90             : 
      91          48 : MoabSkinner::MoabSkinner(const InputParameters & parameters)
      92             :   : GeneralUserObject(parameters),
      93          48 :     _serialized_solution(NumericVector<Number>::build(_communicator).release()),
      94          96 :     _verbose(getParam<bool>("verbose")),
      95          96 :     _temperature_name(getParam<std::string>("temperature")),
      96          96 :     _temperature_min(getParam<Real>("temperature_min")),
      97          96 :     _temperature_max(getParam<Real>("temperature_max")),
      98          96 :     _n_temperature_bins(getParam<unsigned int>("n_temperature_bins")),
      99          48 :     _temperature_bin_width((_temperature_max - _temperature_min) / _n_temperature_bins),
     100          96 :     _bin_by_density(isParamValid("density")),
     101          96 :     _faceting_tol(getParam<Real>("faceting_tol")),
     102          96 :     _geom_tol(getParam<Real>("geom_tol")),
     103          96 :     _graveyard_scale_inner(getParam<double>("graveyard_scale_inner")),
     104          96 :     _graveyard_scale_outer(getParam<double>("graveyard_scale_outer")),
     105          96 :     _output_skins(getParam<bool>("output_skins")),
     106          96 :     _output_full(getParam<bool>("output_full")),
     107          48 :     _scaling(1.0),
     108          48 :     _n_write(0),
     109          48 :     _standalone(true),
     110          96 :     _tet_mesh_built(false)
     111             : {
     112          96 :   _build_graveyard = getParam<bool>("build_graveyard");
     113          96 :   _use_displaced = getParam<bool>("use_displaced_mesh");
     114             : 
     115          96 :   if (isParamSetByUser("implicit_complement_material"))
     116             :   {
     117             :     // If the user specify a material that doesn't exist in materials.xml file, OpenMC
     118             :     // will catch the mistake.
     119           2 :     _set_implicit_complement_material = true;
     120             :     _implicit_complement_group_name =
     121           8 :         "mat:" + getParam<std::string>("implicit_complement_material") + "_comp";
     122             :   }
     123             : 
     124             :   // we can probably support this in the future, it's just not implemented yet
     125          48 :   if (!getMooseMesh().getMesh().is_serial())
     126           0 :     mooseError("MoabSkinner does not yet support distributed meshes!");
     127             : 
     128             :   // Create MOAB interface
     129          48 :   _moab = std::make_shared<moab::Core>();
     130             : 
     131             :   // Create a skinner
     132          96 :   skinner = std::make_unique<moab::Skinner>(_moab.get());
     133             : 
     134             :   // Create a geom topo tool
     135          96 :   gtt = std::make_unique<moab::GeomTopoTool>(_moab.get());
     136             : 
     137          48 :   if (_bin_by_density)
     138             :   {
     139          44 :     checkRequiredParam(parameters, "density_min", "binning by density");
     140          44 :     checkRequiredParam(parameters, "density_max", "binning by density");
     141          44 :     checkRequiredParam(parameters, "n_density_bins", "binning by density");
     142             : 
     143          44 :     _density_min = getParam<Real>("density_min");
     144          44 :     _density_max = getParam<Real>("density_max");
     145          44 :     _n_density_bins = getParam<unsigned int>("n_density_bins");
     146          44 :     _density_name = getParam<std::string>("density");
     147          22 :     _density_bin_width = (_density_max - _density_min) / _n_density_bins;
     148             : 
     149          22 :     if (_density_max < _density_min)
     150           1 :       paramError("density_max", "'density_max' must be greater than 'density_min'");
     151             :   }
     152             :   else
     153             :   {
     154          52 :     checkUnusedParam(parameters, "density_min", "not binning by density");
     155          52 :     checkUnusedParam(parameters, "density_max", "not binning by density");
     156          52 :     checkUnusedParam(parameters, "n_density_bins", "not binning by density");
     157             : 
     158          26 :     _n_density_bins = 1;
     159             :   }
     160             : 
     161          47 :   if (_build_graveyard)
     162             :   {
     163          32 :     if (_graveyard_scale_outer < _graveyard_scale_inner)
     164           1 :       paramError("graveyard_scale_outer",
     165             :                  "'graveyard_scale_outer' must be greater than 'graveyard_scale_inner'!");
     166             :   }
     167             :   else
     168             :   {
     169          30 :     checkUnusedParam(parameters, "graveyard_scale_inner", "'build_graveyard' is false");
     170          30 :     checkUnusedParam(parameters, "graveyard_scale_outer", "'build_graveyard' is false");
     171             :   }
     172             : 
     173             :   // get variable numbers
     174          46 :   _temperature_var_num = getAuxiliaryVariableNumber(_temperature_name, "temperature");
     175          44 :   if (_bin_by_density)
     176             :   {
     177          19 :     if (_temperature_name == _density_name)
     178           1 :       mooseError("The 'temperature' and 'density' variables cannot be the same!");
     179             : 
     180          18 :     _density_var_num = getAuxiliaryVariableNumber(_density_name, "density");
     181             :   }
     182             : 
     183          42 :   if (_temperature_max <= _temperature_min)
     184           1 :     paramError("temperature_max", "'temperature_max' must be greater than 'temperature_min'");
     185             : 
     186         221 :   for (unsigned int i = 0; i < _n_temperature_bins + 1; ++i)
     187         180 :     _temperature_bin_bounds.push_back(_temperature_min + i * _temperature_bin_width);
     188             : 
     189         185 :   for (unsigned int i = 0; i < _n_density_bins + 1; ++i)
     190         144 :     _density_bin_bounds.push_back(_density_min + i * _density_bin_width);
     191             : 
     192             :   // node numberings for first-order tets
     193          82 :   _tet4_nodes.push_back({0, 1, 2, 3});
     194             : 
     195             :   // node numbers for second-order tets
     196          82 :   _tet10_nodes.push_back({0, 4, 6, 7});
     197          82 :   _tet10_nodes.push_back({1, 5, 4, 8});
     198          82 :   _tet10_nodes.push_back({2, 6, 5, 9});
     199          82 :   _tet10_nodes.push_back({7, 8, 9, 3});
     200          82 :   _tet10_nodes.push_back({4, 9, 7, 8});
     201          82 :   _tet10_nodes.push_back({4, 5, 9, 8});
     202          82 :   _tet10_nodes.push_back({4, 7, 9, 6});
     203          82 :   _tet10_nodes.push_back({4, 9, 5, 6});
     204             : 
     205          41 :   moab::MBErrorHandler_Init();
     206          41 : }
     207             : 
     208             : void
     209          55 : MoabSkinner::finalize()
     210             : {
     211          55 :   moab::MBErrorHandler_Finalize();
     212          55 : }
     213             : 
     214             : moab::ErrorCode
     215     2090206 : MoabSkinner::check(const moab::ErrorCode input) const
     216             : {
     217             : #ifdef DEBUG
     218             :   MB_CHK_ERR(input);
     219             : #endif
     220     2090206 :   return moab::MB_SUCCESS;
     221             : }
     222             : 
     223             : unsigned int
     224          64 : MoabSkinner::getAuxiliaryVariableNumber(const std::string & name,
     225             :                                         const std::string & param_name) const
     226             : {
     227          64 :   if (!_fe_problem.getAuxiliarySystem().hasVariable(name))
     228           2 :     paramError(param_name, "Cannot find auxiliary variable '", name, "'!");
     229             : 
     230             :   // we require these variables to be constant monomial
     231          62 :   auto type = _fe_problem.getAuxiliarySystem().getFieldVariable<Real>(0, name).feType();
     232          62 :   if (type.family != MONOMIAL || type.order != 0)
     233           1 :     paramError(param_name, "Auxiliary variable '", name, "' must be a CONSTANT MONOMIAL type!");
     234             : 
     235          61 :   return _fe_problem.getAuxiliarySystem().getFieldVariable<Real>(0, name).number();
     236             : }
     237             : 
     238             : MooseMesh &
     239      103773 : MoabSkinner::getMooseMesh()
     240             : {
     241      138341 :   if (_use_displaced && _fe_problem.getDisplacedProblem() == nullptr)
     242           0 :     mooseError("Displaced mesh was requested but the displaced problem does not exist. "
     243             :                "set use_displaced_mesh = False");
     244       34568 :   return ((_use_displaced && _fe_problem.getDisplacedProblem())
     245      242114 :               ? _fe_problem.getDisplacedProblem()->mesh()
     246      172978 :               : _fe_problem.mesh());
     247             : }
     248             : 
     249             : MeshBase &
     250       77489 : MoabSkinner::getDAGMCGeometryMesh()
     251             : {
     252       77489 :   if (_tet_mesh)
     253             :     return *_tet_mesh;
     254       28865 :   return getMooseMesh().getMesh();
     255             : }
     256             : 
     257             : void
     258          93 : MoabSkinner::buildTetMesh()
     259             : {
     260             :   _tet_mesh.reset();
     261          93 :   _tet_mesh_built = true;
     262             : 
     263          93 :   MeshBase & source = getMooseMesh().getMesh();
     264             : 
     265             :   // Decide whether conversion is needed and validate element types
     266             :   bool needs_conversion = false;
     267      102230 :   for (const auto * elem : source.active_element_ptr_range())
     268             :   {
     269       51034 :     if (elem->type() != TET4 && elem->type() != TET10)
     270             :     {
     271             :       needs_conversion = true;
     272             :       break;
     273             :     }
     274          93 :   }
     275             : 
     276          93 :   if (!needs_conversion)
     277          81 :     return;
     278             : 
     279          12 :   if (_verbose)
     280          12 :     _console << "MoabSkinner: non-tetrahedral elements detected. "
     281          12 :              << "Building internal TET4 copy of the mesh..." << std::endl;
     282             : 
     283          24 :   _tet_mesh = source.clone();
     284          12 :   _tet_mesh->all_first_order();
     285          12 :   _tet_mesh->prepare_for_use();
     286             : 
     287             :   // Stamp each element with its original ID; convert3DMeshToAllTet4 copies element extra
     288             :   // integers onto every child tet, so each tet can report the element it came from.
     289          24 :   const unsigned int orig_id_tag = _tet_mesh->add_elem_integer("orig_id");
     290             :   std::vector<std::pair<dof_id_type, bool>> elems_to_process;
     291       12774 :   for (auto * elem : _tet_mesh->active_element_ptr_range())
     292             :   {
     293       12750 :     elem->set_extra_integer(orig_id_tag, elem->id());
     294       12750 :     elems_to_process.emplace_back(elem->id(), true);
     295          12 :   }
     296             : 
     297             :   std::set<subdomain_id_type> sids;
     298          12 :   _tet_mesh->subdomain_ids(sids);
     299          12 :   const subdomain_id_type tmp_remove_sid = *sids.rbegin() + 1;
     300             : 
     301             :   // convert3DMeshToAllTet4() contracts and re-prepares the mesh internally.
     302             :   std::vector<dof_id_type> converted_ids;
     303          12 :   MooseMeshElementConversionUtils::convert3DMeshToAllTet4(
     304             :       *_tet_mesh, elems_to_process, converted_ids, tmp_remove_sid, true);
     305             : 
     306          12 :   if (_verbose)
     307          12 :     _console << "MoabSkinner: internal TET4 mesh has " << _tet_mesh->n_active_elem()
     308          12 :              << " elements (from " << source.n_active_elem() << " original elements)." << std::endl;
     309          24 : }
     310             : 
     311             : void
     312         138 : MoabSkinner::initialize()
     313             : {
     314         138 :   if (!_tet_mesh_built)
     315          93 :     buildTetMesh();
     316             : 
     317         138 :   findBlocks();
     318             : 
     319         138 :   if (_standalone)
     320             :   {
     321          90 :     checkRequiredParam(
     322             :         parameters(), "material_names", "using skinner independent of an OpenMC [Problem]");
     323         135 :     _material_names = getParam<std::vector<std::string>>("material_names");
     324             : 
     325          45 :     if (_material_names.size() != _n_block_bins)
     326           2 :       paramError("material_names",
     327             :                  "This parameter must be the same length as the number of "
     328           1 :                  "subdomains in the mesh (" +
     329             :                      Moose::stringify(_n_block_bins) + ")");
     330             :   }
     331             :   else
     332             :   {
     333             :     // the external class should have set the value of _material_names
     334         186 :     checkUnusedParam(parameters(),
     335             :                      "material_names",
     336             :                      "using the skinner in conjunction with an OpenMC [Problem]");
     337             :   }
     338             : 
     339             :   // Set spatial dimension in MOAB
     340         137 :   check(_moab->set_dimension(getMooseMesh().getMesh().spatial_dimension()));
     341             : 
     342             :   // Create a meshset representing all of the MOAB tets
     343         137 :   check(_moab->create_meshset(moab::MESHSET_SET, _all_tets));
     344             : 
     345         137 :   createTags();
     346             : 
     347         137 :   createMOABElems();
     348         137 : }
     349             : 
     350             : void
     351          59 : MoabSkinner::execute()
     352             : {
     353          59 :   if (_standalone)
     354          22 :     update();
     355          55 : }
     356             : 
     357             : void
     358          20 : MoabSkinner::setUseDisplacedMesh(const bool & use)
     359             : {
     360          20 :   if ((use != _use_displaced) && isParamSetByUser("use_displaced_mesh"))
     361           0 :     mooseWarning("Overriding 'use_displaced_mesh' to " + std::to_string(use) +
     362             :                  " to match the displaced problem action.");
     363          20 :   _use_displaced = use;
     364          20 : }
     365             : 
     366             : void
     367          59 : MoabSkinner::update()
     368             : {
     369          59 :   _console << "Skinning geometry into " << _n_temperature_bins << " temperature bins, "
     370          59 :            << _n_density_bins << " density bins, and " << _n_block_bins << " block bins... "
     371          59 :            << std::endl;
     372             : 
     373          59 :   if (_use_displaced && _standalone)
     374             :   {
     375             :     // we are responsible for updating the mesh if running in standalone mode; otherwise, the
     376             :     // OpenMCCellAverageProblem class does it
     377           4 :     _fe_problem.getDisplacedProblem()->updateMesh();
     378             :   }
     379             : 
     380             :   // Clear MOAB mesh data from last timestep
     381          59 :   reset();
     382             : 
     383          59 :   _serialized_solution->init(_fe_problem.getAuxiliarySystem().sys().n_dofs(), false, SERIAL);
     384          59 :   _fe_problem.getAuxiliarySystem().solution().localize(*_serialized_solution);
     385             : 
     386             :   // Re-initialise the mesh data
     387          59 :   initialize();
     388             : 
     389             :   // Sort libMesh elements into bins
     390          59 :   sortElemsByResults();
     391             : 
     392             :   // Find the surfaces of local temperature regions
     393          55 :   findSurfaces();
     394          55 : }
     395             : 
     396             : void
     397         138 : MoabSkinner::findBlocks()
     398             : {
     399             :   _blocks.clear();
     400             : 
     401             :   int i = 0;
     402         599 :   for (const auto & b : getMooseMesh().meshSubdomains())
     403         461 :     _blocks[b] = i++;
     404             : 
     405         138 :   _n_block_bins = _blocks.size();
     406         138 : }
     407             : 
     408             : void
     409         137 : MoabSkinner::createMOABElems()
     410             : {
     411             :   // Clear prior results
     412             :   _id_to_elem_handles.clear();
     413             : 
     414             :   std::map<dof_id_type, moab::EntityHandle> node_id_to_handle;
     415             : 
     416             :   double coords[3];
     417             : 
     418         137 :   MeshBase & geom_mesh = getDAGMCGeometryMesh();
     419             : 
     420             :   // Save all the node information
     421       48730 :   for (const auto & node : geom_mesh.node_ptr_range())
     422             :   {
     423             :     // Fetch coords (and scale to correct units)
     424       48456 :     coords[0] = _scaling * (*node)(0);
     425       48456 :     coords[1] = _scaling * (*node)(1);
     426       48456 :     coords[2] = _scaling * (*node)(2);
     427             : 
     428             :     // Add node to MOAB database and get handle
     429       48456 :     moab::EntityHandle ent(0);
     430       48456 :     check(_moab->create_vertex(coords, ent));
     431             : 
     432             :     // Save mapping of libMesh IDs to MOAB vertex handles
     433       48456 :     node_id_to_handle[node->id()] = ent;
     434         137 :   }
     435             : 
     436             :   moab::Range all_elems;
     437             : 
     438             :   // Iterate over elements in the mesh
     439      342800 :   for (const auto & elem : geom_mesh.active_element_ptr_range())
     440             :   {
     441      171263 :     auto nodeSets = getTetSets(elem->type());
     442             : 
     443             :     // Get the connectivity
     444             :     std::vector<dof_id_type> conn_libmesh;
     445      171263 :     elem->connectivity(0, libMesh::IOPackage::VTK, conn_libmesh);
     446             : 
     447             :     // Loop over sub tets
     448      365150 :     for (const auto & nodeSet : nodeSets)
     449             :     {
     450             :       // Set MOAB connectivity
     451      193887 :       std::vector<moab::EntityHandle> conn(NODES_PER_MOAB_TET);
     452      969435 :       for (unsigned int i = 0; i < NODES_PER_MOAB_TET; ++i)
     453             :       {
     454             :         // Get the elem node index of the ith node of the sub-tet
     455      775548 :         unsigned int nodeIndex = nodeSet.at(i);
     456      775548 :         conn[i] = node_id_to_handle[conn_libmesh.at(nodeIndex)];
     457             :       }
     458             : 
     459             :       // Create an element in MOAB database
     460      193887 :       moab::EntityHandle ent(0);
     461      193887 :       check(_moab->create_element(moab::MBTET, conn.data(), NODES_PER_MOAB_TET, ent));
     462             : 
     463             :       // Save mapping between libMesh ids and moab handles
     464      193887 :       auto id = elem->id();
     465      193887 :       if (_id_to_elem_handles.find(id) == _id_to_elem_handles.end())
     466      342526 :         _id_to_elem_handles[id] = std::vector<moab::EntityHandle>();
     467             : 
     468      193887 :       _id_to_elem_handles[id].push_back(ent);
     469             : 
     470             :       // Save the handle for adding to entity sets
     471      193887 :       all_elems.insert(ent);
     472      193887 :     }
     473      171400 :   }
     474             : 
     475             :   // Add the elems to the full meshset
     476         137 :   check(_moab->add_entities(_all_tets, all_elems));
     477             : 
     478             :   // Save the first elem
     479         137 :   offset = all_elems.front();
     480         137 : }
     481             : 
     482             : const std::vector<std::vector<unsigned int>> &
     483      171263 : MoabSkinner::getTetSets(ElemType type) const
     484             : {
     485      171263 :   if (type == TET4)
     486      168031 :     return _tet4_nodes;
     487        3232 :   else if (type == TET10)
     488        3232 :     return _tet10_nodes;
     489             :   else
     490           0 :     mooseError("The MoabSkinner can only be used with a tetrahedral [Mesh]! If your mesh "
     491             :                "contains other element types, MoabSkinner should convert them automatically.");
     492             : }
     493             : 
     494             : void
     495         137 : MoabSkinner::createTags()
     496             : {
     497             :   // First some built-in MOAB tag types
     498         137 :   check(_moab->tag_get_handle(GEOM_DIMENSION_TAG_NAME,
     499             :                               1,
     500             :                               moab::MB_TYPE_INTEGER,
     501         137 :                               geometry_dimension_tag,
     502             :                               moab::MB_TAG_DENSE | moab::MB_TAG_CREAT));
     503             : 
     504         137 :   check(_moab->tag_get_handle(GLOBAL_ID_TAG_NAME,
     505             :                               1,
     506             :                               moab::MB_TYPE_INTEGER,
     507         137 :                               id_tag,
     508             :                               moab::MB_TAG_DENSE | moab::MB_TAG_CREAT));
     509             : 
     510         137 :   check(_moab->tag_get_handle(CATEGORY_TAG_NAME,
     511             :                               CATEGORY_TAG_SIZE,
     512             :                               moab::MB_TYPE_OPAQUE,
     513         137 :                               category_tag,
     514             :                               moab::MB_TAG_SPARSE | moab::MB_TAG_CREAT));
     515             : 
     516         137 :   check(_moab->tag_get_handle(NAME_TAG_NAME,
     517             :                               NAME_TAG_SIZE,
     518             :                               moab::MB_TYPE_OPAQUE,
     519         137 :                               name_tag,
     520             :                               moab::MB_TAG_SPARSE | moab::MB_TAG_CREAT));
     521             : 
     522             :   // Some tags needed for DagMC
     523         137 :   check(_moab->tag_get_handle("FACETING_TOL",
     524             :                               1,
     525             :                               moab::MB_TYPE_DOUBLE,
     526         137 :                               faceting_tol_tag,
     527             :                               moab::MB_TAG_SPARSE | moab::MB_TAG_CREAT));
     528             : 
     529         137 :   check(_moab->tag_get_handle("GEOMETRY_RESABS",
     530             :                               1,
     531             :                               moab::MB_TYPE_DOUBLE,
     532         137 :                               geometry_resabs_tag,
     533             :                               moab::MB_TAG_SPARSE | moab::MB_TAG_CREAT));
     534             : 
     535             :   // Set the values for DagMC faceting / geometry tolerance tags on the mesh entity set
     536         137 :   check(_moab->tag_set_data(faceting_tol_tag, &_all_tets, 1, &_faceting_tol));
     537         137 :   check(_moab->tag_set_data(geometry_resabs_tag, &_all_tets, 1, &_geom_tol));
     538         137 : }
     539             : 
     540             : void
     541        1049 : MoabSkinner::createGroup(const unsigned int & id,
     542             :                          const std::string & name,
     543             :                          moab::EntityHandle & group_set)
     544             : {
     545        1049 :   check(_moab->create_meshset(moab::MESHSET_SET, group_set));
     546        2098 :   setTags(group_set, name, "Group", id, 4);
     547        1049 : }
     548             : 
     549             : void
     550        1849 : MoabSkinner::createVol(const unsigned int & id,
     551             :                        moab::EntityHandle & volume_set,
     552             :                        moab::EntityHandle group_set)
     553             : {
     554        1849 :   check(_moab->create_meshset(moab::MESHSET_SET, volume_set));
     555             : 
     556        3698 :   setTags(volume_set, "", "Volume", id, 3);
     557             : 
     558             :   // Add the volume to group
     559        1849 :   check(_moab->add_entities(group_set, &volume_set, 1));
     560        1849 : }
     561             : 
     562             : void
     563        4289 : MoabSkinner::createSurf(const unsigned int & id,
     564             :                         moab::EntityHandle & surface_set,
     565             :                         moab::Range & faces,
     566             :                         const std::vector<VolData> & voldata)
     567             : {
     568             :   // Create meshset
     569        4289 :   check(_moab->create_meshset(moab::MESHSET_SET, surface_set));
     570             : 
     571             :   // Set tags
     572        8578 :   setTags(surface_set, "", "Surface", id, 2);
     573             : 
     574             :   // Add tris to the surface
     575        4289 :   check(_moab->add_entities(surface_set, faces));
     576             : 
     577             :   // Create entry in map
     578        8578 :   surfsToVols[surface_set] = std::vector<VolData>();
     579             : 
     580             :   // Add volume to list associated with this surface
     581       10983 :   for (const auto & data : voldata)
     582        6694 :     updateSurfData(surface_set, data);
     583        4289 : }
     584             : 
     585             : void
     586        7551 : MoabSkinner::updateSurfData(moab::EntityHandle surface_set, const VolData & data)
     587             : {
     588             :   // Add the surface to the volume set
     589        7551 :   check(_moab->add_parent_child(data.vol, surface_set));
     590             : 
     591             :   // Set the surfaces sense
     592        7551 :   gtt->set_sense(surface_set, data.vol, int(data.sense));
     593             : 
     594        7551 :   surfsToVols[surface_set].push_back(data);
     595        7551 : }
     596             : 
     597             : void
     598        7187 : MoabSkinner::setTags(
     599             :     moab::EntityHandle ent, std::string name, std::string category, unsigned int id, int dim)
     600             : {
     601             :   // Set the name tag
     602        7187 :   if (name != "")
     603        2098 :     setTagData(name_tag, ent, name, NAME_TAG_SIZE);
     604             : 
     605             :   // Set the category tag
     606        7187 :   if (category != "")
     607       14374 :     setTagData(category_tag, ent, category, CATEGORY_TAG_SIZE);
     608             : 
     609             :   // Set the dimension tag
     610        7187 :   setTagData(geometry_dimension_tag, ent, &dim);
     611             : 
     612             :   // Set the id tag
     613        7187 :   setTagData(id_tag, ent, &id);
     614        7187 : }
     615             : 
     616             : void
     617        8236 : MoabSkinner::setTagData(moab::Tag tag, moab::EntityHandle ent, std::string data, unsigned int SIZE)
     618             : {
     619        8236 :   auto namebuf = new char[SIZE];
     620        8236 :   memset(namebuf, '\0', SIZE); // fill C char array with null
     621        8236 :   strncpy(namebuf, data.c_str(), SIZE - 1);
     622        8236 :   check(_moab->tag_set_data(tag, &ent, 1, namebuf));
     623        8236 :   delete[] namebuf;
     624        8236 : }
     625             : 
     626             : void
     627       14374 : MoabSkinner::setTagData(moab::Tag tag, moab::EntityHandle ent, void * data)
     628             : {
     629       14374 :   check(_moab->tag_set_data(tag, &ent, 1, data));
     630       14374 : }
     631             : 
     632             : unsigned int
     633          96 : MoabSkinner::nBins() const
     634             : {
     635          96 :   return _n_block_bins * _n_density_bins * _n_temperature_bins;
     636             : }
     637             : 
     638             : void
     639          59 : MoabSkinner::sortElemsByResults()
     640             : {
     641          59 :   _elem_bins.clear();
     642          59 :   _elem_bins.resize(nBins());
     643             : 
     644             :   // accumulate information for printing diagnostics
     645          59 :   std::vector<unsigned int> n_block_hits(_n_block_bins, 0);
     646          59 :   std::vector<unsigned int> n_temp_hits(_n_temperature_bins, 0);
     647          59 :   std::vector<unsigned int> n_density_hits(_n_density_bins, 0);
     648             : 
     649          59 :   if (!_tet_mesh)
     650             :   {
     651       28748 :     for (unsigned int e = 0; e < getMooseMesh().nElem(); ++e)
     652             :     {
     653       28701 :       const Elem * const elem = getMooseMesh().queryElemPtr(e);
     654       28701 :       if (!elem)
     655           0 :         continue;
     656             : 
     657       28701 :       auto iMat = getSubdomainBin(elem);
     658       28701 :       n_block_hits[iMat] += 1;
     659             : 
     660       28701 :       auto iDenBin = getDensityBin(elem);
     661       28699 :       n_density_hits[iDenBin] += 1;
     662             : 
     663       28699 :       auto iBin = getTemperatureBin(elem);
     664       28697 :       n_temp_hits[iBin] += 1;
     665             : 
     666       28697 :       _elem_bins.at(getBin(iBin, iDenBin, iMat)).insert(elem->id());
     667             :     }
     668             :   }
     669             :   else
     670             :   {
     671             :     // aux vars live on the original mesh; _elem_bins
     672             :     // must hold tet-mesh IDs (keyed the same as _id_to_elem_handles).
     673             : 
     674             :     // compute bin index for every original element.
     675             :     std::unordered_map<dof_id_type, unsigned int> orig_id_to_bin;
     676             : 
     677        8508 :     for (unsigned int e = 0; e < getMooseMesh().nElem(); ++e)
     678             :     {
     679        8500 :       const Elem * const elem = getMooseMesh().queryElemPtr(e);
     680        8500 :       if (!elem)
     681           0 :         continue;
     682             : 
     683        8500 :       auto iMat = getSubdomainBin(elem);
     684        8500 :       n_block_hits[iMat] += 1;
     685             : 
     686        8500 :       auto iDenBin = getDensityBin(elem);
     687        8500 :       n_density_hits[iDenBin] += 1;
     688             : 
     689        8500 :       auto iBin = getTemperatureBin(elem);
     690        8500 :       n_temp_hits[iBin] += 1;
     691             : 
     692        8500 :       orig_id_to_bin[elem->id()] = getBin(iBin, iDenBin, iMat);
     693             :     }
     694             : 
     695             :     // insert each tet ID into its parent's bin.
     696           8 :     const unsigned int orig_id_tag = _tet_mesh->get_elem_integer_index("orig_id");
     697       48616 :     for (const auto * tet : _tet_mesh->active_element_ptr_range())
     698             :     {
     699       48600 :       const dof_id_type orig_id = tet->get_extra_integer(orig_id_tag);
     700             : 
     701             :       auto bin_it = orig_id_to_bin.find(orig_id);
     702       48600 :       if (bin_it == orig_id_to_bin.end())
     703           0 :         mooseError("MoabSkinner::sortElemsByResults(): original elem id=",
     704             :                    orig_id,
     705             :                    " not found in bin map");
     706             : 
     707       48600 :       _elem_bins.at(bin_it->second).insert(tet->id());
     708           8 :     }
     709             :   }
     710             : 
     711          55 :   if (_verbose)
     712             :   {
     713          53 :     VariadicTable<unsigned int, std::string, unsigned int> vtt({"Bin", "Range (K)", "# Elems"});
     714          53 :     VariadicTable<unsigned int, std::string, unsigned int> vtd({"Bin", "Range (kg/m3)", "# Elems"});
     715             : 
     716         228 :     for (unsigned int i = 0; i < _n_temperature_bins; ++i)
     717         175 :       vtt.addRow(i,
     718         525 :                  std::to_string(_temperature_bin_bounds[i]) + " to " +
     719         350 :                      std::to_string(_temperature_bin_bounds[i + 1]),
     720         175 :                  n_temp_hits[i]);
     721             : 
     722         200 :     for (unsigned int i = 0; i < _n_density_bins; ++i)
     723         147 :       vtd.addRow(i,
     724         441 :                  std::to_string(_density_bin_bounds[i]) + " to " +
     725         294 :                      std::to_string(_density_bin_bounds[i + 1]),
     726         147 :                  n_density_hits[i]);
     727             : 
     728          53 :     _console << "\nMapping of Elements to Temperature Bins:" << std::endl;
     729          53 :     vtt.print(_console);
     730          53 :     _console << std::endl;
     731             : 
     732          53 :     if (_bin_by_density)
     733             :     {
     734          19 :       _console << "\n\nMapping of Elements to Density Bins:" << std::endl;
     735          19 :       vtd.print(_console);
     736          19 :       _console << std::endl;
     737             :     }
     738          53 :   }
     739          55 : }
     740             : 
     741             : unsigned int
     742       94580 : MoabSkinner::getTemperatureBin(const Elem * const elem) const
     743             : {
     744       94580 :   auto dof = elem->dof_number(_fe_problem.getAuxiliarySystem().number(), _temperature_var_num, 0);
     745       94580 :   auto value = (*_serialized_solution)(dof);
     746             : 
     747             :   // TODO: add option to truncate instead
     748       94580 :   if ((_temperature_min - value) > BIN_TOLERANCE)
     749           1 :     mooseError("Variable '",
     750           1 :                _temperature_name,
     751             :                "' has value below minimum range of bins. "
     752             :                "Please decrease 'temperature_min'.\n\n"
     753             :                "  value: ",
     754             :                value,
     755             :                "\n  temperature_min: ",
     756             :                _temperature_min);
     757             : 
     758       94579 :   if ((value - _temperature_max) > BIN_TOLERANCE)
     759           1 :     mooseError("Variable '",
     760           1 :                _temperature_name,
     761             :                "' has value above maximum range of bins. "
     762             :                "Please increase 'temperature_max'.\n\n"
     763             :                "  value: ",
     764             :                value,
     765             :                "\n  temperature_max: ",
     766             :                _temperature_max);
     767             : 
     768       94578 :   return bin_utility::linearBin(value, _temperature_bin_bounds);
     769             : }
     770             : 
     771             : unsigned int
     772       92716 : MoabSkinner::getDensityBin(const Elem * const elem) const
     773             : {
     774       92716 :   if (!_bin_by_density)
     775             :     return 0;
     776             : 
     777       55338 :   auto dof = elem->dof_number(_fe_problem.getAuxiliarySystem().number(), _density_var_num, 0);
     778       55338 :   auto value = (*_serialized_solution)(dof);
     779             : 
     780             :   // TODO: add option to truncate instead
     781       55338 :   if ((_density_min - value) > BIN_TOLERANCE)
     782           1 :     mooseError("Variable '",
     783           1 :                _density_name,
     784             :                "' has value below minimum range of bins. "
     785             :                "Please decrease 'density_min'.\n\n"
     786             :                "  value: ",
     787             :                value,
     788             :                "\n  density_min: ",
     789           1 :                _density_min);
     790             : 
     791       55337 :   if ((value - _density_max) > BIN_TOLERANCE)
     792           1 :     mooseError("Variable '",
     793           1 :                _density_name,
     794             :                "' has value above maximum range of bins. "
     795             :                "Please increase 'density_max'.\n\n"
     796             :                "  value: ",
     797             :                value,
     798             :                "\n  density_max: ",
     799           1 :                _density_max);
     800             : 
     801       55336 :   return bin_utility::linearBin(value, _density_bin_bounds);
     802             : }
     803             : 
     804             : std::string
     805        1012 : MoabSkinner::materialName(const unsigned int & block,
     806             :                           const unsigned int & density,
     807             :                           const unsigned int & temp) const
     808             : {
     809        1012 :   return "mat:" + _material_names.at(block);
     810             : }
     811             : 
     812             : void
     813          55 : MoabSkinner::findSurfaces()
     814             : {
     815             :   // Find all neighbours in mesh
     816          55 :   getDAGMCGeometryMesh().find_neighbors();
     817             : 
     818             :   // Counter for volumes
     819          55 :   unsigned int vol_id = 0;
     820             : 
     821             :   // Counter for surfaces
     822          55 :   unsigned int surf_id = 0;
     823             : 
     824             :   // Loop over material bins
     825         253 :   for (unsigned int iMat = 0; iMat < _n_block_bins; iMat++)
     826             :   {
     827             :     // Loop over density bins
     828         566 :     for (unsigned int iDen = 0; iDen < _n_density_bins; iDen++)
     829             :     {
     830             :       // Loop over temperature bins
     831        1380 :       for (unsigned int iVar = 0; iVar < _n_temperature_bins; iVar++)
     832             :       {
     833             :         // Update material name
     834        1012 :         auto updated_mat_name = materialName(iMat, iDen, iVar);
     835             : 
     836             :         // Create a material group
     837        1012 :         int iSortBin = getBin(iVar, iDen, iMat);
     838             : 
     839             :         // For DagMC to fill a cell with a material, we first create a group
     840             :         // with that name, and then assign it with createVol (called inside findSurface)
     841             :         moab::EntityHandle group_set;
     842        1012 :         unsigned int group_id = iSortBin + 1;
     843        1012 :         createGroup(group_id, updated_mat_name, group_set);
     844             : 
     845             :         // Sort elems in this mat-density-temp bin into local regions
     846             :         std::vector<moab::Range> regions;
     847        3036 :         groupLocalElems(_elem_bins.at(iSortBin), regions);
     848             : 
     849             :         // Loop over all regions and find surfaces
     850        2826 :         for (const auto & region : regions)
     851             :         {
     852             :           moab::EntityHandle volume_set;
     853        1814 :           findSurface(region, group_set, vol_id, surf_id, volume_set);
     854             :         }
     855        1012 :       }
     856             :     }
     857             :   }
     858             : 
     859          55 :   if (_build_graveyard)
     860          35 :     buildGraveyard(vol_id, surf_id);
     861             : 
     862          55 :   if (_set_implicit_complement_material)
     863             :   {
     864             :     moab::EntityHandle comp_group;
     865           2 :     unsigned int comp_id = nBins() + 1 + _build_graveyard;
     866           2 :     createGroup(comp_id, _implicit_complement_group_name, comp_group);
     867           2 :     moab::EntityHandle arbitray_volume = 0;
     868           2 :     for (const auto & surf_pair : surfsToVols)
     869             :     {
     870             :       const auto & vols = surf_pair.second;
     871           2 :       arbitray_volume = vols.front().vol;
     872           2 :       break;
     873             :     }
     874           2 :     check(_moab->add_entities(comp_group, &arbitray_volume, 1));
     875             :   }
     876             : 
     877             :   // Write MOAB volume and/or skin meshes to file
     878          55 :   write();
     879          55 : }
     880             : 
     881             : void
     882          55 : MoabSkinner::write()
     883             : {
     884             :   // Only write to file on root process
     885          55 :   if (processor_id() != 0)
     886           0 :     return;
     887             : 
     888          55 :   std::string extension = std::to_string(_n_write) + ".h5m";
     889             : 
     890          55 :   if (_output_skins)
     891             :   {
     892             :     // Generate list of surfaces to write
     893             :     std::vector<moab::EntityHandle> surfs;
     894        3549 :     for (const auto & itsurf : surfsToVols)
     895        3532 :       surfs.push_back(itsurf.first);
     896             : 
     897          17 :     std::string filename = "moab_skins_" + extension;
     898             : 
     899          17 :     if (_verbose)
     900          17 :       _console << "Writing MOAB skins to " << filename << "...";
     901             : 
     902          17 :     check(_moab->write_mesh(filename.c_str(), surfs.data(), surfs.size()));
     903          17 :   }
     904             : 
     905          55 :   if (_output_full)
     906             :   {
     907           1 :     std::string filename = "moab_mesh_" + extension;
     908             : 
     909           1 :     if (_verbose)
     910           1 :       _console << "Writing MOAB mesh to " << filename << std::endl;
     911             : 
     912           1 :     check(_moab->write_mesh(filename.c_str()));
     913             :   }
     914             : 
     915          55 :   _n_write++;
     916             : }
     917             : 
     918             : void
     919        1012 : MoabSkinner::groupLocalElems(std::set<dof_id_type> elems, std::vector<moab::Range> & localElems)
     920             : {
     921        2826 :   while (!elems.empty())
     922             :   {
     923             : 
     924             :     // Create a new local range of moab handles
     925             :     moab::Range local;
     926             : 
     927             :     // Retrieve and remove the fisrt elem
     928             :     auto it = elems.begin();
     929        1814 :     dof_id_type next = *it;
     930        1814 :     elems.erase(it);
     931             : 
     932             :     std::set<dof_id_type> neighbors;
     933        1814 :     neighbors.insert(next);
     934             : 
     935       10904 :     while (!neighbors.empty())
     936             :     {
     937             : 
     938             :       std::set<dof_id_type> new_neighbors;
     939             : 
     940             :       // Loop over all the new neighbors
     941       86387 :       for (auto & next : neighbors)
     942             :       {
     943             : 
     944             :         // Get the MOAB handles, and add to local set
     945             :         // (May be more than one if this libMesh elem has sub-tetrahedra)
     946       77297 :         if (_id_to_elem_handles.find(next) == _id_to_elem_handles.end())
     947           0 :           mooseError("No entity handles found for libmesh id.");
     948             : 
     949       77297 :         std::vector<moab::EntityHandle> ents = _id_to_elem_handles[next];
     950      165906 :         for (const auto ent : ents)
     951       88609 :           local.insert(ent);
     952             : 
     953             :         // Get the libMesh element
     954       77297 :         Elem & elem = getDAGMCGeometryMesh().elem_ref(next);
     955             : 
     956             :         // How many nearest neighbors (general element)?
     957             :         unsigned int NN = elem.n_neighbors();
     958             : 
     959             :         // Loop over neighbors
     960      386485 :         for (unsigned int i = 0; i < NN; i++)
     961             :         {
     962             :           const Elem * nnptr = elem.neighbor_ptr(i);
     963             :           // If on boundary, some may be null ptrs
     964      309188 :           if (nnptr == nullptr)
     965       20804 :             continue;
     966             : 
     967      288384 :           dof_id_type idnn = nnptr->id();
     968             : 
     969             :           // Select only those that are in the current bin
     970      288384 :           if (elems.find(idnn) != elems.end())
     971             :           {
     972       75483 :             new_neighbors.insert(idnn);
     973             :             // Remove from those still available
     974             :             elems.erase(idnn);
     975             :           }
     976             :         }
     977       77297 :       }
     978             : 
     979             :       // Found all the new neighbors, done with current set.
     980             :       neighbors = new_neighbors;
     981             :     }
     982             : 
     983             :     // Save this moab range of local neighbors
     984        1814 :     localElems.push_back(local);
     985             :   }
     986        1012 : }
     987             : 
     988             : void
     989          59 : MoabSkinner::reset()
     990             : {
     991          59 :   _moab.reset(new moab::Core());
     992          59 :   skinner.reset(new moab::Skinner(_moab.get()));
     993          59 :   gtt.reset(new moab::GeomTopoTool(_moab.get()));
     994             : 
     995             :   _tet_mesh.reset();
     996          59 :   _tet_mesh_built = false;
     997             : 
     998             :   // Clear entity set maps
     999             :   surfsToVols.clear();
    1000          59 : }
    1001             : 
    1002             : unsigned int
    1003       74970 : MoabSkinner::getBin(const unsigned int & i_temp,
    1004             :                     const unsigned int & i_density,
    1005             :                     const unsigned int & i_block) const
    1006             : {
    1007       74970 :   return _n_temperature_bins * (_n_density_bins * i_block + i_density) + i_temp;
    1008             : }
    1009             : 
    1010             : void
    1011        1814 : MoabSkinner::findSurface(const moab::Range & region,
    1012             :                          moab::EntityHandle group,
    1013             :                          unsigned int & vol_id,
    1014             :                          unsigned int & surf_id,
    1015             :                          moab::EntityHandle & volume_set)
    1016             : {
    1017             :   // Create a volume set
    1018        1814 :   vol_id++;
    1019        1814 :   createVol(vol_id, volume_set, group);
    1020             : 
    1021             :   // Find surfaces from these regions
    1022             :   moab::Range tris;  // The tris of the surfaces
    1023             :   moab::Range rtris; // The tris which are reversed with respect to their surfaces
    1024        1814 :   skinner->find_skin(0, region, false, tris, &rtris);
    1025             : 
    1026             :   // Create surface sets for the forwards tris
    1027        1814 :   VolData vdata = {volume_set, Sense::FORWARDS};
    1028        1814 :   createSurfaces(tris, vdata, surf_id);
    1029             : 
    1030             :   // Create surface sets for the reversed tris
    1031        1814 :   vdata.sense = Sense::BACKWARDS;
    1032        1814 :   createSurfaces(rtris, vdata, surf_id);
    1033        1814 : }
    1034             : 
    1035             : void
    1036        3628 : MoabSkinner::createSurfaces(moab::Range & faces, VolData & voldata, unsigned int & surf_id)
    1037             : {
    1038        3628 :   if (faces.empty())
    1039             :     return;
    1040             : 
    1041             :   // Loop over the surfaces we have already created
    1042     1800859 :   for (const auto & surfpair : surfsToVols)
    1043             :   {
    1044             :     // Local copies of surf/vols
    1045     1799045 :     moab::EntityHandle surf = surfpair.first;
    1046     1799045 :     std::vector<VolData> vols = surfpair.second;
    1047             : 
    1048             :     // First get the entities in this surface
    1049             :     moab::Range tris;
    1050     1799045 :     check(_moab->get_entities_by_handle(surf, tris));
    1051             : 
    1052             :     // Find any tris that live in both surfs
    1053     1799045 :     moab::Range overlap = moab::intersect(tris, faces);
    1054     1799045 :     if (!overlap.empty())
    1055             :     {
    1056             :       // Check if the tris are a subset or the entire surf
    1057        3262 :       if (tris.size() == overlap.size())
    1058             :       {
    1059             :         // Whole surface -> Just need to update the volume relationships
    1060         857 :         updateSurfData(surf, voldata);
    1061             :       }
    1062             :       else
    1063             :       {
    1064             :         // If overlap is subset, subtract shared tris from this surface and create a new shared
    1065             :         // surface
    1066        2405 :         check(_moab->remove_entities(surf, overlap));
    1067             : 
    1068             :         // Append our new volume to the list that share this surf
    1069        2405 :         vols.push_back(voldata);
    1070             : 
    1071             :         // Create a new shared surface
    1072             :         moab::EntityHandle shared_surf;
    1073        2405 :         surf_id++;
    1074        2405 :         createSurf(surf_id, shared_surf, overlap, vols);
    1075             :       }
    1076             : 
    1077             :       // Subtract from the input list
    1078       23702 :       for (auto & shared : overlap)
    1079       20440 :         faces.erase(shared);
    1080             : 
    1081        3262 :       if (faces.empty())
    1082             :         break;
    1083             :     }
    1084     1799045 :   }
    1085             : 
    1086        3528 :   if (!faces.empty())
    1087             :   {
    1088             :     moab::EntityHandle surface_set;
    1089        1814 :     std::vector<VolData> voldatavec(1, voldata);
    1090        1814 :     surf_id++;
    1091        1814 :     createSurf(surf_id, surface_set, faces, voldatavec);
    1092        1814 :   }
    1093             : }
    1094             : 
    1095             : void
    1096          35 : MoabSkinner::buildGraveyard(unsigned int & vol_id, unsigned int & surf_id)
    1097             : {
    1098             :   // Create the graveyard group
    1099             :   moab::EntityHandle graveyard;
    1100          35 :   unsigned int id = nBins() + 1;
    1101          35 :   createGroup(id, "mat:Graveyard", graveyard);
    1102             : 
    1103             :   // Create a volume set
    1104             :   moab::EntityHandle volume_set;
    1105          35 :   createVol(++vol_id, volume_set, graveyard);
    1106             : 
    1107             :   // Set up for the volume data to pass to surfs
    1108          35 :   VolData vdata = {volume_set, Sense::FORWARDS};
    1109             : 
    1110             :   // Find a bounding box
    1111          35 :   BoundingBox bbox = MeshTools::create_bounding_box(getMooseMesh().getMesh());
    1112             : 
    1113             :   // Build the two cubic surfaces defining the graveyard
    1114          35 :   createSurfaceFromBox(
    1115             :       bbox, vdata, surf_id, false /* normals point into box */, _graveyard_scale_inner);
    1116          35 :   createSurfaceFromBox(
    1117             :       bbox, vdata, surf_id, true /* normals point out of box */, _graveyard_scale_outer);
    1118          35 : }
    1119             : 
    1120             : void
    1121          70 : MoabSkinner::createSurfaceFromBox(const BoundingBox & box,
    1122             :                                   const VolData & voldata,
    1123             :                                   unsigned int & surf_id,
    1124             :                                   bool normalout,
    1125             :                                   const Real & factor)
    1126             : {
    1127          70 :   std::vector<moab::EntityHandle> vert_handles = createNodesFromBox(box, factor);
    1128             : 
    1129             :   // Create the tris in 4 groups of 3 (4 open tetrahedra)
    1130             :   moab::Range tris;
    1131          70 :   createCornerTris(vert_handles, 0, 1, 2, 4, normalout, tris);
    1132          70 :   createCornerTris(vert_handles, 3, 2, 1, 7, normalout, tris);
    1133          70 :   createCornerTris(vert_handles, 6, 4, 2, 7, normalout, tris);
    1134          70 :   createCornerTris(vert_handles, 5, 1, 4, 7, normalout, tris);
    1135             : 
    1136             :   moab::EntityHandle surface_set;
    1137          70 :   std::vector<VolData> voldatavec(1, voldata);
    1138          70 :   surf_id++;
    1139          70 :   createSurf(surf_id, surface_set, tris, voldatavec);
    1140         140 : }
    1141             : 
    1142             : std::vector<moab::EntityHandle>
    1143          70 : MoabSkinner::createNodesFromBox(const BoundingBox & box, const Real & factor) const
    1144             : {
    1145             :   std::vector<moab::EntityHandle> vert_handles;
    1146             : 
    1147             :   // Fetch the vertices of the box
    1148          70 :   auto verts = geom_utils::boxCorners(box, factor);
    1149             : 
    1150             :   // Array to represent a coord in MOAB
    1151             :   double coord[3];
    1152             : 
    1153             :   // Create the vertices in MOAB and get the handles
    1154         630 :   for (const auto & vert : verts)
    1155             :   {
    1156         560 :     coord[0] = vert(0) * _scaling;
    1157         560 :     coord[1] = vert(1) * _scaling;
    1158         560 :     coord[2] = vert(2) * _scaling;
    1159             : 
    1160             :     moab::EntityHandle ent;
    1161         560 :     check(_moab->create_vertex(coord, ent));
    1162         560 :     vert_handles.push_back(ent);
    1163             :   }
    1164             : 
    1165          70 :   return vert_handles;
    1166          70 : }
    1167             : 
    1168             : void
    1169         280 : MoabSkinner::createCornerTris(const std::vector<moab::EntityHandle> & verts,
    1170             :                               unsigned int corner,
    1171             :                               unsigned int v1,
    1172             :                               unsigned int v2,
    1173             :                               unsigned int v3,
    1174             :                               bool normalout,
    1175             :                               moab::Range & surface_tris)
    1176             : {
    1177             :   // Create 3 tris stemming from one corner (i.e. an open tetrahedron)
    1178             :   // Assume first is the central corner, and the others are labelled clockwise looking down on the
    1179             :   // corner
    1180         280 :   unsigned int indices[3] = {v1, v2, v3};
    1181             : 
    1182             :   // Create each tri by a cyclic permutation of indices
    1183             :   // Values of i1, i2 in the loop: 0,1; 1,2; 2;0
    1184        1120 :   for (unsigned int i = 0; i < 3; i++)
    1185             :   {
    1186         840 :     int i1 = indices[i % 3];
    1187         840 :     int i2 = indices[(i + 1) % 3];
    1188         840 :     if (normalout) // anti-clockwise: normal points outwards
    1189         420 :       surface_tris.insert(createTri(verts, corner, i2, i1));
    1190             :     else // clockwise: normal points inwards
    1191         420 :       surface_tris.insert(createTri(verts, corner, i1, i2));
    1192             :   }
    1193         280 : }
    1194             : 
    1195             : moab::EntityHandle
    1196         840 : MoabSkinner::createTri(const std::vector<moab::EntityHandle> & vertices,
    1197             :                        unsigned int v1,
    1198             :                        unsigned int v2,
    1199             :                        unsigned int v3)
    1200             : {
    1201             :   moab::EntityHandle triangle;
    1202         840 :   moab::EntityHandle connectivity[3] = {vertices[v1], vertices[v2], vertices[v3]};
    1203         840 :   check(_moab->create_element(moab::MBTRI, connectivity, 3, triangle));
    1204         840 :   return triangle;
    1205             : }
    1206             : 
    1207             : void
    1208          19 : MoabSkinner::setGraveyard(bool build)
    1209             : {
    1210          19 :   if (build != _build_graveyard)
    1211             :   {
    1212           2 :     std::string original = _build_graveyard ? "true" : "false";
    1213           2 :     std::string change = _build_graveyard ? "false" : "true";
    1214           1 :     mooseWarning("Overriding graveyard setting from ",
    1215             :                  original,
    1216             :                  " to ",
    1217             :                  change,
    1218             :                  ".\n"
    1219             :                  "To hide this warning, set 'build_graveyard = ",
    1220             :                  change,
    1221             :                  "'");
    1222             :   }
    1223             : 
    1224          18 :   _build_graveyard = build;
    1225          18 : }
    1226             : #endif

Generated by: LCOV version 1.14