libMesh
Loading...
Searching...
No Matches
checkpoint_io.C
Go to the documentation of this file.
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/metis_partitioner.h"
35#include "libmesh/remote_elem.h"
36#include "libmesh/xdr_io.h"
37#include "libmesh/xdr_cxx.h"
38#include "libmesh/utility.h"
39#include "libmesh/int_range.h"
40
41// C++ includes
42#include <iostream>
43#include <iomanip>
44#include <cstdio>
45#include <vector>
46#include <string>
47#include <cstring>
48#include <fstream>
49#include <sstream> // for ostringstream
50#include <unordered_map>
51#include <unordered_set>
52#ifdef LIBMESH_HAVE_DIRECT_H
53#include <direct.h> // rmdir() on Windows
54#endif
55#ifdef LIBMESH_HAVE_UNISTD_H
56#include <unistd.h> // rmdir() on Unix
57#endif
58
59namespace
60{
61// chunking computes the number of chunks and first-chunk-offset when splitting a mesh
62// into nsplits pieces using size procs for the given MPI rank. The number of chunks and offset
63// are stored in nchunks and first_chunk respectively.
66{
67 if (nsplits % size == 0) // the chunks divide evenly over the processors
68 {
69 nchunks = nsplits / size;
70 first_chunk = libMesh::cast_int<libMesh::processor_id_type>(nchunks * rank);
71 return;
72 }
73
74 libMesh::processor_id_type nextra = nsplits % size;
75 if (rank < nextra) // leftover chunks cause an extra chunk to be added to this processor
76 {
77 nchunks = libMesh::cast_int<libMesh::processor_id_type>(nsplits / size + 1);
78 first_chunk = libMesh::cast_int<libMesh::processor_id_type>(nchunks * rank);
79 }
80 else // no extra chunks, but first chunk is offset by extras on earlier ranks
81 {
82 nchunks = nsplits / size;
83 // account for the case where nchunks is zero where we want max int
84 first_chunk = libMesh::cast_int<libMesh::processor_id_type>
85 (std::max((int)((nchunks + 1) * (nsplits % size) + nchunks * (rank - nsplits % size)),
86 (1 - (int)nchunks) * std::numeric_limits<int>::max()));
87 }
88}
89
90std::string_view extension(std::string_view s)
91{
92 auto pos = s.rfind(".");
93 if (pos == std::string::npos)
94 return "";
95 return s.substr(pos, s.size() - pos);
96}
97
98std::string split_dir(const std::string & input_name, libMesh::processor_id_type n_procs)
99{
100 return input_name + "/" + std::to_string(n_procs);
101}
102
103
104std::string header_file(const std::string & input_name, libMesh::processor_id_type n_procs)
105{
106 return (split_dir(input_name, n_procs) + "/header").append(extension(input_name));
107}
108
109std::string
110split_file(const std::string & input_name,
113{
114 return (split_dir(input_name, n_procs) + "/split-" + std::to_string(n_procs) + "-" +
115 std::to_string(proc_id)).append(extension(input_name));
116}
117
118void make_dir(const std::string & input_name, libMesh::processor_id_type n_procs)
119{
120 auto ret = libMesh::Utility::mkdir(input_name.c_str());
121 // error only if we failed to create dir - don't care if it was already there
122 libmesh_error_msg_if
123 (ret != 0 && ret != -1,
124 "Failed to create mesh split directory '" << input_name << "': " << std::strerror(ret));
125
126 auto dir_name = split_dir(input_name, n_procs);
127 ret = libMesh::Utility::mkdir(dir_name.c_str());
128 if (ret == -1)
129 libmesh_warning("In CheckpointIO::write, directory '"
130 << dir_name << "' already exists, overwriting contents.");
131 else
132 libmesh_error_msg_if
133 (ret != 0, "Failed to create mesh split directory '" << dir_name << "': " << std::strerror(ret));
134}
135
136} // namespace
137
138namespace libMesh
139{
140
141std::unique_ptr<CheckpointIO> split_mesh(MeshBase & mesh, processor_id_type nsplits)
142{
143 // There is currently an issue with DofObjects not being properly
144 // reset if the mesh is not first repartitioned onto 1 processor
145 // *before* being repartitioned onto the desired number of
146 // processors. So, this is a workaround, but not a particularly
147 // onerous one.
148 mesh.partition(1);
149 mesh.partition(nsplits);
150
151 processor_id_type my_num_chunks = 0;
152 processor_id_type my_first_chunk = 0;
153 chunking(mesh.comm().size(), mesh.comm().rank(), nsplits, my_num_chunks, my_first_chunk);
154
155 auto cpr = std::make_unique<CheckpointIO>(mesh);
156 cpr->current_processor_ids().clear();
157 for (processor_id_type i = my_first_chunk; i < my_first_chunk + my_num_chunks; i++)
158 cpr->current_processor_ids().push_back(i);
159 cpr->current_n_processors() = nsplits;
160 cpr->parallel() = true;
161 return cpr;
162}
163
164
165// ------------------------------------------------------------
166// CheckpointIO members
167CheckpointIO::CheckpointIO (MeshBase & mesh, const bool binary_in) :
168 MeshInput<MeshBase> (mesh,/* is_parallel_format = */ true),
169 MeshOutput<MeshBase>(mesh,/* is_parallel_format = */ true),
171 _binary (binary_in),
172 _parallel (false),
173 _version ("checkpoint-1.6"),
174 _my_processor_ids (1, processor_id()),
175 _my_n_processors (mesh.is_replicated() ? 1 : n_processors())
176{
177}
178
179CheckpointIO::CheckpointIO (const MeshBase & mesh, const bool binary_in) :
180 MeshInput<MeshBase> (), // write-only
181 MeshOutput<MeshBase>(mesh,/* is_parallel_format = */ true),
183 _binary (binary_in),
184 _parallel (false),
185 _version ("checkpoint-1.6"),
186 _my_processor_ids (1, processor_id()),
187 _my_n_processors (mesh.is_replicated() ? 1 : n_processors())
188{
189}
190
191CheckpointIO::~CheckpointIO () = default;
192
193processor_id_type CheckpointIO::select_split_config(const std::string & input_name, header_id_type & data_size)
194{
195 std::string header_name;
196
197 // We'll read a header file from processor 0 and broadcast.
198 if (this->processor_id() == 0)
199 {
200 header_name = header_file(input_name, _my_n_processors);
201
202 {
203 // look for header+splits with nprocs equal to _my_n_processors
204 std::ifstream in (header_name.c_str());
205 if (!in.good())
206 {
207 // otherwise fall back to a serial/single-split mesh
208 auto orig_header_name = header_name;
209 header_name = header_file(input_name, 1);
210 std::ifstream in2 (header_name.c_str());
211 libmesh_error_msg_if(!in2.good(),
212 "ERROR: Neither one of the following files can be located:\n\t'"
213 << orig_header_name << "' nor\n\t'" << input_name << "'\n"
214 << "If you are running a parallel job, double check that you've "
215 << "created a split for " << _my_n_processors << " ranks.\n"
216 << "Note: One of paths above may refer to a valid directory on your "
217 << "system, however we are attempting to read a valid header file.");
218 }
219 }
220
221 Xdr io (header_name, this->binary() ? DECODE : READ);
222
223 // read the version, but don't care about it
224 std::string input_version;
225 io.data(input_version);
226
227 // read the data type
228 io.data (data_size);
229 }
230
231 this->comm().broadcast(data_size);
232 this->comm().broadcast(header_name);
233
234 // How many per-processor files are here?
235 largest_id_type input_n_procs;
236
237 switch (data_size) {
238 case 2:
239 input_n_procs = this->read_header<uint16_t>(header_name);
240 break;
241 case 4:
242 input_n_procs = this->read_header<uint32_t>(header_name);
243 break;
244 case 8:
245 input_n_procs = this->read_header<uint64_t>(header_name);
246 break;
247 default:
248 libmesh_error();
249 }
250
251 if (!input_n_procs)
252 input_n_procs = 1;
253 return cast_int<processor_id_type>(input_n_procs);
254}
255
256void CheckpointIO::cleanup(const std::string & input_name, processor_id_type n_procs)
257{
258 auto header = header_file(input_name, n_procs);
259 auto ret = std::remove(header.c_str());
260 if (ret != 0)
261 libmesh_warning("Failed to clean up checkpoint header '" << header << "': " << std::strerror(ret));
262
263 for (processor_id_type i = 0; i < n_procs; i++)
264 {
265 auto split = split_file(input_name, n_procs, i);
266 ret = std::remove(split.c_str());
267 if (ret != 0)
268 libmesh_warning("Failed to clean up checkpoint split file '" << split << "': " << std::strerror(ret));
269 }
270
271 auto dir = split_dir(input_name, n_procs);
272 ret = rmdir(dir.c_str());
273 if (ret != 0)
274 libmesh_warning("Failed to clean up checkpoint split dir '" << dir << "': " << std::strerror(ret));
275
276 // We expect that this may fail if there are other split configurations still present in this
277 // directory - so don't bother to check/warn for failure.
278 rmdir(input_name.c_str());
279}
280
281
283{
284 return
285 (this->version().find("1.5") != std::string::npos) ||
286 (this->version().find("1.6") != std::string::npos);
287}
288
289
291{
292 return (this->version().find("1.6") != std::string::npos);
293}
294
295
296void CheckpointIO::write (const std::string & name)
297{
298 LOG_SCOPE("write()", "CheckpointIO");
299
300 // convenient reference to our mesh
302
303 // FIXME: For backwards compatibility, we'll assume for now that we
304 // only want to write distributed meshes in parallel. Later we can
305 // do a gather_to_zero() and support that case too.
307
308 processor_id_type use_n_procs = 1;
309 if (_parallel)
310 use_n_procs = _my_n_processors;
311
312 std::string header_file_name = header_file(name, use_n_procs);
313 make_dir(name, use_n_procs);
314
315 // We'll write a header file from processor 0 to make it easier to do unambiguous
316 // restarts later:
317 if (this->processor_id() == 0)
318 {
319 Xdr io (header_file_name, this->binary() ? ENCODE : WRITE);
320
321 // write the version
322 io.data(_version, "# version");
323
324 // write what kind of data type we're using
325 header_id_type data_size = sizeof(largest_id_type);
326 io.data(data_size, "# integer size");
327
328 // Write out the max mesh dimension for backwards compatibility
329 // with code that sets it independently of element dimensions
330 {
331 uint16_t mesh_dimension = cast_int<uint16_t>(mesh.mesh_dimension());
332 io.data(mesh_dimension, "# dimensions");
333 }
334
335 // Write out whether or not this is serial output
336 {
337 uint16_t parallel = _parallel;
338 io.data(parallel, "# parallel");
339 }
340
341 // If we're writing out a parallel mesh then we need to write the number of processors
342 // so we can check it upon reading the file
343 if (_parallel)
344 {
346 io.data(n_procs, "# n_procs");
347 }
348
349 // write subdomain names
350 this->write_subdomain_names(io);
351
352 // write boundary id names
353 const BoundaryInfo & boundary_info = mesh.get_boundary_info();
354 write_bc_names(io, boundary_info, true); // sideset names
355 write_bc_names(io, boundary_info, false); // nodeset names
356
357 // write extra integer names
358 const bool write_extra_integers = this->version_at_least_1_5();
359
360 if (write_extra_integers)
361 {
362 largest_id_type n_node_integers = mesh.n_node_integers();
363 io.data(n_node_integers, "# n_extra_integers per node");
364
365 std::vector<std::string> node_integer_names;
366 for (unsigned int i=0; i != n_node_integers; ++i)
367 node_integer_names.push_back(mesh.get_node_integer_name(i));
368
369 io.data(node_integer_names);
370
371 largest_id_type n_elem_integers = mesh.n_elem_integers();
372 io.data(n_elem_integers, "# n_extra_integers per elem");
373
374 std::vector<std::string> elem_integer_names;
375 for (unsigned int i=0; i != n_elem_integers; ++i)
376 elem_integer_names.push_back(mesh.get_elem_integer_name(i));
377
378 io.data(elem_integer_names);
379 }
380
381
382 }
383
384 // If this is a serial mesh written to a serial file then we're only
385 // going to write local data from processor 0. If this is a mesh being
386 // written in parallel then we're going to write from every
387 // processor.
388 std::vector<processor_id_type> ids_to_write;
389
390 // We're going to sort elements by pid in one pass, to avoid sending
391 // predicated iterators through the whole mesh N_p times.
392 //
393 // The data type here needs to be a non-const-pointer to whatever
394 // our element_iterator is a const-pointer to, for compatibility
395 // later.
396 typedef std::remove_const<MeshBase::const_element_iterator::value_type>::type nc_v_t;
397 std::unordered_map<processor_id_type, std::vector<nc_v_t>> elements_on_pid;
398
399 if (_parallel)
400 {
401 ids_to_write = _my_processor_ids;
402 for (processor_id_type p : ids_to_write)
403 elements_on_pid[p].clear();
404 auto eop_end = elements_on_pid.end();
405 for (auto & elem : mesh.element_ptr_range())
406 {
407 const processor_id_type p = elem->processor_id();
408 if (auto eop_it = elements_on_pid.find(p);
409 eop_it != eop_end)
410 eop_it->second.push_back(elem);
411 }
412 }
413 else if (mesh.is_serial())
414 {
415 if (mesh.processor_id() == 0)
416 {
417 // placeholder
418 ids_to_write.push_back(0);
419 }
420 }
421 else
422 {
423 libmesh_error_msg("Cannot write serial checkpoint from distributed mesh");
424 }
425
426 // Call build_side_list() and build_node_list() just *once* to avoid
427 // redundant expensive sorts during mesh splitting.
428 const BoundaryInfo & boundary_info = mesh.get_boundary_info();
429 std::vector<std::tuple<dof_id_type, unsigned short int, boundary_id_type>>
430 bc_triples = boundary_info.build_side_list();
431 std::vector<std::tuple<dof_id_type, boundary_id_type>>
432 bc_tuples = boundary_info.build_node_list();
433
434 for (const auto & my_pid : ids_to_write)
435 {
436 auto file_name = split_file(name, use_n_procs, my_pid);
437 Xdr io (file_name, this->binary() ? ENCODE : WRITE);
438
439 std::set<const Elem *, CompareElemIdsByLevel> elements;
440
441 // For serial files or for already-distributed meshs, we write
442 // everything we can see.
443 if (!_parallel || !mesh.is_serial())
444 elements.insert(mesh.elements_begin(), mesh.elements_end());
445 // For parallel files written from serial meshes we write what
446 // we'd be required to keep if we were to be deleting remote
447 // elements. This allows us to write proper parallel files even
448 // from a ReplicateMesh.
449 //
450 // WARNING: If we have a DistributedMesh which used
451 // "add_extra_ghost_elem" rather than ghosting functors to
452 // preserve elements and which is *also* currently serialized
453 // then we're not preserving those elements here. As a quick
454 // workaround user code should delete_remote_elements() before
455 // writing the checkpoint; as a long term workaround user code
456 // should use ghosting functors instead of extra_ghost_elem
457 // lists.
458 else
459 {
461 {
462 if (const auto elements_vec_it = elements_on_pid.find(p);
463 elements_vec_it != elements_on_pid.end())
464 {
465 auto & p_elements = elements_vec_it->second;
466
467 // Be compatible with both deprecated and
468 // corrected MeshBase iterator types
470
471 v_t * elempp = p_elements.data();
472 v_t * elemend = elempp + p_elements.size();
473
475 pid_elements_begin = MeshBase::const_element_iterator
476 (elempp, elemend, Predicates::NotNull<v_t *>()),
477 pid_elements_end = MeshBase::const_element_iterator
478 (elemend, elemend, Predicates::NotNull<v_t *>()),
479 active_pid_elements_begin = MeshBase::const_element_iterator
480 (elempp, elemend, Predicates::Active<v_t *>()),
481 active_pid_elements_end = MeshBase::const_element_iterator
482 (elemend, elemend, Predicates::Active<v_t *>());
483
485 (mesh, p, active_pid_elements_begin,
486 active_pid_elements_end, elements);
487 connect_children(mesh, pid_elements_begin,
488 pid_elements_end, elements);
489 }
490 }
491 }
492
493 connected_node_set_type connected_nodes;
494 connect_element_dependencies(mesh, elements, connected_nodes);
495
496 // write the nodal locations
497 this->write_nodes (io, connected_nodes);
498
499 // write connectivity
500 this->write_connectivity (io, elements);
501
502 // write remote_elem connectivity
503 this->write_remote_elem (io, elements);
504
505 // write the boundary condition information
506 this->write_bcs (io, elements, bc_triples);
507
508 // write the nodeset information
509 this->write_nodesets (io, connected_nodes, bc_tuples);
510
511 // close it up
512 io.close();
513 }
514
515 // this->comm().barrier();
516}
517
519{
520 {
522
523 const std::map<subdomain_id_type, std::string> & subdomain_map = mesh.get_subdomain_name_map();
524
525 std::vector<largest_id_type> subdomain_ids; subdomain_ids.reserve(subdomain_map.size());
526 std::vector<std::string> subdomain_names; subdomain_names.reserve(subdomain_map.size());
527
528 // We need to loop over the map and make sure that there aren't any invalid entries. Since we
529 // return writable references in mesh_base, it's possible for the user to leave some entity names
530 // blank. We can't write those to the XDA file.
531 largest_id_type n_subdomain_names = 0;
532 for (const auto & [id, name] : subdomain_map)
533 if (!name.empty())
534 {
535 n_subdomain_names++;
536 subdomain_ids.push_back(id);
537 subdomain_names.push_back(name);
538 }
539
540 io.data(n_subdomain_names, "# subdomain id to name map");
541 // Write out the ids and names in two vectors
542 if (n_subdomain_names)
543 {
544 io.data(subdomain_ids);
545 io.data(subdomain_names);
546 }
547 }
548}
549
550
551
553 const connected_node_set_type & nodeset) const
554{
555 largest_id_type n_nodes_here = nodeset.size();
556
557 io.data(n_nodes_here, "# n_nodes on proc");
558
559 const bool write_extra_integers = this->version_at_least_1_5();
560 const unsigned int n_extra_integers =
561 write_extra_integers ? MeshOutput<MeshBase>::mesh().n_node_integers() : 0;
562
563 // Will hold the node id and pid and extra integers
564 std::vector<largest_id_type> id_pid(2 + n_extra_integers);
565
566 // For the coordinates
567 std::vector<Real> coords(LIBMESH_DIM);
568
569 for (const auto & node : nodeset)
570 {
571 id_pid[0] = node->id();
572 id_pid[1] = node->processor_id();
573
574 libmesh_assert_equal_to(n_extra_integers, node->n_extra_integers());
575 for (unsigned int i=0; i != n_extra_integers; ++i)
576 id_pid[2+i] = node->get_extra_integer(i);
577
578 io.data_stream(id_pid.data(), 2 + n_extra_integers, 2 + n_extra_integers);
579
580#ifdef LIBMESH_ENABLE_UNIQUE_ID
581 largest_id_type unique_id = node->unique_id();
582
583 io.data(unique_id, "# unique id");
584#endif
585
586 coords[0] = (*node)(0);
587
588#if LIBMESH_DIM > 1
589 coords[1] = (*node)(1);
590#endif
591
592#if LIBMESH_DIM > 2
593 coords[2] = (*node)(2);
594#endif
595
596 io.data_stream(coords.data(), LIBMESH_DIM, 3);
597 }
598}
599
600
601
603 const std::set<const Elem *, CompareElemIdsByLevel> & elements) const
604{
605 libmesh_assert (io.writing());
606
607 const bool write_extra_integers = this->version_at_least_1_5();
608 const bool write_runtime_topology = this->version_at_least_1_6();
609 const unsigned int n_extra_integers =
610 write_extra_integers ? MeshOutput<MeshBase>::mesh().n_elem_integers() : 0;
611
612 // Put these out here to reduce memory churn
613 // id type pid subdomain_id parent_id extra_integer_0 ...
614 std::vector<largest_id_type> elem_data(6 + n_extra_integers);
615 std::vector<largest_id_type> conn_data;
616 std::vector<largest_id_type> runtime_topology;
617
618 largest_id_type n_elems_here = elements.size();
619
620 io.data(n_elems_here, "# number of elements");
621
622 for (const auto & elem : elements)
623 {
624 unsigned int n_nodes = elem->n_nodes();
625
626 elem_data[0] = elem->id();
627 elem_data[1] = elem->type();
628 elem_data[2] = elem->processor_id();
629 elem_data[3] = elem->subdomain_id();
630
631#ifdef LIBMESH_ENABLE_AMR
632 if (elem->parent() != nullptr)
633 {
634 elem_data[4] = elem->parent()->id();
635 elem_data[5] = elem->parent()->which_child_am_i(elem);
636 }
637 else
638#endif
639 {
640 elem_data[4] = static_cast<largest_id_type>(-1);
641 elem_data[5] = static_cast<largest_id_type>(-1);
642 }
643
644 for (unsigned int i=0; i != n_extra_integers; ++i)
645 elem_data[6+i] = elem->get_extra_integer(i);
646
647 conn_data.resize(n_nodes);
648
649 for (unsigned int i=0; i<n_nodes; i++)
650 conn_data[i] = elem->node_id(i);
651
652 io.data_stream(elem_data.data(),
653 cast_int<unsigned int>(elem_data.size()),
654 cast_int<unsigned int>(elem_data.size()));
655
656#ifdef LIBMESH_ENABLE_UNIQUE_ID
657 largest_id_type unique_id = elem->unique_id();
658
659 io.data(unique_id, "# unique id");
660#endif
661
662#ifdef LIBMESH_ENABLE_AMR
663 uint16_t p_level = cast_int<uint16_t>(elem->p_level());
664 io.data(p_level, "# p_level");
665
666 uint16_t rflag = elem->refinement_flag();
667 io.data(rflag, "# rflag");
668
669 uint16_t pflag = elem->p_refinement_flag();
670 io.data(pflag, "# pflag");
671#endif
672
673 if (elem->runtime_topology())
674 {
675 libmesh_error_msg_if
676 (!write_runtime_topology,
677 "Checkpoint format 1.6 or newer is required to write " <<
678 Utility::enum_to_string(elem->type()) << " elements.");
679
680 runtime_topology.clear();
681 runtime_topology.push_back(n_nodes);
682 runtime_topology.push_back(elem->n_sides());
683 for (auto s : elem->side_index_range())
684 {
685 const auto side_nodes = elem->nodes_on_side(s);
686 runtime_topology.push_back(side_nodes.size());
687 for (const auto n : side_nodes)
688 runtime_topology.push_back(n);
689 }
690
691 io.data(runtime_topology, "# runtime topology");
692 }
693
694 io.data_stream(conn_data.data(),
695 cast_int<unsigned int>(conn_data.size()),
696 cast_int<unsigned int>(conn_data.size()));
697 }
698}
699
700
702 const std::set<const Elem *, CompareElemIdsByLevel> & elements) const
703{
704 libmesh_assert (io.writing());
705
706 // Find the remote_elem neighbor and child links
707 std::vector<largest_id_type> elem_ids, parent_ids;
708 std::vector<uint16_t> elem_sides, child_numbers;
709
710 for (const auto & elem : elements)
711 {
712 for (auto n : elem->side_index_range())
713 {
714 const Elem * neigh = elem->neighbor_ptr(n);
715 if (neigh == remote_elem ||
716 (neigh && !elements.count(neigh)))
717 {
718 elem_ids.push_back(elem->id());
719 elem_sides.push_back(n);
720 }
721 }
722
723#ifdef LIBMESH_ENABLE_AMR
724 if (elem->has_children())
725 {
726 for (unsigned short c = 0,
727 nc = cast_int<unsigned short>(elem->n_children());
728 c != nc; ++c)
729 {
730 const Elem * child = elem->child_ptr(c);
731 if (child == remote_elem ||
732 (child && !elements.count(child)))
733 {
734 parent_ids.push_back(elem->id());
735 child_numbers.push_back(c);
736 }
737 }
738 }
739#endif
740 }
741
742 io.data(elem_ids, "# remote neighbor elem_ids");
743 io.data(elem_sides, "# remote neighbor elem_sides");
744 io.data(parent_ids, "# remote child parent_ids");
745 io.data(child_numbers, "# remote child_numbers");
746}
747
748
749
751 const std::set<const Elem *, CompareElemIdsByLevel> & elements,
752 const std::vector<std::tuple<dof_id_type, unsigned short int, boundary_id_type>> & bc_triples) const
753{
754 libmesh_assert (io.writing());
755
756 // Build a list of (elem, side, bc) tuples.
757 std::size_t bc_size = bc_triples.size();
758
759 std::vector<largest_id_type> element_id_list;
760 std::vector<uint16_t> side_list;
761 std::vector<largest_id_type> bc_id_list;
762
763 element_id_list.reserve(bc_size);
764 side_list.reserve(bc_size);
765 bc_id_list.reserve(bc_size);
766
767 std::unordered_set<dof_id_type> elems;
768 for (auto & e : elements)
769 elems.insert(e->id());
770
771 for (const auto & t : bc_triples)
772 if (elems.count(std::get<0>(t)))
773 {
774 element_id_list.push_back(std::get<0>(t));
775 side_list.push_back(std::get<1>(t));
776 bc_id_list.push_back(std::get<2>(t));
777 }
778
779
780 io.data(element_id_list, "# element ids for bcs");
781 io.data(side_list, "# sides of elements for bcs");
782 io.data(bc_id_list, "# bc ids");
783}
784
785
786
788 const connected_node_set_type & nodeset,
789 const std::vector<std::tuple<dof_id_type, boundary_id_type>> & bc_tuples) const
790{
791 libmesh_assert (io.writing());
792
793 // convenient reference to our mesh
795
796 // Build a list of (node, bc) tuples
797 std::size_t nodeset_size = bc_tuples.size();
798
799 std::vector<largest_id_type> node_id_list;
800 std::vector<largest_id_type> bc_id_list;
801
802 node_id_list.reserve(nodeset_size);
803 bc_id_list.reserve(nodeset_size);
804
805 for (const auto & t : bc_tuples)
806 if (nodeset.count(mesh.node_ptr(std::get<0>(t))))
807 {
808 node_id_list.push_back(std::get<0>(t));
809 bc_id_list.push_back(std::get<1>(t));
810 }
811
812 io.data(node_id_list, "# node id list");
813 io.data(bc_id_list, "# nodeset bc id list");
814}
815
816
817
818void CheckpointIO::write_bc_names (Xdr & io, const BoundaryInfo & info, bool is_sideset) const
819{
820 const std::map<boundary_id_type, std::string> & boundary_map = is_sideset ?
821 info.get_sideset_name_map() : info.get_nodeset_name_map();
822
823 std::vector<largest_id_type> boundary_ids; boundary_ids.reserve(boundary_map.size());
824 std::vector<std::string> boundary_names; boundary_names.reserve(boundary_map.size());
825
826 // We need to loop over the map and make sure that there aren't any invalid entries. Since we
827 // return writable references in boundary_info, it's possible for the user to leave some entity names
828 // blank. We can't write those to the XDA file.
829 largest_id_type n_boundary_names = 0;
830 for (const auto & [id, name] : boundary_map)
831 if (!name.empty())
832 {
833 n_boundary_names++;
834 boundary_ids.push_back(id);
835 boundary_names.push_back(name);
836 }
837
838 if (is_sideset)
839 io.data(n_boundary_names, "# sideset id to name map");
840 else
841 io.data(n_boundary_names, "# nodeset id to name map");
842 // Write out the ids and names in two vectors
843 if (n_boundary_names)
844 {
845 io.data(boundary_ids);
846 io.data(boundary_names);
847 }
848}
849
850void CheckpointIO::read (const std::string & input_name)
851{
852 LOG_SCOPE("read()","CheckpointIO");
853
855
857
858 header_id_type data_size;
859 processor_id_type input_n_procs = select_split_config(input_name, data_size);
860 auto header_name = header_file(input_name, input_n_procs);
861 bool input_parallel = input_n_procs > 0;
862
863 // If this is a serial read then we're going to only read the mesh
864 // on processor 0, then broadcast it
865 if ((input_parallel && !mesh.is_replicated()) || mesh.processor_id() == 0)
866 {
867 // If we're trying to read a parallel checkpoint file on a
868 // replicated mesh, we'll read every file on processor 0 so we
869 // can broadcast it later. If we're on a distributed mesh then
870 // we'll read every id to it's own processor and we'll "wrap
871 // around" with any ids that exceed our processor count.
872 const processor_id_type begin_proc_id =
873 (input_parallel && !mesh.is_replicated()) ?
874 mesh.processor_id() : 0;
875 const processor_id_type stride =
876 (input_parallel && !mesh.is_replicated()) ?
877 mesh.n_processors() : 1;
878
879 for (processor_id_type proc_id = begin_proc_id; proc_id < input_n_procs;
880 proc_id = cast_int<processor_id_type>(proc_id + stride))
881 {
882 auto file_name = split_file(input_name, input_n_procs, proc_id);
883
884 {
885 std::ifstream in (file_name.c_str());
886
887 libmesh_error_msg_if(!in.good(), "ERROR: cannot locate specified file:\n\t" << file_name);
888 }
889
890 // Do we expect all our files' remote_elem entries to really
891 // be remote? Only if we're not reading multiple input
892 // files on the same processor.
893 const bool expect_all_remote =
894 (input_n_procs <= mesh.n_processors() &&
896
897 Xdr io (file_name, this->binary() ? DECODE : READ);
898
899 switch (data_size) {
900 case 2:
901 this->read_subfile<uint16_t>(io, expect_all_remote);
902 break;
903 case 4:
904 this->read_subfile<uint32_t>(io, expect_all_remote);
905 break;
906 case 8:
907 this->read_subfile<uint64_t>(io, expect_all_remote);
908 break;
909 default:
910 libmesh_error();
911 }
912
913 io.close();
914 }
915 }
916
917 // If the mesh was only read on processor 0 then we need to broadcast it
918 if (mesh.is_replicated())
920 // If the mesh is really distributed then we need to make sure it
921 // knows that
922 else if (mesh.n_processors() > 1)
924
925 // If the mesh isn't getting even critical partitioning then we
926 // should update cached data from the partitioning we just read in
928 {
931 }
932}
933
934
935
936template <typename file_id_type>
937file_id_type CheckpointIO::read_header (const std::string & name)
938{
940
941 // Hack for codes which don't look at all elem dimensions
942 uint16_t mesh_dimension;
943
944 // Will this be a parallel input file? With how many processors? Stay tuned!
945 uint16_t input_parallel;
946 file_id_type input_n_procs;
947
948 std::string input_version;
949 std::vector<std::string> node_integer_names, elem_integer_names;
950
951 // We'll write a header file from processor 0 and broadcast.
952 if (this->processor_id() == 0)
953 {
954 Xdr io (name, this->binary() ? DECODE : READ);
955
956 // read the version
957 io.data(input_version);
958
959 // read the data type, don't care about it this time
960 header_id_type data_size;
961 io.data (data_size);
962
963 // read the dimension
964 io.data (mesh_dimension);
965
966 // Read whether or not this is a parallel file
967 io.data(input_parallel);
968
969 // With how many processors?
970 if (input_parallel)
971 io.data(input_n_procs);
972
973 // read subdomain names
974 this->read_subdomain_names<file_id_type>(io);
975
976 // read boundary names
977 BoundaryInfo & boundary_info = mesh.get_boundary_info();
978
979 this->read_bc_names<file_id_type>(io, boundary_info, true); // sideset names
980 this->read_bc_names<file_id_type>(io, boundary_info, false); // nodeset names
981
982 // read extra integer names?
983 std::swap(input_version, this->version());
984 const bool read_extra_integers = this->version_at_least_1_5();
985 std::swap(input_version, this->version());
986
987 if (read_extra_integers)
988 this->read_integers_names<file_id_type>
989 (io, node_integer_names, elem_integer_names);
990 }
991
992 // broadcast data from processor 0, set values everywhere
993 this->comm().broadcast(input_version);
994 this->version() = input_version;
995
996 this->comm().broadcast(mesh_dimension);
997 mesh.set_mesh_dimension(cast_int<unsigned char>(mesh_dimension));
998
999 this->comm().broadcast(input_parallel);
1000
1001 if (input_parallel)
1002 this->comm().broadcast(input_n_procs);
1003 else
1004 input_n_procs = 1;
1005
1006 std::map<subdomain_id_type, std::string> & subdomain_map =
1008 this->comm().broadcast(subdomain_map);
1009
1010 BoundaryInfo & boundary_info = mesh.get_boundary_info();
1011 this->comm().broadcast(boundary_info.set_sideset_name_map());
1012 this->comm().broadcast(boundary_info.set_nodeset_name_map());
1013
1014 this->comm().broadcast(node_integer_names);
1015 this->comm().broadcast(elem_integer_names);
1016
1017 for (auto & int_name : node_integer_names)
1018 mesh.add_node_integer(int_name);
1019
1020 for (auto & int_name : elem_integer_names)
1021 mesh.add_elem_integer(int_name);
1022
1023 return input_parallel ? input_n_procs : 0;
1024}
1025
1026
1027
1028template <typename file_id_type>
1029void CheckpointIO::read_subfile (Xdr & io, bool expect_all_remote)
1030{
1031 // read the nodal locations
1032 this->read_nodes<file_id_type> (io);
1033
1034 // read connectivity
1035 this->read_connectivity<file_id_type> (io);
1036
1037 // read remote_elem connectivity
1038 this->read_remote_elem<file_id_type> (io, expect_all_remote);
1039
1040 // read the boundary conditions
1041 this->read_bcs<file_id_type> (io);
1042
1043 // read the nodesets
1044 this->read_nodesets<file_id_type> (io);
1045}
1046
1047
1048
1049template <typename file_id_type>
1051{
1053
1054 std::map<subdomain_id_type, std::string> & subdomain_map =
1056
1057 std::vector<file_id_type> subdomain_ids;
1058 subdomain_ids.reserve(subdomain_map.size());
1059
1060 std::vector<std::string> subdomain_names;
1061 subdomain_names.reserve(subdomain_map.size());
1062
1063 file_id_type n_subdomain_names = 0;
1064 io.data(n_subdomain_names, "# subdomain id to name map");
1065
1066 if (n_subdomain_names)
1067 {
1068 io.data(subdomain_ids);
1069 io.data(subdomain_names);
1070
1071 for (auto i : index_range(subdomain_ids))
1072 subdomain_map[cast_int<subdomain_id_type>(subdomain_ids[i])] =
1073 subdomain_names[i];
1074 }
1075}
1076
1077
1078
1079template <typename file_id_type>
1081{
1082 // convenient reference to our mesh
1084
1085 file_id_type n_nodes_here;
1086 io.data(n_nodes_here, "# n_nodes on proc");
1087
1088 const bool read_extra_integers = this->version_at_least_1_5();
1089
1090 const unsigned int n_extra_integers =
1091 read_extra_integers ? mesh.n_node_integers() : 0;
1092
1093 // Will hold the node id and pid and extra integers
1094 std::vector<file_id_type> id_pid(2 + n_extra_integers);
1095
1096 // For the coordinates
1097 std::vector<Real> coords(LIBMESH_DIM);
1098
1099 for (unsigned int i=0; i<n_nodes_here; i++)
1100 {
1101 io.data_stream(id_pid.data(), 2 + n_extra_integers, 2 + n_extra_integers);
1102
1103#ifdef LIBMESH_ENABLE_UNIQUE_ID
1104 file_id_type unique_id = 0;
1105 io.data(unique_id, "# unique id");
1106#endif
1107
1108 io.data_stream(coords.data(), LIBMESH_DIM, LIBMESH_DIM);
1109
1110 Point p;
1111 p(0) = coords[0];
1112
1113#if LIBMESH_DIM > 1
1114 p(1) = coords[1];
1115#endif
1116
1117#if LIBMESH_DIM > 2
1118 p(2) = coords[2];
1119#endif
1120
1121 const dof_id_type id = cast_int<dof_id_type>(id_pid[0]);
1122
1123 // "Wrap around" if we see more processors than we're using.
1124 processor_id_type pid =
1125 cast_int<processor_id_type>(id_pid[1] % mesh.n_processors());
1126
1127 // If we already have this node (e.g. from another file, when
1128 // reading multiple distributed CheckpointIO files into a
1129 // ReplicatedMesh) then we don't want to add it again (because
1130 // ReplicatedMesh can't handle that) but we do want to assert
1131 // consistency between what we're reading and what we have.
1132 const Node * old_node = mesh.query_node_ptr(id);
1133
1134 if (old_node)
1135 {
1136 libmesh_assert_equal_to(pid, old_node->processor_id());
1137
1138 libmesh_assert_equal_to(n_extra_integers, old_node->n_extra_integers());
1139#ifndef NDEBUG
1140 for (unsigned int ei=0; ei != n_extra_integers; ++ei)
1141 {
1142 const dof_id_type extra_int = cast_int<dof_id_type>(id_pid[2+ei]);
1143 libmesh_assert_equal_to(extra_int, old_node->get_extra_integer(ei));
1144 }
1145#endif
1146
1147#ifdef LIBMESH_ENABLE_UNIQUE_ID
1148 libmesh_assert_equal_to(unique_id, old_node->unique_id());
1149#endif
1150 }
1151 else
1152 {
1153 Node * node =
1154 mesh.add_point(p, id, pid);
1155
1156#ifdef LIBMESH_ENABLE_UNIQUE_ID
1157 node->set_unique_id(unique_id);
1158#endif
1159
1160 libmesh_assert_equal_to(n_extra_integers, node->n_extra_integers());
1161
1162 for (unsigned int ei=0; ei != n_extra_integers; ++ei)
1163 {
1164 const dof_id_type extra_int = cast_int<dof_id_type>(id_pid[2+ei]);
1165 node->set_extra_integer(ei, extra_int);
1166 }
1167 }
1168 }
1169}
1170
1171
1172
1173template <typename file_id_type>
1175{
1176 // convenient reference to our mesh
1178
1179 const bool read_extra_integers = this->version_at_least_1_5();
1180 const bool read_runtime_topology = this->version_at_least_1_6();
1181
1182 const unsigned int n_extra_integers =
1183 read_extra_integers ? mesh.n_elem_integers() : 0;
1184
1185 file_id_type n_elems_here;
1186 io.data(n_elems_here);
1187
1188 // Keep track of the highest dimensional element we've added to the mesh
1189 unsigned int highest_elem_dim = mesh.mesh_dimension();
1190
1191 // RHS: Originally we used invalid_processor_id as a "no parent" tag
1192 // number, because I'm an idiot. Let's try to support broken files
1193 // as much as possible.
1194 bool file_is_broken = false;
1195
1196 for (unsigned int i=0; i<n_elems_here; i++)
1197 {
1198 // id type pid subdomain_id parent_id
1199 std::vector<file_id_type> elem_data(6 + n_extra_integers);
1200 io.data_stream
1201 (elem_data.data(), cast_int<unsigned int>(elem_data.size()),
1202 cast_int<unsigned int>(elem_data.size()));
1203
1204#ifdef LIBMESH_ENABLE_UNIQUE_ID
1205 file_id_type unique_id = 0;
1206 io.data(unique_id, "# unique id");
1207#endif
1208
1209#ifdef LIBMESH_ENABLE_AMR
1210 uint16_t p_level = 0;
1211 io.data(p_level, "# p_level");
1212
1213 uint16_t rflag, pflag;
1214 io.data(rflag, "# rflag");
1215 io.data(pflag, "# pflag");
1216#endif
1217
1218 const ElemType elem_type =
1219 static_cast<ElemType>(elem_data[1]);
1220 const bool is_c0polygon = (elem_type == C0POLYGON);
1221 const bool is_c0polyhedron = (elem_type == C0POLYHEDRON);
1222
1223 unsigned int n_nodes = Elem::type_to_n_nodes_map[elem_data[1]];
1224 // Runtime-topology types have no fixed node count in this map.
1225 const bool has_runtime_topology = (n_nodes == invalid_uint);
1226 std::vector<std::vector<unsigned int>> nodes_on_sides;
1227
1228 if (has_runtime_topology)
1229 {
1230 libmesh_error_msg_if
1231 (!read_runtime_topology,
1232 "Checkpoint format 1.6 or newer is required to read " <<
1233 Utility::enum_to_string(elem_type) << " elements.");
1234
1235 std::vector<file_id_type> runtime_topology;
1236 io.data(runtime_topology, "# runtime topology");
1237 libmesh_error_msg_if(runtime_topology.size() < 2,
1238 "Invalid runtime element topology.");
1239
1240 std::size_t topology_index = 0;
1241 n_nodes =
1242 cast_int<unsigned int>(runtime_topology[topology_index++]);
1243 const unsigned int n_sides =
1244 cast_int<unsigned int>(runtime_topology[topology_index++]);
1245 nodes_on_sides.resize(n_sides);
1246
1247 for (auto s : index_range(nodes_on_sides))
1248 {
1249 libmesh_error_msg_if
1250 (topology_index == runtime_topology.size(),
1251 "Incomplete runtime element checkpoint topology.");
1252
1253 const unsigned int n_side_nodes =
1254 cast_int<unsigned int>(runtime_topology[topology_index++]);
1255 libmesh_error_msg_if
1256 (n_side_nodes > runtime_topology.size() - topology_index,
1257 "Invalid runtime element side checkpoint topology.");
1258
1259 auto & side_nodes = nodes_on_sides[s];
1260 side_nodes.resize(n_side_nodes);
1261 for (auto n : index_range(side_nodes))
1262 {
1263 const unsigned int local_node =
1264 cast_int<unsigned int>(runtime_topology[topology_index++]);
1265 libmesh_error_msg_if
1266 (local_node >= n_nodes,
1267 "Runtime element side checkpoint topology references "
1268 "an invalid local node.");
1269 side_nodes[n] = local_node;
1270 }
1271 }
1272
1273 libmesh_error_msg_if
1274 (topology_index != runtime_topology.size(),
1275 "Extra data in runtime element checkpoint topology.");
1276
1277 if (is_c0polygon)
1278 {
1279 libmesh_error_msg_if
1280 (n_nodes < 3 || n_sides != n_nodes,
1281 "Invalid C0POLYGON checkpoint topology.");
1282 for (const auto & side_nodes : nodes_on_sides)
1283 libmesh_error_msg_if
1284 (side_nodes.size() != 2,
1285 "Invalid C0POLYGON side checkpoint topology.");
1286 }
1287 else if (is_c0polyhedron)
1288 {
1289 libmesh_error_msg_if(n_sides < 4,
1290 "Invalid C0POLYHEDRON checkpoint topology.");
1291 for (const auto & side_nodes : nodes_on_sides)
1292 libmesh_error_msg_if
1293 (side_nodes.size() < 3,
1294 "Invalid C0POLYHEDRON side checkpoint topology.");
1295 }
1296 }
1297
1298 // Snag the node ids this element was connected to
1299 std::vector<file_id_type> conn_data(n_nodes);
1300 io.data_stream
1301 (conn_data.data(), cast_int<unsigned int>(conn_data.size()),
1302 cast_int<unsigned int>(conn_data.size()));
1303
1304 const dof_id_type id =
1305 cast_int<dof_id_type> (elem_data[0]);
1306 const processor_id_type proc_id =
1307 cast_int<processor_id_type>
1308 (elem_data[2] % mesh.n_processors());
1309 const subdomain_id_type subdomain_id =
1310 restrict_int<subdomain_id_type>(elem_data[3]);
1311
1312 // Old broken files used processsor_id_type(-1)...
1313 // But we *know* our first element will be level 0
1314 if (i == 0 && elem_data[4] == 65535)
1315 file_is_broken = true;
1316
1317 // On a broken file we can't tell whether a parent of 65535 is a
1318 // null parent or an actual parent of 65535. Assuming the
1319 // former will cause less breakage.
1320 Elem * parent =
1321 (elem_data[4] == static_cast<largest_id_type>(-1) ||
1322 (file_is_broken && elem_data[4] == 65535)) ?
1323 nullptr : mesh.elem_ptr(cast_int<dof_id_type>(elem_data[4]));
1324
1325 const unsigned short int child_num =
1326 (elem_data[5] == static_cast<largest_id_type>(-1) ||
1327 (file_is_broken && elem_data[5] == 65535)) ?
1328 static_cast<unsigned short>(-1) :
1329 cast_int<unsigned short>(elem_data[5]);
1330
1331 if (!parent)
1332 libmesh_assert_equal_to
1333 (child_num, static_cast<unsigned short>(-1));
1334
1335 Elem * old_elem = mesh.query_elem_ptr(id);
1336
1337 // If we already have this element (e.g. from another file,
1338 // when reading multiple distributed CheckpointIO files into
1339 // a ReplicatedMesh) then we don't want to add it again
1340 // (because ReplicatedMesh can't handle that) but we do want
1341 // to assert consistency between what we're reading and what
1342 // we have.
1343 if (old_elem)
1344 {
1345 libmesh_assert_equal_to(elem_type, old_elem->type());
1346 libmesh_assert_equal_to(proc_id, old_elem->processor_id());
1347 libmesh_assert_equal_to(subdomain_id, old_elem->subdomain_id());
1348 if (parent)
1349 libmesh_assert_equal_to(parent, old_elem->parent());
1350 else
1351 libmesh_assert(!old_elem->parent());
1352
1353 libmesh_assert_equal_to(n_extra_integers, old_elem->n_extra_integers());
1354#ifndef NDEBUG
1355 for (unsigned int ei=0; ei != n_extra_integers; ++ei)
1356 {
1357 const dof_id_type extra_int = cast_int<dof_id_type>(elem_data[6+ei]);
1358 libmesh_assert_equal_to(extra_int, old_elem->get_extra_integer(ei));
1359 }
1360#endif
1361
1362 libmesh_assert_equal_to(old_elem->n_nodes(), conn_data.size());
1363
1364 for (unsigned int n=0,
1365 n_conn = cast_int<unsigned int>(conn_data.size());
1366 n != n_conn; n++)
1367 libmesh_assert_equal_to
1368 (old_elem->node_id(n),
1369 cast_int<dof_id_type>(conn_data[n]));
1370
1371 if (has_runtime_topology)
1372 {
1373 libmesh_assert_equal_to(old_elem->n_sides(),
1374 nodes_on_sides.size());
1375#ifndef NDEBUG
1376 for (auto s : index_range(nodes_on_sides))
1377 libmesh_assert(old_elem->nodes_on_side(s) ==
1378 nodes_on_sides[s]);
1379#endif
1380 }
1381 }
1382 else
1383 {
1384 // Create the element
1385 std::unique_ptr<Elem> elem;
1386 std::unique_ptr<Node> generated_mid_elem_node;
1387
1388 if (is_c0polygon)
1389 elem = std::make_unique<C0Polygon>(n_nodes, parent);
1390 else if (is_c0polyhedron)
1391 {
1392 std::vector<std::shared_ptr<Polygon>> sides(nodes_on_sides.size());
1393 for (auto s : index_range(nodes_on_sides))
1394 {
1395 const auto & side_node_indices = nodes_on_sides[s];
1396 auto side =
1397 std::make_shared<C0Polygon>
1398 (cast_int<unsigned int>(side_node_indices.size()));
1399 for (auto n : index_range(side_node_indices))
1400 side->set_node
1401 (n, mesh.node_ptr(cast_int<dof_id_type>
1402 (conn_data[side_node_indices[n]])));
1403 sides[s] = std::move(side);
1404 }
1405
1406 elem = std::make_unique<C0Polyhedron>
1407 (sides, generated_mid_elem_node, parent);
1408
1409 libmesh_error_msg_if
1410 (elem->n_nodes() != conn_data.size(),
1411 "C0POLYHEDRON checkpoint topology is incompatible with "
1412 "this libMesh configuration.");
1413
1414 for (auto n : make_range(elem->n_vertices()))
1415 libmesh_error_msg_if
1416 (elem->node_id(n) !=
1417 cast_int<dof_id_type>(conn_data[n]),
1418 "C0POLYHEDRON checkpoint topology has inconsistent "
1419 "local node ordering.");
1420 }
1421 else
1422 elem = Elem::build(elem_type, parent);
1423
1424 if (has_runtime_topology)
1425 {
1426 libmesh_error_msg_if
1427 (!elem->runtime_topology() ||
1428 elem->n_nodes() != conn_data.size() ||
1429 elem->n_sides() != nodes_on_sides.size(),
1430 Utility::enum_to_string(elem_type) <<
1431 " checkpoint topology is incompatible with this "
1432 "libMesh configuration.");
1433
1434 for (auto s : index_range(nodes_on_sides))
1435 libmesh_error_msg_if
1436 (elem->nodes_on_side(s) != nodes_on_sides[s],
1437 Utility::enum_to_string(elem_type) <<
1438 " checkpoint topology has inconsistent side ordering.");
1439 }
1440
1441#ifdef LIBMESH_ENABLE_UNIQUE_ID
1442 elem->set_unique_id(unique_id);
1443#endif
1444
1445 if (elem->dim() > highest_elem_dim)
1446 highest_elem_dim = elem->dim();
1447
1448 elem->set_id() = id;
1449 elem->processor_id() = proc_id;
1450 elem->subdomain_id() = subdomain_id;
1451
1452#ifdef LIBMESH_ENABLE_AMR
1453 elem->hack_p_level(p_level);
1454
1455 elem->set_refinement_flag (cast_int<Elem::RefinementState>(rflag));
1456 elem->set_p_refinement_flag(cast_int<Elem::RefinementState>(pflag));
1457
1458 // Set parent connections
1459 if (parent)
1460 {
1461 // We must specify a child_num, because we will have
1462 // skipped adding any preceding remote_elem children
1463 parent->add_child(elem.get(), child_num);
1464 }
1465#else
1466 libmesh_ignore(child_num);
1467#endif
1468
1469 libmesh_assert(elem->n_nodes() == conn_data.size());
1470
1471 // Connect all the nodes to this element
1472 for (unsigned int n=0,
1473 n_conn = cast_int<unsigned int>(conn_data.size());
1474 n != n_conn; n++)
1475 elem->set_node(n,
1476 mesh.node_ptr(cast_int<dof_id_type>(conn_data[n])));
1477
1478 Elem * added_elem = mesh.add_elem(std::move(elem));
1479
1480 libmesh_assert_equal_to(n_extra_integers, added_elem->n_extra_integers());
1481 for (unsigned int ei=0; ei != n_extra_integers; ++ei)
1482 {
1483 const dof_id_type extra_int = cast_int<dof_id_type>(elem_data[6+ei]);
1484 added_elem->set_extra_integer(ei, extra_int);
1485 }
1486 }
1487 }
1488
1489 mesh.set_mesh_dimension(cast_int<unsigned char>(highest_elem_dim));
1490}
1491
1492
1493template <typename file_id_type>
1494void CheckpointIO::read_remote_elem (Xdr & io, bool libmesh_dbg_var(expect_all_remote))
1495{
1496 // convenient reference to our mesh
1498
1499 // Find the remote_elem neighbor links
1500 std::vector<file_id_type> elem_ids;
1501 std::vector<uint16_t> elem_sides;
1502
1503 io.data(elem_ids, "# remote neighbor elem_ids");
1504 io.data(elem_sides, "# remote neighbor elem_sides");
1505
1506 libmesh_assert_equal_to(elem_ids.size(), elem_sides.size());
1507
1508 for (auto i : index_range(elem_ids))
1509 {
1510 Elem & elem = mesh.elem_ref(cast_int<dof_id_type>(elem_ids[i]));
1511 if (!elem.neighbor_ptr(elem_sides[i]))
1512 elem.set_neighbor(elem_sides[i],
1513 const_cast<RemoteElem *>(remote_elem));
1514 else
1515 libmesh_assert(!expect_all_remote);
1516 }
1517
1518 // Find the remote_elem children links
1519 std::vector<file_id_type> parent_ids;
1520 std::vector<uint16_t> child_numbers;
1521
1522 io.data(parent_ids, "# remote child parent_ids");
1523 io.data(child_numbers, "# remote child_numbers");
1524
1525#ifdef LIBMESH_ENABLE_AMR
1526 for (auto i : index_range(parent_ids))
1527 {
1528 Elem & elem = mesh.elem_ref(cast_int<dof_id_type>(parent_ids[i]));
1529
1530 // We'd like to assert that no child pointer already exists to
1531 // be overwritten by remote_elem, but Elem doesn't actually have
1532 // an API that will return a child pointer without asserting
1533 // that it isn't nullptr.
1534 const Elem * child = elem.raw_child_ptr(child_numbers[i]);
1535
1536 if (!child)
1537 elem.add_child(const_cast<RemoteElem *>(remote_elem),
1538 child_numbers[i]);
1539 else
1540 libmesh_assert(!expect_all_remote);
1541 }
1542#endif
1543}
1544
1545
1546
1547template <typename file_id_type>
1549{
1550 // convenient reference to our mesh
1552
1553 // and our boundary info object
1554 BoundaryInfo & boundary_info = mesh.get_boundary_info();
1555
1556 std::vector<file_id_type> element_id_list;
1557 std::vector<uint16_t> side_list;
1558 std::vector<file_id_type> bc_id_list;
1559
1560 io.data(element_id_list, "# element ids for bcs");
1561 io.data(side_list, "# sides of elements for bcs");
1562 io.data(bc_id_list, "# bc ids");
1563
1564 for (auto i : index_range(element_id_list))
1565 boundary_info.add_side
1566 (cast_int<dof_id_type>(element_id_list[i]), side_list[i],
1567 cast_int<boundary_id_type>(bc_id_list[i]));
1568}
1569
1570
1571
1572template <typename file_id_type>
1574{
1575 // convenient reference to our mesh
1577
1578 // and our boundary info object
1579 BoundaryInfo & boundary_info = mesh.get_boundary_info();
1580
1581 std::vector<file_id_type> node_id_list;
1582 std::vector<file_id_type> bc_id_list;
1583
1584 io.data(node_id_list, "# node id list");
1585 io.data(bc_id_list, "# nodeset bc id list");
1586
1587 for (auto i : index_range(node_id_list))
1588 boundary_info.add_node
1589 (cast_int<dof_id_type>(node_id_list[i]),
1590 cast_int<boundary_id_type>(bc_id_list[i]));
1591}
1592
1593
1594
1595template <typename file_id_type>
1596void CheckpointIO::read_bc_names(Xdr & io, BoundaryInfo & info, bool is_sideset)
1597{
1598 std::map<boundary_id_type, std::string> & boundary_map = is_sideset ?
1599 info.set_sideset_name_map() : info.set_nodeset_name_map();
1600
1601 std::vector<file_id_type> boundary_ids;
1602 std::vector<std::string> boundary_names;
1603
1604 file_id_type n_boundary_names = 0;
1605
1606 if (is_sideset)
1607 io.data(n_boundary_names, "# sideset id to name map");
1608 else
1609 io.data(n_boundary_names, "# nodeset id to name map");
1610
1611 if (n_boundary_names)
1612 {
1613 io.data(boundary_ids);
1614 io.data(boundary_names);
1615 }
1616
1617 // Add them back into the map
1618 for (auto i : index_range(boundary_ids))
1619 boundary_map[cast_int<boundary_id_type>(boundary_ids[i])] =
1620 boundary_names[i];
1621}
1622
1623
1624template <typename file_id_type>
1626 (Xdr & io,
1627 std::vector<std::string> & node_integer_names,
1628 std::vector<std::string> & elem_integer_names)
1629{
1630 file_id_type n_node_integers, n_elem_integers;
1631
1632 io.data(n_node_integers, "# n_extra_integers per node");
1633 io.data(node_integer_names);
1634 io.data(n_elem_integers, "# n_extra_integers per elem");
1635 io.data(elem_integer_names);
1636}
1637
1638
1641{
1642 unsigned int max_level = 0;
1643
1644 for (const auto & elem : as_range(begin, end))
1645 max_level = std::max(elem->level(), max_level);
1646
1647 return max_level + 1;
1648}
1649
1650} // namespace libMesh
processor_id_type size() const
processor_id_type rank() const
void broadcast(T &data, const unsigned int root_id=0, const bool identical_sizes=false) const
The BoundaryInfo class contains information relevant to boundary conditions including storing faces,...
std::vector< BCTuple > build_side_list(BCTupleSortBy sort_by=BCTupleSortBy::ELEM_ID) const
std::vector< NodeBCTuple > build_node_list(NodeBCTupleSortBy sort_by=NodeBCTupleSortBy::NODE_ID) const
std::map< boundary_id_type, std::string > & set_sideset_name_map()
void add_node(const Node *node, const boundary_id_type id)
Add Node node with boundary id id to the boundary information data structures.
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
Add side side of element number elem with boundary id id to the boundary information data structure.
std::map< boundary_id_type, std::string > & set_nodeset_name_map()
void read_nodes(Xdr &io)
Read the nodal locations for a parallel, distributed mesh.
processor_id_type _my_n_processors
void write_bc_names(Xdr &io, const BoundaryInfo &info, bool is_sideset) const
Write boundary names information (sideset and nodeset)
file_id_type read_header(const std::string &name)
Read header data on processor 0, then broadcast.
void write_nodesets(Xdr &io, const connected_node_set_type &nodeset, const std::vector< std::tuple< dof_id_type, boundary_id_type > > &bc_tuples) const
Write the nodal boundary conditions for part of a mesh.
void read_subdomain_names(Xdr &io)
Read subdomain name information.
CheckpointIO(MeshBase &, const bool=false)
Constructor.
processor_id_type select_split_config(const std::string &input_name, header_id_type &data_size)
void write_connectivity(Xdr &io, const std::set< const Elem *, CompareElemIdsByLevel > &elements) const
Write the connectivity for part of a mesh.
bool version_at_least_1_6() const
virtual ~CheckpointIO()
Destructor.
void write_subdomain_names(Xdr &io) const
Write subdomain name information.
void read_connectivity(Xdr &io)
Read the connectivity for a parallel, distributed mesh.
void write_nodes(Xdr &io, const connected_node_set_type &nodeset) const
Write the nodal locations for part of a mesh.
void read_bcs(Xdr &io)
Read the boundary conditions for a parallel, distributed mesh.
void read_remote_elem(Xdr &io, bool expect_all_remote)
Read the remote_elem neighbor and child links for a parallel, distributed mesh.
void read_bc_names(Xdr &io, BoundaryInfo &info, bool is_sideset)
Read boundary names information (sideset and nodeset)
std::vector< processor_id_type > _my_processor_ids
void write_remote_elem(Xdr &io, const std::set< const Elem *, CompareElemIdsByLevel > &elements) const
Write the remote_elem neighbor and child links for part of a mesh.
void read_integers_names(Xdr &io, std::vector< std::string > &node_integer_names, std::vector< std::string > &elem_integer_names)
Read extra integers names information.
void read_nodesets(Xdr &io)
Read the nodeset conditions for a parallel, distributed mesh.
virtual void read(const std::string &input_name) override
This method implements reading a mesh from a specified file.
bool binary() const
Get/Set the flag indicating if we should read/write binary.
static void cleanup(const std::string &input_name, processor_id_type n_procs)
Used to remove a checkpoint directory and its corresponding files.
const std::string & version() const
Get/Set the version string.
virtual void write(const std::string &name) override
This method implements writing a mesh to a specified file.
void read_subfile(Xdr &io, bool expect_all_remote)
Read a non-header file.
void write_bcs(Xdr &io, const std::set< const Elem *, CompareElemIdsByLevel > &elements, const std::vector< std::tuple< dof_id_type, unsigned short int, boundary_id_type > > &bc_triples) const
Write the side boundary conditions for part of a mesh.
bool parallel() const
Get/Set the flag indicating if we should read/write binary.
bool version_at_least_1_5() const
unsigned int n_active_levels_in(MeshBase::const_element_iterator begin, MeshBase::const_element_iterator end) const
dof_id_type get_extra_integer(const unsigned int index) const
Gets the value on this object of the extra integer associated with index, which should have been obta...
unsigned int n_extra_integers() const
Returns how many extra integers are associated to the DofObject.
processor_id_type processor_id() const
Definition dof_object.h:881
unique_id_type unique_id() const
Definition dof_object.h:835
static constexpr processor_id_type invalid_processor_id
An invalid processor_id to distinguish DoFs that have not been assigned to a processor.
Definition dof_object.h:484
void set_unique_id(unique_id_type new_id)
Sets the unique_id for this DofObject.
Definition dof_object.h:848
void set_extra_integer(const unsigned int index, const dof_id_type value)
Sets the value on this object of the extra integer associated with index, which should have been obta...
This is the base class from which all geometric element types are derived.
Definition elem.h:96
const Elem * raw_child_ptr(unsigned int i) const
Definition elem.h:3171
static const unsigned int type_to_n_nodes_map[INVALID_ELEM]
This array maps the integer representation of the ElemType enum to the number of nodes in the element...
Definition elem.h:643
virtual unsigned int n_nodes() const =0
const Elem * parent() const
Definition elem.h:3047
void set_neighbor(const unsigned int i, Elem *n)
Assigns n as the neighbor.
Definition elem.h:2635
const Elem * child_ptr(unsigned int i) const
Definition elem.h:3180
static std::unique_ptr< Elem > build(const ElemType type, Elem *p=nullptr)
Definition elem.C:442
virtual std::vector< unsigned int > nodes_on_side(const unsigned int) const =0
subdomain_id_type subdomain_id() const
Definition elem.h:2591
void add_child(Elem *elem)
Adds a child pointer to the array of children of this element.
Definition elem.C:2053
virtual ElemType type() const =0
virtual unsigned int n_sides() const =0
dof_id_type node_id(const unsigned int i) const
Definition elem.h:2484
const Elem * neighbor_ptr(unsigned int i) const
Definition elem.h:2615
This is the MeshBase class.
Definition mesh_base.h:81
virtual bool is_serial() const
Definition mesh_base.h:357
unsigned int n_elem_integers() const
Definition mesh_base.h:1090
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition mesh_base.h:170
virtual const Node * node_ptr(const dof_id_type i) const =0
unsigned int mesh_dimension() const
Definition mesh_base.C:430
virtual dof_id_type n_elem() const =0
virtual bool is_replicated() const
Definition mesh_base.h:379
unsigned int recalculate_n_partitions()
In a few (very rare) cases, the user may have manually tagged the elements with specific processor ID...
Definition mesh_base.C:1813
const std::map< subdomain_id_type, std::string > & get_subdomain_name_map() const
Definition mesh_base.h:1926
unsigned int add_node_integer(std::string name, bool allocate_data=true, dof_id_type default_value=DofObject::invalid_id)
Register an integer datum (of type dof_id_type) to be added to each node in the mesh.
Definition mesh_base.C:712
const std::string & get_node_integer_name(unsigned int i) const
Definition mesh_base.h:1201
virtual const Node * query_node_ptr(const dof_id_type i) const =0
void set_mesh_dimension(unsigned char d)
Resets the logical dimension of the mesh.
Definition mesh_base.h:423
virtual Node * add_point(const Point &p, const dof_id_type id=DofObject::invalid_id, const processor_id_type proc_id=DofObject::invalid_processor_id)=0
Add a new Node at Point p to the end of the vertex array, with processor_id procid.
virtual void update_post_partitioning()
Recalculate any cached data (or invalidate any caches that are computed on the fly) after elements an...
Definition mesh_base.C:1180
virtual const Elem * elem_ptr(const dof_id_type i) const =0
unsigned int add_elem_integer(std::string name, bool allocate_data=true, dof_id_type default_value=DofObject::invalid_id)
Register an integer datum (of type dof_id_type) to be added to each element in the mesh.
Definition mesh_base.C:623
const std::string & get_elem_integer_name(unsigned int i) const
Definition mesh_base.h:1079
virtual void set_distributed()
Asserts that not all elements and nodes of the mesh necessarily exist on the current processor.
Definition mesh_base.h:372
unsigned int n_node_integers() const
Definition mesh_base.h:1212
virtual const Elem * query_elem_ptr(const dof_id_type i) const =0
virtual Elem * add_elem(Elem *e)=0
Add elem e to the end of the element array.
virtual const Elem & elem_ref(const dof_id_type i) const
Definition mesh_base.h:788
std::map< subdomain_id_type, std::string > & set_subdomain_name_map()
Definition mesh_base.h:1924
virtual void partition(const unsigned int n_parts)
Call the default partitioner (currently metis_partition()).
Definition mesh_base.C:1769
void skip_partitioning(bool skip)
If true is passed in then nothing on this mesh will be (re)partitioned.
Definition mesh_base.h:1429
This is the MeshCommunication class.
void broadcast(MeshBase &) const
This method takes a mesh (which is assumed to reside on processor 0) and broadcasts it to all the oth...
This class defines an abstract interface for Mesh input.
Definition mesh_input.h:49
This class defines an abstract interface for Mesh output.
Definition mesh_output.h:54
const MT & mesh() const
A Node is like a Point, but with more information.
Definition node.h:55
An object whose state is distributed along a set of processors.
processor_id_type processor_id() const
const Parallel::Communicator & comm() const
processor_id_type n_processors() const
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:40
In parallel meshes where a ghost element has neighbors which do not exist on the local processor,...
Definition remote_elem.h:61
This class implements a C++ interface to the XDR (eXternal Data Representation) format.
Definition xdr_cxx.h:68
bool writing() const
Definition xdr_cxx.h:129
void data_stream(T *val, const unsigned int len, const unsigned int line_break=libMesh::invalid_uint)
Inputs or outputs a raw data stream.
Definition xdr_cxx.C:925
void close()
Closes the file if it is open.
Definition xdr_cxx.C:278
void data(T &a, std::string_view comment="")
Inputs or outputs a single value.
Definition xdr_cxx.C:860
MeshBase & mesh
int mkdir(const char *pathname)
Create a directory.
Definition utility.C:152
std::string enum_to_string(const T e)
The libMesh namespace provides an interface to certain functionality in the library.
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
Helper function that allows us to treat a homogenous pair as a range.
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition int_range.h:153
void query_ghosting_functors(const MeshBase &mesh, processor_id_type pid, MeshBase::const_element_iterator elem_it, MeshBase::const_element_iterator elem_end, connected_elem_set_type &connected_elements)
ElemType
Defines an enum for geometric element types.
void libmesh_ignore(const Args &...)
libmesh_assert(ctx)
const unsigned int invalid_uint
A number which is used quite often to represent an invalid or uninitialized value for an unsigned int...
Definition libmesh.h:303
std::unique_ptr< CheckpointIO > split_mesh(MeshBase &mesh, processor_id_type nsplits)
split_mesh takes the given initialized/opened mesh and partitions it into nsplits pieces or chunks.
uint64_t largest_id_type
Definition id_types.h:148
const RemoteElem * remote_elem
Definition remote_elem.C:57
std::set< const Node * > connected_node_set_type
void connect_element_dependencies(const MeshBase &mesh, connected_elem_set_type &connected_elements, connected_node_set_type &connected_nodes)
uint8_t dof_id_type
Definition id_types.h:67
void connect_children(const MeshBase &mesh, MeshBase::const_element_iterator elem_it, MeshBase::const_element_iterator elem_end, connected_elem_set_type &connected_elements)
uint8_t processor_id_type
Definition id_types.h:104
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition int_range.h:176
The definition of the const_element_iterator struct.
Definition mesh_base.h:2556
Used to iterate over non-nullptr, active entries in a container.
Used to iterate over non-nullptr entries in a container.
const dof_id_type n_nodes
Definition tecplot_io.C:67