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 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  const label groupId,
42  label elementId
43 )
44 {
45  const label n = f.size();
46 
47  if (n == 3)
48  {
49  os << "CTRIA3" << ','
50  << ++elementId << ','
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 << "CTRIA3" << ','
59  << ++elementId << ','
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  << ++elementId << ','
76  << (groupId + 1) << ','
77  << (f[0] + 1) << ','
78  << (f[fp1] + 1) << ','
79  << (f[fp2] + 1) << nl;
80  }
81  }
82 
83  return elementId;
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  this->clear();
108 
109  IFstream is(filename);
110  if (!is.good())
111  {
113  << "Cannot read file " << filename
114  << exit(FatalError);
115  }
116 
117  // Nastran index of points
118  DynamicList<label> pointId;
119  DynamicList<point> dynPoints;
120  DynamicList<Face> dynFaces;
121  DynamicList<label> dynZones;
122  DynamicList<label> dynSizes;
123 
124  Map<label> zoneLookup;
125 
126  // Assume that the groups are not intermixed
127  label zoneId = 0;
128  bool sorted = true;
129 
130  // Name for face group
131  Map<word> nameLookup;
132 
133  // Ansa tags. Denoted by $ANSA_NAME.
134  // These will appear just before the first use of a type.
135  // We read them and store the PSHELL types which are used to name
136  // the zones.
137  label ansaId = -1;
138  word ansaType, ansaName;
139 
140  // A single warning per unrecognized command
141  wordHashSet unhandledCmd;
142 
143  while (is.good())
144  {
145  string::size_type linei = 0; // Parsing position within current line
146  string line;
147  is.getLine(line);
148 
149  // ANSA extension
150  // line 1: $ANSA_NAME;<int>;<word>;
151  // line 2: $partName
152  if (line.starts_with("$ANSA_NAME"))
153  {
154  const auto sem0 = line.find(';', 0);
155  const auto sem1 = line.find(';', sem0+1);
156  const auto sem2 = line.find(';', sem1+1);
157 
158  if
159  (
160  sem0 != std::string::npos
161  && sem1 != std::string::npos
162  && sem2 != std::string::npos
163  )
164  {
165  ansaId = readLabel(line.substr(sem0+1, sem1-sem0-1));
166  ansaType = line.substr(sem1+1, sem2-sem1-1);
167 
168  string rawName;
169  is.getLine(rawName);
170  rawName.removeEnd('\r'); // Possible CR-NL
171  ansaName = word::validate(rawName.substr(1));
172 
173  // Info<< "ANSA tag for NastranID:" << ansaId
174  // << " of type " << ansaType
175  // << " name " << ansaName << endl;
176  }
177  }
178 
179 
180  // HYPERMESH extension
181  // $HMNAME COMP 1"partName"
182  if (line.starts_with("$HMNAME COMP") && line.find('"') != string::npos)
183  {
184  label groupId = readLabel(line.substr(16, 16));
185 
186  // word::validate automatically removes quotes too
187  const word groupName = word::validate(line.substr(32));
188 
189  nameLookup.insert(groupId, groupName);
190  // Info<< "group " << groupId << " => " << groupName << endl;
191  }
192 
193  if (line.empty() || line[0] == '$')
194  {
195  continue; // Skip empty or comment
196  }
197 
198  // Check if character 72 is continuation
199  if (line.size() > 72 && line[72] == '+')
200  {
201  line.resize(72);
202 
203  while (true)
204  {
205  string buf;
206  is.getLine(buf);
207 
208  if (buf.size() > 72 && buf[72] == '+')
209  {
210  line += buf.substr(8, 64);
211  }
212  else
213  {
214  line += buf.substr(8);
215  break;
216  }
217  }
218  }
219 
220  // First word (column 0-8)
221  const word cmd(word::validate(nextNasField(line, linei, 8)));
222 
223  if (cmd == "CTRIA3")
224  {
225  (void) nextNasField(line, linei, 8); // 8-16
226  label groupId = readLabel(nextNasField(line, linei, 8)); // 16-24
227  const auto a = readLabel(nextNasField(line, linei, 8)); // 24-32
228  const auto b = readLabel(nextNasField(line, linei, 8)); // 32-40
229  const auto c = readLabel(nextNasField(line, linei, 8)); // 40-48
230 
231  // Convert groupId into zoneId
232  const auto iterZone = zoneLookup.cfind(groupId);
233  if (iterZone.found())
234  {
235  if (zoneId != *iterZone)
236  {
237  // pshell types are intermixed
238  sorted = false;
239  }
240  zoneId = *iterZone;
241  }
242  else
243  {
244  zoneId = dynSizes.size();
245  zoneLookup.insert(groupId, zoneId);
246  dynSizes.append(0);
247  // Info<< "zone" << zoneId << " => group " << groupId <<nl;
248  }
249 
250  dynFaces.append(Face{a, b, c});
251  dynZones.append(zoneId);
252  dynSizes[zoneId]++;
253  }
254  else if (cmd == "CQUAD4")
255  {
256  (void) nextNasField(line, linei, 8); // 8-16
257  label groupId = readLabel(nextNasField(line, linei, 8)); // 16-24
258  const auto a = readLabel(nextNasField(line, linei, 8)); // 24-32
259  const auto b = readLabel(nextNasField(line, linei, 8)); // 32-40
260  const auto c = readLabel(nextNasField(line, linei, 8)); // 40-48
261  const auto d = readLabel(nextNasField(line, linei, 8)); // 48-56
262 
263  // Convert groupId into zoneId
264  const auto iterZone = zoneLookup.cfind(groupId);
265  if (iterZone.found())
266  {
267  if (zoneId != *iterZone)
268  {
269  // pshell types are intermixed
270  sorted = false;
271  }
272  zoneId = *iterZone;
273  }
274  else
275  {
276  zoneId = dynSizes.size();
277  zoneLookup.insert(groupId, zoneId);
278  dynSizes.append(0);
279  // Info<< "zone" << zoneId << " => group " << groupId <<nl;
280  }
281 
283  {
284  dynFaces.append(Face{a, b, c});
285  dynFaces.append(Face{c, d, a});
286  dynZones.append(zoneId);
287  dynZones.append(zoneId);
288  dynSizes[zoneId] += 2;
289  }
290  else
291  {
292  dynFaces.append(Face{a,b,c,d});
293  dynZones.append(zoneId);
294  dynSizes[zoneId]++;
295  }
296  }
297  else if (cmd == "GRID")
298  {
299  label index = readLabel(nextNasField(line, linei, 8)); // 8-16
300  (void) nextNasField(line, linei, 8); // 16-24
301  scalar x = readNasScalar(nextNasField(line, linei, 8)); // 24-32
302  scalar y = readNasScalar(nextNasField(line, linei, 8)); // 32-40
303  scalar z = readNasScalar(nextNasField(line, linei, 8)); // 40-48
304 
305  pointId.append(index);
306  dynPoints.append(point(x, y, z));
307  }
308  else if (cmd == "GRID*")
309  {
310  // Long format is on two lines with '*' continuation symbol
311  // on start of second line.
312  // Typical line (spaces compacted)
313  // GRID* 126 0 -5.55999875E+02 -5.68730474E+02
314  // * 2.14897901E+02
315 
316  label index = readLabel(nextNasField(line, linei, 16)); // 8-24
317  (void) nextNasField(line, linei, 16); // 24-40
318  scalar x = readNasScalar(nextNasField(line, linei, 16)); // 40-56
319  scalar y = readNasScalar(nextNasField(line, linei, 16)); // 56-72
320 
321  linei = 0; // restart at index 0
322  is.getLine(line);
323  if (line[0] != '*')
324  {
326  << "Expected continuation symbol '*' when reading GRID*"
327  << " (double precision coordinate) format" << nl
328  << "Read:" << line << nl
329  << "File:" << is.name() << " line:" << is.lineNumber()
330  << exit(FatalError);
331  }
332  (void) nextNasField(line, linei, 8); // 0-8
333  scalar z = readNasScalar(nextNasField(line, linei, 16)); // 8-16
334 
335  pointId.append(index);
336  dynPoints.append(point(x, y, z));
337  }
338  else if (cmd == "PSHELL")
339  {
340  // Read shell type since group gives patchnames (ANSA extension)
341  label groupId = readLabel(nextNasField(line, linei, 8)); // 8-16
342  if (groupId == ansaId && ansaType == "PSHELL")
343  {
344  const word groupName = word::validate(ansaName);
345  nameLookup.insert(groupId, groupName);
346  // Info<< "group " << groupId << " => " << groupName << endl;
347  }
348  }
349  else if (unhandledCmd.insert(cmd))
350  {
351  Info<< "Unhandled Nastran command " << line << nl
352  << "File:" << is.name() << " line:" << is.lineNumber()
353  << endl;
354  }
355  }
356 
357  // Info<< "Read faces:" << dynFaces.size()
358  // << " points:" << dynPoints.size()
359  // << endl;
360 
361  // transfer to normal lists
362  this->storedPoints().transfer(dynPoints);
363 
364  pointId.shrink();
365  dynFaces.shrink();
366 
367  // Build inverse mapping (NASTRAN pointId -> index)
368  Map<label> mapPointId(2*pointId.size());
369  forAll(pointId, i)
370  {
371  mapPointId.insert(pointId[i], i);
372  }
373 
374  // Relabel faces
375  // ~~~~~~~~~~~~~
376  for (Face& f : dynFaces)
377  {
378  forAll(f, fp)
379  {
380  f[fp] = mapPointId[f[fp]];
381  }
382  }
383  pointId.clearStorage();
384  mapPointId.clear();
385 
386 
387  // create default zone names, or from ANSA/Hypermesh information
388  List<word> names(dynSizes.size());
389  forAllConstIters(zoneLookup, iter)
390  {
391  const label groupId = iter.key();
392  const label zoneId = iter.val();
393 
394  const auto iterName = nameLookup.cfind(groupId);
395  if (iterName.found())
396  {
397  names[zoneId] = *iterName;
398  }
399  else
400  {
401  names[zoneId] = word("zone") + ::Foam::name(zoneId);
402  }
403  }
404 
405  this->sortFacesAndStore(dynFaces, dynZones, sorted);
406 
407  // Add zones (retaining empty ones)
408  this->addZones(dynSizes, names);
409  this->addZonesToFaces(); // for labelledTri
410 
411  return true;
412 }
413 
414 
415 template<class Face>
417 (
418  const fileName& filename,
419  const MeshedSurfaceProxy<Face>& surf,
420  const dictionary& options
421 )
422 {
423  const UList<point>& pointLst = surf.points();
424  const UList<Face>& faceLst = surf.surfFaces();
425  const UList<label>& faceMap = surf.faceMap();
426 
427  // for no zones, suppress the group name
428  const surfZoneList zones =
429  (
430  surf.surfZones().empty()
431  ? surfaceFormatsCore::oneZone(faceLst, "")
432  : surf.surfZones()
433  );
434 
435  const bool useFaceMap = (surf.useFaceMap() && zones.size() > 1);
436 
437  OFstream os(filename);
438  if (!os.good())
439  {
441  << "Cannot open file for writing " << filename
442  << exit(FatalError);
443  }
444 
445  // For simplicity, use fieldFormat::FREE throughout
446  fileFormats::NASCore::setPrecision(os, fieldFormat::FREE);
447 
448  os << "CEND" << nl
449  << "TITLE = " << os.name().nameLessExt() << nl;
450 
451  // Print zone names as comment
452  forAll(zones, zonei)
453  {
454  // HYPERMESH extension
455  os << "$HMNAME COMP" << setw(20) << (zonei+1)
456  << '"' << zones[zonei].name() << '"' << nl;
457  }
458 
459  // Write vertex coords with 1-based point Id
460  os << "$ GRID POINTS" << nl
461  << "BEGIN BULK" << nl;
462 
463  label pointId = 0;
464  for (const point& pt : pointLst)
465  {
466  os << "GRID" << ','
467  << ++pointId << ','
468  << 0 << ',' // global coordinate system
469  << pt.x() << ',' << pt.y() << ',' << pt.z() << nl;
470  }
471 
472  os << "$ ELEMENTS" << nl;
473 
474  label faceIndex = 0;
475  label zoneIndex = 0;
476  label elementId = 0;
477  for (const surfZone& zone : zones)
478  {
479  const label nLocalFaces = zone.size();
480 
481  if (useFaceMap)
482  {
483  for (label i=0; i<nLocalFaces; ++i)
484  {
485  const Face& f = faceLst[faceMap[faceIndex++]];
486  elementId = writeShell(os, f, zoneIndex, elementId);
487  }
488  }
489  else
490  {
491  for (label i=0; i<nLocalFaces; ++i)
492  {
493  const Face& f = faceLst[faceIndex++];
494  elementId = writeShell(os, f, zoneIndex, elementId);
495  }
496  }
497 
498  ++zoneIndex;
499  }
500 
501  os << "ENDDATA" << nl;
502 }
503 
504 
505 // ************************************************************************* //
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: blockMeshMergeFast.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 into a string function.
Definition: ISstreamI.H:78
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:91
Foam::MeshedSurfaceProxy::useFaceMap
bool useFaceMap() const
Use faceMap?
Definition: MeshedSurfaceProxy.H:186
Foam::IFstream
Input from file stream, using an ISstream.
Definition: IFstream.H:97
Foam::DynamicList< label >
Foam::IFstream::name
virtual const fileName & name() const
Read/write access to the name of the stream.
Definition: ISstream.H:111
validate
thermo validate(args.executable(), "h")
Foam::zone
Base class for mesh zones.
Definition: zone.H:63
Foam::Map< label >
Foam::MeshedSurfaceProxy::points
const pointField & points() const
Return const access to the points.
Definition: MeshedSurfaceProxy.H:160
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:337
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:78
Foam::MeshedSurfaceProxy::faceMap
const labelUList & faceMap() const
Const access to the faceMap, zero-sized when unused.
Definition: MeshedSurfaceProxy.H:180
forAll
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:290
NASsurfaceFormat.H
n
label n
Definition: TABSMDCalcMethod2.H:31
Foam::faceTraits
Traits class for faces.
Definition: faceTraits.H:50
Foam::label
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:62
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
IOmanip.H
Istream and Ostream manipulators taking arguments.
size_type
graph_traits< Graph >::vertices_size_type size_type
Definition: SloanRenumber.C:75
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:99
FatalErrorInFunction
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:355
clear
patchWriters clear()
Foam::nl
constexpr char nl
Definition: Ostream.H:372
forAllConstIters
forAllConstIters(mixture.phases(), phase)
Definition: pEqn.H:28
Foam::fileFormats::NASsurfaceFormat::write
static void write(const fileName &filename, const MeshedSurfaceProxy< Face > &surf, const dictionary &options=dictionary::null)
Write surface mesh components by proxy.
Definition: NASsurfaceFormat.C:417
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:70
Foam::List< word >
Foam::surfZone
A surface zone on a MeshedSurface.
Definition: surfZone.H:65
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:182
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:301
Foam::MeshedSurfaceProxy::surfFaces
const UList< Face > & surfFaces() const
Return const access to the faces.
Definition: MeshedSurfaceProxy.H:166
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:216
faceTraits.H
Foam::string::removeEnd
bool removeEnd(const std::string &text)
Remove the given text from the end of the string.
Definition: string.C:215
Foam::fileFormats::NASsurfaceFormat
Nastran surface reader.
Definition: NASsurfaceFormat.H:67
y
scalar y
Definition: LISASMDCalcMethod1.H:14
Foam::MeshedSurfaceProxy::surfZones
const UList< surfZone > & surfZones() const
Const access to the surface zones.
Definition: MeshedSurfaceProxy.H:174