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 : #ifndef LIBMESH_EXODUSII_IO_HELPER_H
19 : #define LIBMESH_EXODUSII_IO_HELPER_H
20 :
21 : #include "libmesh/libmesh_config.h"
22 :
23 : #ifdef LIBMESH_HAVE_EXODUS_API
24 :
25 : // Local includes
26 : #include "libmesh/parallel_object.h"
27 : #include "libmesh/point.h"
28 : #include "libmesh/boundary_info.h" // BoundaryInfo::BCTuple
29 : #include "libmesh/enum_elem_type.h" // INVALID_ELEM
30 : #include "libmesh/exodus_header_info.h"
31 :
32 : // C++ includes
33 : #include <iostream>
34 : #include <string>
35 : #include <vector>
36 : #include <map>
37 :
38 : // Macros to simplify checking Exodus error codes
39 : #define EX_CHECK_ERR(code, msg) \
40 : do { \
41 : if ((code) < 0) { \
42 : libmesh_error_msg((msg)); \
43 : } } while (0)
44 :
45 : #define EX_EXCEPTIONLESS_CHECK_ERR(code, msg) \
46 : do { \
47 : if ((code) < 0) { \
48 : libMesh::err << (msg) << std::endl; \
49 : libmesh_exceptionless_error(); \
50 : } } while (0)
51 :
52 : // Before we include a header wrapped in a namespace, we'd better make
53 : // sure none of its dependencies end up in that namespace
54 : #include <errno.h>
55 : #include <stddef.h>
56 : #include <stdlib.h>
57 : #include <stdint.h>
58 :
59 : namespace libMesh
60 : {
61 :
62 : // Forward declarations
63 : class MeshBase;
64 :
65 : /**
66 : * This is the \p ExodusII_IO_Helper class. This class hides the
67 : * implementation details of interfacing with the Exodus binary
68 : * format.
69 : *
70 : * \author John W. Peterson
71 : * \date 2002
72 : */
73 8604 : class ExodusII_IO_Helper : public ParallelObject
74 : {
75 : public:
76 : /**
77 : * Constructor. Automatically initializes all the private members of
78 : * the class. Also allows you to set the verbosity level to v=true
79 : * (on) or v=false (off). The second argument, if true, tells the class to only
80 : * perform its actions if running on processor zero. If you initialize this
81 : * to false, the writing methods will run on all processors instead.
82 : */
83 : ExodusII_IO_Helper(const ParallelObject & parent,
84 : bool v=false,
85 : bool run_only_on_proc0=true,
86 : bool single_precision=false);
87 : /**
88 : * Special functions. This class does not manage any dynamically
89 : * allocated resources (file pointers, etc.) so it _should_ be
90 : * default copy/move constructable, but I don't know
91 : * if any existing code actually uses these operations.
92 : */
93 : ExodusII_IO_Helper (const ExodusII_IO_Helper &) = default;
94 : ExodusII_IO_Helper (ExodusII_IO_Helper &&) = default;
95 : virtual ~ExodusII_IO_Helper();
96 :
97 : /**
98 : * This class contains references so it can't be default
99 : * copy/move-assigned.
100 : */
101 : ExodusII_IO_Helper & operator= (const ExodusII_IO_Helper &) = delete;
102 : ExodusII_IO_Helper & operator= (ExodusII_IO_Helper &&) = delete;
103 :
104 : /**
105 : * \returns The ExodusII API version, in "nodot" format; e.g. 822
106 : * for 8.22
107 : */
108 : static int get_exodus_version();
109 :
110 : /**
111 : * \returns The current element type.
112 : *
113 : * \note The default behavior is for this value to be in all capital
114 : * letters, e.g. \p HEX27.
115 : */
116 : const char * get_elem_type() const;
117 :
118 : /**
119 : * Sets whether or not to write extra "side" elements. This is useful for
120 : * plotting SIDE_DISCONTINUOUS data.
121 : */
122 : void set_add_sides(bool add_sides);
123 :
124 : bool get_add_sides();
125 :
126 : /**
127 : * Opens an \p ExodusII mesh file named \p filename. If
128 : * read_only==true, the file will be opened with the EX_READ flag,
129 : * otherwise it will be opened with the EX_WRITE flag.
130 : */
131 : void open(const char * filename, bool read_only);
132 :
133 : /**
134 : * Reads an \p ExodusII mesh file header, leaving this object's
135 : * internal data structures unchanged.
136 : */
137 : ExodusHeaderInfo read_header() const;
138 :
139 : /**
140 : * Reads an \p ExodusII mesh file header, and stores required
141 : * information on this object.
142 : */
143 : void read_and_store_header_info();
144 :
145 : /**
146 : * Reads the QA records from an ExodusII file. We can use this to
147 : * detect when e.g. CUBIT 14+ was used to generate a Mesh file, and
148 : * work around certain known bugs in that version.
149 : */
150 : void read_qa_records();
151 :
152 : /**
153 : * Prints the \p ExodusII mesh file header, which includes the mesh
154 : * title, the number of nodes, number of elements, mesh dimension,
155 : * number of sidesets, and number of nodesets.
156 : */
157 : void print_header();
158 :
159 : /**
160 : * Reads the nodal data (x,y,z coordinates) from the \p ExodusII
161 : * mesh file.
162 : */
163 : void read_nodes();
164 :
165 : /**
166 : * Reads the optional \p node_num_map from the \p ExodusII mesh
167 : * file.
168 : */
169 : void read_node_num_map();
170 :
171 : /**
172 : * Reads the optional \p bex_cv_blocks from the \p ExodusII mesh
173 : * file.
174 : */
175 : void read_bex_cv_blocks();
176 :
177 : /**
178 : * Prints the nodal information, by default to \p libMesh::out.
179 : */
180 : void print_nodes(std::ostream & out_stream = libMesh::out);
181 :
182 : /**
183 : * Reads information for all of the blocks in the \p ExodusII mesh
184 : * file.
185 : */
186 : void read_block_info();
187 :
188 : /**
189 : * Get the block number for the given block index.
190 : */
191 : int get_block_id(int index);
192 :
193 : /**
194 : * Get the block name for the given block index if supplied in
195 : * the mesh file. Otherwise an empty string is returned.
196 : */
197 : std::string get_block_name(int index);
198 :
199 : /**
200 : * Get the side set id for the given side set index.
201 : */
202 : int get_side_set_id(int index);
203 :
204 : /**
205 : * Get the side set name for the given side set index if supplied in
206 : * the mesh file. Otherwise an empty string is returned.
207 : */
208 : std::string get_side_set_name(int index);
209 :
210 : /**
211 : * Get the node set id for the given node set index.
212 : */
213 : int get_node_set_id(int index);
214 :
215 : /**
216 : * Get the node set name for the given node set index if supplied in
217 : * the mesh file. Otherwise an empty string is returned.
218 : */
219 : std::string get_node_set_name(int index);
220 :
221 : /**
222 : * Reads all of the element connectivity for block \p block in the
223 : * \p ExodusII mesh file.
224 : */
225 : void read_elem_in_block(int block);
226 :
227 : /**
228 : * Read in edge blocks, storing information in the BoundaryInfo object.
229 : */
230 : void read_edge_blocks(MeshBase & mesh);
231 :
232 : /**
233 : * Reads the optional \p node_num_map from the \p ExodusII mesh
234 : * file.
235 : */
236 : void read_elem_num_map();
237 :
238 : /**
239 : * Reads information about all of the sidesets in the \p ExodusII
240 : * mesh file.
241 : */
242 : void read_sideset_info();
243 :
244 : /**
245 : * Reads information about all of the nodesets in the \p ExodusII
246 : * mesh file.
247 : */
248 : void read_nodeset_info();
249 :
250 : /**
251 : * Reads information about all of the elemsets in the \p ExodusII
252 : * mesh file.
253 : */
254 : void read_elemset_info();
255 :
256 : /**
257 : * Reads information about sideset \p id and inserts it into the
258 : * global sideset array at the position \p offset.
259 : */
260 : void read_sideset(int id, int offset);
261 :
262 : /**
263 : * Reads information about elemset \p id and inserts it into the
264 : * global elemset array at the position \p offset.
265 : */
266 : void read_elemset(int id, int offset);
267 :
268 : /**
269 : * New API that reads all nodesets simultaneously. This may be slightly
270 : * faster than reading them one at a time. Calls ex_get_concat_node_sets()
271 : * under the hood.
272 : */
273 : void read_all_nodesets();
274 :
275 : /**
276 : * Closes the \p ExodusII mesh file.
277 : *
278 : * This function is called from the ExodusII_IO destructor, so it should
279 : * not throw an exception.
280 : */
281 : void close() noexcept;
282 :
283 : /**
284 : * Reads and stores the timesteps in the 'time_steps' array.
285 : */
286 : void read_time_steps();
287 :
288 : /**
289 : * Reads the number of timesteps currently stored in the Exodus file
290 : * and stores it in the num_time_steps variable.
291 : */
292 : void read_num_time_steps();
293 :
294 : /**
295 : * Reads the nodal values for the variable 'nodal_var_name' at the
296 : * specified time into the 'nodal_var_values' array.
297 : */
298 : void read_nodal_var_values(std::string nodal_var_name, int time_step);
299 :
300 : /**
301 : * Reads elemental values for the variable 'elemental_var_name' at the
302 : * specified timestep into the 'elem_var_value_map' which is passed in.
303 : */
304 : void read_elemental_var_values(std::string elemental_var_name,
305 : int time_step,
306 : std::map<dof_id_type, Real> & elem_var_value_map);
307 :
308 : /**
309 : * Opens an \p ExodusII mesh file named \p filename for writing.
310 : */
311 : virtual void create(std::string filename);
312 :
313 : /**
314 : * Initializes the Exodus file.
315 : */
316 : virtual void initialize(std::string title, const MeshBase & mesh, bool use_discontinuous=false);
317 :
318 : /**
319 : * Writes the nodal coordinates contained in "mesh"
320 : */
321 : virtual void write_nodal_coordinates(const MeshBase & mesh, bool use_discontinuous=false);
322 :
323 : /**
324 : * Writes the elements contained in "mesh". FIXME: This only works
325 : * for Meshes having a single type of element in each subdomain!
326 : *
327 : * If \p use_discontinuous is true, we break apart elements, so that
328 : * shared nodes on faces/edges/vertices can take different values
329 : * from different elements. This is useful for plotting
330 : * discontinuous underlying variables
331 : *
332 : * If \p _add_sides is true, we also output side elements, so that
333 : * shared nodes on edges/vertices can take different values from
334 : * different elements. This is useful for plotting
335 : * SIDE_DISCONTINUOUS representing e.g. inter-element fluxes.
336 : */
337 : virtual void write_elements(const MeshBase & mesh,
338 : bool use_discontinuous=false);
339 :
340 : /**
341 : * Writes the sidesets contained in "mesh"
342 : */
343 : virtual void write_sidesets(const MeshBase & mesh);
344 :
345 : /**
346 : * Writes the nodesets contained in "mesh"
347 : */
348 : virtual void write_nodesets(const MeshBase & mesh);
349 :
350 : /**
351 : * Sets up the nodal variables
352 : */
353 : virtual void initialize_element_variables(std::vector<std::string> names,
354 : const std::vector<std::set<subdomain_id_type>> & vars_active_subdomains);
355 :
356 : /**
357 : * Sets up the nodal variables
358 : */
359 : void initialize_nodal_variables(std::vector<std::string> names);
360 :
361 : /**
362 : * Sets up the global variables
363 : */
364 : void initialize_global_variables(std::vector<std::string> names);
365 :
366 : /**
367 : * Writes the time for the timestep
368 : */
369 : void write_timestep(int timestep, Real time);
370 :
371 : /**
372 : * Write elemsets stored on the Mesh to the exo file.
373 : */
374 : void write_elemsets(const MeshBase & mesh);
375 :
376 : /**
377 : * Write sideset data for the requested timestep.
378 : */
379 : void
380 : write_sideset_data (const MeshBase & mesh,
381 : int timestep,
382 : const std::vector<std::string> & var_names,
383 : const std::vector<std::set<boundary_id_type>> & side_ids,
384 : const std::vector<std::map<BoundaryInfo::BCTuple, Real>> & bc_vals);
385 :
386 : /**
387 : * Read sideset variables, if any, into the provided data structures.
388 : */
389 : void
390 : read_sideset_data (const MeshBase & mesh,
391 : int timestep,
392 : std::vector<std::string> & var_names,
393 : std::vector<std::set<boundary_id_type>> & side_ids,
394 : std::vector<std::map<BoundaryInfo::BCTuple, Real>> & bc_vals);
395 :
396 : /**
397 : * Similar to read_sideset_data(), but instead of creating one
398 : * std::map per sideset per variable, creates a single map of (elem,
399 : * side, boundary_id) tuples, and stores the exo file array indexing
400 : * for any/all sideset variables on that sideset (they are all the
401 : * same). This function does not actually call exII::ex_get_sset_var()
402 : * to get values, and can be useful if only the original ordering of
403 : * (elem, side) pairs in the exo file is required in cases where a
404 : * separate approach is used to read the sideset data arrays.
405 : */
406 : void
407 : get_sideset_data_indices (const MeshBase & mesh,
408 : std::map<BoundaryInfo::BCTuple, unsigned int> & bc_array_indices);
409 :
410 : /**
411 : * Write nodeset data for the requested timestep.
412 : */
413 : void
414 : write_nodeset_data (int timestep,
415 : const std::vector<std::string> & var_names,
416 : const std::vector<std::set<boundary_id_type>> & node_boundary_ids,
417 : const std::vector<std::map<BoundaryInfo::NodeBCTuple, Real>> & bc_vals);
418 :
419 : /**
420 : * Read nodeset variables, if any, into the provided data structures.
421 : */
422 : void
423 : read_nodeset_data (int timestep,
424 : std::vector<std::string> & var_names,
425 : std::vector<std::set<boundary_id_type>> & node_boundary_ids,
426 : std::vector<std::map<BoundaryInfo::NodeBCTuple, Real>> & bc_vals);
427 :
428 : /**
429 : * Similar to read_nodeset_data(), but instead of creating one
430 : * std::map per nodeset per variable, creates a single map of
431 : * (node_id, boundary_id) tuples, and stores the exo file array
432 : * indexing for any/all nodeset variables on that nodeset (they are
433 : * all the same).
434 : */
435 : void
436 : get_nodeset_data_indices (std::map<BoundaryInfo::NodeBCTuple, unsigned int> & bc_array_indices);
437 :
438 : /**
439 : * Write elemset data for the requested timestep.
440 : */
441 : void
442 : write_elemset_data (int timestep,
443 : const std::vector<std::string> & var_names,
444 : const std::vector<std::set<elemset_id_type>> & elemset_ids_in,
445 : const std::vector<std::map<std::pair<dof_id_type, elemset_id_type>, Real>> & elemset_vals);
446 :
447 : /**
448 : * Read elemset variables, if any, into the provided data structures.
449 : */
450 : void
451 : read_elemset_data (int timestep,
452 : std::vector<std::string> & var_names,
453 : std::vector<std::set<elemset_id_type>> & elemset_ids_in,
454 : std::vector<std::map<std::pair<dof_id_type, elemset_id_type>, Real>> & elemset_vals);
455 :
456 : /**
457 : * Similar to read_elemset_data(), but instead of creating one
458 : * std::map per elemset per variable, creates a single map of
459 : * (elem_id, elemset_id) tuples, and stores the exo file array
460 : * indexing for any/all elemset variables on that elemset (they are
461 : * all the same).
462 : */
463 : void
464 : get_elemset_data_indices (std::map<std::pair<dof_id_type, elemset_id_type>, unsigned int> & elemset_array_indices);
465 :
466 : /**
467 : * Writes the vector of values to the element variables.
468 : *
469 : * The 'values' vector is assumed to be in the order:
470 : * {(u1, u2, u3, ..., uN), (v1, v2, v3, ..., vN), (w1, w2, w3, ..., wN)}
471 : * where N is the number of elements.
472 : *
473 : * This ordering is produced by calls to ES::build_elemental_solution_vector().
474 : * ES::build_discontinuous_solution_vector(), on the other hand, produces an
475 : * element-major ordering. See the function below for that case.
476 : */
477 : void write_element_values
478 : (const MeshBase & mesh,
479 : const std::vector<Real> & values,
480 : int timestep,
481 : const std::vector<std::set<subdomain_id_type>> & vars_active_subdomains);
482 :
483 : /**
484 : * Same as the function above, but assume the input 'values' vector is
485 : * in element-major order, i.e.
486 : * {(u1,v1,w1), (u2,v2,w2), ... (uN,vN,wN)}
487 : * This function is called by
488 : * ExodusII_IO::write_element_data_from_discontinuous_nodal_data()
489 : * because ES::build_discontinuous_solution_vector() builds the solution
490 : * vector in this order.
491 : *
492 : * \note If some variables are subdomain-restricted, then the tuples will
493 : * be of different lengths for each element, i.e.
494 : * {(u1,v1,w1), (u2,v2), ... (uN,vN,wN)}
495 : * if variable w is not active on element 2.
496 : */
497 : void write_element_values_element_major
498 : (const MeshBase & mesh,
499 : const std::vector<Real> & values,
500 : int timestep,
501 : const std::vector<std::set<subdomain_id_type>> & vars_active_subdomains,
502 : const std::vector<std::string> & derived_var_names,
503 : const std::map<subdomain_id_type, std::vector<std::string>> & subdomain_to_var_names);
504 :
505 : /**
506 : * Writes the vector of values to a nodal variable.
507 : */
508 : void write_nodal_values(int var_id, const std::vector<Real> & values, int timestep);
509 :
510 : /**
511 : * Writes the vector of information records.
512 : */
513 : void write_information_records(const std::vector<std::string> & records);
514 :
515 : /**
516 : * Writes the vector of global variables.
517 : */
518 : void write_global_values(const std::vector<Real> & values, int timestep);
519 :
520 : /**
521 : * Uses ex_update() to flush buffers to file.
522 : */
523 : void update();
524 :
525 : /**
526 : * Reads the vector of global variables.
527 : */
528 : void read_global_values(std::vector<Real> & values, int timestep);
529 :
530 : /**
531 : * Sets the underlying value of the boolean flag
532 : * _use_mesh_dimension_instead_of_spatial_dimension. By default,
533 : * the value of this flag is false.
534 : *
535 : * See the ExodusII_IO class documentation for a detailed
536 : * description of this flag.
537 : */
538 : void use_mesh_dimension_instead_of_spatial_dimension(bool val);
539 :
540 : /**
541 : * Set to true (the default) to write files in an HDF5-based file
542 : * format (when HDF5 is available), or to false to write files in
543 : * the old NetCDF3-based format. If HDF5 is unavailable, this
544 : * setting does nothing.
545 : */
546 : void set_hdf5_writing(bool write_hdf5);
547 :
548 : /**
549 : * Sets the value of _write_as_dimension.
550 : *
551 : * This directly controls the num_dim which is written to the Exodus
552 : * file. If non-zero, this value supersedes all other dimensions,
553 : * including:
554 : * 1.) MeshBase::spatial_dimension()
555 : * 2.) MeshBase::mesh_dimension()
556 : * 3.) Any value passed to use_mesh_dimension_instead_of_spatial_dimension()
557 : * This is useful/necessary for working around a bug in Paraview which
558 : * prevents the "Plot Over Line" filter from working on 1D meshes.
559 : */
560 : void write_as_dimension(unsigned dim);
561 :
562 : /**
563 : * Allows you to set a vector that is added to the coordinates of all
564 : * of the nodes. Effectively, this "moves" the mesh to a particular position
565 : */
566 : void set_coordinate_offset(Point p);
567 :
568 : /**
569 : * \returns A vector with three copies of each element in the provided name vector,
570 : * starting with r_, i_ and a_ respectively. If the "write_complex_abs" parameter
571 : * is true (default), the complex modulus is written, otherwise only the real and
572 : * imaginary parts are written.
573 : */
574 : std::vector<std::string>
575 : get_complex_names(const std::vector<std::string> & names,
576 : bool write_complex_abs) const;
577 :
578 : /**
579 : * returns a "tripled" copy of \p vars_active_subdomains, which is necessary in the
580 : * complex-valued case.
581 : */
582 : std::vector<std::set<subdomain_id_type>>
583 : get_complex_vars_active_subdomains
584 : (const std::vector<std::set<subdomain_id_type>> & vars_active_subdomains,
585 : bool write_complex_abs) const;
586 :
587 : /**
588 : * Takes a map from subdomain id -> vector of active variable names
589 : * as input and returns a corresponding map where the original
590 : * variable names have been replaced by their complex counterparts.
591 : * Used by the ExodusII_IO::write_element_data_from_discontinuous_nodal_data()
592 : * function.
593 : */
594 : std::map<subdomain_id_type, std::vector<std::string>>
595 : get_complex_subdomain_to_var_names
596 : (const std::map<subdomain_id_type, std::vector<std::string>> & subdomain_to_var_names,
597 : bool write_complex_abs) const;
598 :
599 : /**
600 : * This is the \p ExodusII_IO_Helper Conversion class. It provides
601 : * a data structure which contains \p ExodusII node/edge maps and
602 : * name conversions. It's defined below.
603 : */
604 : class Conversion;
605 :
606 : /**
607 : * This is the \p ExodusII_IO_Helper NamesData class.
608 : * It manages the C data structure necessary for writing out named
609 : * entities to ExodusII files.
610 : */
611 : class NamesData;
612 :
613 : /**
614 : * Prints the message defined in \p msg. Can be turned off if
615 : * verbosity is set to 0.
616 : */
617 : void message(std::string_view msg);
618 :
619 : /**
620 : * Prints the message defined in \p msg, and appends the number \p i
621 : * to the end of the message. Useful for printing messages in
622 : * loops. Can be turned off if verbosity is set to 0.
623 : */
624 : void message(std::string_view msg, int i);
625 :
626 : // File identification flag
627 : int ex_id;
628 :
629 : // General error flag
630 : int ex_err;
631 :
632 : // struct which contains data fields from the Exodus file header
633 : ExodusHeaderInfo header_info;
634 :
635 : // Problem title (Use vector<char> to emulate a char *)
636 : std::vector<char> & title;
637 :
638 : // Number of dimensions in the mesh
639 : int & num_dim;
640 :
641 : // Total number of nodes in the mesh
642 : int & num_nodes;
643 :
644 : // Total number of elements in the mesh
645 : int & num_elem;
646 :
647 : // Smallest element id which exceeds every element id in the mesh.
648 : // (this may exceed num_elem due to mapping)
649 : int end_elem_id() const;
650 :
651 : // Total number of element blocks
652 : int & num_elem_blk;
653 :
654 : // Total number of edges
655 : int & num_edge;
656 :
657 : // Total number of edge blocks. The sum of the number of edges in
658 : // each block must equal num_edge.
659 : int & num_edge_blk;
660 :
661 : // Total number of node sets
662 : int & num_node_sets;
663 :
664 : // Total number of side sets
665 : int & num_side_sets;
666 :
667 : // Total number of element sets
668 : int & num_elem_sets;
669 :
670 : // Number of global variables
671 : int num_global_vars;
672 :
673 : // Number of sideset variables
674 : int num_sideset_vars;
675 :
676 : // Number of nodeset variables
677 : int num_nodeset_vars;
678 :
679 : // Number of elemset variables
680 : int num_elemset_vars;
681 :
682 : // Number of elements in this block
683 : int num_elem_this_blk;
684 :
685 : // Number of nodes in each element
686 : int num_nodes_per_elem;
687 :
688 : // Number of attributes for a given block
689 : int num_attr;
690 :
691 : // Total number of elements in all side sets
692 : int num_elem_all_sidesets;
693 :
694 : // Total number of elements in all elem sets
695 : int num_elem_all_elemsets;
696 :
697 : // Vector of element block identification numbers
698 : std::vector<int> block_ids;
699 :
700 : // Vector of edge block identification numbers
701 : std::vector<int> edge_block_ids;
702 :
703 : // Vector of nodes in an element
704 : std::vector<int> connect;
705 :
706 : // Vector of the sideset IDs
707 : std::vector<int> ss_ids;
708 :
709 : // Vector of the nodeset IDs
710 : std::vector<int> nodeset_ids;
711 :
712 : // Vector of the elemset IDs
713 : std::vector<int> elemset_ids;
714 :
715 : // Number of sides in each sideset
716 : std::vector<int> num_sides_per_set;
717 :
718 : // Number of nodes in each nodeset
719 : std::vector<int> num_nodes_per_set;
720 :
721 : // Number of elems in each elemset
722 : std::vector<int> num_elems_per_set;
723 :
724 : // Number of distribution factors per sideset
725 : std::vector<int> num_df_per_set;
726 :
727 : // Number of distribution factors per nodeset
728 : std::vector<int> num_node_df_per_set;
729 :
730 : // Number of distribution factors per elemset
731 : std::vector<int> num_elem_df_per_set;
732 :
733 : // Starting indices for each nodeset in the node_sets_node_list vector.
734 : // Used in the calls to ex_{put,get}_concat_node_sets().
735 : std::vector<int> node_sets_node_index;
736 :
737 : // Starting indices for each nodeset in the node_sets_dist_fact vector.
738 : // Used in the calls to ex_{put,get}_concat_node_sets().
739 : std::vector<int> node_sets_dist_index;
740 :
741 : // Node ids for all nodes in nodesets, concatenated together.
742 : // Used in the calls to ex_{put,get}_concat_node_sets().
743 : std::vector<int> node_sets_node_list;
744 :
745 : // Distribution factors for all nodes in all nodesets, concatenated together.
746 : // Used in the calls to ex_{put,get}_concat_node_sets().
747 : std::vector<Real> node_sets_dist_fact;
748 :
749 : // List of element numbers in all sidesets
750 : std::vector<int> elem_list;
751 :
752 : // Side (face/edge) number actually on the boundary
753 : std::vector<int> side_list;
754 :
755 : // Side (face/edge) id number
756 : std::vector<int> id_list;
757 :
758 : // List of element numbers in all elemsets
759 : std::vector<int> elemset_list;
760 :
761 : // List of elemset ids for all elements in elemsets
762 : std::vector<int> elemset_id_list;
763 :
764 : // Optional mapping from internal [0,num_nodes) to arbitrary indices
765 : std::vector<int> node_num_map;
766 :
767 : // Optional mapping from internal [0,num_elem) to arbitrary indices
768 : std::vector<int> elem_num_map;
769 :
770 : // x locations of node points
771 : std::vector<Real> x;
772 :
773 : // y locations of node points
774 : std::vector<Real> y;
775 :
776 : // z locations of node points
777 : std::vector<Real> z;
778 :
779 : // Spline weights associated with node points, in IGA meshes
780 : std::vector<Real> w;
781 :
782 : // Number of Bezier Extraction coefficient vectors in a block
783 : unsigned int bex_num_elem_cvs;
784 :
785 : // Bezier Extraction connectivity indices, in IGA meshes
786 : std::vector<std::vector<long unsigned int>> bex_cv_conn;
787 :
788 : // Bezier Extraction coefficient vectors, in IGA meshes
789 : // bex_dense_constraint_vecs[block_num][vec_num][column_num] = coef
790 : std::vector<std::vector<std::vector<Real>>> bex_dense_constraint_vecs;
791 :
792 : // Type of element in a given block
793 : std::vector<char> elem_type;
794 :
795 : // Maps libMesh element numbers to Exodus element numbers
796 : // gets filled in when write_elements gets called
797 : std::map<dof_id_type, dof_id_type> libmesh_elem_num_to_exodus;
798 : std::vector<int> exodus_elem_num_to_libmesh;
799 :
800 : // Map of all node numbers connected to local node numbers to their exodus numbering.
801 : // The exodus numbers are stored in here starting with 1
802 : std::map<dof_id_type, dof_id_type> libmesh_node_num_to_exodus;
803 : std::vector<int> exodus_node_num_to_libmesh;
804 :
805 : // The number of timesteps in the file, as returned by ex_inquire
806 : int num_time_steps;
807 :
808 : // The timesteps stored in the solution file, filled by read_time_steps()
809 : std::vector<Real> time_steps;
810 :
811 : // The number of nodal variables in the Exodus file
812 : int num_nodal_vars;
813 :
814 : // The names of the nodal variables stored in the Exodus file
815 : std::vector<std::string> nodal_var_names;
816 :
817 : // Holds the nodal variable values for a given variable, one value
818 : // per node, indexed by libMesh node id.
819 : // This is a map so it can handle Nemesis files as well.
820 : std::map<dof_id_type, Real> nodal_var_values;
821 :
822 : // The number of elemental variables in the Exodus file
823 : int num_elem_vars;
824 :
825 : // The names of the elemental variables stored in the Exodus file
826 : std::vector<std::string> elem_var_names;
827 :
828 : // Holds the elemental variable values for a given variable, one value per element
829 : std::vector<Real> elem_var_values;
830 :
831 : // The names of the global variables stored in the Exodus file
832 : std::vector<std::string> global_var_names;
833 :
834 : // The names of the sideset variables stored in the Exodus file
835 : std::vector<std::string> sideset_var_names;
836 :
837 : // The names of the nodeset variables stored in the Exodus file
838 : std::vector<std::string> nodeset_var_names;
839 :
840 : // The names of the elemset variables stored in the Exodus file
841 : std::vector<std::string> elemset_var_names;
842 :
843 : // Maps of Ids to named entities
844 : std::map<int, std::string> id_to_block_names;
845 : std::map<int, std::string> id_to_edge_block_names;
846 : std::map<int, std::string> id_to_ss_names;
847 : std::map<int, std::string> id_to_ns_names;
848 : std::map<int, std::string> id_to_elemset_names;
849 :
850 : // On/Off message flag
851 : bool verbose;
852 :
853 : // This flag gets set after the Exodus file has been successfully opened for writing.
854 : // Both the create() and open() (if called with EX_WRITE) functions may set this flag.
855 : bool opened_for_writing;
856 :
857 : // This flag gets set after the open() function has been successfully called.
858 : // We call open() to open an ExodusII file for reading.
859 : bool opened_for_reading;
860 :
861 : // When either create() or open() is called, the Helper stores the
862 : // name of the opened file as current_filename. This way, the
863 : // ExodusII_IO object can check to see if, on subsequent writes, the
864 : // user is asking to write to a *different* filename from the one
865 : // that is currently open, and signal an error. The current
866 : // ExodusII_IO implementation is designed to work with a single file
867 : // only, so if you want to write to multiple Exodus files, use a
868 : // different ExodusII_IO object for each one.
869 : std::string current_filename;
870 :
871 : /**
872 : * Wraps calls to exII::ex_get_var_names() and exII::ex_get_var_param().
873 : * The enumeration controls whether nodal, elemental, global, etc.
874 : * variable names are read and which class members are filled in.
875 : * NODAL: num_nodal_vars nodal_var_names
876 : * ELEMENTAL: num_elem_vars elem_var_names
877 : * GLOBAL: num_global_vars global_var_names
878 : * SIDESET: num_sideset_vars sideset_var_names
879 : * NODESET: num_nodeset_vars nodeset_var_names
880 : */
881 : enum ExodusVarType {NODAL=0, ELEMENTAL=1, GLOBAL=2, SIDESET=3, NODESET=4, ELEMSET=5};
882 : void read_var_names(ExodusVarType type);
883 :
884 : const ExodusII_IO_Helper::Conversion &
885 : get_conversion(const ElemType type) const;
886 :
887 : const ExodusII_IO_Helper::Conversion &
888 : get_conversion(std::string type_str) const;
889 :
890 : /*
891 : * Returns the sum of node "offsets" that are to be expected from a
892 : * parallel nodal solution vector that has had "fake" nodes added on
893 : * each processor. This plus a node id gives a valid nodal solution
894 : * vector index.
895 : */
896 25337129 : dof_id_type node_id_to_vec_id(dof_id_type n) const
897 : {
898 25337129 : if (_added_side_node_offsets.empty())
899 23081560 : return n;
900 :
901 : // Find the processor id that has node_id in the parallel vec
902 12050 : const auto lb = std::upper_bound(_true_node_offsets.begin(),
903 2410 : _true_node_offsets.end(), n);
904 1205 : libmesh_assert(lb != _true_node_offsets.end());
905 14460 : const processor_id_type p = lb - _true_node_offsets.begin();
906 :
907 14460 : return n + (p ? _added_side_node_offsets[p-1] : 0);
908 : }
909 :
910 : /*
911 : * Returns the sum of both added node "offsets" on processors 0
912 : * through p-1 and real nodes added on processors 0 to p.
913 : * This is the starting index for added nodes' data.
914 : */
915 1725 : dof_id_type added_node_offset_on(processor_id_type p) const
916 : {
917 50 : libmesh_assert (p < _true_node_offsets.size());
918 : const dof_id_type added_node_offsets =
919 1800 : (_added_side_node_offsets.empty() || !p) ? 0 :
920 1475 : _added_side_node_offsets[p-1];
921 1825 : return _true_node_offsets[p] + added_node_offsets;
922 : }
923 :
924 :
925 : protected:
926 : /**
927 : * When appending: during initialization, check that variable names
928 : * in the file match those you attempt to initialize with.
929 : */
930 : void check_existing_vars(ExodusVarType type, std::vector<std::string> & names, std::vector<std::string> & names_from_file);
931 :
932 : /**
933 : * Wraps calls to exII::ex_put_var_names() and exII::ex_put_var_param().
934 : * The enumeration controls whether nodal, elemental, or global
935 : * variable names are read and which class members are filled in.
936 : */
937 : void write_var_names(ExodusVarType type, const std::vector<std::string> & names);
938 :
939 : // If true, whenever there is an I/O operation, only perform if if we are on processor 0.
940 : bool _run_only_on_proc0;
941 :
942 : // This flag gets set after the create() function has been successfully called.
943 : bool _opened_by_create;
944 :
945 : // True once the elem vars are initialized
946 : bool _elem_vars_initialized;
947 :
948 : // True once the global vars are initialized
949 : bool _global_vars_initialized;
950 :
951 : // True once the nodal vars are initialized
952 : bool _nodal_vars_initialized;
953 :
954 : // If true, use the Mesh's dimension (as determined by the dimension
955 : // of the elements comprising the mesh) instead of the mesh's
956 : // spatial dimension, when writing. By default this is false.
957 : bool _use_mesh_dimension_instead_of_spatial_dimension;
958 :
959 : // If true, write an HDF5 file when available. If false, write the
960 : // old format.
961 : bool _write_hdf5;
962 :
963 : // Set once the elem num map has been read
964 : int _end_elem_id;
965 :
966 : // Use this for num_dim when writing the Exodus file. If non-zero, supersedes
967 : // any value set in _use_mesh_dimension_instead_of_spatial_dimension.
968 : unsigned _write_as_dimension;
969 :
970 : // On output, shift every point by _coordinate_offset
971 : Point _coordinate_offset;
972 :
973 : // If true, forces single precision I/O
974 : bool _single_precision;
975 :
976 : /**
977 : * If we're adding "fake" sides to visualize SIDE_DISCONTINUOUS
978 : * variables, _added_side_node_offsets[p] gives us the total
979 : * solution vector offset to use on processor p+1 from the nodes on
980 : * those previous ranks' sides.
981 : */
982 : std::vector<dof_id_type> _added_side_node_offsets;
983 :
984 : /**
985 : * If we're adding "fake" sides to visualize SIDE_DISCONTINUOUS
986 : * variables, we also need to know how many real nodes from previous
987 : * ranks are taking up space in a solution vector.
988 : */
989 : std::vector<dof_id_type> _true_node_offsets;
990 :
991 : /**
992 : * This class facilitates inline conversion of an input data vector
993 : * to a different precision level, depending on the underlying type
994 : * of Real and whether or not the single_precision flag is set. This
995 : * should be used whenever floating point data is being written to
996 : * the Exodus file. Note that if no precision conversion has to take
997 : * place, there should be very little overhead involved in using
998 : * this object.
999 : */
1000 : struct MappedOutputVector
1001 : {
1002 : // If necessary, allocates space to store a version of vec_in in a
1003 : // different precision than it was input with.
1004 : MappedOutputVector(const std::vector<Real> & vec_in,
1005 : bool single_precision_in);
1006 :
1007 45312 : ~MappedOutputVector() = default;
1008 :
1009 : // Returns void * pointer to either the mapped data or the
1010 : // original data, as necessary.
1011 : void * data();
1012 :
1013 : private:
1014 : const std::vector<Real> & our_data;
1015 : bool single_precision;
1016 : std::vector<double> double_vec;
1017 : std::vector<float> float_vec;
1018 : };
1019 :
1020 : /**
1021 : * This class facilitates reading in vectors from Exodus file that
1022 : * may be of a different floating point type than Real. It employs
1023 : * basically the same approach as the MappedOutputVector, just going
1024 : * in the opposite direction. For more information, see the
1025 : * MappedOutputVector class docs.
1026 : */
1027 : struct MappedInputVector
1028 : {
1029 : MappedInputVector(std::vector<Real> & vec_in,
1030 : bool single_precision_in);
1031 : ~MappedInputVector();
1032 :
1033 : // Returns void * pointer to either the mapped data or the
1034 : // original data, as necessary.
1035 : void * data();
1036 :
1037 : private:
1038 : std::vector<Real> & our_data;
1039 : bool single_precision;
1040 : std::vector<double> double_vec;
1041 : std::vector<float> float_vec;
1042 : };
1043 :
1044 :
1045 : protected:
1046 :
1047 : /**
1048 : * read_var_names() dispatches to this function. We need to
1049 : * override it slightly for Nemesis.
1050 : */
1051 : virtual void read_var_names_impl(const char * var_type,
1052 : int & count,
1053 : std::vector<std::string> & result);
1054 :
1055 : private:
1056 :
1057 : /**
1058 : * Set to true iff we want to write separate "side" elements too.
1059 : */
1060 : bool _add_sides = false;
1061 :
1062 : /**
1063 : * write_var_names() dispatches to this function.
1064 : */
1065 : void write_var_names_impl(const char * var_type,
1066 : int & count,
1067 : const std::vector<std::string> & names);
1068 :
1069 : /**
1070 : * Defines equivalence classes of Exodus element types that map to
1071 : * libmesh ElemTypes.
1072 : */
1073 : std::map<std::string, ElemType> element_equivalence_map;
1074 : void init_element_equivalence_map();
1075 :
1076 : /**
1077 : * Associates libMesh ElemTypes with node/face/edge/etc. mappings
1078 : * of the corresponding Exodus element types.
1079 : *
1080 : * We have to map based on both ElemType and mesh dimension, because
1081 : * Exodus treats "TRI" side numbering in two different ways
1082 : * depending on whether a triangle is embedded in a 2D or a 3D mesh.
1083 : */
1084 : std::map<int, std::map<ElemType, ExodusII_IO_Helper::Conversion>> conversion_map;
1085 : void init_conversion_map();
1086 : };
1087 :
1088 :
1089 :
1090 1775950 : class ExodusII_IO_Helper::Conversion
1091 : {
1092 : public:
1093 :
1094 : /**
1095 : * Constructor. Zero initializes all variables.
1096 : */
1097 1832850 : Conversion()
1098 1832850 : : node_map(nullptr),
1099 1775950 : inverse_node_map(nullptr),
1100 1775950 : side_map(nullptr),
1101 1775950 : inverse_side_map(nullptr),
1102 1775950 : shellface_map(nullptr),
1103 1775950 : inverse_shellface_map(nullptr),
1104 1775950 : shellface_index_offset(0),
1105 1775950 : libmesh_type(INVALID_ELEM),
1106 1775950 : dim(0),
1107 1775950 : n_nodes(0),
1108 1832850 : exodus_type("")
1109 1832850 : {}
1110 :
1111 : /**
1112 : * \returns The ith component of the node map for this element.
1113 : *
1114 : * The node map maps the exodusII node numbering format to this
1115 : * library's format.
1116 : */
1117 : int get_node_map(int i) const;
1118 :
1119 : /**
1120 : * \returns The ith component of the inverse node map for this
1121 : * element.
1122 : *
1123 : * The inverse node map maps the libmesh node numbering to Exodus'
1124 : * node numbering.
1125 : *
1126 : * \note All elements except Hex27 currently have the same node
1127 : * numbering as libmesh elements.
1128 : */
1129 : int get_inverse_node_map(int i) const;
1130 :
1131 : /**
1132 : * \returns The ith component of the side map for this element.
1133 : *
1134 : * The side map maps the exodusII side numbering format to this
1135 : * library's format.
1136 : */
1137 : int get_side_map(int i) const;
1138 :
1139 : /**
1140 : * \returns The ith component of the side map for this element.
1141 : *
1142 : * The side map maps the libMesh side numbering format to this
1143 : * exodus's format.
1144 : */
1145 : int get_inverse_side_map(int i) const;
1146 :
1147 : /**
1148 : * \returns The ith component of the shellface map for this element.
1149 : * \note Nothing is currently using this.
1150 : */
1151 : int get_shellface_map(int i) const;
1152 :
1153 : /**
1154 : * \returns The ith component of the inverse shellface map for this element.
1155 : */
1156 : int get_inverse_shellface_map(int i) const;
1157 :
1158 : /**
1159 : * \returns The canonical element type for this element.
1160 : *
1161 : * The canonical element type is the standard element type
1162 : * understood by this library.
1163 : */
1164 : ElemType libmesh_elem_type() const;
1165 :
1166 : /**
1167 : * \returns The string corresponding to the Exodus type for this element.
1168 : */
1169 : std::string exodus_elem_type() const;
1170 :
1171 : /**
1172 : * \returns The shellface index offset.
1173 : */
1174 : std::size_t get_shellface_index_offset() const;
1175 :
1176 : /**
1177 : * An invalid_id that can be returned to signal failure in case
1178 : * something goes wrong.
1179 : */
1180 : static const int invalid_id;
1181 :
1182 : /**
1183 : * Pointer to the node map for this element.
1184 : */
1185 : const std::vector<int> * node_map;
1186 :
1187 : /**
1188 : * Pointer to the inverse node map for this element.
1189 : * For all elements except for the Hex27, this is the same
1190 : * as the node map.
1191 : */
1192 : const std::vector<int> * inverse_node_map;
1193 :
1194 : /**
1195 : * Pointer to the side map for this element.
1196 : */
1197 : const std::vector<int> * side_map;
1198 :
1199 : /**
1200 : * Pointer to the inverse side map for this element.
1201 : */
1202 : const std::vector<int> * inverse_side_map;
1203 :
1204 : /**
1205 : * Pointer to the shellface map for this element. Only the inverse
1206 : * is actually used currently, this one is provided for completeness
1207 : * and libmesh_ingore()d to avoid warnings.
1208 : */
1209 : const std::vector<int> * shellface_map;
1210 :
1211 : /**
1212 : * Pointer to the inverse shellface map for this element.
1213 : */
1214 : const std::vector<int> * inverse_shellface_map;
1215 :
1216 : /**
1217 : * The shellface index offset defines the offset due to a difference between libMesh
1218 : * and Exodus in indexing sidesets. This is relevant for shell elements, for
1219 : * example, since Exodus includes extra "shell face" sides in that case.
1220 : */
1221 : size_t shellface_index_offset;
1222 :
1223 : /**
1224 : * The canonical (i.e. standard for this library)
1225 : * element type.
1226 : */
1227 : ElemType libmesh_type;
1228 :
1229 : /**
1230 : * The element dimension; useful since we don't seem to have a cheap
1231 : * way to look this up from ElemType
1232 : */
1233 : int dim;
1234 :
1235 : /**
1236 : * The number of nodes per element; useful likewise
1237 : */
1238 : int n_nodes;
1239 :
1240 : /**
1241 : * The string corresponding to the Exodus type for this element
1242 : */
1243 : std::string exodus_type;
1244 : };
1245 :
1246 :
1247 :
1248 : /**
1249 : * This class is useful for managing anything that requires a char **
1250 : * input/output in ExodusII file. You must know the number of strings
1251 : * and the length of each one at the time you create it.
1252 : */
1253 41914 : class ExodusII_IO_Helper::NamesData
1254 : {
1255 : public:
1256 : /**
1257 : * Constructor. Allocates enough storage to hold n_strings of
1258 : * length string_length. (Actually allocates string_length+1 characters
1259 : * per string to account for the trailing '\0' character.)
1260 : */
1261 : explicit
1262 : NamesData(size_t n_strings, size_t string_length);
1263 :
1264 : /**
1265 : * Adds another name to the current data table.
1266 : */
1267 : void push_back_entry(const std::string & name);
1268 :
1269 : /**
1270 : * Provide access to the underlying C data table
1271 : */
1272 : char ** get_char_star_star();
1273 :
1274 : /**
1275 : * Provide access to the i'th underlying char *
1276 : */
1277 : char * get_char_star(int i);
1278 :
1279 : private:
1280 : // C++ data structures for managing string memory
1281 : std::vector<std::vector<char>> data_table;
1282 : std::vector<char *> data_table_pointers;
1283 :
1284 : size_t counter;
1285 : size_t table_size;
1286 : };
1287 :
1288 :
1289 68 : inline void ExodusII_IO_Helper::set_add_sides(bool add_sides)
1290 : {
1291 2414 : _add_sides = add_sides;
1292 68 : }
1293 :
1294 :
1295 2751 : inline bool ExodusII_IO_Helper::get_add_sides()
1296 : {
1297 14420 : return _add_sides;
1298 : }
1299 :
1300 :
1301 0 : inline int ExodusII_IO_Helper::end_elem_id() const
1302 : {
1303 0 : libmesh_assert_equal_to(std::size_t(num_elem), elem_num_map.size());
1304 357 : return _end_elem_id;
1305 : }
1306 :
1307 :
1308 : } // namespace libMesh
1309 :
1310 : #endif // LIBMESH_HAVE_EXODUS_API
1311 :
1312 : #endif // LIBMESH_EXODUSII_IO_HELPER_H
|