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