Skip to content

Commit 001b6e1

Browse files
authored
Merge pull request #4229 from roystgnr/exodus_names
Expand Exodus name limits
2 parents 5b0ec56 + cc17c74 commit 001b6e1

36 files changed

Lines changed: 147 additions & 32 deletions

include/mesh/exodusII_io.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,16 @@ class ExodusII_IO : public MeshInput<MeshBase>,
578578
*/
579579
void append(bool val);
580580

581+
/**
582+
* For backwards compatibility, libMesh currently truncates names in
583+
* ExodusII output to the old default of 32 characters, but user
584+
* code can expand that to up to 80 characters by setting a larger
585+
* \p max_length manually.
586+
*
587+
* This must be set before a file is opened for writing.
588+
*/
589+
void set_max_name_length(unsigned int max_length);
590+
581591
/**
582592
* Return list of the elemental variable names
583593
*/

include/mesh/exodusII_io_helper.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,12 @@ class ExodusII_IO_Helper : public ParallelObject
604604
*/
605605
void set_coordinate_offset(Point p);
606606

607+
/**
608+
* Set how many characters to use in names when opening a file for
609+
* writing.
610+
*/
611+
void set_max_name_length(unsigned int max_length);
612+
607613
/**
608614
* \returns A vector with three copies of each element in the provided name vector,
609615
* starting with r_, i_ and a_ respectively. If the "write_complex_abs" parameter
@@ -1003,6 +1009,9 @@ class ExodusII_IO_Helper : public ParallelObject
10031009
// old format.
10041010
bool _write_hdf5;
10051011

1012+
// The maximum name length to use when writing
1013+
unsigned int _max_name_length;
1014+
10061015
// Set once the elem num map has been read
10071016
int _end_elem_id;
10081017

include/mesh/mesh_base.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1797,6 +1797,15 @@ class MeshBase : public ParallelObject
17971797
*/
17981798
void cache_elem_data();
17991799

