ensightSurfaceReader.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) 2015-2022 OpenCFD Ltd.
9 -------------------------------------------------------------------------------
10 License
11  This file is part of OpenFOAM.
12 
13  OpenFOAM is free software: you can redistribute it and/or modify it
14  under the terms of the GNU General Public License as published by
15  the Free Software Foundation, either version 3 of the License, or
16  (at your option) any later version.
17 
18  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
19  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21  for more details.
22 
23  You should have received a copy of the GNU General Public License
24  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
25 
26 \*---------------------------------------------------------------------------*/
27 
28 #include "ensightSurfaceReader.H"
29 #include "ensightCase.H"
30 #include "stringOps.H"
32 
33 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
34 
35 namespace Foam
36 {
37  defineTypeNameAndDebug(ensightSurfaceReader, 0);
38  addToRunTimeSelectionTable(surfaceReader, ensightSurfaceReader, fileName);
39 }
40 
41 
42 // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
43 
44 namespace Foam
45 {
46 
47 // Read and discard specified number of elements
48 template<class Type>
49 static inline void discard(label n, ensightReadFile& is)
50 {
51  Type val;
52 
53  while (n > 0)
54  {
55  is.read(val);
56  --n;
57  }
58 }
59 
60 } // End namespace Foam
61 
62 
63 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
64 
65 void Foam::ensightSurfaceReader::skip(const label n, Istream& is) const
66 {
67  label i = 0;
68  token tok;
69  while (is.good() && (i < n))
70  {
71  is >> tok;
72  ++i;
73 
74  DebugInfo
75  << "Skipping token " << tok << nl;
76  }
77 
78  if (i != n)
79  {
81  << "Requested to skip " << n << " tokens, but stream exited after "
82  << i << " tokens. Last token read: " << tok
83  << nl;
84  }
85 }
86 
87 
88 void Foam::ensightSurfaceReader::readLine(ISstream& is, string& line) const
89 {
90  do
91  {
92  is.getLine(line);
93 
94  // Trim out any '#' comments
95  const auto pos = line.find('#');
96  if (pos != std::string::npos)
97  {
98  line.erase(pos);
99  }
101  }
102  while (line.empty() && is.good());
103 }
104 
105 
107 (
108  const word& expected,
109  ISstream& is
110 ) const
111 {
112  string actual;
113  readLine(is, actual);
114 
115  if (expected != actual)
116  {
118  << "Expected section header '" << expected
119  << "' but read " << actual << nl
120  << exit(FatalIOError);
121  }
123  DebugInfo
124  << "Read section header: " << expected << nl;
125 }
126 
127 
129 (
130  const fileName& fName,
131  const label timeIndex
132 )
133 {
134  fileName result(fName);
135 
136  const auto nMask = stringOps::count(fName, '*');
137 
138  // If there are any '*' chars, they are assumed to be contiguous
139  // Eg, data/******/geometry
140 
141  if (nMask)
142  {
143  const std::string maskStr(nMask, '*');
144  const Foam::word indexStr(ensightCase::padded(nMask, timeIndex));
145  result.replace(maskStr, indexStr);
146  }
147 
148  return result;
149 }
150 
151 
153 Foam::ensightSurfaceReader::readGeometryHeader(ensightReadFile& is) const
154 {
155  // Binary flag string if applicable
156  is.readBinaryHeader();
157 
158  string buffer;
159 
160  Pair<idTypes> idHandling(idTypes::NONE, idTypes::NONE);
161 
162  // Ensight Geometry File
163  is.read(buffer);
164  DebugInfo<< "buffer [" << buffer.length() << "] " << buffer << nl;
165 
166  // Description - 1
167  is.read(buffer);
168  DebugInfo<< "buffer [" << buffer.length() << "] " << buffer << nl;
169 
170  // "node id (off|assign|given|ignore)" - "given" is not actually supported
171  is.read(buffer);
172  DebugInfo<< "buffer [" << buffer.length() << "] " << buffer << nl;
173 
174  if (buffer.contains("ignore"))
175  {
176  idHandling.first() = idTypes::IGNORE;
177  }
178  else if (buffer.contains("given"))
179  {
180  idHandling.first() = idTypes::GIVEN;
181  }
182 
183  // "element id (off|assign|given|ignore)"
184  is.read(buffer);
185  DebugInfo<< "buffer [" << buffer.length() << "] " << buffer << nl;
186 
187  if (buffer.contains("ignore"))
188  {
189  idHandling.second() = idTypes::IGNORE;
190  }
191  else if (buffer.contains("given"))
192  {
193  idHandling.second() = idTypes::GIVEN;
194  }
195 
196 
197  // "part" - but could also be an optional "extents"
198  is.read(buffer);
199  DebugInfo<< "buffer [" << buffer.length() << "] " << buffer << nl;
200 
201  if (buffer.contains("extents"))
202  {
203  // Optional extents - read and discard 6 floats
204  // (xmin, xmax, ymin, ymax, zmin, zmax)
205 
206  discard<scalar>(6, is);
207 
208  // Part
209  is.read(buffer);
210  DebugInfo<< "buffer [" << buffer.length() << "] " << buffer << nl;
211  }
212 
213  // The part number
214  label ivalue;
215  is.read(ivalue);
216  DebugInfo<< "ivalue: " << ivalue << nl;
217 
218  // Part description / name
219  is.read(buffer);
220  DebugInfo<< "buffer [" << buffer.length() << "] " << buffer << nl;
221 
222  // "coordinates"
223  is.read(buffer);
224  DebugInfo<< "buffer [" << buffer.length() << "] " << buffer << nl;
225 
226  return idHandling;
227 }
228 
229 
231 {
233 
234  if (!is.good())
235  {
237  << "Cannot read file " << is.name()
238  << exit(FatalError);
239  }
240 
241  string buffer;
242 
243  // Read the file
244 
245  debugSection("FORMAT", is);
246  readLine(is, buffer); // type: ensight gold
247 
248  debugSection("GEOMETRY", is);
249  readLine(is, buffer);
250 
251  // GEOMETRY with any of these
252  // model: 1 xxx.0000.mesh
253  // model: xxx.0000.mesh
254  // model: data/directory/geometry
255  //
256  // - use the last entry
257  meshFileName_ = stringOps::splitSpace(buffer).back().str();
258 
259  DebugInfo << "mesh file:" << meshFileName_ << endl;
260 
261  debugSection("VARIABLE", is);
262 
263  // Read the field description
264  DynamicList<word> fieldNames(16);
265  DynamicList<string> fieldFileNames(16);
266 
267  while (is.good())
268  {
269  readLine(is, buffer);
270 
271  if (buffer == "TIME")
272  {
273  break;
274  }
275 
276  // Read the field name and associated file name. Eg,
277  // scalar per element: 1 p data/********/p
278 
279  const auto parsed = stringOps::splitSpace(buffer);
280 
281  if (!buffer.contains(':') || parsed.size() < 4)
282  {
284  << "Error reading field file name. Current buffer: "
285  << buffer << endl;
286  continue;
287  }
288  else if (debug)
289  {
290  Info<< "variable line: " << parsed.size();
291  for (const auto& s : parsed)
292  {
293  Info<< " " << s.str();
294  }
295  Info<< nl;
296  }
297 
298  fieldNames.push_back(parsed[parsed.size()-2].str());
299  fieldFileNames.push_back(parsed.back().str());
300  }
301  fieldNames_.transfer(fieldNames);
302  fieldFileNames_.transfer(fieldFileNames);
303 
304  DebugInfo
305  << "fieldNames: " << fieldNames_ << nl
306  << "fieldFileNames: " << fieldFileNames_ << nl;
307 
308  // Start reading time information
309  readLine(is, buffer); // time set: <int>
310 
311  readLine(is, buffer);
312  readFromLine(3, buffer, nTimeSteps_); // number of steps: <int>
313  readLine(is, buffer);
314  readFromLine(3, buffer, timeStartIndex_); // filename start number: <int>
315  readLine(is, buffer);
316  readFromLine(2, buffer, timeIncrement_); // filename increment: <int>
317 
318  DebugInfo
319  << "nTimeSteps: " << nTimeSteps_ << nl
320  << "timeStartIndex: " << timeStartIndex_ << nl
321  << "timeIncrement: " << timeIncrement_ << nl;
322 
323  // Read the time values
324  readLine(is, buffer); // time values:
325  timeValues_.resize_nocopy(nTimeSteps_);
326  for (label i = 0; i < nTimeSteps_; ++i)
327  {
328  scalar t(readScalar(is));
329 
330  timeValues_[i].value() = t;
331  // TODO: use character representation of t directly instead of
332  // regenerating from scalar value
333  timeValues_[i].name() = Foam::name(t);
334  }
335 }
336 
337 
338 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
339 
341 (
342  const fileName& fName,
343  const dictionary& options
344 )
345 :
346  surfaceReader(fName, options),
347  masterOnly_
348  (
349  UPstream::parRun()
350  && options.getOrDefault("masterOnly", false)
351  ),
352  readFormat_(IOstreamOption::ASCII), // Placeholder value
353  baseDir_(fName.path()),
354  meshFileName_(),
355  fieldNames_(),
356  fieldFileNames_(),
357  nTimeSteps_(0),
358  timeStartIndex_(0),
359  timeIncrement_(1),
360  timeValues_(),
361  surfPtr_(nullptr)
362 {
363  if (options.getOrDefault("debug", false))
364  {
365  debug |= 1;
366  }
367 
369  {
370  IFstream is(fName);
371  readCase(is);
372  }
373 
374  if (masterOnly_ && UPstream::parRun())
375  {
377  (
380  fieldNames_,
382  nTimeSteps_,
386  );
387  }
388 }
389 
390 
391 // * * * * * * * * * * * * * Public Member Functions * * * * * * * * * * * //
392 
394 (
395  const fileName& geometryFile
396 )
397 {
399 
400  {
401  // Auto-detect ascii/binary format
402  ensightReadFile is(geometryFile);
403 
404  // Format detected from the geometry
405  readFormat_ = is.format();
406 
407  DebugInfo
408  << "File: " << is.name()
409  << " format: "
410  << IOstreamOption::formatNames[readFormat_] << endl;
411 
412  Pair<idTypes> idHandling = readGeometryHeader(is);
413 
414  label nPoints;
415  is.read(nPoints);
416 
417  DebugInfo
418  << "nPoints: " << nPoints << nl;
419 
420  if (idHandling.first() == idTypes::GIVEN)
421  {
423  << "Treating node id 'given' as being 'ignore'" << nl
424  << "If something fails, this could be the reason" << nl
425  << endl;
426 
427  idHandling.first() = idTypes::IGNORE;
428  }
429 
430  if (idHandling.first() == idTypes::IGNORE)
431  {
432  DebugInfo
433  << "Ignore " << nPoints << " node ids" << nl;
434 
435  // Read and discard labels
436  discard<label>(nPoints, is);
437  }
438 
440  for (direction cmpt = 0; cmpt < vector::nComponents; ++cmpt)
441  {
442  for (point& p : points)
443  {
444  is.read(p[cmpt]);
445  }
446  }
447 
448 
449  // Read faces - may be a mix of tria3, quad4, nsided
450  DynamicList<face> dynFaces(nPoints/3);
451  DynamicList<faceInfoTuple> faceTypeInfo(16);
452 
453  string faceType;
454  label faceCount = 0;
455 
456  while (is.good()) // (is.peek() != EOF)
457  {
458  // The element type
459  is.read(faceType);
460 
461  if (!is.good())
462  {
463  break;
464  }
465 
466  if
467  (
468  faceType
469  == ensightFaces::elemNames[ensightFaces::elemType::TRIA3]
470  )
471  {
472  is.read(faceCount);
473 
474  faceTypeInfo.push_back
475  (
476  faceInfoTuple(ensightFaces::elemType::TRIA3, faceCount)
477  );
478 
479  DebugInfo
480  << "faceType <" << faceType.c_str() << "> count: "
481  << faceCount << nl;
482 
483  if
484  (
485  idHandling.second() == idTypes::IGNORE
486  || idHandling.second() == idTypes::GIVEN
487  )
488  {
489  DebugInfo
490  << "Ignore " << faceCount << " element ids" << nl;
491 
492  // Read and discard labels
493  discard<label>(faceCount, is);
494  }
495 
496  for (label facei = 0; facei < faceCount; ++facei)
497  {
498  face f(3);
499  for (label& fp : f)
500  {
501  is.read(fp);
502  }
503 
504  dynFaces.push_back(std::move(f));
505  }
506  }
507  else if
508  (
509  faceType
510  == ensightFaces::elemNames[ensightFaces::elemType::QUAD4]
511  )
512  {
513  is.read(faceCount);
514 
515  faceTypeInfo.push_back
516  (
517  faceInfoTuple(ensightFaces::elemType::QUAD4, faceCount)
518  );
519 
520  DebugInfo
521  << "faceType <" << faceType.c_str() << "> count: "
522  << faceCount << nl;
523 
524  if
525  (
526  idHandling.second() == idTypes::IGNORE
527  || idHandling.second() == idTypes::GIVEN
528  )
529  {
530  DebugInfo
531  << "Ignore " << faceCount << " element ids" << nl;
532 
533  // Read and discard labels
534  discard<label>(faceCount, is);
535  }
536 
537  for (label facei = 0; facei < faceCount; ++facei)
538  {
539  face f(4);
540  for (label& fp : f)
541  {
542  is.read(fp);
543  }
544 
545  dynFaces.push_back(std::move(f));
546  }
547  }
548  else if
549  (
550  faceType
551  == ensightFaces::elemNames[ensightFaces::elemType::NSIDED]
552  )
553  {
554  is.read(faceCount);
555 
556  faceTypeInfo.push_back
557  (
558  faceInfoTuple(ensightFaces::elemType::NSIDED, faceCount)
559  );
560 
561  DebugInfo
562  << "faceType <" << faceType.c_str() << "> count: "
563  << faceCount << nl;
564 
565  if
566  (
567  idHandling.second() == idTypes::IGNORE
568  || idHandling.second() == idTypes::GIVEN
569  )
570  {
571  DebugInfo
572  << "Ignore " << faceCount << " element ids" << nl;
573 
574  // Read and discard labels
575  discard<label>(faceCount, is);
576  }
577 
578  labelList np(faceCount);
579  for (label facei = 0; facei < faceCount; ++facei)
580  {
581  is.read(np[facei]);
582  }
583  for (label facei = 0; facei < faceCount; ++facei)
584  {
585  face f(np[facei]);
586  for (label& fp : f)
587  {
588  is.read(fp);
589  }
590 
591  dynFaces.push_back(std::move(f));
592  }
593  }
594  else
595  {
596  if (debug)
597  {
599  << "Unknown face type: <" << faceType.c_str()
600  << ">. Stopping read and continuing with current "
601  << "elements only" << endl;
602  }
603  break;
604  }
605  }
606 
607  // From 1-based Ensight addressing to 0-based OF addressing
608  for (face& f : dynFaces)
609  {
610  for (label& fp : f)
611  {
612  --fp;
613  }
614  }
615 
616  faceTypeInfo_.transfer(faceTypeInfo);
617  faceList faces(std::move(dynFaces));
618 
619  DebugInfo
620  << "read nFaces: " << faces.size() << nl
621  << "file schema: " << faceTypeInfo_ << nl;
623  return meshedSurface(std::move(points), std::move(faces));
624  }
625 }
626 
627 
629 (
630  const label timeIndex
631 )
632 {
634 
635  if (!surfPtr_)
636  {
637  surfPtr_.reset(new meshedSurface);
638  auto& surf = *surfPtr_;
639 
640  fileName geomFile(baseDir_/replaceMask(meshFileName_, timeIndex));
641 
642  if (!masterOnly_ || UPstream::master(UPstream::worldComm))
643  {
644  surf = readGeometry(geomFile);
645  }
646 
647  if (masterOnly_ && UPstream::parRun())
648  {
649  // Note: don't need faceTypeInfo_ on (non-reading) ranks
651  }
652  }
653 
654  return *surfPtr_;
655 }
656 
657 
659 {
660  return timeValues_;
661 }
662 
663 
665 (
666  const label timeIndex
667 ) const
668 {
669  return fieldNames_;
670 }
671 
672 
674 (
675  const label timeIndex,
676  const label fieldIndex,
677  const scalar& refValue
678 ) const
679 {
680  return readField<scalar>(timeIndex, fieldIndex);
681 }
682 
683 
685 (
686  const label timeIndex,
687  const label fieldIndex,
688  const vector& refValue
689 ) const
690 {
691  return readField<vector>(timeIndex, fieldIndex);
692 }
693 
694 
697 (
698  const label timeIndex,
699  const label fieldIndex,
700  const sphericalTensor& refValue
701 ) const
702 {
703  return readField<sphericalTensor>(timeIndex, fieldIndex);
704 }
705 
706 
708 (
709  const label timeIndex,
710  const label fieldIndex,
711  const symmTensor& refValue
712 ) const
713 {
714  return readField<symmTensor>(timeIndex, fieldIndex);
715 }
716 
717 
719 (
720  const label timeIndex,
721  const label fieldIndex,
722  const tensor& refValue
723 ) const
724 {
725  return readField<tensor>(timeIndex, fieldIndex);
726 }
727 
728 
729 // ************************************************************************* //
std::string::size_type count(const char *s, const char c)
Count the number of occurrences of the specified character.
Definition: stringOps.C:686
string & replace(const std::string &s1, const std::string &s2, size_type pos=0)
Replace first occurrence of sub-string s1 with s2, beginning at pos.
Definition: string.C:101
virtual wordList fieldNames(const label timeIndex) const
Return a list of the available fields at a given time.
virtual Istream & read(char *buf, std::streamsize count) override
Binary read.
uint8_t direction
Definition: direction.H:46
A line primitive.
Definition: line.H:52
A class for handling file names.
Definition: fileName.H:72
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
static word padded(const int nwidth, const label value)
Stringified zero-padded integer value.
Definition: ensightCase.C:38
static const char * elemNames[nTypes]
The ensight &#39;Face&#39; element type names.
Definition: ensightFaces.H:94
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:598
virtual tmp< Field< scalar > > field(const label timeIndex, const label fieldIndex, const scalar &refValue=pTraits< scalar >::zero) const
Return a scalar field at a given time.
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
static const Enum< streamFormat > formatNames
Stream format names (ascii, binary)
List< string > fieldFileNames_
Field file names.
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
static bool & parRun() noexcept
Test if this a parallel run.
Definition: UPstream.H:1049
bool masterOnly_
Read on master and broadcast (in parallel)
List< word > fieldNames_
Field names.
::Foam::direction nComponents(const expressions::valueTypeCode) noexcept
The number of components associated with given valueTypeCode.
Definition: exprTraits.C:40
static label worldComm
Communicator for all ranks. May differ from commGlobal() if local worlds are in use.
Definition: UPstream.H:409
void readLine(ISstream &is, string &buffer) const
Helper function to read an ascii line from file.
Macros for easy insertion into run-time selection tables.
static void broadcast(Type &value, const label comm=UPstream::worldComm)
Broadcast content (contiguous or non-contiguous) to all communicator ranks. Does nothing in non-paral...
fileName meshFileName_
Name of mesh file, including any subdirectory.
List< face > faceList
List of faces.
Definition: faceListFwd.H:39
dimensionedScalar pos(const dimensionedScalar &ds)
void debugSection(const word &expected, ISstream &is) const
Read and check a section header.
void inplaceTrimRight(std::string &s)
Trim trailing whitespace inplace.
Definition: stringOps.C:979
ensightSurfaceReader(const fileName &fName, const dictionary &options=dictionary())
Construct from fileName, with reader options.
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:38
An ordered pair of two objects of type <T> with first() and second() elements.
Definition: instant.H:46
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for expressions::valueTypeCode::INVALID.
Definition: exprTraits.C:127
const pointField & points
A class for handling words, derived from Foam::string.
Definition: word.H:63
label nPoints
MeshedSurface< face > meshedSurface
#define DebugInFunction
Report an information message using Foam::Info.
A variant of IFstream with specialised read() for strings, integers and floats. Correctly handles bin...
static void discard(label n, ensightReadFile &is)
void skip(const label n, Istream &is) const
Helper function to skip forward n steps in stream.
virtual const fileName & name() const override
The name of the input serial stream. (eg, the name of the Fstream file name)
Definition: ISstream.H:141
#define DebugInfo
Report an information message using Foam::Info.
label timeStartIndex_
Start time index.
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
int debug
Static debugging option.
fileName path(UMean.rootPath()/UMean.caseName()/"graphs"/UMean.instance())
label timeIncrement_
Time increment.
defineTypeNameAndDebug(combustionModel, 0)
labelList f(nPoints)
label nTimeSteps_
Number of time steps.
Foam::SubStrings< StringType > splitSpace(const StringType &str)
Split string into sub-strings at whitespace (TAB, NL, VT, FF, CR, SPC)
static void broadcasts(const label comm, Type &arg1, Args &&... args)
Broadcast multiple items to all communicator ranks. Does nothing in non-parallel. ...
virtual instantList times() const
Return a list of the available times.
Generic input stream using a standard (STL) stream.
Definition: ISstream.H:51
vector point
Point is a vector.
Definition: point.H:37
#define WarningInFunction
Report a warning using Foam::Warning.
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:627
bool good() const noexcept
True if next operation might succeed.
Definition: IOstream.H:281
virtual const meshedSurface & geometry(const label timeIndex)
Return a reference to the surface geometry.
Pair< idTypes > readGeometryHeader(ensightReadFile &is) const
Read (and discard) geometry file header.
static bool master(const label communicator=worldComm)
True if process corresponds to the master rank in the communicator.
Definition: UPstream.H:1082
void readCase(ISstream &is)
Read the case file.
static fileName replaceMask(const fileName &fName, const label timeIndex)
Replace the &#39;*&#39; mask chars with a 0 padded string.
messageStream Info
Information stream (stdout output on master, null elsewhere)
label n
List< label > labelList
A List of labels.
Definition: List.H:62
volScalarField & p
A class for managing temporary objects.
Definition: HashPtrTable.H:50
meshedSurface readGeometry(const fileName &geometryFile)
Read and return surface geometry. Updates faceTypeInfo_.
gmvFile<< "tracers "<< particles.size()<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().x()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().y()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().z()<< " ";}gmvFile<< nl;forAll(lagrangianScalarNames, i){ word name=lagrangianScalarNames[i];IOField< scalar > s(IOobject(name, runTime.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
Tensor of scalars, i.e. Tensor<scalar>.
Namespace for OpenFOAM.
label timeIndex
Definition: getTimeIndex.H:24
addToRunTimeSelectionTable(functionObject, pointHistory, dictionary)
IOerror FatalIOError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL IO ERROR&#39; header text and ...