libMesh
equation_systems_io.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2025 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/libmesh_logging.h"
21 
22 
23 // Local Includes
24 #include "libmesh/libmesh_version.h"
25 #include "libmesh/equation_systems.h"
26 #include "libmesh/mesh_base.h"
27 #include "libmesh/mesh_tools.h"
28 #include "libmesh/parallel.h"
29 #include "libmesh/xdr_cxx.h"
30 #include "libmesh/mesh_refinement.h"
31 
32 // C++ Includes
33 #include <iomanip> // setfill
34 #include <sstream>
35 #include <string>
36 
37 namespace libMesh
38 {
39 
40 // Forward Declarations
41 
42 // Anonymous namespace for implementation details.
43 namespace {
44 std::string local_file_name (const unsigned int processor_id,
45  std::string_view basename)
46 {
47  std::ostringstream returnval;
48 
49  std::string_view suffix;
50  if (basename.size() - basename.rfind(".bz2") == 4)
51  {
52  basename.remove_suffix(4);
53  suffix = ".bz2";
54  }
55  else if (basename.size() - basename.rfind(".gz") == 3)
56  {
57  basename.remove_suffix(3);
58  suffix = ".gz";
59  }
60 
61  returnval << basename << '.';
62  returnval << std::setfill('0') << std::setw(4);
63  returnval << processor_id;
64  returnval << suffix;
65 
66  return returnval.str();
67 }
68 }
69 
70 
71 
72 
73 // ------------------------------------------------------------
74 // EquationSystem class implementation
75 template <typename InValType>
76 void EquationSystems::read (std::string_view name,
77  const unsigned int read_flags,
78  bool partition_agnostic)
79 {
80  XdrMODE mode = READ;
81  if (name.find(".xdr") != std::string::npos)
82  mode = DECODE;
83  this->read(name, mode, read_flags, partition_agnostic);
84 }
85 
86 
87 
88 template <typename InValType>
89 void EquationSystems::read (std::string_view name,
90  const XdrMODE mode,
91  const unsigned int read_flags,
92  bool partition_agnostic)
93 {
94  // This will unzip a file with .bz2 as the extension, otherwise it
95  // simply returns the name if the file need not be unzipped.
96  Xdr io ((this->processor_id() == 0) ? std::string(name) : "", mode);
97 
98  std::function<std::unique_ptr<Xdr>()> local_io_functor;
99  local_io_functor = [this,&name,&mode]() {
100  return std::make_unique<Xdr>(local_file_name(this->processor_id(), name), mode); };
101 
102  this->read(io, local_io_functor, read_flags, partition_agnostic);
103 }
104 
105 
106 
107 template <typename InValType>
109  std::function<std::unique_ptr<Xdr>()> & local_io_functor,
110  const unsigned int read_flags,
111  bool partition_agnostic)
112 {
176  // Set booleans from the read_flags argument
177  const bool read_header = read_flags & EquationSystems::READ_HEADER;
178  const bool read_data = read_flags & EquationSystems::READ_DATA;
179  const bool read_additional_data = read_flags & EquationSystems::READ_ADDITIONAL_DATA;
180  const bool read_legacy_format = read_flags & EquationSystems::READ_LEGACY_FORMAT;
181  const bool try_read_ifems = read_flags & EquationSystems::TRY_READ_IFEMS;
182  const bool read_basic_only = read_flags & EquationSystems::READ_BASIC_ONLY;
183  bool read_parallel_files = false;
184 
185  std::vector<std::pair<std::string, System *>> xda_systems;
186 
187  libmesh_assert (io.reading());
188 
189  {
190  // 1.)
191  // Read the version header.
192  std::string version = "legacy";
193  if (!read_legacy_format)
194  {
195  if (this->processor_id() == 0) io.data(version);
196  this->comm().broadcast(version);
197 
198  // All processors have the version header, if it does not contain
199  // the libMesh_label string then it is a legacy file.
200  const std::string libMesh_label = "libMesh-";
201  std::string::size_type lm_pos = version.find(libMesh_label);
202  if (lm_pos==std::string::npos)
203  {
204  io.close();
205 
206  // Recursively call this read() function but with the
207  // EquationSystems::READ_LEGACY_FORMAT bit set.
208  this->read (io, local_io_functor, (read_flags | EquationSystems::READ_LEGACY_FORMAT), partition_agnostic);
209  return;
210  }
211 
212  // Figure out the libMesh version that created this file
213  std::istringstream iss(version.substr(lm_pos + libMesh_label.size()));
214  int ver_major = 0, ver_minor = 0, ver_patch = 0;
215  char dot;
216  iss >> ver_major >> dot >> ver_minor >> dot >> ver_patch;
217  io.set_version(LIBMESH_VERSION_ID(ver_major, ver_minor, ver_patch));
218 
219 
220  read_parallel_files = (version.rfind(" parallel") < version.size());
221 
222  // If requested that we try to read infinite element information,
223  // and the string " with infinite elements" is not in the version,
224  // then tack it on. This is for compatibility reading ifem
225  // files written prior to 11/10/2008 - BSK
226  if (try_read_ifems)
227  if (!(version.rfind(" with infinite elements") < version.size()))
228  version += " with infinite elements";
229 
230  }
231  else
232  libmesh_deprecated();
233 
234  LOG_SCOPE("read()", "EquationSystems");
235 
236  // 2.)
237  // Read the number of equation systems
238  unsigned int n_sys=0;
239  if (this->processor_id() == 0) io.data (n_sys);
240  this->comm().broadcast(n_sys);
241 
242  for (unsigned int sys=0; sys<n_sys; sys++)
243  {
244  // 3.)
245  // Read the name of the sys-th equation system
246  std::string sys_name;
247  if (this->processor_id() == 0) io.data (sys_name);
248  this->comm().broadcast(sys_name);
249 
250  // 4.)
251  // Read the type of the sys-th equation system
252  std::string sys_type;
253  if (this->processor_id() == 0) io.data (sys_type);
254  this->comm().broadcast(sys_type);
255 
256  if (read_header)
257  this->add_system (sys_type, sys_name);
258 
259  // 5.) - 9.)
260  // Let System::read_header() do the job
261  System & new_system = this->get_system(sys_name);
262  new_system.read_header (io,
263  version,
264  read_header,
265  read_additional_data,
266  read_legacy_format);
267 
268  xda_systems.emplace_back(sys_name, &new_system);
269 
270  // If we're only creating "basic" systems, we need to tell
271  // each system that before we call init() later.
272  if (read_basic_only)
273  new_system.set_basic_system_only();
274  }
275  }
276 
277 
278 
279  // Now we are ready to initialize the underlying data
280  // structures. This will initialize the vectors for
281  // storage, the dof_map, etc...
282  if (read_header)
283  this->init();
284 
285  // 10.) & 11.)
286  // Read and set the numeric vector values
287  if (read_data)
288  {
289  std::unique_ptr<Xdr> local_io;
290 
291  // the EquationSystems::read() method should look constant from the mesh
292  // perspective, but we need to assign a temporary numbering to the nodes
293  // and elements in the mesh, which requires that we abuse const_cast
294  if (!read_legacy_format && partition_agnostic)
295  {
296  MeshBase & mesh = const_cast<MeshBase &>(this->get_mesh());
298  }
299 
300  for (auto & pr : xda_systems)
301  if (read_legacy_format)
302  {
303  libmesh_deprecated();
304 #ifdef LIBMESH_ENABLE_DEPRECATED
305  pr.second->read_legacy_data (io, read_additional_data);
306 #endif
307  }
308  else
309  if (read_parallel_files)
310  {
311  if (!local_io)
312  {
313  local_io = local_io_functor();
314  libmesh_assert(local_io->reading());
315  }
316  pr.second->read_parallel_data<InValType> (*local_io, read_additional_data);
317  }
318  else
319  pr.second->read_serialized_data<InValType> (io, read_additional_data);
320 
321 
322  // Undo the temporary numbering.
323  if (!read_legacy_format && partition_agnostic)
325  }
326 
327  // Localize each system's data
328  this->update();
329 
330  #ifdef LIBMESH_ENABLE_AMR
331  MeshRefinement mesh_refine(_mesh);
332  mesh_refine.clean_refinement_flags();
333  #endif
334 }
335 
336 
337 
338 void EquationSystems::write(std::string_view name,
339  const unsigned int write_flags,
340  bool partition_agnostic) const
341 {
342  XdrMODE mode = WRITE;
343  if (name.find(".xdr") != std::string::npos)
344  mode = ENCODE;
345  this->write(name, mode, write_flags, partition_agnostic);
346 }
347 
348 
349 
350 void EquationSystems::write(std::string_view name,
351  const XdrMODE mode,
352  const unsigned int write_flags,
353  bool partition_agnostic) const
354 {
355  Xdr io((this->processor_id()==0) ? std::string(name) : "", mode);
356 
357  std::unique_ptr<Xdr> local_io;
358  // open a parallel buffer if warranted
359  if (write_flags & EquationSystems::WRITE_PARALLEL_FILES && write_flags & EquationSystems::WRITE_DATA)
360  local_io = std::make_unique<Xdr>(local_file_name(this->processor_id(),name), mode);
361 
362  this->write(io, write_flags, partition_agnostic, local_io.get());
363 }
364 
365 
366 
368  const unsigned int write_flags,
369  bool partition_agnostic,
370  Xdr * const local_io) const
371 {
435  // the EquationSystems::write() method should look constant,
436  // but we need to assign a temporary numbering to the nodes
437  // and elements in the mesh, which requires that we abuse const_cast
438  if (partition_agnostic)
439  {
440  MeshBase & mesh = const_cast<MeshBase &>(this->get_mesh());
442  }
443 
444  // set booleans from write_flags argument
445  const bool write_data = write_flags & EquationSystems::WRITE_DATA;
446  const bool write_additional_data = write_flags & EquationSystems::WRITE_ADDITIONAL_DATA;
447 
448  // always write parallel files if we're instructed to write in
449  // parallel
450  const bool write_parallel_files =
452  // Even if we're on a distributed mesh, we may or may not have a
453  // consistent way of reconstructing the same mesh partitioning
454  // later, but we need the same mesh partitioning if we want to
455  // reread the parallel solution safely, so let's write a serial file
456  // unless specifically requested not to.
457  // ||
458  // // but also write parallel files if we haven't been instructed to
459  // // write in serial and we're on a distributed mesh
460  // (!(write_flags & EquationSystems::WRITE_SERIAL_FILES) &&
461  // !this->get_mesh().is_serial())
462  ;
463 
464  if (write_parallel_files && write_data)
465  libmesh_assert(local_io);
466 
467  {
468  libmesh_assert (io.writing());
469 
470  LOG_SCOPE("write()", "EquationSystems");
471 
472  const unsigned int proc_id = this->processor_id();
473 
474  unsigned int n_sys = 0;
475  for (auto & pr : _systems)
476  if (!pr.second->hide_output())
477  n_sys++;
478 
479  // set the version number in the Xdr object
480  io.set_version(LIBMESH_VERSION_ID(LIBMESH_MAJOR_VERSION,
481  LIBMESH_MINOR_VERSION,
482  LIBMESH_MICRO_VERSION));
483 
484  // Only write the header information
485  // if we are processor 0.
486  if (proc_id == 0)
487  {
488  std::string comment;
489 
490  // 1.)
491  // Write the version header
492  std::string version("libMesh-" + libMesh::get_io_compatibility_version());
493  if (write_parallel_files) version += " parallel";
494 
495 #ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
496  version += " with infinite elements";
497 #endif
498  io.data (version, "# File Format Identifier");
499 
500  // 2.)
501  // Write the number of equation systems
502  io.data (n_sys, "# No. of Equation Systems");
503 
504  for (auto & [sys_name, sys] : _systems)
505  {
506  // Ignore this system if it has been marked as hidden
507  if (sys->hide_output()) continue;
508 
509  // 3.)
510  // Write the name of the sys_num-th system
511  {
512  const unsigned int sys_num = sys->number();
513 
514  comment = "# Name, System No. ";
515  comment += std::to_string(sys_num);
516 
517  // Note: There is no Xdr::data overload taking a "const
518  // std::string &" so we need to make a copy.
519  std::string copy = sys_name;
520  io.data (copy, comment);
521  }
522 
523  // 4.)
524  // Write the type of system handled
525  {
526  const unsigned int sys_num = sys->number();
527  std::string sys_type = sys->system_type();
528 
529  comment = "# Type, System No. ";
530  comment += std::to_string(sys_num);
531 
532  io.data (sys_type, comment);
533  }
534 
535  // 5.) - 9.)
536  // Let System::write_header() do the job
537  sys->write_header (io, version, write_additional_data);
538  }
539  }
540 
541  // Start from the first system, again,
542  // to write vectors to disk, if wanted
543  if (write_data)
544  {
545  for (auto & pr : _systems)
546  {
547  // Ignore this system if it has been marked as hidden
548  if (pr.second->hide_output()) continue;
549 
550  // 10.) + 11.)
551  if (write_parallel_files)
552  pr.second->write_parallel_data (*local_io,write_additional_data);
553  else
554  pr.second->write_serialized_data (io,write_additional_data);
555  }
556 
557  if (local_io)
558  local_io->close();
559  }
560 
561  io.close();
562  }
563 
564  // the EquationSystems::write() method should look constant,
565  // but we need to undo the temporary numbering of the nodes
566  // and elements in the mesh, which requires that we abuse const_cast
567  if (partition_agnostic)
568  const_cast<MeshBase &>(_mesh).fix_broken_node_and_element_numbering();
569 }
570 
571 
572 
573 // template specialization
574 
575 template LIBMESH_EXPORT void EquationSystems::read<Number> (Xdr & io, std::function<std::unique_ptr<Xdr>()> & local_io_functor, const unsigned int read_flags, bool partition_agnostic);
576 template LIBMESH_EXPORT void EquationSystems::read<Number> (std::string_view name, const unsigned int read_flags, bool partition_agnostic);
577 template LIBMESH_EXPORT void EquationSystems::read<Number> (std::string_view name, const XdrMODE mode, const unsigned int read_flags, bool partition_agnostic);
578 #ifdef LIBMESH_USE_COMPLEX_NUMBERS
579 template LIBMESH_EXPORT void EquationSystems::read<Real> (Xdr & io, std::function<std::unique_ptr<Xdr>()> & local_io_functor, const unsigned int read_flags, bool partition_agnostic);
580 template LIBMESH_EXPORT void EquationSystems::read<Real> (std::string_view name, const unsigned int read_flags, bool partition_agnostic);
581 template LIBMESH_EXPORT void EquationSystems::read<Real> (std::string_view name, const XdrMODE mode, const unsigned int read_flags, bool partition_agnostic);
582 #endif
583 
584 } // namespace libMesh
std::string name(const ElemQuality q)
This function returns a string containing some name for q.
Definition: elem_quality.C:42
bool writing() const
Definition: xdr_cxx.h:129
void write(std::string_view name, const XdrMODE, const unsigned int write_flags=(WRITE_DATA), bool partition_agnostic=true) const
Write the systems to disk using the XDR data format.
void close()
Closes the file if it is open.
Definition: xdr_cxx.C:277
MeshBase & mesh
const Parallel::Communicator & comm() const
void clean_refinement_flags()
Sets the refinement flag to Elem::DO_NOTHING for each element in the mesh.
virtual void fix_broken_node_and_element_numbering()=0
There is no reason for a user to ever call this function.
The libMesh namespace provides an interface to certain functionality in the library.
const T_sys & get_system(std::string_view name) const
This is the MeshBase class.
Definition: mesh_base.h:75
MeshBase & _mesh
The mesh data structure.
XdrMODE
Defines an enum for read/write mode in Xdr format.
Definition: enum_xdr_mode.h:35
void data(T &a, std::string_view comment="")
Inputs or outputs a single value.
Definition: xdr_cxx.C:778
void set_basic_system_only()
Sets the system to be "basic only": i.e.
Definition: system.h:2422
Implements (adaptive) mesh refinement algorithms for a MeshBase.
std::string get_io_compatibility_version()
Specifier for I/O file compatibility features.
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
Manages consistently variables, degrees of freedom, and coefficient vectors.
Definition: system.h:96
libmesh_assert(ctx)
void read(std::string_view name, const XdrMODE, const unsigned int read_flags=(READ_HEADER|READ_DATA), bool partition_agnostic=true)
Read & initialize the systems from disk using the XDR data format.
void globally_renumber_nodes_and_elements(MeshBase &)
There is no reason for a user to ever call this function.
Definition: mesh_tools.C:2542
void broadcast(T &data, const unsigned int root_id=0, const bool identical_sizes=false) const
This class implements a C++ interface to the XDR (eXternal Data Representation) format.
Definition: xdr_cxx.h:67
void set_version(int ver)
Sets the version of the file that is being read.
Definition: xdr_cxx.h:171
const MeshBase & get_mesh() const
bool reading() const
Definition: xdr_cxx.h:123
virtual void init()
Initialize all the systems.
virtual System & add_system(std::string_view system_type, std::string_view name)
Add the system of type system_type named name to the systems array.
processor_id_type processor_id() const
void update()
Updates local values for all the systems.
std::map< std::string, std::unique_ptr< System >, std::less<> > _systems
Data structure holding the systems.