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
|