1800+
/**
1801+
* libMesh often expects all processors to know about names of all
1802+
* subdomain ids, but distributed mesh generators may only know
1803+
* about part of a mesh when creating names. This method can
1804+
* synchronize the subdomain id to name map across processors,
1805+
* assuming no conflicts exist.
1806+
*/
1807+
void sync_subdomain_name_map();
1808+
18001809
/**
18011810
* Search the mesh for elements that have a neighboring element
18021811
* of dim+1 and set that element as the interior parent

src/mesh/exodusII_io.C

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2410,6 +2410,12 @@ void ExodusII_IO::set_hdf5_writing(bool write_hdf5)
24102410
}
24112411

24122412

2413+
void ExodusII_IO::set_max_name_length(unsigned int max_length)
2414+
{
2415+
exio_helper->set_max_name_length(max_length);
2416+
}
2417+
2418+
24132419
void ExodusII_IO::set_discontinuous_bex(bool disc_bex)
24142420
{
24152421
_disc_bex = disc_bex;

src/mesh/exodusII_io_helper.C

Lines changed: 76 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ extern "C" {
5959
namespace
6060
{
6161

62+
// ExodusII defaults to 32 bytes names, but we've had user complaints
63+
// about truncation with those.
64+
// It looks like the maximum they'll support is 80 byte names.
65+
static constexpr int libmesh_max_str_length = MAX_LINE_LENGTH;
66+
6267
using namespace libMesh;
6368

6469
// File scope constant node/edge/face mapping arrays.
@@ -296,12 +301,13 @@ ExodusII_IO_Helper::ExodusII_IO_Helper(const ParallelObject & parent,
296301
_nodal_vars_initialized(false),
297302
_use_mesh_dimension_instead_of_spatial_dimension(false),
298303
_write_hdf5(true),
304+
_max_name_length(32),
299305
_end_elem_id(0),
300306
_write_as_dimension(0),
301307
_single_precision(single_precision)
302308
{
303309
title.resize(MAX_LINE_LENGTH+1);
304-
elem_type.resize(MAX_STR_LENGTH);
310+
elem_type.resize(libmesh_max_str_length);
305311
init_element_equivalence_map();
306312
init_conversion_map();
307313
}
@@ -693,11 +699,34 @@ void ExodusII_IO_Helper::open(const char * filename, bool read_only)
693699
EX_CHECK_ERR(ex_id, err_msg);
694700
if (verbose) libMesh::out << "File opened successfully." << std::endl;
695701

702+
// If we're writing then we'll want to use the specified length;
703+
// if we're reading then we'll override this by what's in the file.
704+
int max_name_length_to_set = _max_name_length;
705+
696706
if (read_only)
707+
{
697708
opened_for_reading = true;
709+
710+
// ExodusII reads truncate to 32-char strings by default; we'd
711+
// like to support whatever's in the file, so as early as possible
712+
// let's find out what that is.
713+
int max_name_length = exII::ex_inquire_int(ex_id, exII::EX_INQ_DB_MAX_USED_NAME_LENGTH);
714+
715+
libmesh_error_msg_if(max_name_length > MAX_LINE_LENGTH,
716+
"Unexpected maximum name length of " <<
717+
max_name_length << " in file " << filename <<
718+
" exceeds expected " << MAX_LINE_LENGTH);
719+
720+
// I don't think the 32 here should be necessary, but let's make
721+
// sure we don't accidentally make things *worse* for anyone.
722+
max_name_length_to_set = std::max(max_name_length, 32);
723+
}
698724
else
699725
opened_for_writing = true;
700726

727+
ex_err = exII::ex_set_max_name_length(ex_id, max_name_length_to_set);
728+
EX_CHECK_ERR(ex_err, "Error setting max ExodusII name length.");
729+
701730
current_filename = std::string(filename);
702731
}
703732

@@ -791,7 +820,7 @@ void ExodusII_IO_Helper::read_qa_records()
791820
{
792821
qa_storage[i].resize(4);
793822
for (auto j : make_range(4))
794-
qa_storage[i][j].resize(MAX_STR_LENGTH+1);
823+
qa_storage[i][j].resize(libmesh_max_str_length+1);
795824
}
796825

797826
// inner_array_t is a fixed-size array of 4 strings
@@ -873,7 +902,7 @@ void ExodusII_IO_Helper::read_nodes()
873902
if (n_nodal_attr > 0)
874903
{
875904
std::vector<std::vector<char>> attr_name_data
876-
(n_nodal_attr, std::vector<char>(MAX_STR_LENGTH + 1));
905+
(n_nodal_attr, std::vector<char>(libmesh_max_str_length + 1));
877906
std::vector<char *> attr_names(n_nodal_attr);
878907
for (auto i : index_range(attr_names))
879908
attr_names[i] = attr_name_data[i].data();
@@ -934,7 +963,7 @@ void ExodusII_IO_Helper::read_bex_cv_blocks()
934963
std::vector<std::vector<char>> blob_names(n_blobs);
935964
for (auto i : make_range(n_blobs))
936965
{
937-
blob_names[i].resize(MAX_STR_LENGTH+1);
966+
blob_names[i].resize(libmesh_max_str_length+1);
938967
blobs[i].name = blob_names[i].data();
939968
}
940969

@@ -991,7 +1020,7 @@ void ExodusII_IO_Helper::read_bex_cv_blocks()
9911020

9921021
int n_blob_vars;
9931022
exII::ex_get_variable_param(ex_id, exII::EX_BLOB, &n_blob_vars);
994-
std::vector<char> var_name (MAX_STR_LENGTH + 1);
1023+
std::vector<char> var_name (libmesh_max_str_length + 1);
9951024
for (auto v_id : make_range(1,n_blob_vars+1))
9961025
{
9971026
ex_err = exII::ex_get_variable_name(ex_id, exII::EX_BLOB, v_id, var_name.data());
@@ -1052,7 +1081,7 @@ void ExodusII_IO_Helper::read_block_info()
10521081
EX_CHECK_ERR(ex_err, "Error getting block IDs.");
10531082
message("All block IDs retrieved successfully.");
10541083

1055-
char name_buffer[MAX_STR_LENGTH+1];
1084+
char name_buffer[libmesh_max_str_length+1];
10561085
for (int i=0; i<num_elem_blk; ++i)
10571086
{
10581087
ex_err = exII::ex_get_name(ex_id, exII::EX_ELEM_BLOCK,
@@ -1075,7 +1104,7 @@ void ExodusII_IO_Helper::read_block_info()
10751104
message("All edge block IDs retrieved successfully.");
10761105

10771106
// Read in edge block names
1078-
char name_buffer[MAX_STR_LENGTH+1];
1107+
char name_buffer[libmesh_max_str_length+1];
10791108
for (int i=0; i<num_edge_blk; ++i)
10801109
{
10811110
ex_err = exII::ex_get_name(ex_id, exII::EX_EDGE_BLOCK,
@@ -1532,7 +1561,7 @@ void ExodusII_IO_Helper::read_sideset_info()
15321561
id_list.resize (num_elem_all_sidesets);
15331562
}
15341563

1535-
char name_buffer[MAX_STR_LENGTH+1];
1564+
char name_buffer[libmesh_max_str_length+1];
15361565
for (int i=0; i<num_side_sets; ++i)
15371566
{
15381567
ex_err = exII::ex_get_name(ex_id, exII::EX_SIDE_SET,
@@ -1560,7 +1589,7 @@ void ExodusII_IO_Helper::read_nodeset_info()
15601589
num_node_df_per_set.resize(num_node_sets);
15611590
}
15621591

1563-
char name_buffer[MAX_STR_LENGTH+1];
1592+
char name_buffer[libmesh_max_str_length+1];
15641593
for (int i=0; i<num_node_sets; ++i)
15651594
{
15661595
ex_err = exII::ex_get_name(ex_id, exII::EX_NODE_SET,
@@ -1600,7 +1629,7 @@ void ExodusII_IO_Helper::read_elemset_info()
16001629
// libMesh::out << "num_elem_all_elemsets = " << num_elem_all_elemsets << std::endl;
16011630
}
16021631

1603-
char name_buffer[MAX_STR_LENGTH+1];
1632+
char name_buffer[libmesh_max_str_length+1];
16041633
for (int i=0; i<num_elem_sets; ++i)
16051634
{
16061635
ex_err = exII::ex_get_name(ex_id, exII::EX_ELEM_SET,
@@ -1760,7 +1789,7 @@ void ExodusII_IO_Helper::read_all_nodesets()
17601789
EX_CHECK_ERR(ex_err, "Error reading concatenated nodesets");
17611790

17621791
// Read the nodeset names from file!
1763-
char name_buffer[MAX_STR_LENGTH+1];
1792+
char name_buffer[libmesh_max_str_length+1];
17641793
for (int i=0; i<num_node_sets; ++i)
17651794
{
17661795
ex_err = exII::ex_get_name
@@ -1945,7 +1974,7 @@ void ExodusII_IO_Helper::read_var_names_impl(const char * var_type,
19451974
return;
19461975

19471976
// Second read the actual names and convert them into a format we can use
1948-
NamesData names_table(count, MAX_STR_LENGTH);
1977+
NamesData names_table(count, libmesh_max_str_length);
19491978

19501979
ex_err = exII::ex_get_var_names(ex_id,
19511980
var_type,
@@ -2031,16 +2060,17 @@ ExodusII_IO_Helper::write_var_names_impl(const char * var_type,
20312060

20322061
if (count > 0)
20332062
{
2034-
NamesData names_table(count, MAX_STR_LENGTH);
2063+
NamesData names_table(count, _max_name_length);
20352064

20362065
// Store the input names in the format required by Exodus.
20372066
for (int i=0; i != count; ++i)
20382067
{
2039-
if(names[i].length() > MAX_STR_LENGTH)
2068+
if(names[i].length() > _max_name_length)
20402069
libmesh_warning(
2041-
"*** Warning, Exodus variable name \""
2042-
<< names[i] << "\" too long (max " << MAX_STR_LENGTH
2043-
<< " characters). Name will be truncated. ");
2070+
"*** Warning, Exodus variable name \"" <<
2071+
names[i] << "\" too long (current max " <<
2072+
_max_name_length << "/" << libmesh_max_str_length <<
2073+
" characters). Name will be truncated. ");
20442074
names_table.push_back_entry(names[i]);
20452075
}
20462076

@@ -2296,6 +2326,13 @@ void ExodusII_IO_Helper::create(std::string filename)
22962326

22972327
EX_CHECK_ERR(ex_id, "Error creating ExodusII/Nemesis mesh file.");
22982328

2329+
// We don't have access to the names we might be writing until we
2330+
// write them, so we can't set a guaranteed max name length here.
2331+
// But it looks like the most ExodusII can support is 80, so we'll
2332+
// just waste 48 bytes here and there.
2333+
ex_err = exII::ex_set_max_name_length(ex_id, _max_name_length);
2334+
EX_CHECK_ERR(ex_err, "Error setting max ExodusII name length.");
2335+
22992336
if (verbose)
23002337
libMesh::out << "File created successfully." << std::endl;
23012338
}
@@ -2683,13 +2720,13 @@ void ExodusII_IO_Helper::write_elements(const MeshBase & mesh, bool use_disconti
26832720
std::vector<int> num_edges_per_elem_vec;
26842721
std::vector<int> num_faces_per_elem_vec;
26852722
std::vector<int> num_attr_vec;
2686-
NamesData elem_type_table(num_elem_blk, MAX_STR_LENGTH);
2723+
NamesData elem_type_table(num_elem_blk, _max_name_length);
26872724

26882725
// Note: It appears that there is a bug in exodusII::ex_put_name where
26892726
// the index returned from the ex_id_lkup is erroneously used. For now
26902727
// the work around is to use the alternative function ex_put_names, but
26912728
// this function requires a char ** data structure.
2692-
NamesData names_table(num_elem_blk, MAX_STR_LENGTH);
2729+
NamesData names_table(num_elem_blk, _max_name_length);
26932730

26942731
num_elem = 0;
26952732

@@ -2870,14 +2907,14 @@ void ExodusII_IO_Helper::write_elements(const MeshBase & mesh, bool use_disconti
28702907
// be passed to exII::ex_put_concat_all_blocks() at the same time as the
28712908
// information about elem blocks.
28722909
std::vector<int> edge_blk_id;
2873-
NamesData edge_type_table(num_edge_blk, MAX_STR_LENGTH);
2910+
NamesData edge_type_table(num_edge_blk, _max_name_length);
28742911
std::vector<int> num_edge_this_blk_vec;
28752912
std::vector<int> num_nodes_per_edge_vec;
28762913
std::vector<int> num_attr_edge_vec;
28772914

28782915
// We also build a data structure of edge block names which can
28792916
// later be passed to exII::ex_put_names().
2880-
NamesData edge_block_names_table(num_edge_blk, MAX_STR_LENGTH);
2917+
NamesData edge_block_names_table(num_edge_blk, _max_name_length);
28812918

28822919
// Note: We are going to use the edge **boundary** ids as **block** ids.
28832920
for (const auto & pr : edge_id_to_conn)
@@ -3217,7 +3254,7 @@ void ExodusII_IO_Helper::write_sidesets(const MeshBase & mesh)
32173254
// Write out the sideset names, but only if there is something to write
32183255
if (side_boundary_ids.size() > 0)
32193256
{
3220-
NamesData names_table(side_boundary_ids.size(), MAX_STR_LENGTH);
3257+
NamesData names_table(side_boundary_ids.size(), _max_name_length);
32213258

32223259
std::vector<exII::ex_set> sets(side_boundary_ids.size());
32233260

@@ -3298,7 +3335,7 @@ void ExodusII_IO_Helper::write_nodesets(const MeshBase & mesh)
32983335
// Write out the nodeset names, but only if there is something to write
32993336
if (node_boundary_ids.size() > 0)
33003337
{
3301-
NamesData names_table(node_boundary_ids.size(), MAX_STR_LENGTH);
3338+
NamesData names_table(node_boundary_ids.size(), _max_name_length);
33023339

33033340
// Vectors to be filled and passed to exII::ex_put_concat_sets()
33043341
// Use existing class members and avoid variable shadowing.
@@ -3500,10 +3537,10 @@ void ExodusII_IO_Helper::check_existing_vars(ExodusVarType type,
35003537
bool match =
35013538
std::equal(names.begin(), names.end(),
35023539
names_from_file.begin(),
3503-
[](const std::string & a,
3504-
const std::string & b) -> bool
3540+
[this](const std::string & a,
3541+
const std::string & b) -> bool
35053542
{
3506-
return a.compare(/*pos=*/0, /*len=*/MAX_STR_LENGTH, b) == 0;
3543+
return a.compare(/*pos=*/0, /*len=*/_max_name_length, b) == 0;
35073544
});
35083545

