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>
43template<
typename Grid,
typename Format,
typename Comm,
typename... Args>
44auto makeParallelWriter(
const Grid& grid,
const Format& fmt,
const Dune::Communication<Comm>& comm, Args&&... args)
46 return comm.size() > 1
47 ? GridFormat::Writer<Grid>{fmt, grid,
static_cast<Comm
>(comm), std::forward<Args>(args)...}
48 : GridFormat::Writer<Grid>{fmt, grid, std::forward<Args>(args)...};
51template<
typename Grid,
typename Format,
typename... Args>
52auto makeParallelWriter(
const Grid& grid,
const Format& fmt,
const Dune::Communication<Dune::No_Comm>&, Args&&... args)
53{
return GridFormat::Writer<Grid>{fmt, grid, std::forward<Args>(args)...}; }
55template<
typename Grid,
typename Format,
typename... Args>
56auto makeWriter(
const Grid& grid,
const Format& fmt, Args&&... args)
58 const auto& comm = GridFormat::Dune::Traits::GridView<Grid>::get(grid).comm();
59 return makeParallelWriter(grid, fmt, comm, std::forward<Args>(args)...);
63concept Container =
requires(
const T& t) {
72namespace VTK {
using namespace GridFormat::VTK; }
73namespace Format {
using namespace GridFormat::Formats; }
74namespace Encoding {
using namespace GridFormat::Encoding; }
75namespace Compression {
using namespace GridFormat::Compression;
using GridFormat::none; }
77 using GridFormat::float32;
78 using GridFormat::float64;
80 using GridFormat::uint64;
81 using GridFormat::uint32;
82 using GridFormat::uint16;
83 using GridFormat::uint8;
85 using GridFormat::int64;
86 using GridFormat::int32;
87 using GridFormat::int16;
88 using GridFormat::int8;
97struct Order {
static_assert(order > 0,
"order must be > 0"); };
100inline constexpr auto order = Order<o>{};
116template<Gr
idFormat::Concepts::Gr
id Gr
idView,
int order = 1>
119 using Grid = std::conditional_t<
121 CVFELagrangeGrid<GridView, order>,
124 using Cell = GridFormat::Cell<Grid>;
125 using Vertex =
typename GridView::template Codim<GridView::dimension>::Entity;
126 using Element =
typename GridView::template Codim<0>::Entity;
127 using Writer = GridFormat::Writer<Grid>;
134 template<
typename Format>
136 const GridView& gridView,
137 const Order<order>& = {})
138 : gridView_{gridView}
139 , grid_{makeGrid_(gridView)}
140 , writer_{Detail::makeWriter(grid_, fmt)}
142 if (gridView.comm().size() > 0 &&
getParam<bool>(
"IO.GridWriter.AddProcessRank",
true))
143 setCellField(
"process rank", [&](
const Cell&) {
return gridView.comm().rank(); }, Precision::uint64);
150 template<
typename Format>
152 const GridView& gridView,
153 const std::string& filename,
154 const Order<order>& = {})
155 : gridView_{gridView}
156 , grid_{makeGrid_(gridView)}
157 , writer_{Detail::makeWriter(grid_, fmt, filename)}
159 if (gridView.comm().size() > 0 &&
getParam<bool>(
"IO.GridWriter.AddProcessRank",
true))
160 setCellField(
"process rank", [&](
const Cell&) {
return gridView.comm().rank(); }, Precision::uint64);
167 std::string write(
const std::string& name)
const
168 {
return writer_.write(name); }
174 template<std::
floating_po
int T>
175 std::string write(T time)
const
176 {
return writer_.write(time); }
179 template<Gr
idFormat::Concepts::CellFunction<Gr
idView> F,
180 Gr
idFormat::Concepts::Scalar T = Gr
idFormat::FieldScalar<std::invoke_result_t<F, Element>>>
181 void setCellField(
const std::string& name, F&& f,
const GridFormat::Precision<T>& prec = {})
182 { writer_.set_cell_field(name, std::move(f), prec); }
185 template<Gr
idFormat::Dune::Concepts::Function<Gr
idView> F>
186 void setCellField(
const std::string& name, F&& f)
187 { GridFormat::Dune::set_cell_function(std::forward<F>(f), writer_, name); }
190 template<
typename F, Gr
idFormat::Concepts::Scalar T>
191 void setCellField(
const std::string& name, F&& f,
const GridFormat::Precision<T>& prec)
192 { GridFormat::Dune::set_cell_function(std::forward<F>(f), writer_, name, prec); }
195 template<Gr
idFormat::Concepts::Po
intFunction<Gr
idView> F,
196 Gr
idFormat::Concepts::Scalar T = Gr
idFormat::FieldScalar<std::invoke_result_t<F, Vertex>>>
197 void setPointField(
const std::string& name, F&& f,
const GridFormat::Precision<T>& prec = {})
198 requires (order == 1)
199 { writer_.set_point_field(name, std::move(f), prec); }
203 template<Detail::Container C,
204 GridFormat::Concepts::Scalar T = GridFormat::MDRangeScalar<C>>
205 void setPointField(
const std::string& name,
const C& values,
206 const GridFormat::Precision<T>& prec = {})
209 using Point =
typename Grid::Point;
210 writer_.set_point_field(name, [&values](
const Point& p) -> std::ranges::range_value_t<C> {
211 return values[p.dofIndex];
217 template<Gr
idFormat::Dune::Concepts::Function<Gr
idView> F>
218 void setPointField(
const std::string& name, F&& f)
219 { GridFormat::Dune::set_point_function(std::forward<F>(f), writer_, name); }
222 template<Gr
idFormat::Dune::Concepts::Function<Gr
idView> F, Gr
idFormat::Concepts::Scalar T>
223 void setPointField(
const std::string& name, F&& f,
const GridFormat::Precision<T>& prec)
224 { GridFormat::Dune::set_point_function(std::forward<F>(f), writer_, name, prec); }
233 if constexpr (order > 1)
234 grid_.update(gridView_);
238 Grid makeGrid_(
const GridView& gv)
const
240 if constexpr (order > 1)
255template<
typename Gr
idVariables,
typename SolutionVector>
256class OutputModule :
private GridWriter<typename Dumux::GridDiscretization_t<GridVariables>::GridView, 1> {
257 using ParentType = GridWriter<typename Dumux::GridDiscretization_t<GridVariables>::GridView, 1>;
258 using GridView =
typename Dumux::GridDiscretization_t<GridVariables>::GridView;
260 static constexpr bool isCVFE = DiscretizationMethods::isCVFE<typename Dumux::GridDiscretization_t<GridVariables>::DiscretizationMethod>;
261 static constexpr int dimWorld = Dumux::GridDiscretization_t<GridVariables>::GridView::dimensionworld;
262 using Scalar =
typename GridVariables::Scalar;
263 using Vector = Dune::FieldVector<Scalar, dimWorld>;
264 using Tensor = Dune::FieldMatrix<Scalar, dimWorld, dimWorld>;
265 using VolVar =
typename GridVariables::VolumeVariables;
267 class VolVarFieldStorage;
269 static constexpr int defaultVerbosity_ = 1;
272 using VolumeVariables = VolVar;
274 static constexpr auto defaultFileFormat = IO::Format::pvd_with(
275 IO::Format::vtu.with({
276 .encoder = IO::Encoding::ascii,
277 .compressor = IO::Compression::none,
278 .data_format = VTK::DataFormat::inlined
285 explicit OutputModule(
const GridVariables& gridVariables,
286 const SolutionVector& sol,
287 const std::string& filename)
288 : ParentType{defaultFileFormat, Dumux::
gridDiscretization(gridVariables).gridView(), filename, order<1>}
289 , gridVariables_{gridVariables}
290 , solutionVector_{sol}
292 setVerbosity(defaultVerbosity_);
299 template<
typename Format>
301 const GridVariables& gridVariables,
302 const SolutionVector& sol)
304 , gridVariables_{gridVariables}
305 , solutionVector_{sol}
307 setVerbosity(defaultVerbosity_);
314 template<
typename Format>
316 const GridVariables& gridVariables,
317 const SolutionVector& sol,
318 const std::string& filename)
319 : ParentType{fmt, Dumux::
gridDiscretization(gridVariables).gridView(), filename, order<1>}
320 , gridVariables_{gridVariables}
321 , solutionVector_{sol}
323 setVerbosity(defaultVerbosity_);
329 template<std::invocable<const VolumeVariables&> VolVarFunction>
330 void addVolumeVariable(VolVarFunction&& f,
const std::string& name)
332 using ResultType = std::invoke_result_t<std::remove_cvref_t<VolVarFunction>,
const VolumeVariables&>;
333 if constexpr (GridFormat::Concepts::Scalar<ResultType>)
334 setVolVarField_<ResultType>(name, volVarFields_.registerScalarField(name, [_f=std::move(f)] (
const auto& vv) {
335 return static_cast<Scalar>(_f(vv));
337 else if constexpr (GridFormat::mdrange_dimension<ResultType> == 1)
338 setVolVarField_<GridFormat::MDRangeScalar<ResultType>>(name, volVarFields_.registerVectorField(name, [_f=std::move(f)] (
const auto& vv) {
339 return VolVarFieldStorage::toStorageVector(_f(vv));
341 else if constexpr (GridFormat::mdrange_dimension<ResultType> == 2)
342 setVolVarField_<GridFormat::MDRangeScalar<ResultType>>(name, volVarFields_.registerTensorField(name, [_f=std::move(f)] (
const auto& vv) {
343 return VolVarFieldStorage::toStorageTensor(_f(vv));
348 Dune::AlwaysFalse<VolVarFunction>::value,
349 "Could not identify the given volume variable as scalar, vector or tensor."
358 template<Detail::Container C>
359 void addField(
const C& values,
const std::string& name)
360 { addField(values, name, GridFormat::Precision<GridFormat::MDRangeScalar<C>>{}); }
366 template<Detail::Container C, Gr
idFormat::Concepts::Scalar T>
367 void addField(
const C& values,
const std::string& name,
const GridFormat::Precision<T>& prec)
369 const bool hasCellSize = values.size() == gridDisc_().elementMapper().size();
370 const bool hasPointSize = values.size() == gridDisc_().vertexMapper().size();
371 if (hasCellSize && hasPointSize)
372 DUNE_THROW(Dune::InvalidStateException,
"Automatic deduction of field type failed. Please use addCellField or addPointField instead.");
373 if (!hasCellSize && !hasPointSize)
374 DUNE_THROW(Dune::InvalidStateException,
"Automatic deduction of field type failed. Given container size does not match neither the number of points nor cells.");
377 addCellField(values, name, prec);
379 addPointField(values, name, prec);
385 template<Gr
idFormat::Concepts::Po
intFunction<Gr
idView> DofFunction>
386 void addPointField(DofFunction&& f,
const std::string& name)
387 { this->setPointField(name, std::forward<DofFunction>(f)); }
392 template<Detail::Container C>
393 void addPointField(
const C& values,
const std::string& name)
394 { addPointField(values, name, GridFormat::Precision<GridFormat::MDRangeScalar<C>>{}); }
399 template<Detail::Container C, Gr
idFormat::Concepts::Scalar T>
400 void addPointField(
const C& values,
const std::string& name,
const GridFormat::Precision<T>& prec)
402 if (values.size() != gridDisc_().vertexMapper().size())
403 DUNE_THROW(Dune::InvalidStateException,
"Given container does not match the number of points in the grid");
405 addPointField_(values, name, prec);
411 template<Gr
idFormat::Concepts::CellFunction<Gr
idView> DofFunction>
412 void addCellField(DofFunction&& f,
const std::string& name)
413 { this->setCellField(name, std::forward<DofFunction>(f)); }
418 template<Detail::Container C>
419 void addCellField(
const C& values,
const std::string& name)
420 { addCellField(values, name, GridFormat::Precision<GridFormat::MDRangeScalar<C>>{}); }
425 template<Detail::Container C, Gr
idFormat::Concepts::Scalar T>
426 void addCellField(
const C& values,
const std::string& name,
const GridFormat::Precision<T>& prec)
428 if (values.size() != gridDisc_().elementMapper().size())
429 DUNE_THROW(Dune::InvalidStateException,
"Given container does not match the number of cells in the grid");
431 addCellField_(values, name, prec);
437 std::string write(
const std::string& name)
441 volVarFields_.updateFieldData(gridVariables_, solutionVector_);
442 auto filename = ParentType::write(name);
443 volVarFields_.clearFieldData();
447 std::cout << Fmt::format(
448 "Writing output to \"{}\". Took {:.2g} seconds.\n",
449 filename, timer.elapsed()
458 template<std::
floating_po
int T>
459 std::string write(T time)
463 volVarFields_.updateFieldData(gridVariables_, solutionVector_);
464 auto filename = ParentType::write(time);
465 volVarFields_.clearFieldData();
469 std::cout << Fmt::format(
470 "Writing output to \"{}\". Took {:.2g} seconds.\n",
471 filename, timer.elapsed()
480 volVarFields_.clear();
483 void setVerbosity(
int verbosity)
485 if (gridDisc_().gridView().comm().rank() == 0)
486 verbosity_ = verbosity;
491 template<Detail::Container C, Gr
idFormat::Concepts::Scalar T>
492 requires(GridFormat::has_sub_range<C> && std::ranges::range_value_t<C>::size() == 1)
493 void addPointField_(
const C& values,
const std::string& name,
const GridFormat::Precision<T>& prec)
495 this->setPointField(name, [&] (
const auto& vertex) -> T {
496 return values[gridDisc_().vertexMapper().index(vertex)][0];
500 template<Detail::Container C, Gr
idFormat::Concepts::Scalar T>
501 void addPointField_(
const C& values,
const std::string& name,
const GridFormat::Precision<T>& prec)
503 this->setPointField(name, [&] (
const auto& vertex) -> std::ranges::range_value_t<C> {
504 return values[gridDisc_().vertexMapper().index(vertex)];
509 template<Detail::Container C, Gr
idFormat::Concepts::Scalar T>
510 requires(GridFormat::has_sub_range<C> && std::ranges::range_value_t<C>::size() == 1)
511 void addCellField_(
const C& values,
const std::string& name,
const GridFormat::Precision<T>& prec)
513 this->setCellField(name, [&] (
const auto& element) -> T {
514 return values[gridDisc_().elementMapper().index(element)][0];
518 template<Detail::Container C, Gr
idFormat::Concepts::Scalar T>
519 void addCellField_(
const C& values,
const std::string& name,
const GridFormat::Precision<T>& prec)
521 this->setCellField(name, [&] (
const auto& element) -> std::ranges::range_value_t<C> {
522 return values[gridDisc_().elementMapper().index(element)];
526 template<
typename ResultType,
typename Id>
527 void setVolVarField_(
const std::string& name, Id&& volVarFieldId)
529 auto dofEntityField = [&, _id=std::move(volVarFieldId)] (
const auto& entity) {
530 return volVarFields_.getValue(_id, gridDisc_().dofMapper().index(entity));
533 this->setPointField(name, std::move(dofEntityField), GridFormat::Precision<ResultType>{});
535 this->setCellField(name, std::move(dofEntityField), GridFormat::Precision<ResultType>{});
538 const auto& gridDisc_()
const
541 const GridVariables& gridVariables_;
542 const SolutionVector& solutionVector_;
543 VolVarFieldStorage volVarFields_;
548template<
typename Gr
idVariables,
typename SolutionVector>
549class OutputModule<GridVariables, SolutionVector>::VolVarFieldStorage
552 { scalar, vector, tensor };
565 std::function<T(
const VolVar&)> getter;
569 template<FieldType ft>
570 struct FieldId { std::size_t index; };
572 template<std::ranges::range R>
573 static constexpr auto toStorageVector(R&& in)
576 std::ranges::copy(in, result.begin());
580 template<Gr
idFormat::Concepts::MDRange<2> R>
581 static constexpr auto toStorageTensor(R&& in)
584 std::ranges::for_each(in, [&, i=0] (
const auto& row)
mutable {
585 std::ranges::copy(row, result[i++].begin());
590 template<FieldType ft>
591 const auto& getValue(
const FieldId<ft>&
id, std::size_t idx)
const
593 if constexpr (ft == FieldType::scalar)
594 return scalarFieldStorage_.at(
id.index).data.at(idx);
595 else if constexpr (ft == FieldType::vector)
596 return vectorFieldStorage_.at(
id.index).data.at(idx);
598 return tensorFieldStorage_.at(
id.index).data.at(idx);
601 auto registerScalarField(std::string name, std::function<Scalar(
const VolVar&)> f)
602 {
return register_<FieldType::scalar>(std::move(name), scalarFieldStorage_, std::move(f)); }
604 auto registerVectorField(std::string name, std::function<Vector(
const VolVar&)> f)
605 {
return register_<FieldType::vector>(std::move(name), vectorFieldStorage_, std::move(f)); }
607 auto registerTensorField(std::string name, std::function<Tensor(
const VolVar&)> f)
608 {
return register_<FieldType::tensor>(std::move(name), tensorFieldStorage_, std::move(f)); }
610 void updateFieldData(
const GridVariables& gridVars,
const SolutionVector& x)
613 resizeFieldData_(gridDisc.numDofs());
614 const auto range = GridFormat::cells(gridDisc.gridView());
616#
if __cpp_lib_parallel_algorithm >= 201603L
617 std::execution::par_unseq,
619 std::ranges::begin(range),
620 std::ranges::end(range),
621 [&] (
const auto& element) {
622 auto fvGeometry =
localView(gridDisc).bindElement(element);
623 auto elemVolVars =
localView(gridVars.curGridVolVars()).bindElement(element, fvGeometry, x);
624 for (
const auto& scv :
scvs(fvGeometry))
626 const auto& volVars = elemVolVars[scv];
627 for (
auto& s : scalarFieldStorage_) { s.data.at(scv.dofIndex()) = s.getter(volVars); }
628 for (
auto& s : vectorFieldStorage_) { s.data.at(scv.dofIndex()) = s.getter(volVars); }
629 for (
auto& s : tensorFieldStorage_) { s.data.at(scv.dofIndex()) = s.getter(volVars); }
634 void clearFieldData()
636 for (
auto& s : scalarFieldStorage_) { s.data.clear(); }
637 for (
auto& s : vectorFieldStorage_) { s.data.clear(); }
638 for (
auto& s : tensorFieldStorage_) { s.data.clear(); }
644 scalarFieldStorage_.clear();
645 vectorFieldStorage_.clear();
646 tensorFieldStorage_.clear();
650 template<FieldType ft,
typename T>
651 auto register_(std::string&& name,
652 std::vector<FieldStorage<T>>& storage,
653 std::function<T(
const VolVar&)>&& f)
655 if (exists_<ft>(name))
656 DUNE_THROW(Dune::InvalidStateException,
"Volume variables field '" << name <<
"' is already defined.");
658 FieldId<ft>
id{storage.size()};
659 fields_.emplace_back(FieldInfo{std::move(name), ft,
id.index});
660 storage.push_back({{}, std::move(f)});
664 template<FieldType ft>
665 bool exists_(
const std::string& name)
const
667 return std::ranges::any_of(fields_, [&] (
const FieldInfo& info) {
668 return info.type == ft && info.name == name;
672 void resizeFieldData_(std::size_t size)
674 std::ranges::for_each(scalarFieldStorage_, [&] (
auto& s) { s.data.resize(size); });
675 std::ranges::for_each(vectorFieldStorage_, [&] (
auto& s) { s.data.resize(size); });
676 std::ranges::for_each(tensorFieldStorage_, [&] (
auto& s) { s.data.resize(size); });
679 std::vector<FieldInfo> fields_;
680 std::vector<FieldStorage<Scalar>> scalarFieldStorage_;
681 std::vector<FieldStorage<Vector>> vectorFieldStorage_;
682 std::vector<FieldStorage<Tensor>> tensorFieldStorage_;
691template<
class... Args>
695 template<
class... _Args>
700 "GridWriter only available when the GridFormat library is available. "
701 "Use `git submodule update --init` to pull it and reconfigure the project "
702 "(note: C++20 is required)."
707template<
class... Args>
711 template<
class... _Args>
716 "OutputModule only available when the GridFormat library is available. "
717 "Use `git submodule update --init` to pull it and reconfigure the project "
718 "(note: C++20 is required)."
Definition gridwriter.hh:693
GridWriter(_Args &&...)
Definition gridwriter.hh:696
Definition gridwriter.hh:709
OutputModule(_Args &&...)
Definition gridwriter.hh:712
A gridformat-compatible grid type for CVFE discretizations that uses DuMux DOF indices directly for V...
Type traits for classes providing a grid discretization.
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
decltype(auto) gridDiscretization(const T &t, Args &&... args)
The grid discretization.
Definition griddiscretization.hh:65
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.