libMesh
Loading...
Searching...
No Matches
system_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
19#include "libmesh/libmesh_common.h"
20#include "libmesh/parallel.h"
21
22
23// Local Include
24#include "libmesh/libmesh_version.h"
25#include "libmesh/system.h"
26#include "libmesh/mesh_base.h"
27#include "libmesh/elem.h"
28#include "libmesh/xdr_cxx.h"
29#include "libmesh/numeric_vector.h"
30#include "libmesh/dof_map.h"
31
32
33// C++ Includes
34#include <memory>
35#include <numeric> // for std::partial_sum
36#include <set>
37
38
39// Anonymous namespace for implementation details.
40namespace {
41
43using libMesh::Number;
45
46// Comments:
47// ---------
48// - The max_io_blksize governs how many nodes or elements will be
49// treated as a single block when performing parallel IO on large
50// systems.
51// - This parameter only loosely affects the size of the actual IO
52// buffer as this depends on the number of components a given
53// variable has for the nodes/elements in the block.
54// - When reading/writing each processor uses an ID map which is
55// 3*io_blksize*sizeof(dof_id_type) bytes long, so with unsigned int
56// and // io_blksize=256000 we would expect that buffer alone to be
57// ~3Mb.
58// - In general, an increase in max_io_blksize should increase the
59// efficiency of large parallel read/writes by reducing the number
60// of MPI messages at the expense of memory.
61// - If the library exhausts memory during IO you might reduce this
62// parameter.
63
64const std::size_t max_io_blksize = 256000;
65
69template <typename InValType>
70class ThreadedIO
71{
72private:
73 libMesh::Xdr & _io;
74 std::vector<InValType> & _data;
75
76public:
77 ThreadedIO (libMesh::Xdr & io, std::vector<InValType> & data) :
78 _io(io),
79 _data(data)
80 {}
81
82 void operator()()
83 {
84 if (_data.empty()) return;
85 _io.data_stream (_data.data(), cast_int<unsigned int>(_data.size()));
86 }
87};
88}
89
90
91namespace libMesh
92{
93
94
95// ------------------------------------------------------------
96// System class implementation
98 std::string_view version,
99 const bool read_header_in,
100 const bool read_additional_data,
101 const bool read_legacy_format)
102{
103 // This method implements the input of a
104 // System object, embedded in the output of
105 // an EquationSystems<T_sys>. This warrants some
106 // documentation. The output file essentially
107 // consists of 5 sections:
108 //
109 // for this system
110 //
111 // 5.) The number of variables in the system (unsigned int)
112 //
113 // for each variable in the system
114 //
115 // 6.) The name of the variable (string)
116 //
117 // 6.1.) Variable subdomains
118 //
119 // 7.) Combined in an FEType:
120 // - The approximation order(s) of the variable
121 // (Order Enum, cast to int/s)
122 // - The finite element family/ies of the variable
123 // (FEFamily Enum, cast to int/s)
124 //
125 // end variable loop
126 //
127 // 8.) The number of additional vectors (unsigned int),
128 //
129 // for each additional vector in the system object
130 //
131 // 9.) the name of the additional vector (string)
132 //
133 // end system
134 libmesh_assert (io.reading());
135
136 // Possibly clear data structures and start from scratch.
137 if (read_header_in)
138 this->clear ();
139
140 // Figure out if we need to read infinite element information.
141 // This will be true if the version string contains " with infinite elements"
142 const bool read_ifem_info =
143 Utility::contains(version, " with infinite elements") ||
144 libMesh::on_command_line ("--read-ifem-systems");
145
146
147 {
148 // 5.)
149 // Read the number of variables in the system
150 unsigned int nv=0;
151 if (this->processor_id() == 0)
152 io.data (nv);
153 this->comm().broadcast(nv);
154
155 _written_var_indices.clear();
156 _written_var_indices.resize(nv, 0);
157
158 for (unsigned int var=0; var<nv; var++)
159 {
160 // 6.)
161 // Read the name of the var-th variable
162 std::string var_name;
163 if (this->processor_id() == 0)
164 io.data (var_name);
165 this->comm().broadcast(var_name);
166
167 // 6.1.)
168 std::set<subdomain_id_type> domains;
169 if (io.version() >= LIBMESH_VERSION_ID(0,7,2))
170 {
171 std::vector<subdomain_id_type> domain_array;
172 if (this->processor_id() == 0)
173 io.data (domain_array);
174 for (const auto & id : domain_array)
175 domains.insert(id);
176 }
177 this->comm().broadcast(domains);
178
179 // 7.)
180 // Read the approximation order(s) of the var-th variable
181 int order=0;
182 if (this->processor_id() == 0)
183 io.data (order);
184 this->comm().broadcast(order);
185
186
187 // do the same for infinite element radial_order
188 int rad_order=0;
189 if (read_ifem_info)
190 {
191 if (this->processor_id() == 0)
192 io.data(rad_order);
193 this->comm().broadcast(rad_order);
194 }
195
196 // Read the finite element type of the var-th variable
197 int fam=0;
198 if (this->processor_id() == 0)
199 io.data (fam);
200 this->comm().broadcast(fam);
201 FEType type;
202 type.order = static_cast<Order>(order);
203 type.family = static_cast<FEFamily>(fam);
204
205 // Check for incompatibilities. The shape function indexing was
206 // changed for the monomial and xyz finite element families to
207 // simplify extension to arbitrary p. The consequence is that
208 // old restart files will not be read correctly. This is expected
209 // to be an unlikely occurrence, but catch it anyway.
210 if (read_legacy_format)
211 if ((type.family == MONOMIAL || type.family == XYZ) &&
212 ((type.order.get_order() > 2 && this->get_mesh().mesh_dimension() == 2) ||
213 (type.order.get_order() > 1 && this->get_mesh().mesh_dimension() == 3)))
214 {
215 libmesh_here();
216 libMesh::out << "*****************************************************************\n"
217 << "* WARNING: reading a potentially incompatible restart file!!! *\n"
218 << "* contact libmesh-users@lists.sourceforge.net for more details *\n"
219 << "*****************************************************************"
220 << std::endl;
221 }
222
223 // Read additional information for infinite elements
224 int radial_fam=0;
225 int i_map=0;
226 if (read_ifem_info)
227 {
228 if (this->processor_id() == 0)
229 io.data (radial_fam);
230 this->comm().broadcast(radial_fam);
231 if (this->processor_id() == 0)
232 io.data (i_map);
233 this->comm().broadcast(i_map);
234 }
235
236#ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
237
238 type.radial_order = static_cast<Order>(rad_order);
239 type.radial_family = static_cast<FEFamily>(radial_fam);
240 type.inf_map = static_cast<InfMapType>(i_map);
241
242#endif
243
244 if (read_header_in)
245 {
246 if (domains.empty())
247 _written_var_indices[var] = this->add_variable (var_name, type);
248 else
249 _written_var_indices[var] = this->add_variable (var_name, type, &domains);
250 }
251 else
252 _written_var_indices[var] = this->variable_number(var_name);
253 }
254 }
255
256 // 8.)
257 // Read the number of additional vectors.
258 unsigned int nvecs=0;
259 if (this->processor_id() == 0)
260 io.data (nvecs);
261 this->comm().broadcast(nvecs);
262
263 // If nvecs > 0, this means that write_additional_data
264 // was true when this file was written. We will need to
265 // make use of this fact later.
266 this->_additional_data_written = nvecs;
267
268 for (unsigned int vec=0; vec<nvecs; vec++)
269 {
270 // 9.)
271 // Read the name of the vec-th additional vector
272 std::string vec_name;
273 if (this->processor_id() == 0)
274 io.data (vec_name);
275 this->comm().broadcast(vec_name);
276 if (io.version() >= LIBMESH_VERSION_ID(1,7,0))
277 {
278 int vec_projection = 0;
279 if (this->processor_id() == 0)
280 io.data (vec_projection);
281 this->comm().broadcast(vec_projection);
282 int vec_type;
283 if (this->processor_id() == 0)
284 io.data (vec_type);
285 this->comm().broadcast(vec_type);
286
287 if (read_additional_data)
288 this->add_vector(vec_name, bool(vec_projection), ParallelType(vec_type));
289 }
290 else if (read_additional_data)
291 // Systems now can handle adding post-initialization vectors
292 // libmesh_assert(this->_can_add_vectors);
293 // Some systems may have added their own vectors already
294 // libmesh_assert_equal_to (this->_vectors.count(vec_name), 0);
295 this->add_vector(vec_name);
296 }
297}
298
299
300
301template <typename InValType>
303 const bool read_additional_data)
304{
324 // PerfLog pl("IO Performance",false);
325 // pl.push("read_parallel_data");
326 [[maybe_unused]] dof_id_type total_read_size = 0;
327
328 libmesh_assert (io.reading());
329 libmesh_assert (io.is_open());
330
331 // build the ordered nodes and element maps.
332 // when writing/reading parallel files we need to iterate
333 // over our nodes/elements in order of increasing global id().
334 // however, this is not guaranteed to be ordering we obtain
335 // by using the node_iterators/element_iterators directly.
336 // so build a set, sorted by id(), that provides the ordering.
337 // further, for memory economy build the set but then transfer
338 // its contents to vectors, which will be sorted.
339 std::vector<const DofObject *> ordered_nodes, ordered_elements;
340 {
341 std::set<const DofObject *, CompareDofObjectsByID>
342 ordered_nodes_set (this->get_mesh().local_nodes_begin(),
343 this->get_mesh().local_nodes_end());
344
345 ordered_nodes.insert(ordered_nodes.end(),
346 ordered_nodes_set.begin(),
347 ordered_nodes_set.end());
348 }
349 {
350 std::set<const DofObject *, CompareDofObjectsByID>
351 ordered_elements_set (this->get_mesh().local_elements_begin(),
352 this->get_mesh().local_elements_end());
353
354 ordered_elements.insert(ordered_elements.end(),
355 ordered_elements_set.begin(),
356 ordered_elements_set.end());
357 }
358
359 // std::vector<Number> io_buffer;
360 std::vector<InValType> io_buffer;
361
362 // 9.)
363 //
364 // Actually read the solution components
365 // for the ith system to disk
366 io.data(io_buffer);
367
368 total_read_size += cast_int<dof_id_type>(io_buffer.size());
369
370 const unsigned int sys_num = this->number();
371 const unsigned int nv = cast_int<unsigned int>
372 (this->_written_var_indices.size());
373 libmesh_assert_less_equal (nv, this->n_vars());
374
375 dof_id_type cnt=0;
376
377 // Loop over each non-SCALAR variable and each node, and read out the value.
378 for (unsigned int data_var=0; data_var<nv; data_var++)
379 {
380 const unsigned int var = _written_var_indices[data_var];
381 if (this->variable(var).type().family != SCALAR)
382 {
383 // First read the node DOF values
384 for (const auto & node : ordered_nodes)
385 for (auto comp : make_range(node->n_comp(sys_num,var)))
386 {
387 libmesh_assert_not_equal_to (node->dof_number(sys_num, var, comp),
389 libmesh_assert_less (cnt, io_buffer.size());
390 this->solution->set(node->dof_number(sys_num, var, comp), io_buffer[cnt++]);
391 }
392
393 // Then read the element DOF values
394 for (const auto & elem : ordered_elements)
395 for (auto comp : make_range(elem->n_comp(sys_num,var)))
396 {
397 libmesh_assert_not_equal_to (elem->dof_number(sys_num, var, comp),
399 libmesh_assert_less (cnt, io_buffer.size());
400 this->solution->set(elem->dof_number(sys_num, var, comp), io_buffer[cnt++]);
401 }
402 }
403 }
404
405 // Finally, read the SCALAR variables on the last processor
406 for (unsigned int data_var=0; data_var<nv; data_var++)
407 {
408 const unsigned int var = _written_var_indices[data_var];
409 if (this->variable(var).type().family == SCALAR)
410 {
411 if (this->processor_id() == (this->n_processors()-1))
412 {
413 const DofMap & dof_map = this->get_dof_map();
414 std::vector<dof_id_type> SCALAR_dofs;
415 dof_map.SCALAR_dof_indices(SCALAR_dofs, var);
416
417 for (auto dof : SCALAR_dofs)
418 this->solution->set(dof, io_buffer[cnt++]);
419 }
420 }
421 }
422
423 // And we're done setting solution entries
424 this->solution->close();
425
426 // For each additional vector, simply go through the list.
427 // ONLY attempt to do this IF additional data was actually
428 // written to the file for this system (controlled by the
429 // _additional_data_written flag).
430 if (this->_additional_data_written)
431 {
432 const std::size_t nvecs = this->_vectors.size();
433
434 // If the number of additional vectors written is non-zero, and
435 // the number of additional vectors we have is non-zero, and
436 // they don't match, then something is wrong and we can't be
437 // sure we're reading data into the correct places.
438 if (read_additional_data && nvecs &&
439 nvecs != this->_additional_data_written)
440 libmesh_error_msg
441 ("Additional vectors in file do not match system");
442
443 auto pos = _vectors.begin();
444
445 for (std::size_t i = 0; i != this->_additional_data_written; ++i)
446 {
447 cnt=0;
448 io_buffer.clear();
449
450 // 10.)
451 //
452 // Actually read the additional vector components
453 // for the ith system from disk
454 io.data(io_buffer);
455
456 total_read_size += cast_int<dof_id_type>(io_buffer.size());
457
458 // If read_additional_data==true and we have additional vectors,
459 // then we will keep this vector data; otherwise we are going to
460 // throw it away.
461 if (read_additional_data && nvecs)
462 {
463 // Loop over each non-SCALAR variable and each node, and read out the value.
464 for (unsigned int data_var=0; data_var<nv; data_var++)
465 {
466 const unsigned int var = _written_var_indices[data_var];
467 if (this->variable(var).type().family != SCALAR)
468 {
469 // First read the node DOF values
470 for (const auto & node : ordered_nodes)
471 for (auto comp : make_range(node->n_comp(sys_num,var)))
472 {
473 libmesh_assert_not_equal_to (node->dof_number(sys_num, var, comp),
475 libmesh_assert_less (cnt, io_buffer.size());
476 pos->second->set(node->dof_number(sys_num, var, comp), io_buffer[cnt++]);
477 }
478
479 // Then read the element DOF values
480 for (const auto & elem : ordered_elements)
481 for (auto comp : make_range(elem->n_comp(sys_num,var)))
482 {
483 libmesh_assert_not_equal_to (elem->dof_number(sys_num, var, comp),
485 libmesh_assert_less (cnt, io_buffer.size());
486 pos->second->set(elem->dof_number(sys_num, var, comp), io_buffer[cnt++]);
487 }
488 }
489 }
490
491 // Finally, read the SCALAR variables on the last processor
492 for (unsigned int data_var=0; data_var<nv; data_var++)
493 {
494 const unsigned int var = _written_var_indices[data_var];
495 if (this->variable(var).type().family == SCALAR)
496 {
497 if (this->processor_id() == (this->n_processors()-1))
498 {
499 const DofMap & dof_map = this->get_dof_map();
500 std::vector<dof_id_type> SCALAR_dofs;
501 dof_map.SCALAR_dof_indices(SCALAR_dofs, var);
502
503 for (auto dof : SCALAR_dofs)
504 pos->second->set(dof, io_buffer[cnt++]);
505 }
506 }
507 }
508
509 // And we're done setting entries for this variable
510 pos->second->close();
511 }
512
513 // If we've got vectors then we need to be iterating through
514 // those too
515 if (pos != this->_vectors.end())
516 ++pos;
517 }
518 }
519
520 // const Real
521 // dt = pl.get_elapsed_time(),
522 // rate = total_read_size*sizeof(Number)/dt;
523
524 // libMesh::err << "Read " << total_read_size << " \"Number\" values\n"
525 // << " Elapsed time = " << dt << '\n'
526 // << " Rate = " << rate/1.e6 << "(MB/sec)\n\n";
527
528 // pl.pop("read_parallel_data");
529}
530
531
532template <typename InValType>
534 const bool read_additional_data)
535{
536 // This method implements the input of the vectors
537 // contained in this System object, embedded in the
538 // output of an EquationSystems<T_sys>.
539 //
540 // 10.) The global solution vector, re-ordered to be node-major
541 // (More on this later.)
542 //
543 // for each additional vector in the object
544 //
545 // 11.) The global additional vector, re-ordered to be
546 // node-major (More on this later.)
547 parallel_object_only();
548 std::string comment;
549
550 // PerfLog pl("IO Performance",false);
551 // pl.push("read_serialized_data");
552 // std::size_t total_read_size = 0;
553
554 // 10.)
555 // Read the global solution vector
556 {
557 // total_read_size +=
558 this->read_serialized_vector<InValType>(io, this->solution.get());
559
560 // get the comment
561 if (this->processor_id() == 0)
562 io.comment (comment);
563 }
564
565 // 11.)
566 // Only read additional vectors if data is available, and only use
567 // that data to fill our vectors if the user requested it.
568 if (this->_additional_data_written)
569 {
570 const std::size_t nvecs = this->_vectors.size();
571
572 // If the number of additional vectors written is non-zero, and
573 // the number of additional vectors we have is non-zero, and
574 // they don't match, then we can't read additional vectors
575 // and be sure we're reading data into the correct places.
576 if (read_additional_data && nvecs &&
577 nvecs != this->_additional_data_written)
578 libmesh_error_msg
579 ("Additional vectors in file do not match system");
580
581 auto pos = _vectors.begin();
582
583 for (std::size_t i = 0; i != this->_additional_data_written; ++i)
584 {
585 // Read data, but only put it into a vector if we've been
586 // asked to and if we have a corresponding vector to read.
587
588 // total_read_size +=
589 this->read_serialized_vector<InValType>
590 (io, (read_additional_data && nvecs) ? pos->second.get() : nullptr);
591
592 // get the comment
593 if (this->processor_id() == 0)
594 io.comment (comment);
595
596
597 // If we've got vectors then we need to be iterating through
598 // those too
599 if (pos != this->_vectors.end())
600 ++pos;
601 }
602 }
603
604 // const Real
605 // dt = pl.get_elapsed_time(),
606 // rate = total_read_size*sizeof(Number)/dt;
607
608 // libMesh::out << "Read " << total_read_size << " \"Number\" values\n"
609 // << " Elapsed time = " << dt << '\n'
610 // << " Rate = " << rate/1.e6 << "(MB/sec)\n\n";
611
612 // pl.pop("read_serialized_data");
613}
614
615
616
617template <typename iterator_type, typename InValType>
619 const iterator_type begin,
620 const iterator_type end,
621 const InValType ,
622 Xdr & io,
623 const std::vector<NumericVector<Number> *> & vecs,
624 const unsigned int var_to_read) const
625{
626 //-------------------------------------------------------
627 // General order: (IO format 0.7.4 & greater)
628 //
629 // for (objects ...)
630 // for (vecs ....)
631 // for (vars ....)
632 // for (comps ...)
633 //
634 // where objects are nodes or elements, sorted to be
635 // partition independent,
636 // vecs are one or more *identically distributed* solution
637 // coefficient vectors, vars are one or more variables
638 // to write, and comps are all the components for said
639 // vars on the object.
640
641 // variables to read. Unless specified otherwise, defaults to _written_var_indices.
642 std::vector<unsigned int> vars_to_read (_written_var_indices);
643
644 if (var_to_read != libMesh::invalid_uint)
645 vars_to_read.assign({var_to_read});
646
647 const unsigned int
648 sys_num = this->number(),
649 num_vecs = cast_int<unsigned int>(vecs.size());
650 const dof_id_type
651 io_blksize = cast_int<dof_id_type>(std::min(max_io_blksize, static_cast<std::size_t>(n_objs))),
652 num_blks = cast_int<unsigned int>(std::ceil(static_cast<double>(n_objs)/
653 static_cast<double>(io_blksize)));
654
655 libmesh_assert_less_equal (_written_var_indices.size(), this->n_vars());
656
657 std::size_t n_read_values=0;
658
659 std::vector<std::vector<dof_id_type>> xfer_ids(num_blks); // The global IDs and # of components for the local objects in all blocks
660 std::vector<std::vector<Number>> recv_vals(num_blks); // The raw values for the local objects in all blocks
661 std::vector<Parallel::Request>
662 id_requests(num_blks), val_requests(num_blks);
663 std::vector<Parallel::MessageTag>
664 id_tags(num_blks), val_tags(num_blks);
665
666 // ------------------------------------------------------
667 // First pass - count the number of objects in each block
668 // traverse all the objects and figure out which block they
669 // will ultimately live in.
670 std::vector<std::size_t>
671 xfer_ids_size (num_blks,0),
672 recv_vals_size (num_blks,0);
673
674
675 for (iterator_type it=begin; it!=end; ++it)
676 {
677 const dof_id_type
678 id = (*it)->id(),
679 block = id/io_blksize;
680
681 libmesh_assert_less (block, num_blks);
682
683 xfer_ids_size[block] += 2; // for each object, we send its id, as well as the total number of components for all variables
684
685 dof_id_type n_comp_tot=0;
686 for (const auto & var : vars_to_read)
687 n_comp_tot += (*it)->n_comp(sys_num, var); // for each variable, we will receive the nonzero components
688
689 recv_vals_size[block] += n_comp_tot*num_vecs;
690 }
691
692 // knowing the recv_vals_size[block] for each processor allows
693 // us to sum them and find the global size for each block.
694 std::vector<std::size_t> tot_vals_size(recv_vals_size);
695 this->comm().sum (tot_vals_size);
696
697
698 //------------------------------------------
699 // Collect the ids & number of values needed
700 // for all local objects, binning them into
701 // 'blocks' that will be sent to processor 0
702 for (dof_id_type blk=0; blk<num_blks; blk++)
703 {
704 // Each processor should build up its transfer buffers for its
705 // local objects in [first_object,last_object).
706 const dof_id_type
707 first_object = blk*io_blksize,
708 last_object = std::min(cast_int<dof_id_type>((blk+1)*io_blksize), n_objs);
709
710 // convenience
711 std::vector<dof_id_type> & ids (xfer_ids[blk]);
712 std::vector<Number> & vals (recv_vals[blk]);
713
714 // we now know the number of values we will store for each block,
715 // so we can do efficient preallocation
716 ids.clear(); ids.reserve (xfer_ids_size[blk]);
717 vals.resize(recv_vals_size[blk]);
718
719#ifdef DEBUG
720 std::unordered_set<dof_id_type> seen_ids;
721#endif
722
723 if (recv_vals_size[blk] != 0) // only if there are nonzero values to receive
724 for (iterator_type it=begin; it!=end; ++it)
725 {
726 dof_id_type id = (*it)->id();
727#ifdef DEBUG
728 // Any renumbering tricks should not have given us any
729 // duplicate ids.
730 libmesh_assert(!seen_ids.count(id));
731 seen_ids.insert(id);
732#endif
733
734 if ((id >= first_object) && // object in [first_object,last_object)
735 (id < last_object))
736 {
737 ids.push_back(id);
738
739 unsigned int n_comp_tot=0;
740
741 for (const auto & var : vars_to_read)
742 n_comp_tot += (*it)->n_comp(sys_num, var);
743
744 ids.push_back (n_comp_tot*num_vecs);
745 }
746 }
747
748#ifdef LIBMESH_HAVE_MPI
749 id_tags[blk] = this->comm().get_unique_tag(100*num_blks + blk);
750 val_tags[blk] = this->comm().get_unique_tag(200*num_blks + blk);
751
752 // nonblocking send the data for this block
753 this->comm().send (0, ids, id_requests[blk], id_tags[blk]);
754
755 // Go ahead and post the receive too
756 this->comm().receive (0, vals, val_requests[blk], val_tags[blk]);
757#endif
758 }
759
760 //---------------------------------------------------
761 // Here processor 0 will read and distribute the data.
762 // We have to do this block-wise to ensure that we
763 // do not exhaust memory on processor 0.
764
765 // give these variables scope outside the block to avoid reallocation
766 std::vector<std::vector<dof_id_type>> recv_ids (this->n_processors());
767 std::vector<std::vector<Number>> send_vals (this->n_processors());
768 std::vector<Parallel::Request> reply_requests (this->n_processors());
769 std::vector<unsigned int> obj_val_offsets; // map to traverse entry-wise rather than processor-wise
770 std::vector<Number> input_vals; // The input buffer for the current block
771 std::vector<InValType> input_vals_tmp; // The input buffer for the current block
772
773 for (dof_id_type blk=0; blk<num_blks; blk++)
774 {
775 // Each processor should build up its transfer buffers for its
776 // local objects in [first_object,last_object).
777 const dof_id_type
778 first_object = blk*io_blksize,
779 last_object = std::min(cast_int<dof_id_type>((blk+1)*io_blksize), n_objs),
780 n_objects_blk = last_object - first_object;
781
782 // Processor 0 has a special job. It needs to gather the requested indices
783 // in [first_object,last_object) from all processors, read the data from
784 // disk, and reply
785 if (this->processor_id() == 0)
786 {
787 // we know the input buffer size for this block and can begin reading it now
788 input_vals.resize(tot_vals_size[blk]);
789 input_vals_tmp.resize(tot_vals_size[blk]);
790
791 // a ThreadedIO object to perform asynchronous file IO
792 ThreadedIO<InValType> threaded_io(io, input_vals_tmp);
793 Threads::Thread async_io(threaded_io);
794
795 // offset array. this will define where each object's values
796 // map into the actual input_vals buffer. this must get
797 // 0-initialized because 0-component objects are not actually sent
798 obj_val_offsets.resize (n_objects_blk); std::fill (obj_val_offsets.begin(), obj_val_offsets.end(), 0);
799 recv_vals_size.resize(this->n_processors()); // reuse this to count how many values are going to each processor
800
801#ifndef NDEBUG
802 std::size_t n_vals_blk = 0;
803#endif
804
805 // loop over all processors and process their index request
806 for (processor_id_type comm_step=0, tnp=this->n_processors(); comm_step != tnp; ++comm_step)
807 {
808#ifdef LIBMESH_HAVE_MPI
809 // blocking receive indices for this block, imposing no particular order on processor
810 Parallel::Status id_status (this->comm().probe (Parallel::any_source, id_tags[blk]));
811 std::vector<dof_id_type> & ids (recv_ids[id_status.source()]);
812 std::size_t & n_vals_proc (recv_vals_size[id_status.source()]);
813 this->comm().receive (id_status.source(), ids, id_tags[blk]);
814#else
815 // straight copy without MPI
816 std::vector<dof_id_type> & ids (recv_ids[0]);
817 std::size_t & n_vals_proc (recv_vals_size[0]);
818 ids = xfer_ids[blk];
819#endif
820
821 n_vals_proc = 0;
822
823 // note its possible we didn't receive values for objects in
824 // this block if they have no components allocated.
825 for (std::size_t idx=0, sz=ids.size(); idx<sz; idx+=2)
826 {
827 const dof_id_type
828 local_idx = ids[idx+0]-first_object,
829 n_vals_tot_allvecs = ids[idx+1];
830
831 libmesh_assert_less (local_idx, n_objects_blk);
832
833 obj_val_offsets[local_idx] = n_vals_tot_allvecs;
834 n_vals_proc += n_vals_tot_allvecs;
835 }
836
837#ifndef NDEBUG
838 n_vals_blk += n_vals_proc;
839#endif
840 }
841
842 // We need the offsets into the input_vals vector for each object.
843 // fortunately, this is simply the partial sum of the total number
844 // of components for each object
845 std::partial_sum(obj_val_offsets.begin(), obj_val_offsets.end(),
846 obj_val_offsets.begin());
847
848 libmesh_assert_equal_to (n_vals_blk, obj_val_offsets.back());
849 libmesh_assert_equal_to (n_vals_blk, tot_vals_size[blk]);
850
851 // Wait for read completion
852 async_io.join();
853 // now copy the values back to the main vector for transfer
854 for (auto i_val : index_range(input_vals))
855 input_vals[i_val] = input_vals_tmp[i_val];
856
857 n_read_values += input_vals.size();
858
859 // pack data replies for each processor
860 for (auto proc : make_range(this->n_processors()))
861 {
862 const std::vector<dof_id_type> & ids (recv_ids[proc]);
863 std::vector<Number> & vals (send_vals[proc]);
864 const std::size_t & n_vals_proc (recv_vals_size[proc]);
865
866 vals.clear(); vals.reserve(n_vals_proc);
867
868 for (std::size_t idx=0, sz=ids.size(); idx<sz; idx+=2)
869 {
870 const dof_id_type
871 local_idx = ids[idx+0]-first_object,
872 n_vals_tot_allvecs = ids[idx+1];
873
874 std::vector<Number>::const_iterator in_vals(input_vals.begin());
875 if (local_idx != 0)
876 std::advance (in_vals, obj_val_offsets[local_idx-1]);
877
878 for (unsigned int val=0; val<n_vals_tot_allvecs; val++, ++in_vals)
879 {
880 libmesh_assert (in_vals != input_vals.end());
881 //libMesh::out << "*in_vals=" << *in_vals << '\n';
882 vals.push_back(*in_vals);
883 }
884 }
885
886#ifdef LIBMESH_HAVE_MPI
887 // send the relevant values to this processor
888 this->comm().send (proc, vals, reply_requests[proc], val_tags[blk]);
889#else
890 recv_vals[blk] = vals;
891#endif
892 }
893 } // end processor 0 read/reply
894
895 // all processors complete the (already posted) read for this block
896 {
897 Parallel::wait (val_requests[blk]);
898
899 const std::vector<Number> & vals (recv_vals[blk]);
900 std::vector<Number>::const_iterator val_it(vals.begin());
901
902 if (!recv_vals[blk].empty()) // nonzero values to receive
903 for (iterator_type it=begin; it!=end; ++it)
904 if (((*it)->id() >= first_object) && // object in [first_object,last_object)
905 ((*it)->id() < last_object))
906 // unpack & set the values
907 for (auto & vec : vecs)
908 for (const auto & var : vars_to_read)
909 {
910 const unsigned int n_comp = (*it)->n_comp(sys_num, var);
911
912 for (unsigned int comp=0; comp<n_comp; comp++, ++val_it)
913 {
914 const dof_id_type dof_index = (*it)->dof_number (sys_num, var, comp);
915 libmesh_assert (val_it != vals.end());
916 if (vec)
917 {
918 libmesh_assert_greater_equal (dof_index, vec->first_local_index());
919 libmesh_assert_less (dof_index, vec->last_local_index());
920 //libMesh::out << "dof_index, *val_it = \t" << dof_index << ", " << *val_it << '\n';
921 vec->set (dof_index, *val_it);
922 }
923 }
924 }
925 }
926
927 // processor 0 needs to make sure all replies have been handed off
928 if (this->processor_id () == 0)
929 Parallel::wait(reply_requests);
930 }
931
932 Parallel::wait(id_requests);
933
934 return n_read_values;
935}
936
937
938
939unsigned int System::read_SCALAR_dofs (const unsigned int var,
940 Xdr & io,
941 NumericVector<Number> * vec) const
942{
943 unsigned int n_assigned_vals = 0; // the number of values assigned, this will be returned.
944
945 // Processor 0 will read the block from the buffer stream and send it to the last processor
946 const unsigned int n_SCALAR_dofs = this->variable(var).type().order.get_order();
947 std::vector<Number> input_buffer(n_SCALAR_dofs);
948 if (this->processor_id() == 0)
949 io.data_stream(input_buffer.data(), n_SCALAR_dofs);
950
951#ifdef LIBMESH_HAVE_MPI
952 if (this->n_processors() > 1)
953 {
954 const Parallel::MessageTag val_tag = this->comm().get_unique_tag();
955
956 // Post the receive on the last processor
957 if (this->processor_id() == (this->n_processors()-1))
958 this->comm().receive(0, input_buffer, val_tag);
959
960 // Send the data to processor 0
961 if (this->processor_id() == 0)
962 this->comm().send(this->n_processors()-1, input_buffer, val_tag);
963 }
964#endif
965
966 // Finally, set the SCALAR values
967 if (this->processor_id() == (this->n_processors()-1))
968 {
969 const DofMap & dof_map = this->get_dof_map();
970 std::vector<dof_id_type> SCALAR_dofs;
971 dof_map.SCALAR_dof_indices(SCALAR_dofs, var);
972
973 for (auto i : index_range(SCALAR_dofs))
974 {
975 if (vec)
976 vec->set (SCALAR_dofs[i], input_buffer[i]);
977 ++n_assigned_vals;
978 }
979 }
980
981 return n_assigned_vals;
982}
983
984
985template <typename InValType>
988{
989 parallel_object_only();
990
991#ifndef NDEBUG
992 // In parallel we better be reading a parallel vector -- if not
993 // we will not set all of its components below!!
994 if (this->n_processors() > 1 && vec)
995 {
996 libmesh_assert (vec->type() == PARALLEL ||
997 vec->type() == GHOSTED);
998 }
999#endif
1000
1001 libmesh_assert (io.reading());
1002
1003 // vector length
1004 unsigned int vector_length=0; // FIXME? size_t would break binary compatibility...
1005#ifndef NDEBUG
1006 std::size_t n_assigned_vals=0;
1007#endif
1008
1009 // Get the buffer size
1010 if (this->processor_id() == 0)
1011 io.data(vector_length, "# vector length");
1012 this->comm().broadcast(vector_length);
1013
1014 const unsigned int nv = cast_int<unsigned int>
1015 (this->_written_var_indices.size());
1016 const dof_id_type
1017 n_nodes = this->get_mesh().n_nodes(),
1018 n_elem = this->get_mesh().n_elem();
1019
1020 libmesh_assert_less_equal (nv, this->n_vars());
1021
1022 // for newer versions, read variables node/elem major
1023 if (io.version() >= LIBMESH_VERSION_ID(0,7,4))
1024 {
1025 //---------------------------------
1026 // Collect the values for all nodes
1027#ifndef NDEBUG
1028 n_assigned_vals +=
1029#endif
1031 this->get_mesh().local_nodes_begin(),
1032 this->get_mesh().local_nodes_end(),
1033 InValType(),
1034 io,
1035 std::vector<NumericVector<Number> *> (1,vec));
1036
1037
1038 //------------------------------------
1039 // Collect the values for all elements
1040#ifndef NDEBUG
1041 n_assigned_vals +=
1042#endif
1044 this->get_mesh().local_elements_begin(),
1045 this->get_mesh().local_elements_end(),
1046 InValType(),
1047 io,
1048 std::vector<NumericVector<Number> *> (1,vec));
1049 }
1050
1051 // for older versions, read variables var-major
1052 else
1053 {
1054 // Loop over each variable in the system, and then each node/element in the mesh.
1055 for (unsigned int data_var=0; data_var<nv; data_var++)
1056 {
1057 const unsigned int var = _written_var_indices[data_var];
1058 if (this->variable(var).type().family != SCALAR)
1059 {
1060 //---------------------------------
1061 // Collect the values for all nodes
1062#ifndef NDEBUG
1063 n_assigned_vals +=
1064#endif
1066 this->get_mesh().local_nodes_begin(),
1067 this->get_mesh().local_nodes_end(),
1068 InValType(),
1069 io,
1070 std::vector<NumericVector<Number> *> (1,vec),
1071 var);
1072
1073
1074 //------------------------------------
1075 // Collect the values for all elements
1076#ifndef NDEBUG
1077 n_assigned_vals +=
1078#endif
1080 this->get_mesh().local_elements_begin(),
1081 this->get_mesh().local_elements_end(),
1082 InValType(),
1083 io,
1084 std::vector<NumericVector<Number> *> (1,vec),
1085 var);
1086 } // end variable loop
1087 }
1088 }
1089
1090 //-------------------------------------------
1091 // Finally loop over all the SCALAR variables
1092 for (unsigned int data_var=0; data_var<nv; data_var++)
1093 {
1094 const unsigned int var = _written_var_indices[data_var];
1095 if (this->variable(var).type().family == SCALAR)
1096 {
1097#ifndef NDEBUG
1098 n_assigned_vals +=
1099#endif
1100 this->read_SCALAR_dofs (var, io, vec);
1101 }
1102 }
1103
1104 if (vec)
1105 vec->close();
1106
1107#ifndef NDEBUG
1108 this->comm().sum (n_assigned_vals);
1109 libmesh_assert_equal_to (n_assigned_vals, vector_length);
1110#endif
1111
1112 return vector_length;
1113}
1114
1115
1116
1118 std::string_view /* version is currently unused */,
1119 const bool write_additional_data) const
1120{
1154 libmesh_assert (io.writing());
1155
1156
1157 // Only write the header information
1158 // if we are processor 0.
1159 if (this->get_mesh().processor_id() != 0)
1160 return;
1161
1162 std::string comment;
1163
1164 // 5.)
1165 // Write the number of variables in the system
1166
1167 {
1168 // set up the comment
1169 comment = "# No. of Variables in System \"";
1170 comment += this->name();
1171 comment += "\"";
1172
1173 unsigned int nv = this->n_vars();
1174 io.data (nv, comment);
1175 }
1176
1177
1178 for (auto var : make_range(this->n_vars()))
1179 {
1180 // 6.)
1181 // Write the name of the var-th variable
1182 {
1183 // set up the comment
1184 comment = "# Name, Variable No. ";
1185 comment += std::to_string(var);
1186 comment += ", System \"";
1187 comment += this->name();
1188 comment += "\"";
1189
1190 std::string var_name = this->variable_name(var);
1191 io.data (var_name, comment);
1192 }
1193
1194 // 6.1.) Variable subdomains
1195 {
1196 // set up the comment
1197 comment = "# Subdomains, Variable \"";
1198 comment += this->variable_name(var);
1199 comment += "\", System \"";
1200 comment += this->name();
1201 comment += "\"";
1202
1203 const std::set<subdomain_id_type> & domains = this->variable(var).active_subdomains();
1204 std::vector<subdomain_id_type> domain_array;
1205 domain_array.assign(domains.begin(), domains.end());
1206 io.data (domain_array, comment);
1207 }
1208
1209 // 7.)
1210 // Write the approximation order of the var-th variable
1211 // in this system
1212 {
1213 // set up the comment
1214 comment = "# Approximation Order, Variable \"";
1215 comment += this->variable_name(var);
1216 comment += "\", System \"";
1217 comment += this->name();
1218 comment += "\"";
1219
1220 int order = static_cast<int>(this->variable_type(var).order);
1221 io.data (order, comment);
1222 }
1223
1224
1225#ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
1226
1227 // do the same for radial_order
1228 {
1229 comment = "# Radial Approximation Order, Variable \"";
1230 comment += this->variable_name(var);
1231 comment += "\", System \"";
1232 comment += this->name();
1233 comment += "\"";
1234
1235 int rad_order = static_cast<int>(this->variable_type(var).radial_order);
1236 io.data (rad_order, comment);
1237 }
1238
1239#endif
1240
1241 // Write the Finite Element type of the var-th variable
1242 // in this System
1243 {
1244 // set up the comment
1245 comment = "# FE Family, Variable \"";
1246 comment += this->variable_name(var);
1247 comment += "\", System \"";
1248 comment += this->name();
1249 comment += "\"";
1250
1251 const FEType & type = this->variable_type(var);
1252 int fam = static_cast<int>(type.family);
1253 io.data (fam, comment);
1254
1255#ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
1256
1257 comment = "# Radial FE Family, Variable \"";
1258 comment += this->variable_name(var);
1259 comment += "\", System \"";
1260 comment += this->name();
1261 comment += "\"";
1262
1263 int radial_fam = static_cast<int>(type.radial_family);
1264 io.data (radial_fam, comment);
1265
1266 comment = "# Infinite Mapping Type, Variable \"";
1267 comment += this->variable_name(var);
1268 comment += "\", System \"";
1269 comment += this->name();
1270 comment += "\"";
1271
1272 int i_map = static_cast<int>(type.inf_map);
1273 io.data (i_map, comment);
1274#endif
1275 }
1276 } // end of the variable loop
1277
1278 // 8.)
1279 // Write the number of additional vectors in the System.
1280 // If write_additional_data==false, then write zero for
1281 // the number of additional vectors.
1282 {
1283 {
1284 // set up the comment
1285 comment = "# No. of Additional Vectors, System \"";
1286 comment += this->name();
1287 comment += "\"";
1288
1289 unsigned int nvecs = write_additional_data ? this->n_vectors () : 0;
1290 io.data (nvecs, comment);
1291 }
1292
1293 if (write_additional_data)
1294 {
1295 unsigned int cnt=0;
1296 for (const auto & [vec_name, vec] : _vectors)
1297 {
1298 // 9.)
1299 // write the name of the cnt-th additional vector
1300 const std::string dth_vector = std::to_string(cnt++)+"th vector";
1301 comment = "# Name of " + dth_vector;
1302 std::string nonconst_vec_name = vec_name; // Stupid XDR API
1303
1304 io.data (nonconst_vec_name, comment);
1305 int vec_projection = _vector_projections.at(vec_name);
1306 comment = "# Whether to do projections for " + dth_vector;
1307 io.data (vec_projection, comment);
1308 int vec_type = vec->type();
1309 comment = "# Parallel type of " + dth_vector;
1310 io.data (vec_type, comment);
1311 }
1312 }
1313 }
1314}
1315
1316
1317
1319 const bool write_additional_data) const
1320{
1340 // PerfLog pl("IO Performance",false);
1341 // pl.push("write_parallel_data");
1342 // std::size_t total_written_size = 0;
1343
1344 std::string comment;
1345
1346 libmesh_assert (io.writing());
1347
1348 std::vector<Number> io_buffer; io_buffer.reserve(this->solution->local_size());
1349
1350 // build the ordered nodes and element maps.
1351 // when writing/reading parallel files we need to iterate
1352 // over our nodes/elements in order of increasing global id().
1353 // however, this is not guaranteed to be ordering we obtain
1354 // by using the node_iterators/element_iterators directly.
1355 // so build a set, sorted by id(), that provides the ordering.
1356 // further, for memory economy build the set but then transfer
1357 // its contents to vectors, which will be sorted.
1358 std::vector<const DofObject *> ordered_nodes, ordered_elements;
1359 {
1360 std::set<const DofObject *, CompareDofObjectsByID>
1361 ordered_nodes_set (this->get_mesh().local_nodes_begin(),
1362 this->get_mesh().local_nodes_end());
1363
1364 ordered_nodes.insert(ordered_nodes.end(),
1365 ordered_nodes_set.begin(),
1366 ordered_nodes_set.end());
1367 }
1368 {
1369 std::set<const DofObject *, CompareDofObjectsByID>
1370 ordered_elements_set (this->get_mesh().local_elements_begin(),
1371 this->get_mesh().local_elements_end());
1372
1373 ordered_elements.insert(ordered_elements.end(),
1374 ordered_elements_set.begin(),
1375 ordered_elements_set.end());
1376 }
1377
1378 const unsigned int sys_num = this->number();
1379 const unsigned int nv = this->n_vars();
1380
1381 // Loop over each non-SCALAR variable and each node, and write out the value.
1382 for (unsigned int var=0; var<nv; var++)
1383 if (this->variable(var).type().family != SCALAR)
1384 {
1385 // First write the node DOF values
1386 for (const auto & node : ordered_nodes)
1387 for (auto comp : make_range(node->n_comp(sys_num,var)))
1388 {
1389 libmesh_assert_not_equal_to (node->dof_number(sys_num, var, comp),
1391
1392 io_buffer.push_back((*this->solution)(node->dof_number(sys_num, var, comp)));
1393 }
1394
1395 // Then write the element DOF values
1396 for (const auto & elem : ordered_elements)
1397 for (auto comp : make_range(elem->n_comp(sys_num,var)))
1398 {
1399 libmesh_assert_not_equal_to (elem->dof_number(sys_num, var, comp),
1401
1402 io_buffer.push_back((*this->solution)(elem->dof_number(sys_num, var, comp)));
1403 }
1404 }
1405
1406 // Finally, write the SCALAR data on the last processor
1407 for (auto var : make_range(this->n_vars()))
1408 if (this->variable(var).type().family == SCALAR)
1409 {
1410 if (this->processor_id() == (this->n_processors()-1))
1411 {
1412 const DofMap & dof_map = this->get_dof_map();
1413 std::vector<dof_id_type> SCALAR_dofs;
1414 dof_map.SCALAR_dof_indices(SCALAR_dofs, var);
1415
1416 for (auto dof : SCALAR_dofs)
1417 io_buffer.push_back((*this->solution)(dof));
1418 }
1419 }
1420
1421 // 9.)
1422 //
1423 // Actually write the reordered solution vector
1424 // for the ith system to disk
1425
1426 // set up the comment
1427 {
1428 comment = "# System \"";
1429 comment += this->name();
1430 comment += "\" Solution Vector";
1431 }
1432
1433 io.data (io_buffer, comment);
1434
1435 // total_written_size += io_buffer.size();
1436
1437 // Only write additional vectors if wanted
1438 if (write_additional_data)
1439 {
1440 for (auto & [vec_name, vec] : _vectors)
1441 {
1442 io_buffer.clear();
1443 io_buffer.reserve(vec->local_size());
1444
1445 // Loop over each non-SCALAR variable and each node, and write out the value.
1446 for (unsigned int var=0; var<nv; var++)
1447 if (this->variable(var).type().family != SCALAR)
1448 {
1449 // First write the node DOF values
1450 for (const auto & node : ordered_nodes)
1451 for (auto comp : make_range(node->n_comp(sys_num,var)))
1452 {
1453 libmesh_assert_not_equal_to (node->dof_number(sys_num, var, comp),
1455
1456 io_buffer.push_back((*vec)(node->dof_number(sys_num, var, comp)));
1457 }
1458
1459 // Then write the element DOF values
1460 for (const auto & elem : ordered_elements)
1461 for (auto comp : make_range(elem->n_comp(sys_num,var)))
1462 {
1463 libmesh_assert_not_equal_to (elem->dof_number(sys_num, var, comp),
1465
1466 io_buffer.push_back((*vec)(elem->dof_number(sys_num, var, comp)));
1467 }
1468 }
1469
1470 // Finally, write the SCALAR data on the last processor
1471 for (auto var : make_range(this->n_vars()))
1472 if (this->variable(var).type().family == SCALAR)
1473 {
1474 if (this->processor_id() == (this->n_processors()-1))
1475 {
1476 const DofMap & dof_map = this->get_dof_map();
1477 std::vector<dof_id_type> SCALAR_dofs;
1478 dof_map.SCALAR_dof_indices(SCALAR_dofs, var);
1479
1480 for (auto dof : SCALAR_dofs)
1481 io_buffer.push_back((*vec)(dof));
1482 }
1483 }
1484
1485 // 10.)
1486 //
1487 // Actually write the reordered additional vector
1488 // for this system to disk
1489
1490 // set up the comment
1491 {
1492 comment = "# System \"";
1493 comment += this->name();
1494 comment += "\" Additional Vector \"";
1495 comment += vec_name;
1496 comment += "\"";
1497 }
1498
1499 io.data (io_buffer, comment);
1500
1501 // total_written_size += io_buffer.size();
1502 }
1503 }
1504
1505 // const Real
1506 // dt = pl.get_elapsed_time(),
1507 // rate = total_written_size*sizeof(Number)/dt;
1508
1509 // libMesh::err << "Write " << total_written_size << " \"Number\" values\n"
1510 // << " Elapsed time = " << dt << '\n'
1511 // << " Rate = " << rate/1.e6 << "(MB/sec)\n\n";
1512
1513 // pl.pop("write_parallel_data");
1514}
1515
1516
1517
1519 const bool write_additional_data) const
1520{
1534 parallel_object_only();
1535 std::string comment;
1536
1537 // PerfLog pl("IO Performance",false);
1538 // pl.push("write_serialized_data");
1539 // std::size_t total_written_size = 0;
1540
1541 // total_written_size +=
1542 this->write_serialized_vector(io, *this->solution);
1543
1544 // set up the comment
1545 if (this->processor_id() == 0)
1546 {
1547 comment = "# System \"";
1548 comment += this->name();
1549 comment += "\" Solution Vector";
1550
1551 io.comment (comment);
1552 }
1553
1554 // Only write additional vectors if wanted
1555 if (write_additional_data)
1556 {
1557 for (auto & pair : this->_vectors)
1558 {
1559 // total_written_size +=
1560 this->write_serialized_vector(io, *pair.second);
1561
1562 // set up the comment
1563 if (this->processor_id() == 0)
1564 {
1565 comment = "# System \"";
1566 comment += this->name();
1567 comment += "\" Additional Vector \"";
1568 comment += pair.first;
1569 comment += "\"";
1570 io.comment (comment);
1571 }
1572 }
1573 }
1574
1575 // const Real
1576 // dt = pl.get_elapsed_time(),
1577 // rate = total_written_size*sizeof(Number)/dt;
1578
1579 // libMesh::out << "Write " << total_written_size << " \"Number\" values\n"
1580 // << " Elapsed time = " << dt << '\n'
1581 // << " Rate = " << rate/1.e6 << "(MB/sec)\n\n";
1582
1583 // pl.pop("write_serialized_data");
1584
1585
1586
1587
1588 // // test the new method
1589 // {
1590 // std::vector<std::string> names;
1591 // std::vector<NumericVector<Number> *> vectors_to_write;
1592
1593 // names.push_back("Solution Vector");
1594 // vectors_to_write.push_back(this->solution.get());
1595
1596 // // Only write additional vectors if wanted
1597 // if (write_additional_data)
1598 // {
1599 // std::map<std::string, NumericVector<Number> *>::const_iterator
1600 // pos = _vectors.begin();
1601
1602 // for (; pos != this->_vectors.end(); ++pos)
1603 // {
1604 // names.push_back("Additional Vector " + pos->first);
1605 // vectors_to_write.push_back(pos->second);
1606 // }
1607 // }
1608
1609 // total_written_size =
1610 // this->write_serialized_vectors (io, names, vectors_to_write);
1611
1612 // const Real
1613 // dt2 = pl.get_elapsed_time(),
1614 // rate2 = total_written_size*sizeof(Number)/(dt2-dt);
1615
1616 // libMesh::out << "Write (new) " << total_written_size << " \"Number\" values\n"
1617 // << " Elapsed time = " << (dt2-dt) << '\n'
1618 // << " Rate = " << rate2/1.e6 << "(MB/sec)\n\n";
1619
1620 // }
1621}
1622
1623
1624
1625template <typename iterator_type>
1626std::size_t System::write_serialized_blocked_dof_objects (const std::vector<const NumericVector<Number> *> & vecs,
1627 const dof_id_type n_objs,
1628 const iterator_type begin,
1629 const iterator_type end,
1630 Xdr & io,
1631 const unsigned int var_to_write) const
1632{
1633 parallel_object_only();
1634
1635 //-------------------------------------------------------
1636 // General order: (IO format 0.7.4 & greater)
1637 //
1638 // for (objects ...)
1639 // for (vecs ....)
1640 // for (vars ....)
1641 // for (comps ...)
1642 //
1643 // where objects are nodes or elements, sorted to be
1644 // partition independent,
1645 // vecs are one or more *identically distributed* solution
1646 // coefficient vectors, vars are one or more variables
1647 // to write, and comps are all the components for said
1648 // vars on the object.
1649
1650 // We will write all variables unless requested otherwise.
1651 std::vector<unsigned int> vars_to_write(1, var_to_write);
1652
1653 if (var_to_write == libMesh::invalid_uint)
1654 {
1655 vars_to_write.clear(); vars_to_write.reserve(this->n_vars());
1656 for (auto var : make_range(this->n_vars()))
1657 vars_to_write.push_back(var);
1658 }
1659
1660 const dof_id_type io_blksize = cast_int<dof_id_type>
1661 (std::min(max_io_blksize, static_cast<std::size_t>(n_objs)));
1662
1663 const unsigned int
1664 sys_num = this->number(),
1665 num_vecs = cast_int<unsigned int>(vecs.size()),
1666 num_blks = cast_int<unsigned int>(std::ceil(static_cast<double>(n_objs)/
1667 static_cast<double>(io_blksize)));
1668
1669 // libMesh::out << "io_blksize = " << io_blksize
1670 // << ", num_objects = " << n_objs
1671 // << ", num_blks = " << num_blks
1672 // << std::endl;
1673
1674 std::size_t written_length=0; // The numer of values written. This will be returned
1675 std::vector<std::vector<dof_id_type>> xfer_ids(num_blks); // The global IDs and # of components for the local objects in all blocks
1676 std::vector<std::vector<Number>> send_vals(num_blks); // The raw values for the local objects in all blocks
1677 std::vector<Parallel::Request>
1678 id_requests(num_blks), val_requests(num_blks); // send request handle for each block
1679 std::vector<Parallel::MessageTag>
1680 id_tags(num_blks), val_tags(num_blks); // tag number for each block
1681
1682 // ------------------------------------------------------
1683 // First pass - count the number of objects in each block
1684 // traverse all the objects and figure out which block they
1685 // will ultimately live in.
1686 std::vector<unsigned int>
1687 xfer_ids_size (num_blks,0),
1688 send_vals_size (num_blks,0);
1689
1690 for (iterator_type it=begin; it!=end; ++it)
1691 {
1692 const dof_id_type
1693 id = (*it)->id(),
1694 block = id/io_blksize;
1695
1696 libmesh_assert_less (block, num_blks);
1697
1698 xfer_ids_size[block] += 2; // for each object, we store its id, as well as the total number of components for all variables
1699
1700 unsigned int n_comp_tot=0;
1701
1702 for (const auto & var : vars_to_write)
1703 n_comp_tot += (*it)->n_comp(sys_num, var); // for each variable, we will store the nonzero components
1704
1705 send_vals_size[block] += n_comp_tot*num_vecs;
1706 }
1707
1708 //-----------------------------------------
1709 // Collect the values for all local objects,
1710 // binning them into 'blocks' that will be
1711 // sent to processor 0
1712 for (unsigned int blk=0; blk<num_blks; blk++)
1713 {
1714 // libMesh::out << "Writing object block " << blk << std::endl;
1715
1716 // Each processor should build up its transfer buffers for its
1717 // local objects in [first_object,last_object).
1718 const dof_id_type
1719 first_object = blk*io_blksize,
1720 last_object = std::min(cast_int<dof_id_type>((blk+1)*io_blksize), n_objs);
1721
1722 // convenience
1723 std::vector<dof_id_type> & ids (xfer_ids[blk]);
1724 std::vector<Number> & vals (send_vals[blk]);
1725
1726 // we now know the number of values we will store for each block,
1727 // so we can do efficient preallocation
1728 ids.clear(); ids.reserve (xfer_ids_size[blk]);
1729 vals.clear(); vals.reserve (send_vals_size[blk]);
1730
1731 if (send_vals_size[blk] != 0) // only send if we have nonzero components to write
1732 for (iterator_type it=begin; it!=end; ++it)
1733 if (((*it)->id() >= first_object) && // object in [first_object,last_object)
1734 ((*it)->id() < last_object))
1735 {
1736 ids.push_back((*it)->id());
1737
1738 // count the total number of nonzeros transferred for this object
1739 {
1740 unsigned int n_comp_tot=0;
1741
1742 for (const auto & var : vars_to_write)
1743 n_comp_tot += (*it)->n_comp(sys_num, var);
1744
1745 ids.push_back (n_comp_tot*num_vecs); // even if 0 - processor 0 has no way of knowing otherwise...
1746 }
1747
1748 // pack the values to send
1749 for (const auto & vec : vecs)
1750 for (const auto & var : vars_to_write)
1751 {
1752 const unsigned int n_comp = (*it)->n_comp(sys_num, var);
1753
1754 for (unsigned int comp=0; comp<n_comp; comp++)
1755 {
1756 libmesh_assert_greater_equal ((*it)->dof_number(sys_num, var, comp), vec->first_local_index());
1757 libmesh_assert_less ((*it)->dof_number(sys_num, var, comp), vec->last_local_index());
1758 vals.push_back((*vec)((*it)->dof_number(sys_num, var, comp)));
1759 }
1760 }
1761 }
1762
1763#ifdef LIBMESH_HAVE_MPI
1764 id_tags[blk] = this->comm().get_unique_tag(100*num_blks + blk);
1765 val_tags[blk] = this->comm().get_unique_tag(200*num_blks + blk);
1766
1767 // nonblocking send the data for this block
1768 this->comm().send (0, ids, id_requests[blk], id_tags[blk]);
1769 this->comm().send (0, vals, val_requests[blk], val_tags[blk]);
1770#endif
1771 }
1772
1773
1774 if (this->processor_id() == 0)
1775 {
1776 std::vector<std::vector<dof_id_type>> recv_ids (this->n_processors());
1777 std::vector<std::vector<Number>> recv_vals (this->n_processors());
1778 std::vector<unsigned int> obj_val_offsets; // map to traverse entry-wise rather than processor-wise
1779 std::vector<Number> output_vals; // The output buffer for the current block
1780
1781 // a ThreadedIO object to perform asynchronous file IO
1782 ThreadedIO<Number> threaded_io(io, output_vals);
1783 std::unique_ptr<Threads::Thread> async_io;
1784
1785 for (unsigned int blk=0; blk<num_blks; blk++)
1786 {
1787 // Each processor should build up its transfer buffers for its
1788 // local objects in [first_object,last_object).
1789 const dof_id_type
1790 first_object = cast_int<dof_id_type>(blk*io_blksize),
1791 last_object = std::min(cast_int<dof_id_type>((blk+1)*io_blksize), n_objs),
1792 n_objects_blk = last_object - first_object;
1793
1794 // offset array. this will define where each object's values
1795 // map into the actual output_vals buffer. this must get
1796 // 0-initialized because 0-component objects are not actually sent
1797 obj_val_offsets.resize (n_objects_blk); std::fill (obj_val_offsets.begin(), obj_val_offsets.end(), 0);
1798
1799 std::size_t n_val_recvd_blk=0;
1800
1801 // receive this block of data from all processors.
1802 for (processor_id_type comm_step=0, tnp=this->n_processors(); comm_step != tnp; ++comm_step)
1803 {
1804#ifdef LIBMESH_HAVE_MPI
1805 // blocking receive indices for this block, imposing no particular order on processor
1806 Parallel::Status id_status (this->comm().probe (Parallel::any_source, id_tags[blk]));
1807 std::vector<dof_id_type> & ids (recv_ids[id_status.source()]);
1808 this->comm().receive (id_status.source(), ids, id_tags[blk]);
1809#else
1810 std::vector<dof_id_type> & ids (recv_ids[0]);
1811 ids = xfer_ids[blk];
1812#endif
1813
1814 // note its possible we didn't receive values for objects in
1815 // this block if they have no components allocated.
1816 for (std::size_t idx=0, sz=ids.size(); idx<sz; idx+=2)
1817 {
1818 const dof_id_type
1819 local_idx = ids[idx+0]-first_object,
1820 n_vals_tot_allvecs = ids[idx+1];
1821
1822 libmesh_assert_less (local_idx, n_objects_blk);
1823 libmesh_assert_less (local_idx, obj_val_offsets.size());
1824
1825 obj_val_offsets[local_idx] = n_vals_tot_allvecs;
1826 }
1827
1828#ifdef LIBMESH_HAVE_MPI
1829 // blocking receive values for this block, imposing no particular order on processor
1830 Parallel::Status val_status (this->comm().probe (Parallel::any_source, val_tags[blk]));
1831 std::vector<Number> & vals (recv_vals[val_status.source()]);
1832 this->comm().receive (val_status.source(), vals, val_tags[blk]);
1833#else
1834 // straight copy without MPI
1835 std::vector<Number> & vals (recv_vals[0]);
1836 vals = send_vals[blk];
1837#endif
1838
1839 n_val_recvd_blk += vals.size();
1840 }
1841
1842 // We need the offsets into the output_vals vector for each object.
1843 // fortunately, this is simply the partial sum of the total number
1844 // of components for each object
1845 std::partial_sum(obj_val_offsets.begin(), obj_val_offsets.end(),
1846 obj_val_offsets.begin());
1847
1848 // wait on any previous asynchronous IO - this *must* complete before
1849 // we start messing with the output_vals buffer!
1850 if (async_io.get()) async_io->join();
1851
1852 // this is the actual output buffer that will be written to disk.
1853 // at ths point we finally know wha size it will be.
1854 output_vals.resize(n_val_recvd_blk);
1855
1856 // pack data from all processors into output values
1857 for (auto proc : make_range(this->n_processors()))
1858 {
1859 const std::vector<dof_id_type> & ids (recv_ids [proc]);
1860 const std::vector<Number> & vals(recv_vals[proc]);
1861 std::vector<Number>::const_iterator proc_vals(vals.begin());
1862
1863 for (std::size_t idx=0, sz=ids.size(); idx<sz; idx+=2)
1864 {
1865 const dof_id_type
1866 local_idx = ids[idx+0]-first_object,
1867 n_vals_tot_allvecs = ids[idx+1];
1868
1869 // put this object's data into the proper location
1870 // in the output buffer
1871 std::vector<Number>::iterator out_vals(output_vals.begin());
1872 if (local_idx != 0)
1873 std::advance (out_vals, obj_val_offsets[local_idx-1]);
1874
1875 for (unsigned int val=0; val<n_vals_tot_allvecs; val++, ++out_vals, ++proc_vals)
1876 {
1877 libmesh_assert (out_vals != output_vals.end());
1878 libmesh_assert (proc_vals != vals.end());
1879 *out_vals = *proc_vals;
1880 }
1881 }
1882 }
1883
1884 // output_vals buffer is now filled for this block.
1885 // write it to disk
1886 async_io = std::make_unique<Threads::Thread>(threaded_io);
1887 written_length += output_vals.size();
1888 }
1889
1890 // wait on any previous asynchronous IO - this *must* complete before
1891 // our stuff goes out of scope
1892 async_io->join();
1893 }
1894
1895 Parallel::wait(id_requests);
1896 Parallel::wait(val_requests);
1897
1898 // we need some synchronization here. Because this method
1899 // can be called for a range of nodes, then a range of elements,
1900 // we need some mechanism to prevent processors from racing past
1901 // to the next range and overtaking ongoing communication. one
1902 // approach would be to figure out unique tags for each range,
1903 // but for now we just impose a barrier here. And might as
1904 // well have it do some useful work.
1905 this->comm().broadcast(written_length);
1906
1907 return written_length;
1908}
1909
1910
1911
1913 const unsigned int var,
1914 Xdr & io) const
1915{
1916 unsigned int written_length=0;
1917 std::vector<Number> vals; // The raw values for the local objects in the current block
1918 // Collect the SCALARs for the current variable
1919 if (this->processor_id() == (this->n_processors()-1))
1920 {
1921 const DofMap & dof_map = this->get_dof_map();
1922 std::vector<dof_id_type> SCALAR_dofs;
1923 dof_map.SCALAR_dof_indices(SCALAR_dofs, var);
1924 const unsigned int n_scalar_dofs = cast_int<unsigned int>
1925 (SCALAR_dofs.size());
1926
1927 for (unsigned int i=0; i<n_scalar_dofs; i++)
1928 {
1929 vals.push_back( vec(SCALAR_dofs[i]) );
1930 }
1931 }
1932
1933#ifdef LIBMESH_HAVE_MPI
1934 if (this->n_processors() > 1)
1935 {
1936 const Parallel::MessageTag val_tag =
1937 this->comm().get_unique_tag(1);
1938
1939 // Post the receive on processor 0
1940 if (this->processor_id() == 0)
1941 {
1942 this->comm().receive(this->n_processors()-1, vals, val_tag);
1943 }
1944
1945 // Send the data to processor 0
1946 if (this->processor_id() == (this->n_processors()-1))
1947 {
1948 this->comm().send(0, vals, val_tag);
1949 }
1950 }
1951#endif
1952
1953 // -------------------------------------------------------
1954 // Write the output on processor 0.
1955 if (this->processor_id() == 0)
1956 {
1957 const unsigned int vals_size =
1958 cast_int<unsigned int>(vals.size());
1959 io.data_stream (vals.data(), vals_size);
1960 written_length += vals_size;
1961 }
1962
1963 return written_length;
1964}
1965
1966
1967
1969 const NumericVector<Number> & vec) const
1970{
1971 parallel_object_only();
1972
1973 libmesh_assert (io.writing());
1974
1975 dof_id_type vec_length = vec.size();
1976 if (this->processor_id() == 0) io.data (vec_length, "# vector length");
1977
1978 dof_id_type written_length = 0;
1979
1980 //---------------------------------
1981 // Collect the values for all nodes
1982 written_length += cast_int<dof_id_type>
1983 (this->write_serialized_blocked_dof_objects (std::vector<const NumericVector<Number> *>(1,&vec),
1984 this->get_mesh().n_nodes(),
1985 this->get_mesh().local_nodes_begin(),
1986 this->get_mesh().local_nodes_end(),
1987 io));
1988
1989 //------------------------------------
1990 // Collect the values for all elements
1991 written_length += cast_int<dof_id_type>
1992 (this->write_serialized_blocked_dof_objects (std::vector<const NumericVector<Number> *>(1,&vec),
1993 this->get_mesh().n_elem(),
1994 this->get_mesh().local_elements_begin(),
1995 this->get_mesh().local_elements_end(),
1996 io));
1997
1998 //-------------------------------------------
1999 // Finally loop over all the SCALAR variables
2000 for (auto var : make_range(this->n_vars()))
2001 if (this->variable(var).type().family == SCALAR)
2002 {
2003 written_length +=
2004 this->write_SCALAR_dofs (vec, var, io);
2005 }
2006
2007 if (this->processor_id() == 0)
2008 libmesh_assert_equal_to (written_length, vec_length);
2009
2010 return written_length;
2011}
2012
2013
2014template <typename InValType>
2016 const std::vector<NumericVector<Number> *> & vectors) const
2017{
2018 parallel_object_only();
2019
2020 // Error checking
2021 // #ifndef NDEBUG
2022 // // In parallel we better be reading a parallel vector -- if not
2023 // // we will not set all of its components below!!
2024 // if (this->n_processors() > 1)
2025 // {
2026 // libmesh_assert (vec.type() == PARALLEL ||
2027 // vec.type() == GHOSTED);
2028 // }
2029 // #endif
2030
2031 libmesh_assert (io.reading());
2032
2033 if (this->processor_id() == 0)
2034 {
2035 // sizes
2036 unsigned int num_vecs=0;
2037 dof_id_type vector_length=0;
2038
2039 // Get the number of vectors
2040 io.data(num_vecs);
2041 // Get the buffer size
2042 io.data(vector_length);
2043
2044 libmesh_error_msg_if
2045 (num_vecs != vectors.size(),
2046 "Xdr file header declares " << num_vecs << " vectors, but we were asked to read " << vectors.size());
2047
2048 if (num_vecs != 0)
2049 {
2050 libmesh_error_msg_if (vectors[0] == nullptr, "vectors[0] should not be null");
2051 libmesh_error_msg_if (vectors[0]->size() != vector_length, "Inconsistent vector sizes");
2052 }
2053 }
2054
2055 // no need to actually communicate these.
2056 // this->comm().broadcast(num_vecs);
2057 // this->comm().broadcast(vector_length);
2058
2059 // Cache these - they are not free!
2060 const dof_id_type
2061 n_nodes = this->get_mesh().n_nodes(),
2062 n_elem = this->get_mesh().n_elem();
2063
2064 std::size_t read_length = 0;
2065
2066 //---------------------------------
2067 // Collect the values for all nodes
2068 read_length +=
2070 this->get_mesh().local_nodes_begin(),
2071 this->get_mesh().local_nodes_end(),
2072 InValType(),
2073 io,
2074 vectors);
2075
2076 //------------------------------------
2077 // Collect the values for all elements
2078 read_length +=
2080 this->get_mesh().local_elements_begin(),
2081 this->get_mesh().local_elements_end(),
2082 InValType(),
2083 io,
2084 vectors);
2085
2086 //-------------------------------------------
2087 // Finally loop over all the SCALAR variables
2088 for (NumericVector<Number> * vec : vectors)
2089 for (auto var : make_range(this->n_vars()))
2090 if (this->variable(var).type().family == SCALAR)
2091 {
2092 libmesh_assert_not_equal_to (vec, 0);
2093
2094 read_length +=
2095 this->read_SCALAR_dofs (var, io, vec);
2096 }
2097
2098 //---------------------------------------
2099 // last step - must close all the vectors
2100 for (NumericVector<Number> * vec : vectors)
2101 {
2102 libmesh_assert_not_equal_to (vec, 0);
2103 vec->close();
2104 }
2105
2106 return read_length;
2107}
2108
2109
2110
2112 const std::vector<const NumericVector<Number> *> & vectors) const
2113{
2114 parallel_object_only();
2115
2116 libmesh_assert (io.writing());
2117
2118 // Cache these - they are not free!
2119 const dof_id_type
2120 n_nodes = this->get_mesh().n_nodes(),
2121 n_elem = this->get_mesh().n_elem();
2122
2123 std::size_t written_length = 0;
2124
2125 if (this->processor_id() == 0)
2126 {
2127 unsigned int
2128 n_vec = cast_int<unsigned int>(vectors.size());
2130 vec_size = vectors.empty() ? 0 : vectors[0]->size();
2131 // Set the number of vectors
2132 io.data(n_vec, "# number of vectors");
2133 // Set the buffer size
2134 io.data(vec_size, "# vector length");
2135 }
2136
2137 //---------------------------------
2138 // Collect the values for all nodes
2139 written_length +=
2141 n_nodes,
2142 this->get_mesh().local_nodes_begin(),
2143 this->get_mesh().local_nodes_end(),
2144 io);
2145
2146 //------------------------------------
2147 // Collect the values for all elements
2148 written_length +=
2150 n_elem,
2151 this->get_mesh().local_elements_begin(),
2152 this->get_mesh().local_elements_end(),
2153 io);
2154
2155 //-------------------------------------------
2156 // Finally loop over all the SCALAR variables
2157 for (const NumericVector<Number> * vec : vectors)
2158 for (auto var : make_range(this->n_vars()))
2159 if (this->variable(var).type().family == SCALAR)
2160 {
2161 libmesh_assert_not_equal_to (vec, 0);
2162
2163 written_length +=
2164 this->write_SCALAR_dofs (*vec, var, io);
2165 }
2166
2167 return written_length;
2168}
2169
2170
2171
2172
2173template LIBMESH_EXPORT void System::read_parallel_data<Number> (Xdr & io, const bool read_additional_data);
2174template LIBMESH_EXPORT void System::read_serialized_data<Number> (Xdr & io, const bool read_additional_data);
2175template LIBMESH_EXPORT numeric_index_type System::read_serialized_vector<Number> (Xdr & io, NumericVector<Number> * vec);
2176template LIBMESH_EXPORT std::size_t System::read_serialized_vectors<Number> (Xdr & io, const std::vector<NumericVector<Number> *> & vectors) const;
2177#ifdef LIBMESH_USE_COMPLEX_NUMBERS
2178template LIBMESH_EXPORT void System::read_parallel_data<Real> (Xdr & io, const bool read_additional_data);
2179template LIBMESH_EXPORT void System::read_serialized_data<Real> (Xdr & io, const bool read_additional_data);
2180template LIBMESH_EXPORT numeric_index_type System::read_serialized_vector<Real> (Xdr & io, NumericVector<Number> * vec);
2181template LIBMESH_EXPORT std::size_t System::read_serialized_vectors<Real> (Xdr & io, const std::vector<NumericVector<Number> *> & vectors) const;
2182#endif
2183
2184} // namespace libMesh
MessageTag get_unique_tag(int tagvalue=MessageTag::invalid_tag) const
Status receive(const unsigned int dest_processor_id, T &buf, const MessageTag &tag=any_tag) const
void broadcast(T &data, const unsigned int root_id=0, const bool identical_sizes=false) const
void send(const unsigned int dest_processor_id, const T &buf, const MessageTag &tag=no_tag) const
int source() const
This class handles the numbering of degrees of freedom on a mesh.
Definition dof_map.h:181
void SCALAR_dof_indices(std::vector< dof_id_type > &di, const unsigned int vn, const bool old_dofs=false) const
Fills the vector di with the global degree of freedom indices corresponding to the SCALAR variable vn...
Definition dof_map.C:2605
The DofObject defines an abstract base class for objects that have degrees of freedom associated with...
Definition dof_object.h:55
static constexpr dof_id_type invalid_id
An invalid id to distinguish an uninitialized DofObject.
Definition dof_object.h:473
class FEType hides (possibly multiple) FEFamily and approximation orders, thereby enabling specialize...
Definition fe_type.h:197
InfMapType inf_map
The coordinate mapping type of the infinite element.
Definition fe_type.h:284
OrderWrapper radial_order
The approximation order in radial direction of the infinite element.
Definition fe_type.h:263
OrderWrapper order
The approximation order of the element (at 0 p-refinement level).
Definition fe_type.h:203
FEFamily radial_family
The type of approximation in radial direction.
Definition fe_type.h:276
FEFamily family
The type of finite element.
Definition fe_type.h:228
unsigned int mesh_dimension() const
Definition mesh_base.C:430
virtual dof_id_type n_elem() const =0
virtual dof_id_type n_nodes() const =0
Provides a uniform interface to vector storage schemes for different linear algebra libraries.
virtual void set(const numeric_index_type i, const T value)=0
Sets v(i) = value.
ParallelType type() const
virtual void close()=0
Calls the NumericVector's internal assembly routines, ensuring that the values are consistent across ...
virtual numeric_index_type size() const =0
int get_order() const
Explicitly request the order as an int.
Definition fe_type.h:80
processor_id_type processor_id() const
const Parallel::Communicator & comm() const
processor_id_type n_processors() const
unsigned int n_vectors() const
Definition system.h:2499
std::size_t read_serialized_blocked_dof_objects(const dof_id_type n_objects, const iterator_type begin, const iterator_type end, const InValType dummy, Xdr &io, const std::vector< NumericVector< Number > * > &vecs, const unsigned int var_to_read=libMesh::invalid_uint) const
Reads an input vector from the stream io and assigns the values to a set of DofObjects.
Definition system_io.C:618
const std::string & name() const
Definition system.h:2385
std::size_t read_serialized_vectors(Xdr &io, const std::vector< NumericVector< Number > * > &vectors) const
Read a number of identically distributed vectors.
Definition system_io.C:2015
std::size_t write_serialized_blocked_dof_objects(const std::vector< const NumericVector< Number > * > &vecs, const dof_id_type n_objects, const iterator_type begin, const iterator_type end, Xdr &io, const unsigned int var_to_write=libMesh::invalid_uint) const
Writes an output vector to the stream io for a set of DofObjects.
Definition system_io.C:1626
const Variable & variable(unsigned int var) const
Return a constant reference to Variable var.
Definition system.C:2704
void write_parallel_data(Xdr &io, const bool write_additional_data) const
Writes additional data, namely vectors, for this System.
Definition system_io.C:1318
dof_id_type write_serialized_vector(Xdr &io, const NumericVector< Number > &vec) const
Writes a vector for this System.
Definition system_io.C:1968
void read_serialized_data(Xdr &io, const bool read_additional_data=true)
Reads additional data, namely vectors, for this System.
Definition system_io.C:533
std::map< std::string, std::unique_ptr< NumericVector< Number > >, std::less<> > _vectors
Some systems need an arbitrary number of vectors.
Definition system.h:2260
numeric_index_type read_serialized_vector(Xdr &io, NumericVector< Number > *vec)
Reads a vector for this System.
Definition system_io.C:986
std::vector< unsigned int > _written_var_indices
This vector is used only when reading in a system from file.
Definition system.h:2325
const FEType & variable_type(const unsigned int i) const
Definition system.C:2721
unsigned int read_SCALAR_dofs(const unsigned int var, Xdr &io, NumericVector< Number > *vec) const
Reads the SCALAR dofs from the stream io and assigns the values to the appropriate entries of vec.
Definition system_io.C:939
void write_header(Xdr &io, std::string_view version, const bool write_additional_data) const
Writes the basic data header for this System.
Definition system_io.C:1117
unsigned int add_variable(std::string_view var, const FEType &type, const std::set< subdomain_id_type > *const active_subdomains=nullptr)
Adds the variable var to the list of variables for this system.
Definition system.C:1344
NumericVector< Number > & add_vector(std::string_view vec_name, const bool projections=true, const ParallelType type=PARALLEL)
Adds the additional vector vec_name to this system.
Definition system.C:756
std::size_t write_serialized_vectors(Xdr &io, const std::vector< const NumericVector< Number > * > &vectors) const
Serialize & write a number of identically distributed vectors.
Definition system_io.C:2111
virtual void clear()
Clear all the data structures associated with the system.
Definition system.C:173
std::map< std::string, bool, std::less<> > _vector_projections
Holds true if a vector by that name should be projected onto a changed grid, false if it should be ze...
Definition system.h:2266
std::unique_ptr< NumericVector< Number > > solution
Data structure to hold solution values.
Definition system.h:1655
const std::string & variable_name(const unsigned int i) const
Definition system.C:2679
unsigned int variable_number(std::string_view var) const
Definition system.C:1398
void read_parallel_data(Xdr &io, const bool read_additional_data)
Reads additional data, namely vectors, for this System.
Definition system_io.C:302
unsigned int n_vars() const
Definition system.C:2674
const DofMap & get_dof_map() const
Definition system.h:2417
unsigned int _additional_data_written
This flag is used only when reading in a system from file.
Definition system.h:2313
void read_header(Xdr &io, std::string_view version, const bool read_header=true, const bool read_additional_data=true, const bool read_legacy_format=false)
Reads the basic data header for this System.
Definition system_io.C:97
unsigned int number() const
Definition system.h:2393
unsigned int write_SCALAR_dofs(const NumericVector< Number > &vec, const unsigned int var, Xdr &io) const
Writes the SCALAR dofs associated with var to the stream io.
Definition system_io.C:1912
const MeshBase & get_mesh() const
Definition system.h:2401
void write_serialized_data(Xdr &io, const bool write_additional_data=true) const
Writes additional data, namely vectors, for this System.
Definition system_io.C:1518
Simple compatibility class for std::thread 'concurrent' execution.
Definition threads.h:114
void join()
Join is a no-op, since the constructor blocked until completion.
Definition threads.h:127
const std::set< subdomain_id_type > & active_subdomains() const
Definition variable.h:181
const FEType & type() const
Definition variable.h:144
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
bool is_open() const
Definition xdr_cxx.C:346
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 comment(std::string &)
Writes or reads (ignores) a comment line.
Definition xdr_cxx.C:1380
int version() const
Gets the version of the file that is being read.
Definition xdr_cxx.h:176
bool reading() const
Definition xdr_cxx.h:123
void data(T &a, std::string_view comment="")
Inputs or outputs a single value.
Definition xdr_cxx.C:860
Status wait(Request &r)
const unsigned int any_source
bool contains(std::string_view superstring, std::string_view substring)
Look for a substring within a string.
Definition utility.C:205
The libMesh namespace provides an interface to certain functionality in the library.
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
ParallelType
Defines an enum for parallel data structure types.
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
OStreamProxy out
dof_id_type numeric_index_type
Definition id_types.h:99
uint8_t dof_id_type
Definition id_types.h:67
Tnew cast_int(Told oldvar)
bool on_command_line(std::string arg)
Definition libmesh.C:934
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
const dof_id_type n_nodes
Definition tecplot_io.C:67