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 14993 : MeshInfo::validParams()
20 : {
21 14993 : InputParameters params = GeneralReporter::validParams();
22 14993 : 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 14993 : "local_subdomain_elems subdomains subdomain_elems");
30 14993 : params.addParam<MultiMooseEnum>(
31 : "items",
32 : items,
33 : "The iteration information to output, if nothing is provided everything will be output.");
34 :
35 29986 : return params;
36 14993 : }
37 :
38 340 : MeshInfo::MeshInfo(const InputParameters & parameters)
39 : : GeneralReporter(parameters),
40 340 : _items(getParam<MultiMooseEnum>("items")),
41 340 : _num_dofs(declareHelper<unsigned int>("num_dofs", REPORTER_MODE_REPLICATED)),
42 340 : _num_dofs_nl(declareHelper<unsigned int>("num_dofs_nonlinear", REPORTER_MODE_REPLICATED)),
43 340 : _num_dofs_aux(declareHelper<unsigned int>("num_dofs_auxiliary", REPORTER_MODE_REPLICATED)),
44 340 : _num_dofs_constrained(
45 340 : declareHelper<unsigned int>("num_dofs_constrained", REPORTER_MODE_REPLICATED)),
46 340 : _num_elem(declareHelper<unsigned int>("num_elements", REPORTER_MODE_REPLICATED)),
47 340 : _num_node(declareHelper<unsigned int>("num_nodes", REPORTER_MODE_REPLICATED)),
48 340 : _num_local_dofs(declareHelper<unsigned int>("num_local_dofs", REPORTER_MODE_DISTRIBUTED)),
49 340 : _num_local_dofs_nl(
50 340 : declareHelper<unsigned int>("num_dofs_local_nonlinear", REPORTER_MODE_DISTRIBUTED)),
51 340 : _num_local_dofs_aux(
52 340 : declareHelper<unsigned int>("num_dofs_local_auxiliary", REPORTER_MODE_DISTRIBUTED)),
53 340 : _num_local_elem(declareHelper<unsigned int>("num_local_elements", REPORTER_MODE_DISTRIBUTED)),
54 340 : _num_local_node(declareHelper<unsigned int>("num_local_nodes", REPORTER_MODE_DISTRIBUTED)),
55 :
56 340 : _local_sidesets(declareHelper<std::map<BoundaryID, SidesetInfo>>("local_sidesets",
57 : REPORTER_MODE_DISTRIBUTED)),
58 340 : _local_sideset_elems(declareHelper<std::map<BoundaryID, SidesetInfo>>(
59 : "local_sideset_elems", REPORTER_MODE_DISTRIBUTED)),
60 340 : _sidesets(declareHelper<std::map<BoundaryID, SidesetInfo>>("sidesets", REPORTER_MODE_ROOT)),
61 340 : _sideset_elems(
62 340 : declareHelper<std::map<BoundaryID, SidesetInfo>>("sideset_elems", REPORTER_MODE_ROOT)),
63 :
64 340 : _local_subdomains(declareHelper<std::map<SubdomainID, SubdomainInfo>>(
65 : "local_subdomains", REPORTER_MODE_DISTRIBUTED)),
66 340 : _local_subdomain_elems(declareHelper<std::map<SubdomainID, SubdomainInfo>>(
67 : "local_subdomain_elems", REPORTER_MODE_DISTRIBUTED)),
68 340 : _subdomains(
69 340 : declareHelper<std::map<SubdomainID, SubdomainInfo>>("subdomains", REPORTER_MODE_ROOT)),
70 340 : _subdomain_elems(
71 340 : 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 578 : if (include_all || _items.isValueSet("local_sidesets") ||
122 578 : _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 5036 : 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 154 : 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 154 : 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 154 : 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 154 : auto & root_info = send_info[0];
165 622 : for (const auto & pair : sidesets)
166 2016 : for (const auto & side : pair.second.sides)
167 1548 : root_info.emplace_back(pair.second.id, side.first, side.second);
168 :
169 : // Take the received information and insert it into _sideset_elems
170 : auto accumulate_info =
171 154 : [this](processor_id_type,
172 1548 : const std::vector<std::tuple<boundary_id_type, dof_id_type, unsigned int>> & info)
173 : {
174 1702 : for (const auto & tuple : info)
175 : {
176 1548 : const auto id = std::get<0>(tuple);
177 1548 : auto & entry = _sideset_elems[id];
178 1548 : entry.id = id;
179 1548 : entry.sides.emplace_back(std::get<1>(tuple), std::get<2>(tuple));
180 : }
181 154 : };
182 :
183 : // Push the information and insert it into _sideset_elems on root
184 154 : Parallel::push_parallel_vector_data(comm(), send_info, accumulate_info);
185 :
186 154 : sort_sides(_sideset_elems);
187 154 : add_sideset_names(_sideset_elems);
188 154 : }
189 154 : }
190 :
191 : // For global sideset information without elements, we can simplify communication.
192 : // All we need are the boundary IDs from libMesh (may not be reduced, so take the union)
193 : // and then add the names (global)
194 313 : if (include_all || _items.isValueSet("sidesets"))
195 : {
196 57 : _sidesets.clear();
197 :
198 57 : auto boundary_ids = _mesh.get_boundary_info().get_boundary_ids();
199 57 : comm().set_union(boundary_ids, 0);
200 57 : if (processor_id() == 0)
201 : {
202 155 : for (const auto id : boundary_ids)
203 124 : _sidesets[id].id = id;
204 31 : add_sideset_names(_sidesets);
205 : }
206 57 : }
207 313 : }
208 :
209 : void
210 424 : to_json(nlohmann::json & json, const std::map<BoundaryID, MeshInfo::SidesetInfo> & sidesets)
211 : {
212 1156 : for (const auto & pair : sidesets)
213 : {
214 732 : const MeshInfo::SidesetInfo & sideset_info = pair.second;
215 :
216 732 : nlohmann::json sideset_json;
217 732 : sideset_json["id"] = sideset_info.id;
218 732 : if (sideset_info.name.size())
219 732 : sideset_json["name"] = sideset_info.name;
220 732 : if (sideset_info.sides.size())
221 : {
222 488 : auto & sides_json = sideset_json["sides"];
223 :
224 2836 : for (const std::pair<dof_id_type, unsigned int> & pair : sideset_info.sides)
225 : {
226 2348 : nlohmann::json side_json;
227 2348 : side_json["elem_id"] = pair.first;
228 2348 : side_json["side"] = pair.second;
229 2348 : sides_json.push_back(side_json);
230 2348 : }
231 : }
232 :
233 732 : json.push_back(sideset_json);
234 732 : }
235 424 : }
236 :
237 : void
238 95 : dataStore(std::ostream & stream, MeshInfo::SidesetInfo & sideset_info, void * context)
239 : {
240 95 : storeHelper(stream, sideset_info.id, context);
241 95 : storeHelper(stream, sideset_info.name, context);
242 95 : storeHelper(stream, sideset_info.sides, context);
243 95 : }
244 :
245 : void
246 95 : dataLoad(std::istream & stream, MeshInfo::SidesetInfo & sideset_info, void * context)
247 : {
248 95 : loadHelper(stream, sideset_info.id, context);
249 95 : loadHelper(stream, sideset_info.name, context);
250 95 : loadHelper(stream, sideset_info.sides, context);
251 95 : }
252 :
253 : void
254 313 : MeshInfo::possiblyAddSubdomainInfo()
255 : {
256 : // Helper for adding the subdomain names to a given map of subdomains
257 215 : auto add_subdomain_names = [&](std::map<SubdomainID, SubdomainInfo> & subdomains)
258 : {
259 613 : for (auto & pair : subdomains)
260 398 : pair.second.name = _mesh.subdomain_name(pair.second.id);
261 215 : };
262 :
263 : // Helper for sorting all of the elems in each subdomain
264 136 : auto sort_elems = [](std::map<SubdomainID, SubdomainInfo> & subdomains)
265 : {
266 448 : for (auto & pair : subdomains)
267 312 : std::sort(pair.second.elems.begin(), pair.second.elems.end());
268 136 : };
269 :
270 313 : const bool include_all = !_items.isValid();
271 :
272 578 : if (include_all || _items.isValueSet("local_subdomains") ||
273 578 : _items.isValueSet("local_subdomain_elems") || _items.isValueSet("subdomain_elems"))
274 : {
275 136 : _local_subdomains.clear();
276 136 : _local_subdomain_elems.clear();
277 136 : _subdomain_elems.clear();
278 :
279 : // Fill the local subdomain information; all cases need it
280 136 : std::map<SubdomainID, SubdomainInfo> subdomains;
281 3560 : for (const auto & elem : *_fe_problem.mesh().getActiveLocalElementRange())
282 : {
283 3424 : auto & entry = subdomains[elem->subdomain_id()];
284 3424 : entry.id = elem->subdomain_id();
285 3424 : entry.elems.push_back(elem->id());
286 : }
287 :
288 : // For local subdomains: copy over the local info, remove the elems, and add the names
289 136 : if (include_all || _items.isValueSet("local_subdomains"))
290 : {
291 48 : _local_subdomains = subdomains;
292 96 : for (auto & pair : _local_subdomains)
293 48 : pair.second.elems.clear();
294 48 : add_subdomain_names(_local_subdomains);
295 : }
296 :
297 : // For local subdomain elems: copy over the local info, and add the names
298 136 : if (include_all || _items.isValueSet("local_subdomain_elems"))
299 : {
300 48 : _local_subdomain_elems = subdomains;
301 48 : sort_elems(_local_subdomain_elems);
302 48 : add_subdomain_names(_local_subdomain_elems);
303 : }
304 :
305 : // For the global subdomain elems, we need to communicate all of the elems
306 136 : if (include_all || _items.isValueSet("subdomain_elems"))
307 : {
308 : // Set up a structure for sending each (id, elem id) to root
309 136 : std::map<processor_id_type, std::vector<std::pair<subdomain_id_type, dof_id_type>>> send_info;
310 136 : auto & root_info = send_info[0];
311 430 : for (const auto & pair : subdomains)
312 3718 : for (const auto elem_id : pair.second.elems)
313 3424 : root_info.emplace_back(pair.second.id, elem_id);
314 :
315 : // Take the received information and insert it into _subdomain_elems
316 : auto accumulate_info =
317 136 : [this](processor_id_type,
318 3424 : const std::vector<std::pair<subdomain_id_type, dof_id_type>> & info)
319 : {
320 3560 : for (const auto & subdomain_elem_pair : info)
321 : {
322 3424 : auto & entry = _subdomain_elems[subdomain_elem_pair.first];
323 3424 : entry.id = subdomain_elem_pair.first;
324 3424 : entry.elems.emplace_back(subdomain_elem_pair.second);
325 : }
326 136 : };
327 :
328 : // Push the information and insert it into _subdomain_elems on root
329 136 : Parallel::push_parallel_vector_data(comm(), send_info, accumulate_info);
330 :
331 136 : if (processor_id() == 0)
332 : {
333 88 : sort_elems(_subdomain_elems);
334 88 : add_subdomain_names(_subdomain_elems);
335 : }
336 136 : }
337 136 : }
338 :
339 : // For global subdomain information without elements, we can simplify communication.
340 : // All we need are the subdomain IDs from libMesh and then add the names (global)
341 313 : if (include_all || _items.isValueSet("subdomains"))
342 : {
343 57 : _subdomains.clear();
344 :
345 57 : std::set<subdomain_id_type> subdomain_ids;
346 57 : _mesh.subdomain_ids(subdomain_ids);
347 :
348 57 : if (processor_id() == 0)
349 : {
350 69 : for (const auto id : subdomain_ids)
351 38 : _subdomains[id].id = id;
352 31 : add_subdomain_names(_subdomains);
353 : }
354 57 : }
355 313 : }
356 :
357 : void
358 398 : to_json(nlohmann::json & json, const std::map<SubdomainID, MeshInfo::SubdomainInfo> & subdomains)
359 : {
360 780 : for (const auto & pair : subdomains)
361 : {
362 382 : const MeshInfo::SubdomainInfo & subdomain_info = pair.second;
363 :
364 382 : nlohmann::json subdomain_json;
365 382 : subdomain_json["id"] = subdomain_info.id;
366 382 : if (subdomain_info.name.size())
367 254 : subdomain_json["name"] = subdomain_info.name;
368 382 : if (subdomain_info.elems.size())
369 : {
370 304 : auto & sides_json = subdomain_json["elems"];
371 5728 : for (const auto & id : subdomain_info.elems)
372 5424 : sides_json.push_back(id);
373 : }
374 :
375 382 : json.push_back(subdomain_json);
376 382 : }
377 398 : }
378 :
379 : void
380 50 : dataStore(std::ostream & stream, MeshInfo::SubdomainInfo & subdomain_info, void * context)
381 : {
382 50 : storeHelper(stream, subdomain_info.id, context);
383 50 : storeHelper(stream, subdomain_info.name, context);
384 50 : storeHelper(stream, subdomain_info.elems, context);
385 50 : }
386 :
387 : void
388 50 : dataLoad(std::istream & stream, MeshInfo::SubdomainInfo & subdomain_info, void * context)
389 : {
390 50 : loadHelper(stream, subdomain_info.id, context);
391 50 : loadHelper(stream, subdomain_info.name, context);
392 50 : loadHelper(stream, subdomain_info.elems, context);
393 50 : }
|