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-2019 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
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  }
199 
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 * * * * * * * * * * * * * * //
316 
318 :
319  vtmWriter(true)
320 {}
321 
322 
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 
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  label nblock = blocks_.size();
401 
402  if (nblock)
403  {
404  const word curr(blocks_.remove());
405 
406  // Verify expected end tag
407  if (!blockName.empty() && blockName != curr)
408  {
410  << "expecting to end block '" << blockName
411  << "' but found '" << curr << "' instead"
412  << endl;
413  }
414 
415  entries_.append(vtmEntry::endblock());
416  }
417 
418  return blocks_.size();
419 }
420 
421 
423 {
424  if (autoName_)
425  {
426  return append(fileName::nameLessExt(file), file);
427  }
428 
429  return append(word::null, file);
430 }
431 
432 
434 (
435  const fileName& file,
436  vtk::fileTag contentType
437 )
438 {
439  if (autoName_)
440  {
441  return append(fileName::nameLessExt(file), file, contentType);
442  }
443 
444  return append(word::null, file, contentType);
445 }
446 
447 
449 (
450  const word& name,
451  const fileName& file
452 )
453 {
454  if (file.empty())
455  {
456  return false;
457  }
458 
459  entries_.append(vtmEntry::entry(name, file));
460  return true;
461 }
462 
463 
465 (
466  const word& name,
467  const fileName& file,
468  vtk::fileTag contentType
469 )
470 {
471  if (file.empty())
472  {
473  return false;
474  }
475 
476  if (file.hasExt(vtk::fileExtension[contentType]))
477  {
478  entries_.append(vtmEntry::entry(name, file));
479  }
480  else
481  {
482  entries_.append
483  (
484  vtmEntry::entry
485  (
486  name,
487  file + "." + vtk::fileExtension[contentType]
488  )
489  );
490  }
491 
492  return true;
493 }
494 
495 
497 (
498  const word& blockName,
499  const fileName& prefix,
500  const vtmWriter& other
501 )
502 {
503  // Standard sanity repair (block ending), prune empty entries
504  repair();
505 
506  beginBlock(blockName);
507 
508  label depth = 0;
509  bool good = true;
510 
511  for (const vtmEntry& e : other.entries_)
512  {
513  switch (e.type_)
514  {
515  case vtmEntry::NONE:
516  {
517  break;
518  }
519  case vtmEntry::DATA:
520  {
521  if (e.good())
522  {
523  entries_.append(e);
524 
525  if (prefix.size())
526  {
527  fileName& f = entries_.last().file_;
528 
529  f = prefix/f;
530  }
531  }
532 
533  break;
534  }
535  case vtmEntry::BEGIN_BLOCK:
536  {
537  ++depth;
538  entries_.append(e);
539  break;
540  }
541  case vtmEntry::END_BLOCK:
542  {
543  good = (depth > 0);
544  --depth;
545  if (good)
546  {
547  entries_.append(e);
548  }
549  break;
550  }
551  }
552 
553  if (!good) break;
554  }
555 
556  while (depth--)
557  {
558  entries_.append(vtmEntry::endblock());
559  }
560 
561  entries_.append(vtmEntry::endblock());
562 
563  if (!hasTime_ && other.hasTime_)
564  {
565  hasTime_ = true;
566  timeValue_ = other.timeValue_;
567  }
568 }
569 
570 
572 (
573  const word& blockName,
574  const vtmWriter& other
575 )
576 {
577  add(blockName, fileName::null, other);
578 }
579 
580 
581 Foam::label Foam::vtk::vtmWriter::write(const fileName& file)
582 {
583  std::ofstream os_;
584 
585  mkDir(file.path());
586 
587  if (file.hasExt(ext()))
588  {
589  os_.open(file);
590  }
591  else
592  {
593  os_.open(file + "." + ext());
594  }
595 
597 
598 
599  // Contents Header
600  {
601  format().xmlHeader();
602 
603  if (hasTime_)
604  {
605  format().xmlComment
606  (
607  "time='" + Foam::name(timeValue_) + "'"
608  );
609  }
610 
611  format().beginVTKFile<vtk::fileTag::MULTI_BLOCK>();
612  }
613 
614 
615  // Walk the block and dataset contents
616 
617  label depth = 0;
618  label ndata = 0;
619 
620  for (const vtmEntry& e : entries_)
621  {
622  switch (e.type_)
623  {
624  case vtmEntry::DATA:
625  {
626  if (e.file_.empty())
627  {
628  continue; // Empty dataset is junk - skip
629  }
630  ++ndata;
631  break;
632  }
633  case vtmEntry::BEGIN_BLOCK:
634  {
635  ++depth;
636  break;
637  }
638  case vtmEntry::END_BLOCK:
639  {
640  --depth;
641  break;
642  }
643  default:
644  {
645  continue;
646  break;
647  }
648  }
649 
650  if (depth < 0)
651  {
652  // Too many end blocks - stop output now. Should we warn?
653  break;
654  }
655  e.write(format());
656  }
657 
658  // Close any dangling blocks
659  while (depth--)
660  {
661  format().endBlock();
662  }
663 
665 
666 
667  // FieldData for TimeValue
668  if (hasTime_)
669  {
670  format()
671  .beginFieldData()
672  .writeTimeValue(timeValue_)
673  .endFieldData();
674  }
675 
676  format().endVTKFile();
677 
678  format.clear();
679  os_.close();
680 
681  return ndata;
682 }
683 
684 
685 // ************************************************************************* //
Foam::DynamicList::resize
void resize(const label len)
Definition: DynamicListI.H:353
Foam::vtk::vtmWriter::setTime
void setTime(scalar timeValue)
Define "TimeValue" for FieldData (name as per Catalyst output)
Definition: foamVtmWriter.C:375
Foam::vtk::vtmWriter::add
void add(const word &blockName, const vtmWriter &other)
Definition: foamVtmWriter.C:572
OSspecific.H
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
Foam::vtk::vtmWriter::beginBlock
label beginBlock(const word &blockName=word::null)
Start a new block, optionally with a name.
Definition: foamVtmWriter.C:389
Foam::Time
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition: Time.H:73
Foam::expressions::valueTypeCode::NONE
No type, or default initialized type.
Foam::word
A class for handling words, derived from Foam::string.
Definition: word.H:65
Foam::fileName
A class for handling file names.
Definition: fileName.H:73
Foam::fileName::path
static std::string path(const std::string &str)
Return directory path name (part before last /)
Definition: fileNameI.H:176
Foam::Zero
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:131
Foam::vtk::fileExtension
const Foam::Enum< fileTag > fileExtension
File extension (without ".") for some vtk XML file content types.
Foam::vtk::formatType::INLINE_ASCII
XML inline ASCII, asciiFormatter.
Foam::vtk::vtmWriter::endBlock
label endBlock(const word &blockName=word::null)
End the previous block, optionally with name checking.
Definition: foamVtmWriter.C:398
Foam::fileName::nameLessExt
word nameLessExt() const
Return basename, without extension.
Definition: fileNameI.H:224
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:369
Foam::dimensioned::value
const Type & value() const
Return const reference to value.
Definition: dimensionedType.C:434
append
rAUs append(new volScalarField(IOobject::groupName("rAU", phase1.name()), 1.0/(U1Eqn.A()+byDt(max(phase1.residualAlpha() - alpha1, scalar(0)) *rho1))))
Foam::vtk::vtmWriter::dump
void dump(Ostream &os) const
Print debug view of block and dataset contents.
Definition: foamVtmWriter.C:259
Foam::vtk::vtmWriter::size
label size() const
The number of data sets.
Definition: foamVtmWriter.C:359
foamVtmWriter.H
Foam::vtk::vtmWriter::empty
bool empty() const
If there are no data sets.
Definition: foamVtmWriter.C:345
format
word format(conversionProperties.get< word >("format"))
Foam::constant::physicoChemical::b
const dimensionedScalar b
Wien displacement law constant: default SI units: [m.K].
Definition: createFields.H:27
Foam::fileName::hasExt
bool hasExt() const
Various checks for extensions.
Definition: stringI.H:56
Foam::vtk::vtmWriter::write
label write(const fileName &file)
Definition: foamVtmWriter.C:581
Foam::vtk::vtmWriter::clear
void clear()
Clear all entries and reset output.
Definition: foamVtmWriter.C:335
Foam::add
void add(FieldField< Field1, typename typeOfSum< Type1, Type2 >::type > &f, const FieldField< Field1, Type1 > &f1, const FieldField< Field2, Type2 > &f2)
Definition: FieldFieldFunctions.C:939
os
OBJstream os(runTime.globalPath()/outputName)
Foam::beginBlock
Ostream & beginBlock(Ostream &os)
Write begin block group without a name.
Definition: Ostream.H:378
Foam::vtk::vtmWriter::vtmWriter
vtmWriter()
Construct null, with autoName on.
Definition: foamVtmWriter.C:317
Foam::vtk::fileTag
fileTag
Some common XML tags for vtk files.
Definition: foamVtkCore.H:113
Foam::vtk::vtmWriter::append
bool append(const fileName &file)
Add a file. The name is either empty or created with autoName.
Definition: foamVtmWriter.C:422
Foam::fileName::null
static const fileName null
An empty fileName.
Definition: fileName.H:101
Foam::isType
bool isType(const Type &t)
Check is typeid is identical to the TargetType.
Definition: typeInfo.H:218
Time.H
Foam::vtk::fileTag::MULTI_BLOCK
"vtkMultiBlockDataSet"
Foam::vtk::vtmWriter
Provides a means of accumulating file entries for generating a vtkMultiBlockDataSet (....
Definition: foamVtmWriter.H:91
clear
patchWriters clear()
Foam::nl
constexpr char nl
Definition: Ostream.H:404
f
labelList f(nPoints)
Foam::constant::electromagnetic::e
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
Foam::word::null
static const word null
An empty word.
Definition: word.H:80
Foam::vtk::write
void write(vtk::formatter &fmt, const Type &val, const label n=1)
Component-wise write of a value (N times)
Definition: foamVtkOutputTemplates.C:36
Foam::name
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for INVALID.
Definition: exprTraits.C:59
Foam::vtk::newFormatter
autoPtr< vtk::formatter > newFormatter(std::ostream &os, unsigned prec=IOstream::defaultPrecision())
Return a default asciiFormatter.
Definition: foamVtkOutput.C:48
Foam::Ostream
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:56
Foam::vtk::vtmWriter::repair
void repair(bool collapse=false)
Sanity fixes on the data.
Definition: foamVtmWriter.C:206
Foam::mkDir
bool mkDir(const fileName &pathName, mode_t mode=0777)
Make a directory and return an error if it could not be created.
Definition: MSwindows.C:507
WarningInFunction
#define WarningInFunction
Report a warning using Foam::Warning.
Definition: messageStream.H:328