LCOV - code coverage report
Current view: top level - src/mesh - abaqus_io.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4229 (6a9aeb) with base 727f46 Lines: 339 456 74.3 %
Date: 2025-08-19 19:27:09 Functions: 22 25 88.0 %
Legend: Lines: hit not hit

          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             : // Local includes
      19             : #include "libmesh/abaqus_io.h"
      20             : #include "libmesh/point.h"
      21             : #include "libmesh/elem.h"
      22             : #include "libmesh/enum_to_string.h"
      23             : #include "libmesh/boundary_info.h"
      24             : #include "libmesh/utility.h"
      25             : 
      26             : // gzstream for reading compressed files as a stream
      27             : #ifdef LIBMESH_HAVE_GZSTREAM
      28             : # include "gzstream.h"
      29             : #endif
      30             : 
      31             : // C++ includes
      32             : #include <unordered_map>
      33             : #include <string>
      34             : #include <cstdlib> // std::strtol
      35             : #include <sstream>
      36             : #include <cctype> // isspace
      37             : 
      38             : // Anonymous namespace to hold mapping Data for Abaqus/libMesh element types
      39             : namespace
      40             : {
      41             : using namespace libMesh;
      42             : 
      43             : /**
      44             :  * Data structure used for mapping Abaqus IDs to libMesh IDs, and
      45             :  * eventually (possibly) vice-versa.
      46             :  */
      47             : struct ElementDefinition
      48             : {
      49             :   // Maps (zero-based!) Abaqus local node numbers to libmesh local node numbers
      50             :   std::vector<unsigned> abaqus_zero_based_node_id_to_libmesh_node_id;
      51             : 
      52             :   // Maps (zero-based!) Abaqus side numbers to libmesh side numbers
      53             :   std::vector<unsigned short> abaqus_zero_based_side_id_to_libmesh_side_id;
      54             : };
      55             : 
      56             : /**
      57             :  * Locally-available map containing all element data.
      58             :  */
      59             : std::map<ElemType, ElementDefinition> eletypes;
      60             : 
      61             : /**
      62             :  * Helper function to fill up eletypes map
      63             :  */
      64         168 : void add_eletype_entry(ElemType libmesh_elem_type,
      65             :                        const unsigned * node_map,
      66             :                        unsigned node_map_size,
      67             :                        const unsigned short * side_map,
      68             :                        unsigned side_map_size)
      69             : {
      70             :   // If map entry does not exist, this will create it
      71         168 :   ElementDefinition & map_entry = eletypes[libmesh_elem_type];
      72             : 
      73             : 
      74             :   // Use the "swap trick" from Scott Meyer's "Effective STL" to swap
      75             :   // an unnamed temporary vector into the map_entry's vector.  Note:
      76             :   // the vector(iter, iter) constructor is used.
      77         336 :   std::vector<unsigned>
      78         168 :     (node_map, node_map+node_map_size).swap
      79          14 :     (map_entry.abaqus_zero_based_node_id_to_libmesh_node_id);
      80             : 
      81         336 :   std::vector<unsigned short>
      82         168 :     (side_map, side_map+side_map_size).swap
      83          14 :     (map_entry.abaqus_zero_based_side_id_to_libmesh_side_id);
      84         168 : }
      85             : 
      86             : 
      87             : /**
      88             :  * Helper function to initialize the eletypes map.
      89             :  */
      90          48 : void init_eletypes ()
      91             : {
      92             :   // This should happen only once.  The first time this method is
      93             :   // called the eletypes data structure will be empty, and we will
      94             :   // fill it.  Any subsequent calls will find an initialized
      95             :   // eletypes map and will do nothing.
      96          48 :   if (eletypes.empty())
      97             :     {
      98             :       {
      99             :         // NODEELEM
     100          12 :         const unsigned int   node_map[] = {0}; // identity
     101          12 :         const unsigned short side_map[] = {0}; // identity
     102          12 :         add_eletype_entry(NODEELEM, node_map, 1, side_map, 1);
     103             :       }
     104             : 
     105             :       {
     106             :         // EDGE2
     107          12 :         const unsigned int   node_map[] = {0,1}; // identity
     108          12 :         const unsigned short side_map[] = {0,1}; // identity
     109          12 :         add_eletype_entry(EDGE2, node_map, 2, side_map, 2);
     110             :       }
     111             : 
     112             :       {
     113             :         // TRI3
     114          12 :         const unsigned int   node_map[] = {0,1,2}; // identity
     115          12 :         const unsigned short side_map[] = {0,1,2}; // identity
     116          12 :         add_eletype_entry(TRI3, node_map, 3, side_map, 3);
     117             :       }
     118             : 
     119             :       {
     120             :         // TRI6
     121          12 :         const unsigned int   node_map[] = {0,1,2,3,4,5}; // identity
     122          12 :         const unsigned short side_map[] = {0,1,2}; // identity
     123          12 :         add_eletype_entry(TRI6, node_map, 6, side_map, 3);
     124             :       }
     125             : 
     126             :       {
     127             :         // QUAD4
     128          12 :         const unsigned int   node_map[] = {0,1,2,3}; // identity
     129          12 :         const unsigned short side_map[] = {0,1,2,3}; // identity
     130          12 :         add_eletype_entry(QUAD4, node_map, 4, side_map, 4);
     131             :       }
     132             : 
     133             :       {
     134             :         // QUAD8
     135          12 :         const unsigned int   node_map[] = {0,1,2,3,4,5,6,7}; // identity
     136          12 :         const unsigned short side_map[] = {0,1,2,3}; // identity
     137          12 :         add_eletype_entry(QUAD8, node_map, 8, side_map, 4);
     138             :       }
     139             : 
     140             :       {
     141             :         // TET4
     142          12 :         const unsigned int   node_map[] = {0,1,2,3}; // identity
     143          12 :         const unsigned short side_map[] = {0,1,2,3}; // identity
     144          12 :         add_eletype_entry(TET4, node_map, 4, side_map, 4);
     145             :       }
     146             : 
     147             :       {
     148             :         // TET10
     149          12 :         const unsigned int   node_map[] = {0,1,2,3,4,5,6,7,8,9}; // identity
     150          12 :         const unsigned short side_map[] = {0,1,2,3};             // identity
     151          12 :         add_eletype_entry(TET10, node_map, 10, side_map, 4);
     152             :       }
     153             : 
     154             :       {
     155             :         // HEX8
     156          12 :         const unsigned int   node_map[] = {0,1,2,3,4,5,6,7}; // identity
     157          12 :         const unsigned short side_map[] = {0,5,1,2,3,4};     // inverse = 0,2,3,4,5,1
     158          12 :         add_eletype_entry(HEX8, node_map, 8, side_map, 6);
     159             :       }
     160             : 
     161             :       {
     162             :         // HEX20
     163          12 :         const unsigned int   node_map[] = // map is its own inverse
     164             :           {0,1,2,3,4,5,6,7,8,9,10,11,16,17,18,19,12,13,14,15};
     165          12 :         const unsigned short side_map[] = // inverse = 0,2,3,4,5,1
     166             :           {0,5,1,2,3,4};
     167          12 :         add_eletype_entry(HEX20, node_map, 20, side_map, 6);
     168             :       }
     169             : 
     170             :       {
     171             :         // HEX27
     172          12 :         const unsigned int   node_map[] = // inverse = ...,21,23,24,25,26,22,20
     173             :           {0,1,2,3,4,5,6,7,8,9,10,11,16,17,18,19,12,13,14,15,26,20,25,21,22,23,24};
     174          12 :         const unsigned short side_map[] = // inverse = 0,2,3,4,5,1
     175             :           {0,5,1,2,3,4};
     176          12 :         add_eletype_entry(HEX27, node_map, 27, side_map, 6);
     177             :       }
     178             : 
     179             :       {
     180             :         // PRISM6
     181          12 :         const unsigned int   node_map[] = {0,1,2,3,4,5}; // identity
     182          12 :         const unsigned short side_map[] = {0,4,1,2,3};   // inverse = 0,2,3,4,1
     183          12 :         add_eletype_entry(PRISM6, node_map, 6, side_map, 5);
     184             :       }
     185             : 
     186             :       {
     187             :         // PRISM15
     188          12 :         const unsigned int   node_map[] = // map is its own inverse
     189             :           {0,1,2,3,4,5,6,7,8,12,13,14,9,10,11};
     190          12 :         const unsigned short side_map[] = // inverse = 0,2,3,4,1
     191             :           {0,4,1,2,3};
     192          12 :         add_eletype_entry(PRISM15, node_map, 15, side_map, 5);
     193             :       }
     194             : 
     195             :       {
     196             :         // PRISM18
     197          12 :         const unsigned int   node_map[] = // map is its own inverse
     198             :           {0,1,2,3,4,5,6,7,8,12,13,14,9,10,11,15,16,17};
     199          12 :         const unsigned short side_map[] = // inverse = 0,2,3,4,1
     200             :           {0,4,1,2,3};
     201          12 :         add_eletype_entry(PRISM18, node_map, 18, side_map, 5);
     202             :       }
     203             :     } // if (eletypes.empty())
     204          48 : }
     205             : } // anonymous namespace
     206             : 
     207             : 
     208             : 
     209             : 
     210             : 
     211             : namespace libMesh
     212             : {
     213             : 
     214         142 : AbaqusIO::AbaqusIO (MeshBase & mesh_in) :
     215             :   MeshInput<MeshBase> (mesh_in),
     216         134 :   build_sidesets_from_nodesets(false),
     217         142 :   _already_seen_part(false)
     218             : {
     219         142 : }
     220             : 
     221             : 
     222             : 
     223         134 : AbaqusIO::~AbaqusIO () = default;
     224             : 
     225             : 
     226             : 
     227          24 : void AbaqusIO::read (const std::string & fname)
     228             : {
     229             :   // Get a reference to the mesh we are reading
     230          24 :   MeshBase & the_mesh = MeshInput<MeshBase>::mesh();
     231             : 
     232             :   // Clear any existing mesh data
     233          24 :   the_mesh.clear();
     234             : 
     235             :   // Open stream for reading
     236          24 :   const bool gzipped_file = Utility::ends_with(fname, ".gz");
     237             : 
     238          24 :   if (gzipped_file)
     239             :     {
     240             : #ifdef LIBMESH_HAVE_GZSTREAM
     241          26 :       auto inf = std::make_unique<igzstream>();
     242           2 :       libmesh_assert(inf);
     243           2 :       inf->open(fname.c_str(), std::ios::in);
     244          22 :       _in = std::move(inf); // class takes ownership as base class pointer
     245             : #else
     246             :       libmesh_error_msg("ERROR: need gzstream to handle .gz files!!!");
     247             : #endif
     248          20 :     }
     249             :   else
     250             :     {
     251           0 :       auto inf = std::make_unique<std::ifstream>();
     252           0 :       libmesh_assert(inf);
     253             : 
     254           0 :       std::string new_name = Utility::unzip_file(fname);
     255           0 :       inf->open(new_name.c_str(), std::ios::in);
     256           0 :       libmesh_assert(inf->good());
     257           0 :       _in = std::move(inf); // class takes ownership as base class pointer
     258           0 :     }
     259             : 
     260             :   // Initialize the elems_of_dimension array.  We will use this in a
     261             :   // "1-based" manner so that elems_of_dimension[d]==true means
     262             :   // elements of dimension d have been seen.
     263          24 :   elems_of_dimension.resize(4, false);
     264             : 
     265             :   // Read file line-by-line... this is based on a set of different
     266             :   // test input files.  I have not looked at the full input file
     267             :   // specs for Abaqus.
     268           4 :   std::string s;
     269             :   while (true)
     270             :     {
     271             :       // Try to read something.  This may set EOF!
     272        1332 :       std::getline(*_in, s);
     273             : 
     274        1443 :       if (*_in)
     275             :         {
     276             :           // Process s...
     277             :           //
     278             :           // There are many sections in Abaqus files, we read some
     279             :           // but others are just ignored...  Some sections may occur
     280             :           // more than once.  For example for a hybrid grid, you
     281             :           // will have multiple *Element sections...
     282             : 
     283             :           // Some Abaqus files use all upper-case for section names,
     284             :           // so we will just convert s to uppercase
     285        1308 :           std::string upper(s);
     286         109 :           std::transform(upper.begin(), upper.end(), upper.begin(), ::toupper);
     287             : 
     288             :           // 0.) Look for the "*Part" section
     289        1308 :           if (upper.find("*PART") == static_cast<std::string::size_type>(0))
     290             :             {
     291           0 :               libmesh_error_msg_if
     292             :                 (_already_seen_part,
     293             :                  "We currently don't support reading Abaqus files with multiple PART sections");
     294             : 
     295           0 :               _already_seen_part = true;
     296             :             }
     297             : 
     298             :           // 1.) Look for the "*Nodes" section
     299        1308 :           if (upper.find("*NODE") == static_cast<std::string::size_type>(0))
     300             :             {
     301             :               // Some sections that begin with *NODE are actually
     302             :               // unrelated sections which we want to skip.  I
     303             :               // have only seen this with a single space, but it would
     304             :               // probably be more robust to remove whitespace before
     305             :               // making this check.
     306           4 :               bool skip_this_section = false;
     307         238 :               std::vector<std::string> keywords_to_ignore = {"OUTPUT", "PRINT", "FILE", "RESPONSE"};
     308         240 :               for (auto & affix : keywords_to_ignore) {
     309         224 :                 std::stringstream keyword;
     310         176 :                 keyword << "*NODE " << affix;
     311         192 :                 if (upper.find(keyword.str()) == static_cast<std::string::size_type>(0))
     312           2 :                   skip_this_section = true;
     313         160 :               }
     314          48 :               if (skip_this_section)
     315           4 :                 continue;
     316             : 
     317             :               // Some *Node sections also specify an Nset name on the same line.
     318             :               // Look for one here.
     319          50 :               std::string nset_name = this->parse_label(s, "nset");
     320             : 
     321             :               // Process any lines of comments that may be present
     322          24 :               this->process_and_discard_comments();
     323             : 
     324             :               // Read a block of nodes
     325          46 :               this->read_nodes(nset_name);
     326          40 :             }
     327             : 
     328             : 
     329             : 
     330             :           // 2.) Look for the "*Element" section
     331        1260 :           else if (upper.find("*ELEMENT,") == static_cast<std::string::size_type>(0))
     332             :             {
     333             :               // Some sections that begin with *ELEMENT are actually
     334             :               // "*ELEMENT OUTPUT" sections which we want to skip.  I
     335             :               // have only seen this with a single space, but it would
     336             :               // probably be more robust to remove whitespace before
     337             :               // making this check.
     338           2 :               bool skip_this_section = false;
     339         120 :               std::vector<std::string> keywords_to_ignore = {"OUTPUT", "MATRIX", "PROPERTIES", "RESPONSE"};
     340         120 :               for (auto & affix : keywords_to_ignore) {
     341         112 :                 std::stringstream keyword;
     342          88 :                 keyword << "*ELEMENT " << affix;
     343          96 :                 if (upper.find(keyword.str()) == static_cast<std::string::size_type>(0))
     344           0 :                   skip_this_section = true;
     345          80 :               }
     346          24 :               if (skip_this_section)
     347           0 :                 continue;
     348             : 
     349             :               // Some *Element sections also specify an Elset name on the same line.
     350             :               // Look for one here.
     351          50 :               std::string elset_name = this->parse_label(s, "elset");
     352             : 
     353             :               // Process any lines of comments that may be present
     354          24 :               this->process_and_discard_comments();
     355             : 
     356             :               // Read a block of elements
     357          46 :               this->read_elements(upper, elset_name);
     358          20 :             }
     359             : 
     360             : 
     361             : 
     362             :           // 3.) Look for a Nodeset section
     363        1236 :           else if (upper.find("*NSET") == static_cast<std::string::size_type>(0))
     364             :             {
     365         182 :               std::string nset_name = this->parse_label(s, "nset");
     366             : 
     367             :               // I haven't seen an unnamed nset yet, but let's detect it
     368             :               // just in case...
     369          84 :               libmesh_error_msg_if(nset_name == "", "Unnamed nset encountered!");
     370             : 
     371             :               // Is this a "generated" nset, i.e. one which has three
     372             :               // entries corresponding to (first, last, stride)?
     373          84 :               bool is_generated = this->detect_generated_set(upper);
     374             : 
     375             :               // Process any lines of comments that may be present
     376          84 :               this->process_and_discard_comments();
     377             : 
     378             :               // Read the IDs, storing them in _nodeset_ids
     379          84 :               if (is_generated)
     380          92 :                 this->generate_ids(nset_name, _nodeset_ids);
     381             :               else
     382          69 :                 this->read_ids(nset_name, _nodeset_ids);
     383             :             } // *Nodeset
     384             : 
     385             : 
     386             : 
     387             :           // 4.) Look for an Elset section
     388        1152 :           else if (upper.find("*ELSET") == static_cast<std::string::size_type>(0))
     389             :             {
     390         468 :               std::string elset_name = this->parse_label(s, "elset");
     391             : 
     392             :               // I haven't seen an unnamed elset yet, but let's detect it
     393             :               // just in case...
     394         216 :               libmesh_error_msg_if(elset_name == "", "Unnamed elset encountered!");
     395             : 
     396             :               // Is this a "generated" elset, i.e. one which has three
     397             :               // entries corresponding to (first, last, stride)?
     398         216 :               bool is_generated = this->detect_generated_set(upper);
     399             : 
     400             :               // Process any lines of comments that may be present
     401         216 :               this->process_and_discard_comments();
     402             : 
     403             :               // Read the IDs, storing them in _elemset_ids
     404         216 :               if (is_generated)
     405         230 :                 this->generate_ids(elset_name, _elemset_ids);
     406             :               else
     407         184 :                 this->read_ids(elset_name, _elemset_ids);
     408             :             } // *Elset
     409             : 
     410             : 
     411             : 
     412             :           // 5.) Look for a Surface section.  Need to be a little
     413             :           // careful, since there are also "surface interaction"
     414             :           // sections we don't want to read here.
     415         936 :           else if (upper.find("*SURFACE,") == static_cast<std::string::size_type>(0))
     416             :             {
     417             :               // Get the name from the Name=Foo label.  This will be the map key.
     418           0 :               std::string sideset_name = this->parse_label(s, "name");
     419             : 
     420             :               // Some (all?) surfaces also declare a type, which can
     421             :               // help us figure out how they should be parsed, so we
     422             :               // pass this to the read_sideset() function.
     423           0 :               std::string sideset_type = this->parse_label(s, "type");
     424             : 
     425             :               // Process any lines of comments that may be present
     426           0 :               this->process_and_discard_comments();
     427             : 
     428             :               // Read the sideset IDs
     429           0 :               this->read_sideset(sideset_name, sideset_type, _sideset_ids);
     430             :             }
     431             : 
     432             :           // Derived classes could override this to add support for additional sections
     433        1284 :           extended_parsing(upper);
     434             : 
     435        1177 :           continue;
     436             :         } // if (*_in)
     437             : 
     438             :       // If !file, check to see if EOF was set.  If so, break out
     439             :       // of while loop.
     440          24 :       if (_in->eof())
     441           2 :         break;
     442             : 
     443             :       // If !in and !in.eof(), stream is in a bad state!
     444           0 :       libmesh_error_msg("Stream is bad! Perhaps the file: " << fname << " does not exist?");
     445         109 :     } // while
     446             : 
     447             :   // Set the Mesh dimension based on the highest dimension element seen.
     448          24 :   the_mesh.set_mesh_dimension(this->max_elem_dimension_seen());
     449             : 
     450             :   // Set element IDs based on the element sets.
     451          24 :   this->assign_subdomain_ids();
     452             : 
     453             :   // Assign nodeset values to the BoundaryInfo object
     454          24 :   this->assign_boundary_node_ids();
     455             : 
     456             :   // Assign sideset values in the BoundaryInfo object
     457          24 :   this->assign_sideset_ids();
     458             : 
     459             :   // If the Abaqus file contains only nodesets, we can have libmesh
     460             :   // generate sidesets from them. This BoundaryInfo function currently
     461             :   // *overwrites* existing sidesets in surprising ways, so we don't
     462             :   // call it if there are already sidesets present in the original file.
     463          24 :   if (build_sidesets_from_nodesets && the_mesh.get_boundary_info().n_boundary_conds() == 0)
     464           0 :     the_mesh.get_boundary_info().build_side_list_from_node_list();
     465             : 
     466             :   // Delete lower-dimensional elements from the Mesh.  We assume these
     467             :   // were only used for setting BCs, and aren't part of the actual
     468             :   // Mesh.
     469             :   {
     470          24 :     unsigned char max_dim = this->max_elem_dimension_seen();
     471             : 
     472       44222 :     for (auto & elem : the_mesh.element_ptr_range())
     473       24096 :       if (elem->dim() < max_dim)
     474          20 :         the_mesh.delete_elem(elem);
     475             :   }
     476         144 : }
     477             : 
     478             : 
     479             : 
     480             : 
     481             : 
     482             : 
     483             : 
     484          24 : void AbaqusIO::read_nodes(std::string nset_name)
     485             : {
     486             :   // Get a reference to the mesh we are reading
     487          24 :   MeshBase & the_mesh = MeshInput<MeshBase>::mesh();
     488             : 
     489             :   // In the input files I have, Abaqus neither tells what
     490             :   // the mesh dimension is nor how many nodes it has...
     491             :   //
     492             :   // The node line format is:
     493             :   // id, x, y, z
     494             :   // and you do have to parse out the commas.
     495             :   // The z-coordinate will only be present for 3D meshes
     496             : 
     497             :   // Temporary variables for parsing lines of text
     498             :   char c;
     499           4 :   std::string line;
     500             : 
     501             :   // We need to duplicate some of the read_ids code if this *NODE
     502             :   // section also defines an NSET.  We'll set up the id_storage
     503             :   // pointer and push back IDs into this vector in the loop below...
     504           2 :   std::vector<dof_id_type> * id_storage = nullptr;
     505          24 :   if (nset_name != "")
     506           0 :     id_storage = &(_nodeset_ids[nset_name]);
     507             : 
     508             :   // We will read nodes until the next line begins with *, since that will be the
     509             :   // next section.
     510             :   // TODO: Is Abaqus guaranteed to start the line with '*' or can there be leading white space?
     511       33516 :   while (_in->peek() != '*' && _in->peek() != EOF)
     512             :     {
     513             :       // Read an entire line which corresponds to a single point's id
     514             :       // and (x,y,z) values.
     515       33492 :       std::getline(*_in, line);
     516             : 
     517             :       // Remove all whitespace characters from the line.  This way we
     518             :       // can do the remaining parsing without worrying about tabs,
     519             :       // different numbers of spaces, etc.
     520       33492 :       strip_ws(line);
     521             : 
     522             :       // Make a stream out of the modified line so we can stream values
     523             :       // from it in the usual way.
     524       33492 :       std::stringstream ss(line);
     525             : 
     526             :       // Values to be read in from file
     527       33492 :       dof_id_type abaqus_node_id=0;
     528       33492 :       Real x=0, y=0, z=0;
     529             : 
     530             :       // Note: we assume *at least* 2D points here, should we worry about
     531             :       // trying to read 1D Abaqus meshes?
     532       64193 :       ss >> abaqus_node_id >> c >> x >> c >> y;
     533             : 
     534             :       // Peek at the next character.  If it is a comma, then there is another
     535             :       // value to read!
     536       33492 :       if (ss.peek() == ',')
     537       33492 :         ss >> c >> z;
     538             : 
     539             :       // If this *NODE section defines an NSET, also store the abaqus ID in id_storage
     540       33492 :       if (id_storage)
     541           0 :         id_storage->push_back(abaqus_node_id);
     542             : 
     543             :       // Convert from Abaqus 1-based to libMesh 0-based numbering
     544       33492 :       libmesh_error_msg_if(abaqus_node_id < 1,
     545             :                            "Invalid Abaqus node ID found");
     546       33492 :       const dof_id_type libmesh_node_id = abaqus_node_id-1;
     547             : 
     548       33492 :       libmesh_error_msg_if(the_mesh.query_node_ptr(libmesh_node_id),
     549             :                            "Duplicate Abaqus node ID found");
     550             : 
     551             :       // Add the point to the mesh using libmesh's numbering,
     552             :       // and post-increment the libmesh node counter.
     553       36283 :       the_mesh.add_point(Point(x,y,z), libmesh_node_id);
     554       27910 :     } // while
     555          24 : }
     556             : 
     557             : 
     558             : 
     559             : 
     560             : 
     561          24 : void AbaqusIO::read_elements(std::string upper, std::string elset_name)
     562             : {
     563             :   // Get a reference to the mesh we are reading
     564          24 :   MeshBase & the_mesh = MeshInput<MeshBase>::mesh();
     565             : 
     566             :   // initialize the eletypes map (eletypes is a file-global variable)
     567          24 :   init_eletypes();
     568             : 
     569          24 :   ElemType elem_type = INVALID_ELEM;
     570           2 :   unsigned n_nodes_per_elem = 0;
     571             : 
     572             :   // Within s, we should have "type=XXXX"
     573          48 :   if (upper.find("T3D2") != std::string::npos ||
     574          22 :       upper.find("B31") != std::string::npos)
     575             :     {
     576           0 :       elem_type = EDGE2;
     577           0 :       n_nodes_per_elem = 2;
     578           0 :       elems_of_dimension[1] = true;
     579             :     }
     580          24 :   else if (upper.find("B32") != std::string::npos)
     581             :     {
     582           0 :       elem_type = EDGE3;
     583           0 :       n_nodes_per_elem = 3;
     584           0 :       elems_of_dimension[1] = true;
     585             :     }
     586          46 :   else if (upper.find("S3") != std::string::npos ||
     587          48 :            upper.find("CPE3") != std::string::npos ||
     588          22 :            upper.find("2D3") != std::string::npos)
     589             :     {
     590           0 :       elem_type = TRI3;
     591           0 :       n_nodes_per_elem = 3;
     592           0 :       elems_of_dimension[2] = true;
     593             :     }
     594          46 :   else if (upper.find("CPE4") != std::string::npos ||
     595          46 :            upper.find("S4") != std::string::npos ||
     596          70 :            upper.find("CPEG4") != std::string::npos ||
     597          22 :            upper.find("2D4") != std::string::npos)
     598             :     {
     599           0 :       elem_type = QUAD4;
     600           0 :       n_nodes_per_elem = 4;
     601           0 :       elems_of_dimension[2] = true;
     602             :     }
     603          46 :   else if (upper.find("CPE6") != std::string::npos ||
     604          46 :            upper.find("S6") != std::string::npos ||
     605          70 :            upper.find("CPEG6") != std::string::npos ||
     606          22 :            upper.find("2D6") != std::string::npos)
     607             :     {
     608           0 :       elem_type = TRI6;
     609           0 :       n_nodes_per_elem = 6;
     610           0 :       elems_of_dimension[2] = true;
     611             :     }
     612          46 :   else if (upper.find("CPE8") != std::string::npos ||
     613          46 :            upper.find("S8") != std::string::npos ||
     614          70 :            upper.find("CPEG8") != std::string::npos ||
     615          22 :            upper.find("2D8") != std::string::npos)
     616             :     {
     617           0 :       elem_type = QUAD8;
     618           0 :       n_nodes_per_elem = 8;
     619           0 :       elems_of_dimension[2] = true;
     620             :     }
     621          24 :   else if (upper.find("3D8") != std::string::npos)
     622             :     {
     623          24 :       elem_type = HEX8;
     624           2 :       n_nodes_per_elem = 8;
     625           4 :       elems_of_dimension[3] = true;
     626             :     }
     627           0 :   else if (upper.find("3D4") != std::string::npos)
     628             :     {
     629           0 :       elem_type = TET4;
     630           0 :       n_nodes_per_elem = 4;
     631           0 :       elems_of_dimension[3] = true;
     632             :     }
     633           0 :   else if (upper.find("3D20") != std::string::npos)
     634             :     {
     635           0 :       elem_type = HEX20;
     636           0 :       n_nodes_per_elem = 20;
     637           0 :       elems_of_dimension[3] = true;
     638             :     }
     639           0 :   else if (upper.find("3D27") != std::string::npos)
     640             :     {
     641           0 :       elem_type = HEX27;
     642           0 :       n_nodes_per_elem = 27;
     643           0 :       elems_of_dimension[3] = true;
     644             :     }
     645           0 :   else if (upper.find("3D6") != std::string::npos)
     646             :     {
     647           0 :       elem_type = PRISM6;
     648           0 :       n_nodes_per_elem = 6;
     649           0 :       elems_of_dimension[3] = true;
     650             :     }
     651           0 :   else if (upper.find("3D15") != std::string::npos)
     652             :     {
     653           0 :       elem_type = PRISM15;
     654           0 :       n_nodes_per_elem = 15;
     655           0 :       elems_of_dimension[3] = true;
     656             :     }
     657           0 :   else if (upper.find("3D10") != std::string::npos)
     658             :     {
     659           0 :       elem_type = TET10;
     660           0 :       n_nodes_per_elem = 10;
     661           0 :       elems_of_dimension[3] = true;
     662             :     }
     663           0 :   else if (upper.find("MASS") != std::string::npos)
     664             :     {
     665             :       // "MASS" elements are used to indicate point masses in Abaqus
     666             :       // models. These are mapped to NODEELEMs in libmesh.
     667           0 :       elem_type = NODEELEM;
     668           0 :       n_nodes_per_elem = 1;
     669           0 :       elems_of_dimension[0] = true;
     670             :     }
     671             :   else
     672           0 :     libmesh_error_msg("Unrecognized element type: " << upper);
     673             : 
     674             :   // Insert the elem type we detected into the set of all elem types for this mesh
     675          22 :   _elem_types.insert(elem_type);
     676             : 
     677             :   // Grab a reference to the element definition for this element type
     678          24 :   const ElementDefinition & eledef = eletypes[elem_type];
     679             : 
     680             :   // If the element definition was not found, the call above would have
     681             :   // created one with an uninitialized struct.  Check for that here...
     682          24 :   libmesh_error_msg_if
     683             :     (eledef.abaqus_zero_based_node_id_to_libmesh_node_id.size() == 0,
     684             :      "No Abaqus->LibMesh mapping information for ElemType "
     685             :      << Utility::enum_to_string(elem_type) << "!");
     686             : 
     687             :   // We will read elements until the next line begins with *, since that will be the
     688             :   // next section.
     689       24120 :   while (_in->peek() != '*' && _in->peek() != EOF)
     690             :     {
     691             :       // Read the element ID, it is the first number on each line.  It is
     692             :       // followed by a comma, so read that also.  We will need this ID later
     693             :       // when we try to assign subdomain IDs
     694       24096 :       dof_id_type abaqus_elem_id = 0;
     695             :       char c;
     696       24096 :       *_in >> abaqus_elem_id >> c;
     697             : 
     698             :       // Add an element of the appropriate type to the Mesh, with the
     699             :       // abaqus element ID.
     700       26104 :       std::unique_ptr<Elem> new_elem = Elem::build(elem_type);
     701       24096 :       new_elem->set_id() = abaqus_elem_id-1;
     702             : 
     703       28112 :       Elem * elem = the_mesh.add_elem(std::move(new_elem));
     704             : 
     705             :       // The count of the total number of IDs read for the current element.
     706        2008 :       unsigned id_count=0;
     707             : 
     708             :       // Continue reading line-by-line until we have read enough nodes for this element
     709       48192 :       while (id_count < n_nodes_per_elem)
     710             :         {
     711             :           // Read entire line (up to carriage return) of comma-separated values
     712        4016 :           std::string csv_line;
     713       24096 :           std::getline(*_in, csv_line);
     714             : 
     715             :           // Create a stream object out of the current line
     716       28112 :           std::stringstream line_stream(csv_line);
     717             : 
     718             :           // Process the comma-separated values
     719        4016 :           std::string cell;
     720      216864 :           while (std::getline(line_stream, cell, ','))
     721             :             {
     722             :               dof_id_type abaqus_global_node_id;
     723      192768 :               bool success = string_to_num(cell, abaqus_global_node_id);
     724             : 
     725      192768 :               if (success)
     726             :                 {
     727             :                   // Map the id'th element ID (Abaqus 1-based numbering) to LibMesh numbering
     728      192768 :                   libmesh_error_msg_if(abaqus_global_node_id < 1,
     729             :                                        "Invalid Abaqus node ID found");
     730      192768 :                   const dof_id_type libmesh_global_node_id = abaqus_global_node_id-1;
     731             : 
     732             :                   // Grab the node pointer from the mesh for this ID
     733      192768 :                   Node * node = the_mesh.node_ptr(libmesh_global_node_id);
     734             : 
     735             :                   // If node_ptr() returns nullptr, it may mean we have not yet read the
     736             :                   // *Nodes section, though I assumed that always came before the *Elements section...
     737      192768 :                   libmesh_error_msg_if
     738             :                     (node == nullptr,
     739             :                      "Error!  Mesh::node_ptr() returned nullptr.  Either no node exists with ID "
     740             :                      << libmesh_global_node_id
     741             :                      << " or perhaps this input file has *Elements defined before *Nodes?");
     742             : 
     743             :                   // Note: id_count is the zero-based abaqus (elem local) node index.  We therefore map
     744             :                   // it to a libmesh elem local node index using the element definition map
     745             :                   unsigned libmesh_elem_local_node_id =
     746      192768 :                     eledef.abaqus_zero_based_node_id_to_libmesh_node_id[id_count];
     747             : 
     748             :                   // Set this node pointer within the element.
     749      192768 :                   elem->set_node(libmesh_elem_local_node_id, node);
     750             : 
     751             :                   // Increment the count of IDs read for this element
     752      192768 :                   id_count++;
     753             :                 } // end if (success)
     754             :             } // end while getline(',')
     755       20080 :         } // end while (id_count)
     756             : 
     757             :       // Ensure that we read *exactly* as many nodes as we were expecting to, no more.
     758       24096 :       libmesh_error_msg_if
     759             :         (id_count != n_nodes_per_elem,
     760             :          "Error: Needed to read "
     761             :          << n_nodes_per_elem
     762             :          << " nodes, but read "
     763             :          << id_count
     764             :          << " instead!");
     765             : 
     766             :       // If we are recording Elset IDs, add this element to the correct set for later processing.
     767             :       // Make sure to add it with the Abaqus ID, not the libmesh one!
     768       24096 :       if (elset_name != "")
     769           0 :         _elemset_ids[elset_name].push_back(abaqus_elem_id);
     770       20080 :     } // end while (peek)
     771          24 : }
     772             : 
     773             : 
     774             : 
     775             : 
     776         348 : std::string AbaqusIO::parse_label(std::string line, std::string label_name) const
     777             : {
     778             :   // Handle files which have weird line endings from e.g. windows.
     779             :   // You can check what kind of line endings you have with 'cat -vet'.
     780             :   // For example, some files may have two kinds of line endings like:
     781             :   //
     782             :   // 4997,^I496,^I532,^I487,^I948^M$
     783             :   //
     784             :   // and we don't want to deal with this when extracting a label, so
     785             :   // just remove all the space characters, which should include all
     786             :   // kinds of remaining newlines.  (I don't think Abaqus allows
     787             :   // whitespace in label names.)
     788         348 :   strip_ws(line);
     789             : 
     790             :   // Do all string comparisons in upper-case
     791             :   std::string
     792         348 :     upper_line(line),
     793         348 :     upper_label_name(label_name);
     794          29 :   std::transform(upper_line.begin(), upper_line.end(), upper_line.begin(), ::toupper);
     795          29 :   std::transform(upper_label_name.begin(), upper_label_name.end(), upper_label_name.begin(), ::toupper);
     796             : 
     797             :   // Get index of start of "label="
     798         348 :   size_t label_index = upper_line.find(upper_label_name + "=");
     799             : 
     800         348 :   if (label_index != std::string::npos)
     801             :     {
     802             :       // Location of the first comma following "label="
     803         275 :       size_t comma_index = upper_line.find(",", label_index);
     804             : 
     805             :       // Construct iterators from which to build the sub-string.
     806             :       // Note: The +1 while initializing beg is to skip past the "=" which follows the label name
     807             :       std::string::iterator
     808          50 :         beg = line.begin() + label_name.size() + 1 + label_index,
     809         300 :         end = (comma_index == std::string::npos) ? line.end() : line.begin() + comma_index;
     810             : 
     811         275 :       return std::string(beg, end);
     812             :     }
     813             : 
     814             :   // The label index was not found, return the empty string
     815          48 :   return std::string("");
     816             : }
     817             : 
     818             : 
     819             : 
     820             : 
     821         300 : bool AbaqusIO::detect_generated_set(std::string upper) const
     822             : {
     823             :   // Avoid issues with weird line endings, spaces before commas, etc.
     824         300 :   strip_ws(upper);
     825             : 
     826             :   // Check each comma-separated value in "upper" to see if it is the generate flag.
     827          50 :   std::string cell;
     828         350 :   std::stringstream line_stream(upper);
     829        1008 :   while (std::getline(line_stream, cell, ','))
     830         876 :     if (cell == "GENERATE")
     831          14 :       return true;
     832             : 
     833          11 :   return false;
     834         250 : }
     835             : 
     836             : 
     837             : 
     838         132 : void AbaqusIO::read_ids(std::string set_name, container_t & container)
     839             : {
     840             :   // Grab a reference to a vector that will hold all the IDs
     841         132 :   std::vector<dof_id_type> & id_storage = container[set_name];
     842             : 
     843             :   // Read until the start of another section is detected, or EOF is encountered
     844        1176 :   while (_in->peek() != '*' && _in->peek() != EOF)
     845             :     {
     846             :       // Read entire comma-separated line into a string
     847         174 :       std::string csv_line;
     848        1044 :       std::getline(*_in, csv_line);
     849             : 
     850             :       // On that line, use std::getline again to parse each
     851             :       // comma-separated entry.
     852         174 :       std::string cell;
     853        1218 :       std::stringstream line_stream(csv_line);
     854       17208 :       while (std::getline(line_stream, cell, ','))
     855             :         {
     856             :           dof_id_type id;
     857       16164 :           bool success = string_to_num(cell, id);
     858             : 
     859             :           // Note that lists of comma-separated values in abaqus also
     860             :           // *end* with a comma, so the last call to getline on a given
     861             :           // line will get an empty string, which we must detect.
     862       16164 :           if (success)
     863       16164 :             id_storage.push_back(id);
     864             :         }
     865         870 :     }
     866         132 : }
     867             : 
     868             : 
     869             : 
     870             : 
     871         168 : void AbaqusIO::generate_ids(std::string set_name, container_t & container)
     872             : {
     873             :   // Grab a reference to a vector that will hold all the IDs
     874         168 :   std::vector<dof_id_type> & id_storage = container[set_name];
     875             : 
     876             :   // Read until the start of another section is detected, or EOF is
     877             :   // encountered.  "generate" sections seem to only have one line,
     878             :   // although I suppose it's possible they could have more.
     879         336 :   while (_in->peek() != '*' && _in->peek() != EOF)
     880             :     {
     881             :       // Read entire comma-separated line into a string
     882          28 :       std::string csv_line;
     883         168 :       std::getline(*_in, csv_line);
     884             : 
     885             :       // Remove all whitespaces from csv_line.
     886         168 :       strip_ws(csv_line);
     887             : 
     888             :       // Create a new stringstream object from the string, and stream
     889             :       // in the comma-separated values.
     890             :       char c;
     891             :       dof_id_type start, end, stride;
     892         196 :       std::stringstream line_stream(csv_line);
     893         322 :       line_stream >> start >> c >> end >> c >> stride;
     894             : 
     895             :       // Generate entries in the id_storage.  Note: each element can
     896             :       // only belong to a single Elset (since this corresponds to the
     897             :       // subdomain_id) so if an element appears in multiple Elsets,
     898             :       // the "last" one (alphabetically, based on set name) in the
     899             :       // _elemset_ids map will "win".
     900       29160 :       for (dof_id_type current = start; current <= end; current += stride)
     901       28992 :         id_storage.push_back(current);
     902         140 :     }
     903         168 : }
     904             : 
     905             : 
     906             : 
     907             : 
     908           0 : void AbaqusIO::read_sideset(const std::string & sideset_name,
     909             :                             const std::string & sideset_type,
     910             :                             sideset_container_t & container)
     911             : {
     912             :   // Grab a reference to a vector that will hold all the IDs
     913           0 :   std::vector<std::pair<dof_id_type, unsigned>> & id_storage = container[sideset_name];
     914             : 
     915             :   // Variables for storing values read in from file
     916           0 :   unsigned side_id=0;
     917             :   char c;
     918           0 :   std::string elem_id_or_set, dummy;
     919             : 
     920             :   // Read until the start of another section is detected, or EOF is encountered
     921           0 :   while (_in->peek() != '*' && _in->peek() != EOF)
     922             :     {
     923             :       // Read first string up to and including the comma, which is discarded.
     924           0 :       std::getline(*_in, elem_id_or_set, ',');
     925             : 
     926             :       // Strip any leading or trailing trailing whitespace from this
     927             :       // string, since some Abaqus files may have this.
     928           0 :       strip_ws(elem_id_or_set);
     929             : 
     930             :       // Handle sidesets of type "NODE"
     931           0 :       if (sideset_type == "NODE")
     932             :         {
     933             :           // Create a sideset from a nodeset. This is not currently
     934             :           // supported, so print a warning and keep going.
     935           0 :           if (_nodeset_ids.count(elem_id_or_set))
     936           0 :             libMesh::out << "Warning: skipped creating a sideset from a "
     937           0 :                          << "nodeset, this is not yet supported."
     938           0 :                          << std::endl;
     939             :         }
     940             : 
     941             :       // Otherwise, we assume it's a sideset of the form: "391, S2" or "Elset_1, S3".
     942             :       // If it's not one of these forms, we'll throw an error instead
     943             :       // of letting the stream get into a bad state.
     944             :       else // sideset_type != "NODE"
     945             :         {
     946             :           // Read the character "S", followed by the side id. Note: the >> operator
     947             :           // eats whitespace until it reaches a valid character, so this should work
     948             :           // whether or not there is a space after the previous comma.
     949           0 :           *_in >> c >> side_id;
     950             : 
     951             :           // Try to convert first string to an integer.
     952             :           dof_id_type elem_id;
     953           0 :           bool success = string_to_num(elem_id_or_set, elem_id);
     954             : 
     955           0 :           if (success)
     956             :             {
     957             :               // if the side set is of the form of "391, S2"
     958           0 :               id_storage.emplace_back(elem_id, side_id);
     959             :             }
     960             : 
     961           0 :           if (!success)
     962             :             {
     963             :               // if the side set is of the form of "Elset_1, S3"
     964           0 :               const auto & vec = libmesh_map_find(_elemset_ids, elem_id_or_set);
     965           0 :               for (const auto & elem_id_in_elset : vec)
     966           0 :                 id_storage.emplace_back(elem_id_in_elset, side_id);
     967             :             }
     968             :         }
     969             : 
     970             :       // Successful or not, we extract the remaining characters on the
     971             :       // line, including the newline, to (hopefully) go to the next section.
     972           0 :       std::getline(*_in, dummy);
     973             :     } // while
     974           0 : }
     975             : 
     976             : 
     977             : 
     978             : 
     979          24 : void AbaqusIO::assign_subdomain_ids()
     980             : {
     981             :   // Get a reference to the mesh we are reading
     982          24 :   MeshBase & the_mesh = MeshInput<MeshBase>::mesh();
     983             : 
     984             :   // The number of elemsets we've found while reading
     985           2 :   std::size_t n_elemsets = _elemset_ids.size();
     986             : 
     987             :   // Fill in a temporary map with (ElemType, index) pairs based on the _elem_types set.  This
     988             :   // will allow us to easily look up this index in the loop below.
     989           4 :   std::map<ElemType, unsigned> elem_types_map;
     990             :   {
     991           2 :     unsigned ctr=0;
     992          48 :     for (const auto & type : _elem_types)
     993          24 :       elem_types_map[type] = ctr++;
     994             :   }
     995             : 
     996             :   // Loop over each Elemset and assign subdomain IDs to Mesh elements
     997             :   {
     998             :     // The maximum element dimension seen while reading the Mesh
     999          24 :     unsigned char max_dim = this->max_elem_dimension_seen();
    1000             : 
    1001             :     // The elemset_id counter assigns a logical numbering to the _elemset_ids keys
    1002           2 :     container_t::iterator it = _elemset_ids.begin(), end = _elemset_ids.end();
    1003         240 :     for (unsigned elemset_id=0; it != end; ++it, ++elemset_id)
    1004             :       {
    1005             :         // Grab a reference to the vector of IDs
    1006          18 :         std::vector<dof_id_type> & id_vector = it->second;
    1007             : 
    1008             :         // Loop over this vector
    1009       18168 :         for (const auto & id : id_vector)
    1010             :           {
    1011             :             // Map the id'th element ID (Abaqus 1-based numbering) to LibMesh numbering
    1012       17952 :             libmesh_error_msg_if(id < 1,
    1013             :                                  "Invalid Abaqus element ID found");
    1014       17952 :             const dof_id_type libmesh_elem_id = id-1;
    1015             : 
    1016             :             // Get reference to that element
    1017       17952 :             Elem & elem = the_mesh.elem_ref(libmesh_elem_id);
    1018             : 
    1019             :             // We won't assign subdomain ids to lower-dimensional
    1020             :             // elements, as they are assumed to represent boundary
    1021             :             // conditions.  Since lower-dimensional elements can
    1022             :             // appear in multiple sidesets, it doesn't make sense to
    1023             :             // assign them a single subdomain id... only the last one
    1024             :             // assigned would actually "stick".
    1025       17952 :             if (elem.dim() < max_dim)
    1026           0 :               break;
    1027             : 
    1028             :             // Compute the proper subdomain ID, based on the formula in the
    1029             :             // documentation for this function.
    1030             :             subdomain_id_type computed_id = cast_int<subdomain_id_type>
    1031       17952 :               (elemset_id + (elem_types_map[elem.type()] * n_elemsets));
    1032             : 
    1033             :             // Assign this ID to the element in question
    1034       17952 :             elem.subdomain_id() = computed_id;
    1035             : 
    1036             :             // We will also assign a unique name to the computed_id,
    1037             :             // which is created by appending the geometric element
    1038             :             // name to the elset name provided by the user in the
    1039             :             // Abaqus file.
    1040       37400 :             std::string computed_name = it->first + "_" + Utility::enum_to_string(elem.type());
    1041       17952 :             the_mesh.subdomain_name(computed_id) = computed_name;
    1042             :           }
    1043             :       }
    1044             :   }
    1045          24 : }
    1046             : 
    1047             : 
    1048             : 
    1049             : 
    1050          24 : void AbaqusIO::assign_boundary_node_ids()
    1051             : {
    1052             :   // Get a reference to the mesh we are reading
    1053          24 :   MeshBase & the_mesh = MeshInput<MeshBase>::mesh();
    1054             : 
    1055             :   // Iterate over the container of nodesets
    1056           2 :   container_t::iterator it = _nodeset_ids.begin(), end = _nodeset_ids.end();
    1057         108 :   for (boundary_id_type current_id=0; it != end; ++it, ++current_id)
    1058             :     {
    1059             :       // Associate current_id with the name we determined earlier
    1060          91 :       the_mesh.get_boundary_info().nodeset_name(current_id) = it->first;
    1061             : 
    1062             :       // Get a reference to the current vector of nodeset ID values
    1063           7 :       std::vector<dof_id_type> & nodeset_ids = it->second;
    1064             : 
    1065       27288 :       for (const auto & id : nodeset_ids)
    1066             :         {
    1067             :           // Map the id'th element ID (Abaqus 1-based numbering) to LibMesh numbering
    1068       27204 :           libmesh_error_msg_if(id < 1,
    1069             :                                "Invalid Abaqus node ID found");
    1070       27204 :           const dof_id_type libmesh_global_node_id = id-1;
    1071             : 
    1072             :           // Get node pointer from the mesh
    1073       27204 :           Node * node = the_mesh.node_ptr(libmesh_global_node_id);
    1074             : 
    1075       27204 :           libmesh_error_msg_if(node == nullptr,
    1076             :                                "Error! Mesh::node_ptr() returned nullptr!");
    1077             : 
    1078             :           // Add this node with the current_id (which is determined by the
    1079             :           // alphabetical ordering of the map) to the BoundaryInfo object
    1080       27204 :           the_mesh.get_boundary_info().add_node(node, current_id);
    1081             :         }
    1082             :     }
    1083          24 : }
    1084             : 
    1085             : 
    1086             : 
    1087             : 
    1088          24 : void AbaqusIO::assign_sideset_ids()
    1089             : {
    1090             :   // Get a reference to the mesh we are reading
    1091          24 :   MeshBase & the_mesh = MeshInput<MeshBase>::mesh();
    1092             : 
    1093             :   // initialize the eletypes map (eletypes is a file-global variable)
    1094          24 :   init_eletypes();
    1095             : 
    1096             :   // Iterate over the container of sidesets
    1097             :   {
    1098           2 :     sideset_container_t::iterator it = _sideset_ids.begin(), end = _sideset_ids.end();
    1099          24 :     for (boundary_id_type current_id=0; it != end; ++it, ++current_id)
    1100             :       {
    1101             :         // Associate current_id with the name we determined earlier
    1102           0 :         the_mesh.get_boundary_info().sideset_name(current_id) = it->first;
    1103             : 
    1104             :         // Get a reference to the current vector of nodeset ID values
    1105           0 :         std::vector<std::pair<dof_id_type,unsigned>> & sideset_ids = it->second;
    1106             : 
    1107           0 :         for (const auto & [abaqus_elem_id, abaqus_side_number] : sideset_ids)
    1108             :           {
    1109             :             // Map the id'th element ID (Abaqus 1-based numbering) to LibMesh numbering
    1110           0 :             libmesh_error_msg_if(abaqus_elem_id < 1,
    1111             :                                  "Invalid Abaqus element ID found");
    1112           0 :             const dof_id_type libmesh_elem_id = abaqus_elem_id-1;
    1113             : 
    1114             :             // Get a reference to that element
    1115           0 :             Elem & elem = the_mesh.elem_ref(libmesh_elem_id);
    1116             : 
    1117             :             // Grab a reference to the element definition for this element type
    1118           0 :             const ElementDefinition & eledef = eletypes[elem.type()];
    1119             : 
    1120             :             // If the element definition was not found, the call above would have
    1121             :             // created one with an uninitialized struct.  Check for that here...
    1122           0 :             libmesh_error_msg_if(eledef.abaqus_zero_based_side_id_to_libmesh_side_id.size() == 0,
    1123             :                                  "No Abaqus->LibMesh mapping information for ElemType "
    1124             :                                  << Utility::enum_to_string(elem.type()) << "!");
    1125             : 
    1126             :             // Add this node with the current_id (which is determined by the
    1127             :             // alphabetical ordering of the map).  Side numbers in Abaqus are 1-based,
    1128             :             // so we subtract 1 here before passing the abaqus side number to the
    1129             :             // mapping array
    1130           0 :             the_mesh.get_boundary_info().add_side
    1131           0 :               (&elem,
    1132           0 :                eledef.abaqus_zero_based_side_id_to_libmesh_side_id[abaqus_side_number-1],
    1133             :                current_id);
    1134             :           }
    1135             :       }
    1136             :   }
    1137             : 
    1138             : 
    1139             :   // Some elsets (if they contain lower-dimensional elements) also
    1140             :   // define sidesets.  So loop over them and build a searchable data
    1141             :   // structure we can use to assign sidesets.
    1142             :   {
    1143          24 :     unsigned char max_dim = this->max_elem_dimension_seen();
    1144             : 
    1145             :     // multimap from lower-dimensional-element-hash-key to
    1146             :     // pair(lower-dimensional-element, boundary_id).  The
    1147             :     // lower-dimensional element is used to verify the results of the
    1148             :     // hash table search.  The boundary_id will be used to set a
    1149             :     // boundary ID on a higher-dimensional element.  We use a multimap
    1150             :     // because the lower-dimensional elements can belong to more than
    1151             :     // 1 sideset, and multiple lower-dimensional elements can hash to
    1152             :     // the same value, but this is very rare.
    1153           4 :     std::unordered_multimap<dof_id_type, std::pair<Elem *, boundary_id_type>> provide_bcs;
    1154             : 
    1155             :     // The elemset_id counter assigns a logical numbering to the
    1156             :     // _elemset_ids keys.  We are going to use these ids as boundary
    1157             :     // ids, so elemset_id is of type boundary_id_type.
    1158           2 :     container_t::iterator it = _elemset_ids.begin(), end = _elemset_ids.end();
    1159         240 :     for (boundary_id_type elemset_id=0; it != end; ++it, ++elemset_id)
    1160             :       {
    1161             :         // Grab a reference to the vector of IDs
    1162          18 :         std::vector<dof_id_type> & id_vector = it->second;
    1163             : 
    1164             :         // Loop over this vector
    1165         216 :         for (const auto & id : id_vector)
    1166             :           {
    1167             :             // Map the id'th element ID (Abaqus 1-based numbering) to LibMesh numbering
    1168         216 :             libmesh_error_msg_if(id < 1,
    1169             :                                  "Invalid Abaqus element ID found");
    1170         216 :             const dof_id_type libmesh_elem_id = id-1;
    1171             : 
    1172             :             // Get a reference to that element
    1173         216 :             Elem & elem = the_mesh.elem_ref(libmesh_elem_id);
    1174             : 
    1175             :             // If the element dimension is equal to the maximum
    1176             :             // dimension seen, we can break out of this for loop --
    1177             :             // this elset does not contain sideset information.
    1178         216 :             if (elem.dim() == max_dim)
    1179          18 :               break;
    1180             : 
    1181             :             // If the element dimension is zero, this elset contains
    1182             :             // NodeElems which AFAIK are not used for sidesets, they
    1183             :             // are only used to define point masses.
    1184           0 :             if (elem.dim() == 0)
    1185           0 :               break;
    1186             : 
    1187             :             // We can only handle elements that are *exactly*
    1188             :             // one dimension lower than the max element
    1189             :             // dimension.  Not sure if "edge" BCs in 3D
    1190             :             // actually make sense/are required...
    1191           0 :             libmesh_error_msg_if(elem.dim()+1 != max_dim,
    1192             :                                  "ERROR: Expected boundary element of dimension " << max_dim-1 << " but got " << elem.dim());
    1193             : 
    1194             :             // Insert the current (key, pair(elem,id)) into the multimap.
    1195           0 :             provide_bcs.emplace(elem.key(), std::make_pair(&elem, elemset_id));
    1196             : 
    1197             :             // Associate the name of this sideset with the ID we've
    1198             :             // chosen.  It's not necessary to do this for every
    1199             :             // element in the set, but it's convenient to do it here
    1200             :             // since we have all the necessary information...
    1201           0 :             the_mesh.get_boundary_info().sideset_name(elemset_id) = it->first;
    1202             :           }
    1203             :       }
    1204             : 
    1205             :     // Loop over elements and try to assign boundary information
    1206       44222 :     for (auto & elem : the_mesh.active_element_ptr_range())
    1207       24096 :       if (elem->dim() == max_dim)
    1208      170680 :         for (auto sn : elem->side_index_range())
    1209             :           {
    1210             :             // This is a max-dimension element that may require BCs.
    1211             :             // For each of its sides, including internal sides, we'll
    1212             :             // see if a lower-dimensional element provides boundary
    1213             :             // information for it.  Note that we have not yet called
    1214             :             // find_neighbors(), so we can't use elem->neighbor(sn) in
    1215             :             // this algorithm...
    1216      144576 :             auto bounds = provide_bcs.equal_range (elem->key(sn));
    1217             : 
    1218             :             // Add boundary information for each side in the range.
    1219      144576 :             for (const auto & pr : as_range(bounds))
    1220             :               {
    1221             :                 // We'll need to compare the lower dimensional element against the current side.
    1222           0 :                 std::unique_ptr<Elem> side (elem->build_side_ptr(sn));
    1223             : 
    1224             :                 // Extract the relevant data. We don't need the key for anything.
    1225           0 :                 Elem * lower_dim_elem = pr.second.first;
    1226           0 :                 boundary_id_type bid = pr.second.second;
    1227             : 
    1228             :                 // This was a hash, so it might not be perfect.  Let's verify...
    1229           0 :                 if (*lower_dim_elem == *side)
    1230           0 :                   the_mesh.get_boundary_info().add_side(elem, sn, bid);
    1231           0 :               }
    1232          20 :           }
    1233             :   }
    1234          24 : }
    1235             : 
    1236             : 
    1237             : 
    1238         348 : void AbaqusIO::process_and_discard_comments()
    1239             : {
    1240          58 :   std::string dummy;
    1241             :   while (true)
    1242             :     {
    1243             :       // We assume we are at the beginning of a line that may be
    1244             :       // comments or may be data.  We need to only discard the line if
    1245             :       // it begins with **, but we must avoid calling std::getline()
    1246             :       // since there's no way to put that back.
    1247         348 :       if (_in->peek() == '*')
    1248             :         {
    1249             :           // The first character was a star, so actually read it from the stream.
    1250           0 :           _in->get();
    1251             : 
    1252             :           // Peek at the next character...
    1253           0 :           if (_in->peek() == '*')
    1254             :             {
    1255             :               // OK, second character was star also, by definition this
    1256             :               // line must be a comment!  Read the rest of the line and discard!
    1257           0 :               std::getline(*_in, dummy);
    1258             :             }
    1259             :           else
    1260             :             {
    1261             :               // The second character was _not_ a star, so put back the first star
    1262             :               // we pulled out so that the line can be parsed correctly by somebody
    1263             :               // else!
    1264           0 :               _in->unget();
    1265             : 
    1266             :               // Finally, break out of the while loop, we are done parsing comments
    1267           0 :               break;
    1268             :             }
    1269             :         }
    1270             :       else
    1271             :         {
    1272             :           // First character was not *, so this line must be data! Break out of the
    1273             :           // while loop!
    1274          29 :           break;
    1275             :         }
    1276             :     }
    1277         348 : }
    1278             : 
    1279             : 
    1280             : 
    1281          96 : unsigned char AbaqusIO::max_elem_dimension_seen ()
    1282             : {
    1283           8 :   unsigned char max_dim = 0;
    1284             : 
    1285             :   unsigned char elem_dimensions_size = cast_int<unsigned char>
    1286           8 :     (elems_of_dimension.size());
    1287             :   // The elems_of_dimension array is 1-based in the UNV reader
    1288         384 :   for (unsigned char i=1; i<elem_dimensions_size; ++i)
    1289         312 :     if (elems_of_dimension[i])
    1290           8 :       max_dim = i;
    1291             : 
    1292          96 :   return max_dim;
    1293             : }
    1294             : 
    1295      208932 : bool AbaqusIO::string_to_num(const std::string &input, dof_id_type &output) const
    1296             : {
    1297             :   char *endptr;
    1298      208932 :   output = cast_int<dof_id_type>(std::strtol(input.c_str(), &endptr, /*base=*/10));
    1299             : 
    1300      208932 :   return (output != 0 || endptr != input.c_str());
    1301             : }
    1302             : 
    1303       34308 : void AbaqusIO::strip_ws(std::string &line) const
    1304             : {
    1305       28590 :   line.erase(std::remove_if(line.begin(), line.end(),
    1306      138761 :                             [](unsigned char const c)
    1307     1529029 :                             { return std::isspace(c); }),
    1308        8577 :              line.end());
    1309       34308 : }
    1310             : 
    1311             : } // namespace

Generated by: LCOV version 1.14