Line data Source code
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 : // 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 5210 : void NameBasedIO::read (const std::string & name)
70 : {
71 5210 : MeshBase & mymesh = MeshInput<MeshBase>::mesh();
72 :
73 5210 : 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 5372 : 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 5210 : else if (contains(basename, ".cp")) {} // Do error checking in the reader
104 : else
105 : {
106 5384 : std::ifstream in (name.c_str());
107 5068 : libmesh_error_msg_if(!in.good(), "ERROR: cannot locate specified file:\n\t" << name);
108 4752 : }
109 :
110 : // Look for parallel formats first
111 5210 : if (is_parallel_file_format(basename))
112 : {
113 : // no need to handle bz2 files here -- the Xdr class does that.
114 4369 : if (contains(basename, ".xda") ||
115 971 : contains(basename, ".xdr"))
116 : {
117 3740 : XdrIO xdr_io(mymesh);
118 :
119 : // .xda* ==> bzip2/gzip/ASCII flavors
120 3512 : if (contains(basename, ".xda"))
121 : {
122 2939 : xdr_io.binary() = false;
123 2939 : 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 3512 : 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 3284 : }
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 1600 : if (mymesh.processor_id() == 0)
176 : {
177 44 : LOG_SCOPE("read()", "NameBasedIO");
178 :
179 302 : std::ostringstream pid_suffix;
180 258 : pid_suffix << '_' << getpid();
181 : // Nasty hack for reading/writing zipped files
182 258 : std::string new_name = name;
183 258 : 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 258 : 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 258 : if (contains(basename, ".mat"))
213 0 : MatlabIO(mymesh).read(new_name);
214 :
215 258 : else if (contains(basename, ".ucd"))
216 46 : UCDIO(mymesh).read (new_name);
217 :
218 437 : else if (contains(basename, ".off") ||
219 475 : contains(basename, ".ogl") ||
220 456 : contains(basename, ".oogl"))
221 23 : OFFIO(mymesh).read (new_name);
222 :
223 222 : else if (contains(basename, ".unv"))
224 12 : UNVIO(mymesh).read (new_name);
225 :
226 420 : else if (contains(basename, ".node") ||
227 420 : contains(basename, ".ele"))
228 0 : TetGenIO(mymesh).read (new_name);
229 :
230 420 : else if (contains(basename, ".exd") ||
231 250 : contains(basename, ".e"))
232 186 : ExodusII_IO(mymesh).read (new_name);
233 :
234 24 : else if (contains(basename, ".msh"))
235 0 : GmshIO(mymesh).read (new_name);
236 :
237 24 : else if (contains(basename, ".gmv"))
238 0 : GMVIO(mymesh).read (new_name);
239 :
240 24 : else if (contains(basename, ".stl"))
241 0 : STLIO(mymesh).read (new_name);
242 :
243 48 : else if (contains(basename, ".pvtu") ||
244 48 : contains(basename, ".vtu"))
245 0 : VTKIO(mymesh).read(new_name);
246 :
247 24 : else if (contains(basename, ".inp"))
248 0 : AbaqusIO(mymesh).read(new_name);
249 :
250 48 : else if (contains(basename, ".bext") ||
251 48 : contains(basename, ".bxt"))
252 0 : DynaIO(mymesh).read (new_name);
253 :
254 24 : else if (contains(basename, ".bez"))
255 24 : DynaIO(mymesh, false).read (new_name);
256 :
257 : else
258 : {
259 0 : libmesh_error_msg(" ERROR: Unrecognized file extension: " \
260 : << name \
261 : << "\n I understand the following:\n\n" \
262 : << " *.bext -- Bezier files in DYNA format\n" \
263 : << " *.bez -- Bezier DYNA files, omit spline nodes\n" \
264 : << " *.bxt -- Bezier files in DYNA format\n" \
265 : << " *.cpa -- libMesh Checkpoint ASCII format\n" \
266 : << " *.cpr -- libMesh Checkpoint binary format\n" \
267 : << " *.e -- Sandia's ExodusII format\n" \
268 : << " *.exd -- Sandia's ExodusII format\n" \
269 : << " *.gmv -- LANL's General Mesh Viewer format\n" \
270 : << " *.inp -- Abaqus .inp format\n" \
271 : << " *.mat -- Matlab triangular ASCII file\n" \
272 : << " *.n -- Sandia's Nemesis format\n" \
273 : << " *.nem -- Sandia's Nemesis format\n" \
274 : << " *.off -- OOGL OFF surface format\n" \
275 : << " *.ogl -- OOGL OFF surface format\n" \
276 : << " *.oogl -- OOGL OFF surface format\n" \
277 : << " *.pvtu -- Paraview VTK format\n" \
278 : << " *.stl -- STereoLithography triangulation format\n" \
279 : << " *.ucd -- AVS's ASCII UCD format\n" \
280 : << " *.unv -- I-deas Universal format\n" \
281 : << " *.vtu -- Paraview VTK format\n" \
282 : << " *.xda -- libMesh ASCII format\n" \
283 : << " *.xdr -- libMesh binary format\n" \
284 : << " *.gz -- any above format gzipped\n" \
285 : << " *.bz2 -- any above format bzip2'ed\n" \
286 : << " *.xz -- any above format xzipped\n" \
287 : );
288 : }
289 :
290 : // If we temporarily decompressed a file, remove the
291 : // uncompressed version
292 258 : if (ends_with(basename, ".bz2"))
293 0 : std::remove(new_name.c_str());
294 258 : if (ends_with(basename, ".xz"))
295 0 : std::remove(new_name.c_str());
296 214 : }
297 :
298 : // Send the mesh & bcs (which are now only on processor 0) to the other
299 : // processors
300 1556 : MeshCommunication().broadcast (mymesh);
301 : }
302 5210 : }
303 :
304 :
305 2868 : void NameBasedIO::write (const std::string & name)
306 : {
307 1192 : const MeshBase & mymesh = MeshOutput<MeshBase>::mesh();
308 :
309 2868 : const std::string_view basename = Utility::basename_of(name);
310 :
311 : using Utility::contains;
312 : using Utility::ends_with;
313 :
314 : // parallel formats are special -- they may choose to write
315 : // separate files, let's not try to handle the zipping here.
316 2868 : if (is_parallel_file_format(basename))
317 : {
318 : // no need to handle bz2 files here -- the Xdr class does that.
319 1709 : if (contains(basename, ".xda"))
320 852 : XdrIO(mymesh).write(name);
321 :
322 857 : else if (contains(basename, ".xdr"))
323 573 : XdrIO(mymesh,true).write(name);
324 :
325 426 : else if (contains(basename, ".nem") ||
326 288 : contains(basename, ".n"))
327 142 : Nemesis_IO(mymesh).write(name);
328 :
329 142 : else if (contains(basename, ".cpa"))
330 71 : CheckpointIO(mymesh,false).write(name);
331 :
332 71 : else if (contains(basename, ".cpr"))
333 71 : CheckpointIO(mymesh,true).write(name);
334 :
335 : else
336 0 : libmesh_error_msg("Couldn't deduce filetype for " << name);
337 : }
338 :
339 : // serial file formats
340 : else
341 : {
342 : // Nasty hack for reading/writing zipped files
343 1159 : std::string new_name = name;
344 1159 : int pid_0 = 0;
345 1705 : if (mymesh.processor_id() == 0)
346 1096 : pid_0 = getpid();
347 1159 : mymesh.comm().broadcast(pid_0);
348 2251 : std::ostringstream pid_suffix;
349 1159 : pid_suffix << '_' << pid_0;
350 :
351 1159 : if (ends_with(name, ".bz2"))
352 : {
353 0 : new_name.erase(new_name.end() - 4, new_name.end());
354 0 : new_name += pid_suffix.str();
355 : }
356 1159 : else if (ends_with(name, ".xz"))
357 : {
358 0 : new_name.erase(new_name.end() - 3, new_name.end());
359 0 : new_name += pid_suffix.str();
360 : }
361 :
362 : // New scope so that io will close before we try to zip the file
363 : {
364 : // Write the file based on extension
365 1159 : if (contains(basename, ".dat"))
366 1080 : TecplotIO(mymesh).write (new_name);
367 :
368 79 : else if (contains(basename, ".plt"))
369 0 : TecplotIO(mymesh,true).write (new_name);
370 :
371 79 : else if (contains(basename, ".ucd"))
372 0 : UCDIO (mymesh).write (new_name);
373 :
374 79 : else if (contains(basename, ".gmv"))
375 0 : if (mymesh.n_partitions() > 1)
376 0 : GMVIO(mymesh).write (new_name);
377 : else
378 : {
379 0 : GMVIO io(mymesh);
380 0 : io.partitioning() = false;
381 0 : io.write (new_name);
382 0 : }
383 :
384 158 : else if (contains(basename, ".exd") ||
385 85 : contains(basename, ".e"))
386 79 : ExodusII_IO(mymesh).write(new_name);
387 :
388 0 : else if (contains(basename, ".unv"))
389 0 : UNVIO(mymesh).write (new_name);
390 :
391 0 : else if (contains(basename, ".mesh"))
392 0 : MEDITIO(mymesh).write (new_name);
393 :
394 0 : else if (contains(basename, ".poly"))
395 0 : TetGenIO(mymesh).write (new_name);
396 :
397 0 : else if (contains(basename, ".msh"))
398 0 : GmshIO(mymesh).write (new_name);
399 :
400 0 : else if (contains(basename, ".fro"))
401 0 : FroIO(mymesh).write (new_name);
402 :
403 0 : else if (contains(basename, ".pvtu"))
404 0 : VTKIO(mymesh).write (name);
405 :
406 0 : else if (contains(basename, ".stl"))
407 0 : STLIO(mymesh).write (new_name);
408 :
409 : else
410 : {
411 : libMesh::err
412 0 : << " ERROR: Unrecognized file extension: " << name
413 0 : << "\n I understand the following:\n\n"
414 0 : << " *.cpa -- libMesh ASCII checkpoint format\n"
415 0 : << " *.cpr -- libMesh binary checkpoint format,\n"
416 0 : << " *.dat -- Tecplot ASCII file\n"
417 0 : << " *.e -- Sandia's ExodusII format\n"
418 0 : << " *.exd -- Sandia's ExodusII format\n"
419 0 : << " *.fro -- ACDL's surface triangulation file\n"
420 0 : << " *.gmv -- LANL's GMV (General Mesh Viewer) format\n"
421 0 : << " *.mesh -- MEdit mesh format\n"
422 0 : << " *.msh -- GMSH ASCII file\n"
423 0 : << " *.n -- Sandia's Nemesis format\n"
424 0 : << " *.nem -- Sandia's Nemesis format\n"
425 0 : << " *.plt -- Tecplot binary file\n"
426 0 : << " *.poly -- TetGen ASCII file\n"
427 0 : << " *.pvtu -- VTK (paraview-readable) format\n"
428 0 : << " *.stl -- STereoLithography triangulation format\n" \
429 0 : << " *.ucd -- AVS's ASCII UCD format\n"
430 0 : << " *.unv -- I-deas Universal format\n"
431 0 : << " *.xda -- libMesh ASCII format\n"
432 0 : << " *.xdr -- libMesh binary format,\n"
433 0 : << std::endl
434 0 : << "\n Exiting without writing output\n";
435 : }
436 : }
437 :
438 : // Nasty hack for reading/writing zipped files
439 1159 : if (ends_with(basename, ".bz2"))
440 : {
441 0 : LOG_SCOPE("system(bzip2)", "NameBasedIO");
442 0 : if (mymesh.processor_id() == 0)
443 : {
444 0 : std::string system_string = "bzip2 -f -c ";
445 0 : system_string += new_name + " > " + name;
446 0 : if (std::system(system_string.c_str()))
447 0 : libmesh_file_error(system_string);
448 0 : std::remove(new_name.c_str());
449 : }
450 0 : mymesh.comm().barrier();
451 : }
452 1159 : if (ends_with(basename, ".xz"))
453 : {
454 0 : LOG_SCOPE("system(xz)", "NameBasedIO");
455 0 : if (mymesh.processor_id() == 0)
456 : {
457 0 : std::string system_string = "xz -f -c ";
458 0 : system_string += new_name + " > " + name;
459 0 : if (std::system(system_string.c_str()))
460 0 : libmesh_file_error(system_string);
461 0 : std::remove(new_name.c_str());
462 : }
463 0 : mymesh.comm().barrier();
464 : }
465 67 : }
466 2868 : }
467 :
468 :
469 0 : void NameBasedIO::write_nodal_data (const std::string & name,
470 : const std::vector<Number> & v,
471 : const std::vector<std::string> & vn)
472 : {
473 0 : const MeshBase & mymesh = MeshOutput<MeshBase>::mesh();
474 :
475 : using Utility::contains;
476 : using Utility::ends_with;
477 :
478 : // Write the file based on extension
479 0 : if (contains(name, ".dat"))
480 0 : TecplotIO(mymesh).write_nodal_data (name, v, vn);
481 :
482 0 : else if (contains(name, ".exd") ||
483 0 : contains(name, ".e"))
484 0 : ExodusII_IO(mymesh).write_nodal_data(name, v, vn);
485 :
486 0 : else if (contains(name, ".gmv"))
487 : {
488 0 : if (mymesh.n_subdomains() > 1)
489 0 : GMVIO(mymesh).write_nodal_data (name, v, vn);
490 : else
491 : {
492 0 : GMVIO io(mymesh);
493 0 : io.partitioning() = false;
494 0 : io.write_nodal_data (name, v, vn);
495 0 : }
496 : }
497 :
498 0 : else if (contains(name, ".mesh"))
499 0 : MEDITIO(mymesh).write_nodal_data (name, v, vn);
500 :
501 0 : else if (contains(name, ".msh"))
502 0 : GmshIO(mymesh).write_nodal_data (name, v, vn);
503 :
504 0 : else if (contains(name, ".nem") ||
505 0 : contains(name, ".n"))
506 0 : Nemesis_IO(mymesh).write_nodal_data(name, v, vn);
507 :
508 0 : else if (contains(name, ".plt"))
509 0 : TecplotIO(mymesh,true).write_nodal_data (name, v, vn);
510 :
511 0 : else if (contains(name, ".pvtu"))
512 0 : VTKIO(mymesh).write_nodal_data (name, v, vn);
513 :
514 0 : else if (contains(name, ".ucd"))
515 0 : UCDIO (mymesh).write_nodal_data (name, v, vn);
516 :
517 : else
518 : {
519 : libMesh::err
520 0 : << " ERROR: Unrecognized file extension: " << name
521 0 : << "\n I understand the following:\n\n"
522 0 : << " *.dat -- Tecplot ASCII file\n"
523 0 : << " *.e -- Sandia's ExodusII format\n"
524 0 : << " *.exd -- Sandia's ExodusII format\n"
525 0 : << " *.gmv -- LANL's GMV (General Mesh Viewer) format\n"
526 0 : << " *.mesh -- MEdit mesh format\n"
527 0 : << " *.msh -- GMSH ASCII file\n"
528 0 : << " *.n -- Sandia's Nemesis format\n"
529 0 : << " *.nem -- Sandia's Nemesis format\n"
530 0 : << " *.plt -- Tecplot binary file\n"
531 0 : << " *.pvtu -- Paraview VTK file\n"
532 0 : << " *.ucd -- AVS's ASCII UCD format\n"
533 0 : << "\n Exiting without writing output\n";
534 : }
535 0 : }
536 :
537 :
538 0 : void NameBasedIO::write_equation_systems (const std::string & filename,
539 : const EquationSystems & es,
540 : const std::set<std::string> * system_names)
541 : {
542 : // XDA/XDR require a separate code path, and currently only support
543 : // writing complete restarts
544 0 : if (!system_names)
545 : {
546 : const std::string_view basename =
547 0 : Utility::basename_of(filename);
548 :
549 0 : if (Utility::contains(basename, ".xda"))
550 : {
551 0 : es.write(filename,WRITE,
552 : EquationSystems::WRITE_DATA |
553 : EquationSystems::WRITE_ADDITIONAL_DATA);
554 0 : return;
555 : }
556 0 : else if (Utility::contains(basename, ".xdr"))
557 : {
558 0 : es.write(filename,ENCODE,
559 : EquationSystems::WRITE_DATA |
560 : EquationSystems::WRITE_ADDITIONAL_DATA);
561 0 : return;
562 : }
563 : }
564 :
565 : // Other formats just use the default "write nodal values" path
566 : MeshOutput<MeshBase>::write_equation_systems
567 0 : (filename, es, system_names);
568 : }
569 :
570 :
571 8078 : bool NameBasedIO::is_parallel_file_format(std::string_view name)
572 : {
573 : using Utility::contains;
574 : using Utility::ends_with;
575 14904 : return (contains(name, ".xda") || contains(name, ".xdr") ||
576 16655 : ends_with(name, ".nem") || ends_with(name, ".n") ||
577 4515 : contains(name, ".cp"));
578 : }
579 :
580 :
581 : } // namespace libMesh
|