35093546
if (!match)
@@ -3553,7 +3590,7 @@ ExodusII_IO_Helper::write_elemsets(const MeshBase & mesh)
35533590
return;
35543591

35553592
// TODO: Add support for named elemsets
3556-
// NamesData names_table(elemsets.size(), MAX_STR_LENGTH);
3593+
// NamesData names_table(elemsets.size(), _max_name_length);
35573594

35583595
// We only need to write elemsets if the Mesh has an extra elem
35593596
// integer called "elemset_code" defined on it.
@@ -4811,6 +4848,18 @@ void ExodusII_IO_Helper::set_hdf5_writing(bool write_hdf5)
48114848
}
48124849

48134850

4851+
void ExodusII_IO_Helper::set_max_name_length(unsigned int max_length)
4852+
{
4853+
// Opt mode error, because this may be exposed to users
4854+
libmesh_error_msg_if (max_length > libmesh_max_str_length,
4855+
"Exodus maximum name length is limited to " <<
4856+
libmesh_max_str_length << " characters");
4857+
4858+
// Devel+dbg mode assertion, because developers should do better
4859+
libmesh_assert(!opened_for_writing);
4860+
4861+
_max_name_length = max_length;
4862+
}
48144863

48154864

48164865
void ExodusII_IO_Helper::write_as_dimension(unsigned dim)

src/mesh/mesh_base.C

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1882,6 +1882,16 @@ void MeshBase::cache_elem_data()
18821882
}
18831883
}
18841884

1885+
1886+
void MeshBase::sync_subdomain_name_map()
1887+
{
1888+
// This requires every processor
1889+
parallel_object_only();
1890+
1891+
this->comm().set_union(_block_id_to_name);
1892+
}
1893+
1894+
18851895
void MeshBase::detect_interior_parents()
18861896
{
18871897
// This requires an inspection on every processor

0 commit comments

Comments
 (0)