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 3777 : MeshInfo::validParams()
20 : {
21 3777 : InputParameters params = GeneralReporter::validParams();
22 7554 : 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 7554 : "local_subdomain_elems subdomains subdomain_elems");
30 11331 : params.addParam<MultiMooseEnum>(
31 : "items",
32 : items,
33 : "The iteration information to output, if nothing is provided everything will be output.");
34 :
35 7554 : return params;
36 3777 : }
37 :
38 340 : MeshInfo::MeshInfo(const InputParameters & parameters)
39 : : GeneralReporter(parameters),
40 340 : _items(getParam<MultiMooseEnum>("items")),
41 1020 : _num_dofs(declareHelper<unsigned int>("num_dofs", REPORTER_MODE_REPLICATED)),
42 1020 : _num_dofs_nl(declareHelper<unsigned int>("num_dofs_nonlinear", REPORTER_MODE_REPLICATED)),
43 1020 : _num_dofs_aux(declareHelper<unsigned int>("num_dofs_auxiliary", REPORTER_MODE_REPLICATED)),
44 340 : _num_dofs_constrained(
45 1020 : declareHelper<unsigned int>("num_dofs_constrained", REPORTER_MODE_REPLICATED)),
46 1020 : _num_elem(declareHelper<unsigned int>("num_elements", REPORTER_MODE_REPLICATED)),
47 1020 : _num_node(declareHelper<unsigned int>("num_nodes", REPORTER_MODE_REPLICATED)),
48 1020 : _num_local_dofs(declareHelper<unsigned int>("num_local_dofs", REPORTER_MODE_DISTRIBUTED)),
49 340 : _num_local_dofs_nl(
50 1020 : declareHelper<unsigned int>("num_dofs_local_nonlinear", REPORTER_MODE_DISTRIBUTED)),
51 340 : _num_local_dofs_aux(
52 1020 : declareHelper<unsigned int>("num_dofs_local_auxiliary", REPORTER_MODE_DISTRIBUTED)),
53 1020 : _num_local_elem(declareHelper<unsigned int>("num_local_elements", REPORTER_MODE_DISTRIBUTED)),
54 1020 : _num_local_node(declareHelper<unsigned int>("num_local_nodes", REPORTER_MODE_DISTRIBUTED)),
55 :
56 1020 : _local_sidesets(declareHelper<std::map<BoundaryID, SidesetInfo>>("local_sidesets",
57 : REPORTER_MODE_DISTRIBUTED)),
58 1020 : _local_sideset_elems(declareHelper<std::map<BoundaryID, SidesetInfo>>(
59 : "local_sideset_elems", REPORTER_MODE_DISTRIBUTED)),
60 1020 : _sidesets(declareHelper<std::map<BoundaryID, SidesetInfo>>("sidesets", REPORTER_MODE_ROOT)),
61 340 : _sideset_elems(
62 1020 : declareHelper<std::map<BoundaryID, SidesetInfo>>("sideset_elems", REPORTER_MODE_ROOT)),
63 :
64 1020 : _local_subdomains(declareHelper<std::map<SubdomainID, SubdomainInfo>>(
65 : "local_subdomains", REPORTER_MODE_DISTRIBUTED)),
66 1020 : _local_subdomain_elems(declareHelper<std::map<SubdomainID, SubdomainInfo>>(
67 : "local_subdomain_elems", REPORTER_MODE_DISTRIBUTED)),
68 340 : _subdomains(
69 1020 : declareHelper<std::map<SubdomainID, SubdomainInfo>>("subdomains", REPORTER_MODE_ROOT)),
70 340 : _subdomain_elems(
71 1020 : declareHelper<std::map<SubdomainID, SubdomainInfo>>("subdomain_elems", REPORTER_MODE_ROOT)),
72 :
73 340 : _equation_systems(_fe_problem.es()),
74 340 : _nonlinear_system(_fe_problem.es().get_system("nl0")),
75 340 : _aux_system(_fe_problem.es().get_system("aux0")),
76 680 : _mesh(_fe_problem.mesh().getMesh())
77 : {
78 340 : }
79 :
80 : void
81 313 : MeshInfo::execute()
82 : {
83 313 : _num_dofs_nl = _nonlinear_system.n_dofs();
84 313 : _num_dofs_aux = _aux_system.n_dofs();
85 313 : _num_dofs = _equation_systems.n_dofs();
86 313 : _num_dofs_constrained = 0;
87 939 : for (auto s : make_range(_equation_systems.n_systems()))
88 626 : _num_dofs_constrained += _equation_systems.get_system(s).n_constrained_dofs();
89 :
90 313 : _num_node = _mesh.n_nodes();
91 313 : _num_elem = _mesh.n_elem();
92 313 : _num_local_dofs_nl = _nonlinear_system.n_local_dofs();
93 313 : _num_local_dofs_aux = _aux_system.n_local_dofs();
94 313 : _num_local_dofs = _num_local_dofs_nl + _num_local_dofs_aux;
95 313 : _num_local_node = _mesh.n_local_nodes();
96 313 : _num_local_elem = _mesh.n_local_elem();
97 :
98 313 : possiblyAddSidesetInfo();
99 313 : possiblyAddSubdomainInfo();
100 313 : }
101 :
102 : void
103 313 : MeshInfo::possiblyAddSidesetInfo()
104 : {
105 : // Helper for adding the sideset names to a given map of sidesets
106 281 : auto add_sideset_names = [&](std::map<BoundaryID, SidesetInfo> & sidesets)
107 : {
108 1061 : for (auto & pair : sidesets)
109 780 : pair.second.name = _mesh.get_boundary_info().get_sideset_name(pair.second.id);
110 281 : };
111 :
112 : // Helper for sorting all of the sides in each sideset
113 202 : auto sort_sides = [](std::map<BoundaryID, SidesetInfo> & sidesets)
114 : {
115 714 : for (auto & pair : sidesets)
116 512 : std::sort(pair.second.sides.begin(), pair.second.sides.end());
117 202 : };
118 :
119 313 : const bool include_all = !_items.isValid();
120 :
121 1108 : if (include_all || _items.isValueSet("local_sidesets") ||
122 1638 : _items.isValueSet("local_sideset_elems") || _items.isValueSet("sideset_elems"))
123 : {
124 154 : _local_sidesets.clear();
125 154 : _local_sideset_elems.clear();
126 154 : _sideset_elems.clear();
127 :
128 : // Fill the local sideset information; all cases need it
129 154 : std::map<BoundaryID, SidesetInfo> sidesets;
130 154 : for (const auto & bnd_elem :
131 2672 : as_range(_fe_problem.mesh().bndElemsBegin(), _fe_problem.mesh().bndElemsEnd()))
132 2364 : if (bnd_elem->_elem->processor_id() == processor_id())
133 : {
134 1548 : auto & entry = sidesets[bnd_elem->_bnd_id];
135 1548 : entry.id = bnd_elem->_bnd_id;
136 1548 : entry.sides.emplace_back(bnd_elem->_elem->id(), bnd_elem->_side);
137 154 : }
138 :
139 : // For local sidesets: copy over the local info, remove the sides, and add the names
140 366 : if (include_all || _items.isValueSet("local_sidesets"))
141 : {
142 : // Copy over the local sideset info, remove the sides, and add the names
143 48 : _local_sidesets = sidesets;
144 192 : for (auto & pair : _local_sidesets)
145 144 : pair.second.sides.clear();
146 48 : add_sideset_names(_local_sidesets);
147 : }
148 :
149 : // For local sideset elems: copy over the local info, and add the names
150 366 : if (include_all || _items.isValueSet("local_sideset_elems"))
151 : {
152 48 : _local_sideset_elems = sidesets;
153 48 : sort_sides(_local_sideset_elems);
154 48 : add_sideset_names(_local_sideset_elems);
155 : }
156 :
157 : // For the global sideset elems, we need to communicate all of the elems
158 366 : 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 154 : send_info;
164 : // Avoid empty sends
165 154 : if (!sidesets.empty())
166 : {
167 154 : auto & root_info = send_info[0];
168 622 : for (const auto & pair : sidesets)
169 2016 : for (const auto & side : pair.second.sides)
170 1548 : 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 154 : [this](processor_id_type,
176 : const std::vector<std::tuple<boundary_id_type, dof_id_type, unsigned int>> & info)
177 : {
178 1702 : for (const auto & tuple : info)
179 : {
180 1548 : const auto id = std::get<0>(tuple);
181 1548 : auto & entry = _sideset_elems[id];
182 1548 : entry.id = id;
183 1548 : entry.sides.emplace_back(std::get<1>(tuple), std::get<2>(tuple));
184 : }
185 154 : };
186 :
187 : // Push the information and insert it into _sideset_elems on root
188 154 : Parallel::push_parallel_vector_data(comm(), send_info, accumulate_info);
189 :
190 154 : sort_sides(_sideset_elems);
191 154 : add_sideset_names(_sideset_elems);
192 154 : }
193 154 : }
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 843 : if (include_all || _items.isValueSet("sidesets"))
199 : {
200 57 : _sidesets.clear();
201 :
202 57 : auto boundary_ids = _mesh.get_boundary_info().get_boundary_ids();
203 57 : comm().set_union(boundary_ids, 0);
204 57 : if (processor_id() == 0)
205 : {
206 155 : for (const auto id : boundary_ids)
207 124 : _sidesets[id].id = id;
208 31 : add_sideset_names(_sidesets);
209 : }
210 57 : }
211 313 : }
212 :
213 : void
214 424 : to_json(nlohmann::json & json, const std::map<BoundaryID, MeshInfo::SidesetInfo> & sidesets)
215 : {
216 1156 : for (const auto & pair : sidesets)
217 : {
218 732 : const MeshInfo::SidesetInfo & sideset_info = pair.second;
219 :
220 732 : nlohmann::json sideset_json;
221 732 : sideset_json["id"] = sideset_info.id;
222 732 : if (sideset_info.name.size())
223 732 : sideset_json["name"] = sideset_info.name;
224 732 : if (sideset_info.sides.size())
225 : {
226 488 : auto & sides_json = sideset_json["sides"];
227 :
228 2836 : for (const std::pair<dof_id_type, unsigned int> & pair : sideset_info.sides)
229 : {
230 2348 : nlohmann::json side_json;
231 2348 : side_json["elem_id"] = pair.first;
232 2348 : side_json["side"] = pair.second;
233 2348 : sides_json.push_back(side_json);
234 2348 : }
235 : }
236 :
237 732 : json.push_back(sideset_json);
238 732 : }
239 424 : }
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 313 : MeshInfo::possiblyAddSubdomainInfo()
259 : {
260 : // Helper for adding the subdomain names to a given map of subdomains
261 215 : auto add_subdomain_names = [&](std::map<SubdomainID, SubdomainInfo> & subdomains)
262 : {
263 613 : for (auto & pair : subdomains)
264 398 : pair.second.name = _mesh.subdomain_name(pair.second.id);
265 215 : };
266 :
267 : // Helper for sorting all of the elems in each subdomain
268 136 : auto sort_elems = [](std::map<SubdomainID, SubdomainInfo> & subdomains)
269 : {
270 448 : for (auto & pair : subdomains)
271 312 : std::sort(pair.second.elems.begin(), pair.second.elems.end());
272 136 : };
273 :
274 313 : const bool include_all = !_items.isValid();
275 :
276 1108 : if (include_all || _items.isValueSet("local_subdomains") ||
277 1638 : _items.isValueSet("local_subdomain_elems") || _items.isValueSet("subdomain_elems"))
278 : {
279 136 : _local_subdomains.clear();
280 136 : _local_subdomain_elems.clear();
281 136 : _subdomain_elems.clear();
282 :
283 : // Fill the local subdomain information; all cases need it
284 136 : std::map<SubdomainID, SubdomainInfo> subdomains;
285 3560 : for (const auto & elem : *_fe_problem.mesh().getActiveLocalElementRange())
286 : {
287 3424 : auto & entry = subdomains[elem->subdomain_id()];
288 3424 : entry.id = elem->subdomain_id();
289 3424 : 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 312 : if (include_all || _items.isValueSet("local_subdomains"))
294 : {
295 48 : _local_subdomains = subdomains;
296 96 : for (auto & pair : _local_subdomains)
297 48 : pair.second.elems.clear();
298 48 : add_subdomain_names(_local_subdomains);
299 : }
300 :
301 : // For local subdomain elems: copy over the local info, and add the names
302 312 : if (include_all || _items.isValueSet("local_subdomain_elems"))
303 : {
304 48 : _local_subdomain_elems = subdomains;
305 48 : sort_elems(_local_subdomain_elems);
306 48 : add_subdomain_names(_local_subdomain_elems);
307 : }
308 :
309 : // For the global subdomain elems, we need to communicate all of the elems
310 312 : if (include_all || _items.isValueSet("subdomain_elems"))
311 : {
312 : // Set up a structure for sending each (id, elem id) to root
313 136 : std::map<processor_id_type, std::vector<std::pair<subdomain_id_type, dof_id_type>>> send_info;
314 : // Avoid creating empty entry
315 136 : if (!subdomains.empty())
316 : {
317 136 : auto & root_info = send_info[0];
318 430 : for (const auto & pair : subdomains)
319 3718 : for (const auto elem_id : pair.second.elems)
320 3424 : 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 136 : [this](processor_id_type,
326 : const std::vector<std::pair<subdomain_id_type, dof_id_type>> & info)
327 : {
328 3560 : for (const auto & subdomain_elem_pair : info)
329 : {
330 3424 : auto & entry = _subdomain_elems[subdomain_elem_pair.first];
331 3424 : entry.id = subdomain_elem_pair.first;
332 3424 : entry.elems.emplace_back(subdomain_elem_pair.second);
333 : }
334 136 : };
335 :
336 : // Push the information and insert it into _subdomain_elems on root
337 136 : Parallel::push_parallel_vector_data(comm(), send_info, accumulate_info);
338 :
339 136 : if (processor_id() == 0)
340 : {
341 88 : sort_elems(_subdomain_elems);
342 88 : add_subdomain_names(_subdomain_elems);
343 : }
344 136 : }
345 136 : }
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 843 : if (include_all || _items.isValueSet("subdomains"))
350 : {
351 57 : _subdomains.clear();
352 :
353 57 : std::set<subdomain_id_type> subdomain_ids;
354 57 : _mesh.subdomain_ids(subdomain_ids);
355 :
356 57 : if (processor_id() == 0)
357 : {
358 69 : for (const auto id : subdomain_ids)
359 38 : _subdomains[id].id = id;
360 31 : add_subdomain_names(_subdomains);
361 : }
362 57 : }
363 313 : }
364 :
365 : void
366 398 : to_json(nlohmann::json & json, const std::map<SubdomainID, MeshInfo::SubdomainInfo> & subdomains)
367 : {
368 780 : for (const auto & pair : subdomains)
369 : {
370 382 : const MeshInfo::SubdomainInfo & subdomain_info = pair.second;
371 :
372 382 : nlohmann::json subdomain_json;
373 382 : subdomain_json["id"] = subdomain_info.id;
374 382 : if (subdomain_info.name.size())
375 254 : subdomain_json["name"] = subdomain_info.name;
376 382 : if (subdomain_info.elems.size())
377 : {
378 304 : auto & sides_json = subdomain_json["elems"];
379 5728 : for (const auto & id : subdomain_info.elems)
380 5424 : sides_json.push_back(id);
381 : }
382 :
383 382 : json.push_back(subdomain_json);
384 382 : }
385 398 : }
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 : }
|