![]() |
Mesh Oriented datABase
(version 5.4.1)
Array-based unstructured mesh datastructure
|
00001 /**
00002 * MOAB, a Mesh-Oriented datABase, is a software component for creating,
00003 * storing and accessing finite element mesh data.
00004 *
00005 * Copyright 2004 Sandia Corporation. Under the terms of Contract
00006 * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government
00007 * retains certain rights in this software.
00008 *
00009 * This library is free software; you can redistribute it and/or
00010 * modify it under the terms of the GNU Lesser General Public
00011 * License as published by the Free Software Foundation; either
00012 * version 2.1 of the License, or (at your option) any later version.
00013 *
00014 */
00015
00016 #include "ReadNASTRAN.hpp"
00017
00018 #include
00019 #include
00020 #include
00021 #include
00022 #include
00023 #include
00024 #include
00025
00026 #include "moab/Interface.hpp"
00027 #include "moab/ReadUtilIface.hpp"
00028 #include "Internals.hpp" // For MB_START_ID
00029 #include "moab/Range.hpp"
00030 #include "moab/FileOptions.hpp"
00031 #include "FileTokenizer.hpp"
00032 #include "MBTagConventions.hpp"
00033 #include "moab/CN.hpp"
00034
00035 namespace moab
00036 {
00037
00038 ReaderIface* ReadNASTRAN::factory( Interface* iface )
00039 {
00040 return new ReadNASTRAN( iface );
00041 }
00042
00043 // Constructor
00044 ReadNASTRAN::ReadNASTRAN( Interface* impl ) : MBI( impl )
00045 {
00046 assert( NULL != impl );
00047 MBI->query_interface( readMeshIface );
00048 assert( NULL != readMeshIface );
00049 }
00050
00051 // Destructor
00052 ReadNASTRAN::~ReadNASTRAN()
00053 {
00054 if( readMeshIface )
00055 {
00056 MBI->release_interface( readMeshIface );
00057 readMeshIface = 0;
00058 }
00059 }
00060
00061 ErrorCode ReadNASTRAN::read_tag_values( const char* /*file_name*/,
00062 const char* /*tag_name*/,
00063 const FileOptions& /*opts*/,
00064 std::vector< int >& /*tag_values_out*/,
00065 const SubsetList* /*subset_list*/ )
00066 {
00067 return MB_NOT_IMPLEMENTED;
00068 }
00069
00070 // Load the file as called by the Interface function
00071 ErrorCode ReadNASTRAN::load_file( const char* filename,
00072 const EntityHandle* /* file_set */,
00073 const FileOptions& /* opts */,
00074 const ReaderIface::SubsetList* subset_list,
00075 const Tag* file_id_tag )
00076 {
00077 // At this time there is no support for reading a subset of the file
00078 if( subset_list )
00079 {
00080 MB_SET_ERR( MB_UNSUPPORTED_OPERATION, "Reading subset of files not supported for NASTRAN" );
00081 }
00082
00083 nodeIdMap.clear();
00084 elemIdMap.clear();
00085
00086 bool debug = false;
00087 if( debug ) std::cout << "begin ReadNASTRAN::load_file" << std::endl;
00088 ErrorCode result;
00089
00090 // Count the entities of each type in the file. This is used to allocate the node array.
00091 int entity_count[MBMAXTYPE];
00092 for( int i = 0; i < MBMAXTYPE; i++ )
00093 entity_count[i] = 0;
00094
00095 /* Determine the line_format of the first line. Assume that the entire file
00096 has the same format. */
00097 std::string line;
00098 std::ifstream file( filename );
00099 if( !getline( file, line ) ) return MB_FILE_DOES_NOT_EXIST;
00100 line_format format;
00101 result = determine_line_format( line, format );
00102 if( MB_SUCCESS != result ) return result;
00103
00104 /* Count the number of each entity in the file. This allows one to allocate
00105 a sequential array of vertex handles. */
00106 while( !file.eof() )
00107 {
00108 // Cut the line into fields as determined by the line format.
00109 // Use a vector to allow for an unknown number of tokens (continue lines).
00110 // Continue lines are not implemented.
00111 std::vector< std::string > tokens;
00112 tokens.reserve( 10 ); // assume 10 fields to avoid extra vector resizing
00113 result = tokenize_line( line, format, tokens );
00114 if( MB_SUCCESS != result ) return result;
00115
00116 // Process the tokens of the line. The first token describes the entity type.
00117 EntityType type;
00118 result = determine_entity_type( ( tokens.empty() ) ? "" : tokens.front(), type );
00119 if( MB_SUCCESS != result ) return result;
00120 entity_count[type]++;
00121 getline( file, line );
00122 }
00123
00124 if( debug )
00125 {
00126 for( int i = 0; i < MBMAXTYPE; i++ )
00127 {
00128 std::cout << "entity_count[" << i << "]=" << entity_count[i] << std::endl;
00129 }
00130 }
00131
00132 // Keep list of material sets
00133 std::vector< Range > materials;
00134
00135 // Now that the number of vertices is known, create the vertices.
00136 EntityHandle start_vert = 0;
00137 std::vector< double* > coord_arrays( 3 );
00138 result = readMeshIface->get_node_coords( 3, entity_count[0], MB_START_ID, start_vert, coord_arrays );
00139 if( MB_SUCCESS != result ) return result;
00140 if( 0 == start_vert ) return MB_FAILURE; // check for NULL
00141 int id, vert_index = 0;
00142 if( debug ) std::cout << "allocated coord arrays" << std::endl;
00143
00144 // Read the file again to create the entities.
00145 file.clear(); // Clear eof state from object
00146 file.seekg( 0 ); // Rewind file
00147 while( !file.eof() )
00148 {
00149 getline( file, line );
00150
00151 // Cut the line into fields as determined by the line format.
00152 // Use a vector to allow for an unknown number of tokens (continue lines).
00153 // Continue lines are not implemented.
00154 std::vector< std::string > tokens;
00155 tokens.reserve( 10 ); // assume 10 fields to avoid extra vector resizing
00156 result = tokenize_line( line, format, tokens );
00157 if( MB_SUCCESS != result ) return result;
00158
00159 // Process the tokens of the line. The first token describes the entity type.
00160 EntityType type;
00161 result = determine_entity_type( tokens.front(), type );
00162 if( MB_SUCCESS != result ) return result;
00163
00164 // Create the entity.
00165 if( MBVERTEX == type )
00166 {
00167 double* coords[3] = { coord_arrays[0] + vert_index, coord_arrays[1] + vert_index,
00168 coord_arrays[2] + vert_index };
00169 result = read_node( tokens, debug, coords, id );
00170 if( MB_SUCCESS != result ) return result;
00171 if( !nodeIdMap.insert( id, start_vert + vert_index, 1 ).second ) return MB_FAILURE; // Duplicate IDs!
00172 ++vert_index;
00173 }
00174 else
00175 {
00176 result = read_element( tokens, materials, type, debug );
00177 if( MB_SUCCESS != result ) return result;
00178 }
00179 }
00180
00181 result = create_materials( materials );
00182 if( MB_SUCCESS != result ) return result;
00183
00184 result = assign_ids( file_id_tag );
00185 if( MB_SUCCESS != result ) return result;
00186
00187 file.close();
00188 nodeIdMap.clear();
00189 elemIdMap.clear();
00190 return MB_SUCCESS;
00191 }
00192
00193 /* Determine the type of NASTRAN line: small field, large field, or free field.
00194 small field: each line has 10 fields of 8 characters
00195 large field: 1x8, 4x16, 1x8. Field 1 must have an asterisk following the character string
00196 free field: each line entry must be separated by a comma
00197 Implementation tries to avoid more searches than necessary. */
00198 ErrorCode ReadNASTRAN::determine_line_format( const std::string& line, line_format& format )
00199 {
00200 std::string::size_type found_asterisk = line.find( "*" );
00201 if( std::string::npos != found_asterisk )
00202 {
00203 format = LARGE_FIELD;
00204 return MB_SUCCESS;
00205 }
00206 else
00207 {
00208 std::string::size_type found_comma = line.find( "," );
00209 if( std::string::npos != found_comma )
00210 {
00211 format = FREE_FIELD;
00212 return MB_SUCCESS;
00213 }
00214 else
00215 {
00216 format = SMALL_FIELD;
00217 return MB_SUCCESS;
00218 }
00219 }
00220 }
00221
00222 /* Tokenize the line. Continue-lines have not been implemented. */
00223 ErrorCode ReadNASTRAN::tokenize_line( const std::string& line,
00224 const line_format format,
00225 std::vector< std::string >& tokens )
00226 {
00227 size_t line_size = line.size();
00228 switch( format )
00229 {
00230 case SMALL_FIELD: {
00231 // Expect 10 fields of 8 characters.
00232 // The sample file does not have all 10 fields in each line
00233 const int field_length = 8;
00234 unsigned int n_tokens = line_size / field_length;
00235 for( unsigned int i = 0; i < n_tokens; i++ )
00236 {
00237 tokens.push_back( line.substr( i * field_length, field_length ) );
00238 }
00239 break;
00240 }
00241 case LARGE_FIELD:
00242 return MB_NOT_IMPLEMENTED;
00243 case FREE_FIELD:
00244 return MB_NOT_IMPLEMENTED;
00245 default:
00246 return MB_FAILURE;
00247 }
00248
00249 return MB_SUCCESS;
00250 }
00251
00252 ErrorCode ReadNASTRAN::determine_entity_type( const std::string& first_token, EntityType& type )
00253 {
00254 if( 0 == first_token.compare( "GRID " ) )
00255 type = MBVERTEX;
00256 else if( 0 == first_token.compare( "CTETRA " ) )
00257 type = MBTET;
00258 else if( 0 == first_token.compare( "CPENTA " ) )
00259 type = MBPRISM;
00260 else if( 0 == first_token.compare( "CHEXA " ) )
00261 type = MBHEX;
00262 else
00263 return MB_NOT_IMPLEMENTED;
00264
00265 return MB_SUCCESS;
00266 }
00267
00268 /* Some help from Jason:
00269 Nastran floats must contain a decimal point, may contain
00270 a leading '-' and may contain an exponent. The 'E' is optional
00271 when specifying an exponent. A '-' or '+' at any location other
00272 than the first position indicates an exponent. For a positive
00273 exponent, either a '+' or an 'E' must be specified. For a
00274 negative exponent, the 'E' is option and the '-' is always specified.
00275 Examples for the real value 7.0 from mcs2006 quick reference guide:
00276 7.0 .7E1 0.7+1 .70+1 7.E+0 70.-1
00277
00278 From the test file created in SC/Tetra:
00279 GRID 1 03.9804546.9052-15.6008-1
00280 has the coordinates: ( 3.980454, 6.9052e-1, 5.6008e-1 )
00281 GRID 200005 04.004752-3.985-15.4955-1
00282 has the coordinates: ( 4.004752, -3.985e-1, 5.4955e-1 ) */
00283 ErrorCode ReadNASTRAN::get_real( const std::string& token, double& real )
00284 {
00285 std::string significand = token;
00286 std::string exponent = "0";
00287
00288 // Cut off the first digit because a "-" could be here indicating a negative
00289 // number. Instead we are looking for a negative exponent.
00290 std::string back_token = token.substr( 1 );
00291
00292 // A minus that is not the first digit is always a negative exponent
00293 std::string::size_type found_minus = back_token.find( "-" );
00294 if( std::string::npos != found_minus )
00295 {
00296 // separate the significand from the exponent at the "-"
00297 exponent = token.substr( found_minus + 1 );
00298 significand = token.substr( 0, found_minus + 1 );
00299
00300 // If the significand has an "E", remove it
00301 if( std::string::npos != significand.find( "E" ) )
00302 // Assume the "E" is at the end of the significand.
00303 significand = significand.substr( 1, significand.size() - 2 );
00304
00305 // If a minus does not exist past the 1st digit, but an "E" or "+" does, then
00306 // it is a positive exponent. First look for an "E",
00307 }
00308 else
00309 {
00310 std::string::size_type found_E = token.find( "E" );
00311 if( std::string::npos != found_E )
00312 {
00313 significand = token.substr( 0, found_E - 1 );
00314 exponent = token.substr( found_E + 1 );
00315 // If there is a "+" on the exponent, cut it off
00316 std::size_t found_plus = exponent.find( "+" );
00317 if( std::string::npos != found_plus )
00318 {
00319 exponent = exponent.substr( found_plus + 1 );
00320 }
00321 }
00322 else
00323 {
00324 // If there is a "+" on the exponent, cut it off
00325 std::size_t found_plus = token.find( "+" );
00326 if( std::string::npos != found_plus )
00327 {
00328 significand = token.substr( 0, found_plus - 1 );
00329 exponent = token.substr( found_plus + 1 );
00330 }
00331 }
00332 }
00333
00334 // Now assemble the real number
00335 double signi = atof( significand.c_str() );
00336 double expon = atof( exponent.c_str() );
00337
00338 if( HUGE_VAL == signi || HUGE_VAL == expon ) return MB_FAILURE;
00339
00340 real = signi * pow( 10, expon );
00341
00342 return MB_SUCCESS;
00343 }
00344
00345 /* It has been determined that this line is a vertex. Read the rest of
00346 the line and create the vertex. */
00347 ErrorCode ReadNASTRAN::read_node( const std::vector< std::string >& tokens,
00348 const bool debug,
00349 double* coords[3],
00350 int& id )
00351 {
00352 // Read the node's id (unique)
00353 ErrorCode result;
00354 id = atoi( tokens[1].c_str() );
00355
00356 // Read the node's coordinate system number
00357 // "0" or blank refers to the basic coordinate system.
00358 int coord_system = atoi( tokens[2].c_str() );
00359 if( 0 != coord_system )
00360 {
00361 std::cerr << "ReadNASTRAN: alternative coordinate systems not implemented" << std::endl;
00362 return MB_NOT_IMPLEMENTED;
00363 }
00364
00365 // Read the coordinates
00366 for( unsigned int i = 0; i < 3; i++ )
00367 {
00368 result = get_real( tokens[i + 3], *coords[i] );
00369 if( MB_SUCCESS != result ) return result;
00370 if( debug ) std::cout << "read_node: coords[" << i << "]=" << coords[i] << std::endl;
00371 }
00372
00373 return MB_SUCCESS;
00374 }
00375
00376 /* The type of element has already been identified. Read the rest of the
00377 line and create the element. Assume that all of the nodes have already
00378 been created. */
00379 ErrorCode ReadNASTRAN::read_element( const std::vector< std::string >& tokens,
00380 std::vector< Range >& materials,
00381 const EntityType element_type,
00382 const bool /*debug*/ )
00383 {
00384 // Read the element's id (unique) and material set
00385 ErrorCode result;
00386 int id = atoi( tokens[1].c_str() );
00387 int material = atoi( tokens[2].c_str() );
00388
00389 // Resize materials list if necessary. This code is somewhat complicated
00390 // so as to avoid copying of Ranges
00391 if( material >= (int)materials.size() )
00392 {
00393 if( (int)materials.capacity() < material )
00394 materials.resize( material + 1 );
00395 else
00396 {
00397 std::vector< Range > new_mat( material + 1 );
00398 for( size_t i = 0; i < materials.size(); ++i )
00399 new_mat[i].swap( materials[i] );
00400 materials.swap( new_mat );
00401 }
00402 }
00403
00404 // The size of the connectivity array depends on the element type
00405 int n_conn = CN::VerticesPerEntity( element_type );
00406 EntityHandle conn_verts[27];
00407 assert( n_conn <= (int)( sizeof( conn_verts ) / sizeof( EntityHandle ) ) );
00408
00409 // Read the connected node ids from the file
00410 for( int i = 0; i < n_conn; i++ )
00411 {
00412 int n = atoi( tokens[3 + i].c_str() );
00413 conn_verts[i] = nodeIdMap.find( n );
00414 if( !conn_verts[i] ) // invalid vertex id
00415 return MB_FAILURE;
00416 }
00417
00418 // Create the element and set the global id from the NASTRAN file
00419 EntityHandle element;
00420 result = MBI->create_element( element_type, conn_verts, n_conn, element );
00421 if( MB_SUCCESS != result ) return result;
00422 elemIdMap.insert( id, element, 1 );
00423
00424 materials[material].insert( element );
00425 return MB_SUCCESS;
00426 }
00427
00428 ErrorCode ReadNASTRAN::create_materials( const std::vector< Range >& materials )
00429 {
00430 ErrorCode result;
00431 Tag material_tag;
00432 int negone = -1;
00433 result = MBI->tag_get_handle( MATERIAL_SET_TAG_NAME, 1, MB_TYPE_INTEGER, material_tag, MB_TAG_SPARSE | MB_TAG_CREAT,
00434 &negone );
00435 if( MB_SUCCESS != result ) return result;
00436
00437 for( size_t i = 0; i < materials.size(); ++i )
00438 {
00439 if( materials[i].empty() ) continue;
00440
00441 // Merge with existing or create new? Original code effectively
00442 // created new by only merging with existing in current file set,
00443 // so do the same here. - j.kraftcheck
00444
00445 EntityHandle handle;
00446 result = MBI->create_meshset( MESHSET_SET, handle );
00447 if( MB_SUCCESS != result ) return result;
00448
00449 result = MBI->add_entities( handle, materials[i] );
00450 if( MB_SUCCESS != result ) return result;
00451
00452 int id = i;
00453 result = MBI->tag_set_data( material_tag, &handle, 1, &id );
00454 if( MB_SUCCESS != result ) return result;
00455 }
00456
00457 return MB_SUCCESS;
00458 }
00459
00460 ErrorCode ReadNASTRAN::assign_ids( const Tag* file_id_tag )
00461 {
00462 // Create tag
00463 ErrorCode result;
00464 Tag id_tag = MBI->globalId_tag();
00465
00466 RangeMap< int, EntityHandle >::iterator i;
00467 for( int t = 0; t < 2; ++t )
00468 {
00469 RangeMap< int, EntityHandle >& fileIdMap = t ? elemIdMap : nodeIdMap;
00470 for( i = fileIdMap.begin(); i != fileIdMap.end(); ++i )
00471 {
00472 Range range( i->value, i->value + i->count - 1 );
00473
00474 result = readMeshIface->assign_ids( id_tag, range, i->begin );
00475 if( MB_SUCCESS != result ) return result;
00476
00477 if( file_id_tag && *file_id_tag != id_tag )
00478 {
00479 result = readMeshIface->assign_ids( *file_id_tag, range, i->begin );
00480 if( MB_SUCCESS != result ) return result;
00481 }
00482 }
00483 }
00484
00485 return MB_SUCCESS;
00486 }
00487
00488 } // namespace moab