NASsurfaceFormat.C
Go to the documentation of this file.
1/*---------------------------------------------------------------------------*\
2 ========= |
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4 \\ / O peration |
5 \\ / A nd | www.openfoam.com
6 \\/ M anipulation |
7-------------------------------------------------------------------------------
8 Copyright (C) 2011-2015 OpenFOAM Foundation
9 Copyright (C) 2017-2020 OpenCFD Ltd.
10-------------------------------------------------------------------------------
11License
12 This file is part of OpenFOAM.
13
14 OpenFOAM is free software: you can redistribute it and/or modify it
15 under the terms of the GNU General Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26
27\*---------------------------------------------------------------------------*/
28
29#include "NASsurfaceFormat.H"
30#include "ListOps.H"
31#include "IFstream.H"
32#include "IOmanip.H"
33#include "faceTraits.H"
34
35// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
36
37template<class Face>
39(
40 Ostream& os,
41 const Face& f,
42 label elemId,
43 const label groupId
44)
45{
46 const label n = f.size();
47
48 if (n == 3)
49 {
50 os << "CTRIA3" << ','
51 << (++elemId) << ','
52 << (groupId + 1) << ','
53 << (f[0] + 1) << ','
54 << (f[1] + 1) << ','
55 << (f[2] + 1) << nl;
56 }
57 else if (n == 4)
58 {
59 os << "CQUAD4" << ','
60 << (++elemId) << ','
61 << (groupId + 1) << ','
62 << (f[0] + 1) << ','
63 << (f[1] + 1) << ','
64 << (f[2] + 1) << ','
65 << (f[3] + 1) << nl;
66 }
67 else
68 {
69 // simple triangulation about f[0].
70 // better triangulation should have been done before
71 for (label fp1 = 1; fp1 < f.size() - 1; ++fp1)
72 {
73 const label fp2 = f.fcIndex(fp1);
74
75 os << "CTRIA3" << ','
76 << (++elemId) << ','
77 << (groupId + 1) << ','
78 << (f[0] + 1) << ','
79 << (f[fp1] + 1) << ','
80 << (f[fp2] + 1) << nl;
81 }
82 }
83
84 return elemId;
85}
86
87
88// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
89
90template<class Face>
92(
93 const fileName& filename
94)
95{
96 read(filename);
97}
98
99
100// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
101
102template<class Face>
104(
105 const fileName& filename
106)
107{
108 // Clear everything
109 this->clear();
110
111 IFstream is(filename);
112 if (!is.good())
113 {
115 << "Cannot read file " << filename << nl
116 << exit(FatalError);
117 }
118
119 DynamicList<label> pointId; // Nastran point id (1-based)
120 DynamicList<point> dynPoints;
121
122 DynamicList<label> dynElemId; // Nastran element id (1-based)
123 DynamicList<Face> dynFaces;
124 DynamicList<label> dynZones;
125 DynamicList<label> dynSizes;
126
127 Map<label> zoneLookup;
128
129 // Assume that the groups are not intermixed
130 label zoneId = 0;
131 bool sorted = true;
132
133 // Element id gets trashed with decompose into a triangle!
134 bool ignoreElemId = false;
135
136 // Name for face group
137 Map<word> nameLookup;
138
139 // Ansa tags. Denoted by $ANSA_NAME.
140 // These will appear just before the first use of a type.
141 // We read them and store the PSHELL types which are used to name
142 // the zones.
143 label ansaId = -1;
144 word ansaType, ansaName;
145
146 // A single warning per unrecognized command
147 wordHashSet unhandledCmd;
148
149 string line;
150 while (is.good())
151 {
152 string::size_type linei = 0; // Parsing position within current line
153 is.getLine(line);
154
155 // ANSA extension
156 // line 1: $ANSA_NAME;<int>;<word>;
157 // line 2: $partName
158 if (line.starts_with("$ANSA_NAME"))
159 {
160 const auto sem0 = line.find(';', 0);
161 const auto sem1 = line.find(';', sem0+1);
162 const auto sem2 = line.find(';', sem1+1);
163
164 if
165 (
166 sem0 != std::string::npos
167 && sem1 != std::string::npos
168 && sem2 != std::string::npos
169 )
170 {
171 ansaId = readLabel(line.substr(sem0+1, sem1-sem0-1));
172 ansaType = line.substr(sem1+1, sem2-sem1-1);
173
174 string rawName;
175 is.getLine(rawName);
176 rawName.removeEnd('\r'); // Possible CR-NL
177 ansaName = word::validate(rawName.substr(1));
178
179 // Info<< "ANSA tag for NastranID:" << ansaId
180 // << " of type " << ansaType
181 // << " name " << ansaName << endl;
182 }
183 }
184
185
186 // HYPERMESH extension
187 // $HMNAME COMP 1"partName"
188 if (line.starts_with("$HMNAME COMP") && line.find('"') != string::npos)
189 {
190 label groupId = readLabel(line.substr(16, 16));
191
192 // word::validate automatically removes quotes too
193 const word groupName = word::validate(line.substr(32));
194
195 nameLookup.insert(groupId, groupName);
196 // Info<< "group " << groupId << " => " << groupName << endl;
197 }
198
199 if (line.empty() || line[0] == '$')
200 {
201 continue; // Skip empty or comment
202 }
203
204 // Check if character 72 is continuation
205 if (line.size() > 72 && line[72] == '+')
206 {
207 line.resize(72);
208
209 while (true)
210 {
211 string buf;
212 is.getLine(buf);
213
214 if (buf.size() > 72 && buf[72] == '+')
215 {
216 line += buf.substr(8, 64);
217 }
218 else
219 {
220 line += buf.substr(8);
221 break;
222 }
223 }
224 }
225
226 // First word (column 0-8)
227 const word cmd(word::validate(nextNasField(line, linei, 8)));
228
229 if (cmd == "CTRIA3")
230 {
231 label elemId = readLabel(nextNasField(line, linei, 8)); // 8-16
232 label groupId = readLabel(nextNasField(line, linei, 8)); // 16-24
233 const auto a = readLabel(nextNasField(line, linei, 8)); // 24-32
234 const auto b = readLabel(nextNasField(line, linei, 8)); // 32-40
235 const auto c = readLabel(nextNasField(line, linei, 8)); // 40-48
236
237 // Convert groupId into zoneId
238 const auto iterZone = zoneLookup.cfind(groupId);
239 if (iterZone.found())
240 {
241 if (zoneId != *iterZone)
242 {
243 // pshell types are intermixed
244 sorted = false;
245 }
246 zoneId = *iterZone;
247 }
248 else
249 {
250 zoneId = dynSizes.size();
251 zoneLookup.insert(groupId, zoneId);
252 dynSizes.append(0);
253 // Info<< "zone" << zoneId << " => group " << groupId <<nl;
254 }
255
256 --elemId; // Convert 1-based -> 0-based
257 dynElemId.append(elemId);
258 dynFaces.append(Face{a, b, c});
259 dynZones.append(zoneId);
260 dynSizes[zoneId]++;
261 }
262 else if (cmd == "CQUAD4")
263 {
264 label elemId = readLabel(nextNasField(line, linei, 8)); // 8-16
265 label groupId = readLabel(nextNasField(line, linei, 8)); // 16-24
266 const auto a = readLabel(nextNasField(line, linei, 8)); // 24-32
267 const auto b = readLabel(nextNasField(line, linei, 8)); // 32-40
268 const auto c = readLabel(nextNasField(line, linei, 8)); // 40-48
269 const auto d = readLabel(nextNasField(line, linei, 8)); // 48-56
270
271 // Convert groupId into zoneId
272 const auto iterZone = zoneLookup.cfind(groupId);
273 if (iterZone.found())
274 {
275 if (zoneId != *iterZone)
276 {
277 // pshell types are intermixed
278 sorted = false;
279 }
280 zoneId = *iterZone;
281 }
282 else
283 {
284 zoneId = dynSizes.size();
285 zoneLookup.insert(groupId, zoneId);
286 dynSizes.append(0);
287 // Info<< "zone" << zoneId << " => group " << groupId <<nl;
288 }
289
291 {
292 ignoreElemId = true;
293 dynElemId.clear();
294
295 dynFaces.append(Face{a, b, c});
296 dynFaces.append(Face{c, d, a});
297 dynZones.append(zoneId);
298 dynZones.append(zoneId);
299 dynSizes[zoneId] += 2;
300 }
301 else
302 {
303 --elemId; // Convert 1-based -> 0-based
304
305 dynElemId.append(elemId);
306 dynFaces.append(Face{a,b,c,d});
307 dynZones.append(zoneId);
308 dynSizes[zoneId]++;
309 }
310 }
311 else if (cmd == "GRID")
312 {
313 label index = readLabel(nextNasField(line, linei, 8)); // 8-16
314 (void) nextNasField(line, linei, 8); // 16-24
315 scalar x = readNasScalar(nextNasField(line, linei, 8)); // 24-32
316 scalar y = readNasScalar(nextNasField(line, linei, 8)); // 32-40
317 scalar z = readNasScalar(nextNasField(line, linei, 8)); // 40-48
318
319 pointId.append(index);
320 dynPoints.append(point(x, y, z));
321 }
322 else if (cmd == "GRID*")
323 {
324 // Long format is on two lines with '*' continuation symbol
325 // on start of second line.
326 // Typical line (spaces compacted)
327 // GRID* 126 0 -5.55999875E+02 -5.68730474E+02
328 // * 2.14897901E+02
329
330 label index = readLabel(nextNasField(line, linei, 16)); // 8-24
331 (void) nextNasField(line, linei, 16); // 24-40
332 scalar x = readNasScalar(nextNasField(line, linei, 16)); // 40-56
333 scalar y = readNasScalar(nextNasField(line, linei, 16)); // 56-72
334
335 linei = 0; // restart at index 0
336 is.getLine(line);
337 if (line[0] != '*')
338 {
340 << "Expected continuation symbol '*' when reading GRID*"
341 << " (double precision coordinate) format" << nl
342 << "Read:" << line << nl
343 << "File:" << is.name() << " line:" << is.lineNumber()
344 << exit(FatalError);
345 }
346 (void) nextNasField(line, linei, 8); // 0-8
347 scalar z = readNasScalar(nextNasField(line, linei, 16)); // 8-16
348
349 pointId.append(index);
350 dynPoints.append(point(x, y, z));
351 }
352 else if (cmd == "PSHELL")
353 {
354 // Read shell type since group gives patchnames (ANSA extension)
355 label groupId = readLabel(nextNasField(line, linei, 8)); // 8-16
356 if (groupId == ansaId && ansaType == "PSHELL")
357 {
358 const word groupName = word::validate(ansaName);
359 nameLookup.insert(groupId, groupName);
360 // Info<< "group " << groupId << " => " << groupName << endl;
361 }
362 }
363 else if (unhandledCmd.insert(cmd))
364 {
365 Info<< "Unhandled Nastran command " << line << nl
366 << "File:" << is.name() << " line:" << is.lineNumber()
367 << endl;
368 }
369 }
370
371 // Info<< "Read faces:" << dynFaces.size()
372 // << " points:" << dynPoints.size()
373 // << endl;
374
375 if (ignoreElemId)
376 {
377 dynElemId.clear();
378 }
379
380 // Transfer to normal lists
381 this->storedPoints().transfer(dynPoints);
382
383 pointId.shrink();
384 dynFaces.shrink();
385
386 // Build inverse mapping (NASTRAN pointId -> index)
387 Map<label> mapPointId(2*pointId.size());
388 forAll(pointId, i)
389 {
390 mapPointId.insert(pointId[i], i);
391 }
392
393 // Relabel faces
394 // ~~~~~~~~~~~~~
395 for (Face& f : dynFaces)
396 {
397 for (label& vert : f)
398 {
399 vert = mapPointId[vert];
400 }
401 }
402 pointId.clearStorage();
403 mapPointId.clear();
404
405
406 // Create default zone names, or from ANSA/Hypermesh information
407 List<word> names(dynSizes.size());
408 forAllConstIters(zoneLookup, iter)
409 {
410 const label groupId = iter.key();
411 const label zoneId = iter.val();
412
413 const auto iterName = nameLookup.cfind(groupId);
414 if (iterName.found())
415 {
416 names[zoneId] = *iterName;
417 }
418 else
419 {
420 names[zoneId] = surfZone::defaultName(zoneId);
421 }
422 }
423
424 this->sortFacesAndStore(dynFaces, dynZones, dynElemId, sorted);
425
426 // Add zones (retaining empty ones)
427 this->addZones(dynSizes, names);
428 this->addZonesToFaces(); // for labelledTri
429
430 return true;
431}
432
433
434template<class Face>
436(
437 const fileName& filename,
438 const MeshedSurfaceProxy<Face>& surf,
439 IOstreamOption streamOpt,
440 const dictionary&
441)
442{
443 // ASCII only, allow output compression
444 streamOpt.format(IOstream::ASCII);
445
446 const UList<point>& pointLst = surf.points();
447 const UList<Face>& faceLst = surf.surfFaces();
448 const UList<label>& faceMap = surf.faceMap();
449 const UList<label>& elemIds = surf.faceIds();
450
451 // for no zones, suppress the group name
452 const surfZoneList zones =
453 (
454 surf.surfZones().empty()
455 ? surfaceFormatsCore::oneZone(faceLst, "")
456 : surf.surfZones()
457 );
458
459 const bool useFaceMap = (surf.useFaceMap() && zones.size() > 1);
460
461 // Possible to use faceIds?
462 // - cannot if there are negative ids (eg, encoded solid/side)
463 bool useOrigFaceIds =
464 (
465 !useFaceMap
466 && elemIds.size() == faceLst.size()
467 && !ListOps::found(elemIds, lessOp1<label>(0))
468 );
469
470 // Not possible with on-the-fly face decomposition
471 if (useOrigFaceIds)
472 {
473 for (const auto& f : faceLst)
474 {
475 if (f.size() > 4)
476 {
477 useOrigFaceIds = false;
478 break;
479 }
480 }
481 }
482
483
484 OFstream os(filename, streamOpt);
485 if (!os.good())
486 {
488 << "Cannot write file " << filename << nl
489 << exit(FatalError);
490 }
491
492 // For simplicity, use fieldFormat::FREE throughout
493 fileFormats::NASCore::setPrecision(os, fieldFormat::FREE);
494
495 os << "CEND" << nl
496 << "TITLE = " << os.name().nameLessExt() << nl;
497
498 // Print zone names as comment
499 forAll(zones, zonei)
500 {
501 // HYPERMESH extension
502 os << "$HMNAME COMP" << setw(20) << (zonei+1)
503 << '"' << zones[zonei].name() << '"' << nl;
504 }
505
506 // Write vertex coords with 1-based point Id
507 os << "$ GRID POINTS" << nl
508 << "BEGIN BULK" << nl;
509
510 label pointId = 0;
511 for (const point& pt : pointLst)
512 {
513 os << "GRID" << ','
514 << ++pointId << ','
515 << 0 << ',' // global coordinate system
516 << pt.x() << ',' << pt.y() << ',' << pt.z() << nl;
517 }
518
519 os << "$ ELEMENTS" << nl;
520
521 label faceIndex = 0;
522 label zoneIndex = 0;
523 label elemId = 0;
524
525 for (const surfZone& zone : zones)
526 {
527 for (label nLocal = zone.size(); nLocal--; ++faceIndex)
528 {
529 const label facei =
530 (useFaceMap ? faceMap[faceIndex] : faceIndex);
531
532 const Face& f = faceLst[facei];
533
534 if (useOrigFaceIds)
535 {
536 elemId = elemIds[facei];
537 }
538
539 elemId = writeShell(os, f, elemId, zoneIndex);
540 }
541
542 ++zoneIndex;
543 }
544
545 os << "ENDDATA" << nl;
546}
547
548
549// ************************************************************************* //
Istream and Ostream manipulators taking arguments.
scalar y
Various functions to operate on Lists.
label n
label size_type
The type to represent the size of a buffer.
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects.
Definition: DynamicList.H:72
void clear() noexcept
Clear the addressed list, i.e. set the size to zero.
Definition: DynamicListI.H:391
DynamicList< T, SizeMin > & shrink()
Shrink the allocated space to the number of elements used.
Definition: DynamicListI.H:434
void append(const T &val)
Copy append an element to the end of this list.
Definition: DynamicListI.H:503
void clearStorage()
Clear the list and delete storage.
Definition: DynamicListI.H:398
bool insert(const Key &key)
Insert a new entry, not overwriting existing entries.
Definition: HashSet.H:191
const_iterator cfind(const Key &key) const
Find and return an const_iterator set at the hashed entry.
Definition: HashTableI.H:141
bool insert(const Key &key, const T &obj)
Copy insert a new entry, not overwriting existing entries.
Definition: HashTableI.H:180
void clear()
Clear all entries from table.
Definition: HashTable.C:678
Input from file stream, using an ISstream.
Definition: IFstream.H:57
virtual const fileName & name() const
Read/write access to the name of the stream.
Definition: ISstream.H:113
The IOstreamOption is a simple container for options an IOstream can normally have.
streamFormat format() const noexcept
Get the current stream format.
label lineNumber() const noexcept
Const access to the current stream line number.
Definition: IOstream.H:318
bool good() const noexcept
True if next operation might succeed.
Definition: IOstream.H:233
ISstream & getLine(std::string &str, char delim='\n')
Raw, low-level getline (until delimiter) into a string.
Definition: ISstreamI.H:76
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: List.H:77
A HashTable to objects of type <T> with a label key.
Definition: Map.H:60
A proxy for writing MeshedSurface, UnsortedMeshedSurface and surfMesh to various file formats.
const UList< surfZone > & surfZones() const
Const access to the surface zones.
const UList< Face > & surfFaces() const
Return const access to the faces.
bool useFaceMap() const
Can/should use faceMap?
const labelUList & faceIds() const
Const access to the faceIds, zero-sized when unused.
const pointField & points() const
Return const access to the points.
const labelUList & faceMap() const
Const access to the faceMap, zero-sized when unused.
Output to file stream, using an OSstream.
Definition: OFstream.H:57
virtual bool read()
Re-read model coefficients if they have changed.
A 1D vector of objects of type <T>, where the size of the vector is known and can be used for subscri...
Definition: UList.H:94
bool empty() const noexcept
True if the UList is empty (ie, size() is zero)
Definition: UListI.H:427
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:114
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition: dictionary.H:126
Traits class for faces.
Definition: faceTraits.H:51
Nastran surface reader/writer.
A class for handling file names.
Definition: fileName.H:76
virtual bool write()
Write the output fields.
A line primitive.
Definition: line.H:68
bool removeEnd(const std::string &text)
Remove the given text from the end of the string.
Definition: string.C:229
A surface zone on a MeshedSurface.
Definition: surfZone.H:59
A class for handling words, derived from Foam::string.
Definition: word.H:68
Base class for mesh zones.
Definition: zone.H:67
patchWriters clear()
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:453
OBJstream os(runTime.globalPath()/outputName)
Pair< int > faceMap(const label facePi, const face &faceP, const label faceNi, const face &faceN)
label readLabel(const char *buf)
Parse entire buffer as a label, skipping leading/trailing whitespace.
Definition: label.H:66
messageStream Info
Information stream (stdout output on master, null elsewhere)
Omanip< int > setw(const int i)
Definition: IOmanip.H:199
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:372
error FatalError
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for INVALID.
Definition: exprTraits.C:59
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
constexpr char nl
The newline '\n' character (0x0a)
Definition: Ostream.H:53
labelList f(nPoints)
volScalarField & b
Definition: createFields.H:27
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:333
#define forAllConstIters(container, iter)
Iterate across all elements of the container object with const access.
Definition: stdFoam.H:278