Line data Source code
1 : // The libMesh Finite Element Library.
2 : // Copyright (C) 2002-2026 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 :
4 : // This library is free software; you can redistribute it and/or
5 : // modify it under the terms of the GNU Lesser General Public
6 : // License as published by the Free Software Foundation; either
7 : // version 2.1 of the License, or (at your option) any later version.
8 :
9 : // This library is distributed in the hope that it will be useful,
10 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : // Lesser General Public License for more details.
13 :
14 : // You should have received a copy of the GNU Lesser General Public
15 : // License along with this library; if not, write to the Free Software
16 : // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 :
18 : // Local includes
19 : #include "libmesh/checkpoint_io.h"
20 : #include "libmesh/boundary_info.h"
21 : #include "libmesh/cell_c0polyhedron.h"
22 : #include "libmesh/distributed_mesh.h"
23 : #include "libmesh/elem.h"
24 : #include "libmesh/enum_to_string.h"
25 : #include "libmesh/enum_xdr_mode.h"
26 : #include "libmesh/face_c0polygon.h"
27 : #include "libmesh/libmesh_logging.h"
28 : #include "libmesh/mesh_base.h"
29 : #include "libmesh/mesh_communication.h"
30 : #include "libmesh/mesh_tools.h"
31 : #include "libmesh/node.h"
32 : #include "libmesh/parallel.h"
33 : #include "libmesh/partitioner.h"
34 : #include "libmesh/periodic_boundaries.h"
35 : #include "libmesh/periodic_boundary.h"
36 : #include "libmesh/metis_partitioner.h"
37 : #include "libmesh/remote_elem.h"
38 : #include "libmesh/xdr_io.h"
39 : #include "libmesh/xdr_cxx.h"
40 : #include "libmesh/utility.h"
41 : #include "libmesh/int_range.h"
42 :
43 : // C++ includes
44 : #include <algorithm>
45 : #include <iostream>
46 : #include <iomanip>
47 : #include <cstdio>
48 : #include <vector>
49 : #include <string>
50 : #include <cstring>
51 : #include <filesystem>
52 : #include <fstream>
53 : #include <sstream> // for ostringstream
54 : #include <unordered_map>
55 : #include <unordered_set>
56 : #ifdef LIBMESH_HAVE_DIRECT_H
57 : #include <direct.h> // rmdir() on Windows
58 : #endif
59 : #ifdef LIBMESH_HAVE_UNISTD_H
60 : #include <unistd.h> // rmdir() on Unix
61 : #endif
62 :
63 : namespace
64 : {
65 : // chunking computes the number of chunks and first-chunk-offset when splitting a mesh
66 : // into nsplits pieces using size procs for the given MPI rank. The number of chunks and offset
67 : // are stored in nchunks and first_chunk respectively.
68 0 : void chunking(libMesh::processor_id_type size, libMesh::processor_id_type rank, libMesh::processor_id_type nsplits,
69 : libMesh::processor_id_type & nchunks, libMesh::processor_id_type & first_chunk)
70 : {
71 0 : if (nsplits % size == 0) // the chunks divide evenly over the processors
72 : {
73 0 : nchunks = nsplits / size;
74 0 : first_chunk = libMesh::cast_int<libMesh::processor_id_type>(nchunks * rank);
75 0 : return;
76 : }
77 :
78 0 : libMesh::processor_id_type nextra = nsplits % size;
79 0 : if (rank < nextra) // leftover chunks cause an extra chunk to be added to this processor
80 : {
81 0 : nchunks = libMesh::cast_int<libMesh::processor_id_type>(nsplits / size + 1);
82 0 : first_chunk = libMesh::cast_int<libMesh::processor_id_type>(nchunks * rank);
83 : }
84 : else // no extra chunks, but first chunk is offset by extras on earlier ranks
85 : {
86 0 : nchunks = nsplits / size;
87 : // account for the case where nchunks is zero where we want max int
88 0 : first_chunk = libMesh::cast_int<libMesh::processor_id_type>
89 0 : (std::max((int)((nchunks + 1) * (nsplits % size) + nchunks * (rank - nsplits % size)),
90 0 : (1 - (int)nchunks) * std::numeric_limits<int>::max()));
91 : }
92 : }
93 :
94 3344 : std::string_view extension(std::string_view s)
95 : {
96 3204 : auto pos = s.rfind(".");
97 3344 : if (pos == std::string::npos)
98 0 : return "";
99 3344 : return s.substr(pos, s.size() - pos);
100 : }
101 :
102 4340 : std::string split_dir(const std::string & input_name, libMesh::processor_id_type n_procs)
103 : {
104 8504 : return input_name + "/" + std::to_string(n_procs);
105 : }
106 :
107 :
108 2112 : std::string header_file(const std::string & input_name, libMesh::processor_id_type n_procs)
109 : {
110 8120 : return (split_dir(input_name, n_procs) + "/header").append(extension(input_name));
111 : }
112 :
113 : std::string
114 1232 : split_file(const std::string & input_name,
115 : libMesh::processor_id_type n_procs,
116 : libMesh::processor_id_type proc_id)
117 : {
118 4696 : return (split_dir(input_name, n_procs) + "/split-" + std::to_string(n_procs) + "-" +
119 4812 : std::to_string(proc_id)).append(extension(input_name));
120 : }
121 :
122 : // Report, as a comma-separated list, the processor counts for which a split
123 : // already exists as a numbered subdirectory of input_name, to help a user
124 : // pick out why the split they asked for could not be found.
125 12 : std::string existing_splits(const std::string & input_name)
126 : {
127 1 : std::error_code ec;
128 23 : if (!std::filesystem::is_directory(input_name, ec))
129 0 : return "";
130 :
131 3 : std::vector<std::string> splits;
132 59 : for (const auto & entry : std::filesystem::directory_iterator(input_name, ec))
133 : {
134 24 : const auto name = entry.path().filename().string();
135 24 : if (!name.empty() && name.find_first_not_of("0123456789") == std::string::npos &&
136 1 : entry.is_directory(ec))
137 12 : splits.push_back(name);
138 : }
139 12 : std::sort(splits.begin(), splits.end());
140 :
141 2 : std::string list;
142 24 : for (const auto & split : splits)
143 23 : list += (list.empty() ? "" : ", ") + split;
144 11 : return list;
145 10 : }
146 :
147 912 : void make_dir(const std::string & input_name, libMesh::processor_id_type n_procs)
148 : {
149 912 : auto ret = libMesh::Utility::mkdir(input_name.c_str());
150 : // error only if we failed to create dir - don't care if it was already there
151 912 : libmesh_error_msg_if
152 : (ret != 0 && ret != -1,
153 : "Failed to create mesh split directory '" << input_name << "': " << std::strerror(ret));
154 :
155 941 : auto dir_name = split_dir(input_name, n_procs);
156 912 : ret = libMesh::Utility::mkdir(dir_name.c_str());
157 912 : if (ret == -1)
158 : libmesh_warning("In CheckpointIO::write, directory '"
159 : << dir_name << "' already exists, overwriting contents.");
160 : else
161 118 : libmesh_error_msg_if
162 : (ret != 0, "Failed to create mesh split directory '" << dir_name << "': " << std::strerror(ret));
163 912 : }
164 :
165 : } // namespace
166 :
167 : namespace libMesh
168 : {
169 :
170 0 : std::unique_ptr<CheckpointIO> split_mesh(MeshBase & mesh, processor_id_type nsplits)
171 : {
172 : // There is currently an issue with DofObjects not being properly
173 : // reset if the mesh is not first repartitioned onto 1 processor
174 : // *before* being repartitioned onto the desired number of
175 : // processors. So, this is a workaround, but not a particularly
176 : // onerous one.
177 0 : mesh.partition(1);
178 0 : mesh.partition(nsplits);
179 :
180 0 : processor_id_type my_num_chunks = 0;
181 0 : processor_id_type my_first_chunk = 0;
182 0 : chunking(mesh.comm().size(), mesh.comm().rank(), nsplits, my_num_chunks, my_first_chunk);
183 :
184 0 : auto cpr = std::make_unique<CheckpointIO>(mesh);
185 0 : cpr->current_processor_ids().clear();
186 0 : for (processor_id_type i = my_first_chunk; i < my_first_chunk + my_num_chunks; i++)
187 0 : cpr->current_processor_ids().push_back(i);
188 0 : cpr->current_n_processors() = nsplits;
189 0 : cpr->parallel() = true;
190 0 : return cpr;
191 0 : }
192 :
193 :
194 : // ------------------------------------------------------------
195 : // CheckpointIO members
196 1682 : CheckpointIO::CheckpointIO (MeshBase & mesh, const bool binary_in) :
197 : MeshInput<MeshBase> (mesh,/* is_parallel_format = */ true),
198 : MeshOutput<MeshBase>(mesh,/* is_parallel_format = */ true),
199 : ParallelObject (mesh),
200 1574 : _binary (binary_in),
201 1574 : _parallel (false),
202 0 : _version ("checkpoint-1.7"),
203 1790 : _my_processor_ids (1, processor_id()),
204 3310 : _my_n_processors (mesh.is_replicated() ? 1 : n_processors())
205 : {
206 1682 : }
207 :
208 142 : CheckpointIO::CheckpointIO (const MeshBase & mesh, const bool binary_in) :
209 : MeshInput<MeshBase> (), // write-only
210 : MeshOutput<MeshBase>(mesh,/* is_parallel_format = */ true),
211 : ParallelObject (mesh),
212 134 : _binary (binary_in),
213 134 : _parallel (false),
214 0 : _version ("checkpoint-1.7"),
215 150 : _my_processor_ids (1, processor_id()),
216 280 : _my_n_processors (mesh.is_replicated() ? 1 : n_processors())
217 : {
218 142 : }
219 :
220 1708 : CheckpointIO::~CheckpointIO () = default;
221 :
222 912 : processor_id_type CheckpointIO::select_split_config(const std::string & input_name, header_id_type & data_size)
223 : {
224 58 : std::string header_name;
225 :
226 : // We'll read a header file from processor 0 and broadcast.
227 941 : if (this->processor_id() == 0)
228 : {
229 391 : header_name = header_file(input_name, _my_n_processors);
230 :
231 : {
232 : // look for header+splits with nprocs equal to _my_n_processors
233 238 : std::ifstream in (header_name.c_str());
234 204 : if (!in.good())
235 : {
236 : // otherwise fall back to a serial/single-split mesh
237 12 : auto orig_header_name = header_name;
238 33 : header_name = header_file(input_name, 1);
239 14 : std::ifstream in2 (header_name.c_str());
240 13 : const auto splits = existing_splits(input_name);
241 110 : libmesh_error_msg_if(!in2.good(),
242 : "ERROR: Neither one of the following files can be located:\n\t'"
243 : << orig_header_name << "' nor\n\t'" << input_name << "'\n"
244 : << "If you are running a parallel job, double check that you've "
245 : << "created a split for " << _my_n_processors << " ranks.\n"
246 : << "Note: One of paths above may refer to a valid directory on your "
247 : << "system, however we are attempting to read a valid header file.\n"
248 : << (splits.empty()
249 : ? "No processor-count splits were found in '" + input_name + "'."
250 : : "Splits that do exist in '" + input_name +
251 : "': " + splits + "."));
252 10 : }
253 170 : }
254 :
255 507 : Xdr io (header_name, this->binary() ? DECODE : READ);
256 :
257 : // read the version, but don't care about it
258 16 : std::string input_version;
259 192 : io.data(input_version);
260 :
261 : // read the data type
262 192 : io.data (data_size);
263 160 : }
264 :
265 900 : this->comm().broadcast(data_size);
266 900 : this->comm().broadcast(header_name);
267 :
268 : // How many per-processor files are here?
269 : largest_id_type input_n_procs;
270 :
271 900 : switch (data_size) {
272 0 : case 2:
273 0 : input_n_procs = this->read_header<uint16_t>(header_name);
274 0 : break;
275 0 : case 4:
276 0 : input_n_procs = this->read_header<uint32_t>(header_name);
277 0 : break;
278 900 : case 8:
279 900 : input_n_procs = this->read_header<uint64_t>(header_name);
280 28 : break;
281 0 : default:
282 0 : libmesh_error();
283 : }
284 :
285 56 : if (!input_n_procs)
286 12 : input_n_procs = 1;
287 928 : return cast_int<processor_id_type>(input_n_procs);
288 : }
289 :
290 84 : void CheckpointIO::cleanup(const std::string & input_name, processor_id_type n_procs)
291 : {
292 91 : auto header = header_file(input_name, n_procs);
293 84 : auto ret = std::remove(header.c_str());
294 : if (ret != 0)
295 : libmesh_warning("Failed to clean up checkpoint header '" << header << "': " << std::strerror(ret));
296 :
297 292 : for (processor_id_type i = 0; i < n_procs; i++)
298 : {
299 216 : auto split = split_file(input_name, n_procs, i);
300 208 : ret = std::remove(split.c_str());
301 : if (ret != 0)
302 : libmesh_warning("Failed to clean up checkpoint split file '" << split << "': " << std::strerror(ret));
303 : }
304 :
305 91 : auto dir = split_dir(input_name, n_procs);
306 84 : ret = rmdir(dir.c_str());
307 : if (ret != 0)
308 : libmesh_warning("Failed to clean up checkpoint split dir '" << dir << "': " << std::strerror(ret));
309 :
310 : // We expect that this may fail if there are other split configurations still present in this
311 : // directory - so don't bother to check/warn for failure.
312 84 : rmdir(input_name.c_str());
313 84 : }
314 :
315 :
316 2444 : bool CheckpointIO::version_at_least_1_5() const
317 : {
318 : return
319 4755 : (this->version().find("1.5") != std::string::npos) ||
320 4888 : (this->version().find("1.6") != std::string::npos) ||
321 2577 : (this->version().find("1.7") != std::string::npos);
322 : }
323 :
324 :
325 1024 : bool CheckpointIO::version_at_least_1_6() const
326 : {
327 : return
328 2048 : (this->version().find("1.6") != std::string::npos) ||
329 1074 : (this->version().find("1.7") != std::string::npos);
330 : }
331 :
332 :
333 1296 : bool CheckpointIO::version_at_least_1_7() const
334 : {
335 1296 : return (this->version().find("1.7") != std::string::npos);
336 : }
337 :
338 :
339 912 : void CheckpointIO::write (const std::string & name)
340 : {
341 87 : LOG_SCOPE("write()", "CheckpointIO");
342 :
343 : // convenient reference to our mesh
344 58 : const MeshBase & mesh = MeshOutput<MeshBase>::mesh();
345 :
346 : // FIXME: For backwards compatibility, we'll assume for now that we
347 : // only want to write distributed meshes in parallel. Later we can
348 : // do a gather_to_zero() and support that case too.
349 912 : _parallel = _parallel || !mesh.is_serial();
350 :
351 29 : processor_id_type use_n_procs = 1;
352 912 : if (_parallel)
353 868 : use_n_procs = _my_n_processors;
354 :
355 941 : std::string header_file_name = header_file(name, use_n_procs);
356 912 : make_dir(name, use_n_procs);
357 :
358 : // We'll write a header file from processor 0 to make it easier to do unambiguous
359 : // restarts later:
360 941 : if (this->processor_id() == 0)
361 : {
362 532 : Xdr io (header_file_name, this->binary() ? ENCODE : WRITE);
363 :
364 : // write the version
365 204 : io.data(_version, "# version");
366 :
367 : // write what kind of data type we're using
368 204 : header_id_type data_size = sizeof(largest_id_type);
369 204 : io.data(data_size, "# integer size");
370 :
371 : // Write out the max mesh dimension for backwards compatibility
372 : // with code that sets it independently of element dimensions
373 : {
374 204 : uint16_t mesh_dimension = cast_int<uint16_t>(mesh.mesh_dimension());
375 204 : io.data(mesh_dimension, "# dimensions");
376 : }
377 :
378 : // Write out whether or not this is serial output
379 : {
380 204 : uint16_t parallel = _parallel;
381 204 : io.data(parallel, "# parallel");
382 : }
383 :
384 : // If we're writing out a parallel mesh then we need to write the number of processors
385 : // so we can check it upon reading the file
386 204 : if (_parallel)
387 : {
388 172 : largest_id_type n_procs = _my_n_processors;
389 172 : io.data(n_procs, "# n_procs");
390 : }
391 :
392 : // write subdomain names
393 204 : this->write_subdomain_names(io);
394 :
395 : // write boundary id names
396 17 : const BoundaryInfo & boundary_info = mesh.get_boundary_info();
397 204 : write_bc_names(io, boundary_info, true); // sideset names
398 204 : write_bc_names(io, boundary_info, false); // nodeset names
399 :
400 : // write extra integer names
401 204 : const bool write_extra_integers = this->version_at_least_1_5();
402 :
403 204 : if (write_extra_integers)
404 : {
405 204 : largest_id_type n_node_integers = mesh.n_node_integers();
406 204 : io.data(n_node_integers, "# n_extra_integers per node");
407 :
408 51 : std::vector<std::string> node_integer_names;
409 312 : for (unsigned int i=0; i != n_node_integers; ++i)
410 108 : node_integer_names.push_back(mesh.get_node_integer_name(i));
411 :
412 204 : io.data(node_integer_names);
413 :
414 204 : largest_id_type n_elem_integers = mesh.n_elem_integers();
415 204 : io.data(n_elem_integers, "# n_extra_integers per elem");
416 :
417 34 : std::vector<std::string> elem_integer_names;
418 258 : for (unsigned int i=0; i != n_elem_integers; ++i)
419 54 : elem_integer_names.push_back(mesh.get_elem_integer_name(i));
420 :
421 204 : io.data(elem_integer_names);
422 170 : }
423 :
424 204 : if (this->version_at_least_1_7())
425 204 : this->write_disjoint_neighbor_boundary_pairs(io);
426 170 : }
427 :
428 : // If this is a serial mesh written to a serial file then we're only
429 : // going to write local data from processor 0. If this is a mesh being
430 : // written in parallel then we're going to write from every
431 : // processor.
432 58 : std::vector<processor_id_type> ids_to_write;
433 :
434 : // We're going to sort elements by pid in one pass, to avoid sending
435 : // predicated iterators through the whole mesh N_p times.
436 : //
437 : // The data type here needs to be a non-const-pointer to whatever
438 : // our element_iterator is a const-pointer to, for compatibility
439 : // later.
440 : typedef std::remove_const<MeshBase::const_element_iterator::value_type>::type nc_v_t;
441 58 : std::unordered_map<processor_id_type, std::vector<nc_v_t>> elements_on_pid;
442 :
443 912 : if (_parallel)
444 : {
445 868 : ids_to_write = _my_processor_ids;
446 1360 : for (processor_id_type p : ids_to_write)
447 18 : elements_on_pid[p].clear();
448 17 : auto eop_end = elements_on_pid.end();
449 13863 : for (auto & elem : mesh.element_ptr_range())
450 : {
451 6300 : const processor_id_type p = elem->processor_id();
452 6528 : if (auto eop_it = elements_on_pid.find(p);
453 228 : eop_it != eop_end)
454 1752 : eop_it->second.push_back(elem);
455 834 : }
456 : }
457 44 : else if (mesh.is_serial())
458 : {
459 56 : if (mesh.processor_id() == 0)
460 : {
461 : // placeholder
462 32 : ids_to_write.push_back(0);
463 : }
464 : }
465 : else
466 : {
467 0 : libmesh_error_msg("Cannot write serial checkpoint from distributed mesh");
468 : }
469 :
470 : // Call build_side_list() and build_node_list() just *once* to avoid
471 : // redundant expensive sorts during mesh splitting.
472 29 : const BoundaryInfo & boundary_info = mesh.get_boundary_info();
473 : std::vector<std::tuple<dof_id_type, unsigned short int, boundary_id_type>>
474 941 : bc_triples = boundary_info.build_side_list();
475 : std::vector<std::tuple<dof_id_type, boundary_id_type>>
476 941 : bc_tuples = boundary_info.build_node_list();
477 :
478 1436 : for (const auto & my_pid : ids_to_write)
479 : {
480 550 : auto file_name = split_file(name, use_n_procs, my_pid);
481 1346 : Xdr io (file_name, this->binary() ? ENCODE : WRITE);
482 :
483 52 : std::set<const Elem *, CompareElemIdsByLevel> elements;
484 :
485 : // For serial files or for already-distributed meshs, we write
486 : // everything we can see.
487 524 : if (!_parallel || !mesh.is_serial())
488 800 : elements.insert(mesh.elements_begin(), mesh.elements_end());
489 : // For parallel files written from serial meshes we write what
490 : // we'd be required to keep if we were to be deleting remote
491 : // elements. This allows us to write proper parallel files even
492 : // from a ReplicateMesh.
493 : //
494 : // WARNING: If we have a DistributedMesh which used
495 : // "add_extra_ghost_elem" rather than ghosting functors to
496 : // preserve elements and which is *also* currently serialized
497 : // then we're not preserving those elements here. As a quick
498 : // workaround user code should delete_remote_elements() before
499 : // writing the checkpoint; as a long term workaround user code
500 : // should use ghosting functors instead of extra_ghost_elem
501 : // lists.
502 : else
503 : {
504 348 : for (processor_id_type p : {my_pid, DofObject::invalid_processor_id})
505 : {
506 232 : if (const auto elements_vec_it = elements_on_pid.find(p);
507 20 : elements_vec_it != elements_on_pid.end())
508 : {
509 10 : auto & p_elements = elements_vec_it->second;
510 :
511 : // Be compatible with both deprecated and
512 : // corrected MeshBase iterator types
513 : typedef MeshBase::const_element_iterator::value_type v_t;
514 :
515 116 : v_t * elempp = p_elements.data();
516 116 : v_t * elemend = elempp + p_elements.size();
517 :
518 : const MeshBase::const_element_iterator
519 : pid_elements_begin = MeshBase::const_element_iterator
520 126 : (elempp, elemend, Predicates::NotNull<v_t *>()),
521 : pid_elements_end = MeshBase::const_element_iterator
522 126 : (elemend, elemend, Predicates::NotNull<v_t *>()),
523 : active_pid_elements_begin = MeshBase::const_element_iterator
524 126 : (elempp, elemend, Predicates::Active<v_t *>()),
525 : active_pid_elements_end = MeshBase::const_element_iterator
526 222 : (elemend, elemend, Predicates::Active<v_t *>());
527 :
528 : query_ghosting_functors
529 222 : (mesh, p, active_pid_elements_begin,
530 : active_pid_elements_end, elements);
531 222 : connect_children(mesh, pid_elements_begin,
532 : pid_elements_end, elements);
533 : }
534 : }
535 : }
536 :
537 52 : connected_node_set_type connected_nodes;
538 524 : connect_element_dependencies(mesh, elements, connected_nodes);
539 :
540 : // write the nodal locations
541 524 : this->write_nodes (io, connected_nodes);
542 :
543 : // write connectivity
544 524 : this->write_connectivity (io, elements);
545 :
546 : // write remote_elem connectivity
547 524 : this->write_remote_elem (io, elements);
548 :
549 : // write the boundary condition information
550 524 : this->write_bcs (io, elements, bc_triples);
551 :
552 : // write the nodeset information
553 524 : this->write_nodesets (io, connected_nodes, bc_tuples);
554 :
555 : // close it up
556 524 : io.close();
557 472 : }
558 :
559 : // this->comm().barrier();
560 912 : }
561 :
562 204 : void CheckpointIO::write_subdomain_names(Xdr & io) const
563 : {
564 : {
565 34 : const MeshBase & mesh = MeshOutput<MeshBase>::mesh();
566 :
567 17 : const std::map<subdomain_id_type, std::string> & subdomain_map = mesh.get_subdomain_name_map();
568 :
569 221 : std::vector<largest_id_type> subdomain_ids; subdomain_ids.reserve(subdomain_map.size());
570 238 : std::vector<std::string> subdomain_names; subdomain_names.reserve(subdomain_map.size());
571 :
572 : // We need to loop over the map and make sure that there aren't any invalid entries. Since we
573 : // return writable references in mesh_base, it's possible for the user to leave some entity names
574 : // blank. We can't write those to the XDA file.
575 204 : largest_id_type n_subdomain_names = 0;
576 204 : for (const auto & [id, name] : subdomain_map)
577 0 : if (!name.empty())
578 : {
579 0 : n_subdomain_names++;
580 0 : subdomain_ids.push_back(id);
581 0 : subdomain_names.push_back(name);
582 : }
583 :
584 204 : io.data(n_subdomain_names, "# subdomain id to name map");
585 : // Write out the ids and names in two vectors
586 204 : if (n_subdomain_names)
587 : {
588 0 : io.data(subdomain_ids);
589 0 : io.data(subdomain_names);
590 : }
591 170 : }
592 204 : }
593 :
594 :
595 :
596 524 : void CheckpointIO::write_nodes (Xdr & io,
597 : const connected_node_set_type & nodeset) const
598 : {
599 524 : largest_id_type n_nodes_here = nodeset.size();
600 :
601 524 : io.data(n_nodes_here, "# n_nodes on proc");
602 :
603 524 : const bool write_extra_integers = this->version_at_least_1_5();
604 : const unsigned int n_extra_integers =
605 524 : write_extra_integers ? MeshOutput<MeshBase>::mesh().n_node_integers() : 0;
606 :
607 : // Will hold the node id and pid and extra integers
608 550 : std::vector<largest_id_type> id_pid(2 + n_extra_integers);
609 :
610 : // For the coordinates
611 550 : std::vector<Real> coords(LIBMESH_DIM);
612 :
613 6814 : for (const auto & node : nodeset)
614 : {
615 6290 : id_pid[0] = node->id();
616 6290 : id_pid[1] = node->processor_id();
617 :
618 428 : libmesh_assert_equal_to(n_extra_integers, node->n_extra_integers());
619 13182 : for (unsigned int i=0; i != n_extra_integers; ++i)
620 7348 : id_pid[2+i] = node->get_extra_integer(i);
621 :
622 6290 : io.data_stream(id_pid.data(), 2 + n_extra_integers, 2 + n_extra_integers);
623 :
624 : #ifdef LIBMESH_ENABLE_UNIQUE_ID
625 6290 : largest_id_type unique_id = node->unique_id();
626 :
627 6290 : io.data(unique_id, "# unique id");
628 : #endif
629 :
630 6290 : coords[0] = (*node)(0);
631 :
632 : #if LIBMESH_DIM > 1
633 6290 : coords[1] = (*node)(1);
634 : #endif
635 :
636 : #if LIBMESH_DIM > 2
637 6290 : coords[2] = (*node)(2);
638 : #endif
639 :
640 6290 : io.data_stream(coords.data(), LIBMESH_DIM, 3);
641 : }
642 524 : }
643 :
644 :
645 :
646 524 : void CheckpointIO::write_connectivity (Xdr & io,
647 : const std::set<const Elem *, CompareElemIdsByLevel> & elements) const
648 : {
649 26 : libmesh_assert (io.writing());
650 :
651 524 : const bool write_extra_integers = this->version_at_least_1_5();
652 524 : const bool write_runtime_topology = this->version_at_least_1_6();
653 : const unsigned int n_extra_integers =
654 524 : write_extra_integers ? MeshOutput<MeshBase>::mesh().n_elem_integers() : 0;
655 :
656 : // Put these out here to reduce memory churn
657 : // id type pid subdomain_id parent_id extra_integer_0 ...
658 550 : std::vector<largest_id_type> elem_data(6 + n_extra_integers);
659 52 : std::vector<largest_id_type> conn_data;
660 52 : std::vector<largest_id_type> runtime_topology;
661 :
662 524 : largest_id_type n_elems_here = elements.size();
663 :
664 524 : io.data(n_elems_here, "# number of elements");
665 :
666 3488 : for (const auto & elem : elements)
667 : {
668 2964 : unsigned int n_nodes = elem->n_nodes();
669 :
670 2964 : elem_data[0] = elem->id();
671 2964 : elem_data[1] = elem->type();
672 2964 : elem_data[2] = elem->processor_id();
673 2964 : elem_data[3] = elem->subdomain_id();
674 :
675 : #ifdef LIBMESH_ENABLE_AMR
676 3185 : if (elem->parent() != nullptr)
677 : {
678 0 : elem_data[4] = elem->parent()->id();
679 0 : elem_data[5] = elem->parent()->which_child_am_i(elem);
680 : }
681 : else
682 : #endif
683 : {
684 2964 : elem_data[4] = static_cast<largest_id_type>(-1);
685 2964 : elem_data[5] = static_cast<largest_id_type>(-1);
686 : }
687 :
688 4059 : for (unsigned int i=0; i != n_extra_integers; ++i)
689 1173 : elem_data[6+i] = elem->get_extra_integer(i);
690 :
691 2964 : conn_data.resize(n_nodes);
692 :
693 16612 : for (unsigned int i=0; i<n_nodes; i++)
694 15510 : conn_data[i] = elem->node_id(i);
695 :
696 3185 : io.data_stream(elem_data.data(),
697 : cast_int<unsigned int>(elem_data.size()),
698 : cast_int<unsigned int>(elem_data.size()));
699 :
700 : #ifdef LIBMESH_ENABLE_UNIQUE_ID
701 2964 : largest_id_type unique_id = elem->unique_id();
702 :
703 2964 : io.data(unique_id, "# unique id");
704 : #endif
705 :
706 : #ifdef LIBMESH_ENABLE_AMR
707 3185 : uint16_t p_level = cast_int<uint16_t>(elem->p_level());
708 2964 : io.data(p_level, "# p_level");
709 :
710 2964 : uint16_t rflag = elem->refinement_flag();
711 2964 : io.data(rflag, "# rflag");
712 :
713 2964 : uint16_t pflag = elem->p_refinement_flag();
714 2964 : io.data(pflag, "# pflag");
715 : #endif
716 :
717 2964 : if (elem->runtime_topology())
718 : {
719 48 : libmesh_error_msg_if
720 : (!write_runtime_topology,
721 : "Checkpoint format 1.6 or newer is required to write " <<
722 : Utility::enum_to_string(elem->type()) << " elements.");
723 :
724 4 : runtime_topology.clear();
725 48 : runtime_topology.push_back(n_nodes);
726 48 : runtime_topology.push_back(elem->n_sides());
727 364 : for (auto s : elem->side_index_range())
728 : {
729 338 : const auto side_nodes = elem->nodes_on_side(s);
730 338 : runtime_topology.push_back(side_nodes.size());
731 1416 : for (const auto n : side_nodes)
732 1104 : runtime_topology.push_back(n);
733 : }
734 :
735 48 : io.data(runtime_topology, "# runtime topology");
736 : }
737 :
738 3185 : io.data_stream(conn_data.data(),
739 : cast_int<unsigned int>(conn_data.size()),
740 : cast_int<unsigned int>(conn_data.size()));
741 : }
742 524 : }
743 :
744 :
745 524 : void CheckpointIO::write_remote_elem (Xdr & io,
746 : const std::set<const Elem *, CompareElemIdsByLevel> & elements) const
747 : {
748 26 : libmesh_assert (io.writing());
749 :
750 : // Find the remote_elem neighbor and child links
751 52 : std::vector<largest_id_type> elem_ids, parent_ids;
752 52 : std::vector<uint16_t> elem_sides, child_numbers;
753 :
754 3488 : for (const auto & elem : elements)
755 : {
756 15769 : for (auto n : elem->side_index_range())
757 : {
758 12584 : const Elem * neigh = elem->neighbor_ptr(n);
759 13452 : if (neigh == remote_elem ||
760 868 : (neigh && !elements.count(neigh)))
761 : {
762 708 : elem_ids.push_back(elem->id());
763 708 : elem_sides.push_back(n);
764 : }
765 : }
766 :
767 : #ifdef LIBMESH_ENABLE_AMR
768 2964 : if (elem->has_children())
769 : {
770 0 : for (unsigned short c = 0,
771 0 : nc = cast_int<unsigned short>(elem->n_children());
772 0 : c != nc; ++c)
773 : {
774 0 : const Elem * child = elem->child_ptr(c);
775 0 : if (child == remote_elem ||
776 0 : (child && !elements.count(child)))
777 : {
778 0 : parent_ids.push_back(elem->id());
779 0 : child_numbers.push_back(c);
780 : }
781 : }
782 : }
783 : #endif
784 : }
785 :
786 524 : io.data(elem_ids, "# remote neighbor elem_ids");
787 524 : io.data(elem_sides, "# remote neighbor elem_sides");
788 524 : io.data(parent_ids, "# remote child parent_ids");
789 524 : io.data(child_numbers, "# remote child_numbers");
790 524 : }
791 :
792 :
793 :
794 524 : void CheckpointIO::write_bcs (Xdr & io,
795 : const std::set<const Elem *, CompareElemIdsByLevel> & elements,
796 : const std::vector<std::tuple<dof_id_type, unsigned short int, boundary_id_type>> & bc_triples) const
797 : {
798 26 : libmesh_assert (io.writing());
799 :
800 : // Build a list of (elem, side, bc) tuples.
801 52 : std::size_t bc_size = bc_triples.size();
802 :
803 52 : std::vector<largest_id_type> element_id_list;
804 52 : std::vector<uint16_t> side_list;
805 52 : std::vector<largest_id_type> bc_id_list;
806 :
807 524 : element_id_list.reserve(bc_size);
808 524 : side_list.reserve(bc_size);
809 524 : bc_id_list.reserve(bc_size);
810 :
811 26 : std::unordered_set<dof_id_type> elems;
812 3488 : for (auto & e : elements)
813 2964 : elems.insert(e->id());
814 :
815 4488 : for (const auto & t : bc_triples)
816 508 : if (elems.count(std::get<0>(t)))
817 : {
818 3436 : element_id_list.push_back(std::get<0>(t));
819 3436 : side_list.push_back(std::get<1>(t));
820 3436 : bc_id_list.push_back(std::get<2>(t));
821 : }
822 :
823 :
824 524 : io.data(element_id_list, "# element ids for bcs");
825 524 : io.data(side_list, "# sides of elements for bcs");
826 524 : io.data(bc_id_list, "# bc ids");
827 524 : }
828 :
829 :
830 :
831 524 : void CheckpointIO::write_nodesets (Xdr & io,
832 : const connected_node_set_type & nodeset,
833 : const std::vector<std::tuple<dof_id_type, boundary_id_type>> & bc_tuples) const
834 : {
835 26 : libmesh_assert (io.writing());
836 :
837 : // convenient reference to our mesh
838 52 : const MeshBase & mesh = MeshOutput<MeshBase>::mesh();
839 :
840 : // Build a list of (node, bc) tuples
841 52 : std::size_t nodeset_size = bc_tuples.size();
842 :
843 52 : std::vector<largest_id_type> node_id_list;
844 26 : std::vector<largest_id_type> bc_id_list;
845 :
846 524 : node_id_list.reserve(nodeset_size);
847 524 : bc_id_list.reserve(nodeset_size);
848 :
849 6712 : for (const auto & t : bc_tuples)
850 6244 : if (nodeset.count(mesh.node_ptr(std::get<0>(t))))
851 : {
852 5572 : node_id_list.push_back(std::get<0>(t));
853 5572 : bc_id_list.push_back(std::get<1>(t));
854 : }
855 :
856 524 : io.data(node_id_list, "# node id list");
857 524 : io.data(bc_id_list, "# nodeset bc id list");
858 524 : }
859 :
860 :
861 :
862 408 : void CheckpointIO::write_bc_names (Xdr & io, const BoundaryInfo & info, bool is_sideset) const
863 : {
864 408 : const std::map<boundary_id_type, std::string> & boundary_map = is_sideset ?
865 34 : info.get_sideset_name_map() : info.get_nodeset_name_map();
866 :
867 442 : std::vector<largest_id_type> boundary_ids; boundary_ids.reserve(boundary_map.size());
868 476 : std::vector<std::string> boundary_names; boundary_names.reserve(boundary_map.size());
869 :
870 : // We need to loop over the map and make sure that there aren't any invalid entries. Since we
871 : // return writable references in boundary_info, it's possible for the user to leave some entity names
872 : // blank. We can't write those to the XDA file.
873 408 : largest_id_type n_boundary_names = 0;
874 1464 : for (const auto & [id, name] : boundary_map)
875 1056 : if (!name.empty())
876 : {
877 1056 : n_boundary_names++;
878 1056 : boundary_ids.push_back(id);
879 1056 : boundary_names.push_back(name);
880 : }
881 :
882 408 : if (is_sideset)
883 204 : io.data(n_boundary_names, "# sideset id to name map");
884 : else
885 204 : io.data(n_boundary_names, "# nodeset id to name map");
886 : // Write out the ids and names in two vectors
887 408 : if (n_boundary_names)
888 : {
889 264 : io.data(boundary_ids);
890 264 : io.data(boundary_names);
891 : }
892 748 : }
893 :
894 204 : void CheckpointIO::write_disjoint_neighbor_boundary_pairs (Xdr & io) const
895 : {
896 34 : std::vector<largest_id_type> pair_ids;
897 34 : std::vector<Real> translations;
898 :
899 : #ifdef LIBMESH_ENABLE_PERIODIC
900 34 : const MeshBase & mesh = MeshOutput<MeshBase>::mesh();
901 :
902 : // MeshBase stores each pair once per direction; write each pair once,
903 : // from its smaller boundary id, and re-register both directions on read.
904 204 : if (const PeriodicBoundaries * db = mesh.get_disjoint_neighbor_boundary_pairs())
905 72 : for (const auto & [id, pb] : *db)
906 : {
907 52 : if (id > pb->pairedboundary)
908 24 : continue;
909 :
910 : // MeshBase only registers translations, which we can recover
911 : // as the image of the origin.
912 24 : libmesh_error_msg_if(!dynamic_cast<const PeriodicBoundary *>(pb.get()) ||
913 : pb->has_transformation_matrix(),
914 : "CheckpointIO can only write translational disjoint neighbor "
915 : "boundary pairs");
916 :
917 24 : pair_ids.push_back(id);
918 24 : pair_ids.push_back(pb->pairedboundary);
919 :
920 26 : const Point translation = pb->get_corresponding_pos(Point(0));
921 96 : for (auto d : make_range(LIBMESH_DIM))
922 78 : translations.push_back(translation(d));
923 : }
924 : #endif
925 :
926 221 : largest_id_type n_pairs = pair_ids.size() / 2;
927 204 : io.data(n_pairs, "# disjoint neighbor boundary pairs");
928 204 : if (n_pairs)
929 : {
930 24 : io.data(pair_ids);
931 : // data_stream, unlike the generic vector path, handles extended
932 : // precision Real
933 26 : io.data_stream(translations.data(),
934 : cast_int<unsigned int>(translations.size()),
935 : LIBMESH_DIM);
936 : }
937 204 : }
938 :
939 912 : void CheckpointIO::read (const std::string & input_name)
940 : {
941 87 : LOG_SCOPE("read()","CheckpointIO");
942 :
943 912 : MeshBase & mesh = MeshInput<MeshBase>::mesh();
944 :
945 29 : libmesh_assert(!mesh.n_elem());
946 :
947 : header_id_type data_size;
948 912 : processor_id_type input_n_procs = select_split_config(input_name, data_size);
949 928 : auto header_name = header_file(input_name, input_n_procs);
950 28 : bool input_parallel = input_n_procs > 0;
951 :
952 : // If this is a serial read then we're going to only read the mesh
953 : // on processor 0, then broadcast it
954 900 : if ((input_parallel && !mesh.is_replicated()) || mesh.processor_id() == 0)
955 : {
956 : // If we're trying to read a parallel checkpoint file on a
957 : // replicated mesh, we'll read every file on processor 0 so we
958 : // can broadcast it later. If we're on a distributed mesh then
959 : // we'll read every id to it's own processor and we'll "wrap
960 : // around" with any ids that exceed our processor count.
961 : const processor_id_type begin_proc_id =
962 652 : (input_parallel && !mesh.is_replicated()) ?
963 16 : mesh.processor_id() : 0;
964 : const processor_id_type stride =
965 652 : (input_parallel && !mesh.is_replicated()) ?
966 16 : mesh.n_processors() : 1;
967 :
968 1152 : for (processor_id_type proc_id = begin_proc_id; proc_id < input_n_procs;
969 500 : proc_id = cast_int<processor_id_type>(proc_id + stride))
970 : {
971 524 : auto file_name = split_file(input_name, input_n_procs, proc_id);
972 :
973 : {
974 548 : std::ifstream in (file_name.c_str());
975 :
976 500 : libmesh_error_msg_if(!in.good(), "ERROR: cannot locate specified file:\n\t" << file_name);
977 452 : }
978 :
979 : // Do we expect all our files' remote_elem entries to really
980 : // be remote? Only if we're not reading multiple input
981 : // files on the same processor.
982 : const bool expect_all_remote =
983 1016 : (input_n_procs <= mesh.n_processors() &&
984 492 : !mesh.is_replicated());
985 :
986 798 : Xdr io (file_name, this->binary() ? DECODE : READ);
987 :
988 500 : switch (data_size) {
989 0 : case 2:
990 0 : this->read_subfile<uint16_t>(io, expect_all_remote);
991 0 : break;
992 0 : case 4:
993 0 : this->read_subfile<uint32_t>(io, expect_all_remote);
994 0 : break;
995 500 : case 8:
996 500 : this->read_subfile<uint64_t>(io, expect_all_remote);
997 24 : break;
998 0 : default:
999 0 : libmesh_error();
1000 : }
1001 :
1002 500 : io.close();
1003 452 : }
1004 : }
1005 :
1006 : // If the mesh was only read on processor 0 then we need to broadcast it
1007 900 : if (mesh.is_replicated())
1008 328 : MeshCommunication().broadcast(mesh);
1009 : // If the mesh is really distributed then we need to make sure it
1010 : // knows that
1011 580 : else if (mesh.n_processors() > 1)
1012 528 : mesh.set_distributed();
1013 :
1014 : // If the mesh isn't getting even critical partitioning then we
1015 : // should update cached data from the partitioning we just read in
1016 900 : if (mesh.skip_partitioning())
1017 : {
1018 0 : mesh.recalculate_n_partitions();
1019 0 : mesh.update_post_partitioning();
1020 : }
1021 900 : }
1022 :
1023 :
1024 :
1025 : template <typename file_id_type>
1026 900 : file_id_type CheckpointIO::read_header (const std::string & name)
1027 : {
1028 900 : MeshBase & mesh = MeshInput<MeshBase>::mesh();
1029 :
1030 : // Hack for codes which don't look at all elem dimensions
1031 : uint16_t mesh_dimension;
1032 :
1033 : // Will this be a parallel input file? With how many processors? Stay tuned!
1034 : uint16_t input_parallel;
1035 : file_id_type input_n_procs;
1036 :
1037 56 : std::string input_version;
1038 84 : std::vector<std::string> node_integer_names, elem_integer_names;
1039 56 : std::vector<file_id_type> disjoint_pair_ids;
1040 28 : std::vector<Real> disjoint_translations;
1041 :
1042 : // We'll write a header file from processor 0 and broadcast.
1043 928 : if (this->processor_id() == 0)
1044 : {
1045 496 : Xdr io (name, this->binary() ? DECODE : READ);
1046 :
1047 : // read the version
1048 192 : io.data(input_version);
1049 :
1050 : // read the data type, don't care about it this time
1051 : header_id_type data_size;
1052 192 : io.data (data_size);
1053 :
1054 : // read the dimension
1055 192 : io.data (mesh_dimension);
1056 :
1057 : // Read whether or not this is a parallel file
1058 192 : io.data(input_parallel);
1059 :
1060 : // With how many processors?
1061 192 : if (input_parallel)
1062 160 : io.data(input_n_procs);
1063 :
1064 : // read subdomain names
1065 192 : this->read_subdomain_names<file_id_type>(io);
1066 :
1067 : // read boundary names
1068 16 : BoundaryInfo & boundary_info = mesh.get_boundary_info();
1069 :
1070 192 : this->read_bc_names<file_id_type>(io, boundary_info, true); // sideset names
1071 192 : this->read_bc_names<file_id_type>(io, boundary_info, false); // nodeset names
1072 :
1073 : // read extra integer names?
1074 16 : std::swap(input_version, this->version());
1075 192 : const bool read_extra_integers = this->version_at_least_1_5();
1076 16 : std::swap(input_version, this->version());
1077 :
1078 192 : if (read_extra_integers)
1079 32 : this->read_integers_names<file_id_type>
1080 160 : (io, node_integer_names, elem_integer_names);
1081 :
1082 16 : std::swap(input_version, this->version());
1083 192 : const bool read_disjoint_pairs = this->version_at_least_1_7();
1084 16 : std::swap(input_version, this->version());
1085 :
1086 192 : if (read_disjoint_pairs)
1087 32 : this->read_disjoint_neighbor_boundary_pairs<file_id_type>
1088 160 : (io, disjoint_pair_ids, disjoint_translations);
1089 160 : }
1090 :
1091 : // broadcast data from processor 0, set values everywhere
1092 900 : this->comm().broadcast(input_version);
1093 28 : this->version() = input_version;
1094 :
1095 900 : this->comm().broadcast(mesh_dimension);
1096 1772 : mesh.set_mesh_dimension(cast_int<unsigned char>(mesh_dimension));
1097 :
1098 900 : this->comm().broadcast(input_parallel);
1099 :
1100 900 : if (input_parallel)
1101 856 : this->comm().broadcast(input_n_procs);
1102 : else
1103 44 : input_n_procs = 1;
1104 :
1105 : std::map<subdomain_id_type, std::string> & subdomain_map =
1106 28 : mesh.set_subdomain_name_map();
1107 900 : this->comm().broadcast(subdomain_map);
1108 :
1109 28 : BoundaryInfo & boundary_info = mesh.get_boundary_info();
1110 900 : this->comm().broadcast(boundary_info.set_sideset_name_map());
1111 900 : this->comm().broadcast(boundary_info.set_nodeset_name_map());
1112 :
1113 900 : this->comm().broadcast(node_integer_names);
1114 900 : this->comm().broadcast(elem_integer_names);
1115 :
1116 1488 : for (auto & int_name : node_integer_names)
1117 1152 : mesh.add_node_integer(int_name);
1118 :
1119 1194 : for (auto & int_name : elem_integer_names)
1120 576 : mesh.add_elem_integer(int_name);
1121 :
1122 900 : this->comm().broadcast(disjoint_pair_ids);
1123 900 : this->comm().broadcast(disjoint_translations);
1124 :
1125 : #ifdef LIBMESH_ENABLE_PERIODIC
1126 : // The file is the complete record of the pairs, so it replaces any
1127 : // pairs left on a reused mesh rather than merging with them
1128 900 : if (this->version_at_least_1_7())
1129 900 : if (PeriodicBoundaries * db = mesh.get_disjoint_neighbor_boundary_pairs())
1130 4 : db->clear();
1131 :
1132 1070 : for (auto i : make_range(disjoint_pair_ids.size() / 2))
1133 : {
1134 4 : RealVectorValue translation;
1135 568 : for (auto d : make_range(LIBMESH_DIM))
1136 438 : translation(d) = disjoint_translations[LIBMESH_DIM * i + d];
1137 16 : mesh.add_disjoint_neighbor_boundary_pairs
1138 142 : (cast_int<boundary_id_type>(disjoint_pair_ids[2 * i]),
1139 146 : cast_int<boundary_id_type>(disjoint_pair_ids[2 * i + 1]),
1140 : translation);
1141 : }
1142 : #else
1143 : libmesh_error_msg_if(!disjoint_pair_ids.empty(),
1144 : "Reading disjoint neighbor boundary pairs requires --enable-periodic");
1145 : #endif
1146 :
1147 1800 : return input_parallel ? input_n_procs : 0;
1148 844 : }
1149 :
1150 :
1151 :
1152 : template <typename file_id_type>
1153 500 : void CheckpointIO::read_subfile (Xdr & io, bool expect_all_remote)
1154 : {
1155 : // read the nodal locations
1156 500 : this->read_nodes<file_id_type> (io);
1157 :
1158 : // read connectivity
1159 500 : this->read_connectivity<file_id_type> (io);
1160 :
1161 : // read remote_elem connectivity
1162 500 : this->read_remote_elem<file_id_type> (io, expect_all_remote);
1163 :
1164 : // read the boundary conditions
1165 500 : this->read_bcs<file_id_type> (io);
1166 :
1167 : // read the nodesets
1168 500 : this->read_nodesets<file_id_type> (io);
1169 500 : }
1170 :
1171 :
1172 :
1173 : template <typename file_id_type>
1174 192 : void CheckpointIO::read_subdomain_names(Xdr & io)
1175 : {
1176 192 : MeshBase & mesh = MeshInput<MeshBase>::mesh();
1177 :
1178 : std::map<subdomain_id_type, std::string> & subdomain_map =
1179 16 : mesh.set_subdomain_name_map();
1180 :
1181 32 : std::vector<file_id_type> subdomain_ids;
1182 192 : subdomain_ids.reserve(subdomain_map.size());
1183 :
1184 48 : std::vector<std::string> subdomain_names;
1185 192 : subdomain_names.reserve(subdomain_map.size());
1186 :
1187 192 : file_id_type n_subdomain_names = 0;
1188 192 : io.data(n_subdomain_names, "# subdomain id to name map");
1189 :
1190 192 : if (n_subdomain_names)
1191 : {
1192 0 : io.data(subdomain_ids);
1193 0 : io.data(subdomain_names);
1194 :
1195 0 : for (auto i : index_range(subdomain_ids))
1196 0 : subdomain_map[cast_int<subdomain_id_type>(subdomain_ids[i])] =
1197 0 : subdomain_names[i];
1198 : }
1199 352 : }
1200 :
1201 :
1202 :
1203 : template <typename file_id_type>
1204 500 : void CheckpointIO::read_nodes (Xdr & io)
1205 : {
1206 : // convenient reference to our mesh
1207 500 : MeshBase & mesh = MeshInput<MeshBase>::mesh();
1208 :
1209 : file_id_type n_nodes_here;
1210 500 : io.data(n_nodes_here, "# n_nodes on proc");
1211 :
1212 500 : const bool read_extra_integers = this->version_at_least_1_5();
1213 :
1214 48 : const unsigned int n_extra_integers =
1215 476 : read_extra_integers ? mesh.n_node_integers() : 0;
1216 :
1217 : // Will hold the node id and pid and extra integers
1218 524 : std::vector<file_id_type> id_pid(2 + n_extra_integers);
1219 :
1220 : // For the coordinates
1221 524 : std::vector<Real> coords(LIBMESH_DIM);
1222 :
1223 6574 : for (unsigned int i=0; i<n_nodes_here; i++)
1224 : {
1225 6074 : io.data_stream(id_pid.data(), 2 + n_extra_integers, 2 + n_extra_integers);
1226 :
1227 : #ifdef LIBMESH_ENABLE_UNIQUE_ID
1228 6074 : file_id_type unique_id = 0;
1229 6074 : io.data(unique_id, "# unique id");
1230 : #endif
1231 :
1232 6074 : io.data_stream(coords.data(), LIBMESH_DIM, LIBMESH_DIM);
1233 :
1234 410 : Point p;
1235 6074 : p(0) = coords[0];
1236 :
1237 : #if LIBMESH_DIM > 1
1238 6074 : p(1) = coords[1];
1239 : #endif
1240 :
1241 : #if LIBMESH_DIM > 2
1242 6074 : p(2) = coords[2];
1243 : #endif
1244 :
1245 6074 : const dof_id_type id = cast_int<dof_id_type>(id_pid[0]);
1246 :
1247 : // "Wrap around" if we see more processors than we're using.
1248 410 : processor_id_type pid =
1249 6484 : cast_int<processor_id_type>(id_pid[1] % mesh.n_processors());
1250 :
1251 : // If we already have this node (e.g. from another file, when
1252 : // reading multiple distributed CheckpointIO files into a
1253 : // ReplicatedMesh) then we don't want to add it again (because
1254 : // ReplicatedMesh can't handle that) but we do want to assert
1255 : // consistency between what we're reading and what we have.
1256 6074 : const Node * old_node = mesh.query_node_ptr(id);
1257 :
1258 6074 : if (old_node)
1259 : {
1260 60 : libmesh_assert_equal_to(pid, old_node->processor_id());
1261 :
1262 60 : libmesh_assert_equal_to(n_extra_integers, old_node->n_extra_integers());
1263 : #ifndef NDEBUG
1264 60 : for (unsigned int ei=0; ei != n_extra_integers; ++ei)
1265 : {
1266 0 : const dof_id_type extra_int = cast_int<dof_id_type>(id_pid[2+ei]);
1267 0 : libmesh_assert_equal_to(extra_int, old_node->get_extra_integer(ei));
1268 : }
1269 : #endif
1270 :
1271 : #ifdef LIBMESH_ENABLE_UNIQUE_ID
1272 60 : libmesh_assert_equal_to(unique_id, old_node->unique_id());
1273 : #endif
1274 : }
1275 : else
1276 : {
1277 : Node * node =
1278 5408 : mesh.add_point(p, id, pid);
1279 :
1280 : #ifdef LIBMESH_ENABLE_UNIQUE_ID
1281 5408 : node->set_unique_id(unique_id);
1282 : #endif
1283 :
1284 350 : libmesh_assert_equal_to(n_extra_integers, node->n_extra_integers());
1285 :
1286 12300 : for (unsigned int ei=0; ei != n_extra_integers; ++ei)
1287 : {
1288 7120 : const dof_id_type extra_int = cast_int<dof_id_type>(id_pid[2+ei]);
1289 6892 : node->set_extra_integer(ei, extra_int);
1290 : }
1291 : }
1292 : }
1293 500 : }
1294 :
1295 :
1296 :
1297 : template <typename file_id_type>
1298 500 : void CheckpointIO::read_connectivity (Xdr & io)
1299 : {
1300 : // convenient reference to our mesh
1301 500 : MeshBase & mesh = MeshInput<MeshBase>::mesh();
1302 :
1303 500 : const bool read_extra_integers = this->version_at_least_1_5();
1304 500 : const bool read_runtime_topology = this->version_at_least_1_6();
1305 :
1306 48 : const unsigned int n_extra_integers =
1307 476 : read_extra_integers ? mesh.n_elem_integers() : 0;
1308 :
1309 : file_id_type n_elems_here;
1310 500 : io.data(n_elems_here);
1311 :
1312 : // Keep track of the highest dimensional element we've added to the mesh
1313 500 : unsigned int highest_elem_dim = mesh.mesh_dimension();
1314 :
1315 : // RHS: Originally we used invalid_processor_id as a "no parent" tag
1316 : // number, because I'm an idiot. Let's try to support broken files
1317 : // as much as possible.
1318 24 : bool file_is_broken = false;
1319 :
1320 5810 : for (unsigned int i=0; i<n_elems_here; i++)
1321 : {
1322 : // id type pid subdomain_id parent_id
1323 3081 : std::vector<file_id_type> elem_data(6 + n_extra_integers);
1324 639 : io.data_stream
1325 2442 : (elem_data.data(), cast_int<unsigned int>(elem_data.size()),
1326 : cast_int<unsigned int>(elem_data.size()));
1327 :
1328 : #ifdef LIBMESH_ENABLE_UNIQUE_ID
1329 2868 : file_id_type unique_id = 0;
1330 2868 : io.data(unique_id, "# unique id");
1331 : #endif
1332 :
1333 : #ifdef LIBMESH_ENABLE_AMR
1334 2868 : uint16_t p_level = 0;
1335 2868 : io.data(p_level, "# p_level");
1336 :
1337 : uint16_t rflag, pflag;
1338 2868 : io.data(rflag, "# rflag");
1339 2868 : io.data(pflag, "# pflag");
1340 : #endif
1341 :
1342 2868 : const ElemType elem_type =
1343 2868 : static_cast<ElemType>(elem_data[1]);
1344 213 : const bool is_c0polygon = (elem_type == C0POLYGON);
1345 213 : const bool is_c0polyhedron = (elem_type == C0POLYHEDRON);
1346 :
1347 2868 : unsigned int n_nodes = Elem::type_to_n_nodes_map[elem_data[1]];
1348 : // Runtime-topology types have no fixed node count in this map.
1349 213 : const bool has_runtime_topology = (n_nodes == invalid_uint);
1350 639 : std::vector<std::vector<unsigned int>> nodes_on_sides;
1351 :
1352 2868 : if (has_runtime_topology)
1353 : {
1354 48 : libmesh_error_msg_if
1355 : (!read_runtime_topology,
1356 : "Checkpoint format 1.6 or newer is required to read " <<
1357 : Utility::enum_to_string(elem_type) << " elements.");
1358 :
1359 8 : std::vector<file_id_type> runtime_topology;
1360 48 : io.data(runtime_topology, "# runtime topology");
1361 52 : libmesh_error_msg_if(runtime_topology.size() < 2,
1362 : "Invalid runtime element topology.");
1363 :
1364 4 : std::size_t topology_index = 0;
1365 44 : n_nodes =
1366 48 : cast_int<unsigned int>(runtime_topology[topology_index++]);
1367 : const unsigned int n_sides =
1368 48 : cast_int<unsigned int>(runtime_topology[topology_index++]);
1369 48 : nodes_on_sides.resize(n_sides);
1370 :
1371 360 : for (auto s : index_range(nodes_on_sides))
1372 : {
1373 338 : libmesh_error_msg_if
1374 : (topology_index == runtime_topology.size(),
1375 : "Incomplete runtime element checkpoint topology.");
1376 :
1377 : const unsigned int n_side_nodes =
1378 312 : cast_int<unsigned int>(runtime_topology[topology_index++]);
1379 312 : libmesh_error_msg_if
1380 : (n_side_nodes > runtime_topology.size() - topology_index,
1381 : "Invalid runtime element side checkpoint topology.");
1382 :
1383 52 : auto & side_nodes = nodes_on_sides[s];
1384 312 : side_nodes.resize(n_side_nodes);
1385 1416 : for (auto n : index_range(side_nodes))
1386 : {
1387 : const unsigned int local_node =
1388 1196 : cast_int<unsigned int>(runtime_topology[topology_index++]);
1389 1104 : libmesh_error_msg_if
1390 : (local_node >= n_nodes,
1391 : "Runtime element side checkpoint topology references "
1392 : "an invalid local node.");
1393 1104 : side_nodes[n] = local_node;
1394 : }
1395 : }
1396 :
1397 52 : libmesh_error_msg_if
1398 : (topology_index != runtime_topology.size(),
1399 : "Extra data in runtime element checkpoint topology.");
1400 :
1401 48 : if (is_c0polygon)
1402 : {
1403 24 : libmesh_error_msg_if
1404 : (n_nodes < 3 || n_sides != n_nodes,
1405 : "Invalid C0POLYGON checkpoint topology.");
1406 144 : for (const auto & side_nodes : nodes_on_sides)
1407 130 : libmesh_error_msg_if
1408 : (side_nodes.size() != 2,
1409 : "Invalid C0POLYGON side checkpoint topology.");
1410 : }
1411 24 : else if (is_c0polyhedron)
1412 : {
1413 24 : libmesh_error_msg_if(n_sides < 4,
1414 : "Invalid C0POLYHEDRON checkpoint topology.");
1415 216 : for (const auto & side_nodes : nodes_on_sides)
1416 208 : libmesh_error_msg_if
1417 : (side_nodes.size() < 3,
1418 : "Invalid C0POLYHEDRON side checkpoint topology.");
1419 : }
1420 : }
1421 :
1422 : // Snag the node ids this element was connected to
1423 3081 : std::vector<file_id_type> conn_data(n_nodes);
1424 639 : io.data_stream
1425 2442 : (conn_data.data(), cast_int<unsigned int>(conn_data.size()),
1426 : cast_int<unsigned int>(conn_data.size()));
1427 :
1428 : const dof_id_type id =
1429 2868 : cast_int<dof_id_type> (elem_data[0]);
1430 213 : const processor_id_type proc_id =
1431 : cast_int<processor_id_type>
1432 3081 : (elem_data[2] % mesh.n_processors());
1433 213 : const subdomain_id_type subdomain_id =
1434 2868 : restrict_int<subdomain_id_type>(elem_data[3]);
1435 :
1436 : // Old broken files used processsor_id_type(-1)...
1437 : // But we *know* our first element will be level 0
1438 2868 : if (i == 0 && elem_data[4] == 65535)
1439 0 : file_is_broken = true;
1440 :
1441 : // On a broken file we can't tell whether a parent of 65535 is a
1442 : // null parent or an actual parent of 65535. Assuming the
1443 : // former will cause less breakage.
1444 2655 : Elem * parent =
1445 213 : (elem_data[4] == static_cast<largest_id_type>(-1) ||
1446 2868 : (file_is_broken && elem_data[4] == 65535)) ?
1447 0 : nullptr : mesh.elem_ptr(cast_int<dof_id_type>(elem_data[4]));
1448 :
1449 213 : const unsigned short int child_num =
1450 213 : (elem_data[5] == static_cast<largest_id_type>(-1) ||
1451 2868 : (file_is_broken && elem_data[5] == 65535)) ?
1452 : static_cast<unsigned short>(-1) :
1453 0 : cast_int<unsigned short>(elem_data[5]);
1454 :
1455 213 : if (!parent)
1456 213 : libmesh_assert_equal_to
1457 : (child_num, static_cast<unsigned short>(-1));
1458 :
1459 2868 : Elem * old_elem = mesh.query_elem_ptr(id);
1460 :
1461 : // If we already have this element (e.g. from another file,
1462 : // when reading multiple distributed CheckpointIO files into
1463 : // a ReplicatedMesh) then we don't want to add it again
1464 : // (because ReplicatedMesh can't handle that) but we do want
1465 : // to assert consistency between what we're reading and what
1466 : // we have.
1467 2868 : if (old_elem)
1468 : {
1469 32 : libmesh_assert_equal_to(elem_type, old_elem->type());
1470 32 : libmesh_assert_equal_to(proc_id, old_elem->processor_id());
1471 32 : libmesh_assert_equal_to(subdomain_id, old_elem->subdomain_id());
1472 32 : if (parent)
1473 0 : libmesh_assert_equal_to(parent, old_elem->parent());
1474 : else
1475 32 : libmesh_assert(!old_elem->parent());
1476 :
1477 32 : libmesh_assert_equal_to(n_extra_integers, old_elem->n_extra_integers());
1478 : #ifndef NDEBUG
1479 32 : for (unsigned int ei=0; ei != n_extra_integers; ++ei)
1480 : {
1481 0 : const dof_id_type extra_int = cast_int<dof_id_type>(elem_data[6+ei]);
1482 0 : libmesh_assert_equal_to(extra_int, old_elem->get_extra_integer(ei));
1483 : }
1484 : #endif
1485 :
1486 32 : libmesh_assert_equal_to(old_elem->n_nodes(), conn_data.size());
1487 :
1488 128 : for (unsigned int n=0,
1489 32 : n_conn = cast_int<unsigned int>(conn_data.size());
1490 160 : n != n_conn; n++)
1491 128 : libmesh_assert_equal_to
1492 : (old_elem->node_id(n),
1493 : cast_int<dof_id_type>(conn_data[n]));
1494 :
1495 32 : if (has_runtime_topology)
1496 : {
1497 0 : libmesh_assert_equal_to(old_elem->n_sides(),
1498 : nodes_on_sides.size());
1499 : #ifndef NDEBUG
1500 0 : for (auto s : index_range(nodes_on_sides))
1501 0 : libmesh_assert(old_elem->nodes_on_side(s) ==
1502 : nodes_on_sides[s]);
1503 : #endif
1504 : }
1505 : }
1506 : else
1507 : {
1508 : // Create the element
1509 2331 : std::unique_ptr<Elem> elem;
1510 2512 : std::unique_ptr<Node> generated_mid_elem_node;
1511 :
1512 2512 : if (is_c0polygon)
1513 26 : elem = std::make_unique<C0Polygon>(n_nodes, parent);
1514 2488 : else if (is_c0polyhedron)
1515 : {
1516 30 : std::vector<std::shared_ptr<Polygon>> sides(nodes_on_sides.size());
1517 216 : for (auto s : index_range(nodes_on_sides))
1518 : {
1519 32 : const auto & side_node_indices = nodes_on_sides[s];
1520 16 : auto side =
1521 : std::make_shared<C0Polygon>
1522 208 : (cast_int<unsigned int>(side_node_indices.size()));
1523 1056 : for (auto n : index_range(side_node_indices))
1524 288 : side->set_node
1525 720 : (n, mesh.node_ptr(cast_int<dof_id_type>
1526 936 : (conn_data[side_node_indices[n]])));
1527 32 : sides[s] = std::move(side);
1528 : }
1529 :
1530 24 : elem = std::make_unique<C0Polyhedron>
1531 : (sides, generated_mid_elem_node, parent);
1532 :
1533 24 : libmesh_error_msg_if
1534 : (elem->n_nodes() != conn_data.size(),
1535 : "C0POLYHEDRON checkpoint topology is incompatible with "
1536 : "this libMesh configuration.");
1537 :
1538 312 : for (auto n : make_range(elem->n_vertices()))
1539 360 : libmesh_error_msg_if
1540 : (elem->node_id(n) !=
1541 : cast_int<dof_id_type>(conn_data[n]),
1542 : "C0POLYHEDRON checkpoint topology has inconsistent "
1543 : "local node ordering.");
1544 20 : }
1545 : else
1546 4751 : elem = Elem::build(elem_type, parent);
1547 :
1548 2512 : if (has_runtime_topology)
1549 : {
1550 48 : libmesh_error_msg_if
1551 : (!elem->runtime_topology() ||
1552 : elem->n_nodes() != conn_data.size() ||
1553 : elem->n_sides() != nodes_on_sides.size(),
1554 : Utility::enum_to_string(elem_type) <<
1555 : " checkpoint topology is incompatible with this "
1556 : "libMesh configuration.");
1557 :
1558 360 : for (auto s : index_range(nodes_on_sides))
1559 624 : libmesh_error_msg_if
1560 : (elem->nodes_on_side(s) != nodes_on_sides[s],
1561 : Utility::enum_to_string(elem_type) <<
1562 : " checkpoint topology has inconsistent side ordering.");
1563 : }
1564 :
1565 : #ifdef LIBMESH_ENABLE_UNIQUE_ID
1566 2512 : elem->set_unique_id(unique_id);
1567 : #endif
1568 :
1569 2512 : if (elem->dim() > highest_elem_dim)
1570 0 : highest_elem_dim = elem->dim();
1571 :
1572 2512 : elem->set_id() = id;
1573 2512 : elem->processor_id() = proc_id;
1574 2512 : elem->subdomain_id() = subdomain_id;
1575 :
1576 : #ifdef LIBMESH_ENABLE_AMR
1577 2512 : elem->hack_p_level(p_level);
1578 :
1579 2512 : elem->set_refinement_flag (cast_int<Elem::RefinementState>(rflag));
1580 2512 : elem->set_p_refinement_flag(cast_int<Elem::RefinementState>(pflag));
1581 :
1582 : // Set parent connections
1583 2512 : if (parent)
1584 : {
1585 : // We must specify a child_num, because we will have
1586 : // skipped adding any preceding remote_elem children
1587 0 : parent->add_child(elem.get(), child_num);
1588 : }
1589 : #else
1590 : libmesh_ignore(child_num);
1591 : #endif
1592 :
1593 181 : libmesh_assert(elem->n_nodes() == conn_data.size());
1594 :
1595 : // Connect all the nodes to this element
1596 11840 : for (unsigned int n=0,
1597 362 : n_conn = cast_int<unsigned int>(conn_data.size());
1598 14352 : n != n_conn; n++)
1599 13382 : elem->set_node(n,
1600 11840 : mesh.node_ptr(cast_int<dof_id_type>(conn_data[n])));
1601 :
1602 2874 : Elem * added_elem = mesh.add_elem(std::move(elem));
1603 :
1604 181 : libmesh_assert_equal_to(n_extra_integers, added_elem->n_extra_integers());
1605 3607 : for (unsigned int ei=0; ei != n_extra_integers; ++ei)
1606 : {
1607 1134 : const dof_id_type extra_int = cast_int<dof_id_type>(elem_data[6+ei]);
1608 1095 : added_elem->set_extra_integer(ei, extra_int);
1609 : }
1610 2150 : }
1611 : }
1612 :
1613 500 : mesh.set_mesh_dimension(cast_int<unsigned char>(highest_elem_dim));
1614 500 : }
1615 :
1616 :
1617 : template <typename file_id_type>
1618 500 : void CheckpointIO::read_remote_elem (Xdr & io, bool libmesh_dbg_var(expect_all_remote))
1619 : {
1620 : // convenient reference to our mesh
1621 500 : MeshBase & mesh = MeshInput<MeshBase>::mesh();
1622 :
1623 : // Find the remote_elem neighbor links
1624 48 : std::vector<file_id_type> elem_ids;
1625 48 : std::vector<uint16_t> elem_sides;
1626 :
1627 500 : io.data(elem_ids, "# remote neighbor elem_ids");
1628 500 : io.data(elem_sides, "# remote neighbor elem_sides");
1629 :
1630 24 : libmesh_assert_equal_to(elem_ids.size(), elem_sides.size());
1631 :
1632 1208 : for (auto i : index_range(elem_ids))
1633 : {
1634 836 : Elem & elem = mesh.elem_ref(cast_int<dof_id_type>(elem_ids[i]));
1635 836 : if (!elem.neighbor_ptr(elem_sides[i]))
1636 708 : elem.set_neighbor(elem_sides[i],
1637 : const_cast<RemoteElem *>(remote_elem));
1638 : else
1639 0 : libmesh_assert(!expect_all_remote);
1640 : }
1641 :
1642 : // Find the remote_elem children links
1643 48 : std::vector<file_id_type> parent_ids;
1644 48 : std::vector<uint16_t> child_numbers;
1645 :
1646 500 : io.data(parent_ids, "# remote child parent_ids");
1647 500 : io.data(child_numbers, "# remote child_numbers");
1648 :
1649 : #ifdef LIBMESH_ENABLE_AMR
1650 500 : for (auto i : index_range(parent_ids))
1651 : {
1652 0 : Elem & elem = mesh.elem_ref(cast_int<dof_id_type>(parent_ids[i]));
1653 :
1654 : // We'd like to assert that no child pointer already exists to
1655 : // be overwritten by remote_elem, but Elem doesn't actually have
1656 : // an API that will return a child pointer without asserting
1657 : // that it isn't nullptr.
1658 0 : const Elem * child = elem.raw_child_ptr(child_numbers[i]);
1659 :
1660 0 : if (!child)
1661 0 : elem.add_child(const_cast<RemoteElem *>(remote_elem),
1662 0 : child_numbers[i]);
1663 : else
1664 0 : libmesh_assert(!expect_all_remote);
1665 : }
1666 : #endif
1667 500 : }
1668 :
1669 :
1670 :
1671 : template <typename file_id_type>
1672 500 : void CheckpointIO::read_bcs (Xdr & io)
1673 : {
1674 : // convenient reference to our mesh
1675 500 : MeshBase & mesh = MeshInput<MeshBase>::mesh();
1676 :
1677 : // and our boundary info object
1678 24 : BoundaryInfo & boundary_info = mesh.get_boundary_info();
1679 :
1680 48 : std::vector<file_id_type> element_id_list;
1681 48 : std::vector<uint16_t> side_list;
1682 48 : std::vector<file_id_type> bc_id_list;
1683 :
1684 500 : io.data(element_id_list, "# element ids for bcs");
1685 500 : io.data(side_list, "# sides of elements for bcs");
1686 500 : io.data(bc_id_list, "# bc ids");
1687 :
1688 3744 : for (auto i : index_range(element_id_list))
1689 570 : boundary_info.add_side
1690 3434 : (cast_int<dof_id_type>(element_id_list[i]), side_list[i],
1691 3434 : cast_int<boundary_id_type>(bc_id_list[i]));
1692 500 : }
1693 :
1694 :
1695 :
1696 : template <typename file_id_type>
1697 500 : void CheckpointIO::read_nodesets (Xdr & io)
1698 : {
1699 : // convenient reference to our mesh
1700 500 : MeshBase & mesh = MeshInput<MeshBase>::mesh();
1701 :
1702 : // and our boundary info object
1703 24 : BoundaryInfo & boundary_info = mesh.get_boundary_info();
1704 :
1705 48 : std::vector<file_id_type> node_id_list;
1706 48 : std::vector<file_id_type> bc_id_list;
1707 :
1708 500 : io.data(node_id_list, "# node id list");
1709 500 : io.data(bc_id_list, "# nodeset bc id list");
1710 :
1711 5784 : for (auto i : index_range(node_id_list))
1712 792 : boundary_info.add_node
1713 5284 : (cast_int<dof_id_type>(node_id_list[i]),
1714 5548 : cast_int<boundary_id_type>(bc_id_list[i]));
1715 500 : }
1716 :
1717 :
1718 :
1719 : template <typename file_id_type>
1720 384 : void CheckpointIO::read_bc_names(Xdr & io, BoundaryInfo & info, bool is_sideset)
1721 : {
1722 384 : std::map<boundary_id_type, std::string> & boundary_map = is_sideset ?
1723 32 : info.set_sideset_name_map() : info.set_nodeset_name_map();
1724 :
1725 64 : std::vector<file_id_type> boundary_ids;
1726 96 : std::vector<std::string> boundary_names;
1727 :
1728 384 : file_id_type n_boundary_names = 0;
1729 :
1730 384 : if (is_sideset)
1731 192 : io.data(n_boundary_names, "# sideset id to name map");
1732 : else
1733 192 : io.data(n_boundary_names, "# nodeset id to name map");
1734 :
1735 384 : if (n_boundary_names)
1736 : {
1737 240 : io.data(boundary_ids);
1738 240 : io.data(boundary_names);
1739 : }
1740 :
1741 : // Add them back into the map
1742 1344 : for (auto i : index_range(boundary_ids))
1743 2000 : boundary_map[cast_int<boundary_id_type>(boundary_ids[i])] =
1744 160 : boundary_names[i];
1745 704 : }
1746 :
1747 :
1748 : template <typename file_id_type>
1749 192 : void CheckpointIO::read_disjoint_neighbor_boundary_pairs
1750 : (Xdr & io,
1751 : std::vector<file_id_type> & pair_ids,
1752 : std::vector<Real> & translations)
1753 : {
1754 192 : file_id_type n_pairs = 0;
1755 192 : io.data(n_pairs, "# disjoint neighbor boundary pairs");
1756 192 : if (n_pairs)
1757 : {
1758 24 : io.data(pair_ids);
1759 26 : libmesh_error_msg_if(pair_ids.size() != 2 * n_pairs,
1760 : "Expected " << 2 * n_pairs << " disjoint neighbor boundary ids, found "
1761 : << pair_ids.size());
1762 :
1763 24 : translations.resize(LIBMESH_DIM * n_pairs);
1764 26 : io.data_stream(translations.data(),
1765 : cast_int<unsigned int>(translations.size()),
1766 : LIBMESH_DIM);
1767 : }
1768 192 : }
1769 :
1770 :
1771 :
1772 : template <typename file_id_type>
1773 192 : void CheckpointIO::read_integers_names
1774 : (Xdr & io,
1775 : std::vector<std::string> & node_integer_names,
1776 : std::vector<std::string> & elem_integer_names)
1777 : {
1778 : file_id_type n_node_integers, n_elem_integers;
1779 :
1780 192 : io.data(n_node_integers, "# n_extra_integers per node");
1781 192 : io.data(node_integer_names);
1782 192 : io.data(n_elem_integers, "# n_extra_integers per elem");
1783 192 : io.data(elem_integer_names);
1784 192 : }
1785 :
1786 :
1787 0 : unsigned int CheckpointIO::n_active_levels_in(MeshBase::const_element_iterator begin,
1788 : MeshBase::const_element_iterator end) const
1789 : {
1790 0 : unsigned int max_level = 0;
1791 :
1792 0 : for (const auto & elem : as_range(begin, end))
1793 0 : max_level = std::max(elem->level(), max_level);
1794 :
1795 0 : return max_level + 1;
1796 : }
1797 :
1798 : } // namespace libMesh
|