Branch data Line data Source code
1 : : /**
2 : : * MOAB, a Mesh-Oriented datABase, is a software component for creating,
3 : : * storing and accessing finite element mesh data.
4 : : *
5 : : * Copyright 2004 Sandia Corporation. Under the terms of Contract
6 : : * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government
7 : : * retains certain rights in this software.
8 : : *
9 : : * This library is free software; you can redistribute it and/or
10 : : * modify it under the terms of the GNU Lesser General Public
11 : : * License as published by the Free Software Foundation; either
12 : : * version 2.1 of the License, or (at your option) any later version.
13 : : *
14 : : */
15 : :
16 : : #include "ReadNASTRAN.hpp"
17 : :
18 : : #include <iostream>
19 : : #include <sstream>
20 : : #include <fstream>
21 : : #include <vector>
22 : : #include <cstdlib>
23 : : #include <assert.h>
24 : : #include <cmath>
25 : :
26 : : #include "moab/Interface.hpp"
27 : : #include "moab/ReadUtilIface.hpp"
28 : : #include "Internals.hpp" // For MB_START_ID
29 : : #include "moab/Range.hpp"
30 : : #include "moab/FileOptions.hpp"
31 : : #include "FileTokenizer.hpp"
32 : : #include "MBTagConventions.hpp"
33 : : #include "moab/CN.hpp"
34 : :
35 : : namespace moab
36 : : {
37 : :
38 : 1 : ReaderIface* ReadNASTRAN::factory( Interface* iface )
39 : : {
40 [ + - ]: 1 : return new ReadNASTRAN( iface );
41 : : }
42 : :
43 : : // Constructor
44 [ + - ][ + - ]: 2 : ReadNASTRAN::ReadNASTRAN( Interface* impl ) : MBI( impl )
45 : : {
46 [ - + ]: 1 : assert( NULL != impl );
47 [ + - ]: 1 : MBI->query_interface( readMeshIface );
48 [ - + ]: 1 : assert( NULL != readMeshIface );
49 : 1 : }
50 : :
51 : : // Destructor
52 : 3 : ReadNASTRAN::~ReadNASTRAN()
53 : : {
54 [ + - ]: 1 : if( readMeshIface )
55 : : {
56 : 1 : MBI->release_interface( readMeshIface );
57 : 1 : readMeshIface = 0;
58 : : }
59 [ - + ]: 2 : }
60 : :
61 : 0 : ErrorCode ReadNASTRAN::read_tag_values( const char* /*file_name*/, const char* /*tag_name*/,
62 : : const FileOptions& /*opts*/, std::vector< int >& /*tag_values_out*/,
63 : : const SubsetList* /*subset_list*/ )
64 : : {
65 : 0 : return MB_NOT_IMPLEMENTED;
66 : : }
67 : :
68 : : // Load the file as called by the Interface function
69 : 1 : ErrorCode ReadNASTRAN::load_file( const char* filename, const EntityHandle* /* file_set */,
70 : : const FileOptions& /* opts */, const ReaderIface::SubsetList* subset_list,
71 : : const Tag* file_id_tag )
72 : : {
73 : : // At this time there is no support for reading a subset of the file
74 [ - + ][ # # ]: 1 : if( subset_list ) { MB_SET_ERR( MB_UNSUPPORTED_OPERATION, "Reading subset of files not supported for NASTRAN" ); }
[ # # ][ # # ]
[ # # ][ # # ]
75 : :
76 [ + - ]: 1 : nodeIdMap.clear();
77 [ + - ]: 1 : elemIdMap.clear();
78 : :
79 : 1 : bool debug = false;
80 [ - + ][ # # ]: 1 : if( debug ) std::cout << "begin ReadNASTRAN::load_file" << std::endl;
[ # # ]
81 : : ErrorCode result;
82 : :
83 : : // Count the entities of each type in the file. This is used to allocate the node array.
84 : : int entity_count[MBMAXTYPE];
85 [ + + ]: 13 : for( int i = 0; i < MBMAXTYPE; i++ )
86 : 12 : entity_count[i] = 0;
87 : :
88 : : /* Determine the line_format of the first line. Assume that the entire file
89 : : has the same format. */
90 [ + - ]: 1 : std::string line;
91 [ + - ]: 2 : std::ifstream file( filename );
92 [ + - ][ + - ]: 1 : if( !getline( file, line ) ) return MB_FILE_DOES_NOT_EXIST;
[ - + ]
93 : : line_format format;
94 [ + - ]: 1 : result = determine_line_format( line, format );
95 [ - + ]: 1 : if( MB_SUCCESS != result ) return result;
96 : :
97 : : /* Count the number of each entity in the file. This allows one to allocate
98 : : a sequential array of vertex handles. */
99 [ + - ][ + - ]: 1 : while( !file.eof() )
100 : : {
101 : : // Cut the line into fields as determined by the line format.
102 : : // Use a vector to allow for an unknown number of tokens (continue lines).
103 : : // Continue lines are not implemented.
104 [ + - ]: 1 : std::vector< std::string > tokens;
105 [ + - ]: 1 : tokens.reserve( 10 ); // assume 10 fields to avoid extra vector resizing
106 [ + - ]: 1 : result = tokenize_line( line, format, tokens );
107 [ - + ]: 1 : if( MB_SUCCESS != result ) return result;
108 : :
109 : : // Process the tokens of the line. The first token describes the entity type.
110 : : EntityType type;
111 [ - + ][ # # ]: 1 : result = determine_entity_type( ( tokens.empty() ) ? "" : tokens.front(), type );
[ + - ][ + - ]
[ + - ][ - + ]
[ # # ]
112 [ + - ]: 1 : if( MB_SUCCESS != result ) return result;
113 : 0 : entity_count[type]++;
114 [ # # ][ - + ]: 1 : getline( file, line );
115 : 1 : }
116 : :
117 [ # # ]: 0 : if( debug )
118 : : {
119 [ # # ]: 0 : for( int i = 0; i < MBMAXTYPE; i++ )
120 : : {
121 [ # # ][ # # ]: 0 : std::cout << "entity_count[" << i << "]=" << entity_count[i] << std::endl;
[ # # ][ # # ]
[ # # ]
122 : : }
123 : : }
124 : :
125 : : // Keep list of material sets
126 [ # # ]: 0 : std::vector< Range > materials;
127 : :
128 : : // Now that the number of vertices is known, create the vertices.
129 : 0 : EntityHandle start_vert = 0;
130 [ # # ]: 0 : std::vector< double* > coord_arrays( 3 );
131 [ # # ]: 0 : result = readMeshIface->get_node_coords( 3, entity_count[0], MB_START_ID, start_vert, coord_arrays );
132 [ # # ]: 0 : if( MB_SUCCESS != result ) return result;
133 [ # # ]: 0 : if( 0 == start_vert ) return MB_FAILURE; // check for NULL
134 : 0 : int id, vert_index = 0;
135 [ # # ][ # # ]: 0 : if( debug ) std::cout << "allocated coord arrays" << std::endl;
[ # # ]
136 : :
137 : : // Read the file again to create the entities.
138 [ # # ]: 0 : file.clear(); // Clear eof state from object
139 [ # # ][ # # ]: 0 : file.seekg( 0 ); // Rewind file
140 [ # # ][ # # ]: 0 : while( !file.eof() )
141 : : {
142 [ # # ]: 0 : getline( file, line );
143 : :
144 : : // Cut the line into fields as determined by the line format.
145 : : // Use a vector to allow for an unknown number of tokens (continue lines).
146 : : // Continue lines are not implemented.
147 [ # # ]: 0 : std::vector< std::string > tokens;
148 [ # # ]: 0 : tokens.reserve( 10 ); // assume 10 fields to avoid extra vector resizing
149 [ # # ]: 0 : result = tokenize_line( line, format, tokens );
150 [ # # ]: 0 : if( MB_SUCCESS != result ) return result;
151 : :
152 : : // Process the tokens of the line. The first token describes the entity type.
153 : : EntityType type;
154 [ # # ][ # # ]: 0 : result = determine_entity_type( tokens.front(), type );
155 [ # # ]: 0 : if( MB_SUCCESS != result ) return result;
156 : :
157 : : // Create the entity.
158 [ # # ]: 0 : if( MBVERTEX == type )
159 : : {
160 [ # # ][ # # ]: 0 : double* coords[3] = { coord_arrays[0] + vert_index, coord_arrays[1] + vert_index,
161 [ # # ]: 0 : coord_arrays[2] + vert_index };
162 [ # # ]: 0 : result = read_node( tokens, debug, coords, id );
163 [ # # ]: 0 : if( MB_SUCCESS != result ) return result;
164 [ # # ][ # # ]: 0 : if( !nodeIdMap.insert( id, start_vert + vert_index, 1 ).second ) return MB_FAILURE; // Duplicate IDs!
165 : 0 : ++vert_index;
166 : : }
167 : : else
168 : : {
169 [ # # ]: 0 : result = read_element( tokens, materials, type, debug );
170 [ # # ][ # # ]: 0 : if( MB_SUCCESS != result ) return result;
171 : : }
172 : 0 : }
173 : :
174 [ # # ]: 0 : result = create_materials( materials );
175 [ # # ]: 0 : if( MB_SUCCESS != result ) return result;
176 : :
177 [ # # ]: 0 : result = assign_ids( file_id_tag );
178 [ # # ]: 0 : if( MB_SUCCESS != result ) return result;
179 : :
180 [ # # ]: 0 : file.close();
181 [ # # ]: 0 : nodeIdMap.clear();
182 [ # # ]: 0 : elemIdMap.clear();
183 : 1 : return MB_SUCCESS;
184 : : }
185 : :
186 : : /* Determine the type of NASTRAN line: small field, large field, or free field.
187 : : small field: each line has 10 fields of 8 characters
188 : : large field: 1x8, 4x16, 1x8. Field 1 must have an asterisk following the character string
189 : : free field: each line entry must be separated by a comma
190 : : Implementation tries to avoid more searches than necessary. */
191 : 1 : ErrorCode ReadNASTRAN::determine_line_format( const std::string& line, line_format& format )
192 : : {
193 : 1 : std::string::size_type found_asterisk = line.find( "*" );
194 [ - + ]: 1 : if( std::string::npos != found_asterisk )
195 : : {
196 : 0 : format = LARGE_FIELD;
197 : 0 : return MB_SUCCESS;
198 : : }
199 : : else
200 : : {
201 : 1 : std::string::size_type found_comma = line.find( "," );
202 [ - + ]: 1 : if( std::string::npos != found_comma )
203 : : {
204 : 0 : format = FREE_FIELD;
205 : 0 : return MB_SUCCESS;
206 : : }
207 : : else
208 : : {
209 : 1 : format = SMALL_FIELD;
210 : 1 : return MB_SUCCESS;
211 : : }
212 : : }
213 : : }
214 : :
215 : : /* Tokenize the line. Continue-lines have not been implemented. */
216 : 1 : ErrorCode ReadNASTRAN::tokenize_line( const std::string& line, const line_format format,
217 : : std::vector< std::string >& tokens )
218 : : {
219 : 1 : size_t line_size = line.size();
220 [ + - - - ]: 1 : switch( format )
221 : : {
222 : : case SMALL_FIELD: {
223 : : // Expect 10 fields of 8 characters.
224 : : // The sample file does not have all 10 fields in each line
225 : 1 : const int field_length = 8;
226 : 1 : unsigned int n_tokens = line_size / field_length;
227 [ + + ]: 83 : for( unsigned int i = 0; i < n_tokens; i++ )
228 : : {
229 [ + - ]: 82 : tokens.push_back( line.substr( i * field_length, field_length ) );
230 : : }
231 : 1 : break;
232 : : }
233 : : case LARGE_FIELD:
234 : 0 : return MB_NOT_IMPLEMENTED;
235 : : case FREE_FIELD:
236 : 0 : return MB_NOT_IMPLEMENTED;
237 : : default:
238 : 0 : return MB_FAILURE;
239 : : }
240 : :
241 : 1 : return MB_SUCCESS;
242 : : }
243 : :
244 : 1 : ErrorCode ReadNASTRAN::determine_entity_type( const std::string& first_token, EntityType& type )
245 : : {
246 [ - + ]: 1 : if( 0 == first_token.compare( "GRID " ) )
247 : 0 : type = MBVERTEX;
248 [ - + ]: 1 : else if( 0 == first_token.compare( "CTETRA " ) )
249 : 0 : type = MBTET;
250 [ - + ]: 1 : else if( 0 == first_token.compare( "CPENTA " ) )
251 : 0 : type = MBPRISM;
252 [ - + ]: 1 : else if( 0 == first_token.compare( "CHEXA " ) )
253 : 0 : type = MBHEX;
254 : : else
255 : 1 : return MB_NOT_IMPLEMENTED;
256 : :
257 : 0 : return MB_SUCCESS;
258 : : }
259 : :
260 : : /* Some help from Jason:
261 : : Nastran floats must contain a decimal point, may contain
262 : : a leading '-' and may contain an exponent. The 'E' is optional
263 : : when specifying an exponent. A '-' or '+' at any location other
264 : : than the first position indicates an exponent. For a positive
265 : : exponent, either a '+' or an 'E' must be specified. For a
266 : : negative exponent, the 'E' is option and the '-' is always specified.
267 : : Examples for the real value 7.0 from mcs2006 quick reference guide:
268 : : 7.0 .7E1 0.7+1 .70+1 7.E+0 70.-1
269 : :
270 : : From the test file created in SC/Tetra:
271 : : GRID 1 03.9804546.9052-15.6008-1
272 : : has the coordinates: ( 3.980454, 6.9052e-1, 5.6008e-1 )
273 : : GRID 200005 04.004752-3.985-15.4955-1
274 : : has the coordinates: ( 4.004752, -3.985e-1, 5.4955e-1 ) */
275 : 0 : ErrorCode ReadNASTRAN::get_real( const std::string& token, double& real )
276 : : {
277 [ # # ]: 0 : std::string significand = token;
278 [ # # ]: 0 : std::string exponent = "0";
279 : :
280 : : // Cut off the first digit because a "-" could be here indicating a negative
281 : : // number. Instead we are looking for a negative exponent.
282 [ # # ]: 0 : std::string back_token = token.substr( 1 );
283 : :
284 : : // A minus that is not the first digit is always a negative exponent
285 [ # # ]: 0 : std::string::size_type found_minus = back_token.find( "-" );
286 [ # # ]: 0 : if( std::string::npos != found_minus )
287 : : {
288 : : // separate the significand from the exponent at the "-"
289 [ # # ][ # # ]: 0 : exponent = token.substr( found_minus + 1 );
290 [ # # ][ # # ]: 0 : significand = token.substr( 0, found_minus + 1 );
291 : :
292 : : // If the significand has an "E", remove it
293 [ # # ][ # # ]: 0 : if( std::string::npos != significand.find( "E" ) )
294 : : // Assume the "E" is at the end of the significand.
295 [ # # ][ # # ]: 0 : significand = significand.substr( 1, significand.size() - 2 );
296 : :
297 : : // If a minus does not exist past the 1st digit, but an "E" or "+" does, then
298 : : // it is a positive exponent. First look for an "E",
299 : : }
300 : : else
301 : : {
302 [ # # ]: 0 : std::string::size_type found_E = token.find( "E" );
303 [ # # ]: 0 : if( std::string::npos != found_E )
304 : : {
305 [ # # ][ # # ]: 0 : significand = token.substr( 0, found_E - 1 );
306 [ # # ][ # # ]: 0 : exponent = token.substr( found_E + 1 );
307 : : // If there is a "+" on the exponent, cut it off
308 [ # # ]: 0 : std::size_t found_plus = exponent.find( "+" );
309 [ # # ][ # # ]: 0 : if( std::string::npos != found_plus ) { exponent = exponent.substr( found_plus + 1 ); }
[ # # ]
310 : : }
311 : : else
312 : : {
313 : : // If there is a "+" on the exponent, cut it off
314 [ # # ]: 0 : std::size_t found_plus = token.find( "+" );
315 [ # # ]: 0 : if( std::string::npos != found_plus )
316 : : {
317 [ # # ][ # # ]: 0 : significand = token.substr( 0, found_plus - 1 );
318 [ # # ][ # # ]: 0 : exponent = token.substr( found_plus + 1 );
319 : : }
320 : : }
321 : : }
322 : :
323 : : // Now assemble the real number
324 : 0 : double signi = atof( significand.c_str() );
325 : 0 : double expon = atof( exponent.c_str() );
326 : :
327 [ # # ][ # # ]: 0 : if( HUGE_VAL == signi || HUGE_VAL == expon ) return MB_FAILURE;
328 : :
329 : 0 : real = signi * pow( 10, expon );
330 : :
331 : 0 : return MB_SUCCESS;
332 : : }
333 : :
334 : : /* It has been determined that this line is a vertex. Read the rest of
335 : : the line and create the vertex. */
336 : 0 : ErrorCode ReadNASTRAN::read_node( const std::vector< std::string >& tokens, const bool debug, double* coords[3],
337 : : int& id )
338 : : {
339 : : // Read the node's id (unique)
340 : : ErrorCode result;
341 : 0 : id = atoi( tokens[1].c_str() );
342 : :
343 : : // Read the node's coordinate system number
344 : : // "0" or blank refers to the basic coordinate system.
345 : 0 : int coord_system = atoi( tokens[2].c_str() );
346 [ # # ]: 0 : if( 0 != coord_system )
347 : : {
348 : 0 : std::cerr << "ReadNASTRAN: alternative coordinate systems not implemented" << std::endl;
349 : 0 : return MB_NOT_IMPLEMENTED;
350 : : }
351 : :
352 : : // Read the coordinates
353 [ # # ]: 0 : for( unsigned int i = 0; i < 3; i++ )
354 : : {
355 : 0 : result = get_real( tokens[i + 3], *coords[i] );
356 [ # # ]: 0 : if( MB_SUCCESS != result ) return result;
357 [ # # ]: 0 : if( debug ) std::cout << "read_node: coords[" << i << "]=" << coords[i] << std::endl;
358 : : }
359 : :
360 : 0 : return MB_SUCCESS;
361 : : }
362 : :
363 : : /* The type of element has already been identified. Read the rest of the
364 : : line and create the element. Assume that all of the nodes have already
365 : : been created. */
366 : 0 : ErrorCode ReadNASTRAN::read_element( const std::vector< std::string >& tokens, std::vector< Range >& materials,
367 : : const EntityType element_type, const bool /*debug*/ )
368 : : {
369 : : // Read the element's id (unique) and material set
370 : : ErrorCode result;
371 [ # # ]: 0 : int id = atoi( tokens[1].c_str() );
372 [ # # ]: 0 : int material = atoi( tokens[2].c_str() );
373 : :
374 : : // Resize materials list if necessary. This code is somewhat complicated
375 : : // so as to avoid copying of Ranges
376 [ # # ]: 0 : if( material >= (int)materials.size() )
377 : : {
378 [ # # ]: 0 : if( (int)materials.capacity() < material )
379 [ # # ]: 0 : materials.resize( material + 1 );
380 : : else
381 : : {
382 [ # # ]: 0 : std::vector< Range > new_mat( material + 1 );
383 [ # # ]: 0 : for( size_t i = 0; i < materials.size(); ++i )
384 [ # # ][ # # ]: 0 : new_mat[i].swap( materials[i] );
[ # # ]
385 : 0 : materials.swap( new_mat );
386 : : }
387 : : }
388 : :
389 : : // The size of the connectivity array depends on the element type
390 [ # # ]: 0 : int n_conn = CN::VerticesPerEntity( element_type );
391 : : EntityHandle conn_verts[27];
392 [ # # ]: 0 : assert( n_conn <= (int)( sizeof( conn_verts ) / sizeof( EntityHandle ) ) );
393 : :
394 : : // Read the connected node ids from the file
395 [ # # ]: 0 : for( int i = 0; i < n_conn; i++ )
396 : : {
397 [ # # ]: 0 : int n = atoi( tokens[3 + i].c_str() );
398 [ # # ]: 0 : conn_verts[i] = nodeIdMap.find( n );
399 [ # # ]: 0 : if( !conn_verts[i] ) // invalid vertex id
400 : 0 : return MB_FAILURE;
401 : : }
402 : :
403 : : // Create the element and set the global id from the NASTRAN file
404 : : EntityHandle element;
405 [ # # ]: 0 : result = MBI->create_element( element_type, conn_verts, n_conn, element );
406 [ # # ]: 0 : if( MB_SUCCESS != result ) return result;
407 [ # # ]: 0 : elemIdMap.insert( id, element, 1 );
408 : :
409 [ # # ][ # # ]: 0 : materials[material].insert( element );
410 : 0 : return MB_SUCCESS;
411 : : }
412 : :
413 : 0 : ErrorCode ReadNASTRAN::create_materials( const std::vector< Range >& materials )
414 : : {
415 : : ErrorCode result;
416 : : Tag material_tag;
417 : 0 : int negone = -1;
418 : : result = MBI->tag_get_handle( MATERIAL_SET_TAG_NAME, 1, MB_TYPE_INTEGER, material_tag, MB_TAG_SPARSE | MB_TAG_CREAT,
419 [ # # ]: 0 : &negone );
420 [ # # ]: 0 : if( MB_SUCCESS != result ) return result;
421 : :
422 [ # # ]: 0 : for( size_t i = 0; i < materials.size(); ++i )
423 : : {
424 [ # # ][ # # ]: 0 : if( materials[i].empty() ) continue;
[ # # ]
425 : :
426 : : // Merge with existing or create new? Original code effectively
427 : : // created new by only merging with existing in current file set,
428 : : // so do the same here. - j.kraftcheck
429 : :
430 : : EntityHandle handle;
431 [ # # ]: 0 : result = MBI->create_meshset( MESHSET_SET, handle );
432 [ # # ]: 0 : if( MB_SUCCESS != result ) return result;
433 : :
434 [ # # ][ # # ]: 0 : result = MBI->add_entities( handle, materials[i] );
435 [ # # ]: 0 : if( MB_SUCCESS != result ) return result;
436 : :
437 : 0 : int id = i;
438 [ # # ]: 0 : result = MBI->tag_set_data( material_tag, &handle, 1, &id );
439 [ # # ]: 0 : if( MB_SUCCESS != result ) return result;
440 : : }
441 : :
442 : 0 : return MB_SUCCESS;
443 : : }
444 : :
445 : 0 : ErrorCode ReadNASTRAN::assign_ids( const Tag* file_id_tag )
446 : : {
447 : : // Create tag
448 : : ErrorCode result;
449 [ # # ]: 0 : Tag id_tag = MBI->globalId_tag();
450 : :
451 : 0 : RangeMap< int, EntityHandle >::iterator i;
452 [ # # ]: 0 : for( int t = 0; t < 2; ++t )
453 : : {
454 [ # # ]: 0 : RangeMap< int, EntityHandle >& fileIdMap = t ? elemIdMap : nodeIdMap;
455 [ # # ][ # # ]: 0 : for( i = fileIdMap.begin(); i != fileIdMap.end(); ++i )
[ # # ][ # # ]
[ # # ]
456 : : {
457 [ # # ][ # # ]: 0 : Range range( i->value, i->value + i->count - 1 );
[ # # ][ # # ]
458 : :
459 [ # # ][ # # ]: 0 : result = readMeshIface->assign_ids( id_tag, range, i->begin );
460 [ # # ]: 0 : if( MB_SUCCESS != result ) return result;
461 : :
462 [ # # ][ # # ]: 0 : if( file_id_tag && *file_id_tag != id_tag )
463 : : {
464 [ # # ][ # # ]: 0 : result = readMeshIface->assign_ids( *file_id_tag, range, i->begin );
465 [ # # ][ # # ]: 0 : if( MB_SUCCESS != result ) return result;
466 : : }
467 : 0 : }
468 : : }
469 : :
470 : 0 : return MB_SUCCESS;
471 : : }
472 : :
473 [ + - ][ + - ]: 228 : } // namespace moab
|