12#ifndef DUMUX_IO_GRID_WRITER_HH
13#define DUMUX_IO_GRID_WRITER_HH
17#if DUMUX_HAVE_GRIDFORMAT
24#include <gridformat/common/type_traits.hpp>
25#include <gridformat/gridformat.hpp>
26#include <gridformat/traits/dune.hpp>
28#include <dune/common/exceptions.hh>
29#include <dune/common/timer.hh>
42template<
typename Grid,
typename Format,
typename Comm,
typename... Args>
43auto makeParallelWriter(
const Grid& grid,
const Format& fmt,
const Dune::Communication<Comm>& comm, Args&&... args)
45 return comm.size() > 1
46 ? GridFormat::Writer<Grid>{fmt, grid,
static_cast<Comm
>(comm), std::forward<Args>(args)...}
47 : GridFormat::Writer<Grid>{fmt, grid, std::forward<Args>(args)...};
50template<
typename Grid,
typename Format,
typename... Args>
51auto makeParallelWriter(
const Grid& grid,
const Format& fmt,
const Dune::Communication<Dune::No_Comm>&, Args&&... args)
52{
return GridFormat::Writer<Grid>{fmt, grid, std::forward<Args>(args)...}; }
54template<
typename Grid,
typename Format,
typename... Args>
55auto makeWriter(
const Grid& grid,
const Format& fmt, Args&&... args)
57 const auto& comm = GridFormat::Dune::Traits::GridView<Grid>::get(grid).comm();
58 return makeParallelWriter(grid, fmt, comm, std::forward<Args>(args)...);
62concept Container =
requires(
const T& t) {
71namespace VTK {
using namespace GridFormat::VTK; }
72namespace Format {
using namespace GridFormat::Formats; }
73namespace Encoding {
using namespace GridFormat::Encoding; }
74namespace Compression {
using namespace GridFormat::Compression;
using GridFormat::none; }
76 using GridFormat::float32;
77 using GridFormat::float64;
79 using GridFormat::uint64;
80 using GridFormat::uint32;
81 using GridFormat::uint16;
82 using GridFormat::uint8;
84 using GridFormat::int64;
85 using GridFormat::int32;
86 using GridFormat::int16;
87 using GridFormat::int8;
96struct Order {
static_assert(order > 0,
"order must be > 0"); };
99inline constexpr auto order = Order<o>{};
115template<Gr
idFormat::Concepts::Gr
id Gr
idView,
int order = 1>
118 using Grid = std::conditional_t<
120 CVFELagrangeGrid<GridView, order>,
123 using Cell = GridFormat::Cell<Grid>;
124 using Vertex =
typename GridView::template Codim<GridView::dimension>::Entity;
125 using Element =
typename GridView::template Codim<0>::Entity;
126 using Writer = GridFormat::Writer<Grid>;
133 template<
typename Format>
135 const GridView& gridView,
136 const Order<order>& = {})
137 : gridView_{gridView}
138 , grid_{makeGrid_(gridView)}
139 , writer_{Detail::makeWriter(grid_, fmt)}
141 if (gridView.comm().size() > 0 &&
getParam<bool>(
"IO.GridWriter.AddProcessRank",
true))
142 setCellField(
"process rank", [&](
const Cell&) {
return gridView.comm().rank(); }, Precision::uint64);
149 template<
typename Format>
151 const GridView& gridView,
152 const std::string& filename,
153 const Order<order>& = {})
154 : gridView_{gridView}
155 , grid_{makeGrid_(gridView)}
156 , writer_{Detail::makeWriter(grid_, fmt, filename)}
158 if (gridView.comm().size() > 0 &&
getParam<bool>(
"IO.GridWriter.AddProcessRank",
true))
159 setCellField(
"process rank", [&](
const Cell&) {
return gridView.comm().rank(); }, Precision::uint64);
166 std::string write(
const std::string& name)
const
167 {
return writer_.write(name); }
173 template<std::
floating_po
int T>
174 std::string write(T time)
const
175 {
return writer_.write(time); }
178 template<Gr
idFormat::Concepts::CellFunction<Gr
idView> F,
179 Gr
idFormat::Concepts::Scalar T = Gr
idFormat::FieldScalar<std::invoke_result_t<F, Element>>>
180 void setCellField(
const std::string& name, F&& f,
const GridFormat::Precision<T>& prec = {})
181 { writer_.set_cell_field(name, std::move(f), prec); }
184 template<Gr
idFormat::Dune::Concepts::Function<Gr
idView> F>
185 void setCellField(
const std::string& name, F&& f)
186 { GridFormat::Dune::set_cell_function(std::forward<F>(f), writer_, name); }
189 template<
typename F, Gr
idFormat::Concepts::Scalar T>
190 void setCellField(
const std::string& name, F&& f,
const GridFormat::Precision<T>& prec)
191 { GridFormat::Dune::set_cell_function(std::forward<F>(f), writer_, name, prec); }
194 template<Gr
idFormat::Concepts::Po
intFunction<Gr
idView> F,
195 Gr
idFormat::Concepts::Scalar T = Gr
idFormat::FieldScalar<std::invoke_result_t<F, Vertex>>>
196 void setPointField(
const std::string& name, F&& f,
const GridFormat::Precision<T>& prec = {})
197 requires (order == 1)
198 { writer_.set_point_field(name, std::move(f), prec); }
202 template<Detail::Container C,
203 GridFormat::Concepts::Scalar T = GridFormat::MDRangeScalar<C>>
204 void setPointField(
const std::string& name,
const C& values,
205 const GridFormat::Precision<T>& prec = {})
208 using Point =
typename Grid::Point;
209 writer_.set_point_field(name, [&values](
const Point& p) -> std::ranges::range_value_t<C> {
210 return values[p.dofIndex];
216 template<Gr
idFormat::Dune::Concepts::Function<Gr
idView> F>
217 void setPointField(
const std::string& name, F&& f)
218 { GridFormat::Dune::set_point_function(std::forward<F>(f), writer_, name); }
221 template<Gr
idFormat::Dune::Concepts::Function<Gr
idView> F, Gr
idFormat::Concepts::Scalar T>
222 void setPointField(
const std::string& name, F&& f,
const GridFormat::Precision<T>& prec)
223 { GridFormat::Dune::set_point_function(std::forward<F>(f), writer_, name, prec); }
232 if constexpr (order > 1)
233 grid_.update(gridView_);
237 Grid makeGrid_(
const GridView& gv)
const
239 if constexpr (order > 1)
254template<
typename Gr
idVariables,
typename SolutionVector>
256 using ParentType = GridWriter<typename GridVariables::GridGeometry::GridView, 1>;
257 using GridView =
typename GridVariables::GridGeometry::GridView;
260 static constexpr int dimWorld = GridVariables::GridGeometry::GridView::dimensionworld;
261 using Scalar =
typename GridVariables::Scalar;
262 using Vector = Dune::FieldVector<Scalar, dimWorld>;
263 using Tensor = Dune::FieldMatrix<Scalar, dimWorld, dimWorld>;
264 using VolVar =
typename GridVariables::VolumeVariables;
266 class VolVarFieldStorage;
268 static constexpr int defaultVerbosity_ = 1;
271 using VolumeVariables = VolVar;
273 static constexpr auto defaultFileFormat = IO::Format::pvd_with(
274 IO::Format::vtu.with({
275 .encoder = IO::Encoding::ascii,
276 .compressor = IO::Compression::none,
277 .data_format = VTK::DataFormat::inlined
284 explicit OutputModule(
const GridVariables& gridVariables,
285 const SolutionVector& sol,
286 const std::string& filename)
287 : ParentType{defaultFileFormat, gridVariables.gridGeometry().gridView(), filename, order<1>}
288 , gridVariables_{gridVariables}
289 , solutionVector_{sol}
291 setVerbosity(defaultVerbosity_);
298 template<
typename Format>
300 const GridVariables& gridVariables,
301 const SolutionVector& sol)
302 : ParentType{fmt, gridVariables.gridGeometry().gridView(), order<1>}
303 , gridVariables_{gridVariables}
304 , solutionVector_{sol}
306 setVerbosity(defaultVerbosity_);
313 template<
typename Format>
315 const GridVariables& gridVariables,
316 const SolutionVector& sol,
317 const std::string& filename)
318 : ParentType{fmt, gridVariables.gridGeometry().gridView(), filename, order<1>}
319 , gridVariables_{gridVariables}
320 , solutionVector_{sol}
322 setVerbosity(defaultVerbosity_);
328 template<std::invocable<const VolumeVariables&> VolVarFunction>
329 void addVolumeVariable(VolVarFunction&& f,
const std::string& name)
331 using ResultType = std::invoke_result_t<std::remove_cvref_t<VolVarFunction>,
const VolumeVariables&>;
332 if constexpr (GridFormat::Concepts::Scalar<ResultType>)
333 setVolVarField_<ResultType>(name, volVarFields_.registerScalarField(name, [_f=std::move(f)] (
const auto& vv) {
334 return static_cast<Scalar>(_f(vv));
336 else if constexpr (GridFormat::mdrange_dimension<ResultType> == 1)
337 setVolVarField_<GridFormat::MDRangeScalar<ResultType>>(name, volVarFields_.registerVectorField(name, [_f=std::move(f)] (
const auto& vv) {
338 return VolVarFieldStorage::toStorageVector(_f(vv));
340 else if constexpr (GridFormat::mdrange_dimension<ResultType> == 2)
341 setVolVarField_<GridFormat::MDRangeScalar<ResultType>>(name, volVarFields_.registerTensorField(name, [_f=std::move(f)] (
const auto& vv) {
342 return VolVarFieldStorage::toStorageTensor(_f(vv));
347 Dune::AlwaysFalse<VolVarFunction>::value,
348 "Could not identify the given volume variable as scalar, vector or tensor."
357 template<Detail::Container C>
358 void addField(
const C& values,
const std::string& name)
359 { addField(values, name, GridFormat::Precision<GridFormat::MDRangeScalar<C>>{}); }
365 template<Detail::Container C, Gr
idFormat::Concepts::Scalar T>
366 void addField(
const C& values,
const std::string& name,
const GridFormat::Precision<T>& prec)
368 const bool hasCellSize = values.size() == gridVariables_.gridGeometry().elementMapper().size();
369 const bool hasPointSize = values.size() == gridVariables_.gridGeometry().vertexMapper().size();
370 if (hasCellSize && hasPointSize)
371 DUNE_THROW(Dune::InvalidStateException,
"Automatic deduction of field type failed. Please use addCellField or addPointField instead.");
372 if (!hasCellSize && !hasPointSize)
373 DUNE_THROW(Dune::InvalidStateException,
"Automatic deduction of field type failed. Given container size does not match neither the number of points nor cells.");
376 addCellField(values, name, prec);
378 addPointField(values, name, prec);
384 template<Gr
idFormat::Concepts::Po
intFunction<Gr
idView> DofFunction>
385 void addPointField(DofFunction&& f,
const std::string& name)
386 { this->setPointField(name, std::forward<DofFunction>(f)); }
391 template<Detail::Container C>
392 void addPointField(
const C& values,
const std::string& name)
393 { addPointField(values, name, GridFormat::Precision<GridFormat::MDRangeScalar<C>>{}); }
398 template<Detail::Container C, Gr
idFormat::Concepts::Scalar T>
399 void addPointField(
const C& values,
const std::string& name,
const GridFormat::Precision<T>& prec)
401 if (values.size() != gridVariables_.gridGeometry().vertexMapper().size())
402 DUNE_THROW(Dune::InvalidStateException,
"Given container does not match the number of points in the grid");
404 addPointField_(values, name, prec);
410 template<Gr
idFormat::Concepts::CellFunction<Gr
idView> DofFunction>
411 void addCellField(DofFunction&& f,
const std::string& name)
412 { this->setCellField(name, std::forward<DofFunction>(f)); }
417 template<Detail::Container C>
418 void addCellField(
const C& values,
const std::string& name)
419 { addCellField(values, name, GridFormat::Precision<GridFormat::MDRangeScalar<C>>{}); }
424 template<Detail::Container C, Gr
idFormat::Concepts::Scalar T>
425 void addCellField(
const C& values,
const std::string& name,
const GridFormat::Precision<T>& prec)
427 if (values.size() != gridVariables_.gridGeometry().elementMapper().size())
428 DUNE_THROW(Dune::InvalidStateException,
"Given container does not match the number of cells in the grid");
430 addCellField_(values, name, prec);
436 std::string write(
const std::string& name)
440 volVarFields_.updateFieldData(gridVariables_, solutionVector_);
441 auto filename = ParentType::write(name);
442 volVarFields_.clearFieldData();
446 std::cout << Fmt::format(
447 "Writing output to \"{}\". Took {:.2g} seconds.\n",
448 filename, timer.elapsed()
457 template<std::
floating_po
int T>
458 std::string write(T time)
462 volVarFields_.updateFieldData(gridVariables_, solutionVector_);
463 auto filename = ParentType::write(time);
464 volVarFields_.clearFieldData();
468 std::cout << Fmt::format(
469 "Writing output to \"{}\". Took {:.2g} seconds.\n",
470 filename, timer.elapsed()
479 volVarFields_.clear();
482 void setVerbosity(
int verbosity)
484 if (gridVariables_.gridGeometry().gridView().comm().rank() == 0)
485 verbosity_ = verbosity;
490 template<Detail::Container C, Gr
idFormat::Concepts::Scalar T>
491 requires(GridFormat::has_sub_range<C> && std::ranges::range_value_t<C>::size() == 1)
492 void addPointField_(
const C& values,
const std::string& name,
const GridFormat::Precision<T>& prec)
494 this->setPointField(name, [&] (
const auto& vertex) -> T {
495 return values[gridVariables_.gridGeometry().vertexMapper().index(vertex)][0];
499 template<Detail::Container C, Gr
idFormat::Concepts::Scalar T>
500 void addPointField_(
const C& values,
const std::string& name,
const GridFormat::Precision<T>& prec)
502 this->setPointField(name, [&] (
const auto& vertex) -> std::ranges::range_value_t<C> {
503 return values[gridVariables_.gridGeometry().vertexMapper().index(vertex)];
508 template<Detail::Container C, Gr
idFormat::Concepts::Scalar T>
509 requires(GridFormat::has_sub_range<C> && std::ranges::range_value_t<C>::size() == 1)
510 void addCellField_(
const C& values,
const std::string& name,
const GridFormat::Precision<T>& prec)
512 this->setCellField(name, [&] (
const auto& element) -> T {
513 return values[gridVariables_.gridGeometry().elementMapper().index(element)][0];
517 template<Detail::Container C, Gr
idFormat::Concepts::Scalar T>
518 void addCellField_(
const C& values,
const std::string& name,
const GridFormat::Precision<T>& prec)
520 this->setCellField(name, [&] (
const auto& element) -> std::ranges::range_value_t<C> {
521 return values[gridVariables_.gridGeometry().elementMapper().index(element)];
525 template<
typename ResultType,
typename Id>
526 void setVolVarField_(
const std::string& name, Id&& volVarFieldId)
528 auto dofEntityField = [&, _id=std::move(volVarFieldId)] (
const auto& entity) {
529 return volVarFields_.getValue(_id, gridVariables_.gridGeometry().dofMapper().index(entity));
532 this->setPointField(name, std::move(dofEntityField), GridFormat::Precision<ResultType>{});
534 this->setCellField(name, std::move(dofEntityField), GridFormat::Precision<ResultType>{});
537 const GridVariables& gridVariables_;
538 const SolutionVector& solutionVector_;
539 VolVarFieldStorage volVarFields_;
544template<
typename Gr
idVariables,
typename SolutionVector>
545class OutputModule<GridVariables, SolutionVector>::VolVarFieldStorage
548 { scalar, vector, tensor };
561 std::function<T(
const VolVar&)> getter;
565 template<FieldType ft>
566 struct FieldId { std::size_t index; };
568 template<std::ranges::range R>
569 static constexpr auto toStorageVector(R&& in)
572 std::ranges::copy(in, result.begin());
576 template<Gr
idFormat::Concepts::MDRange<2> R>
577 static constexpr auto toStorageTensor(R&& in)
580 std::ranges::for_each(in, [&, i=0] (
const auto& row)
mutable {
581 std::ranges::copy(row, result[i++].begin());
586 template<FieldType ft>
587 const auto& getValue(
const FieldId<ft>&
id, std::size_t idx)
const
589 if constexpr (ft == FieldType::scalar)
590 return scalarFieldStorage_.at(
id.index).data.at(idx);
591 else if constexpr (ft == FieldType::vector)
592 return vectorFieldStorage_.at(
id.index).data.at(idx);
594 return tensorFieldStorage_.at(
id.index).data.at(idx);
597 auto registerScalarField(std::string name, std::function<Scalar(
const VolVar&)> f)
598 {
return register_<FieldType::scalar>(std::move(name), scalarFieldStorage_, std::move(f)); }
600 auto registerVectorField(std::string name, std::function<Vector(
const VolVar&)> f)
601 {
return register_<FieldType::vector>(std::move(name), vectorFieldStorage_, std::move(f)); }
603 auto registerTensorField(std::string name, std::function<Tensor(
const VolVar&)> f)
604 {
return register_<FieldType::tensor>(std::move(name), tensorFieldStorage_, std::move(f)); }
606 void updateFieldData(
const GridVariables& gridVars,
const SolutionVector& x)
608 resizeFieldData_(gridVars.gridGeometry().numDofs());
609 const auto range = GridFormat::cells(gridVars.gridGeometry().gridView());
611#
if __cpp_lib_parallel_algorithm >= 201603L
612 std::execution::par_unseq,
614 std::ranges::begin(range),
615 std::ranges::end(range),
616 [&] (
const auto& element) {
617 auto fvGeometry =
localView(gridVars.gridGeometry()).bindElement(element);
618 auto elemVolVars =
localView(gridVars.curGridVolVars()).bindElement(element, fvGeometry, x);
619 for (
const auto& scv :
scvs(fvGeometry))
621 const auto& volVars = elemVolVars[scv];
622 for (
auto& s : scalarFieldStorage_) { s.data.at(scv.dofIndex()) = s.getter(volVars); }
623 for (
auto& s : vectorFieldStorage_) { s.data.at(scv.dofIndex()) = s.getter(volVars); }
624 for (
auto& s : tensorFieldStorage_) { s.data.at(scv.dofIndex()) = s.getter(volVars); }
629 void clearFieldData()
631 for (
auto& s : scalarFieldStorage_) { s.data.clear(); }
632 for (
auto& s : vectorFieldStorage_) { s.data.clear(); }
633 for (
auto& s : tensorFieldStorage_) { s.data.clear(); }
639 scalarFieldStorage_.clear();
640 vectorFieldStorage_.clear();
641 tensorFieldStorage_.clear();
645 template<FieldType ft,
typename T>
646 auto register_(std::string&& name,
647 std::vector<FieldStorage<T>>& storage,
648 std::function<T(
const VolVar&)>&& f)
650 if (exists_<ft>(name))
651 DUNE_THROW(Dune::InvalidStateException,
"Volume variables field '" << name <<
"' is already defined.");
653 FieldId<ft>
id{storage.size()};
654 fields_.emplace_back(FieldInfo{std::move(name), ft,
id.index});
655 storage.push_back({{}, std::move(f)});
659 template<FieldType ft>
660 bool exists_(
const std::string& name)
const
662 return std::ranges::any_of(fields_, [&] (
const FieldInfo& info) {
663 return info.type == ft && info.name == name;
667 void resizeFieldData_(std::size_t size)
669 std::ranges::for_each(scalarFieldStorage_, [&] (
auto& s) { s.data.resize(size); });
670 std::ranges::for_each(vectorFieldStorage_, [&] (
auto& s) { s.data.resize(size); });
671 std::ranges::for_each(tensorFieldStorage_, [&] (
auto& s) { s.data.resize(size); });
674 std::vector<FieldInfo> fields_;
675 std::vector<FieldStorage<Scalar>> scalarFieldStorage_;
676 std::vector<FieldStorage<Vector>> vectorFieldStorage_;
677 std::vector<FieldStorage<Tensor>> tensorFieldStorage_;
686template<
class... Args>
690 template<
class... _Args>
695 "GridWriter only available when the GridFormat library is available. "
696 "Use `git submodule update --init` to pull it and reconfigure the project "
697 "(note: C++20 is required)."
702template<
class... Args>
706 template<
class... _Args>
711 "OutputModule only available when the GridFormat library is available. "
712 "Use `git submodule update --init` to pull it and reconfigure the project "
713 "(note: C++20 is required)."
Definition gridwriter.hh:688
GridWriter(_Args &&...)
Definition gridwriter.hh:691
Definition gridwriter.hh:704
OutputModule(_Args &&...)
Definition gridwriter.hh:707
A gridformat-compatible grid type for CVFE discretizations that uses DuMux DOF indices directly for V...
GridCache::LocalView localView(const GridCache &gridCache)
Free function to get the local view of a grid cache object.
Definition localview.hh:26
T getParam(Args &&... args)
A free function to get a parameter from the parameter tree singleton.
Definition parameters.hh:139
The available discretization methods in Dumux.
constexpr bool isCVFE
Definition method.hh:67
Definition cvfegridfunction.hh:31
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.