version 3.11-dev
Loading...
Searching...
No Matches
io/vtkoutputmodule.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// SPDX-FileCopyrightText: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
5// SPDX-License-Identifier: GPL-3.0-or-later
6//
12#ifndef DUMUX_IO_VTK_OUTPUT_MODULE_HH
13#define DUMUX_IO_VTK_OUTPUT_MODULE_HH
14
15#include <functional>
16#include <memory>
17#include <string>
18
19#include <dune/common/timer.hh>
20#include <dune/common/fvector.hh>
21#include <dune/common/typetraits.hh>
22
23#include <dune/geometry/type.hh>
24#include <dune/geometry/multilineargeometry.hh>
25
26#include <dune/grid/common/mcmgmapper.hh>
27#include <dune/grid/common/partitionset.hh>
28#include <dune/grid/io/file/vtk/vtkwriter.hh>
29#include <dune/grid/io/file/vtk/vtksequencewriter.hh>
30#include <dune/grid/common/partitionset.hh>
31
32#include <dumux/common/concepts/variables_.hh>
34#include <dumux/common/typetraits/localdofs_.hh>
35#include <dumux/io/format.hh>
39
42#include "velocityoutput.hh"
43
44namespace Dumux {
45
51template<class GridGeometry>
53{
54 using GridView = typename GridGeometry::GridView;
55 static constexpr int dim = GridView::dimension;
56
57public:
59 using Field = Vtk::template Field<GridView>;
60
61 VtkOutputModuleBase(const GridGeometry& gridGeometry,
62 const std::string& name,
63 const std::string& paramGroup = "",
64 Dune::VTK::DataMode dm = Dune::VTK::conforming,
65 bool verbose = true)
66 : gridGeometry_(gridGeometry)
67 , name_(name)
68 , paramGroup_(paramGroup)
69 , dm_(dm)
70 , verbose_(gridGeometry.gridView().comm().rank() == 0 && verbose)
71 {
72 const auto precisionString = getParamFromGroup<std::string>(paramGroup, "Vtk.Precision", "Float32");
73 precision_ = Dumux::Vtk::stringToPrecision(precisionString);
74 const auto coordPrecision = Dumux::Vtk::stringToPrecision(getParamFromGroup<std::string>(paramGroup, "Vtk.CoordPrecision", precisionString));
75 writer_ = std::make_shared<Dune::VTKWriter<GridView>>(gridGeometry.gridView(), dm, coordPrecision);
76 sequenceWriter_ = std::make_unique<Dune::VTKSequenceWriter<GridView>>(writer_, name);
77 addProcessRank_ = getParamFromGroup<bool>(this->paramGroup(), "Vtk.AddProcessRank", true);
78 }
79
80 virtual ~VtkOutputModuleBase() = default;
81
83 const std::string& paramGroup() const
84 { return paramGroup_; }
85
95 template<typename Vector>
96 void addField(const Vector& v,
97 const std::string& name,
99 { addField(v, name, this->precision(), fieldType); }
100
111 template<typename Vector>
112 void addField(const Vector& v,
113 const std::string& name,
114 Dumux::Vtk::Precision precision,
116 {
117 // Deduce the number of components from the given vector type
118 const auto nComp = getNumberOfComponents_(v);
119
120 const auto numElemDofs = gridGeometry().elementMapper().size();
121 const auto numVertexDofs = gridGeometry().vertexMapper().size();
122
123 // Automatically deduce the field type ...
124 if(fieldType == Vtk::FieldType::automatic)
125 {
126 if(numElemDofs == numVertexDofs)
127 DUNE_THROW(Dune::InvalidStateException, "Automatic deduction of FieldType failed. Please explicitly specify FieldType::element or FieldType::vertex.");
128
129 if(v.size() == numElemDofs)
130 fieldType = Vtk::FieldType::element;
131 else if(v.size() == numVertexDofs)
132 fieldType = Vtk::FieldType::vertex;
133 else
134 DUNE_THROW(Dune::RangeError, "Size mismatch of added field!");
135 }
136 // ... or check if the user-specified type matches the size of v
137 else
138 {
139 if(fieldType == Vtk::FieldType::element)
140 if(v.size() != numElemDofs)
141 DUNE_THROW(Dune::RangeError, "Size mismatch of added field!");
142
143 if(fieldType == Vtk::FieldType::vertex)
144 if(v.size() != numVertexDofs)
145 DUNE_THROW(Dune::RangeError, "Size mismatch of added field!");
146 }
147
148 // add the appropriate field
149 if (fieldType == Vtk::FieldType::element)
150 addField(Field(gridGeometry_.gridView(), gridGeometry_.elementMapper(), v, name, nComp, 0, dm_, precision));
151 else
152 addField(Field(gridGeometry_.gridView(), gridGeometry_.vertexMapper(), v, name, nComp, dim, dm_, precision));
153 }
154
159 void addField(Field&& field)
160 {
161 // data arrays in the vtk output have to be unique within cell and point data
162 // look if we have a field by the same name with the same codim, if so, replace it
163 for (auto i = 0UL; i < fields_.size(); ++i)
164 {
165 if (fields_[i].name() == field.name() && fields_[i].codim() == field.codim())
166 {
167 fields_[i] = std::move(field);
168 std::cout << Fmt::format(
169 "VtkOutputModule: Replaced field \"{}\" (codim {}). "
170 "A field by the same name & codim had already been registered previously.\n",
171 field.name(), field.codim()
172 );
173 return;
174 }
175 }
176
177 // otherwise add it to the end of the fields
178 fields_.push_back(std::move(field));
179 }
180
186 void write(double time, Dune::VTK::OutputType type = Dune::VTK::ascii)
187 {
188 Dune::Timer timer;
189
190 // write to file depending on data mode
191 if (dm_ == Dune::VTK::conforming)
192 writeConforming_(time, type);
193 else if (dm_ == Dune::VTK::nonconforming)
194 writeNonConforming_(time, type);
195 else
196 DUNE_THROW(Dune::NotImplemented, "Output for provided VTK data mode");
197
199 timer.stop();
200 if (verbose_)
201 std::cout << Fmt::format("Writing output for problem \"{}\". Took {:.2g} seconds.\n", name_, timer.elapsed());
202 }
203
204protected:
205 const GridGeometry& gridGeometry() const { return gridGeometry_; }
206
207 bool verbose() const { return verbose_; }
208 const std::string& name() const { return name_; }
209 Dune::VTK::DataMode dataMode() const { return dm_; }
210 Dumux::Vtk::Precision precision() const { return precision_; }
211
212 Dune::VTKWriter<GridView>& writer() { return *writer_; }
213 Dune::VTKSequenceWriter<GridView>& sequenceWriter() { return *sequenceWriter_; }
214
215 const std::vector<Field>& fields() const { return fields_; }
216
217 void addCellData(const Field& field)
218 {
219 if (std::ranges::find(addedCellData_, field.name()) == addedCellData_.end())
220 {
221 this->sequenceWriter().addCellData(field.get());
222 addedCellData_.push_back(field.name());
223 }
224 }
225
226 void addVertexData(const Field& field)
227 {
228 if (std::ranges::find(addedVertexData_, field.name()) == addedVertexData_.end())
229 {
230 this->sequenceWriter().addVertexData(field.get());
231 addedVertexData_.push_back(field.name());
232 }
233 }
234
235 // keep track of what has been already added because Dune::VTK::Writer
236 // potentially adds the same field twice which is not allowed in VTK/Paraview
237 std::vector<std::string> addedCellData_;
238 std::vector<std::string> addedVertexData_;
239
240private:
242 virtual void writeConforming_(double time, Dune::VTK::OutputType type)
243 {
247
248 // process rank
249 std::vector<int> rank;
250
252 if (!fields_.empty() || addProcessRank_)
253 {
254 const auto numCells = gridGeometry_.gridView().size(0);
255
256 // maybe allocate space for the process rank
257 if (addProcessRank_)
258 {
259 rank.resize(numCells);
260
261 for (const auto& element : elements(gridGeometry_.gridView(), Dune::Partitions::interior))
262 {
263 const auto eIdxGlobal = gridGeometry_.elementMapper().index(element);
264 rank[eIdxGlobal] = gridGeometry_.gridView().comm().rank();
265 }
266 }
267
271
272 // the process rank
273 if (addProcessRank_)
274 this->addCellData(Field(gridGeometry_.gridView(), gridGeometry_.elementMapper(), rank, "process rank", 1, 0));
275
276 // also register additional (non-standardized) user fields if any
277 for (auto&& field : fields_)
278 {
279 if (field.codim() == 0)
280 this->addCellData(field);
281 else if (field.codim() == dim)
282 this->addVertexData(field);
283 else
284 DUNE_THROW(Dune::RangeError, "Cannot add wrongly sized vtk scalar field!");
285 }
286 }
287
291 this->sequenceWriter().write(time, type);
292
296 this->writer().clear();
297
298 this->addedCellData_.clear();
299 this->addedVertexData_.clear();
300 }
301
303 virtual void writeNonConforming_(double time, Dune::VTK::OutputType type)
304 {
305 DUNE_THROW(Dune::NotImplemented, "Non-conforming VTK output");
306 }
307
309 template<class Vector>
310 std::size_t getNumberOfComponents_(const Vector& v)
311 {
312 if constexpr (Dune::IsIndexable<decltype(std::declval<Vector>()[0])>::value)
313 return v[0].size();
314 else
315 return 1;
316 }
317
318 const GridGeometry& gridGeometry_;
319 std::string name_;
320 const std::string paramGroup_;
321 Dune::VTK::DataMode dm_;
322 bool verbose_;
323 Dumux::Vtk::Precision precision_;
324
325 std::shared_ptr<Dune::VTKWriter<GridView>> writer_;
326 std::unique_ptr<Dune::VTKSequenceWriter<GridView>> sequenceWriter_;
327
328 std::vector<Field> fields_;
329
330 bool addProcessRank_ = true;
331};
332
345template<class GridVariables, class SolutionVector>
346class VtkOutputModule : public VtkOutputModuleBase<typename GridVariables::GridGeometry>
347{
349 using GridGeometry = typename GridVariables::GridGeometry;
350
351 using VV = Concept::Variables_t<GridVariables>;
352 using Scalar = typename GridVariables::Scalar;
353
354 using GridView = typename GridGeometry::GridView;
355
356 enum {
357 dim = GridView::dimension,
358 dimWorld = GridView::dimensionworld
359 };
360
361 using Element = typename GridView::template Codim<0>::Entity;
362 using VolVarsVector = Dune::FieldVector<Scalar, dimWorld>;
363
364 static constexpr bool isBox = GridGeometry::discMethod == DiscretizationMethods::box;
365 static constexpr bool isDiamond = GridGeometry::discMethod == DiscretizationMethods::fcdiamond;
366 static constexpr bool isPQ1Bubble = GridGeometry::discMethod == DiscretizationMethods::pq1bubble;
367 static constexpr bool isPQ2 = GridGeometry::discMethod == DiscretizationMethods::pq2;
368
369 struct VolVarScalarDataInfo { std::function<Scalar(const VV&)> get; std::string name; Dumux::Vtk::Precision precision_; };
370 struct VolVarVectorDataInfo { std::function<VolVarsVector(const VV&)> get; std::string name; Dumux::Vtk::Precision precision_; };
371
372 using VelocityOutputType = Dumux::VelocityOutput<GridVariables>;
373
374public:
376 using Field = Vtk::template Field<GridView>;
378 using VolumeVariables = VV;
379
380 VtkOutputModule(const GridVariables& gridVariables,
381 const SolutionVector& sol,
382 const std::string& name,
383 const std::string& paramGroup = "",
384 Dune::VTK::DataMode dm = Dune::VTK::conforming,
385 bool verbose = true)
386 : ParentType(gridVariables.gridGeometry(), name, paramGroup, dm, verbose)
387 , gridVariables_(gridVariables)
388 , sol_(sol)
389 , velocityOutput_(std::make_shared<VelocityOutputType>())
390 {
391 enableVelocityOutput_ = getParamFromGroup<bool>(this->paramGroup(), "Vtk.AddVelocity", false);
392 addProcessRank_ = getParamFromGroup<bool>(this->paramGroup(), "Vtk.AddProcessRank", true);
393 }
394
399
406 void addVelocityOutput(std::shared_ptr<VelocityOutputType> velocityOutput)
407 { velocityOutput_ = velocityOutput; }
408
412 void addVolumeVariable(std::function<Scalar(const VolumeVariables&)>&& f,
413 const std::string& name)
414 {
415 // data arrays in the vtk output have to be unique within cell and point data
416 // look if we have a field by the same name with the same codim, if so, replace it
417 for (auto i = 0UL; i < volVarScalarDataInfo_.size(); ++i)
418 {
419 if (volVarScalarDataInfo_[i].name == name)
420 {
421 volVarScalarDataInfo_[i] = VolVarScalarDataInfo{f, name, this->precision()};
422 std::cout << Fmt::format(
423 "VtkOutputModule: Replaced volume variable output \"{}\". "
424 "A field by the same name had already been registered previously.\n",
425 name
426 );
427 return;
428 }
429 }
430
431 // otherwise add it to the end of the fields
432 volVarScalarDataInfo_.push_back(VolVarScalarDataInfo{f, name, this->precision()});
433 }
434
439 template<class VVV = VolVarsVector, typename std::enable_if_t<(VVV::dimension > 1), int> = 0>
440 void addVolumeVariable(std::function<VolVarsVector(const VolumeVariables&)>&& f,
441 const std::string& name)
442 {
443 // data arrays in the vtk output have to be unique within cell and point data
444 // look if we have a field by the same name with the same codim, if so, replace it
445 for (auto i = 0UL; i < volVarVectorDataInfo_.size(); ++i)
446 {
447 if (volVarVectorDataInfo_[i].name == name)
448 {
449 volVarVectorDataInfo_[i] = VolVarVectorDataInfo{f, name, this->precision()};
450 std::cout << Fmt::format(
451 "VtkOutputModule: Replaced volume variable output \"{}\". "
452 "A field by the same name had already been registered previously.\n",
453 name
454 );
455 return;
456 }
457 }
458
459 // otherwise add it to the end of the fields
460 volVarVectorDataInfo_.push_back(VolVarVectorDataInfo{f, name, this->precision()});
461 }
462
463protected:
464 // some return functions for differing implementations to use
465 const auto& problem() const { return curGridVariables_().problem(); }
466 const GridVariables& gridVariables() const { return gridVariables_; }
467 const GridGeometry& gridGeometry() const { return gridVariables_.gridGeometry(); }
468 const SolutionVector& sol() const { return sol_; }
469
470 const std::vector<VolVarScalarDataInfo>& volVarScalarDataInfo() const { return volVarScalarDataInfo_; }
471 const std::vector<VolVarVectorDataInfo>& volVarVectorDataInfo() const { return volVarVectorDataInfo_; }
472
473 using VelocityOutput = VelocityOutputType;
474 const VelocityOutput& velocityOutput() const { return *velocityOutput_; }
475
476 const auto& curGridVariables_() const
477 {
478 if constexpr (Concept::FVGridVariables<GridVariables>)
479 return gridVariables_.curGridVolVars();
480 else
481 return gridVariables_.curGridVars();
482 }
483
484private:
485
487 void writeConforming_(double time, Dune::VTK::OutputType type) override
488 {
489 const Dune::VTK::DataMode dm = Dune::VTK::conforming;
493
494 // instantiate the velocity output
495 using VelocityVector = typename VelocityOutput::VelocityVector;
496 std::vector<VelocityVector> velocity(velocityOutput_->numFluidPhases());
497
498 // process rank
499 std::vector<double> rank;
500
501 // volume variable data
502 std::vector<std::vector<Scalar>> volVarScalarData;
503 std::vector<std::vector<VolVarsVector>> volVarVectorData;
504
506 if (!volVarScalarDataInfo_.empty()
507 || !volVarVectorDataInfo_.empty()
508 || !this->fields().empty()
509 || velocityOutput_->enableOutput()
510 || addProcessRank_)
511 {
512 const auto numCells = gridGeometry().gridView().size(0);
513 const auto numDofs = numDofs_();
514
515 // get fields for all volume variables
516 if (!volVarScalarDataInfo_.empty())
517 volVarScalarData.resize(volVarScalarDataInfo_.size(), std::vector<Scalar>(numDofs));
518 if (!volVarVectorDataInfo_.empty())
519 volVarVectorData.resize(volVarVectorDataInfo_.size(), std::vector<VolVarsVector>(numDofs));
520
521 if (velocityOutput_->enableOutput())
522 {
523 for (int phaseIdx = 0; phaseIdx < velocityOutput_->numFluidPhases(); ++phaseIdx)
524 {
525 if (velocityOutput_->fieldType() == VelocityOutput::FieldType::element)
526 velocity[phaseIdx].resize(numCells);
527 else if (velocityOutput_->fieldType() == VelocityOutput::FieldType::vertex)
528 velocity[phaseIdx].resize(numDofs);
529 else
530 {
531 if(isBox && dim == 1)
532 velocity[phaseIdx].resize(numCells);
533 else
534 velocity[phaseIdx].resize(numDofs);
535 }
536 }
537 }
538
539 // maybe allocate space for the process rank
540 if (addProcessRank_) rank.resize(numCells);
541
542 auto fvGeometry = localView(gridGeometry());
543 auto elemVolVars = localView(curGridVariables_());
544 for (const auto& element : elements(gridGeometry().gridView()))
545 {
546 if (!velocityOutput_->enableOutput() &&
547 element.partitionType() != Dune::PartitionType::InteriorEntity)
548 {
549 continue;
550 }
551 const auto eIdxGlobal = gridGeometry().elementMapper().index(element);
552 // If velocity output is enabled we need to bind to the whole stencil
553 // otherwise element-local data is sufficient
554 if (velocityOutput_->enableOutput())
555 {
556 fvGeometry.bind(element);
557 elemVolVars.bind(element, fvGeometry, sol_);
558 }
559 else
560 {
561 fvGeometry.bindElement(element);
562 elemVolVars.bindElement(element, fvGeometry, sol_);
563 }
564
565 // velocity output
566 if (velocityOutput_->enableOutput())
567 {
568 if constexpr (Concept::FVGridVariables<GridVariables>)
569 {
570 const auto elemFluxVarsCache = localView(gridVariables_.gridFluxVarsCache()).bind(element, fvGeometry, elemVolVars);
571
572 for (int phaseIdx = 0; phaseIdx < velocityOutput_->numFluidPhases(); ++phaseIdx)
573 velocityOutput_->calculateVelocity(velocity[phaseIdx], element, fvGeometry, elemVolVars, elemFluxVarsCache, phaseIdx);
574 }
575 else
576 {
577 for (int phaseIdx = 0; phaseIdx < velocityOutput_->numFluidPhases(); ++phaseIdx)
578 velocityOutput_->calculateVelocity(velocity[phaseIdx], element, fvGeometry, elemVolVars, phaseIdx);
579 }
580 }
581 else if (element.partitionType() != Dune::PartitionType::InteriorEntity)
582 {
583 continue;
584 }
585
586 if (!volVarScalarDataInfo_.empty() || !volVarVectorDataInfo_.empty())
587 {
588 using ElementDisc = typename GridGeometry::LocalView;
590 {
591 for (const auto& scv : scvs(fvGeometry))
592 {
593 const auto dofIdxGlobal = scv.dofIndex();
594 const auto& volVars = elemVolVars[scv];
595
596 // get the scalar-valued data
597 for (std::size_t i = 0; i < volVarScalarDataInfo_.size(); ++i)
598 volVarScalarData[i][dofIdxGlobal] = volVarScalarDataInfo_[i].get(volVars);
599
600 // get the vector-valued data
601 for (std::size_t i = 0; i < volVarVectorDataInfo_.size(); ++i)
602 volVarVectorData[i][dofIdxGlobal] = volVarVectorDataInfo_[i].get(volVars);
603 }
604 }
607 {
608 for (const auto& localDof : nonCVLocalDofs(fvGeometry))
609 {
610 const auto dofIdxGlobal = localDof.dofIndex();
611 const auto& volVars = elemVolVars[localDof];
612
613 // get the scalar-valued data
614 for (std::size_t i = 0; i < volVarScalarDataInfo_.size(); ++i)
615 volVarScalarData[i][dofIdxGlobal] = volVarScalarDataInfo_[i].get(volVars);
616
617 // get the vector-valued data
618 for (std::size_t i = 0; i < volVarVectorDataInfo_.size(); ++i)
619 volVarVectorData[i][dofIdxGlobal] = volVarVectorDataInfo_[i].get(volVars);
620 }
621 }
622 }
623
625 if (addProcessRank_)
626 rank[eIdxGlobal] = static_cast<double>(gridGeometry().gridView().comm().rank());
627 }
628
632
633 // volume variables if any
634 if constexpr (isBox || isPQ1Bubble || isPQ2)
635 {
636 for (std::size_t i = 0; i < volVarScalarDataInfo_.size(); ++i)
637 this->addVertexData( Field(gridGeometry().gridView(), gridGeometry().dofMapper(), volVarScalarData[i],
638 volVarScalarDataInfo_[i].name, /*numComp*/1, /*codim*/dim, dm, this->precision()) );
639 for (std::size_t i = 0; i < volVarVectorDataInfo_.size(); ++i)
640 this->addVertexData( Field(gridGeometry().gridView(), gridGeometry().dofMapper(), volVarVectorData[i],
641 volVarVectorDataInfo_[i].name, /*numComp*/dimWorld, /*codim*/dim, dm, this->precision()) );
642
643 if constexpr (isPQ1Bubble)
644 {
645 for (std::size_t i = 0; i < volVarScalarDataInfo_.size(); ++i)
646 this->addCellData( Field(gridGeometry().gridView(), gridGeometry().dofMapper(), volVarScalarData[i],
647 volVarScalarDataInfo_[i].name, /*numComp*/1, /*codim*/0,dm, this->precision()) );
648 for (std::size_t i = 0; i < volVarVectorDataInfo_.size(); ++i)
649 this->addCellData( Field(gridGeometry().gridView(), gridGeometry().dofMapper(), volVarVectorData[i],
650 volVarVectorDataInfo_[i].name, /*numComp*/dimWorld, /*codim*/0,dm, this->precision()) );
651 }
652
653 }
654 else
655 {
656 for (std::size_t i = 0; i < volVarScalarDataInfo_.size(); ++i)
657 this->addCellData( Field(gridGeometry().gridView(), gridGeometry().elementMapper(), volVarScalarData[i],
658 volVarScalarDataInfo_[i].name, /*numComp*/1, /*codim*/0,dm, this->precision()) );
659 for (std::size_t i = 0; i < volVarVectorDataInfo_.size(); ++i)
660 this->addCellData( Field(gridGeometry().gridView(), gridGeometry().elementMapper(), volVarVectorData[i],
661 volVarVectorDataInfo_[i].name, /*numComp*/dimWorld, /*codim*/0,dm, this->precision()) );
662 }
663
664 // the velocity field
665 if (velocityOutput_->enableOutput())
666 {
667 if (velocityOutput_->fieldType() == VelocityOutput::FieldType::vertex
668 || ( (velocityOutput_->fieldType() == VelocityOutput::FieldType::automatic) && dim > 1 && isBox ))
669 {
670 for (int phaseIdx = 0; phaseIdx < velocityOutput_->numFluidPhases(); ++phaseIdx)
671 this->addVertexData( Field(gridGeometry().gridView(), gridGeometry().vertexMapper(), velocity[phaseIdx],
672 "velocity_" + velocityOutput_->phaseName(phaseIdx) + " (m/s)",
673 /*numComp*/dimWorld, /*codim*/dim, dm, this->precision()) );
674 }
675 // cell-centered models
676 else
677 {
678 for (int phaseIdx = 0; phaseIdx < velocityOutput_->numFluidPhases(); ++phaseIdx)
679 this->addCellData( Field(gridGeometry().gridView(), gridGeometry().elementMapper(), velocity[phaseIdx],
680 "velocity_" + velocityOutput_->phaseName(phaseIdx) + " (m/s)",
681 /*numComp*/dimWorld, /*codim*/0, dm, this->precision()) );
682 }
683 }
684
685 // the process rank
686 if (addProcessRank_)
687 this->addCellData(Field(gridGeometry().gridView(), gridGeometry().elementMapper(), rank, "process rank", 1, 0));
688
689 // also register additional (non-standardized) user fields if any
690 for (auto&& field : this->fields())
691 {
692 if (field.codim() == 0)
693 this->addCellData(field);
694 else if (field.codim() == dim)
695 this->addVertexData(field);
696 else
697 DUNE_THROW(Dune::RangeError, "Cannot add wrongly sized vtk scalar field!");
698 }
699 }
700
704 this->sequenceWriter().write(time, type);
705
709 this->writer().clear();
710
711 this->addedCellData_.clear();
712 this->addedVertexData_.clear();
713 }
714
716 void writeNonConforming_(double time, Dune::VTK::OutputType type) override
717 {
718 const Dune::VTK::DataMode dm = Dune::VTK::nonconforming;
719
720 // only supports finite-element-like discretization schemes
721 if(!isBox && !isDiamond)
722 DUNE_THROW(Dune::NotImplemented,
723 "Non-conforming output for discretization scheme " << GridGeometry::discMethod
724 );
725
729
730 // check the velocity output
731 if (enableVelocityOutput_ && !velocityOutput_->enableOutput())
732 std::cerr << "Warning! Velocity output was enabled in the input file"
733 << " but no velocity output policy was set for the VTK output module:"
734 << " There will be no velocity output."
735 << " Use the addVelocityOutput member function of the VTK output module." << std::endl;
736 using VelocityVector = typename VelocityOutput::VelocityVector;
737 std::vector<VelocityVector> velocity(velocityOutput_->numFluidPhases());
738
739 // process rank
740 std::vector<double> rank;
741
742 // volume variable data (indexing: volvardata/element/localindex)
743 using ScalarDataContainer = std::vector< std::vector<Scalar> >;
744 using VectorDataContainer = std::vector< std::vector<VolVarsVector> >;
745 std::vector< ScalarDataContainer > volVarScalarData;
746 std::vector< VectorDataContainer > volVarVectorData;
747
749 if (!volVarScalarDataInfo_.empty()
750 || !volVarVectorDataInfo_.empty()
751 || !this->fields().empty()
752 || velocityOutput_->enableOutput()
753 || addProcessRank_)
754 {
755 const auto numCells = gridGeometry().gridView().size(0);
756 const auto outputSize = numDofs_();
757
758 // get fields for all volume variables
759 if (!volVarScalarDataInfo_.empty())
760 volVarScalarData.resize(volVarScalarDataInfo_.size(), ScalarDataContainer(numCells));
761 if (!volVarVectorDataInfo_.empty())
762 volVarVectorData.resize(volVarVectorDataInfo_.size(), VectorDataContainer(numCells));
763
764 if (velocityOutput_->enableOutput())
765 {
766 for (int phaseIdx = 0; phaseIdx < velocityOutput_->numFluidPhases(); ++phaseIdx)
767 {
768 if((isBox && dim == 1) || isDiamond)
769 velocity[phaseIdx].resize(numCells);
770 else
771 velocity[phaseIdx].resize(outputSize);
772 }
773 }
774
775 // maybe allocate space for the process rank
776 if (addProcessRank_) rank.resize(numCells);
777
778 // now we go element-local to extract values at local dof locations
779 auto fvGeometry = localView(gridGeometry());
780 auto elemVolVars = localView(curGridVariables_());
781 for (const auto& element : elements(gridGeometry().gridView()))
782 {
783 if (!velocityOutput_->enableOutput() &&
784 element.partitionType() != Dune::PartitionType::InteriorEntity)
785 {
786 continue;
787 }
788 const auto eIdxGlobal = gridGeometry().elementMapper().index(element);
789 // If velocity output is enabled we need to bind to the whole stencil
790 // otherwise element-local data is sufficient
791 if (velocityOutput_->enableOutput())
792 {
793 fvGeometry.bind(element);
794 elemVolVars.bind(element, fvGeometry, sol_);
795 }
796 else
797 {
798 fvGeometry.bindElement(element);
799 elemVolVars.bindElement(element, fvGeometry, sol_);
800 }
801
802 const auto numLocalDofs = Dumux::Detail::LocalDofs::numLocalDofs(fvGeometry);
803 // resize element-local data containers
804 for (std::size_t i = 0; i < volVarScalarDataInfo_.size(); ++i)
805 volVarScalarData[i][eIdxGlobal].resize(numLocalDofs);
806 for (std::size_t i = 0; i < volVarVectorDataInfo_.size(); ++i)
807 volVarVectorData[i][eIdxGlobal].resize(numLocalDofs);
808
809 // velocity output
810 if (velocityOutput_->enableOutput())
811 {
812 if constexpr (Concept::FVGridVariables<GridVariables>)
813 {
814 const auto elemFluxVarsCache = localView(gridVariables_.gridFluxVarsCache()).bind(element, fvGeometry, elemVolVars);
815 for (int phaseIdx = 0; phaseIdx < velocityOutput_->numFluidPhases(); ++phaseIdx)
816 velocityOutput_->calculateVelocity(velocity[phaseIdx], element, fvGeometry, elemVolVars, elemFluxVarsCache, phaseIdx);
817 }
818 else
819 {
820 for (int phaseIdx = 0; phaseIdx < velocityOutput_->numFluidPhases(); ++phaseIdx)
821 velocityOutput_->calculateVelocity(velocity[phaseIdx], element, fvGeometry, elemVolVars, phaseIdx);
822 }
823 }
824 else if (element.partitionType() != Dune::PartitionType::InteriorEntity)
825 {
826 continue;
827 }
828
829 if (!volVarScalarDataInfo_.empty() || !volVarVectorDataInfo_.empty())
830 {
831 using ElementDisc = typename GridGeometry::LocalView;
833 {
834 for (const auto& scv : scvs(fvGeometry))
835 {
836 const auto& volVars = elemVolVars[scv];
837
838 // get the scalar-valued data
839 for (std::size_t i = 0; i < volVarScalarDataInfo_.size(); ++i)
840 volVarScalarData[i][eIdxGlobal][scv.localDofIndex()] = volVarScalarDataInfo_[i].get(volVars);
841
842 // get the vector-valued data
843 for (std::size_t i = 0; i < volVarVectorDataInfo_.size(); ++i)
844 volVarVectorData[i][eIdxGlobal][scv.localDofIndex()] = volVarVectorDataInfo_[i].get(volVars);
845 }
846 }
849 {
850 for (const auto& localDof : nonCVLocalDofs(fvGeometry))
851 {
852 const auto& volVars = elemVolVars[localDof];
853
854 // get the scalar-valued data
855 for (std::size_t i = 0; i < volVarScalarDataInfo_.size(); ++i)
856 volVarScalarData[i][eIdxGlobal][localDof.index()] = volVarScalarDataInfo_[i].get(volVars);
857
858 // get the vector-valued data
859 for (std::size_t i = 0; i < volVarVectorDataInfo_.size(); ++i)
860 volVarVectorData[i][eIdxGlobal][localDof.index()] = volVarVectorDataInfo_[i].get(volVars);
861 }
862 }
863 }
864
866 if (addProcessRank_)
867 rank[eIdxGlobal] = static_cast<double>(gridGeometry().gridView().comm().rank());
868 }
869
873
874 // volume variables if any
875 static constexpr int dofLocCodim = isDiamond ? 1 : dim;
876 for (std::size_t i = 0; i < volVarScalarDataInfo_.size(); ++i)
877 this->addVertexData(Field(
878 gridGeometry().gridView(), gridGeometry().elementMapper(),
879 volVarScalarData[i], volVarScalarDataInfo_[i].name,
880 /*numComp*/1, /*codim*/dofLocCodim, /*nonconforming*/dm, this->precision()
881 ));
882
883 for (std::size_t i = 0; i < volVarVectorDataInfo_.size(); ++i)
884 this->addVertexData(Field(
885 gridGeometry().gridView(), gridGeometry().elementMapper(),
886 volVarVectorData[i], volVarVectorDataInfo_[i].name,
887 /*numComp*/dimWorld, /*codim*/dofLocCodim, /*nonconforming*/dm, this->precision()
888 ));
889
890 // the velocity field
891 if (velocityOutput_->enableOutput())
892 {
893 // node-wise velocities
894 if (dim > 1 && !isDiamond)
895 for (int phaseIdx = 0; phaseIdx < velocityOutput_->numFluidPhases(); ++phaseIdx)
896 this->addVertexData(Field(
897 gridGeometry().gridView(), gridGeometry().vertexMapper(), velocity[phaseIdx],
898 "velocity_" + velocityOutput_->phaseName(phaseIdx) + " (m/s)",
899 /*numComp*/dimWorld, /*codim*/dofLocCodim, dm, this->precision()
900 ));
901
902 // cell-wise velocities
903 else
904 for (int phaseIdx = 0; phaseIdx < velocityOutput_->numFluidPhases(); ++phaseIdx)
905 this->addCellData(Field(
906 gridGeometry().gridView(), gridGeometry().elementMapper(), velocity[phaseIdx],
907 "velocity_" + velocityOutput_->phaseName(phaseIdx) + " (m/s)",
908 /*numComp*/dimWorld, /*codim*/0, dm, this->precision()
909 ));
910 }
911 }
912
913 // the process rank
914 if (addProcessRank_)
915 this->addCellData(Field(
916 gridGeometry().gridView(), gridGeometry().elementMapper(),
917 rank, "process rank", /*numComp*/1, /*codim*/0
918 ));
919
920 // also register additional (non-standardized) user fields if any
921 for (const auto& field : this->fields())
922 {
923 if (field.codim() == 0)
924 this->addCellData(field);
925 else if (field.codim() == dim || field.codim() == 1)
926 this->addVertexData(field);
927 else
928 DUNE_THROW(Dune::RangeError, "Cannot add wrongly sized vtk scalar field!");
929 }
930
934 this->sequenceWriter().write(time, type);
935
939 this->writer().clear();
940
941 this->addedCellData_.clear();
942 this->addedVertexData_.clear();
943 }
944
946 std::size_t numDofs_() const
947 {
948 // TODO this should actually always be dofMapper.size()
949 // maybe some discretizations needs special treatment (?)
950 if constexpr (isBox || isDiamond || isPQ1Bubble || isPQ2)
951 return gridGeometry().dofMapper().size();
952 else
953 return gridGeometry().elementMapper().size();
954 }
955
956 const GridVariables& gridVariables_;
957 const SolutionVector& sol_;
958
959 std::vector<VolVarScalarDataInfo> volVarScalarDataInfo_;
960 std::vector<VolVarVectorDataInfo> volVarVectorDataInfo_;
961
962 std::shared_ptr<VelocityOutput> velocityOutput_;
963 bool enableVelocityOutput_ = false;
964 bool addProcessRank_ = true;
965};
966
967} // end namespace Dumux
968
969#endif
@ vertex
Definition io/velocityoutput.hh:47
@ automatic
Definition io/velocityoutput.hh:47
@ element
Definition io/velocityoutput.hh:47
std::vector< Dune::FieldVector< Scalar, dimWorld > > VelocityVector
Definition io/velocityoutput.hh:40
Velocity output for implicit (porous media) models.
Definition io/velocityoutput.hh:27
Dumux::Vtk::Precision precision() const
Definition io/vtkoutputmodule.hh:210
const std::string & paramGroup() const
Definition io/vtkoutputmodule.hh:83
void addCellData(const Field &field)
Definition io/vtkoutputmodule.hh:217
void write(double time, Dune::VTK::OutputType type=Dune::VTK::ascii)
Definition io/vtkoutputmodule.hh:186
VtkOutputModuleBase(const GridGeometry &gridGeometry, const std::string &name, const std::string &paramGroup="", Dune::VTK::DataMode dm=Dune::VTK::conforming, bool verbose=true)
Definition io/vtkoutputmodule.hh:61
Dune::VTK::DataMode dataMode() const
Definition io/vtkoutputmodule.hh:209
Dune::VTKWriter< GridView > & writer()
Definition io/vtkoutputmodule.hh:212
Dune::VTKSequenceWriter< GridView > & sequenceWriter()
Definition io/vtkoutputmodule.hh:213
const std::string & name() const
Definition io/vtkoutputmodule.hh:208
const std::vector< Field > & fields() const
Definition io/vtkoutputmodule.hh:215
virtual ~VtkOutputModuleBase()=default
virtual void writeNonConforming_(double time, Dune::VTK::OutputType type)
Assembles the fields and adds them to the writer (nonconforming output).
Definition io/vtkoutputmodule.hh:303
std::vector< std::string > addedCellData_
Definition io/vtkoutputmodule.hh:237
void addField(const Vector &v, const std::string &name, Dumux::Vtk::Precision precision, Vtk::FieldType fieldType=Vtk::FieldType::automatic)
Add a scalar or vector valued vtk field.
Definition io/vtkoutputmodule.hh:112
bool verbose() const
Definition io/vtkoutputmodule.hh:207
Vtk::template Field< GridView > Field
the type of Field that can be added to this writer
Definition io/vtkoutputmodule.hh:59
std::vector< std::string > addedVertexData_
Definition io/vtkoutputmodule.hh:238
virtual void writeConforming_(double time, Dune::VTK::OutputType type)
Assembles the fields and adds them to the writer (conforming output).
Definition io/vtkoutputmodule.hh:242
void addField(const Vector &v, const std::string &name, Vtk::FieldType fieldType=Vtk::FieldType::automatic)
Add a scalar or vector valued vtk field.
Definition io/vtkoutputmodule.hh:96
void addVertexData(const Field &field)
Definition io/vtkoutputmodule.hh:226
const GridGeometry & gridGeometry() const
Definition io/vtkoutputmodule.hh:205
void addField(Field &&field)
Add a scalar or vector valued vtk field.
Definition io/vtkoutputmodule.hh:159
const auto & problem() const
Definition io/vtkoutputmodule.hh:465
const GridVariables & gridVariables() const
Definition io/vtkoutputmodule.hh:466
void addVolumeVariable(std::function< VolVarsVector(const VolumeVariables &)> &&f, const std::string &name)
Definition io/vtkoutputmodule.hh:440
VV VolumeVariables
export type of the volume variables for the outputfields
Definition io/vtkoutputmodule.hh:378
void addVolumeVariable(std::function< Scalar(const VolumeVariables &)> &&f, const std::string &name)
Definition io/vtkoutputmodule.hh:412
void addVelocityOutput(std::shared_ptr< VelocityOutputType > velocityOutput)
Add a velocity output policy.
Definition io/vtkoutputmodule.hh:406
const auto & curGridVariables_() const
Definition io/vtkoutputmodule.hh:476
const VelocityOutput & velocityOutput() const
Definition io/vtkoutputmodule.hh:474
VelocityOutputType VelocityOutput
Definition io/vtkoutputmodule.hh:473
Vtk::template Field< GridView > Field
the type of Field that can be added to this writer
Definition io/vtkoutputmodule.hh:376
void writeNonConforming_(double time, Dune::VTK::OutputType type) override
Assembles the fields and adds them to the writer (nonconforming output).
Definition io/vtkoutputmodule.hh:716
const SolutionVector & sol() const
Definition io/vtkoutputmodule.hh:468
const GridGeometry & gridGeometry() const
Definition io/vtkoutputmodule.hh:467
void writeConforming_(double time, Dune::VTK::OutputType type) override
Assembles the fields and adds them to the writer (conforming output).
Definition io/vtkoutputmodule.hh:487
VtkOutputModule(const GridVariables &gridVariables, const SolutionVector &sol, const std::string &name, const std::string &paramGroup="", Dune::VTK::DataMode dm=Dune::VTK::conforming, bool verbose=true)
Definition io/vtkoutputmodule.hh:380
const std::vector< VolVarScalarDataInfo > & volVarScalarDataInfo() const
Definition io/vtkoutputmodule.hh:470
const std::vector< VolVarVectorDataInfo > & volVarVectorDataInfo() const
Definition io/vtkoutputmodule.hh:471
Concept for pure finite-element discretizations (no FV structure).
Definition concepts.hh:50
Concept for finite-volume discretizations.
Definition concepts.hh:26
Concept for hybrid finite-element/finite-volume discretizations.
Definition concepts.hh:39
Concepts for discretization types.
Vtk field types available in Dumux.
Formatting based on the fmt-library which implements std::format of C++20.
Dune style VTK functions.
GridCache::LocalView localView(const GridCache &gridCache)
Free function to get the local view of a grid cache object.
Definition localview.hh:26
Precision stringToPrecision(std::string_view precisionName)
Maps a string (e.g. from input) to a Dune precision type.
Definition precision.hh:27
FieldType
Identifier for vtk field types.
Definition fieldtype.hh:22
@ vertex
Definition fieldtype.hh:23
@ automatic
Definition fieldtype.hh:23
@ element
Definition fieldtype.hh:23
T getParamFromGroup(Args &&... args)
A free function to get a parameter from the parameter tree singleton with a model group.
Definition parameters.hh:149
Default velocity output policy for porous media models.
Class representing dofs on elements for control-volume finite element schemes.
The available discretization methods in Dumux.
constexpr FCDiamond fcdiamond
Definition method.hh:184
constexpr PQ2 pq2
Definition method.hh:178
constexpr Box box
Definition method.hh:176
constexpr PQ1Bubble pq1bubble
Definition method.hh:180
Definition adapt.hh:17
std::ranges::range auto scvs(const FVElementGeometry &fvGeometry, const LocalDof &localDof)
Definition localdof.hh:82
The infrastructure to retrieve run-time parameters from Dune::ParameterTrees.