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 "ListOps.H"
31 #include "IFstream.H"
32 #include "IOmanip.H"
33 #include "faceTraits.H"
34 
35 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
36 
37 template<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 
90 template<class Face>
92 (
93  const fileName& filename
94 )
95 {
96  read(filename);
97 }
98 
99 
100 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
101 
102 template<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 
434 template<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 // ************************************************************************* //
Foam::fileFormats::NASsurfaceFormat::read
virtual bool read(const fileName &filename)
Read from file.
Definition: NASsurfaceFormat.C:104
Foam::faceMap
Pair< int > faceMap(const label facePi, const face &faceP, const label faceNi, const face &faceN)
Definition: blockMeshMergeTopological.C:94
Foam::word
A class for handling words, derived from Foam::string.
Definition: word.H:65
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:73
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:53
Foam::DynamicList< label >
Foam::IFstream::name
virtual const fileName & name() const
Read/write access to the name of the stream.
Definition: ISstream.H:113
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:286
Foam::Map< label >
Foam::MeshedSurfaceProxy::points
const pointField & points() const
Return const access to the points.
Definition: MeshedSurfaceProxy.H:171
Foam::IOstream::lineNumber
label lineNumber() const noexcept
Const access to the current stream line number.
Definition: IOstream.H:318
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:369
Foam::fileFormats::NASsurfaceFormat::NASsurfaceFormat
NASsurfaceFormat(const fileName &filename)
Construct from file name.
Definition: NASsurfaceFormat.C:92
Foam::HashSet< word, Hash< word > >
Foam::DynamicList::shrink
DynamicList< T, SizeMin > & shrink()
Shrink the allocated space to the number of elements used.
Definition: DynamicListI.H:434
Foam::DynamicList::clear
void clear() noexcept
Clear the addressed list, i.e. set the size to zero.
Definition: DynamicListI.H:391
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
Foam::IOstream::good
bool good() const noexcept
True if next operation might succeed.
Definition: IOstream.H:233
NASsurfaceFormat.H
Foam::blockMeshTools::read
void read(Istream &, label &val, const dictionary &)
In-place read with dictionary lookup.
Definition: blockMeshTools.C:57
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 (stdout output on master, null elsewhere)
Foam::DynamicList::append
DynamicList< T, SizeMin > & append(const T &val)
Append an element to the end of this list.
Definition: DynamicListI.H:511
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
IFstream.H
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:123
os
OBJstream os(runTime.globalPath()/outputName)
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:53
found
bool found
Definition: TABSMDCalcMethod2.H:32
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:453
clear
patchWriters clear()
Foam::nl
constexpr char nl
Definition: Ostream.H:404
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::lessOp1
Definition: ops.H:245
Foam::HashSet::insert
bool insert(const Key &key)
Insert a new entry, not overwriting existing entries.
Definition: HashSet.H:191
Foam::DynamicList::clearStorage
void clearStorage()
Clear the list and delete storage.
Definition: DynamicListI.H:398
x
x
Definition: LISASMDCalcMethod2.H:52
Foam::line
A line primitive.
Definition: line.H:53
Foam::constant::universal::c
const dimensionedScalar c
Speed of light in a vacuum.
Foam::name
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for INVALID.
Definition: exprTraits.C:59
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:436
ListOps.H
Various functions to operate on Lists.
Foam::MeshedSurfaceProxy::surfFaces
const UList< Face > & surfFaces() const
Return const access to the faces.
Definition: MeshedSurfaceProxy.H:177
Foam::UList::size
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:114
Foam::point
vector point
Point is a vector.
Definition: point.H:43
Foam::stringOps::validate
StringType validate(const std::string &str, const UnaryPredicate &accept, const bool invert=false)
Return a copy of the input string with validated characters.
Definition: stringOpsTemplates.C:71
faceTraits.H
Foam::PtrListOps::names
List< word > names(const UPtrList< T > &list, const UnaryMatchPredicate &matcher)
Foam::string::removeEnd
bool removeEnd(const std::string &text)
Remove the given text from the end of the string.
Definition: string.C:229
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