Line data Source code
1 : // The libMesh Finite Element Library.
2 : // Copyright (C) 2002-2026 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 :
4 : // This library is free software; you can redistribute it and/or
5 : // modify it under the terms of the GNU Lesser General Public
6 : // License as published by the Free Software Foundation; either
7 : // version 2.1 of the License, or (at your option) any later version.
8 :
9 : // This library is distributed in the hope that it will be useful,
10 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : // Lesser General Public License for more details.
13 :
14 : // You should have received a copy of the GNU Lesser General Public
15 : // License along with this library; if not, write to the Free Software
16 : // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 :
18 :
19 : // Local includes
20 : #include "libmesh/libmesh_logging.h"
21 : #include "libmesh/mesh_base.h"
22 : #include "libmesh/mesh_communication.h"
23 : #include "libmesh/namebased_io.h"
24 : #include "libmesh/dyna_io.h"
25 : #include "libmesh/exodusII_io.h"
26 : #include "libmesh/gmv_io.h"
27 : #include "libmesh/tecplot_io.h"
28 : #include "libmesh/tetgen_io.h"
29 : #include "libmesh/ucd_io.h"
30 : #include "libmesh/unv_io.h"
31 : #include "libmesh/utility.h"
32 : #include "libmesh/matlab_io.h"
33 : #include "libmesh/off_io.h"
34 : #include "libmesh/medit_io.h"
35 : #include "libmesh/nemesis_io.h"
36 : #include "libmesh/gmsh_io.h"
37 : #include "libmesh/fro_io.h"
38 : #include "libmesh/stl_io.h"
39 : #include "libmesh/xdr_io.h"
40 : #include "libmesh/vtk_io.h"
41 : #include "libmesh/abaqus_io.h"
42 : #include "libmesh/checkpoint_io.h"
43 : #include "libmesh/equation_systems.h"
44 : #include "libmesh/enum_xdr_mode.h"
45 : #include "libmesh/parallel.h" // broadcast
46 :
47 : // C++ includes
48 : #include <iomanip>
49 : #include <fstream>
50 : #include <vector>
51 :
52 : #ifdef LIBMESH_HAVE_UNISTD_H
53 : #include <sys/types.h>
54 : #include <unistd.h> // for getpid() on Unix
55 : #endif
56 :
57 : #ifdef LIBMESH_HAVE_PROCESS_H
58 : #include <process.h> // for getpid() on Windows
59 : #endif
60 :
61 :
62 : namespace libMesh
63 : {
64 :
65 :
66 :
67 : // ------------------------------------------------------------
68 : // NameBasedIO members
69 5634 : void NameBasedIO::read (const std::string & name)
70 : {
71 5634 : MeshBase & mymesh = MeshInput<MeshBase>::mesh();
72 :
73 5634 : const std::string_view basename = Utility::basename_of(name);
74 :
75 : using Utility::ends_with;
76 : using Utility::contains;
77 :
78 : // See if the file exists. Perform this check on all processors
79 : // so that the code is terminated properly in the case that the
80 : // file does not exist.
81 :
82 : // For Nemesis files, the name we try to read will have suffixes
83 : // identifying processor rank
84 5808 : if (ends_with(basename, ".nem") || ends_with(basename, ".n"))
85 : {
86 0 : std::ostringstream full_name;
87 :
88 : // Find the length of a string which represents the highest processor ID
89 0 : full_name << (mymesh.n_processors());
90 0 : int field_width = cast_int<int>(full_name.str().size());
91 :
92 : // reset the string stream
93 0 : full_name.str("");
94 :
95 : // And build up the full filename
96 : full_name << name
97 0 : << '.' << mymesh.n_processors()
98 0 : << '.' << std::setfill('0') << std::setw(field_width) << mymesh.processor_id();
99 :
100 0 : std::ifstream in (full_name.str().c_str());
101 0 : libmesh_error_msg_if(!in.good(), "ERROR: cannot locate specified file:\n\t" << full_name.str());
102 0 : }
103 5634 : else if (contains(basename, ".cp")) {} // Do error checking in the reader
104 : else
105 : {
106 5832 : std::ifstream in (name.c_str());
107 5492 : libmesh_error_msg_if(!in.good(), "ERROR: cannot locate specified file:\n\t" << name);
108 5152 : }
109 :
110 : // Look for parallel formats first
111 5634 : if (is_parallel_file_format(basename))
112 : {
113 : // no need to handle bz2 files here -- the Xdr class does that.
114 4651 : if (contains(basename, ".xda") ||
115 979 : contains(basename, ".xdr"))
116 : {
117 4038 : XdrIO xdr_io(mymesh);
118 :
119 : // .xda* ==> bzip2/gzip/ASCII flavors
120 3794 : if (contains(basename, ".xda"))
121 : {
122 3221 : xdr_io.binary() = false;
123 3221 : xdr_io.read (name);
124 : }
125 : else // .xdr* ==> true binary XDR file
126 : {
127 573 : xdr_io.binary() = true;
128 573 : xdr_io.read (name);
129 : }
130 :
131 : // The xdr_io object gets constructed with legacy() == false.
132 : // if legacy() == true then it means that a legacy file was detected and
133 : // thus processor 0 performed the read. We therefore need to broadcast the
134 : // mesh. Further, for this flavor of mesh solution data ordering is tied
135 : // to the node ordering, so we better not reorder the nodes!
136 3794 : if (xdr_io.legacy())
137 : {
138 0 : mymesh.allow_renumbering(false);
139 0 : MeshCommunication().broadcast(mymesh);
140 : }
141 :
142 : // libHilbert-enabled libMesh builds should construct files
143 : // with a canonical node ordering, which libHilbert-enabled
144 : // builds will be able to read in again regardless of any
145 : // renumbering. So in that case we're free to renumber.
146 : // However, if either the writer or the reader of this file
147 : // don't have libHilbert, then we'll have to skip
148 : // renumbering because we need the numbering to remain
149 : // consistent with any solution file we read in next.
150 : #ifdef LIBMESH_HAVE_LIBHILBERT
151 : // if (!xdr_io.libhilbert_ordering())
152 : // skip_renumber_nodes_and_elements = true;
153 : #else
154 : mymesh.allow_renumbering(false);
155 : #endif
156 3550 : }
157 284 : else if (contains(basename, ".nem") ||
158 284 : contains(basename, ".n"))
159 0 : Nemesis_IO(mymesh).read (name);
160 142 : else if (contains(basename, ".cp"))
161 : {
162 142 : if (contains(basename, ".cpa"))
163 71 : CheckpointIO(mymesh, false).read(name);
164 : else
165 71 : CheckpointIO(mymesh, true).read(name);
166 : }
167 : }
168 :
169 : // Serial mesh formats
170 : else
171 : {
172 : // Read the file based on extension. Only processor 0
173 : // needs to read the mesh. It will then broadcast it and
174 : // the other processors will pick it up
175 1746 : if (mymesh.processor_id() == 0)
176 : {
177 48 : LOG_SCOPE("read()", "NameBasedIO");
178 :
179 330 : std::ostringstream pid_suffix;
180 282 : pid_suffix << '_' << getpid();
181 : // Nasty hack for reading/writing zipped files
182 282 : std::string new_name = name;
183 282 : if (ends_with(name, ".bz2"))
184 : {
185 : #ifdef LIBMESH_HAVE_BZIP
186 0 : new_name.erase(new_name.end() - 4, new_name.end());
187 0 : new_name += pid_suffix.str();
188 0 : std::string system_string = "bunzip2 -f -k -c ";
189 0 : system_string += name + " > " + new_name;
190 0 : LOG_SCOPE("system(bunzip2)", "NameBasedIO");
191 0 : if (std::system(system_string.c_str()))
192 0 : libmesh_file_error(system_string);
193 : #else
194 : libmesh_error_msg("ERROR: need bzip2/bunzip2 to open .bz2 file " << name);
195 : #endif
196 : }
197 282 : else if (ends_with(name, ".xz"))
198 : {
199 : #ifdef LIBMESH_HAVE_XZ
200 0 : new_name.erase(new_name.end() - 3, new_name.end());
201 0 : new_name += pid_suffix.str();
202 0 : std::string system_string = "xz -f -d -k -c ";
203 0 : system_string += name + " > " + new_name;
204 0 : LOG_SCOPE("system(xz -d)", "XdrIO");
205 0 : if (std::system(system_string.c_str()))
206 0 : libmesh_file_error(system_string);
207 : #else
208 : libmesh_error_msg("ERROR: need xz to open .xz file " << name);
209 : #endif
210 : }
211 :
212 282 : if (contains(basename, ".mat"))
213 0 : MatlabIO(mymesh).read(new_name);
214 :
215 282 : else if (contains(basename, ".ucd"))
216 46 : UCDIO(mymesh).read (new_name);
217 :
218 483 : else if (contains(basename, ".off") ||
219 525 : contains(basename, ".ogl") ||
220 504 : contains(basename, ".oogl"))
221 23 : OFFIO(mymesh).read (new_name);
222 :
223 246 : else if (contains(basename, ".unv"))
224 12 : UNVIO(mymesh).read (new_name);
225 :
226 468 : else if (contains(basename, ".node") ||
227 468 : contains(basename, ".ele"))
228 0 : TetGenIO(mymesh).read (new_name);
229 :
230 448 : else if (contains(basename, ".exd") ||
231 300 : contains(basename, ".exo") ||
232 88 : contains(basename, ".e"))
233 210 : ExodusII_IO(mymesh).read (new_name);
234 :
235 24 : else if (contains(basename, ".msh"))
236 0 : GmshIO(mymesh).read (new_name);
237 :
238 24 : else if (contains(basename, ".gmv"))
239 0 : GMVIO(mymesh).read (new_name);
240 :
241 24 : else if (contains(basename, ".stl"))
242 0 : STLIO(mymesh).read (new_name);
243 :
244 48 : else if (contains(basename, ".pvtu") ||
245 48 : contains(basename, ".vtu"))
246 0 : VTKIO(mymesh).read(new_name);
247 :
248 24 : else if (contains(basename, ".inp"))
249 0 : AbaqusIO(mymesh).read(new_name);
250 :
251 48 : else if (contains(basename, ".bext") ||
252 48 : contains(basename, ".bxt"))
253 0 : DynaIO(mymesh).read (new_name);
254 :
255 24 : else if (contains(basename, ".bez"))
256 24 : DynaIO(mymesh, false).read (new_name);
257 :
258 : else
259 : {
260 0 : libmesh_error_msg(" ERROR: Unrecognized file extension: " \
261 : << name \
262 : << "\n I understand the following:\n\n" \
263 : << " *.bext -- Bezier files in DYNA format\n" \
264 : << " *.bez -- Bezier DYNA files, omit spline nodes\n" \
265 : << " *.bxt -- Bezier files in DYNA format\n" \
266 : << " *.cpa -- libMesh Checkpoint ASCII format\n" \
267 : << " *.cpr -- libMesh Checkpoint binary format\n" \
268 : << " *.e -- Sandia's ExodusII format\n" \
269 : << " *.exo -- Sandia's ExodusII format\n" \
270 : << " *.gmv -- LANL's General Mesh Viewer format\n" \
271 : << " *.inp -- Abaqus .inp format\n" \
272 : << " *.mat -- Matlab triangular ASCII file\n" \
273 : << " *.n -- Sandia's Nemesis format\n" \
274 : << " *.nem -- Sandia's Nemesis format\n" \
275 : << " *.off -- OOGL OFF surface format\n" \
276 : << " *.ogl -- OOGL OFF surface format\n" \
277 : << " *.oogl -- OOGL OFF surface format\n" \
278 : << " *.pvtu -- Paraview VTK format\n" \
279 : << " *.stl -- STereoLithography triangulation format\n" \
280 : << " *.ucd -- AVS's ASCII UCD format\n" \
281 : << " *.unv -- I-deas Universal format\n" \
282 : << " *.vtu -- Paraview VTK format\n" \
283 : << " *.xda -- libMesh ASCII format\n" \
284 : << " *.xdr -- libMesh binary format\n" \
285 : << " *.gz -- any above format gzipped\n" \
286 : << " *.bz2 -- any above format bzip2'ed\n" \
287 : << " *.xz -- any above format xzipped\n" \
288 : );
289 : }
290 :
291 : // If we temporarily decompressed a file, remove the
292 : // uncompressed version
293 282 : if (ends_with(basename, ".bz2"))
294 0 : std::remove(new_name.c_str());
295 282 : if (ends_with(basename, ".xz"))
296 0 : std::remove(new_name.c_str());
297 234 : }
298 :
299 : // Send the mesh & bcs (which are now only on processor 0) to the other
300 : // processors
301 1698 : MeshCommunication().broadcast (mymesh);
302 : }
303 5634 : }
304 :
305 :
306 2938 : void NameBasedIO::write (const std::string & name)
307 : {
308 1196 : const MeshBase & mymesh = MeshOutput<MeshBase>::mesh();
309 :
310 2938 : const std::string_view basename = Utility::basename_of(name);
311 :
312 : using Utility::contains;
313 : using Utility::ends_with;
314 :
315 : // parallel formats are special -- they may choose to write
316 : // separate files, let's not try to handle the zipping here.
317 2938 : if (is_parallel_file_format(basename))
318 : {
319 : // no need to handle bz2 files here -- the Xdr class does that.
320 1779 : if (contains(basename, ".xda"))
321 922 : XdrIO(mymesh).write(name);
322 :
323 857 : else if (contains(basename, ".xdr"))
324 573 : XdrIO(mymesh,true).write(name);
325 :
326 426 : else if (contains(basename, ".nem") ||
327 288 : contains(basename, ".n"))
328 142 : Nemesis_IO(mymesh).write(name);
329 :
330 142 : else if (contains(basename, ".cpa"))
331 71 : CheckpointIO(mymesh,false).write(name);
332 :
333 71 : else if (contains(basename, ".cpr"))
334 71 : CheckpointIO(mymesh,true).write(name);
335 :
336 : else
337 0 : libmesh_error_msg("Couldn't deduce filetype for " << name);
338 : }
339 :
340 : // serial file formats
341 : else
342 : {
343 : // Nasty hack for reading/writing zipped files
344 1159 : std::string new_name = name;
345 1159 : int pid_0 = 0;
346 1705 : if (mymesh.processor_id() == 0)
347 1096 : pid_0 = getpid();
348 1159 : mymesh.comm().broadcast(pid_0);
349 2251 : std::ostringstream pid_suffix;
350 1159 : pid_suffix << '_' << pid_0;
351 :
352 1159 : if (ends_with(name, ".bz2"))
353 : {
354 0 : new_name.erase(new_name.end() - 4, new_name.end());
355 0 : new_name += pid_suffix.str();
356 : }
357 1159 : else if (ends_with(name, ".xz"))
358 : {
359 0 : new_name.erase(new_name.end() - 3, new_name.end());
360 0 : new_name += pid_suffix.str();
361 : }
362 :
363 : // New scope so that io will close before we try to zip the file
364 : {
365 : // Write the file based on extension
366 1159 : if (contains(basename, ".dat"))
367 1080 : TecplotIO(mymesh).write (new_name);
368 :
369 79 : else if (contains(basename, ".plt"))
370 0 : TecplotIO(mymesh,true).write (new_name);
371 :
372 79 : else if (contains(basename, ".ucd"))
373 0 : UCDIO (mymesh).write (new_name);
374 :
375 79 : else if (contains(basename, ".gmv"))
376 0 : if (mymesh.n_partitions() > 1)
377 0 : GMVIO(mymesh).write (new_name);
378 : else
379 : {
380 0 : GMVIO io(mymesh);
381 0 : io.partitioning() = false;
382 0 : io.write (new_name);
383 0 : }
384 :
385 152 : else if (contains(basename, ".exd") ||
386 164 : contains(basename, ".exo") ||
387 85 : contains(basename, ".e"))
388 79 : ExodusII_IO(mymesh).write(new_name);
389 :
390 0 : else if (contains(basename, ".unv"))
391 0 : UNVIO(mymesh).write (new_name);
392 :
393 0 : else if (contains(basename, ".mesh"))
394 0 : MEDITIO(mymesh).write (new_name);
395 :
396 0 : else if (contains(basename, ".poly"))
397 0 : TetGenIO(mymesh).write (new_name);
398 :
399 0 : else if (contains(basename, ".msh"))
400 0 : GmshIO(mymesh).write (new_name);
401 :
402 0 : else if (contains(basename, ".fro"))
403 0 : FroIO(mymesh).write (new_name);
404 :
405 0 : else if (contains(basename, ".pvtu"))
406 0 : VTKIO(mymesh).write (name);
407 :
408 0 : else if (contains(basename, ".stl"))
409 0 : STLIO(mymesh).write (new_name);
410 :
411 : else
412 : {
413 : libMesh::err
414 0 : << " ERROR: Unrecognized file extension: " << name
415 0 : << "\n I understand the following:\n\n"
416 0 : << " *.cpa -- libMesh ASCII checkpoint format\n"
417 0 : << " *.cpr -- libMesh binary checkpoint format,\n"
418 0 : << " *.dat -- Tecplot ASCII file\n"
419 0 : << " *.e -- Sandia's ExodusII format\n"
420 0 : << " *.exo -- Sandia's ExodusII format\n"
421 0 : << " *.fro -- ACDL's surface triangulation file\n"
422 0 : << " *.gmv -- LANL's GMV (General Mesh Viewer) format\n"
423 0 : << " *.mesh -- MEdit mesh format\n"
424 0 : << " *.msh -- GMSH ASCII file\n"
425 0 : << " *.n -- Sandia's Nemesis format\n"
426 0 : << " *.nem -- Sandia's Nemesis format\n"
427 0 : << " *.plt -- Tecplot binary file\n"
428 0 : << " *.poly -- TetGen ASCII file\n"
429 0 : << " *.pvtu -- VTK (paraview-readable) format\n"
430 0 : << " *.stl -- STereoLithography triangulation format\n" \
431 0 : << " *.ucd -- AVS's ASCII UCD format\n"
432 0 : << " *.unv -- I-deas Universal format\n"
433 0 : << " *.xda -- libMesh ASCII format\n"
434 0 : << " *.xdr -- libMesh binary format,\n"
435 0 : << std::endl
436 0 : << "\n Exiting without writing output\n";
437 : }
438 : }
439 :
440 : // Nasty hack for reading/writing zipped files
441 1159 : if (ends_with(basename, ".bz2"))
442 : {
443 0 : LOG_SCOPE("system(bzip2)", "NameBasedIO");
444 0 : if (mymesh.processor_id() == 0)
445 : {
446 0 : std::string system_string = "bzip2 -f -c ";
447 0 : system_string += new_name + " > " + name;
448 0 : if (std::system(system_string.c_str()))
449 0 : libmesh_file_error(system_string);
450 0 : std::remove(new_name.c_str());
451 : }
452 0 : mymesh.comm().barrier();
453 : }
454 1159 : if (ends_with(basename, ".xz"))
455 : {
456 0 : LOG_SCOPE("system(xz)", "NameBasedIO");
457 0 : if (mymesh.processor_id() == 0)
458 : {
459 0 : std::string system_string = "xz -f -c ";
460 0 : system_string += new_name + " > " + name;
461 0 : if (std::system(system_string.c_str()))
462 0 : libmesh_file_error(system_string);
463 0 : std::remove(new_name.c_str());
464 : }
465 0 : mymesh.comm().barrier();
466 : }
467 67 : }
468 2938 : }
469 :
470 :
471 0 : void NameBasedIO::write_nodal_data (const std::string & name,
472 : const std::vector<Number> & v,
473 : const std::vector<std::string> & vn)
474 : {
475 0 : const MeshBase & mymesh = MeshOutput<MeshBase>::mesh();
476 :
477 : using Utility::contains;
478 : using Utility::ends_with;
479 :
480 : // Write the file based on extension
481 0 : if (contains(name, ".dat"))
482 0 : TecplotIO(mymesh).write_nodal_data (name, v, vn);
483 :
484 0 : else if (contains(name, ".exd") ||
485 0 : contains(name, ".exo") ||
486 0 : contains(name, ".e"))
487 0 : ExodusII_IO(mymesh).write_nodal_data(name, v, vn);
488 :
489 0 : else if (contains(name, ".gmv"))
490 : {
491 0 : if (mymesh.n_subdomains() > 1)
492 0 : GMVIO(mymesh).write_nodal_data (name, v, vn);
493 : else
494 : {
495 0 : GMVIO io(mymesh);
496 0 : io.partitioning() = false;
497 0 : io.write_nodal_data (name, v, vn);
498 0 : }
499 : }
500 :
501 0 : else if (contains(name, ".mesh"))
502 0 : MEDITIO(mymesh).write_nodal_data (name, v, vn);
503 :
504 0 : else if (contains(name, ".msh"))
505 0 : GmshIO(mymesh).write_nodal_data (name, v, vn);
506 :
507 0 : else if (contains(name, ".nem") ||
508 0 : contains(name, ".n"))
509 0 : Nemesis_IO(mymesh).write_nodal_data(name, v, vn);
510 :
511 0 : else if (contains(name, ".plt"))
512 0 : TecplotIO(mymesh,true).write_nodal_data (name, v, vn);
513 :
514 0 : else if (contains(name, ".pvtu"))
515 0 : VTKIO(mymesh).write_nodal_data (name, v, vn);
516 :
517 0 : else if (contains(name, ".ucd"))
518 0 : UCDIO (mymesh).write_nodal_data (name, v, vn);
519 :
520 : else
521 : {
522 : libMesh::err
523 0 : << " ERROR: Unrecognized file extension: " << name
524 0 : << "\n I understand the following:\n\n"
525 0 : << " *.dat -- Tecplot ASCII file\n"
526 0 : << " *.e -- Sandia's ExodusII format\n"
527 0 : << " *.exo -- Sandia's ExodusII format\n"
528 0 : << " *.gmv -- LANL's GMV (General Mesh Viewer) format\n"
529 0 : << " *.mesh -- MEdit mesh format\n"
530 0 : << " *.msh -- GMSH ASCII file\n"
531 0 : << " *.n -- Sandia's Nemesis format\n"
532 0 : << " *.nem -- Sandia's Nemesis format\n"
533 0 : << " *.plt -- Tecplot binary file\n"
534 0 : << " *.pvtu -- Paraview VTK file\n"
535 0 : << " *.ucd -- AVS's ASCII UCD format\n"
536 0 : << "\n Exiting without writing output\n";
537 : }
538 0 : }
539 :
540 :
541 0 : void NameBasedIO::write_equation_systems (const std::string & filename,
542 : const EquationSystems & es,
543 : const std::set<std::string> * system_names)
544 : {
545 : // XDA/XDR require a separate code path, and currently only support
546 : // writing complete restarts
547 0 : if (!system_names)
548 : {
549 : const std::string_view basename =
550 0 : Utility::basename_of(filename);
551 :
552 0 : if (Utility::contains(basename, ".xda"))
553 : {
554 0 : es.write(filename,WRITE,
555 : EquationSystems::WRITE_DATA |
556 : EquationSystems::WRITE_ADDITIONAL_DATA);
557 0 : return;
558 : }
559 0 : else if (Utility::contains(basename, ".xdr"))
560 : {
561 0 : es.write(filename,ENCODE,
562 : EquationSystems::WRITE_DATA |
563 : EquationSystems::WRITE_ADDITIONAL_DATA);
564 0 : return;
565 : }
566 : }
567 :
568 : // Other formats just use the default "write nodal values" path
569 : MeshOutput<MeshBase>::write_equation_systems
570 0 : (filename, es, system_names);
571 : }
572 :
573 :
574 8572 : bool NameBasedIO::is_parallel_file_format(std::string_view name)
575 : {
576 : using Utility::contains;
577 : using Utility::ends_with;
578 15678 : return (contains(name, ".xda") || contains(name, ".xdr") ||
579 17571 : ends_with(name, ".nem") || ends_with(name, ".n") ||
580 4685 : contains(name, ".cp"));
581 : }
582 :
583 :
584 : } // namespace libMesh
|