foamVtmWriter.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 <fstream>
29 #include "foamVtmWriter.H"
30 #include "Time.H"
31 #include "OSspecific.H"
32 
33 // * * * * * * * * * * * * * * * * Local Class * * * * * * * * * * * * * * * //
34 
36 {
37  type_ = NONE;
38  name_.clear();
39  file_.clear();
40 }
41 
42 
43 bool Foam::vtk::vtmWriter::vtmEntry::good() const noexcept
44 {
45  return
46  (
47  type_ == vtmEntry::BEGIN_BLOCK
48  || type_ == vtmEntry::END_BLOCK
49  || (type_ == vtmEntry::DATA && file_.size())
50  );
51 }
52 
53 
54 bool Foam::vtk::vtmWriter::vtmEntry::write(vtk::formatter& format) const
55 {
56  if (type_ == vtmEntry::BEGIN_BLOCK)
57  {
58  format.openTag(vtk::fileTag::BLOCK);
59  if (name_.size())
60  {
61  format.xmlAttr("name", name_);
62  }
63  format.closeTag();
64 
65  return true;
66  }
67  else if (type_ == vtmEntry::END_BLOCK)
68  {
69  format.endBlock();
70  return true;
71  }
72  else if (type_ == vtmEntry::DATA && file_.size())
73  {
74  format.openTag(vtk::fileTag::DATA_SET);
75 
76  if (name_.size())
77  {
78  format.xmlAttr("name", name_);
79  }
80 
81  format.xmlAttr("file", file_);
82 
83  format.closeTag(true); // Empty tag. ie, <DataSet ... />
84  return true;
85  }
86 
87  return false;
88 }
89 
90 
91 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
92 
93 bool Foam::vtk::vtmWriter::pruneEmpty()
94 {
95  const label nEntries = entries_.size();
96 
97  label dst=0;
98 
99  for (label src=0; src < nEntries; ++src)
100  {
101  if (entries_[src].good())
102  {
103  if (dst != src)
104  {
105  entries_[dst] = std::move(entries_[src]);
106  }
107  ++dst;
108  }
109  }
110 
111  const bool changed = (dst != nEntries);
112  entries_.resize(dst);
113 
114  return changed;
115 }
116 
117 
118 bool Foam::vtk::vtmWriter::pruneEmptyBlocks()
119 {
120  bool pruned = false;
121 
122  const label nEntries = entries_.size();
123 
124  while (true)
125  {
126  bool changed = false;
127 
128  for (label i=0; i < nEntries; ++i)
129  {
130  vtmEntry& e = entries_[i];
131 
132  if (e.isType(vtmEntry::BEGIN_BLOCK))
133  {
134  for (label j=i+1; j < nEntries; ++j)
135  {
136  if (entries_[j].isType(vtmEntry::END_BLOCK))
137  {
138  e.clear();
139  entries_[j].clear();
140 
141  changed = true;
142  break;
143  }
144  else if (!entries_[j].isType(vtmEntry::NONE))
145  {
146  break;
147  }
148  }
149  }
150  }
151 
152  if (changed)
153  {
154  pruned = true;
155  }
156  else
157  {
158  break;
159  }
160  }
161 
162  // Collapse single-entry blocks when the names allow it
163 
164  // Transcribe, removing NONE entries
165  pruneEmpty();
166 
167  return pruned;
168 }
169 
170 
171 bool Foam::vtk::vtmWriter::collapseBlocks()
172 {
173  bool collapsed = false;
174 
175  const label nEntries = entries_.size();
176 
177  for (label i=0; i < nEntries-2; ++i)
178  {
179  vtmEntry& b = entries_[i]; // begin
180  vtmEntry& d = entries_[i+1]; // data
181  vtmEntry& e = entries_[i+2]; // end
182 
183  if
184  (
185  b.isType(vtmEntry::BEGIN_BLOCK)
186  && e.isType(vtmEntry::END_BLOCK)
187  && d.isType(vtmEntry::DATA)
188  && (d.name_.empty() || d.name_ == b.name_)
189  )
190  {
191  d.name_ = std::move(b.name_);
192 
193  b.clear();
194  e.clear();
195 
196  collapsed = true;
197  }
198  }
200  pruneEmpty();
201 
202  return collapsed;
203 }
204 
205 
206 void Foam::vtk::vtmWriter::repair(bool collapse)
207 {
208  // Add or remove END_BLOCK
209 
210  label depth = 0;
211  label nEntries = 0;
212 
213  for (vtmEntry& e : entries_)
214  {
215  if (e.isType(vtmEntry::BEGIN_BLOCK))
216  {
217  ++depth;
218  }
219  else if (e.isType(vtmEntry::END_BLOCK))
220  {
221  --depth;
222 
223  if (depth < 0)
224  {
225  // Truncate now and exit
226  entries_.resize(nEntries);
227  break;
228  }
229  }
230  else if (e.isType(vtmEntry::DATA))
231  {
232  if (e.file_.empty())
233  {
234  // Bad entry - reset to NONE
235  e.clear();
236  }
237  }
238 
239  ++nEntries;
240  }
241 
242  // Close any dangling blocks
243  while (depth--)
244  {
245  entries_.append(vtmEntry::endblock());
246  }
247 
248  blocks_.clear();
249  pruneEmpty();
250 
251  if (collapse)
252  {
253  pruneEmptyBlocks();
254  collapseBlocks();
255  }
256 }
257 
258 
260 {
261  label depth = 0;
262 
263  // Output format is a mix of dictionary and JSON
264  // the only purpose being for diagnostics
265 
266  for (const vtmEntry& e : entries_)
267  {
268  switch (e.type_)
269  {
270  case vtmEntry::NONE:
271  {
272  os.indent();
273  os << "none" << nl;
274  break;
275  }
276  case vtmEntry::DATA:
277  {
278  os.indent();
279  os << "{ \"name\" : " << e.name_
280  << ", \"file\" : " << e.file_ << " }" << nl;
281  break;
282  }
283  case vtmEntry::BEGIN_BLOCK:
284  {
285  ++depth;
286  os.beginBlock(e.name_);
287  break;
288  }
289  case vtmEntry::END_BLOCK:
290  {
291  --depth;
292  os.endBlock();
293  os << nl;
294  break;
295  }
296  }
297  }
298 
299  for (label i=0; i < depth; ++i)
300  {
301  os.decrIndent();
302  }
303 
304  if (depth > 0)
305  {
306  os << "# Had " << depth << " unclosed blocks" << nl;
307  }
308  if (depth < 0)
309  {
310  os << "# Had " << (-depth) << " too many end blocks" << nl;
311  }
312 }
313 
314 
315 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
318 :
319  vtmWriter(true)
320 {}
321 
322 
323 Foam::vtk::vtmWriter::vtmWriter(bool autoName)
324 :
325  autoName_(autoName),
326  hasTime_(false),
327  entries_(),
328  blocks_(),
329  timeValue_(Zero)
330 {}
331 
332 
333 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
334 
336 {
337  entries_.clear();
338  blocks_.clear();
339 
340  timeValue_ = Zero;
341  hasTime_ = false;
342 }
343 
344 
345 bool Foam::vtk::vtmWriter::empty() const
346 {
347  for (const auto& e : entries_)
348  {
349  if (e.isType(vtmEntry::DATA) && e.name_.size())
350  {
351  return false;
352  }
353  }
354 
355  return true;
356 }
357 
358 
359 Foam::label Foam::vtk::vtmWriter::size() const
360 {
361  label ndata = 0;
362 
363  for (const auto& e : entries_)
364  {
365  if (e.isType(vtmEntry::DATA) && e.file_.size())
366  {
367  ++ndata;
368  }
369  }
370 
371  return ndata;
372 }
373 
374 
375 void Foam::vtk::vtmWriter::setTime(scalar timeValue)
376 {
377  timeValue_ = timeValue;
378  hasTime_ = true;
379 }
380 
381 
383 {
384  timeValue_ = t.value();
385  hasTime_ = true;
386 }
387 
388 
389 Foam::label Foam::vtk::vtmWriter::beginBlock(const word& blockName)
390 {
391  entries_.append(vtmEntry::block(blockName));
392  blocks_.append(blockName);
393 
394  return blocks_.size();
395 }
396 
397 
398 Foam::label Foam::vtk::vtmWriter::endBlock(const word& blockName)
399 {
400  if (!blocks_.empty())
401  {
402  // Verify expected end tag
403  if (!blockName.empty() && blockName != blocks_.back())
404  {
406  << "expecting to end block '" << blockName
407  << "' but found '" << blocks_.back() << "' instead"
408  << endl;
409  }
410 
411  blocks_.pop_back();
412  entries_.push_back(vtmEntry::endblock());
413  }
414 
415  return blocks_.size();
416 }
417 
418 
419 bool Foam::vtk::vtmWriter::append(const fileName& file)
420 {
421  if (autoName_)
422  {
423  return append(fileName::stem(file), file);
424  }
425 
426  return append(word::null, file);
427 }
428 
429 
431 (
432  const fileName& file,
433  vtk::fileTag contentType
434 )
435 {
436  if (autoName_)
437  {
438  return append(fileName::stem(file), file, contentType);
439  }
440 
441  return append(word::null, file, contentType);
442 }
443 
444 
446 (
447  const word& name,
448  const fileName& file
449 )
450 {
451  if (file.empty())
452  {
453  return false;
454  }
456  entries_.append(vtmEntry::entry(name, file));
457  return true;
458 }
459 
460 
462 (
463  const word& name,
464  const fileName& file,
465  vtk::fileTag contentType
466 )
467 {
468  if (file.empty())
469  {
470  return false;
471  }
472 
473  if (file.has_ext(vtk::fileExtension[contentType]))
474  {
475  entries_.append(vtmEntry::entry(name, file));
476  }
477  else
478  {
479  entries_.append
480  (
481  vtmEntry::entry
482  (
483  name,
484  file + "." + vtk::fileExtension[contentType]
485  )
486  );
487  }
488 
489  return true;
490 }
491 
492 
494 (
495  const word& blockName,
496  const fileName& prefix,
497  const vtmWriter& other
498 )
499 {
500  // Standard sanity repair (block ending), prune empty entries
501  repair();
502 
503  beginBlock(blockName);
504 
505  label depth = 0;
506  bool good = true;
507 
508  for (const vtmEntry& e : other.entries_)
509  {
510  switch (e.type_)
511  {
512  case vtmEntry::NONE:
513  {
514  break;
515  }
516  case vtmEntry::DATA:
517  {
518  if (e.good())
519  {
520  entries_.append(e);
521 
522  if (prefix.size())
523  {
524  fileName& f = entries_.last().file_;
525 
526  f = prefix/f;
527  }
528  }
529 
530  break;
531  }
532  case vtmEntry::BEGIN_BLOCK:
533  {
534  ++depth;
535  entries_.append(e);
536  break;
537  }
538  case vtmEntry::END_BLOCK:
539  {
540  good = (depth > 0);
541  --depth;
542  if (good)
543  {
544  entries_.append(e);
545  }
546  break;
547  }
548  }
549 
550  if (!good) break;
551  }
552 
553  while (depth--)
554  {
555  entries_.append(vtmEntry::endblock());
556  }
557 
558  entries_.append(vtmEntry::endblock());
559 
560  if (!hasTime_ && other.hasTime_)
561  {
562  hasTime_ = true;
563  timeValue_ = other.timeValue_;
564  }
565 }
566 
567 
569 (
570  const word& blockName,
571  const vtmWriter& other
572 )
573 {
574  add(blockName, fileName::null, other);
575 }
576 
577 
578 Foam::label Foam::vtk::vtmWriter::write(const fileName& file)
579 {
580  std::ofstream os_;
581 
582  mkDir(file.path());
583 
584  if (file.has_ext(ext()))
585  {
586  os_.open(file);
587  }
588  else
589  {
590  os_.open(file + "." + ext());
591  }
592 
594 
595 
596  // Contents Header
597  {
598  format().xmlHeader();
599 
600  if (hasTime_)
601  {
602  format().xmlComment
603  (
604  "time='" + Foam::name(timeValue_) + "'"
605  );
606  }
607 
608  format().beginVTKFile<vtk::fileTag::MULTI_BLOCK>();
609  }
610 
611 
612  // Walk the block and dataset contents
613 
614  label depth = 0;
615  label ndata = 0;
616 
617  for (const vtmEntry& e : entries_)
618  {
619  switch (e.type_)
620  {
621  case vtmEntry::DATA:
622  {
623  if (e.file_.empty())
624  {
625  continue; // Empty dataset is junk - skip
626  }
627  ++ndata;
628  break;
629  }
630  case vtmEntry::BEGIN_BLOCK:
631  {
632  ++depth;
633  break;
634  }
635  case vtmEntry::END_BLOCK:
636  {
637  --depth;
638  break;
639  }
640  default:
641  {
642  continue;
643  break;
644  }
645  }
646 
647  if (depth < 0)
648  {
649  // Too many end blocks - stop output now. Should we warn?
650  break;
651  }
652  e.write(format());
653  }
654 
655  // Close any dangling blocks
656  while (depth--)
657  {
658  format().endBlock();
659  }
660 
662 
663 
664  // FieldData for TimeValue
665  if (hasTime_)
666  {
667  format()
668  .beginFieldData()
669  .writeTimeValue(timeValue_)
670  .endFieldData();
671  }
672 
673  format().endVTKFile();
674 
675  format.clear();
676  os_.close();
677 
678  return ndata;
679 }
680 
681 
682 // ************************************************************************* //
Provides a means of accumulating file entries for generating a vtkMultiBlockDataSet (...
Definition: foamVtmWriter.H:87
const Type & value() const noexcept
Return const reference to value.
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:116
A class for handling file names.
Definition: fileName.H:72
"vtkMultiBlockDataSet"
word stem() const
Return basename, without extension.
Definition: fileNameI.H:217
label write(const fileName &file)
Open file for writing (creates parent directory) and write the blocks and TimeValue.
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
autoPtr< vtk::formatter > newFormatter(std::ostream &os, unsigned prec=IOstream::defaultPrecision())
Return a default asciiFormatter.
Definition: foamVtkOutput.C:41
static const fileName null
An empty fileName.
Definition: fileName.H:111
void resize(const label len)
Alter addressable list size, allocating new space if required while recovering old content...
Definition: DynamicListI.H:353
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
No type, or default initialized type.
vtmWriter()
Default construct, with autoName on.
static std::string path(const std::string &str)
Return directory path name (part before last /)
Definition: fileNameI.H:169
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition: Time.H:69
label size() const
The number of data sets.
XML inline ASCII, asciiFormatter.
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
void write(vtk::formatter &fmt, const Type &val, const label n=1)
Component-wise write of a value (N times)
bool append(const fileName &file)
Add a file. The name is either empty or created with autoName.
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
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
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for expressions::valueTypeCode::INVALID.
Definition: exprTraits.C:127
const dimensionedScalar b
Wien displacement law constant: default SI units: [m.K].
Definition: createFields.H:27
A class for handling words, derived from Foam::string.
Definition: word.H:63
bool isType(const U &obj)
Check if typeid of the object and Type are identical.
Definition: typeInfo.H:99
void setTime(scalar timeValue)
Define "TimeValue" for FieldData (name as per Catalyst output)
static const word null
An empty word.
Definition: word.H:84
patchWriters clear()
An Ostream is an abstract base class for all output systems (streams, files, token lists...
Definition: Ostream.H:56
const direction noexcept
Definition: Scalar.H:258
void add(FieldField< Field1, typename typeOfSum< Type1, Type2 >::type > &f, const FieldField< Field1, Type1 > &f1, const FieldField< Field2, Type2 > &f2)
void repair(bool collapse=false)
Sanity fixes on the data.
label beginBlock(const word &blockName=word::null)
Start a new block, optionally with a name.
OBJstream os(runTime.globalPath()/outputName)
void dump(Ostream &os) const
Print debug view of block and dataset contents.
labelList f(nPoints)
bool empty() const
If there are no data sets.
word format(conversionProperties.get< word >("format"))
rAUs append(new volScalarField(IOobject::groupName("rAU", phase1.name()), 1.0/(U1Eqn.A()+byDt(max(phase1.residualAlpha() - alpha1, scalar(0)) *rho1))))
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
vtk::vtmWriter vtmWriter
label endBlock(const word &blockName=word::null)
End the previous block, optionally with name checking.
bool has_ext() const
Various checks for extensions.
Definition: stringI.H:43
Ostream & beginBlock(Ostream &os)
Write begin block group without a name.
Definition: Ostream.H:543
void add(const word &blockName, const vtmWriter &other)
Add in content from another vtm and place under the given block name.
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:127
void clear()
Clear all entries and reset output.