vtkmultiwriter.hh
Go to the documentation of this file.
1// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2// vi: set et ts=4 sw=4 sts=4:
3/*
4 This file is part of the Open Porous Media project (OPM).
5
6 OPM is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 2 of the License, or
9 (at your option) any later version.
10
11 OPM is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with OPM. If not, see <http://www.gnu.org/licenses/>.
18
19 Consult the COPYING file in the top-level source directory of this
20 module for the precise wording of the license and the list of
21 copyright holders.
22*/
28#ifndef EWOMS_VTK_MULTI_WRITER_HH
29#define EWOMS_VTK_MULTI_WRITER_HH
30
31#include <dune/common/fvector.hh>
32#include <dune/common/version.hh>
33#include <dune/grid/io/file/vtk/vtkwriter.hh>
34#include <dune/istl/bvector.hh>
35
36#include <opm/material/common/Valgrind.hpp>
37
42
44
45#include <cstddef>
46#include <filesystem>
47#include <fstream>
48#include <memory>
49#include <sstream>
50#include <string>
51#include <string_view>
52#include <vector>
53
54namespace Opm {
55
63template <class GridView, Dune::VTK::OutputType vtkFormat>
65{
66 class WriteDataTasklet : public TaskletInterface
67 {
68 public:
69 explicit WriteDataTasklet(VtkMultiWriter& multiWriter)
70 : multiWriter_(multiWriter)
71 {}
72
73 void run() override final
74 {
75 std::string fileName;
76 // write the actual data as vtu or vtp (plus the pieces file in the parallel case)
77 if (multiWriter_.commSize_ > 1) {
78 fileName = multiWriter_.curWriter_->pwrite(/*name=*/multiWriter_.curOutFileName_,
79 /*path=*/multiWriter_.outputDir_,
80 /*extendPath=*/"",
81 vtkFormat);
82 }
83 else {
84 fileName = multiWriter_.curWriter_->write(/*name=*/multiWriter_.outputDir_ + "/" +
85 multiWriter_.curOutFileName_,
86 vtkFormat);
87 }
88
89 // determine name to write into the multi-file for the
90 // current time step
91 // The file names in the pvd file are relative, the path should therefore be stripped.
92 const std::filesystem::path fullPath{fileName};
93 const std::string localFileName = fullPath.filename();
94 multiWriter_.multiFile_.precision(16);
95 multiWriter_.multiFile_ << " <DataSet timestep=\"" << multiWriter_.curTime_ << "\" file=\""
96 << localFileName << "\"/>\n";
97 }
98
99 private:
100 VtkMultiWriter& multiWriter_;
101 };
102
103 enum { dim = GridView::dimension };
104
105 using VertexMapper = Dune::MultipleCodimMultipleGeomTypeMapper<GridView>;
106 using ElementMapper = Dune::MultipleCodimMultipleGeomTypeMapper<GridView>;
107
108public:
115
116 using VtkWriter = Dune::VTKWriter<GridView>;
117
118 VtkMultiWriter(bool asyncWriting,
119 const GridView& gridView,
120 const std::string& outputDir,
121 const std::string& simName = "",
122 const std::string& multiFileName = "")
123 : gridView_(gridView)
124 , elementMapper_(gridView, Dune::mcmgElementLayout())
125 , vertexMapper_(gridView, Dune::mcmgVertexLayout())
126 , outputDir_(outputDir.empty() ? "." : outputDir)
127 , simName_(simName.empty() ? "sim" : simName)
128 , multiFileName_(multiFileName.empty() ? outputDir_ + "/" + simName_ + ".pvd" : multiFileName)
129 , commSize_(gridView.comm().size())
130 , commRank_(gridView.comm().rank())
131 , curWriterNum_(0)
132 , taskletRunner_(/*numThreads=*/asyncWriting ? 1 : 0)
133 {}
134
136 {
137 taskletRunner_.barrier();
138 releaseBuffers_();
139 finishMultiFile_();
140
141 if (commRank_ == 0) {
142 multiFile_.close();
143 }
144 }
145
149 int curWriterNum() const
150 { return curWriterNum_; }
151
167 void gridViewChanged(const GridView& gridView)
168 {
169 gridView_ = gridView;
170 gridChanged();
171 }
172
174 {
175 elementMapper_.update(gridView_);
176 vertexMapper_.update(gridView_);
177 }
178
182 void beginWrite(double t) override
183 {
184 if (!multiFile_.is_open()) {
185 startMultiFile_(multiFileName_);
186 }
187
188 // make sure that all previous output has been written and no other thread
189 // accesses the memory used as the target for the extracted quantities
190 taskletRunner_.barrier();
191 releaseBuffers_();
192
193 curTime_ = t;
194 curOutFileName_ = fileName_();
195
196 curWriter_ = std::make_unique<VtkWriter>(gridView_, Dune::VTK::conforming);
197 ++curWriterNum_;
198 }
199
207 {
208 managedScalarBuffers_.emplace_back(std::make_unique<ScalarBuffer>(numEntities));
209 return managedScalarBuffers_.back().get();
210 }
211
218 VectorBuffer* allocateManagedVectorBuffer(std::size_t numOuter, std::size_t numInner)
219 {
220 managedVectorBuffers_.emplace_back(std::make_unique<VectorBuffer>(numOuter));
221 for (auto& buffer : *managedVectorBuffers_.back()) {
222 buffer.resize(numInner);
223 }
224
225 return managedVectorBuffers_.back().get();
226 }
227
244 void attachScalarVertexData(ScalarBuffer& buf, std::string_view name) override
245 {
246 sanitizeScalarBuffer_(buf);
247
249 curWriter_->addVertexData(std::make_shared<VtkFn>(name,
250 gridView_,
251 vertexMapper_,
252 buf,
253 /*codim=*/dim));
254 }
255
271 void attachScalarElementData(ScalarBuffer& buf, std::string_view name) override
272 {
273 sanitizeScalarBuffer_(buf);
274
276 curWriter_->addCellData(std::make_shared<VtkFn>(name,
277 gridView_,
278 elementMapper_,
279 buf,
280 /*codim=*/0));
281 }
282
299 void attachVectorVertexData(VectorBuffer& buf, std::string_view name) override
300 {
301 sanitizeVectorBuffer_(buf);
302
304 curWriter_->addVertexData(std::make_shared<VtkFn>(name,
305 gridView_,
306 vertexMapper_,
307 buf,
308 /*codim=*/dim));
309 }
310
314 void attachTensorVertexData(TensorBuffer& buf, std::string_view name) override
315 {
317
318 for (unsigned colIdx = 0; colIdx < buf[0].N(); ++colIdx) {
319 std::ostringstream oss;
320 oss << name << "[" << colIdx << "]";
321 curWriter_->addVertexData(std::make_shared<VtkFn>(oss.str(),
322 gridView_,
323 vertexMapper_,
324 buf,
325 /*codim=*/dim,
326 colIdx));
327 }
328 }
329
345 void attachVectorElementData(VectorBuffer& buf, std::string_view name) override
346 {
347 sanitizeVectorBuffer_(buf);
348
350 curWriter_->addCellData(std::make_shared<VtkFn>(name,
351 gridView_,
352 elementMapper_,
353 buf,
354 /*codim=*/0));
355 }
356
360 void attachTensorElementData(TensorBuffer& buf, std::string_view name) override
361 {
363
364 for (unsigned colIdx = 0; colIdx < buf[0].N(); ++colIdx) {
365 std::ostringstream oss;
366 oss << name << "[" << colIdx << "]";
367 curWriter_->addCellData(std::make_shared<VtkFn>(oss.str(),
368 gridView_,
369 elementMapper_,
370 buf,
371 /*codim=*/0,
372 colIdx));
373 }
374 }
375
383 void endWrite(bool onlyDiscard) override
384 {
385 if (!onlyDiscard) {
386 auto tasklet = std::make_shared<WriteDataTasklet>(*this);
387 taskletRunner_.dispatch(tasklet);
388 }
389 else {
390 --curWriterNum_;
391 }
392
393 // temporarily write the closing XML mumbo-jumbo to the mashup
394 // file so that the data set can be loaded even if the
395 // simulation is aborted (or not yet finished)
396 finishMultiFile_();
397 }
398
402 template <class Restarter>
403 void serialize(Restarter& res)
404 {
405 res.serializeSectionBegin("VTKMultiWriter");
406 res.serializeStream() << curWriterNum_ << "\n";
407
408 if (commRank_ == 0) {
409 std::streamsize fileLen = 0;
410 std::streamoff filePos = 0;
411 if (multiFile_.is_open()) {
412 // write the meta file into the restart file
413 filePos = multiFile_.tellp();
414 multiFile_.seekp(0, std::ios::end);
415 fileLen = multiFile_.tellp();
416 multiFile_.seekp(filePos);
417 }
418
419 res.serializeStream() << fileLen << " " << filePos << "\n";
420
421 if (fileLen > 0) {
422 std::ifstream multiFileIn(multiFileName_.c_str());
423 std::vector<char> tmp(fileLen);
424 multiFileIn.read(tmp.data(), static_cast<long>(fileLen));
425 res.serializeStream().write(tmp.data(), fileLen);
426 }
427 }
428
429 res.serializeSectionEnd();
430 }
431
435 template <class Restarter>
436 void deserialize(Restarter& res)
437 {
438 res.deserializeSectionBegin("VTKMultiWriter");
439 res.deserializeStream() >> curWriterNum_;
440
441 if (commRank_ == 0) {
442 std::string dummy;
443 std::getline(res.deserializeStream(), dummy);
444
445 // recreate the meta file from the restart file
446 std::streamoff filePos;
447 std::streamsize fileLen;
448 res.deserializeStream() >> fileLen >> filePos;
449 std::getline(res.deserializeStream(), dummy);
450 if (multiFile_.is_open()) {
451 multiFile_.close();
452 }
453
454 if (fileLen > 0) {
455 multiFile_.open(multiFileName_);
456
457 std::vector<char> tmp(fileLen);
458 res.deserializeStream().read(tmp.data(), fileLen);
459 multiFile_.write(tmp.data(), fileLen);
460 }
461
462 multiFile_.seekp(filePos);
463 }
464 else {
465 std::string tmp;
466 std::getline(res.deserializeStream(), tmp);
467 }
468 res.deserializeSectionEnd();
469 }
470
471private:
472 std::string fileName_() const
473 {
474 // use a new file name for each time step
475 std::ostringstream oss;
476 oss << simName_ << "-"
477 << std::setw(5) << std::setfill('0') << curWriterNum_;
478 return oss.str();
479 }
480
481 static std::string fileSuffix_()
482 { return (GridView::dimension == 1) ? "vtp" : "vtu"; }
483
484 void startMultiFile_(const std::string& multiFileName)
485 {
486 // only the first process writes to the multi-file
487 if (commRank_ == 0) {
488 // generate one meta vtk-file holding the individual time steps
489 multiFile_.open(multiFileName);
490 multiFile_ << "<?xml version=\"1.0\"?>\n"
491 "<VTKFile type=\"Collection\"\n"
492 " version=\"0.1\"\n"
493 " byte_order=\"LittleEndian\"\n"
494 " compressor=\"vtkZLibDataCompressor\">\n"
495 " <Collection>\n";
496 }
497 }
498
499 void finishMultiFile_()
500 {
501 // only the first process writes to the multi-file
502 if (commRank_ == 0) {
503 // make sure that we always have a working meta file
504 std::ofstream::pos_type pos = multiFile_.tellp();
505 multiFile_ << " </Collection>\n"
506 "</VTKFile>\n";
507 multiFile_.seekp(pos);
508 multiFile_.flush();
509 }
510 }
511
512 // make sure the field is well defined if running under valgrind
513 // and make sure that all values can be displayed by paraview
514 void sanitizeScalarBuffer_(ScalarBuffer&)
515 {
516 // nothing to do: this is done by VtkScalarFunction
517 }
518
519 void sanitizeVectorBuffer_(VectorBuffer&)
520 {
521 // nothing to do: this is done by VtkVectorFunction
522 }
523
524 // release the memory occupied by all buffer objects managed by the multi-writer
525 void releaseBuffers_()
526 {
527 // discard managed objects and the current VTK writer
528 curWriter_.reset();
529 managedScalarBuffers_.clear();
530 managedVectorBuffers_.clear();
531 }
532
533 GridView gridView_;
534 ElementMapper elementMapper_;
535 VertexMapper vertexMapper_;
536
537 const std::string outputDir_;
538 const std::string simName_;
539 std::ofstream multiFile_;
540 const std::string multiFileName_;
541
542 const int commSize_; // number of processes in the communicator
543 const int commRank_; // rank of the current process in the communicator
544
545 std::unique_ptr<VtkWriter> curWriter_;
546 double curTime_{};
547 std::string curOutFileName_;
548 int curWriterNum_;
549
550 std::vector<std::unique_ptr<ScalarBuffer>> managedScalarBuffers_;
551 std::vector<std::unique_ptr<VectorBuffer>> managedVectorBuffers_;
552
553 TaskletRunner taskletRunner_;
554};
555
556} // namespace Opm
557
558#endif
The base class for all output writers.
Definition: baseoutputwriter.hh:46
Dune::DynamicVector< double > Vector
Definition: baseoutputwriter.hh:50
std::vector< Tensor > TensorBuffer
Definition: baseoutputwriter.hh:52
Dune::DynamicMatrix< double > Tensor
Definition: baseoutputwriter.hh:49
std::vector< Vector > VectorBuffer
Definition: baseoutputwriter.hh:53
std::vector< Scalar > ScalarBuffer
Definition: baseoutputwriter.hh:51
double Scalar
Definition: baseoutputwriter.hh:48
The base class for tasklets.
Definition: tasklets.hpp:45
void barrier()
Make sure that all tasklets have been completed after this method has been called.
void dispatch(std::shared_ptr< TaskletInterface > tasklet)
Add a new tasklet.
Simplifies writing multi-file VTK datasets.
Definition: vtkmultiwriter.hh:65
VectorBuffer * allocateManagedVectorBuffer(std::size_t numOuter, std::size_t numInner)
Allocate a managed buffer for a vector field.
Definition: vtkmultiwriter.hh:218
void attachTensorElementData(TensorBuffer &buf, std::string_view name) override
Add a finished element-centered tensor field to the output.
Definition: vtkmultiwriter.hh:360
void deserialize(Restarter &res)
Read the multi-writer's state from a restart file.
Definition: vtkmultiwriter.hh:436
void endWrite(bool onlyDiscard) override
Finalizes the current writer.
Definition: vtkmultiwriter.hh:383
void attachScalarVertexData(ScalarBuffer &buf, std::string_view name) override
Add a finished vertex centered vector field to the output.
Definition: vtkmultiwriter.hh:244
BaseOutputWriter::ScalarBuffer ScalarBuffer
Definition: vtkmultiwriter.hh:112
int curWriterNum() const
Returns the number of the current VTK file.
Definition: vtkmultiwriter.hh:149
void gridChanged()
Definition: vtkmultiwriter.hh:173
void beginWrite(double t) override
Called whenever a new time step must be written.
Definition: vtkmultiwriter.hh:182
void attachVectorVertexData(VectorBuffer &buf, std::string_view name) override
Add a finished vertex centered vector field to the output.
Definition: vtkmultiwriter.hh:299
ScalarBuffer * allocateManagedScalarBuffer(std::size_t numEntities)
Allocate a managed buffer for a scalar field.
Definition: vtkmultiwriter.hh:206
void attachVectorElementData(VectorBuffer &buf, std::string_view name) override
Add a element centered quantity to the output.
Definition: vtkmultiwriter.hh:345
BaseOutputWriter::VectorBuffer VectorBuffer
Definition: vtkmultiwriter.hh:114
Dune::VTKWriter< GridView > VtkWriter
Definition: vtkmultiwriter.hh:116
void attachScalarElementData(ScalarBuffer &buf, std::string_view name) override
Add a element centered quantity to the output.
Definition: vtkmultiwriter.hh:271
VtkMultiWriter(bool asyncWriting, const GridView &gridView, const std::string &outputDir, const std::string &simName="", const std::string &multiFileName="")
Definition: vtkmultiwriter.hh:118
void attachTensorVertexData(TensorBuffer &buf, std::string_view name) override
Add a finished vertex-centered tensor field to the output.
Definition: vtkmultiwriter.hh:314
~VtkMultiWriter() override
Definition: vtkmultiwriter.hh:135
void gridViewChanged(const GridView &gridView)
Updates the internal data structures after mesh refinement.
Definition: vtkmultiwriter.hh:167
void serialize(Restarter &res)
Write the multi-writer's state to a restart file.
Definition: vtkmultiwriter.hh:403
Provides a vector-valued function using Dune::FieldVectors as elements.
Definition: vtkscalarfunction.hh:50
Provides a tensor-valued function using Dune::FieldMatrix objects as elements.
Definition: vtktensorfunction.hh:49
Provides a vector-valued function using Dune::FieldVectors as elements.
Definition: vtkvectorfunction.hh:48
Definition: fvbaseprimaryvariables.hh:161
Definition: blackoilbioeffectsmodules.hh:45
Provides a mechanism to dispatch work to separate threads.