libMesh
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.
40 namespace {
41 
42 using libMesh::DofObject;
43 using libMesh::Number;
44 using libMesh::cast_int;
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 
64 const std::size_t max_io_blksize = 256000;
65 
69 template <typename InValType>
70 class ThreadedIO
71 {
72 private:
73  libMesh::Xdr & _io;
74  std::vector<InValType> & _data;
75 
76 public:
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 
91 namespace 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 
301 template <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 
532 template <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 
617 template <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  {
646  vars_to_read.clear();
647  vars_to_read.push_back(var_to_read);
648  }
649 
650  const unsigned int
651  sys_num = this->number(),
652  num_vecs = cast_int<unsigned int>(vecs.size());
653  const dof_id_type
654  io_blksize = cast_int<dof_id_type>(std::min(max_io_blksize, static_cast<std::size_t>(n_objs))),
655  num_blks = cast_int<unsigned int>(std::ceil(static_cast<double>(n_objs)/
656  static_cast<double>(io_blksize)));
657 
658  libmesh_assert_less_equal (_written_var_indices.size(), this->n_vars());
659 
660  std::size_t n_read_values=0;
661 
662  std::vector<std::vector<dof_id_type>> xfer_ids(num_blks); // The global IDs and # of components for the local objects in all blocks
663  std::vector<std::vector<Number>> recv_vals(num_blks); // The raw values for the local objects in all blocks
664  std::vector<Parallel::Request>
665  id_requests(num_blks), val_requests(num_blks);
666  std::vector<Parallel::MessageTag>
667  id_tags(num_blks), val_tags(num_blks);
668 
669  // ------------------------------------------------------
670  // First pass - count the number of objects in each block
671  // traverse all the objects and figure out which block they
672  // will ultimately live in.
673  std::vector<std::size_t>
674  xfer_ids_size (num_blks,0),
675  recv_vals_size (num_blks,0);
676 
677 
678  for (iterator_type it=begin; it!=end; ++it)
679  {
680  const dof_id_type
681  id = (*it)->id(),
682  block = id/io_blksize;
683 
684  libmesh_assert_less (block, num_blks);
685 
686  xfer_ids_size[block] += 2; // for each object, we send its id, as well as the total number of components for all variables
687 
688  dof_id_type n_comp_tot=0;
689  for (const auto & var : vars_to_read)
690  n_comp_tot += (*it)->n_comp(sys_num, var); // for each variable, we will receive the nonzero components
691 
692  recv_vals_size[block] += n_comp_tot*num_vecs;
693  }
694 
695  // knowing the recv_vals_size[block] for each processor allows
696  // us to sum them and find the global size for each block.
697  std::vector<std::size_t> tot_vals_size(recv_vals_size);
698  this->comm().sum (tot_vals_size);
699 
700 
701  //------------------------------------------
702  // Collect the ids & number of values needed
703  // for all local objects, binning them into
704  // 'blocks' that will be sent to processor 0
705  for (dof_id_type blk=0; blk<num_blks; blk++)
706  {
707  // Each processor should build up its transfer buffers for its
708  // local objects in [first_object,last_object).
709  const dof_id_type
710  first_object = blk*io_blksize,
711  last_object = std::min(cast_int<dof_id_type>((blk+1)*io_blksize), n_objs);
712 
713  // convenience
714  std::vector<dof_id_type> & ids (xfer_ids[blk]);
715  std::vector<Number> & vals (recv_vals[blk]);
716 
717  // we now know the number of values we will store for each block,
718  // so we can do efficient preallocation
719  ids.clear(); ids.reserve (xfer_ids_size[blk]);
720  vals.resize(recv_vals_size[blk]);
721 
722 #ifdef DEBUG
723  std::unordered_set<dof_id_type> seen_ids;
724 #endif
725 
726  if (recv_vals_size[blk] != 0) // only if there are nonzero values to receive
727  for (iterator_type it=begin; it!=end; ++it)
728  {
729  dof_id_type id = (*it)->id();
730 #ifdef DEBUG
731  // Any renumbering tricks should not have given us any
732  // duplicate ids.
733  libmesh_assert(!seen_ids.count(id));
734  seen_ids.insert(id);
735 #endif
736 
737  if ((id >= first_object) && // object in [first_object,last_object)
738  (id < last_object))
739  {
740  ids.push_back(id);
741 
742  unsigned int n_comp_tot=0;
743 
744  for (const auto & var : vars_to_read)
745  n_comp_tot += (*it)->n_comp(sys_num, var);
746 
747  ids.push_back (n_comp_tot*num_vecs);
748  }
749  }
750 
751 #ifdef LIBMESH_HAVE_MPI
752  id_tags[blk] = this->comm().get_unique_tag(100*num_blks + blk);
753  val_tags[blk] = this->comm().get_unique_tag(200*num_blks + blk);
754 
755  // nonblocking send the data for this block
756  this->comm().send (0, ids, id_requests[blk], id_tags[blk]);
757 
758  // Go ahead and post the receive too
759  this->comm().receive (0, vals, val_requests[blk], val_tags[blk]);
760 #endif
761  }
762 
763  //---------------------------------------------------
764  // Here processor 0 will read and distribute the data.
765  // We have to do this block-wise to ensure that we
766  // do not exhaust memory on processor 0.
767 
768  // give these variables scope outside the block to avoid reallocation
769  std::vector<std::vector<dof_id_type>> recv_ids (this->n_processors());
770  std::vector<std::vector<Number>> send_vals (this->n_processors());
771  std::vector<Parallel::Request> reply_requests (this->n_processors());
772  std::vector<unsigned int> obj_val_offsets; // map to traverse entry-wise rather than processor-wise
773  std::vector<Number> input_vals; // The input buffer for the current block
774  std::vector<InValType> input_vals_tmp; // The input buffer for the current block
775 
776  for (dof_id_type blk=0; blk<num_blks; blk++)
777  {
778  // Each processor should build up its transfer buffers for its
779  // local objects in [first_object,last_object).
780  const dof_id_type
781  first_object = blk*io_blksize,
782  last_object = std::min(cast_int<dof_id_type>((blk+1)*io_blksize), n_objs),
783  n_objects_blk = last_object - first_object;
784 
785  // Processor 0 has a special job. It needs to gather the requested indices
786  // in [first_object,last_object) from all processors, read the data from
787  // disk, and reply
788  if (this->processor_id() == 0)
789  {
790  // we know the input buffer size for this block and can begin reading it now
791  input_vals.resize(tot_vals_size[blk]);
792  input_vals_tmp.resize(tot_vals_size[blk]);
793 
794  // a ThreadedIO object to perform asynchronous file IO
795  ThreadedIO<InValType> threaded_io(io, input_vals_tmp);
796  Threads::Thread async_io(threaded_io);
797 
798  // offset array. this will define where each object's values
799  // map into the actual input_vals buffer. this must get
800  // 0-initialized because 0-component objects are not actually sent
801  obj_val_offsets.resize (n_objects_blk); std::fill (obj_val_offsets.begin(), obj_val_offsets.end(), 0);
802  recv_vals_size.resize(this->n_processors()); // reuse this to count how many values are going to each processor
803 
804 #ifndef NDEBUG
805  std::size_t n_vals_blk = 0;
806 #endif
807 
808  // loop over all processors and process their index request
809  for (processor_id_type comm_step=0, tnp=this->n_processors(); comm_step != tnp; ++comm_step)
810  {
811 #ifdef LIBMESH_HAVE_MPI
812  // blocking receive indices for this block, imposing no particular order on processor
813  Parallel::Status id_status (this->comm().probe (Parallel::any_source, id_tags[blk]));
814  std::vector<dof_id_type> & ids (recv_ids[id_status.source()]);
815  std::size_t & n_vals_proc (recv_vals_size[id_status.source()]);
816  this->comm().receive (id_status.source(), ids, id_tags[blk]);
817 #else
818  // straight copy without MPI
819  std::vector<dof_id_type> & ids (recv_ids[0]);
820  std::size_t & n_vals_proc (recv_vals_size[0]);
821  ids = xfer_ids[blk];
822 #endif
823 
824  n_vals_proc = 0;
825 
826  // note its possible we didn't receive values for objects in
827  // this block if they have no components allocated.
828  for (std::size_t idx=0, sz=ids.size(); idx<sz; idx+=2)
829  {
830  const dof_id_type
831  local_idx = ids[idx+0]-first_object,
832  n_vals_tot_allvecs = ids[idx+1];
833 
834  libmesh_assert_less (local_idx, n_objects_blk);
835 
836  obj_val_offsets[local_idx] = n_vals_tot_allvecs;
837  n_vals_proc += n_vals_tot_allvecs;
838  }
839 
840 #ifndef NDEBUG
841  n_vals_blk += n_vals_proc;
842 #endif
843  }
844 
845  // We need the offsets into the input_vals vector for each object.
846  // fortunately, this is simply the partial sum of the total number
847  // of components for each object
848  std::partial_sum(obj_val_offsets.begin(), obj_val_offsets.end(),
849  obj_val_offsets.begin());
850 
851  libmesh_assert_equal_to (n_vals_blk, obj_val_offsets.back());
852  libmesh_assert_equal_to (n_vals_blk, tot_vals_size[blk]);
853 
854  // Wait for read completion
855  async_io.join();
856  // now copy the values back to the main vector for transfer
857  for (auto i_val : index_range(input_vals))
858  input_vals[i_val] = input_vals_tmp[i_val];
859 
860  n_read_values += input_vals.size();
861 
862  // pack data replies for each processor
863  for (auto proc : make_range(this->n_processors()))
864  {
865  const std::vector<dof_id_type> & ids (recv_ids[proc]);
866  std::vector<Number> & vals (send_vals[proc]);
867  const std::size_t & n_vals_proc (recv_vals_size[proc]);
868 
869  vals.clear(); vals.reserve(n_vals_proc);
870 
871  for (std::size_t idx=0, sz=ids.size(); idx<sz; idx+=2)
872  {
873  const dof_id_type
874  local_idx = ids[idx+0]-first_object,
875  n_vals_tot_allvecs = ids[idx+1];
876 
877  std::vector<Number>::const_iterator in_vals(input_vals.begin());
878  if (local_idx != 0)
879  std::advance (in_vals, obj_val_offsets[local_idx-1]);
880 
881  for (unsigned int val=0; val<n_vals_tot_allvecs; val++, ++in_vals)
882  {
883  libmesh_assert (in_vals != input_vals.end());
884  //libMesh::out << "*in_vals=" << *in_vals << '\n';
885  vals.push_back(*in_vals);
886  }
887  }
888 
889 #ifdef LIBMESH_HAVE_MPI
890  // send the relevant values to this processor
891  this->comm().send (proc, vals, reply_requests[proc], val_tags[blk]);
892 #else
893  recv_vals[blk] = vals;
894 #endif
895  }
896  } // end processor 0 read/reply
897 
898  // all processors complete the (already posted) read for this block
899  {
900  Parallel::wait (val_requests[blk]);
901 
902  const std::vector<Number> & vals (recv_vals[blk]);
903  std::vector<Number>::const_iterator val_it(vals.begin());
904 
905  if (!recv_vals[blk].empty()) // nonzero values to receive
906  for (iterator_type it=begin; it!=end; ++it)
907  if (((*it)->id() >= first_object) && // object in [first_object,last_object)
908  ((*it)->id() < last_object))
909  // unpack & set the values
910  for (auto & vec : vecs)
911  for (const auto & var : vars_to_read)
912  {
913  const unsigned int n_comp = (*it)->n_comp(sys_num, var);
914 
915  for (unsigned int comp=0; comp<n_comp; comp++, ++val_it)
916  {
917  const dof_id_type dof_index = (*it)->dof_number (sys_num, var, comp);
918  libmesh_assert (val_it != vals.end());
919  if (vec)
920  {
921  libmesh_assert_greater_equal (dof_index, vec->first_local_index());
922  libmesh_assert_less (dof_index, vec->last_local_index());
923  //libMesh::out << "dof_index, *val_it = \t" << dof_index << ", " << *val_it << '\n';
924  vec->set (dof_index, *val_it);
925  }
926  }
927  }
928  }
929 
930  // processor 0 needs to make sure all replies have been handed off
931  if (this->processor_id () == 0)
932  Parallel::wait(reply_requests);
933  }
934 
935  Parallel::wait(id_requests);
936 
937  return n_read_values;
938 }
939 
940 
941 
942 unsigned int System::read_SCALAR_dofs (const unsigned int var,
943  Xdr & io,
944  NumericVector<Number> * vec) const
945 {
946  unsigned int n_assigned_vals = 0; // the number of values assigned, this will be returned.
947 
948  // Processor 0 will read the block from the buffer stream and send it to the last processor
949  const unsigned int n_SCALAR_dofs = this->variable(var).type().order.get_order();
950  std::vector<Number> input_buffer(n_SCALAR_dofs);
951  if (this->processor_id() == 0)
952  io.data_stream(input_buffer.data(), n_SCALAR_dofs);
953 
954 #ifdef LIBMESH_HAVE_MPI
955  if (this->n_processors() > 1)
956  {
957  const Parallel::MessageTag val_tag = this->comm().get_unique_tag();
958 
959  // Post the receive on the last processor
960  if (this->processor_id() == (this->n_processors()-1))
961  this->comm().receive(0, input_buffer, val_tag);
962 
963  // Send the data to processor 0
964  if (this->processor_id() == 0)
965  this->comm().send(this->n_processors()-1, input_buffer, val_tag);
966  }
967 #endif
968 
969  // Finally, set the SCALAR values
970  if (this->processor_id() == (this->n_processors()-1))
971  {
972  const DofMap & dof_map = this->get_dof_map();
973  std::vector<dof_id_type> SCALAR_dofs;
974  dof_map.SCALAR_dof_indices(SCALAR_dofs, var);
975 
976  for (auto i : index_range(SCALAR_dofs))
977  {
978  if (vec)
979  vec->set (SCALAR_dofs[i], input_buffer[i]);
980  ++n_assigned_vals;
981  }
982  }
983 
984  return n_assigned_vals;
985 }
986 
987 
988 template <typename InValType>
990  NumericVector<Number> * vec)
991 {
992  parallel_object_only();
993 
994 #ifndef NDEBUG
995  // In parallel we better be reading a parallel vector -- if not
996  // we will not set all of its components below!!
997  if (this->n_processors() > 1 && vec)
998  {
999  libmesh_assert (vec->type() == PARALLEL ||
1000  vec->type() == GHOSTED);
1001  }
1002 #endif
1003 
1004  libmesh_assert (io.reading());
1005 
1006  // vector length
1007  unsigned int vector_length=0; // FIXME? size_t would break binary compatibility...
1008 #ifndef NDEBUG
1009  std::size_t n_assigned_vals=0;
1010 #endif
1011 
1012  // Get the buffer size
1013  if (this->processor_id() == 0)
1014  io.data(vector_length, "# vector length");
1015  this->comm().broadcast(vector_length);
1016 
1017  const unsigned int nv = cast_int<unsigned int>
1018  (this->_written_var_indices.size());
1019  const dof_id_type
1020  n_nodes = this->get_mesh().n_nodes(),
1021  n_elem = this->get_mesh().n_elem();
1022 
1023  libmesh_assert_less_equal (nv, this->n_vars());
1024 
1025  // for newer versions, read variables node/elem major
1026  if (io.version() >= LIBMESH_VERSION_ID(0,7,4))
1027  {
1028  //---------------------------------
1029  // Collect the values for all nodes
1030 #ifndef NDEBUG
1031  n_assigned_vals +=
1032 #endif
1033  this->read_serialized_blocked_dof_objects (n_nodes,
1034  this->get_mesh().local_nodes_begin(),
1035  this->get_mesh().local_nodes_end(),
1036  InValType(),
1037  io,
1038  std::vector<NumericVector<Number> *> (1,vec));
1039 
1040 
1041  //------------------------------------
1042  // Collect the values for all elements
1043 #ifndef NDEBUG
1044  n_assigned_vals +=
1045 #endif
1047  this->get_mesh().local_elements_begin(),
1048  this->get_mesh().local_elements_end(),
1049  InValType(),
1050  io,
1051  std::vector<NumericVector<Number> *> (1,vec));
1052  }
1053 
1054  // for older versions, read variables var-major
1055  else
1056  {
1057  // Loop over each variable in the system, and then each node/element in the mesh.
1058  for (unsigned int data_var=0; data_var<nv; data_var++)
1059  {
1060  const unsigned int var = _written_var_indices[data_var];
1061  if (this->variable(var).type().family != SCALAR)
1062  {
1063  //---------------------------------
1064  // Collect the values for all nodes
1065 #ifndef NDEBUG
1066  n_assigned_vals +=
1067 #endif
1068  this->read_serialized_blocked_dof_objects (n_nodes,
1069  this->get_mesh().local_nodes_begin(),
1070  this->get_mesh().local_nodes_end(),
1071  InValType(),
1072  io,
1073  std::vector<NumericVector<Number> *> (1,vec),
1074  var);
1075 
1076 
1077  //------------------------------------
1078  // Collect the values for all elements
1079 #ifndef NDEBUG
1080  n_assigned_vals +=
1081 #endif
1083  this->get_mesh().local_elements_begin(),
1084  this->get_mesh().local_elements_end(),
1085  InValType(),
1086  io,
1087  std::vector<NumericVector<Number> *> (1,vec),
1088  var);
1089  } // end variable loop
1090  }
1091  }
1092 
1093  //-------------------------------------------
1094  // Finally loop over all the SCALAR variables
1095  for (unsigned int data_var=0; data_var<nv; data_var++)
1096  {
1097  const unsigned int var = _written_var_indices[data_var];
1098  if (this->variable(var).type().family == SCALAR)
1099  {
1100 #ifndef NDEBUG
1101  n_assigned_vals +=
1102 #endif
1103  this->read_SCALAR_dofs (var, io, vec);
1104  }
1105  }
1106 
1107  if (vec)
1108  vec->close();
1109 
1110 #ifndef NDEBUG
1111  this->comm().sum (n_assigned_vals);
1112  libmesh_assert_equal_to (n_assigned_vals, vector_length);
1113 #endif
1114 
1115  return vector_length;
1116 }
1117 
1118 
1119 
1121  std::string_view /* version is currently unused */,
1122  const bool write_additional_data) const
1123 {
1157  libmesh_assert (io.writing());
1158 
1159 
1160  // Only write the header information
1161  // if we are processor 0.
1162  if (this->get_mesh().processor_id() != 0)
1163  return;
1164 
1165  std::string comment;
1166 
1167  // 5.)
1168  // Write the number of variables in the system
1169 
1170  {
1171  // set up the comment
1172  comment = "# No. of Variables in System \"";
1173  comment += this->name();
1174  comment += "\"";
1175 
1176  unsigned int nv = this->n_vars();
1177  io.data (nv, comment);
1178  }
1179 
1180 
1181  for (auto var : make_range(this->n_vars()))
1182  {
1183  // 6.)
1184  // Write the name of the var-th variable
1185  {
1186  // set up the comment
1187  comment = "# Name, Variable No. ";
1188  comment += std::to_string(var);
1189  comment += ", System \"";
1190  comment += this->name();
1191  comment += "\"";
1192 
1193  std::string var_name = this->variable_name(var);
1194  io.data (var_name, comment);
1195  }
1196 
1197  // 6.1.) Variable subdomains
1198  {
1199  // set up the comment
1200  comment = "# Subdomains, Variable \"";
1201  comment += this->variable_name(var);
1202  comment += "\", System \"";
1203  comment += this->name();
1204  comment += "\"";
1205 
1206  const std::set<subdomain_id_type> & domains = this->variable(var).active_subdomains();
1207  std::vector<subdomain_id_type> domain_array;
1208  domain_array.assign(domains.begin(), domains.end());
1209  io.data (domain_array, comment);
1210  }
1211 
1212  // 7.)
1213  // Write the approximation order of the var-th variable
1214  // in this system
1215  {
1216  // set up the comment
1217  comment = "# Approximation Order, Variable \"";
1218  comment += this->variable_name(var);
1219  comment += "\", System \"";
1220  comment += this->name();
1221  comment += "\"";
1222 
1223  int order = static_cast<int>(this->variable_type(var).order);
1224  io.data (order, comment);
1225  }
1226 
1227 
1228 #ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
1229 
1230  // do the same for radial_order
1231  {
1232  comment = "# Radial Approximation Order, Variable \"";
1233  comment += this->variable_name(var);
1234  comment += "\", System \"";
1235  comment += this->name();
1236  comment += "\"";
1237 
1238  int rad_order = static_cast<int>(this->variable_type(var).radial_order);
1239  io.data (rad_order, comment);
1240  }
1241 
1242 #endif
1243 
1244  // Write the Finite Element type of the var-th variable
1245  // in this System
1246  {
1247  // set up the comment
1248  comment = "# FE Family, Variable \"";
1249  comment += this->variable_name(var);
1250  comment += "\", System \"";
1251  comment += this->name();
1252  comment += "\"";
1253 
1254  const FEType & type = this->variable_type(var);
1255  int fam = static_cast<int>(type.family);
1256  io.data (fam, comment);
1257 
1258 #ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
1259 
1260  comment = "# Radial FE Family, Variable \"";
1261  comment += this->variable_name(var);
1262  comment += "\", System \"";
1263  comment += this->name();
1264  comment += "\"";
1265 
1266  int radial_fam = static_cast<int>(type.radial_family);
1267  io.data (radial_fam, comment);
1268 
1269  comment = "# Infinite Mapping Type, Variable \"";
1270  comment += this->variable_name(var);
1271  comment += "\", System \"";
1272  comment += this->name();
1273  comment += "\"";
1274 
1275  int i_map = static_cast<int>(type.inf_map);
1276  io.data (i_map, comment);
1277 #endif
1278  }
1279  } // end of the variable loop
1280 
1281  // 8.)
1282  // Write the number of additional vectors in the System.
1283  // If write_additional_data==false, then write zero for
1284  // the number of additional vectors.
1285  {
1286  {
1287  // set up the comment
1288  comment = "# No. of Additional Vectors, System \"";
1289  comment += this->name();
1290  comment += "\"";
1291 
1292  unsigned int nvecs = write_additional_data ? this->n_vectors () : 0;
1293  io.data (nvecs, comment);
1294  }
1295 
1296  if (write_additional_data)
1297  {
1298  unsigned int cnt=0;
1299  for (const auto & [vec_name, vec] : _vectors)
1300  {
1301  // 9.)
1302  // write the name of the cnt-th additional vector
1303  const std::string dth_vector = std::to_string(cnt++)+"th vector";
1304  comment = "# Name of " + dth_vector;
1305  std::string nonconst_vec_name = vec_name; // Stupid XDR API
1306 
1307  io.data (nonconst_vec_name, comment);
1308  int vec_projection = _vector_projections.at(vec_name);
1309  comment = "# Whether to do projections for " + dth_vector;
1310  io.data (vec_projection, comment);
1311  int vec_type = vec->type();
1312  comment = "# Parallel type of " + dth_vector;
1313  io.data (vec_type, comment);
1314  }
1315  }
1316  }
1317 }
1318 
1319 
1320 
1322  const bool write_additional_data) const
1323 {
1343  // PerfLog pl("IO Performance",false);
1344  // pl.push("write_parallel_data");
1345  // std::size_t total_written_size = 0;
1346 
1347  std::string comment;
1348 
1349  libmesh_assert (io.writing());
1350 
1351  std::vector<Number> io_buffer; io_buffer.reserve(this->solution->local_size());
1352 
1353  // build the ordered nodes and element maps.
1354  // when writing/reading parallel files we need to iterate
1355  // over our nodes/elements in order of increasing global id().
1356  // however, this is not guaranteed to be ordering we obtain
1357  // by using the node_iterators/element_iterators directly.
1358  // so build a set, sorted by id(), that provides the ordering.
1359  // further, for memory economy build the set but then transfer
1360  // its contents to vectors, which will be sorted.
1361  std::vector<const DofObject *> ordered_nodes, ordered_elements;
1362  {
1363  std::set<const DofObject *, CompareDofObjectsByID>
1364  ordered_nodes_set (this->get_mesh().local_nodes_begin(),
1365  this->get_mesh().local_nodes_end());
1366 
1367  ordered_nodes.insert(ordered_nodes.end(),
1368  ordered_nodes_set.begin(),
1369  ordered_nodes_set.end());
1370  }
1371  {
1372  std::set<const DofObject *, CompareDofObjectsByID>
1373  ordered_elements_set (this->get_mesh().local_elements_begin(),
1374  this->get_mesh().local_elements_end());
1375 
1376  ordered_elements.insert(ordered_elements.end(),
1377  ordered_elements_set.begin(),
1378  ordered_elements_set.end());
1379  }
1380 
1381  const unsigned int sys_num = this->number();
1382  const unsigned int nv = this->n_vars();
1383 
1384  // Loop over each non-SCALAR variable and each node, and write out the value.
1385  for (unsigned int var=0; var<nv; var++)
1386  if (this->variable(var).type().family != SCALAR)
1387  {
1388  // First write the node DOF values
1389  for (const auto & node : ordered_nodes)
1390  for (auto comp : make_range(node->n_comp(sys_num,var)))
1391  {
1392  libmesh_assert_not_equal_to (node->dof_number(sys_num, var, comp),
1394 
1395  io_buffer.push_back((*this->solution)(node->dof_number(sys_num, var, comp)));
1396  }
1397 
1398  // Then write the element DOF values
1399  for (const auto & elem : ordered_elements)
1400  for (auto comp : make_range(elem->n_comp(sys_num,var)))
1401  {
1402  libmesh_assert_not_equal_to (elem->dof_number(sys_num, var, comp),
1404 
1405  io_buffer.push_back((*this->solution)(elem->dof_number(sys_num, var, comp)));
1406  }
1407  }
1408 
1409  // Finally, write the SCALAR data on the last processor
1410  for (auto var : make_range(this->n_vars()))
1411  if (this->variable(var).type().family == SCALAR)
1412  {
1413  if (this->processor_id() == (this->n_processors()-1))
1414  {
1415  const DofMap & dof_map = this->get_dof_map();
1416  std::vector<dof_id_type> SCALAR_dofs;
1417  dof_map.SCALAR_dof_indices(SCALAR_dofs, var);
1418 
1419  for (auto dof : SCALAR_dofs)
1420  io_buffer.push_back((*this->solution)(dof));
1421  }
1422  }
1423 
1424  // 9.)
1425  //
1426  // Actually write the reordered solution vector
1427  // for the ith system to disk
1428 
1429  // set up the comment
1430  {
1431  comment = "# System \"";
1432  comment += this->name();
1433  comment += "\" Solution Vector";
1434  }
1435 
1436  io.data (io_buffer, comment);
1437 
1438  // total_written_size += io_buffer.size();
1439 
1440  // Only write additional vectors if wanted
1441  if (write_additional_data)
1442  {
1443  for (auto & [vec_name, vec] : _vectors)
1444  {
1445  io_buffer.clear();
1446  io_buffer.reserve(vec->local_size());
1447 
1448  // Loop over each non-SCALAR variable and each node, and write out the value.
1449  for (unsigned int var=0; var<nv; var++)
1450  if (this->variable(var).type().family != SCALAR)
1451  {
1452  // First write the node DOF values
1453  for (const auto & node : ordered_nodes)
1454  for (auto comp : make_range(node->n_comp(sys_num,var)))
1455  {
1456  libmesh_assert_not_equal_to (node->dof_number(sys_num, var, comp),
1458 
1459  io_buffer.push_back((*vec)(node->dof_number(sys_num, var, comp)));
1460  }
1461 
1462  // Then write the element DOF values
1463  for (const auto & elem : ordered_elements)
1464  for (auto comp : make_range(elem->n_comp(sys_num,var)))
1465  {
1466  libmesh_assert_not_equal_to (elem->dof_number(sys_num, var, comp),
1468 
1469  io_buffer.push_back((*vec)(elem->dof_number(sys_num, var, comp)));
1470  }
1471  }
1472 
1473  // Finally, write the SCALAR data on the last processor
1474  for (auto var : make_range(this->n_vars()))
1475  if (this->variable(var).type().family == SCALAR)
1476  {
1477  if (this->processor_id() == (this->n_processors()-1))
1478  {
1479  const DofMap & dof_map = this->get_dof_map();
1480  std::vector<dof_id_type> SCALAR_dofs;
1481  dof_map.SCALAR_dof_indices(SCALAR_dofs, var);
1482 
1483  for (auto dof : SCALAR_dofs)
1484  io_buffer.push_back((*vec)(dof));
1485  }
1486  }
1487 
1488  // 10.)
1489  //
1490  // Actually write the reordered additional vector
1491  // for this system to disk
1492 
1493  // set up the comment
1494  {
1495  comment = "# System \"";
1496  comment += this->name();
1497  comment += "\" Additional Vector \"";
1498  comment += vec_name;
1499  comment += "\"";
1500  }
1501 
1502  io.data (io_buffer, comment);
1503 
1504  // total_written_size += io_buffer.size();
1505  }
1506  }
1507 
1508  // const Real
1509  // dt = pl.get_elapsed_time(),
1510  // rate = total_written_size*sizeof(Number)/dt;
1511 
1512  // libMesh::err << "Write " << total_written_size << " \"Number\" values\n"
1513  // << " Elapsed time = " << dt << '\n'
1514  // << " Rate = " << rate/1.e6 << "(MB/sec)\n\n";
1515 
1516  // pl.pop("write_parallel_data");
1517 }
1518 
1519 
1520 
1522  const bool write_additional_data) const
1523 {
1537  parallel_object_only();
1538  std::string comment;
1539 
1540  // PerfLog pl("IO Performance",false);
1541  // pl.push("write_serialized_data");
1542  // std::size_t total_written_size = 0;
1543 
1544  // total_written_size +=
1545  this->write_serialized_vector(io, *this->solution);
1546 
1547  // set up the comment
1548  if (this->processor_id() == 0)
1549  {
1550  comment = "# System \"";
1551  comment += this->name();
1552  comment += "\" Solution Vector";
1553 
1554  io.comment (comment);
1555  }
1556 
1557  // Only write additional vectors if wanted
1558  if (write_additional_data)
1559  {
1560  for (auto & pair : this->_vectors)
1561  {
1562  // total_written_size +=
1563  this->write_serialized_vector(io, *pair.second);
1564 
1565  // set up the comment
1566  if (this->processor_id() == 0)
1567  {
1568  comment = "# System \"";
1569  comment += this->name();
1570  comment += "\" Additional Vector \"";
1571  comment += pair.first;
1572  comment += "\"";
1573  io.comment (comment);
1574  }
1575  }
1576  }
1577 
1578  // const Real
1579  // dt = pl.get_elapsed_time(),
1580  // rate = total_written_size*sizeof(Number)/dt;
1581 
1582  // libMesh::out << "Write " << total_written_size << " \"Number\" values\n"
1583  // << " Elapsed time = " << dt << '\n'
1584  // << " Rate = " << rate/1.e6 << "(MB/sec)\n\n";
1585 
1586  // pl.pop("write_serialized_data");
1587 
1588 
1589 
1590 
1591  // // test the new method
1592  // {
1593  // std::vector<std::string> names;
1594  // std::vector<NumericVector<Number> *> vectors_to_write;
1595 
1596  // names.push_back("Solution Vector");
1597  // vectors_to_write.push_back(this->solution.get());
1598 
1599  // // Only write additional vectors if wanted
1600  // if (write_additional_data)
1601  // {
1602  // std::map<std::string, NumericVector<Number> *>::const_iterator
1603  // pos = _vectors.begin();
1604 
1605  // for (; pos != this->_vectors.end(); ++pos)
1606  // {
1607  // names.push_back("Additional Vector " + pos->first);
1608  // vectors_to_write.push_back(pos->second);
1609  // }
1610  // }
1611 
1612  // total_written_size =
1613  // this->write_serialized_vectors (io, names, vectors_to_write);
1614 
1615  // const Real
1616  // dt2 = pl.get_elapsed_time(),
1617  // rate2 = total_written_size*sizeof(Number)/(dt2-dt);
1618 
1619  // libMesh::out << "Write (new) " << total_written_size << " \"Number\" values\n"
1620  // << " Elapsed time = " << (dt2-dt) << '\n'
1621  // << " Rate = " << rate2/1.e6 << "(MB/sec)\n\n";
1622 
1623  // }
1624 }
1625 
1626 
1627 
1628 template <typename iterator_type>
1629 std::size_t System::write_serialized_blocked_dof_objects (const std::vector<const NumericVector<Number> *> & vecs,
1630  const dof_id_type n_objs,
1631  const iterator_type begin,
1632  const iterator_type end,
1633  Xdr & io,
1634  const unsigned int var_to_write) const
1635 {
1636  parallel_object_only();
1637 
1638  //-------------------------------------------------------
1639  // General order: (IO format 0.7.4 & greater)
1640  //
1641  // for (objects ...)
1642  // for (vecs ....)
1643  // for (vars ....)
1644  // for (comps ...)
1645  //
1646  // where objects are nodes or elements, sorted to be
1647  // partition independent,
1648  // vecs are one or more *identically distributed* solution
1649  // coefficient vectors, vars are one or more variables
1650  // to write, and comps are all the components for said
1651  // vars on the object.
1652 
1653  // We will write all variables unless requested otherwise.
1654  std::vector<unsigned int> vars_to_write(1, var_to_write);
1655 
1656  if (var_to_write == libMesh::invalid_uint)
1657  {
1658  vars_to_write.clear(); vars_to_write.reserve(this->n_vars());
1659  for (auto var : make_range(this->n_vars()))
1660  vars_to_write.push_back(var);
1661  }
1662 
1663  const dof_id_type io_blksize = cast_int<dof_id_type>
1664  (std::min(max_io_blksize, static_cast<std::size_t>(n_objs)));
1665 
1666  const unsigned int
1667  sys_num = this->number(),
1668  num_vecs = cast_int<unsigned int>(vecs.size()),
1669  num_blks = cast_int<unsigned int>(std::ceil(static_cast<double>(n_objs)/
1670  static_cast<double>(io_blksize)));
1671 
1672  // libMesh::out << "io_blksize = " << io_blksize
1673  // << ", num_objects = " << n_objs
1674  // << ", num_blks = " << num_blks
1675  // << std::endl;
1676 
1677  std::size_t written_length=0; // The numer of values written. This will be returned
1678  std::vector<std::vector<dof_id_type>> xfer_ids(num_blks); // The global IDs and # of components for the local objects in all blocks
1679  std::vector<std::vector<Number>> send_vals(num_blks); // The raw values for the local objects in all blocks
1680  std::vector<Parallel::Request>
1681  id_requests(num_blks), val_requests(num_blks); // send request handle for each block
1682  std::vector<Parallel::MessageTag>
1683  id_tags(num_blks), val_tags(num_blks); // tag number for each block
1684 
1685  // ------------------------------------------------------
1686  // First pass - count the number of objects in each block
1687  // traverse all the objects and figure out which block they
1688  // will ultimately live in.
1689  std::vector<unsigned int>
1690  xfer_ids_size (num_blks,0),
1691  send_vals_size (num_blks,0);
1692 
1693  for (iterator_type it=begin; it!=end; ++it)
1694  {
1695  const dof_id_type
1696  id = (*it)->id(),
1697  block = id/io_blksize;
1698 
1699  libmesh_assert_less (block, num_blks);
1700 
1701  xfer_ids_size[block] += 2; // for each object, we store its id, as well as the total number of components for all variables
1702 
1703  unsigned int n_comp_tot=0;
1704 
1705  for (const auto & var : vars_to_write)
1706  n_comp_tot += (*it)->n_comp(sys_num, var); // for each variable, we will store the nonzero components
1707 
1708  send_vals_size[block] += n_comp_tot*num_vecs;
1709  }
1710 
1711  //-----------------------------------------
1712  // Collect the values for all local objects,
1713  // binning them into 'blocks' that will be
1714  // sent to processor 0
1715  for (unsigned int blk=0; blk<num_blks; blk++)
1716  {
1717  // libMesh::out << "Writing object block " << blk << std::endl;
1718 
1719  // Each processor should build up its transfer buffers for its
1720  // local objects in [first_object,last_object).
1721  const dof_id_type
1722  first_object = blk*io_blksize,
1723  last_object = std::min(cast_int<dof_id_type>((blk+1)*io_blksize), n_objs);
1724 
1725  // convenience
1726  std::vector<dof_id_type> & ids (xfer_ids[blk]);
1727  std::vector<Number> & vals (send_vals[blk]);
1728 
1729  // we now know the number of values we will store for each block,
1730  // so we can do efficient preallocation
1731  ids.clear(); ids.reserve (xfer_ids_size[blk]);
1732  vals.clear(); vals.reserve (send_vals_size[blk]);
1733 
1734  if (send_vals_size[blk] != 0) // only send if we have nonzero components to write
1735  for (iterator_type it=begin; it!=end; ++it)
1736  if (((*it)->id() >= first_object) && // object in [first_object,last_object)
1737  ((*it)->id() < last_object))
1738  {
1739  ids.push_back((*it)->id());
1740 
1741  // count the total number of nonzeros transferred for this object
1742  {
1743  unsigned int n_comp_tot=0;
1744 
1745  for (const auto & var : vars_to_write)
1746  n_comp_tot += (*it)->n_comp(sys_num, var);
1747 
1748  ids.push_back (n_comp_tot*num_vecs); // even if 0 - processor 0 has no way of knowing otherwise...
1749  }
1750 
1751  // pack the values to send
1752  for (const auto & vec : vecs)
1753  for (const auto & var : vars_to_write)
1754  {
1755  const unsigned int n_comp = (*it)->n_comp(sys_num, var);
1756 
1757  for (unsigned int comp=0; comp<n_comp; comp++)
1758  {
1759  libmesh_assert_greater_equal ((*it)->dof_number(sys_num, var, comp), vec->first_local_index());
1760  libmesh_assert_less ((*it)->dof_number(sys_num, var, comp), vec->last_local_index());
1761  vals.push_back((*vec)((*it)->dof_number(sys_num, var, comp)));
1762  }
1763  }
1764  }
1765 
1766 #ifdef LIBMESH_HAVE_MPI
1767  id_tags[blk] = this->comm().get_unique_tag(100*num_blks + blk);
1768  val_tags[blk] = this->comm().get_unique_tag(200*num_blks + blk);
1769 
1770  // nonblocking send the data for this block
1771  this->comm().send (0, ids, id_requests[blk], id_tags[blk]);
1772  this->comm().send (0, vals, val_requests[blk], val_tags[blk]);
1773 #endif
1774  }
1775 
1776 
1777  if (this->processor_id() == 0)
1778  {
1779  std::vector<std::vector<dof_id_type>> recv_ids (this->n_processors());
1780  std::vector<std::vector<Number>> recv_vals (this->n_processors());
1781  std::vector<unsigned int> obj_val_offsets; // map to traverse entry-wise rather than processor-wise
1782  std::vector<Number> output_vals; // The output buffer for the current block
1783 
1784  // a ThreadedIO object to perform asynchronous file IO
1785  ThreadedIO<Number> threaded_io(io, output_vals);
1786  std::unique_ptr<Threads::Thread> async_io;
1787 
1788  for (unsigned int blk=0; blk<num_blks; blk++)
1789  {
1790  // Each processor should build up its transfer buffers for its
1791  // local objects in [first_object,last_object).
1792  const dof_id_type
1793  first_object = cast_int<dof_id_type>(blk*io_blksize),
1794  last_object = std::min(cast_int<dof_id_type>((blk+1)*io_blksize), n_objs),
1795  n_objects_blk = last_object - first_object;
1796 
1797  // offset array. this will define where each object's values
1798  // map into the actual output_vals buffer. this must get
1799  // 0-initialized because 0-component objects are not actually sent
1800  obj_val_offsets.resize (n_objects_blk); std::fill (obj_val_offsets.begin(), obj_val_offsets.end(), 0);
1801 
1802  std::size_t n_val_recvd_blk=0;
1803 
1804  // receive this block of data from all processors.
1805  for (processor_id_type comm_step=0, tnp=this->n_processors(); comm_step != tnp; ++comm_step)
1806  {
1807 #ifdef LIBMESH_HAVE_MPI
1808  // blocking receive indices for this block, imposing no particular order on processor
1809  Parallel::Status id_status (this->comm().probe (Parallel::any_source, id_tags[blk]));
1810  std::vector<dof_id_type> & ids (recv_ids[id_status.source()]);
1811  this->comm().receive (id_status.source(), ids, id_tags[blk]);
1812 #else
1813  std::vector<dof_id_type> & ids (recv_ids[0]);
1814  ids = xfer_ids[blk];
1815 #endif
1816 
1817  // note its possible we didn't receive values for objects in
1818  // this block if they have no components allocated.
1819  for (std::size_t idx=0, sz=ids.size(); idx<sz; idx+=2)
1820  {
1821  const dof_id_type
1822  local_idx = ids[idx+0]-first_object,
1823  n_vals_tot_allvecs = ids[idx+1];
1824 
1825  libmesh_assert_less (local_idx, n_objects_blk);
1826  libmesh_assert_less (local_idx, obj_val_offsets.size());
1827 
1828  obj_val_offsets[local_idx] = n_vals_tot_allvecs;
1829  }
1830 
1831 #ifdef LIBMESH_HAVE_MPI
1832  // blocking receive values for this block, imposing no particular order on processor
1833  Parallel::Status val_status (this->comm().probe (Parallel::any_source, val_tags[blk]));
1834  std::vector<Number> & vals (recv_vals[val_status.source()]);
1835  this->comm().receive (val_status.source(), vals, val_tags[blk]);
1836 #else
1837  // straight copy without MPI
1838  std::vector<Number> & vals (recv_vals[0]);
1839  vals = send_vals[blk];
1840 #endif
1841 
1842  n_val_recvd_blk += vals.size();
1843  }
1844 
1845  // We need the offsets into the output_vals vector for each object.
1846  // fortunately, this is simply the partial sum of the total number
1847  // of components for each object
1848  std::partial_sum(obj_val_offsets.begin(), obj_val_offsets.end(),
1849  obj_val_offsets.begin());
1850 
1851  // wait on any previous asynchronous IO - this *must* complete before
1852  // we start messing with the output_vals buffer!
1853  if (async_io.get()) async_io->join();
1854 
1855  // this is the actual output buffer that will be written to disk.
1856  // at ths point we finally know wha size it will be.
1857  output_vals.resize(n_val_recvd_blk);
1858 
1859  // pack data from all processors into output values
1860  for (auto proc : make_range(this->n_processors()))
1861  {
1862  const std::vector<dof_id_type> & ids (recv_ids [proc]);
1863  const std::vector<Number> & vals(recv_vals[proc]);
1864  std::vector<Number>::const_iterator proc_vals(vals.begin());
1865 
1866  for (std::size_t idx=0, sz=ids.size(); idx<sz; idx+=2)
1867  {
1868  const dof_id_type
1869  local_idx = ids[idx+0]-first_object,
1870  n_vals_tot_allvecs = ids[idx+1];
1871 
1872  // put this object's data into the proper location
1873  // in the output buffer
1874  std::vector<Number>::iterator out_vals(output_vals.begin());
1875  if (local_idx != 0)
1876  std::advance (out_vals, obj_val_offsets[local_idx-1]);
1877 
1878  for (unsigned int val=0; val<n_vals_tot_allvecs; val++, ++out_vals, ++proc_vals)
1879  {
1880  libmesh_assert (out_vals != output_vals.end());
1881  libmesh_assert (proc_vals != vals.end());
1882  *out_vals = *proc_vals;
1883  }
1884  }
1885  }
1886 
1887  // output_vals buffer is now filled for this block.
1888  // write it to disk
1889  async_io = std::make_unique<Threads::Thread>(threaded_io);
1890  written_length += output_vals.size();
1891  }
1892 
1893  // wait on any previous asynchronous IO - this *must* complete before
1894  // our stuff goes out of scope
1895  async_io->join();
1896  }
1897 
1898  Parallel::wait(id_requests);
1899  Parallel::wait(val_requests);
1900 
1901  // we need some synchronization here. Because this method
1902  // can be called for a range of nodes, then a range of elements,
1903  // we need some mechanism to prevent processors from racing past
1904  // to the next range and overtaking ongoing communication. one
1905  // approach would be to figure out unique tags for each range,
1906  // but for now we just impose a barrier here. And might as
1907  // well have it do some useful work.
1908  this->comm().broadcast(written_length);
1909 
1910  return written_length;
1911 }
1912 
1913 
1914 
1916  const unsigned int var,
1917  Xdr & io) const
1918 {
1919  unsigned int written_length=0;
1920  std::vector<Number> vals; // The raw values for the local objects in the current block
1921  // Collect the SCALARs for the current variable
1922  if (this->processor_id() == (this->n_processors()-1))
1923  {
1924  const DofMap & dof_map = this->get_dof_map();
1925  std::vector<dof_id_type> SCALAR_dofs;
1926  dof_map.SCALAR_dof_indices(SCALAR_dofs, var);
1927  const unsigned int n_scalar_dofs = cast_int<unsigned int>
1928  (SCALAR_dofs.size());
1929 
1930  for (unsigned int i=0; i<n_scalar_dofs; i++)
1931  {
1932  vals.push_back( vec(SCALAR_dofs[i]) );
1933  }
1934  }
1935 
1936 #ifdef LIBMESH_HAVE_MPI
1937  if (this->n_processors() > 1)
1938  {
1939  const Parallel::MessageTag val_tag =
1940  this->comm().get_unique_tag(1);
1941 
1942  // Post the receive on processor 0
1943  if (this->processor_id() == 0)
1944  {
1945  this->comm().receive(this->n_processors()-1, vals, val_tag);
1946  }
1947 
1948  // Send the data to processor 0
1949  if (this->processor_id() == (this->n_processors()-1))
1950  {
1951  this->comm().send(0, vals, val_tag);
1952  }
1953  }
1954 #endif
1955 
1956  // -------------------------------------------------------
1957  // Write the output on processor 0.
1958  if (this->processor_id() == 0)
1959  {
1960  const unsigned int vals_size =
1961  cast_int<unsigned int>(vals.size());
1962  io.data_stream (vals.data(), vals_size);
1963  written_length += vals_size;
1964  }
1965 
1966  return written_length;
1967 }
1968 
1969 
1970 
1972  const NumericVector<Number> & vec) const
1973 {
1974  parallel_object_only();
1975 
1976  libmesh_assert (io.writing());
1977 
1978  dof_id_type vec_length = vec.size();
1979  if (this->processor_id() == 0) io.data (vec_length, "# vector length");
1980 
1981  dof_id_type written_length = 0;
1982 
1983  //---------------------------------
1984  // Collect the values for all nodes
1985  written_length += cast_int<dof_id_type>
1986  (this->write_serialized_blocked_dof_objects (std::vector<const NumericVector<Number> *>(1,&vec),
1987  this->get_mesh().n_nodes(),
1988  this->get_mesh().local_nodes_begin(),
1989  this->get_mesh().local_nodes_end(),
1990  io));
1991 
1992  //------------------------------------
1993  // Collect the values for all elements
1994  written_length += cast_int<dof_id_type>
1995  (this->write_serialized_blocked_dof_objects (std::vector<const NumericVector<Number> *>(1,&vec),
1996  this->get_mesh().n_elem(),
1997  this->get_mesh().local_elements_begin(),
1998  this->get_mesh().local_elements_end(),
1999  io));
2000 
2001  //-------------------------------------------
2002  // Finally loop over all the SCALAR variables
2003  for (auto var : make_range(this->n_vars()))
2004  if (this->variable(var).type().family == SCALAR)
2005  {
2006  written_length +=
2007  this->write_SCALAR_dofs (vec, var, io);
2008  }
2009 
2010  if (this->processor_id() == 0)
2011  libmesh_assert_equal_to (written_length, vec_length);
2012 
2013  return written_length;
2014 }
2015 
2016 
2017 template <typename InValType>
2019  const std::vector<NumericVector<Number> *> & vectors) const
2020 {
2021  parallel_object_only();
2022 
2023  // Error checking
2024  // #ifndef NDEBUG
2025  // // In parallel we better be reading a parallel vector -- if not
2026  // // we will not set all of its components below!!
2027  // if (this->n_processors() > 1)
2028  // {
2029  // libmesh_assert (vec.type() == PARALLEL ||
2030  // vec.type() == GHOSTED);
2031  // }
2032  // #endif
2033 
2034  libmesh_assert (io.reading());
2035 
2036  if (this->processor_id() == 0)
2037  {
2038  // sizes
2039  unsigned int num_vecs=0;
2040  dof_id_type vector_length=0;
2041 
2042  // Get the number of vectors
2043  io.data(num_vecs);
2044  // Get the buffer size
2045  io.data(vector_length);
2046 
2047  libmesh_error_msg_if
2048  (num_vecs != vectors.size(),
2049  "Xdr file header declares " << num_vecs << " vectors, but we were asked to read " << vectors.size());
2050 
2051  if (num_vecs != 0)
2052  {
2053  libmesh_error_msg_if (vectors[0] == nullptr, "vectors[0] should not be null");
2054  libmesh_error_msg_if (vectors[0]->size() != vector_length, "Inconsistent vector sizes");
2055  }
2056  }
2057 
2058  // no need to actually communicate these.
2059  // this->comm().broadcast(num_vecs);
2060  // this->comm().broadcast(vector_length);
2061 
2062  // Cache these - they are not free!
2063  const dof_id_type
2064  n_nodes = this->get_mesh().n_nodes(),
2065  n_elem = this->get_mesh().n_elem();
2066 
2067  std::size_t read_length = 0;
2068 
2069  //---------------------------------
2070  // Collect the values for all nodes
2071  read_length +=
2072  this->read_serialized_blocked_dof_objects (n_nodes,
2073  this->get_mesh().local_nodes_begin(),
2074  this->get_mesh().local_nodes_end(),
2075  InValType(),
2076  io,
2077  vectors);
2078 
2079  //------------------------------------
2080  // Collect the values for all elements
2081  read_length +=
2083  this->get_mesh().local_elements_begin(),
2084  this->get_mesh().local_elements_end(),
2085  InValType(),
2086  io,
2087  vectors);
2088 
2089  //-------------------------------------------
2090  // Finally loop over all the SCALAR variables
2091  for (NumericVector<Number> * vec : vectors)
2092  for (auto var : make_range(this->n_vars()))
2093  if (this->variable(var).type().family == SCALAR)
2094  {
2095  libmesh_assert_not_equal_to (vec, 0);
2096 
2097  read_length +=
2098  this->read_SCALAR_dofs (var, io, vec);
2099  }
2100 
2101  //---------------------------------------
2102  // last step - must close all the vectors
2103  for (NumericVector<Number> * vec : vectors)
2104  {
2105  libmesh_assert_not_equal_to (vec, 0);
2106  vec->close();
2107  }
2108 
2109  return read_length;
2110 }
2111 
2112 
2113 
2115  const std::vector<const NumericVector<Number> *> & vectors) const
2116 {
2117  parallel_object_only();
2118 
2119  libmesh_assert (io.writing());
2120 
2121  // Cache these - they are not free!
2122  const dof_id_type
2123  n_nodes = this->get_mesh().n_nodes(),
2124  n_elem = this->get_mesh().n_elem();
2125 
2126  std::size_t written_length = 0;
2127 
2128  if (this->processor_id() == 0)
2129  {
2130  unsigned int
2131  n_vec = cast_int<unsigned int>(vectors.size());
2132  dof_id_type
2133  vec_size = vectors.empty() ? 0 : vectors[0]->size();
2134  // Set the number of vectors
2135  io.data(n_vec, "# number of vectors");
2136  // Set the buffer size
2137  io.data(vec_size, "# vector length");
2138  }
2139 
2140  //---------------------------------
2141  // Collect the values for all nodes
2142  written_length +=
2143  this->write_serialized_blocked_dof_objects (vectors,
2144  n_nodes,
2145  this->get_mesh().local_nodes_begin(),
2146  this->get_mesh().local_nodes_end(),
2147  io);
2148 
2149  //------------------------------------
2150  // Collect the values for all elements
2151  written_length +=
2152  this->write_serialized_blocked_dof_objects (vectors,
2153  n_elem,
2154  this->get_mesh().local_elements_begin(),
2155  this->get_mesh().local_elements_end(),
2156  io);
2157 
2158  //-------------------------------------------
2159  // Finally loop over all the SCALAR variables
2160  for (const NumericVector<Number> * vec : vectors)
2161  for (auto var : make_range(this->n_vars()))
2162  if (this->variable(var).type().family == SCALAR)
2163  {
2164  libmesh_assert_not_equal_to (vec, 0);
2165 
2166  written_length +=
2167  this->write_SCALAR_dofs (*vec, var, io);
2168  }
2169 
2170  return written_length;
2171 }
2172 
2173 
2174 
2175 
2176 template LIBMESH_EXPORT void System::read_parallel_data<Number> (Xdr & io, const bool read_additional_data);
2177 template LIBMESH_EXPORT void System::read_serialized_data<Number> (Xdr & io, const bool read_additional_data);
2178 template LIBMESH_EXPORT numeric_index_type System::read_serialized_vector<Number> (Xdr & io, NumericVector<Number> * vec);
2179 template LIBMESH_EXPORT std::size_t System::read_serialized_vectors<Number> (Xdr & io, const std::vector<NumericVector<Number> *> & vectors) const;
2180 #ifdef LIBMESH_USE_COMPLEX_NUMBERS
2181 template LIBMESH_EXPORT void System::read_parallel_data<Real> (Xdr & io, const bool read_additional_data);
2182 template LIBMESH_EXPORT void System::read_serialized_data<Real> (Xdr & io, const bool read_additional_data);
2183 template LIBMESH_EXPORT numeric_index_type System::read_serialized_vector<Real> (Xdr & io, NumericVector<Number> * vec);
2184 template LIBMESH_EXPORT std::size_t System::read_serialized_vectors<Real> (Xdr & io, const std::vector<NumericVector<Number> *> & vectors) const;
2185 #endif
2186 
2187 } // namespace libMesh
class FEType hides (possibly multiple) FEFamily and approximation orders, thereby enabling specialize...
Definition: fe_type.h:196
Simple compatibility class for std::thread &#39;concurrent&#39; execution.
Definition: threads.h:113
bool writing() const
Definition: xdr_cxx.h:129
FEFamily family
The type of finite element.
Definition: fe_type.h:228
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:1521
virtual void clear()
Clear all the data structures associated with the system.
Definition: system.C:173
Order
defines an enum for polynomial orders.
Definition: enum_order.h:40
void write_parallel_data(Xdr &io, const bool write_additional_data) const
Writes additional data, namely vectors, for this System.
Definition: system_io.C:1321
const Variable & variable(unsigned int var) const
Return a constant reference to Variable var.
Definition: system.C:2704
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
void join()
Join is a no-op, since the constructor blocked until completion.
Definition: threads.h:127
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:1120
dof_id_type n_elem(const MeshBase::const_element_iterator &begin, const MeshBase::const_element_iterator &end)
Count up the number of elements of a specific type (as defined by an iterator range).
Definition: mesh_tools.C:1004
MessageTag get_unique_tag(int tagvalue=MessageTag::invalid_tag) const
void comment(std::string &)
Writes or reads (ignores) a comment line.
Definition: xdr_cxx.C:1380
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
virtual numeric_index_type size() const =0
OrderWrapper radial_order
The approximation order in radial direction of the infinite element.
Definition: fe_type.h:263
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:1915
void sum(T &r) const
int version() const
Gets the version of the file that is being read.
Definition: xdr_cxx.h:176
const Parallel::Communicator & comm() const
std::map< std::string, std::unique_ptr< NumericVector< Number > >, std::less<> > _vectors
Some systems need an arbitrary number of vectors.
Definition: system.h:2260
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
OrderWrapper order
The approximation order of the element (at 0 p-refinement level).
Definition: fe_type.h:203
The libMesh namespace provides an interface to certain functionality in the library.
int source() const
const MeshBase & get_mesh() const
Definition: system.h:2401
uint8_t processor_id_type
Definition: id_types.h:104
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
unsigned int variable_number(std::string_view var) const
Definition: system.C:1398
Tnew cast_int(Told oldvar)
void data(T &a, std::string_view comment="")
Inputs or outputs a single value.
Definition: xdr_cxx.C:860
This class handles the numbering of degrees of freedom on a mesh.
Definition: dof_map.h:179
processor_id_type n_processors() const
const dof_id_type n_nodes
Definition: tecplot_io.C:67
unsigned int number() const
Definition: system.h:2393
Status receive(const unsigned int dest_processor_id, T &buf, const MessageTag &tag=any_tag) const
const std::set< subdomain_id_type > & active_subdomains() const
Definition: variable.h:181
dof_id_type numeric_index_type
Definition: id_types.h:99
static constexpr dof_id_type invalid_id
An invalid id to distinguish an uninitialized DofObject.
Definition: dof_object.h:473
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 n_vectors() const
Definition: system.h:2499
numeric_index_type read_serialized_vector(Xdr &io, NumericVector< Number > *vec)
Reads a vector for this System.
Definition: system_io.C:989
std::unique_ptr< NumericVector< Number > > solution
Data structure to hold solution values.
Definition: system.h:1655
libmesh_assert(ctx)
unsigned int _additional_data_written
This flag is used only when reading in a system from file.
Definition: system.h:2313
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
const std::string & variable_name(const unsigned int i) const
Definition: system.C:2679
InfMapType inf_map
The coordinate mapping type of the infinite element.
Definition: fe_type.h:284
bool contains(std::string_view superstring, std::string_view substring)
Look for a substring within a string.
Definition: utility.C:205
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:2018
void broadcast(T &data, const unsigned int root_id=0, const bool identical_sizes=false) const
virtual void close()=0
Calls the NumericVector&#39;s internal assembly routines, ensuring that the values are consistent across ...
This class implements a C++ interface to the XDR (eXternal Data Representation) format.
Definition: xdr_cxx.h:67
FEFamily radial_family
The type of approximation in radial direction.
Definition: fe_type.h:276
int get_order() const
Explicitly request the order as an int.
Definition: fe_type.h:80
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:2114
void read_parallel_data(Xdr &io, const bool read_additional_data)
Reads additional data, namely vectors, for this System.
Definition: system_io.C:302
ParallelType type() const
const FEType & variable_type(const unsigned int i) const
Definition: system.C:2721
InfMapType
defines an enum for the types of coordinate mappings available in infinite elements.
bool is_open() const
Definition: xdr_cxx.C:346
void send(const unsigned int dest_processor_id, const T &buf, const MessageTag &tag=no_tag) const
OStreamProxy out
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 DofObject defines an abstract base class for objects that have degrees of freedom associated with...
Definition: dof_object.h:54
unsigned int mesh_dimension() const
Definition: mesh_base.C:430
bool reading() const
Definition: xdr_cxx.h:123
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:942
virtual void set(const numeric_index_type i, const T value)=0
Sets v(i) = value.
bool on_command_line(std::string arg)
Definition: libmesh.C:934
const std::string & name() const
Definition: system.h:2385
FEFamily
defines an enum for finite element families.
unsigned int n_vars() const
Definition: system.C:2674
virtual dof_id_type n_elem() const =0
processor_id_type processor_id() const
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
const DofMap & get_dof_map() const
Definition: system.h:2417
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
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
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
virtual dof_id_type n_nodes() const =0
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:1629
std::vector< unsigned int > _written_var_indices
This vector is used only when reading in a system from file.
Definition: system.h:2325
unsigned int idx(const ElemType type, const unsigned int nx, const unsigned int i, const unsigned int j)
A useful inline function which replaces the macros used previously.
uint8_t dof_id_type
Definition: id_types.h:67
const FEType & type() const
Definition: variable.h:144
ParallelType
Defines an enum for parallel data structure types.
dof_id_type write_serialized_vector(Xdr &io, const NumericVector< Number > &vec) const
Writes a vector for this System.
Definition: system_io.C:1971