$extrastylesheet
abaqus_io.C
Go to the documentation of this file.
00001 // The libMesh Finite Element Library.
00002 // Copyright (C) 2002-2014 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
00003 
00004 // This library is free software; you can redistribute it and/or
00005 // modify it under the terms of the GNU Lesser General Public
00006 // License as published by the Free Software Foundation; either
00007 // version 2.1 of the License, or (at your option) any later version.
00008 
00009 // This library is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012 // Lesser General Public License for more details.
00013 
00014 // You should have received a copy of the GNU Lesser General Public
00015 // License along with this library; if not, write to the Free Software
00016 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017 
00018 // C++ includes
00019 #include <string>
00020 #include <cstdlib> // std::strtol
00021 #include <sstream>
00022 #include <ctype.h> // isspace
00023 
00024 // Local includes
00025 #include "libmesh/abaqus_io.h"
00026 #include "libmesh/point.h"
00027 #include "libmesh/elem.h"
00028 #include "libmesh/string_to_enum.h"
00029 #include "libmesh/boundary_info.h"
00030 #include "libmesh/utility.h"
00031 
00032 // Anonymous namespace to hold mapping Data for Abaqus/libMesh element types
00033 namespace
00034 {
00035 using namespace libMesh;
00036 
00041 struct ElementDefinition
00042 {
00043   // Maps (zero-based!) Abaqus local node numbers to libmesh local node numbers
00044   std::vector<unsigned> abaqus_zero_based_node_id_to_libmesh_node_id;
00045 
00046   // Maps (zero-based!) Abaqus side numbers to libmesh side numbers
00047   std::vector<unsigned short> abaqus_zero_based_side_id_to_libmesh_side_id;
00048 };
00049 
00053 std::map<ElemType, ElementDefinition> eletypes;
00054 
00058 void add_eletype_entry(ElemType libmesh_elem_type,
00059                        const unsigned* node_map,
00060                        unsigned node_map_size,
00061                        const unsigned short* side_map,
00062                        unsigned side_map_size)
00063 {
00064   // If map entry does not exist, this will create it
00065   ElementDefinition& map_entry = eletypes[libmesh_elem_type];
00066 
00067 
00068   // Use the "swap trick" from Scott Meyer's "Effective STL" to swap
00069   // an unnamed temporary vector into the map_entry's vector.  Note:
00070   // the vector(iter, iter) constructor is used.
00071   std::vector<unsigned>
00072     (node_map, node_map+node_map_size).swap
00073     (map_entry.abaqus_zero_based_node_id_to_libmesh_node_id);
00074 
00075   std::vector<unsigned short>
00076     (side_map, side_map+side_map_size).swap
00077     (map_entry.abaqus_zero_based_side_id_to_libmesh_side_id);
00078 }
00079 
00080 
00084 void init_eletypes ()
00085 {
00086   // This should happen only once.  The first time this method is
00087   // called the eletypes data struture will be empty, and we will
00088   // fill it.  Any subsequent calls will find an initialized
00089   // eletypes map and will do nothing.
00090   if (eletypes.empty())
00091     {
00092       {
00093         // EDGE2
00094         const unsigned int   node_map[] = {0,1}; // identity
00095         const unsigned short side_map[] = {0,1}; // identity
00096         add_eletype_entry(EDGE2, node_map, 2, side_map, 2);
00097       }
00098 
00099       {
00100         // TRI3
00101         const unsigned int   node_map[] = {0,1,2}; // identity
00102         const unsigned short side_map[] = {0,1,2}; // identity
00103         add_eletype_entry(TRI3, node_map, 3, side_map, 3);
00104       }
00105 
00106       {
00107         // QUAD4
00108         const unsigned int   node_map[] = {0,1,2,3}; // identity
00109         const unsigned short side_map[] = {0,1,2,3}; // identity
00110         add_eletype_entry(QUAD4, node_map, 4, side_map, 4);
00111       }
00112 
00113       {
00114         // TET4
00115         const unsigned int   node_map[] = {0,1,2,3}; // identity
00116         const unsigned short side_map[] = {0,1,2,3}; // identity
00117         add_eletype_entry(TET4, node_map, 4, side_map, 4);
00118       }
00119 
00120       {
00121         // TET10
00122         const unsigned int   node_map[] = {0,1,2,3,4,5,6,7,8,9}; // identity
00123         const unsigned short side_map[] = {0,1,2,3};             // identity
00124         add_eletype_entry(TET10, node_map, 10, side_map, 4);
00125       }
00126 
00127       {
00128         // HEX8
00129         const unsigned int   node_map[] = {0,1,2,3,4,5,6,7}; // identity
00130         const unsigned short side_map[] = {0,5,1,2,3,4};     // inverse = 0,2,3,4,5,1
00131         add_eletype_entry(HEX8, node_map, 8, side_map, 6);
00132       }
00133 
00134       {
00135         // HEX20
00136         const unsigned int   node_map[] = // map is its own inverse
00137           {0,1,2,3,4,5,6,7,8,9,10,11,16,17,18,19,12,13,14,15};
00138         const unsigned short side_map[] = // inverse = 0,2,3,4,5,1
00139           {0,5,1,2,3,4};
00140         add_eletype_entry(HEX20, node_map, 20, side_map, 6);
00141       }
00142 
00143       {
00144         // HEX27
00145         const unsigned int   node_map[] = // inverse = ...,21,23,24,25,26,22,20
00146           {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};
00147         const unsigned short side_map[] = // inverse = 0,2,3,4,5,1
00148           {0,5,1,2,3,4};
00149         add_eletype_entry(HEX27, node_map, 27, side_map, 6);
00150       }
00151 
00152       {
00153         // PRISM6
00154         const unsigned int   node_map[] = {0,1,2,3,4,5}; // identity
00155         const unsigned short side_map[] = {0,4,1,2,3};   // inverse = 0,2,3,4,1
00156         add_eletype_entry(PRISM6, node_map, 6, side_map, 5);
00157       }
00158 
00159       {
00160         // PRISM15
00161         const unsigned int   node_map[] = // map is its own inverse
00162           {0,1,2,3,4,5,6,7,8,12,13,14,9,10,11};
00163         const unsigned short side_map[] = // inverse = 0,2,3,4,1
00164           {0,4,1,2,3};
00165         add_eletype_entry(PRISM15, node_map, 15, side_map, 5);
00166       }
00167 
00168       {
00169         // PRISM18
00170         const unsigned int   node_map[] = // map is its own inverse
00171           {0,1,2,3,4,5,6,7,8,12,13,14,9,10,11,15,16,17};
00172         const unsigned short side_map[] = // inverse = 0,2,3,4,1
00173           {0,4,1,2,3};
00174         add_eletype_entry(PRISM18, node_map, 18, side_map, 5);
00175       }
00176     } // if (eletypes.empty())
00177 }
00178 } // anonymous namespace
00179 
00180 
00181 
00182 
00183 
00184 namespace libMesh
00185 {
00186 
00187 AbaqusIO::AbaqusIO (MeshBase& mesh_in) :
00188   MeshInput<MeshBase> (mesh_in),
00189   build_sidesets_from_nodesets(false),
00190   _already_seen_part(false)
00191 {
00192 }
00193 
00194 
00195 
00196 
00197 AbaqusIO::~AbaqusIO ()
00198 {
00199 }
00200 
00201 
00202 
00203 
00204 void AbaqusIO::read (const std::string& fname)
00205 {
00206   // Get a reference to the mesh we are reading
00207   MeshBase& the_mesh = MeshInput<MeshBase>::mesh();
00208 
00209   // Clear any existing mesh data
00210   the_mesh.clear();
00211 
00212   // Open stream for reading
00213   _in.open(fname.c_str());
00214   libmesh_assert(_in.good());
00215 
00216   // Initialize the elems_of_dimension array.  We will use this in a
00217   // "1-based" manner so that elems_of_dimension[d]==true means
00218   // elements of dimension d have been seen.
00219   elems_of_dimension.resize(4, false);
00220 
00221   // Read file line-by-line... this is based on a set of different
00222   // test input files.  I have not looked at the full input file
00223   // specs for Abaqus.
00224   std::string s;
00225   while (true)
00226     {
00227       // Try to read something.  This may set EOF!
00228       std::getline(_in, s);
00229 
00230       if (_in)
00231         {
00232           // Process s...
00233           //
00234           // There are many sections in Abaqus files, we read some
00235           // but others are just ignored...  Some sections may occur
00236           // more than once.  For example for a hybrid grid, you
00237           // will have multiple *Element sections...
00238 
00239           // Some Abaqus files use all upper-case for section names,
00240           // so we will just convert s to uppercase
00241           std::string upper(s);
00242           std::transform(upper.begin(), upper.end(), upper.begin(), ::toupper);
00243 
00244           // 0.) Look for the "*Part" section
00245           if (upper.find("*PART") == static_cast<std::string::size_type>(0))
00246             {
00247               if (_already_seen_part)
00248                 libmesh_error_msg("We currently don't support reading Abaqus files with multiple PART sections");
00249 
00250               _already_seen_part = true;
00251             }
00252 
00253           // 1.) Look for the "*Nodes" section
00254           if (upper.find("*NODE") == static_cast<std::string::size_type>(0))
00255             {
00256               // Some sections that begin with *NODE are actually
00257               // "*NODE OUTPUT" sections which we want to skip.  I
00258               // have only seen this with a single space, but it would
00259               // probably be more robust to remove whitespace before
00260               // making this check.
00261               if (upper.find("*NODE OUTPUT") == static_cast<std::string::size_type>(0))
00262                 continue;
00263 
00264               // Some *Node sections also specify an Nset name on the same line.
00265               // Look for one here.
00266               std::string nset_name = this->parse_label(s, "nset");
00267 
00268               // Process any lines of comments that may be present
00269               this->process_and_discard_comments();
00270 
00271               // Read a block of nodes
00272               this->read_nodes(nset_name);
00273             }
00274 
00275 
00276 
00277           // 2.) Look for the "*Element" section
00278           else if (upper.find("*ELEMENT,") == static_cast<std::string::size_type>(0))
00279             {
00280               // Some sections that begin with *ELEMENT are actually
00281               // "*ELEMENT OUTPUT" sections which we want to skip.  I
00282               // have only seen this with a single space, but it would
00283               // probably be more robust to remove whitespace before
00284               // making this check.
00285               if (upper.find("*ELEMENT OUTPUT") == static_cast<std::string::size_type>(0))
00286                 continue;
00287 
00288               // Some *Element sections also specify an Elset name on the same line.
00289               // Look for one here.
00290               std::string elset_name = this->parse_label(s, "elset");
00291 
00292               // Process any lines of comments that may be present
00293               this->process_and_discard_comments();
00294 
00295               // Read a block of elements
00296               this->read_elements(upper, elset_name);
00297             }
00298 
00299 
00300 
00301           // 3.) Look for a Nodeset section
00302           else if (upper.find("*NSET") == static_cast<std::string::size_type>(0))
00303             {
00304               std::string nset_name = this->parse_label(s, "nset");
00305 
00306               // I haven't seen an unnamed nset yet, but let's detect it
00307               // just in case...
00308               if (nset_name == "")
00309                 libmesh_error_msg("Unnamed nset encountered!");
00310 
00311               // Process any lines of comments that may be present
00312               this->process_and_discard_comments();
00313 
00314               // Read the IDs, storing them in _nodeset_ids
00315               this->read_ids(nset_name, _nodeset_ids);
00316             } // *Nodeset
00317 
00318 
00319 
00320           // 4.) Look for an Elset section
00321           else if (upper.find("*ELSET") == static_cast<std::string::size_type>(0))
00322             {
00323               std::string elset_name = this->parse_label(s, "elset");
00324 
00325               // I haven't seen an unnamed elset yet, but let's detect it
00326               // just in case...
00327               if (elset_name == "")
00328                 libmesh_error_msg("Unnamed elset encountered!");
00329 
00330               // Process any lines of comments that may be present
00331               this->process_and_discard_comments();
00332 
00333               // Read the IDs, storing them in _elemset_ids
00334               this->read_ids(elset_name, _elemset_ids);
00335             } // *Elset
00336 
00337 
00338 
00339           // 5.) Look for a Surface section.  Need to be a little
00340           // careful, since there are also "surface interaction"
00341           // sections we don't want to read here.
00342           else if (upper.find("*SURFACE,") == static_cast<std::string::size_type>(0))
00343             {
00344               // Get the name from the Name=Foo label.  This will be the map key.
00345               std::string sideset_name = this->parse_label(s, "name");
00346 
00347               // Process any lines of comments that may be present
00348               this->process_and_discard_comments();
00349 
00350               // Read the sideset IDs
00351               this->read_sideset(sideset_name, _sideset_ids);
00352             }
00353 
00354           continue;
00355         } // if (_in)
00356 
00357       // If !file, check to see if EOF was set.  If so, break out
00358       // of while loop.
00359       if (_in.eof())
00360         break;
00361 
00362       // If !in and !in.eof(), stream is in a bad state!
00363       libmesh_error_msg("Stream is bad! Perhaps the file: " << fname << " does not exist?");
00364     } // while
00365 
00366   // Set the Mesh dimension based on the highest dimension element seen.
00367   the_mesh.set_mesh_dimension(this->max_elem_dimension_seen());
00368 
00369   // Set element IDs based on the element sets.
00370   this->assign_subdomain_ids();
00371 
00372   // Assign nodeset values to the BoundaryInfo object
00373   this->assign_boundary_node_ids();
00374 
00375   // Assign sideset values in the BoundaryInfo object
00376   this->assign_sideset_ids();
00377 
00378   // Abaqus files only contain nodesets by default.  To be useful in
00379   // applying most types of BCs in libmesh, we will definitely need
00380   // sidesets.  So we can call the new BoundaryInfo function which
00381   // generates sidesets from nodesets.
00382   if (build_sidesets_from_nodesets)
00383     the_mesh.get_boundary_info().build_side_list_from_node_list();
00384 
00385   // Delete lower-dimensional elements from the Mesh.  We assume these
00386   // were only used for setting BCs, and aren't part of the actual
00387   // Mesh.
00388   {
00389     unsigned char max_dim = this->max_elem_dimension_seen();
00390 
00391     MeshBase::element_iterator       el     = the_mesh.elements_begin();
00392     const MeshBase::element_iterator end_el = the_mesh.elements_end();
00393 
00394     for (; el != end_el; ++el)
00395       {
00396         Elem* elem = *el;
00397 
00398         if (elem->dim() < max_dim)
00399           the_mesh.delete_elem(elem);
00400       }
00401   }
00402 }
00403 
00404 
00405 
00406 
00407 
00408 
00409 
00410 void AbaqusIO::read_nodes(std::string nset_name)
00411 {
00412   // Get a reference to the mesh we are reading
00413   MeshBase& the_mesh = MeshInput<MeshBase>::mesh();
00414 
00415   // In the input files I have, Abaqus neither tells what
00416   // the mesh dimension is nor how many nodes it has...
00417   //
00418   // The node line format is:
00419   // id, x, y, z
00420   // and you do have to parse out the commas.
00421   // The z-coordinate will only be present for 3D meshes
00422 
00423   // Temporary variables for parsing lines of text
00424   char c;
00425   std::string dummy;
00426 
00427   // Defines the sequential node numbering used by libmesh.  Since
00428   // there can be multiple *NODE sections in an Abaqus file, we always
00429   // start our numbering with the number of nodes currently in the
00430   // Mesh.
00431   dof_id_type libmesh_node_id = the_mesh.n_nodes();
00432 
00433   // We need to duplicate some of the read_ids code if this *NODE
00434   // section also defines an NSET.  We'll set up the id_storage
00435   // pointer and push back IDs into this vector in the loop below...
00436   std::vector<dof_id_type>* id_storage = NULL;
00437   if (nset_name != "")
00438     id_storage = &(_nodeset_ids[nset_name]);
00439 
00440   // We will read nodes until the next line begins with *, since that will be the
00441   // next section.
00442   // TODO: Is Abaqus guaranteed to start the line with '*' or can there be leading white space?
00443   while (_in.peek() != '*' && _in.peek() != EOF)
00444     {
00445       // Re-Initialize variables to be read in from file
00446       dof_id_type abaqus_node_id=0;
00447       Real x=0, y=0, z=0;
00448 
00449       // Note: we assume *at least* 2D points here, should we worry about
00450       // trying to read 1D Abaqus meshes?
00451       _in >> abaqus_node_id >> c >> x >> c >> y;
00452 
00453       // Peek at the next character.  If it is a comma, then there is another
00454       // value to read!
00455       if (_in.peek() == ',')
00456         _in >> c >> z;
00457 
00458       // Read (and discard) the rest of the line, including the newline.
00459       // This is required so that our 'peek()' at the beginning of this
00460       // loop doesn't read the newline character, for example.
00461       std::getline(_in, dummy);
00462 
00463       // If this *NODE section defines an NSET, also store the abaqus ID in id_storage
00464       if (id_storage)
00465         id_storage->push_back(abaqus_node_id);
00466 
00467       // Set up the abaqus -> libmesh node mapping.  This is usually just the
00468       // "off-by-one" map.
00469       _abaqus_to_libmesh_node_mapping[abaqus_node_id] = libmesh_node_id;
00470 
00471       // Add the point to the mesh using libmesh's numbering,
00472       // and post-increment the libmesh node counter.
00473       the_mesh.add_point(Point(x,y,z), libmesh_node_id++);
00474     } // while
00475 }
00476 
00477 
00478 
00479 
00480 
00481 void AbaqusIO::read_elements(std::string upper, std::string elset_name)
00482 {
00483   // Get a reference to the mesh we are reading
00484   MeshBase& the_mesh = MeshInput<MeshBase>::mesh();
00485 
00486   // initialize the eletypes map (eletypes is a file-global variable)
00487   init_eletypes();
00488 
00489   ElemType elem_type = INVALID_ELEM;
00490   unsigned n_nodes_per_elem = 0;
00491 
00492   // Within s, we should have "type=XXXX"
00493   if (upper.find("T3D2") != std::string::npos)
00494     {
00495       elem_type = EDGE2;
00496       n_nodes_per_elem = 2;
00497       elems_of_dimension[1] = true;
00498     }
00499   else if (upper.find("CPE4") != std::string::npos ||
00500            upper.find("CPS4") != std::string::npos)
00501     {
00502       elem_type = QUAD4;
00503       n_nodes_per_elem = 4;
00504       elems_of_dimension[2] = true;
00505     }
00506   else if (upper.find("CPS3") != std::string::npos ||
00507            upper.find("S3R") != std::string::npos)
00508     {
00509       elem_type = TRI3;
00510       n_nodes_per_elem = 3;
00511       elems_of_dimension[2] = true;
00512     }
00513   else if (upper.find("C3D8") != std::string::npos)
00514     {
00515       elem_type = HEX8;
00516       n_nodes_per_elem = 8;
00517       elems_of_dimension[3] = true;
00518     }
00519   else if (upper.find("C3D4") != std::string::npos)
00520     {
00521       elem_type = TET4;
00522       n_nodes_per_elem = 4;
00523       elems_of_dimension[3] = true;
00524     }
00525   else if (upper.find("C3D20") != std::string::npos)
00526     {
00527       elem_type = HEX20;
00528       n_nodes_per_elem = 20;
00529       elems_of_dimension[3] = true;
00530     }
00531   else if (upper.find("C3D6") != std::string::npos)
00532     {
00533       elem_type = PRISM6;
00534       n_nodes_per_elem = 6;
00535       elems_of_dimension[3] = true;
00536     }
00537   else if (upper.find("C3D15") != std::string::npos)
00538     {
00539       elem_type = PRISM15;
00540       n_nodes_per_elem = 15;
00541       elems_of_dimension[3] = true;
00542     }
00543   else if (upper.find("C3D10") != std::string::npos)
00544     {
00545       elem_type = TET10;
00546       n_nodes_per_elem = 10;
00547       elems_of_dimension[3] = true;
00548     }
00549   else
00550     libmesh_error_msg("Unrecognized element type: " << upper);
00551 
00552   // Insert the elem type we detected into the set of all elem types for this mesh
00553   _elem_types.insert(elem_type);
00554 
00555   // Grab a reference to the element definition for this element type
00556   const ElementDefinition& eledef = eletypes[elem_type];
00557 
00558   // If the element definition was not found, the call above would have
00559   // created one with an uninitialized struct.  Check for that here...
00560   if (eledef.abaqus_zero_based_node_id_to_libmesh_node_id.size() == 0)
00561     libmesh_error_msg("No Abaqus->LibMesh mapping information for ElemType " \
00562                       << Utility::enum_to_string(elem_type)             \
00563                       << "!");
00564 
00565   // We will read elements until the next line begins with *, since that will be the
00566   // next section.
00567   while (_in.peek() != '*' && _in.peek() != EOF)
00568     {
00569       // Read the element ID, it is the first number on each line.  It is
00570       // followed by a comma, so read that also.  We will need this ID later
00571       // when we try to assign subdomain IDs
00572       dof_id_type abaqus_elem_id = 0;
00573       char c;
00574       _in >> abaqus_elem_id >> c;
00575 
00576       // Add an element of the appropriate type to the Mesh.
00577       Elem* elem = the_mesh.add_elem(Elem::build(elem_type).release());
00578 
00579       // Associate the ID returned from libmesh with the abaqus element ID
00580       //_libmesh_to_abaqus_elem_mapping[elem->id()] = abaqus_elem_id;
00581       _abaqus_to_libmesh_elem_mapping[abaqus_elem_id] = elem->id();
00582 
00583       // The count of the total number of IDs read for the current element.
00584       unsigned id_count=0;
00585 
00586       // Continue reading line-by-line until we have read enough nodes for this element
00587       while (id_count < n_nodes_per_elem)
00588         {
00589           // Read entire line (up to carriage return) of comma-separated values
00590           std::string csv_line;
00591           std::getline(_in, csv_line);
00592 
00593           // Create a stream object out of the current line
00594           std::stringstream line_stream(csv_line);
00595 
00596           // Process the comma-separated values
00597           std::string cell;
00598           while (std::getline(line_stream, cell, ','))
00599             {
00600               // FIXME: factor out this strtol stuff into a utility function.
00601               char* endptr;
00602               dof_id_type abaqus_global_node_id = cast_int<dof_id_type>
00603                 (std::strtol(cell.c_str(), &endptr, /*base=*/10));
00604 
00605               if (abaqus_global_node_id!=0 || cell.c_str() != endptr)
00606                 {
00607                   // Use the global node number mapping to determine the corresponding libmesh global node id
00608                   dof_id_type libmesh_global_node_id = _abaqus_to_libmesh_node_mapping[abaqus_global_node_id];
00609 
00610                   // Grab the node pointer from the mesh for this ID
00611                   Node* node = the_mesh.node_ptr(libmesh_global_node_id);
00612 
00613                   // If node_ptr() returns NULL, it may mean we have not yet read the
00614                   // *Nodes section, though I assumed that always came before the *Elements section...
00615                   if (node == NULL)
00616                     libmesh_error_msg("Error!  Mesh returned NULL Node pointer.  Either no node exists with ID " \
00617                                       << libmesh_global_node_id         \
00618                                       << " or perhaps this input file has *Elements defined before *Nodes?");
00619 
00620                   // Note: id_count is the zero-based abaqus (elem local) node index.  We therefore map
00621                   // it to a libmesh elem local node index using the element definition map
00622                   unsigned libmesh_elem_local_node_id =
00623                     eledef.abaqus_zero_based_node_id_to_libmesh_node_id[id_count];
00624 
00625                   // Set this node pointer within the element.
00626                   elem->set_node(libmesh_elem_local_node_id) = node;
00627 
00628                   // Increment the count of IDs read for this element
00629                   id_count++;
00630                 } // end if strtol success
00631             } // end while getline(',')
00632         } // end while (id_count)
00633 
00634       // Ensure that we read *exactly* as many nodes as we were expecting to, no more.
00635       if (id_count != n_nodes_per_elem)
00636         libmesh_error_msg("Error: Needed to read " \
00637                           << n_nodes_per_elem      \
00638                           << " nodes, but read "   \
00639                           << id_count              \
00640                           << " instead!");
00641 
00642       // If we are recording Elset IDs, add this element to the correct set for later processing.
00643       // Make sure to add it with the Abaqus ID, not the libmesh one!
00644       if (elset_name != "")
00645         _elemset_ids[elset_name].push_back(abaqus_elem_id);
00646     } // end while (peek)
00647 }
00648 
00649 
00650 
00651 
00652 std::string AbaqusIO::parse_label(std::string line, std::string label_name)
00653 {
00654   // Handle files which have weird line endings from e.g. windows.
00655   // You can check what kind of line endings you have with 'cat -vet'.
00656   // For example, some files may have two kinds of line endings like:
00657   //
00658   // 4997,^I496,^I532,^I487,^I948^M$
00659   //
00660   // and we don't want to deal with this when extracting a label, so
00661   // just remove all the space characters, which should include all
00662   // kinds of remaining newlines.  (I don't think Abaqus allows
00663   // whitespace in label names.)
00664   line.erase(std::remove_if(line.begin(), line.end(), isspace), line.end());
00665 
00666   // Do all string comparisons in upper-case
00667   std::string
00668     upper_line(line),
00669     upper_label_name(label_name);
00670   std::transform(upper_line.begin(), upper_line.end(), upper_line.begin(), ::toupper);
00671   std::transform(upper_label_name.begin(), upper_label_name.end(), upper_label_name.begin(), ::toupper);
00672 
00673   // Get index of start of "label="
00674   size_t label_index = upper_line.find(upper_label_name + "=");
00675 
00676   if (label_index != std::string::npos)
00677     {
00678       // Location of the first comma following "label="
00679       size_t comma_index = upper_line.find(",", label_index);
00680 
00681       // Construct iterators from which to build the sub-string.
00682       // Note: The +1 while initializing beg is to skip past the "=" which follows the label name
00683       std::string::iterator
00684         beg = line.begin() + label_name.size() + 1 + label_index,
00685         end = (comma_index == std::string::npos) ? line.end() : line.begin() + comma_index;
00686 
00687       return std::string(beg, end);
00688     }
00689 
00690   // The label index was not found, return the empty string
00691   return std::string("");
00692 }
00693 
00694 
00695 
00696 
00697 void AbaqusIO::read_ids(std::string set_name, container_t& container)
00698 {
00699   // Grab a reference to a vector that will hold all the IDs
00700   std::vector<dof_id_type>& id_storage = container[set_name];
00701 
00702   // Read until the start of another section is detected, or EOF is encountered
00703   while (_in.peek() != '*' && _in.peek() != EOF)
00704     {
00705       // Read entire comma-separated line into a string
00706       std::string csv_line;
00707       std::getline(_in, csv_line);
00708 
00709       // On that line, use std::getline again to parse each
00710       // comma-separated entry.
00711       std::string cell;
00712       std::stringstream line_stream(csv_line);
00713       while (std::getline(line_stream, cell, ','))
00714         {
00715           // If no conversion can be performed by strtol, 0 is returned.
00716           //
00717           // If endptr is not NULL, strtol() stores the address of the
00718           // first invalid character in *endptr.  If there were no
00719           // digits at all, however, strtol() stores the original
00720           // value of str in *endptr.
00721           char* endptr;
00722 
00723           // FIXME - this needs to be updated for 64-bit inputs
00724           dof_id_type id = cast_int<dof_id_type>
00725             (std::strtol(cell.c_str(), &endptr, /*base=*/10));
00726 
00727           // Note that lists of comma-separated values in abaqus also
00728           // *end* with a comma, so the last call to getline on a given
00729           // line will get an empty string, which we must detect.
00730           if (id != 0 || cell.c_str() != endptr)
00731             {
00732               // 'cell' is now a string with an integer id in it
00733               id_storage.push_back( id );
00734             }
00735         }
00736     }
00737 }
00738 
00739 
00740 
00741 
00742 void AbaqusIO::read_sideset(std::string sideset_name, sideset_container_t& container)
00743 {
00744   // Grab a reference to a vector that will hold all the IDs
00745   std::vector<std::pair<dof_id_type, unsigned> >& id_storage = container[sideset_name];
00746 
00747   // Variables for storing values read in from file
00748   dof_id_type elem_id=0;
00749   unsigned side_id=0;
00750   char c;
00751   std::string dummy;
00752 
00753   // Read until the start of another section is detected, or EOF is encountered
00754   while (_in.peek() != '*' && _in.peek() != EOF)
00755     {
00756       // The strings are of the form: "391, S2"
00757 
00758       // Read the element ID and the leading comma
00759       _in >> elem_id >> c;
00760 
00761       // Read another character (the 'S') and finally the side ID
00762       _in >> c >> side_id;
00763 
00764       // Store this pair of data in the vector
00765       id_storage.push_back( std::make_pair(elem_id, side_id) );
00766 
00767       // Extract remaining characters on line including newline
00768       std::getline(_in, dummy);
00769     } // while
00770 }
00771 
00772 
00773 
00774 
00775 void AbaqusIO::assign_subdomain_ids()
00776 {
00777   // Get a reference to the mesh we are reading
00778   MeshBase& the_mesh = MeshInput<MeshBase>::mesh();
00779 
00780   // The number of elemsets we've found while reading
00781   std::size_t n_elemsets = _elemset_ids.size();
00782 
00783   // Fill in a temporary map with (ElemType, index) pairs based on the _elem_types set.  This
00784   // will allow us to easily look up this index in the loop below.
00785   std::map<ElemType, unsigned> elem_types_map;
00786   {
00787     unsigned ctr=0;
00788     for (std::set<ElemType>::iterator it=_elem_types.begin(); it!=_elem_types.end(); ++it)
00789       elem_types_map[*it] = ctr++;
00790   }
00791 
00792   // Loop over each Elemset and assign subdomain IDs to Mesh elements
00793   {
00794     // The maximum element dimension seen while reading the Mesh
00795     unsigned char max_dim = this->max_elem_dimension_seen();
00796 
00797     // The elemset_id counter assigns a logical numbering to the _elemset_ids keys
00798     container_t::iterator it = _elemset_ids.begin();
00799     for (unsigned elemset_id=0; it != _elemset_ids.end(); ++it, ++elemset_id)
00800       {
00801         // Grab a reference to the vector of IDs
00802         std::vector<dof_id_type>& id_vector = it->second;
00803 
00804         // Loop over this vector
00805         for (std::size_t i=0; i<id_vector.size(); ++i)
00806           {
00807             // Map the id_vector[i]'th element ID (Abaqus numbering) to LibMesh numbering
00808             dof_id_type libmesh_elem_id = _abaqus_to_libmesh_elem_mapping[ id_vector[i] ];
00809 
00810             // Get pointer to that element
00811             Elem* elem = the_mesh.elem(libmesh_elem_id);
00812 
00813             if (elem == NULL)
00814               libmesh_error_msg("Mesh returned NULL pointer for Elem " << libmesh_elem_id);
00815 
00816             // We won't assign subdomain ids to lower-dimensional
00817             // elements, as they are assumed to represent boundary
00818             // conditions.  Since lower-dimensional elements can
00819             // appear in multiple sidesets, it doesn't make sense to
00820             // assign them a single subdomain id... only the last one
00821             // assigned would actually "stick".
00822             if (elem->dim() < max_dim)
00823               break;
00824 
00825             // Compute the proper subdomain ID, based on the formula in the
00826             // documentation for this function.
00827             subdomain_id_type computed_id = cast_int<subdomain_id_type>
00828               (elemset_id + (elem_types_map[elem->type()] * n_elemsets));
00829 
00830             // Assign this ID to the element in question
00831             elem->subdomain_id() = computed_id;
00832 
00833             // We will also assign a unique name to the computed_id,
00834             // which is created by appending the geometric element
00835             // name to the elset name provided by the user in the
00836             // Abaqus file.
00837             std::string computed_name = it->first + "_" + Utility::enum_to_string(elem->type());
00838             the_mesh.subdomain_name(computed_id) = computed_name;
00839           }
00840       }
00841   }
00842 }
00843 
00844 
00845 
00846 
00847 void AbaqusIO::assign_boundary_node_ids()
00848 {
00849   // Get a reference to the mesh we are reading
00850   MeshBase& the_mesh = MeshInput<MeshBase>::mesh();
00851 
00852   // Iterate over the container of nodesets
00853   container_t::iterator it = _nodeset_ids.begin();
00854   for (unsigned short current_id=0; it != _nodeset_ids.end(); ++it, ++current_id)
00855     {
00856       // Associate current_id with the name we determined earlier
00857       the_mesh.get_boundary_info().nodeset_name(current_id) = it->first;
00858 
00859       // Get a reference to the current vector of nodeset ID values
00860       std::vector<dof_id_type>& nodeset_ids = it->second;
00861 
00862       for (std::size_t i=0; i<nodeset_ids.size(); ++i)
00863         {
00864           // Map the Abaqus global node ID to the libmesh node ID
00865           dof_id_type libmesh_global_node_id = _abaqus_to_libmesh_node_mapping[nodeset_ids[i]];
00866 
00867           // Get node pointer from the mesh
00868           Node* node = the_mesh.node_ptr(libmesh_global_node_id);
00869 
00870           if (node == NULL)
00871             libmesh_error_msg("Error! Mesh returned NULL node pointer!");
00872 
00873           // Add this node with the current_id (which is determined by the
00874           // alphabetical ordering of the map) to the BoundaryInfo object
00875           the_mesh.get_boundary_info().add_node(node, current_id);
00876         }
00877     }
00878 }
00879 
00880 
00881 
00882 
00883 void AbaqusIO::assign_sideset_ids()
00884 {
00885   // Get a reference to the mesh we are reading
00886   MeshBase& the_mesh = MeshInput<MeshBase>::mesh();
00887 
00888   // initialize the eletypes map (eletypes is a file-global variable)
00889   init_eletypes();
00890 
00891   // Iterate over the container of sidesets
00892   {
00893     sideset_container_t::iterator it = _sideset_ids.begin();
00894     for (unsigned short current_id=0; it != _sideset_ids.end(); ++it, ++current_id)
00895       {
00896         // Associate current_id with the name we determined earlier
00897         the_mesh.get_boundary_info().sideset_name(current_id) = it->first;
00898 
00899         // Get a reference to the current vector of nodeset ID values
00900         std::vector<std::pair<dof_id_type,unsigned> >& sideset_ids = it->second;
00901 
00902         for (std::size_t i=0; i<sideset_ids.size(); ++i)
00903           {
00904             // sideset_ids is a vector of pairs (elem id, side id).  Pull them out
00905             // now to make the code below more readable.
00906             dof_id_type  abaqus_elem_id = sideset_ids[i].first;
00907             unsigned abaqus_side_number = sideset_ids[i].second;
00908 
00909             // Map the Abaqus element ID to LibMesh numbering
00910             dof_id_type libmesh_elem_id = _abaqus_to_libmesh_elem_mapping[ abaqus_elem_id ];
00911 
00912             // Get pointer to that element
00913             Elem* elem = the_mesh.elem(libmesh_elem_id);
00914 
00915             // Check that the pointer returned from the Mesh is non-NULL
00916             if (elem == NULL)
00917               libmesh_error_msg("Mesh returned NULL pointer for Elem " << libmesh_elem_id);
00918 
00919             // Grab a reference to the element definition for this element type
00920             const ElementDefinition& eledef = eletypes[elem->type()];
00921 
00922             // If the element definition was not found, the call above would have
00923             // created one with an uninitialized struct.  Check for that here...
00924             if (eledef.abaqus_zero_based_side_id_to_libmesh_side_id.size() == 0)
00925               libmesh_error_msg("No Abaqus->LibMesh mapping information for ElemType " \
00926                                 << Utility::enum_to_string(elem->type())  \
00927                                 << "!");
00928 
00929             // Add this node with the current_id (which is determined by the
00930             // alphabetical ordering of the map).  Side numbers in Abaqus are 1-based,
00931             // so we subtract 1 here before passing the abaqus side number to the
00932             // mapping array
00933             the_mesh.get_boundary_info().add_side
00934               (elem,
00935                eledef.abaqus_zero_based_side_id_to_libmesh_side_id[abaqus_side_number-1],
00936                current_id);
00937           }
00938       }
00939   }
00940 
00941 
00942   // Some elsets (if they contain lower-dimensional elements) also
00943   // define sidesets.  So loop over them and build a searchable data
00944   // structure we can use to assign sidesets.
00945   {
00946     unsigned char max_dim = this->max_elem_dimension_seen();
00947 
00948     // multimap from "vector-of-lower-dimensional-element-node-ids" to subdomain ID which should be applied.
00949     // We use a multimap because the lower-dimensional elements can belong to more than 1 sideset.
00950     typedef std::multimap<std::vector<dof_id_type>, unsigned> provide_bcs_t;
00951     provide_bcs_t provide_bcs;
00952 
00953     // The elemset_id counter assigns a logical numbering to the _elemset_ids keys
00954     container_t::iterator it = _elemset_ids.begin();
00955     for (unsigned short elemset_id=0; it != _elemset_ids.end(); ++it, ++elemset_id)
00956       {
00957         // Grab a reference to the vector of IDs
00958         std::vector<dof_id_type>& id_vector = it->second;
00959 
00960         // Loop over this vector
00961         for (std::size_t i=0; i<id_vector.size(); ++i)
00962           {
00963             // Map the id_vector[i]'th element ID (Abaqus numbering) to LibMesh numbering
00964             dof_id_type libmesh_elem_id = _abaqus_to_libmesh_elem_mapping[ id_vector[i] ];
00965 
00966             // Get pointer to that element
00967             Elem* elem = the_mesh.elem(libmesh_elem_id);
00968 
00969             if (elem == NULL)
00970               libmesh_error_msg("Mesh returned NULL pointer for Elem " << libmesh_elem_id);
00971 
00972             // If the element dimension is equal to the maximum
00973             // dimension seen, we can break out of this for loop --
00974             // this elset does not contain sideset information.
00975             if (elem->dim() == max_dim)
00976               break;
00977 
00978             // We can only handle elements that are *exactly*
00979             // one dimension lower than the max element
00980             // dimension.  Not sure if "edge" BCs in 3D
00981             // actually make sense/are required...
00982             if (elem->dim()+1 != max_dim)
00983               libmesh_error_msg("ERROR: Expected boundary element of dimension " << max_dim-1 << " but got " << elem->dim());
00984 
00985             // To be pushed into the provide_bcs data container
00986             std::vector<dof_id_type> elem_node_ids(elem->n_nodes());
00987 
00988             // Save node IDs in a local vector which will be used as a key for the map.
00989             for (unsigned n=0; n<elem->n_nodes(); n++)
00990               elem_node_ids[n] = elem->node(n);
00991 
00992             // Sort before putting into the map
00993             std::sort(elem_node_ids.begin(), elem_node_ids.end());
00994 
00995             // Insert the (key, id) pair into the multimap
00996             provide_bcs.insert(std::make_pair(elem_node_ids, elemset_id));
00997 
00998             // Associate the name of this sideset with the ID we've
00999             // chosen.  It's not necessary to do this for every
01000             // element in the set, but it's convenient to do it here
01001             // since we have all the necessary information...
01002             the_mesh.get_boundary_info().sideset_name(elemset_id) = it->first;
01003           }
01004       }
01005 
01006     // Loop over elements and try to assign boundary information
01007     {
01008       MeshBase::element_iterator       e_it  = the_mesh.active_elements_begin();
01009       const MeshBase::element_iterator end = the_mesh.active_elements_end();
01010       for ( ; e_it != end; ++e_it)
01011         {
01012           Elem* elem = *e_it;
01013 
01014           if (elem->dim() == max_dim)
01015             {
01016               // This is a max-dimension element that may require BCs.
01017               // For each of its sides, including internal sides, we'll
01018               // see if a lower-dimensional element provides boundary
01019               // information for it.  Note that we have not yet called
01020               // find_neighbors(), so we can't use elem->neighbor(sn) in
01021               // this algorithm...
01022               for (unsigned short sn=0; sn<elem->n_sides(); sn++)
01023                 {
01024                   UniquePtr<Elem> side (elem->build_side(sn));
01025 
01026                   // Build up a node_ids vector, which is the key
01027                   std::vector<dof_id_type> node_ids(side->n_nodes());
01028                   for (unsigned n=0; n<side->n_nodes(); n++)
01029                     node_ids[n] = side->node(n);
01030 
01031                   // Sort the vector before using it as a key
01032                   std::sort(node_ids.begin(), node_ids.end());
01033 
01034                   // Look for this key in the provide_bcs multimap
01035                   std::pair<provide_bcs_t::const_iterator, provide_bcs_t::const_iterator>
01036                     range = provide_bcs.equal_range (node_ids);
01037 
01038                   // Add boundary information for each side in the range.
01039                   for (provide_bcs_t::const_iterator s_it = range.first;
01040                        s_it != range.second; ++s_it)
01041                     the_mesh.get_boundary_info().add_side
01042                       (elem, sn, cast_int<unsigned short>(s_it->second));
01043                 }
01044             }
01045         }
01046     }
01047   }
01048 }
01049 
01050 
01051 
01052 void AbaqusIO::process_and_discard_comments()
01053 {
01054   std::string dummy;
01055   while (true)
01056     {
01057       // We assume we are at the beginning of a line that may be
01058       // comments or may be data.  We need to only discard the line if
01059       // it begins with **, but we must avoid calling std::getline()
01060       // since there's no way to put that back.
01061       if (_in.peek() == '*')
01062         {
01063           // The first character was a star, so actually read it from the stream.
01064           _in.get();
01065 
01066           // Peek at the next character...
01067           if (_in.peek() == '*')
01068             {
01069               // OK, second character was star also, by definition this
01070               // line must be a comment!  Read the rest of the line and discard!
01071               std::getline(_in, dummy);
01072             }
01073           else
01074             {
01075               // The second character was _not_ a star, so put back the first star
01076               // we pulled out so that the line can be parsed correctly by somebody
01077               // else!
01078               _in.unget();
01079 
01080               // Finally, break out of the while loop, we are done parsing comments
01081               break;
01082             }
01083         }
01084       else
01085         {
01086           // First character was not *, so this line must be data! Break out of the
01087           // while loop!
01088           break;
01089         }
01090     }
01091 }
01092 
01093 
01094 
01095 unsigned char AbaqusIO::max_elem_dimension_seen ()
01096 {
01097   unsigned char max_dim = 0;
01098 
01099   unsigned char elem_dimensions_size = cast_int<unsigned char>
01100     (elems_of_dimension.size());
01101   // The elems_of_dimension array is 1-based in the UNV reader
01102   for (unsigned char i=1; i<elem_dimensions_size; ++i)
01103     if (elems_of_dimension[i])
01104       max_dim = i;
01105 
01106   return max_dim;
01107 }
01108 
01109 
01110 } // namespace