@@ -59,6 +59,11 @@ extern "C" {
5959namespace
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+
6267using 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
48164865void ExodusII_IO_Helper ::write_as_dimension (unsigned dim )
0 commit comments