nastranSurfaceWriter.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) 2012-2016 OpenFOAM Foundation
9  Copyright (C) 2015-2022 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 "nastranSurfaceWriter.H"
30 #include "Pair.H"
31 #include "IOmanip.H"
32 #include "ListOps.H"
33 #include "OSspecific.H"
34 #include "surfaceWriterMethods.H"
36 
37 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
38 
39 namespace Foam
40 {
41 namespace surfaceWriters
42 {
43  defineTypeName(nastranWriter);
44  addToRunTimeSelectionTable(surfaceWriter, nastranWriter, word);
45  addToRunTimeSelectionTable(surfaceWriter, nastranWriter, wordDict);
46 }
47 }
48 
49 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
50 
51 // Field writing implementation
53 
54 // Field writing methods
56 
57 
58 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
59 
60 Foam::Ostream& Foam::surfaceWriters::nastranWriter::writeKeyword
61 (
62  Ostream& os,
63  const word& keyword
64 ) const
65 {
66  return fileFormats::NASCore::writeKeyword(os, keyword, writeFormat_);
67 }
68 
69 
70 void Foam::surfaceWriters::nastranWriter::writeCoord
71 (
72  Ostream& os,
73  const point& p,
74  const label pointId
75 ) const
76 {
77  fileFormats::NASCore::writeCoord(os, p, pointId, writeFormat_);
78 }
79 
80 
81 void Foam::surfaceWriters::nastranWriter::writeFace
82 (
83  Ostream& os,
84  const word& faceType,
85  const labelUList& facePts,
86  const label elemId,
87  const label propId
88 ) const
89 {
90  // Only valid surface elements are CTRIA3 and CQUAD4
91 
92  // Fixed short/long formats:
93  // 1 CQUAD4
94  // 2 EID : element ID
95  // 3 PID : property element ID; default = EID (blank)
96  // 4 G1 : grid point index - requires starting index of 1
97  // 5 G2 : grid point index
98  // 6 G3 : grid point index
99  // 7 G4 : grid point index
100  // 8 onwards - not used
101 
102  // For CTRIA3 elements, cols 7 onwards are not used
103 
104  writeKeyword(os, faceType) << separator_;
105 
106  os.setf(std::ios_base::right);
107 
108  writeValue(os, elemId) << separator_;
109  writeValue(os, propId);
110 
111  switch (writeFormat_)
112  {
113  case fieldFormat::SHORT :
114  {
115  for (const label pointi : facePts)
116  {
117  writeValue(os, pointi + 1);
118  }
119 
120  break;
121  }
122 
123  case fieldFormat::LONG :
124  {
125  forAll(facePts, i)
126  {
127  writeValue(os, facePts[i] + 1);
128  if (i == 1)
129  {
130  os << nl;
131  os.unsetf(std::ios_base::right);
132  writeKeyword(os, "");
133  os.setf(std::ios_base::right);
134  }
135  }
136 
137  break;
138  }
139 
140  case fieldFormat::FREE :
141  {
142  for (const label pointi : facePts)
143  {
144  os << separator_;
145  writeValue(os, pointi + 1);
146  }
147 
148  break;
149  }
150  }
151 
152  os << nl;
153  os.unsetf(std::ios_base::right);
154 }
155 
156 
157 void Foam::surfaceWriters::nastranWriter::writeGeometry
158 (
159  Ostream& os,
160  const meshedSurf& surf,
161  labelList& decompOffsets,
162  DynamicList<face>& decompFaces
163 ) const
164 {
165  const pointField& points = surf.points();
166  const faceList& faces = surf.faces();
167  const labelList& zones = surf.zoneIds();
168  const labelList& elemIds = surf.faceIds();
169 
170  // Possible to use faceIds?
171  bool useOrigFaceIds =
172  (
173  elemIds.size() == faces.size()
174  && !ListOps::found(elemIds, lessOp1<label>(0))
175  );
176 
177  // Not possible with on-the-fly face decomposition
178  if (useOrigFaceIds)
179  {
180  for (const auto& f : faces)
181  {
182  if (f.size() > 4)
183  {
184  useOrigFaceIds = false;
185  break;
186  }
187  }
188  }
189 
190 
191  // Write points
192 
193  os << '$' << nl
194  << "$ Points" << nl
195  << '$' << nl;
196 
197  forAll(points, pointi)
198  {
199  writeCoord(os, points[pointi], pointi);
200  }
201 
202  // Write faces, with on-the-fly decomposition (triangulation)
203  decompOffsets.resize(faces.size()+1);
204  decompFaces.clear();
205 
206  decompOffsets[0] = 0; // The first offset is always zero
207 
208  os << '$' << nl
209  << "$ Faces" << nl
210  << '$' << nl;
211 
212  label elemId = 0; // The element-id
213  forAll(faces, facei)
214  {
215  const face& f = faces[facei];
216 
217  if (useOrigFaceIds)
218  {
219  elemId = elemIds[facei];
220  }
221 
222  // 1-offset for PID
223  const label propId = 1 + (facei < zones.size() ? zones[facei] : 0);
224 
225  if (f.size() == 3)
226  {
227  writeFace(os, "CTRIA3", f, ++elemId, propId);
228  }
229  else if (f.size() == 4)
230  {
231  writeFace(os, "CQUAD4", f, ++elemId, propId);
232  }
233  else
234  {
235  // Decompose into tris
236  f.triangles(points, decompFaces);
237 
238  for
239  (
240  label decompi = decompOffsets[facei];
241  decompi < decompFaces.size();
242  ++decompi
243  )
244  {
245  writeFace
246  (
247  os,
248  "CTRIA3",
249  decompFaces[decompi],
250  ++elemId,
251  propId
252  );
253  }
254  }
255 
256  // The end offset, which is the next begin offset
257  decompOffsets[facei+1] = decompFaces.size();
258  }
259 
260 
261  //
262  // SHELL/MAT information
263  //
264 
265  // Zone id have been used for the PID. Find unique values.
266 
267  labelList pidsUsed = labelHashSet(surf.zoneIds()).sortedToc();
268  if (pidsUsed.empty())
269  {
270  pidsUsed.resize(1, Zero); // fallback
271  }
272 
273  for (auto pid : pidsUsed)
274  {
275  writeKeyword(os, "PSHELL") << separator_;
276  writeValue(os, pid+1); // 1-offset for PID
277 
278  for (label i = 0; i < 7; ++i)
279  {
280  // Dummy values
281  os << separator_;
282  writeValue(os, 1);
283  }
284  os << nl;
285  }
286 
287 
288  // Use single material ID
289 
290  const label MID = 1;
291 
292  writeKeyword(os, "MAT1") << separator_;
293  writeValue(os, MID);
294 
295  for (label i = 0; i < 7; ++i)
296  {
297  // Dummy values
298  os << separator_;
299  writeValue(os, "");
300  }
301  os << nl;
302 }
303 
304 
305 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
306 
308 :
309  surfaceWriter(),
310  writeFormat_(fieldFormat::SHORT),
311  fieldMap_(),
312  commonGeometry_(false),
313  separator_()
314 {
315  // if (writeFormat_ == fieldFormat::FREE)
316  // {
317  // separator_ = ",";
318  // }
319 }
320 
321 
323 (
324  const dictionary& options
325 )
326 :
327  surfaceWriter(options),
328  writeFormat_
329  (
330  fileFormats::NASCore::fieldFormatNames.getOrDefault
331  (
332  "format",
333  options,
334  fieldFormat::LONG
335  )
336  ),
337  fieldMap_(),
338  commonGeometry_(options.getOrDefault("commonGeometry", false)),
339  separator_()
340 {
341  if (writeFormat_ == fieldFormat::FREE)
342  {
343  separator_ = ",";
344  }
345 
346  List<Pair<word>> fieldPairs;
347  options.readEntry("fields", fieldPairs);
348 
349  for (const Pair<word>& item : fieldPairs)
350  {
351  // (field name => load format)
352  fieldMap_.insert
353  (
354  item.first(),
356  );
357  }
358 }
359 
360 
362 (
363  const meshedSurf& surf,
364  const fileName& outputPath,
365  bool parallel,
366  const dictionary& options
367 )
368 :
369  nastranWriter(options)
370 {
371  open(surf, outputPath, parallel);
372 }
373 
374 
376 (
377  const pointField& points,
378  const faceList& faces,
379  const fileName& outputPath,
380  bool parallel,
381  const dictionary& options
382 )
383 :
384  nastranWriter(options)
385 {
386  open(points, faces, outputPath, parallel);
387 }
388 
389 
390 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
391 
393 {
394  checkOpen();
395 
396  // Geometry: rootdir/<TIME>/surfaceName.nas
397 
398  fileName outputFile = outputPath_;
399  if (useTimeDir() && !timeName().empty())
400  {
401  // Splice in time-directory
402  outputFile = outputPath_.path() / timeName() / outputPath_.name();
403  }
404  outputFile.ext("nas");
405 
406  if (verbose_)
407  {
408  Info<< "Writing nastran geometry to " << outputFile << endl;
409  }
410 
411 
412  // const meshedSurf& surf = surface();
413  const meshedSurfRef& surf = adjustSurface();
414 
415  if (UPstream::master() || !parallel_)
416  {
417  if (!isDir(outputFile.path()))
418  {
419  mkDir(outputFile.path());
420  }
421 
422  OFstream os(outputFile);
423  fileFormats::NASCore::setPrecision(os, writeFormat_);
424 
425  os << "TITLE=OpenFOAM " << outputPath_.name() << " geometry" << nl
426  << "BEGIN BULK" << nl;
427 
428  labelList decompOffsets;
429  DynamicList<face> decompFaces;
430 
431  writeGeometry(os, surf, decompOffsets, decompFaces);
432 
433  os << "ENDDATA" << nl;
434  }
435 
436  wroteGeom_ = true;
437  return outputFile;
438 }
439 
440 
441 // ************************************************************************* //
A surface writer for the Nastran file format - both surface mesh and fields.
A class for handling file names.
Definition: fileName.H:72
writer writeGeometry()
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:129
bool found(const ListType &input, const UnaryPredicate &pred, const label start=0)
Same as found_if.
Definition: ListOps.H:826
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: BitOps.H:56
virtual fileName write()
Write surface geometry to file.
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
static const Enum< loadFormat > loadFormatNames
Selection names for the NASTRAN file field formats.
Definition: NASCore.H:92
static std::string path(const std::string &str)
Return directory path name (part before last /)
Definition: fileNameI.H:169
defineSurfaceWriterWriteFields(Foam::surfaceWriters::nastranWriter)
word ext() const
Return file name extension (part after last .)
Definition: fileNameI.H:211
Macros for easy insertion into run-time selection tables.
UList< label > labelUList
A UList of labels.
Definition: UList.H:78
Abstract definition of a meshed surface defined by faces and points.
Definition: meshedSurf.H:43
Various functions to operate on Lists.
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
bool isDir(const fileName &name, const bool followLink=true)
Does the name exist as a DIRECTORY in the file system?
Definition: POSIX.C:860
HashSet< label, Hash< label > > labelHashSet
A HashSet of labels, uses label hasher.
Definition: HashSet.H:85
bool insert(const Key &key, const T &obj)
Copy insert a new entry, not overwriting existing entries.
Definition: HashTableI.H:152
bool readEntry(const word &keyword, T &val, enum keyType::option matchOpt=keyType::REGEX, IOobjectOption::readOption readOpt=IOobjectOption::MUST_READ) const
Find entry and assign to T val. FatalIOError if it is found and the number of tokens is incorrect...
List< face > faceList
List of faces.
Definition: faceListFwd.H:39
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
word timeName
Definition: getTimeIndex.H:3
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:38
bool mkDir(const fileName &pathName, mode_t mode=0777)
Make a directory and return an error if it could not be created.
Definition: POSIX.C:614
static void setPrecision(Ostream &os, const fieldFormat format)
Set output stream precision and format flags.
Definition: NASCore.C:153
const pointField & points
static void writeCoord(Ostream &os, const point &p, const label pointId, const fieldFormat format)
Write a GRID point.
Definition: NASCore.C:221
pid_t pid()
Return the PID of this process.
Definition: POSIX.C:314
An Ostream is an abstract base class for all output systems (streams, files, token lists...
Definition: Ostream.H:56
Istream and Ostream manipulators taking arguments.
OBJstream os(runTime.globalPath()/outputName)
labelList f(nPoints)
nastranWriter()
Default construct. Default SHORT format.
Convenience macros for instantiating surfaceWriter methods.
defineTypeName(abaqusWriter)
vector point
Point is a vector.
Definition: point.H:37
static bool master(const label communicator=worldComm)
True if process corresponds to the master rank in the communicator.
Definition: UPstream.H:1082
List< label > sortedToc(const UList< bool > &bools)
Return the (sorted) values corresponding to &#39;true&#39; entries.
Definition: BitOps.C:195
messageStream Info
Information stream (stdout output on master, null elsewhere)
Base class for surface writers.
List< label > labelList
A List of labels.
Definition: List.H:62
volScalarField & p
addToRunTimeSelectionTable(surfaceWriter, abaqusWriter, word)
static Ostream & writeKeyword(Ostream &os, const word &keyword, const fieldFormat format)
Write initial keyword (eg, &#39;GRID&#39; or &#39;GRID*&#39;) followed by the requisite number of spaces for the fiel...
Definition: NASCore.C:187
Namespace for OpenFOAM.
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:127