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