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