Line data Source code
1 : //* This file is part of the MOOSE framework
2 : //* https://mooseframework.inl.gov
3 : //*
4 : //* All rights reserved, see COPYRIGHT for full restrictions
5 : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 : //*
7 : //* Licensed under LGPL 2.1, please see LICENSE for details
8 : //* https://www.gnu.org/licenses/lgpl-2.1.html
9 :
10 : #include "MeshInfo.h"
11 : #include "SubProblem.h"
12 : #include "libmesh/system.h"
13 : #include "libmesh/equation_systems.h"
14 : #include "libmesh/parallel_sync.h"
15 :
16 : registerMooseObject("MooseApp", MeshInfo);
17 :
18 : InputParameters
19 15535 : MeshInfo::validParams()
20 : {
21 15535 : InputParameters params = GeneralReporter::validParams();
22 31070 : params.addClassDescription(
23 : "Report mesh information, such as the number of elements, nodes, and degrees of freedom.");
24 :
25 : MultiMooseEnum items(
26 : "num_dofs num_dofs_nonlinear num_dofs_auxiliary num_elements num_nodes num_local_dofs "
27 : "num_local_dofs_nonlinear num_local_dofs_auxiliary num_local_elements num_local_nodes "
28 : "local_sidesets local_sideset_elems sidesets sideset_elems local_subdomains "
29 31070 : "local_subdomain_elems subdomains subdomain_elems");
30 46605 : params.addParam<MultiMooseEnum>(
31 : "items",
32 : items,
33 : "The iteration information to output, if nothing is provided everything will be output.");
34 :
35 31070 : return params;
36 15535 : }
37 :
38 371 : MeshInfo::MeshInfo(const InputParameters & parameters)
39 : : GeneralReporter(parameters),
40 371 : _items(getParam<MultiMooseEnum>("items")),
41 1113 : _num_dofs(declareHelper<unsigned int>("num_dofs", REPORTER_MODE_REPLICATED)),
42 1113 : _num_dofs_nl(declareHelper<unsigned int>("num_dofs_nonlinear", REPORTER_MODE_REPLICATED)),
43 1113 : _num_dofs_aux(declareHelper<unsigned int>("num_dofs_auxiliary", REPORTER_MODE_REPLICATED)),
44 371 : _num_dofs_constrained(
45 1113 : declareHelper<unsigned int>("num_dofs_constrained", REPORTER_MODE_REPLICATED)),
46 1113 : _num_elem(declareHelper<unsigned int>("num_elements", REPORTER_MODE_REPLICATED)),
47 1113 : _num_node(declareHelper<unsigned int>("num_nodes", REPORTER_MODE_REPLICATED)),
48 1113 : _num_local_dofs(declareHelper<unsigned int>("num_local_dofs", REPORTER_MODE_DISTRIBUTED)),
49 371 : _num_local_dofs_nl(
50 1113 : declareHelper<unsigned int>("num_dofs_local_nonlinear", REPORTER_MODE_DISTRIBUTED)),
51 371 : _num_local_dofs_aux(
52 1113 : declareHelper<unsigned int>("num_dofs_local_auxiliary", REPORTER_MODE_DISTRIBUTED)),
53 1113 : _num_local_elem(declareHelper<unsigned int>("num_local_elements", REPORTER_MODE_DISTRIBUTED)),
54 1113 : _num_local_node(declareHelper<unsigned int>("num_local_nodes", REPORTER_MODE_DISTRIBUTED)),
55 :
56 1113 : _local_sidesets(declareHelper<std::map<BoundaryID, SidesetInfo>>("local_sidesets",
57 : REPORTER_MODE_DISTRIBUTED)),
58 1113 : _local_sideset_elems(declareHelper<std::map<BoundaryID, SidesetInfo>>(
59 : "local_sideset_elems", REPORTER_MODE_DISTRIBUTED)),
60 1113 : _sidesets(declareHelper<std::map<BoundaryID, SidesetInfo>>("sidesets", REPORTER_MODE_ROOT)),
61 371 : _sideset_elems(
62 1113 : declareHelper<std::map<BoundaryID, SidesetInfo>>("sideset_elems", REPORTER_MODE_ROOT)),
63 :
64 1113 : _local_subdomains(declareHelper<std::map<SubdomainID, SubdomainInfo>>(
65 : "local_subdomains", REPORTER_MODE_DISTRIBUTED)),
66 1113 : _local_subdomain_elems(declareHelper<std::map<SubdomainID, SubdomainInfo>>(
67 : "local_subdomain_elems", REPORTER_MODE_DISTRIBUTED)),
68 371 : _subdomains(
69 1113 : declareHelper<std::map<SubdomainID, SubdomainInfo>>("subdomains", REPORTER_MODE_ROOT)),
70 371 : _subdomain_elems(
71 1113 : declareHelper<std::map<SubdomainID, SubdomainInfo>>("subdomain_elems", REPORTER_MODE_ROOT)),
72 :
73 371 : _equation_systems(_fe_problem.es()),
74 371 : _nonlinear_system(_fe_problem.es().get_system("nl0")),
75 371 : _aux_system(_fe_problem.es().get_system("aux0")),
76 742 : _mesh(_fe_problem.mesh().getMesh())
77 : {
78 371 : }
79 :
80 : void
81 344 : MeshInfo::execute()
82 : {
83 344 : _num_dofs_nl = _nonlinear_system.n_dofs();
84 344 : _num_dofs_aux = _aux_system.n_dofs();
85 344 : _num_dofs = _equation_systems.n_dofs();
86 344 : _num_dofs_constrained = 0;
87 1032 : for (auto s : make_range(_equation_systems.n_systems()))
88 688 : _num_dofs_constrained += _equation_systems.get_system(s).n_constrained_dofs();
89 :
90 344 : _num_node = _mesh.n_nodes();
91 344 : _num_elem = _mesh.n_elem();
92 344 : _num_local_dofs_nl = _nonlinear_system.n_local_dofs();
93 344 : _num_local_dofs_aux = _aux_system.n_local_dofs();
94 344 : _num_local_dofs = _num_local_dofs_nl + _num_local_dofs_aux;
95 344 : _num_local_node = _mesh.n_local_nodes();
96 344 : _num_local_elem = _mesh.n_local_elem();
97 :
98 344 : possiblyAddSidesetInfo();
99 344 : possiblyAddSubdomainInfo();
100 344 : }
101 :
102 : void
103 344 : MeshInfo::possiblyAddSidesetInfo()
104 : {
105 : // Helper for adding the sideset names to a given map of sidesets
106 313 : auto add_sideset_names = [&](std::map<BoundaryID, SidesetInfo> & sidesets)
107 : {
108 1192 : for (auto & pair : sidesets)
109 879 : pair.second.name = _mesh.get_boundary_info().get_sideset_name(pair.second.id);
110 313 : };
111 :
112 : // Helper for sorting all of the sides in each sideset
113 224 : auto sort_sides = [](std::map<BoundaryID, SidesetInfo> & sidesets)
114 : {
115 801 : for (auto & pair : sidesets)
116 577 : std::sort(pair.second.sides.begin(), pair.second.sides.end());
117 224 : };
118 :
119 344 : const bool include_all = !_items.isValid();
120 :
121 1214 : if (include_all || _items.isValueSet("local_sidesets") ||
122 1794 : _items.isValueSet("local_sideset_elems") || _items.isValueSet("sideset_elems"))
123 : {
124 170 : _local_sidesets.clear();
125 170 : _local_sideset_elems.clear();
126 170 : _sideset_elems.clear();
127 :
128 : // Fill the local sideset information; all cases need it
129 170 : std::map<BoundaryID, SidesetInfo> sidesets;
130 170 : for (const auto & bnd_elem :
131 2984 : as_range(_fe_problem.mesh().bndElemsBegin(), _fe_problem.mesh().bndElemsEnd()))
132 2644 : if (bnd_elem->_elem->processor_id() == processor_id())
133 : {
134 1744 : auto & entry = sidesets[bnd_elem->_bnd_id];
135 1744 : entry.id = bnd_elem->_bnd_id;
136 1744 : entry.sides.emplace_back(bnd_elem->_elem->id(), bnd_elem->_side);
137 170 : }
138 :
139 : // For local sidesets: copy over the local info, remove the sides, and add the names
140 402 : if (include_all || _items.isValueSet("local_sidesets"))
141 : {
142 : // Copy over the local sideset info, remove the sides, and add the names
143 54 : _local_sidesets = sidesets;
144 216 : for (auto & pair : _local_sidesets)
145 162 : pair.second.sides.clear();
146 54 : add_sideset_names(_local_sidesets);
147 : }
148 :
149 : // For local sideset elems: copy over the local info, and add the names
150 402 : if (include_all || _items.isValueSet("local_sideset_elems"))
151 : {
152 54 : _local_sideset_elems = sidesets;
153 54 : sort_sides(_local_sideset_elems);
154 54 : add_sideset_names(_local_sideset_elems);
155 : }
156 :
157 : // For the global sideset elems, we need to communicate all of the elems
158 402 : if (include_all || _items.isValueSet("sideset_elems"))
159 : {
160 : // Set up a structure for sending each (id, elem id, side) tuple to root
161 : std::map<processor_id_type,
162 : std::vector<std::tuple<boundary_id_type, dof_id_type, unsigned int>>>
163 170 : send_info;
164 : // Avoid empty sends
165 170 : if (!sidesets.empty())
166 : {
167 170 : auto & root_info = send_info[0];
168 691 : for (const auto & pair : sidesets)
169 2265 : for (const auto & side : pair.second.sides)
170 1744 : root_info.emplace_back(pair.second.id, side.first, side.second);
171 : }
172 :
173 : // Take the received information and insert it into _sideset_elems
174 : auto accumulate_info =
175 170 : [this](processor_id_type,
176 : const std::vector<std::tuple<boundary_id_type, dof_id_type, unsigned int>> & info)
177 : {
178 1914 : for (const auto & tuple : info)
179 : {
180 1744 : const auto id = std::get<0>(tuple);
181 1744 : auto & entry = _sideset_elems[id];
182 1744 : entry.id = id;
183 1744 : entry.sides.emplace_back(std::get<1>(tuple), std::get<2>(tuple));
184 : }
185 170 : };
186 :
187 : // Push the information and insert it into _sideset_elems on root
188 170 : Parallel::push_parallel_vector_data(comm(), send_info, accumulate_info);
189 :
190 170 : sort_sides(_sideset_elems);
191 170 : add_sideset_names(_sideset_elems);
192 170 : }
193 170 : }
194 :
195 : // For global sideset information without elements, we can simplify communication.
196 : // All we need are the boundary IDs from libMesh (may not be reduced, so take the union)
197 : // and then add the names (global)
198 924 : if (include_all || _items.isValueSet("sidesets"))
199 : {
200 64 : _sidesets.clear();
201 :
202 64 : auto boundary_ids = _mesh.get_boundary_info().get_boundary_ids();
203 64 : comm().set_union(boundary_ids, 0);
204 64 : if (processor_id() == 0)
205 : {
206 175 : for (const auto id : boundary_ids)
207 140 : _sidesets[id].id = id;
208 35 : add_sideset_names(_sidesets);
209 : }
210 64 : }
211 344 : }
212 :
213 : void
214 478 : to_json(nlohmann::json & json, const std::map<BoundaryID, MeshInfo::SidesetInfo> & sidesets)
215 : {
216 1303 : for (const auto & pair : sidesets)
217 : {
218 825 : const MeshInfo::SidesetInfo & sideset_info = pair.second;
219 :
220 825 : nlohmann::json sideset_json;
221 825 : sideset_json["id"] = sideset_info.id;
222 825 : if (sideset_info.name.size())
223 825 : sideset_json["name"] = sideset_info.name;
224 825 : if (sideset_info.sides.size())
225 : {
226 550 : auto & sides_json = sideset_json["sides"];
227 :
228 3194 : for (const std::pair<dof_id_type, unsigned int> & pair : sideset_info.sides)
229 : {
230 2644 : nlohmann::json side_json;
231 2644 : side_json["elem_id"] = pair.first;
232 2644 : side_json["side"] = pair.second;
233 2644 : sides_json.push_back(side_json);
234 2644 : }
235 : }
236 :
237 825 : json.push_back(sideset_json);
238 825 : }
239 478 : }
240 :
241 : void
242 95 : dataStore(std::ostream & stream, MeshInfo::SidesetInfo & sideset_info, void * context)
243 : {
244 95 : storeHelper(stream, sideset_info.id, context);
245 95 : storeHelper(stream, sideset_info.name, context);
246 95 : storeHelper(stream, sideset_info.sides, context);
247 95 : }
248 :
249 : void
250 95 : dataLoad(std::istream & stream, MeshInfo::SidesetInfo & sideset_info, void * context)
251 : {
252 95 : loadHelper(stream, sideset_info.id, context);
253 95 : loadHelper(stream, sideset_info.name, context);
254 95 : loadHelper(stream, sideset_info.sides, context);
255 95 : }
256 :
257 : void
258 344 : MeshInfo::possiblyAddSubdomainInfo()
259 : {
260 : // Helper for adding the subdomain names to a given map of subdomains
261 242 : auto add_subdomain_names = [&](std::map<SubdomainID, SubdomainInfo> & subdomains)
262 : {
263 690 : for (auto & pair : subdomains)
264 448 : pair.second.name = _mesh.subdomain_name(pair.second.id);
265 242 : };
266 :
267 : // Helper for sorting all of the elems in each subdomain
268 153 : auto sort_elems = [](std::map<SubdomainID, SubdomainInfo> & subdomains)
269 : {
270 504 : for (auto & pair : subdomains)
271 351 : std::sort(pair.second.elems.begin(), pair.second.elems.end());
272 153 : };
273 :
274 344 : const bool include_all = !_items.isValid();
275 :
276 1214 : if (include_all || _items.isValueSet("local_subdomains") ||
277 1794 : _items.isValueSet("local_subdomain_elems") || _items.isValueSet("subdomain_elems"))
278 : {
279 150 : _local_subdomains.clear();
280 150 : _local_subdomain_elems.clear();
281 150 : _subdomain_elems.clear();
282 :
283 : // Fill the local subdomain information; all cases need it
284 150 : std::map<SubdomainID, SubdomainInfo> subdomains;
285 4002 : for (const auto & elem : *_fe_problem.mesh().getActiveLocalElementRange())
286 : {
287 3852 : auto & entry = subdomains[elem->subdomain_id()];
288 3852 : entry.id = elem->subdomain_id();
289 3852 : entry.elems.push_back(elem->id());
290 : }
291 :
292 : // For local subdomains: copy over the local info, remove the elems, and add the names
293 342 : if (include_all || _items.isValueSet("local_subdomains"))
294 : {
295 54 : _local_subdomains = subdomains;
296 108 : for (auto & pair : _local_subdomains)
297 54 : pair.second.elems.clear();
298 54 : add_subdomain_names(_local_subdomains);
299 : }
300 :
301 : // For local subdomain elems: copy over the local info, and add the names
302 342 : if (include_all || _items.isValueSet("local_subdomain_elems"))
303 : {
304 54 : _local_subdomain_elems = subdomains;
305 54 : sort_elems(_local_subdomain_elems);
306 54 : add_subdomain_names(_local_subdomain_elems);
307 : }
308 :
309 : // For the global subdomain elems, we need to communicate all of the elems
310 342 : if (include_all || _items.isValueSet("subdomain_elems"))
311 : {
312 : // Set up a structure for sending each (id, elem id) to root
313 150 : std::map<processor_id_type, std::vector<std::pair<subdomain_id_type, dof_id_type>>> send_info;
314 : // Avoid creating empty entry
315 150 : if (!subdomains.empty())
316 : {
317 150 : auto & root_info = send_info[0];
318 480 : for (const auto & pair : subdomains)
319 4182 : for (const auto elem_id : pair.second.elems)
320 3852 : root_info.emplace_back(pair.second.id, elem_id);
321 : }
322 :
323 : // Take the received information and insert it into _subdomain_elems
324 : auto accumulate_info =
325 150 : [this](processor_id_type,
326 : const std::vector<std::pair<subdomain_id_type, dof_id_type>> & info)
327 : {
328 4002 : for (const auto & subdomain_elem_pair : info)
329 : {
330 3852 : auto & entry = _subdomain_elems[subdomain_elem_pair.first];
331 3852 : entry.id = subdomain_elem_pair.first;
332 3852 : entry.elems.emplace_back(subdomain_elem_pair.second);
333 : }
334 150 : };
335 :
336 : // Push the information and insert it into _subdomain_elems on root
337 150 : Parallel::push_parallel_vector_data(comm(), send_info, accumulate_info);
338 :
339 150 : if (processor_id() == 0)
340 : {
341 99 : sort_elems(_subdomain_elems);
342 99 : add_subdomain_names(_subdomain_elems);
343 : }
344 150 : }
345 150 : }
346 :
347 : // For global subdomain information without elements, we can simplify communication.
348 : // All we need are the subdomain IDs from libMesh and then add the names (global)
349 924 : if (include_all || _items.isValueSet("subdomains"))
350 : {
351 64 : _subdomains.clear();
352 :
353 64 : std::set<subdomain_id_type> subdomain_ids;
354 64 : _mesh.subdomain_ids(subdomain_ids);
355 :
356 64 : if (processor_id() == 0)
357 : {
358 78 : for (const auto id : subdomain_ids)
359 43 : _subdomains[id].id = id;
360 35 : add_subdomain_names(_subdomains);
361 : }
362 64 : }
363 344 : }
364 :
365 : void
366 448 : to_json(nlohmann::json & json, const std::map<SubdomainID, MeshInfo::SubdomainInfo> & subdomains)
367 : {
368 878 : for (const auto & pair : subdomains)
369 : {
370 430 : const MeshInfo::SubdomainInfo & subdomain_info = pair.second;
371 :
372 430 : nlohmann::json subdomain_json;
373 430 : subdomain_json["id"] = subdomain_info.id;
374 430 : if (subdomain_info.name.size())
375 286 : subdomain_json["name"] = subdomain_info.name;
376 430 : if (subdomain_info.elems.size())
377 : {
378 342 : auto & sides_json = subdomain_json["elems"];
379 6444 : for (const auto & id : subdomain_info.elems)
380 6102 : sides_json.push_back(id);
381 : }
382 :
383 430 : json.push_back(subdomain_json);
384 430 : }
385 448 : }
386 :
387 : void
388 50 : dataStore(std::ostream & stream, MeshInfo::SubdomainInfo & subdomain_info, void * context)
389 : {
390 50 : storeHelper(stream, subdomain_info.id, context);
391 50 : storeHelper(stream, subdomain_info.name, context);
392 50 : storeHelper(stream, subdomain_info.elems, context);
393 50 : }
394 :
395 : void
396 50 : dataLoad(std::istream & stream, MeshInfo::SubdomainInfo & subdomain_info, void * context)
397 : {
398 50 : loadHelper(stream, subdomain_info.id, context);
399 50 : loadHelper(stream, subdomain_info.name, context);
400 50 : loadHelper(stream, subdomain_info.elems, context);
401 50 : }
|