foamVtkFileWriter.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) 2018-2023 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 "foamVtkFileWriter.H"
29 #include "globalIndex.H"
30 #include "OSspecific.H"
31 
32 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
33 
34 const Foam::Enum
35 <
37 >
39 ({
40  { outputState::CLOSED, "closed" },
41  { outputState::OPENED, "opened" },
42  { outputState::DECLARED, "declared" },
43  { outputState::FIELD_DATA, "FieldData" },
44  { outputState::PIECE, "Piece" },
45  { outputState::CELL_DATA, "CellData" },
46  { outputState::POINT_DATA, "PointData" },
47 });
48 
49 
50 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
51 
53 {
54  // In parallel can be unallocated on non-master nodes
55  if ((parallel_ ? Pstream::master() : true) && !format_)
56  {
58  << "unallocated formatter" << endl
59  << exit(FatalError);
60  }
61 }
62 
63 
65 (
66  Ostream& os,
67  outputState expected
68 ) const
69 {
70  os << "Bad writer state (" << stateNames[state_]
71  << ") - should be (" << stateNames[expected] << ')';
72  return os;
73 }
74 
75 
77 (
78  Ostream& os,
79  outputState expected1,
80  outputState expected2
81 ) const
82 {
83  reportBadState(os, expected1)
84  << " or (" << stateNames[expected2] << ')';
85  return os;
86 }
87 
88 
90 {
91  // Finish other output
92  endFieldData();
93 
94  if (isState(outputState::OPENED))
95  {
96  beginFile();
97  }
98  if (notState(outputState::DECLARED))
99  {
100  reportBadState(FatalErrorInFunction, outputState::DECLARED)
101  << exit(FatalError);
102  }
104  nCellData_ = nPointData_ = 0;
105 
106  return true;
107 }
108 
109 
111 {
112  // Finish other output
113  endCellData();
114  endPointData();
115 
116  if (notState(outputState::PIECE))
117  {
118  // Skip if not in Piece
119  return false;
120  }
121  state_ = outputState::DECLARED; // Mark as having been flushed
122 
123  if (format_)
124  {
125  format().endPiece();
126  }
127 
128  return true;
129 }
130 
131 
132 bool Foam::vtk::fileWriter::enter_CellData(label nEntries, label nFields)
133 {
134  // Already in CellData?
135  if (isState(outputState::CELL_DATA)) return false;
136 
137  // Finish other output
138  endPointData();
139 
140  if (notState(outputState::PIECE))
141  {
143  << exit(FatalError);
144  }
145 
146  nCellData_ = 0;
147 
148  // Do nothing for legacy when nFields == 0
149  if (legacy() && !nFields) return false;
150 
151  state_ = outputState::CELL_DATA;
152 
153  if (format_)
154  {
155  if (legacy())
156  {
157  legacy::beginCellData(format(), nEntries, nFields);
158  }
159  else
160  {
161  format().beginCellData();
162  }
163  }
164 
165  return true;
166 }
167 
168 
169 bool Foam::vtk::fileWriter::enter_PointData(label nEntries, label nFields)
170 {
171  // Already in PointData?
172  if (isState(outputState::POINT_DATA)) return false;
173 
174  // Finish other output
175  endCellData();
176 
177  if (notState(outputState::PIECE))
178  {
180  << exit(FatalError);
181  }
182 
183  nPointData_ = 0;
184 
185  // Do nothing for legacy when nFields == 0
186  if (legacy() && !nFields) return false;
187 
188  state_ = outputState::POINT_DATA;
189 
190  if (format_)
191  {
192  if (legacy())
193  {
194  legacy::beginPointData(format(), nEntries, nFields);
195  }
196  else
197  {
198  format().beginPointData();
199  }
200  }
201 
202  return true;
203 }
204 
205 
207 {
208  if (format_)
209  {
210  format().flush();
211  format().endDataArray();
212  }
213 }
214 
215 
217 {
218  if (format_)
219  {
220  if (legacy())
221  {
223  }
224  else
225  {
226  const uint64_t payLoad =
227  vtk::sizeofData<float, 3>(nPoints);
228 
229  format()
231  .beginDataArray<float, 3>(vtk::dataArrayAttr::POINTS);
233  format().writeSize(payLoad);
234  }
235  }
236 }
237 
238 
240 {
241  if (format_)
242  {
243  format().flush();
244  format().endDataArray();
245 
246  if (!legacy())
247  {
249  .endTag(vtk::fileTag::POINTS);
250  }
251  }
252 }
253 
254 
256 {
257  // Finish other output
258  endFieldData();
259  endPiece();
260 
261  if (isState(outputState::DECLARED))
262  {
263  if (format_ && !legacy())
264  {
265  format().endTag(contentType_).endVTKFile();
266  }
267  state_ = outputState::OPENED; // Mark as having been flushed
268  }
269 
270  // Must now be in CLOSED or OPENED states only
271 
272  if (isState(outputState::CLOSED) || isState(outputState::OPENED))
273  {
274  return true;
275  }
276 
277  reportBadState(WarningInFunction, outputState::CLOSED, outputState::OPENED)
278  << " for contentType (" << vtk::fileTagNames[contentType_] << ')'
279  << nl << endl;
280 
281  return false;
282 }
283 
284 
285 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
286 
288 (
289  const vtk::fileTag contentType,
290  const vtk::outputOptions opts
291 )
292 :
293  state_(outputState::CLOSED),
294  contentType_(contentType),
295  parallel_(false),
296  opts_(opts),
297  nCellData_(0),
298  nPointData_(0),
299  outputFile_(),
300  format_(nullptr),
301  os_()
302 {
303  // We do not currently support append mode at all
304  opts_.append(false);
305 }
306 
307 
308 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
309 
311 {
312  close();
313 }
314 
315 
316 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
317 
318 bool Foam::vtk::fileWriter::open(const fileName& file, bool parallel)
319 {
320  if (notState(outputState::CLOSED))
321  {
322  reportBadState(FatalErrorInFunction, outputState::CLOSED)
323  << exit(FatalError);
324  }
325 
326  if (format_)
327  {
328  format_.reset(nullptr);
329  os_.close();
330  }
331  nCellData_ = nPointData_ = 0;
332  outputFile_ = file;
333 
334  if
335  (
336  legacy()
337  ? outputFile_.has_ext(vtk::fileExtension[contentType_])
338  : outputFile_.has_ext(vtk::legacy::fileExtension)
339  )
340  {
341  // Inappropriate extension. Legacy instead of xml, or vice versa.
342  outputFile_.remove_ext();
343  }
344 
345  if (!outputFile_.has_ext(ext()))
346  {
347  // Add extension if required
348  outputFile_.ext(ext());
349  }
350 
351 
352  // Only set parallel flag if really is a parallel run.
353  parallel_ = parallel && Pstream::parRun();
354 
355  // Open a file and attach a formatter
356  // - on master (always)
357  // - on subproc (if not parallel)
358  //
359  // This means we can always check if format_ is defined to know if output
360  // is desired on any particular process.
361 
362  if (!parallel_ || Pstream::master())
363  {
364  mkDir(outputFile_.path());
365 
366  os_.open(outputFile_);
367 
368  format_ = opts_.newFormatter(os_);
369  }
370 
371  state_ = outputState::OPENED;
372  return true;
373 }
374 
375 
377 {
378  exit_File();
379 
380  if (format_)
381  {
382  format_.reset(nullptr);
383  os_.close();
384  }
386  state_ = outputState::CLOSED;
387  outputFile_.clear();
388  nCellData_ = nPointData_ = 0;
389 }
390 
391 
392 bool Foam::vtk::fileWriter::beginFile(std::string title)
393 {
394  if (isState(outputState::DECLARED))
395  {
396  // Skip if already emitted
397  return false;
398  }
399  if (notState(outputState::OPENED))
400  {
401  reportBadState(FatalErrorInFunction, outputState::OPENED)
402  << exit(FatalError);
403  }
404  state_ = outputState::DECLARED;
405 
406  if (format_)
407  {
408  if (legacy())
409  {
410  legacy::fileHeader(format(), title, contentType_);
411  }
412  else
413  {
414  // XML (inline)
415 
416  format().xmlHeader();
417 
418  if (title.size())
419  {
420  format().xmlComment(title);
421  }
422 
423  format().beginVTKFile(contentType_);
424  }
425  }
426 
427  return true;
428 }
429 
430 
431 bool Foam::vtk::fileWriter::beginFieldData(label nFields)
432 {
433  // Do nothing for legacy when nFields == 0
434  if (legacy() && !nFields) return false;
435 
436  if (isState(outputState::OPENED))
437  {
438  beginFile();
439  }
440  if (notState(outputState::DECLARED))
441  {
442  reportBadState(FatalErrorInFunction, outputState::DECLARED)
443  << exit(FatalError);
444  }
445  state_ = outputState::FIELD_DATA;
446 
447  if (format_)
448  {
449  if (legacy())
450  {
451  legacy::beginFieldData(format(), nFields);
452  }
453  else
454  {
455  format().beginFieldData();
456  }
457  }
458 
459  return true;
460 }
461 
462 
464 {
465  if (notState(outputState::FIELD_DATA))
466  {
467  // Skip if not in FieldData
468  return false;
469  }
470  state_ = outputState::DECLARED; // Toggle back to DECLARED
471 
472  if (format_ && !legacy())
473  {
474  format().endFieldData();
475  }
476 
477  return true;
478 }
479 
480 
482 {
483  if (notState(outputState::CELL_DATA))
484  {
485  // Skip if not in CellData
486  return false;
487  }
488  state_ = outputState::PIECE; // Toggle back to PIECE
489 
490  if (format_ && !legacy())
491  {
492  format().endCellData();
493  }
494 
495  return true;
496 }
497 
498 
500 {
501  if (notState(outputState::POINT_DATA))
502  {
503  // Skip if not in PointData
504  return false;
505  }
506  state_ = outputState::PIECE; // Toggle back to PIECE
507 
508  if (format_ && !legacy())
509  {
510  format().endPointData();
511  }
512 
513  return true;
514 }
515 
516 
517 void Foam::vtk::fileWriter::writeTimeValue(scalar timeValue)
518 {
519  // Convenience - switch to FieldData
520  if (isState(outputState::OPENED) || isState(outputState::DECLARED))
521  {
522  beginFieldData(1);
523  }
524  if (notState(outputState::FIELD_DATA))
525  {
527  << exit(FatalError);
528  }
529 
530  // No collectives - can skip on sub-procs
531  if (!format_) return;
532 
533  if (legacy())
534  {
535  legacy::writeTimeValue(format(), timeValue);
536  }
537  else
538  {
539  format().writeTimeValue(timeValue);
540  }
541 }
542 
543 
544 bool Foam::vtk::fileWriter::writeProcIDs(const label nValues)
545 {
546  // Write procIDs whenever running in parallel
547 
548  if (!Pstream::parRun())
549  {
550  return false; // Non-parallel: skip
551  }
552 
553  if (isState(outputState::CELL_DATA))
554  {
555  ++nCellData_;
556  }
557  else if (isState(outputState::POINT_DATA))
558  {
559  ++nPointData_;
560  }
561  else
562  {
563  reportBadState
564  (
568  ) << " for procID field" << nl << endl
569  << exit(FatalError);
570 
571  return false;
572  }
573 
574 
575  const globalIndex procAddr
576  (
577  parallel_
578  ? globalIndex(globalIndex::gatherOnly{}, nValues)
579  : globalIndex(globalIndex::gatherNone{}, nValues)
580  );
581 
582  const label totalCount = procAddr.totalSize();
583 
584  this->beginDataArray<label>("procID", totalCount);
585 
586  bool good = false;
587 
588  if (parallel_)
589  {
590  if (Pstream::master())
591  {
592  // Per-processor ids
593  for (const label proci : procAddr.allProcs())
594  {
595  vtk::write(format(), proci, procAddr.localSize(proci));
596  }
597  good = true;
598  }
599  }
600  else
601  {
602  vtk::write(format(), label(Pstream::myProcNo()), totalCount);
603  good = true;
604  }
605 
606 
607  this->endDataArray();
608 
609  // MPI barrier
610  return parallel_ ? returnReduceOr(good) : good;
611 }
612 
613 
614 // ************************************************************************* //
autoPtr< vtk::formatter > format_
The VTK formatter in use (only valid on master process)
A class for handling file names.
Definition: fileName.H:72
void checkFormatterValidity() const
Verify that formatter in either allocated or not required.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
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
bool append() const noexcept
True if output format uses an append mode.
const word fileExtension
Legacy file extension ("vtk")
vtk::outputOptions opts_
Requested output options.
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
void endDataArray()
Flush formatter and end of DataArray output (non-legacy)
bool enter_CellData(label nEntries, label nFields)
Trigger change state to CellData.
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 enter_PointData(label nEntries, label nFields)
Trigger change state to PointData.
void beginFieldData(vtk::formatter &fmt, label nFields)
Emit legacy FIELD FieldData nFields.
void beginPointData(vtk::formatter &fmt, label nPoints, label nFields)
Emit legacy POINT_DATA nPoints, FIELD FieldData nFields.
void writeTimeValue(scalar timeValue)
Write "TimeValue" FieldData (name as per Catalyst output)
static int myProcNo(const label communicator=worldComm)
Rank of this process in the communicator (starting from masterNo()). Can be negative if the process i...
Definition: UPstream.H:1074
outputState
Internal tracking of the output state.
fileWriter(const fileWriter &)=delete
No copy construct.
bool endFieldData()
Explicitly end FieldData output and switch to DECLARED state.
bool parallel_
Parallel writing (via master)
void endPoints()
End of a POINTS DataArray.
Encapsulated combinations of output format options. This is primarily useful when defining the output...
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
Ostream & reportBadState(Ostream &, outputState expected) const
Generate message reporting bad writer state.
void write(vtk::formatter &fmt, const Type &val, const label n=1)
Component-wise write of a value (N times)
void beginPoints(std::ostream &os, label nPoints)
Emit header for POINTS (with trailing newline).
virtual ~fileWriter()
Destructor.
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
bool enter_Piece()
Trigger change state to Piece. Resets nCellData_, nPointData_.
void fileHeader(std::ostream &os, const std::string &title, bool binary)
Emit header for legacy file (vtk DataFile Version 2.0)
bool writeProcIDs(const label nValues)
Write nValues of processor ids as CellData or PointData (no-op in serial)
label nPoints
void writeTimeValue(vtk::formatter &fmt, scalar timeValue)
Emit "TimeValue" for a FIELD entry (name as per Catalyst output)
bool endCellData()
Explicitly end CellData output and switch to PIECE state.
void beginCellData(vtk::formatter &fmt, label nCells, label nFields)
Emit legacy CELL_DATA nCells, FIELD FieldData nFields.
bool endPointData()
Explicitly end PointData output and switch to PIECE state.
An Ostream is an abstract base class for all output systems (streams, files, token lists...
Definition: Ostream.H:56
OBJstream os(runTime.globalPath()/outputName)
virtual bool open(const fileName &file, bool parallel=UPstream::parRun())
Open file for writing (creates parent directory).
bool remove_ext()
Remove extension, returning true if string changed.
Definition: stringI.H:93
const Foam::Enum< fileTag > fileTagNames
Strings corresponding to the vtk XML tags.
bool beginFieldData(label nFields=0)
Begin FieldData output section for specified number of fields.
word format(conversionProperties.get< word >("format"))
virtual bool beginFile(std::string title="")
Write file header (non-collective)
void beginPoints(const label nPoints)
Start of a POINTS DataArray.
const Foam::Enum< fileTag > fileExtension
File extension (without ".") for some vtk XML file content types.
#define WarningInFunction
Report a warning using Foam::Warning.
fileTag
Some common XML tags for vtk files.
Definition: foamVtkCore.H:122
globalIndex procAddr(aMesh.nFaces())
static const Enum< outputState > stateNames
Names for the output state (for messages, not for file output).
Enum is a wrapper around a list of names/values that represent particular enumeration (or int) values...
Definition: error.H:64
void close()
End the file contents and close the file after writing.
static bool master(const label communicator=worldComm)
True if process corresponds to the master rank in the communicator.
Definition: UPstream.H:1082
bool endPiece()
Explicitly end Piece output and switch to DECLARED state.
bool returnReduceOr(const bool value, const label comm=UPstream::worldComm)
Perform logical (or) MPI Allreduce on a copy. Uses UPstream::reduceOr.
bool exit_File()
Emit file footer (end data, end piece, end file)