Line data Source code
1 : #pragma once
2 :
3 : #include "GeneralUserObject.h"
4 : #include "MaterialBase.h"
5 : #include "MooseMesh.h"
6 :
7 : #include "moab/Core.hpp"
8 : #include "moab/Skinner.hpp"
9 : #include "moab/GeomTopoTool.hpp"
10 : #include "MBTagConventions.hpp"
11 :
12 : /**
13 : * \brief Skins the [Mesh] according to individual bins for temperature, density, and subdomain ID
14 : *
15 : * Skins a [Mesh] according to temperature, density, and subdomain. The MOAB surfaces bounding
16 : * those grouped elements are then generated, providing geometry information needed for DAGMC
17 : * to then track particles on this new geometry.
18 : */
19 : class MoabSkinner : public GeneralUserObject
20 : {
21 : public:
22 : MoabSkinner(const InputParameters & parameters);
23 :
24 : static InputParameters validParams();
25 :
26 : virtual void execute() override;
27 :
28 : virtual void initialize() override;
29 :
30 : virtual void finalize() override;
31 :
32 0 : virtual void threadJoin(const UserObject & /* uo */) override {}
33 :
34 : /**
35 : * Wrap the error handling in MOAB to print errors to user
36 : * @param[in] input MOAB error code
37 : * @return error mode
38 : */
39 : virtual moab::ErrorCode check(const moab::ErrorCode input) const;
40 :
41 : std::string materialName(const unsigned int & block, const unsigned int & density, const unsigned int & temp) const;
42 :
43 : /// Perform the skinning operation
44 : virtual void update();
45 :
46 : /**
47 : * Set the names to be used for naming the subdomains in the skinned mesh;
48 : * there should be one name per subdomain.
49 : * @param[in] names names for subdomains
50 : */
51 18 : virtual void setMaterialNames(std::vector<std::string> names) { _material_names = names; }
52 :
53 : /**
54 : * Get the total number of bins
55 : * @return total number of bins
56 : */
57 : unsigned int nBins() const;
58 :
59 : /**
60 : * Get the bin index for the temperature
61 : * @param[in] elem element
62 : * @return temperature bin index
63 : */
64 : virtual unsigned int getTemperatureBin(const Elem * const elem) const;
65 :
66 : /**
67 : * Get the bin index for the density
68 : * @param[in] elem element
69 : * @return density bin index
70 : */
71 : virtual unsigned int getDensityBin(const Elem * const elem) const;
72 :
73 : /**
74 : * Get the bin index for the subdomain
75 : * @param[in] elem element
76 : * @return subdomain bin index
77 : */
78 51652 : virtual unsigned int getSubdomainBin(const Elem * const elem) const
79 : {
80 51652 : return _blocks.at(elem->subdomain_id());
81 : }
82 :
83 : /**
84 : * Override the user parameter for use_displaced
85 : * @param[in] use whether to use the displaced mesh
86 : */
87 : void setUseDisplacedMesh(const bool & use);
88 :
89 : /**
90 : * Set the length multiplier to get from [Mesh] units into centimeters
91 : * @param[in] scale multiplier
92 : */
93 19 : virtual void setScaling(const Real & scale) { _scaling = scale; }
94 :
95 : /**
96 : * Set the verbosity level
97 : * @param[in] verbose whether to print diagnostic information
98 : */
99 19 : virtual void setVerbosity(const bool & verbose) { _verbose = verbose; }
100 :
101 : /**
102 : * Indicate whether this userobject is run by itself (for testing purposes)
103 : * or controlled by some other class.
104 : */
105 19 : virtual void makeDependentOnExternalAction() { _standalone = false; }
106 :
107 : /**
108 : * Get variable number in the auxiliary system
109 : * @param[in] name variable name
110 : * @param[in] param_name parameter name, for printing a helpful error message
111 : * @return variable number
112 : */
113 : unsigned int getAuxiliaryVariableNumber(const std::string & name,
114 : const std::string & param_name) const;
115 :
116 : /// Clear mesh data
117 : void reset();
118 :
119 : /**
120 : * Get total bin index given individual indices for the temperature, density, and subdomain bins
121 : * @param[in] temp_bin temperature bin
122 : * @param[in] density_bin density bin
123 : * @param[in] subdomain_bin subdomain ID bin
124 : * @return total bin index
125 : */
126 : virtual unsigned int getBin(const unsigned int & temp_bin,
127 : const unsigned int & density_bin,
128 : const unsigned int & subdomain_bin) const;
129 :
130 : /**
131 : * Whether the skinner builds a graveyard
132 : * @return whether a graveyard is built
133 : */
134 0 : virtual const bool & hasGraveyard() const { return _build_graveyard; }
135 :
136 : /**
137 : * Set the graveyard setting
138 : * @param[in] build whether to build a graveyard
139 : */
140 : void setGraveyard(bool build);
141 :
142 : /**
143 : * Number of density bins; if greater than 1, this means we must be re-generating
144 : * OpenMC materials during the course of the simulation.
145 : * @return number of density bins
146 : */
147 0 : virtual unsigned int nDensityBins() const { return _n_density_bins; }
148 :
149 : /**
150 : * Whether density skinning is applied
151 : * @return using density skinning
152 : */
153 22 : virtual bool hasDensitySkinning() const { return _bin_by_density; }
154 :
155 : /**
156 : * Get pointer to underlying moab interface
157 : * @return pointer to moab interface
158 : */
159 : const std::shared_ptr<moab::Interface> & moabPtr() const { return _moab; }
160 :
161 : protected:
162 : std::unique_ptr<NumericVector<Number>> _serialized_solution;
163 :
164 : /// MOAB interface
165 : std::shared_ptr<moab::Interface> _moab;
166 :
167 : /// Whether to print diagnostic information
168 : bool _verbose;
169 :
170 : /// Name of the temperature variable
171 : const std::string & _temperature_name;
172 :
173 : /// Lower bound of temperature bins
174 : const Real & _temperature_min;
175 :
176 : /// Upper bound of temperature bins
177 : const Real & _temperature_max;
178 :
179 : /// Number of temperature bins
180 : const unsigned int & _n_temperature_bins;
181 :
182 : /// Temperature bin width
183 : const Real _temperature_bin_width;
184 :
185 : /// Whether elements are binned by density (in addition to temperature and block)
186 : const bool _bin_by_density;
187 :
188 : /// Material names corresponding to each subdomain. These are used to name the
189 : /// new skinned volumes in MOAB
190 : std::vector<std::string> _material_names;
191 :
192 : /// Faceting tolerence needed by DAGMC
193 : const Real & _faceting_tol;
194 :
195 : /// Geometry tolerence needed by DAGMC
196 : const Real & _geom_tol;
197 :
198 : /// Multiplier on bounding box for inner surface of graveyard
199 : const Real & _graveyard_scale_inner;
200 :
201 : /// Multiplier on bounding box for outer surface of graveyard
202 : const Real & _graveyard_scale_outer;
203 :
204 : /// Whether to output the MOAB mesh skins to a .h5m file
205 : const bool & _output_skins;
206 :
207 : /// Whether to output the MOAB mesh to a .h5m file
208 : const bool & _output_full;
209 :
210 : /**
211 : * Whether to build a graveyard as two additional cube surfaces surrounding the mesh.
212 : * This is only needed if the skinned geometry is fed into a Monte Carlo code.
213 : */
214 : bool _build_graveyard;
215 :
216 : /// Whether to assign a material to the implicit complement region
217 : bool _set_implicit_complement_material = false;
218 :
219 : /// OpenMC material name or ID which will be assigned to the implicit complement
220 : std::string _implicit_complement_group_name;
221 :
222 : /// Whether the skinned mesh should be generated from a displaced mesh
223 : bool _use_displaced;
224 :
225 : /// Length multiplier to get from [Mesh] units into OpenMC's centimeters
226 : Real _scaling;
227 :
228 : /// Count number of times output files have been written
229 : unsigned int _n_write;
230 :
231 : /// Whether this class runs by itself, or is controlled by an external class
232 : bool _standalone;
233 :
234 : /// Encode the whether the surface normal faces into or out of the volume
235 : enum Sense
236 : {
237 : BACKWARDS = -1,
238 : FORWARDS = 1
239 : };
240 :
241 : /// Encode MOAB information about volumes needed when creating surfaces
242 : struct VolData
243 : {
244 : moab::EntityHandle vol;
245 : Sense sense;
246 : };
247 :
248 : /// Moose mesh
249 : MooseMesh & getMooseMesh();
250 : /**
251 : * Copy the libMesh [Mesh] into a MOAB mesh. This first loops through all of the
252 : * nodes, and rebuilds each as a MOAB vertex. Then, we loop over all of the elements
253 : * and rebuild each as a TET4 (if the libMesh mesh has TET10 elements, they are each
254 : * rebuilt into 8 TET4 elements).
255 : */
256 : void createMOABElems();
257 :
258 : /// Helper method to create MOAB tags
259 : virtual void createTags();
260 :
261 : /**
262 : * Helper method to create MOAB group entity set
263 : * @param[in] id ID for the group
264 : * @param[in] name name for the group
265 : * @param[in] group_set group of entities
266 : */
267 : void createGroup(const unsigned int & id, const std::string & name, moab::EntityHandle & group_set);
268 :
269 : /// Helper method to create MOAB volume entity set
270 : void createVol(const unsigned int & id, moab::EntityHandle & volume_set, moab::EntityHandle group_set);
271 :
272 : /// Helper method to create MOAB surface entity set
273 : void createSurf(const unsigned int & id,
274 : moab::EntityHandle & surface_set,
275 : moab::Range & faces,
276 : const std::vector<VolData> & voldata);
277 :
278 : /// Helper method to create MOAB surfaces with no overlaps
279 : void createSurfaces(moab::Range & reversed, VolData & voldata, unsigned int & surf_id);
280 :
281 : /**
282 : * Create a MOAB surface from a bounding box
283 : */
284 : void createSurfaceFromBox(const BoundingBox & box,
285 : const VolData & voldata,
286 : unsigned int & surf_id,
287 : bool normalout,
288 : const Real & factor);
289 :
290 : /**
291 : * Create MOAB nodes from a bounding box
292 : * @param[in] box bounding box
293 : * @param[in] factor multiplicative factor to resize the bounding box sides
294 : * @return nodes
295 : */
296 : std::vector<moab::EntityHandle> createNodesFromBox(const BoundingBox & box,
297 : const Real & factor) const;
298 :
299 : /// Create 3 tri faces stemming from one corner of a cude (an open tetrahedron)
300 : void createCornerTris(const std::vector<moab::EntityHandle> & verts,
301 : unsigned int corner,
302 : unsigned int v1,
303 : unsigned int v2,
304 : unsigned int v3,
305 : bool normalout,
306 : moab::Range & surface_tris);
307 :
308 : /// Create MOAB tri surface element
309 : moab::EntityHandle createTri(const std::vector<moab::EntityHandle> & vertices,
310 : unsigned int v1,
311 : unsigned int v2,
312 : unsigned int v3);
313 :
314 : /// Add parent-child metadata relating a surface to its volume
315 : void updateSurfData(moab::EntityHandle surface_set, const VolData & data);
316 :
317 : /// Generic method to set the tags that DAGMC requires
318 : void
319 : setTags(moab::EntityHandle ent, std::string name, std::string category, unsigned int id, int dim);
320 :
321 : /// Helper function to wrap moab::tag_set_data for a string
322 : void setTagData(moab::Tag tag, moab::EntityHandle ent, std::string data, unsigned int SIZE);
323 :
324 : /// Helper function to wrap moab::tag_set_data for a generic pointer
325 : void setTagData(moab::Tag tag, moab::EntityHandle ent, void * data);
326 :
327 : /**
328 : * Get the node numberings for the MOAB TET4 elements to build for each [Mesh] element
329 : * @param[in] type element type
330 : */
331 : const std::vector<std::vector<unsigned int>> & getTetSets(ElemType type) const;
332 :
333 : /**
334 : * \brief Build a graveyard volume around the domain
335 : *
336 : * The graveyard is a containing volume which bounds the volume of interest. This is
337 : * only needed if the skinned geometry is going to be input into a Monte Carlo solver. For
338 : * performance reasons, a cubic shell is optimal. So, here we build two cubic surfaces,
339 : * both larger than the bounding box of the "actual" geometry. We name this region
340 : * "mat:Graveyard", so that when OpenMC parses the geometry it knows to assign "void"
341 : * to this region, and set vacuum BCs on the outer surfaces of the cubic shell. The
342 : * remaining space between the "actual" geometry and the inner graveyard surface is
343 : * treated as the implicit complement of the rest of the geometry (e.g. a transmissive region).
344 : */
345 : void buildGraveyard(unsigned int & vol_id, unsigned int & surf_id);
346 :
347 : /// Store a mapping from [Mesh] subdomain IDs to an index, to be used for binning by block ID
348 : virtual void findBlocks();
349 :
350 : /// Sort all the elements in the [Mesh] into bins for temperature, density, and subdomain.
351 : virtual void sortElemsByResults();
352 :
353 : /// Group the binned elems into local temperature regions and find their surfaces
354 : void findSurfaces();
355 :
356 : /// Group a given bin into local regions
357 : /// NB elems in param is a copy, localElems is a reference
358 : void groupLocalElems(std::set<dof_id_type> elems, std::vector<moab::Range> & localElems);
359 :
360 : /// Clear MOAB entity sets
361 : bool resetMOAB();
362 :
363 : /// Find the surfaces for the provided range and add to group
364 : void findSurface(const moab::Range & region,
365 : moab::EntityHandle group,
366 : unsigned int & vol_id,
367 : unsigned int & surf_id,
368 : moab::EntityHandle & volume_set);
369 :
370 : /// Write MOAB volume and/or skin meshes to file
371 : virtual void write();
372 :
373 : /// Moab skinner for finding temperature surfaces
374 : std::unique_ptr<moab::Skinner> skinner;
375 :
376 : /// Topology tool for setting surface sense
377 : std::unique_ptr<moab::GeomTopoTool> gtt;
378 :
379 : /// Map from libmesh id to MOAB element entity handles
380 : std::map<dof_id_type, std::vector<moab::EntityHandle>> _id_to_elem_handles;
381 :
382 : /// Save the first tet entity handle
383 : moab::EntityHandle offset;
384 :
385 : /// Name of the MOOSE variable containing the density
386 : std::string _density_name;
387 :
388 : /// Lower bound of density bins
389 : Real _density_min;
390 :
391 : /// Upper bound of density bins
392 : Real _density_max;
393 :
394 : /// Density bin width
395 : Real _density_bin_width;
396 :
397 : /// Number of density bins
398 : unsigned int _n_density_bins;
399 :
400 : /// Number of block bins
401 : unsigned int _n_block_bins;
402 :
403 : /// Mapping from total bin ID to a set of elements sorted into that bin
404 : std::vector<std::set<dof_id_type>> _elem_bins;
405 :
406 : /// Blocks in the [Mesh]
407 : std::map<SubdomainID, unsigned int> _blocks;
408 :
409 : /// Entity handle to represent the set of all tets
410 : moab::EntityHandle _all_tets;
411 :
412 : /// Save some topological data: map from surface handle to vol handle and sense
413 : std::map<moab::EntityHandle, std::vector<VolData>> surfsToVols;
414 :
415 : /// Tag for dimension for geometry
416 : moab::Tag geometry_dimension_tag;
417 :
418 : /// Tag for entitiy set ID
419 : moab::Tag id_tag;
420 :
421 : /// Tag for faceting tolerance
422 : moab::Tag faceting_tol_tag;
423 :
424 : /// Tag needed by DAGMC
425 : moab::Tag geometry_resabs_tag;
426 :
427 : /// Tag for type of entity set
428 : moab::Tag category_tag;
429 :
430 : /// Tag for name of entity set
431 : moab::Tag name_tag;
432 :
433 : /// Bounds of the temperature bins
434 : std::vector<Real> _temperature_bin_bounds;
435 :
436 : /// Bounds of the density bins
437 : std::vector<Real> _density_bin_bounds;
438 :
439 : /// Node ordering for a TET4 MOAB element, based on libMesh node numberings
440 : std::vector<std::vector<unsigned int>> _tet4_nodes;
441 :
442 : /**
443 : * Node ordering for eight TET4 MOAB elements, based on libMesh node numberings
444 : * for a TET10 element. We re-build the libMesh element into first-order MOAB elements.
445 : */
446 : std::vector<std::vector<unsigned int>> _tet10_nodes;
447 :
448 : /// Auxiliary variable number for temperature
449 : unsigned int _temperature_var_num;
450 :
451 : /// Auxiliary variable number for density
452 : unsigned int _density_var_num;
453 :
454 : /// Number of nodes per MOAB tet (which are first order, so TET4)
455 : const unsigned int NODES_PER_MOAB_TET = 4;
456 :
457 : /// Tolerance to use for comparing values to bin bounds
458 : const Real BIN_TOLERANCE = 1e-6;
459 : };
|