version 3.11-dev
Loading...
Searching...
No Matches
gridwriter.hh
Go to the documentation of this file.
1// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2// vi: set et ts=4 sw=2 sts=2:
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_GRID_WRITER_HH
13#define DUMUX_IO_GRID_WRITER_HH
14
15#include <config.h>
16
17#if DUMUX_HAVE_GRIDFORMAT
18
19#include <ranges>
20#include <execution>
21#include <concepts>
22#include <type_traits>
23
24#include <gridformat/common/type_traits.hpp>
25#include <gridformat/gridformat.hpp>
26#include <gridformat/traits/dune.hpp>
27
28#include <dune/common/exceptions.hh>
29#include <dune/common/timer.hh>
30
31#include <dumux/io/format.hh>
35
36namespace Dumux::IO {
37
38
39#ifndef DOXYGEN
40namespace Detail {
41
42template<typename Grid, typename Format, typename Comm, typename... Args>
43auto makeParallelWriter(const Grid& grid, const Format& fmt, const Dune::Communication<Comm>& comm, Args&&... args)
44{
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)...};
48}
49
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)...}; }
53
54template<typename Grid, typename Format, typename... Args>
55auto makeWriter(const Grid& grid, const Format& fmt, Args&&... args)
56{
57 const auto& comm = GridFormat::Dune::Traits::GridView<Grid>::get(grid).comm();
58 return makeParallelWriter(grid, fmt, comm, std::forward<Args>(args)...);
59}
60
61template<typename T>
62concept Container = requires(const T& t) {
63 { t.size() };
64 { t[std::size_t{}] };
65};
66
67} // namespace Detail
68#endif // DOXYGEN
69
70
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; }
75namespace Precision {
76 using GridFormat::float32;
77 using GridFormat::float64;
78
79 using GridFormat::uint64;
80 using GridFormat::uint32;
81 using GridFormat::uint16;
82 using GridFormat::uint8;
83
84 using GridFormat::int64;
85 using GridFormat::int32;
86 using GridFormat::int16;
87 using GridFormat::int8;
88} // namespace Precision
89
90
95template<int order>
96struct Order { static_assert(order > 0, "order must be > 0"); };
97
98template<int o>
99inline constexpr auto order = Order<o>{};
100
115template<GridFormat::Concepts::Grid GridView, int order = 1>
116class GridWriter
117{
118 using Grid = std::conditional_t<
119 (order > 1),
120 CVFELagrangeGrid<GridView, order>,
121 GridView
122 >;
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>;
127
128 public:
133 template<typename Format>
134 explicit GridWriter(const Format& fmt,
135 const GridView& gridView,
136 const Order<order>& = {})
137 : gridView_{gridView}
138 , grid_{makeGrid_(gridView)}
139 , writer_{Detail::makeWriter(grid_, fmt)}
140 {
141 if (gridView.comm().size() > 0 && getParam<bool>("IO.GridWriter.AddProcessRank", true))
142 setCellField("process rank", [&](const Cell&) { return gridView.comm().rank(); }, Precision::uint64);
143 }
144
149 template<typename Format>
150 explicit GridWriter(const Format& fmt,
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)}
157 {
158 if (gridView.comm().size() > 0 && getParam<bool>("IO.GridWriter.AddProcessRank", true))
159 setCellField("process rank", [&](const Cell&) { return gridView.comm().rank(); }, Precision::uint64);
160 }
161
166 std::string write(const std::string& name) const
167 { return writer_.write(name); }
168
173 template<std::floating_point T>
174 std::string write(T time) const
175 { return writer_.write(time); }
176
178 template<GridFormat::Concepts::CellFunction<GridView> F,
179 GridFormat::Concepts::Scalar T = GridFormat::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); }
182
184 template<GridFormat::Dune::Concepts::Function<GridView> F>
185 void setCellField(const std::string& name, F&& f)
186 { GridFormat::Dune::set_cell_function(std::forward<F>(f), writer_, name); }
187
189 template<typename F, GridFormat::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); }
192
194 template<GridFormat::Concepts::PointFunction<GridView> F,
195 GridFormat::Concepts::Scalar T = GridFormat::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); }
199
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 = {})
206 requires (order > 1)
207 {
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];
211 }, prec);
212 }
213
216 template<GridFormat::Dune::Concepts::Function<GridView> F>
217 void setPointField(const std::string& name, F&& f)
218 { GridFormat::Dune::set_point_function(std::forward<F>(f), writer_, name); }
219
221 template<GridFormat::Dune::Concepts::Function<GridView> F, GridFormat::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); }
224
226 void clear()
227 { writer_.clear(); }
228
230 void update()
231 {
232 if constexpr (order > 1)
233 grid_.update(gridView_);
234 }
235
236 private:
237 Grid makeGrid_(const GridView& gv) const
238 {
239 if constexpr (order > 1)
240 return Grid{gv};
241 else
242 return gv;
243 }
244
245 GridView gridView_;
246 Grid grid_;
247 Writer writer_;
248};
249
254template<typename GridVariables, typename SolutionVector>
255class OutputModule : private GridWriter<typename GridVariables::GridGeometry::GridView, 1> {
256 using ParentType = GridWriter<typename GridVariables::GridGeometry::GridView, 1>;
257 using GridView = typename GridVariables::GridGeometry::GridView;
258
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;
265
266 class VolVarFieldStorage;
267
268 static constexpr int defaultVerbosity_ = 1;
269
270 public:
271 using VolumeVariables = VolVar;
272
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
278 })
279 );
280
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}
290 {
291 setVerbosity(defaultVerbosity_);
292 }
293
298 template<typename Format>
299 explicit OutputModule(const Format& fmt,
300 const GridVariables& gridVariables,
301 const SolutionVector& sol)
302 : ParentType{fmt, gridVariables.gridGeometry().gridView(), order<1>}
303 , gridVariables_{gridVariables}
304 , solutionVector_{sol}
305 {
306 setVerbosity(defaultVerbosity_);
307 }
308
313 template<typename Format>
314 explicit OutputModule(const Format& fmt,
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}
321 {
322 setVerbosity(defaultVerbosity_);
323 }
324
328 template<std::invocable<const VolumeVariables&> VolVarFunction>
329 void addVolumeVariable(VolVarFunction&& f, const std::string& name)
330 {
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));
335 }));
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));
339 }));
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));
343 }));
344 else
345 {
346 static_assert(
347 Dune::AlwaysFalse<VolVarFunction>::value,
348 "Could not identify the given volume variable as scalar, vector or tensor."
349 );
350 }
351 }
352
357 template<Detail::Container C>
358 void addField(const C& values, const std::string& name)
359 { addField(values, name, GridFormat::Precision<GridFormat::MDRangeScalar<C>>{}); }
360
365 template<Detail::Container C, GridFormat::Concepts::Scalar T>
366 void addField(const C& values, const std::string& name, const GridFormat::Precision<T>& prec)
367 {
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.");
374
375 if (hasCellSize)
376 addCellField(values, name, prec);
377 else
378 addPointField(values, name, prec);
379 }
380
384 template<GridFormat::Concepts::PointFunction<GridView> DofFunction>
385 void addPointField(DofFunction&& f, const std::string& name)
386 { this->setPointField(name, std::forward<DofFunction>(f)); }
387
391 template<Detail::Container C>
392 void addPointField(const C& values, const std::string& name)
393 { addPointField(values, name, GridFormat::Precision<GridFormat::MDRangeScalar<C>>{}); }
394
398 template<Detail::Container C, GridFormat::Concepts::Scalar T>
399 void addPointField(const C& values, const std::string& name, const GridFormat::Precision<T>& prec)
400 {
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");
403
404 addPointField_(values, name, prec);
405 }
406
410 template<GridFormat::Concepts::CellFunction<GridView> DofFunction>
411 void addCellField(DofFunction&& f, const std::string& name)
412 { this->setCellField(name, std::forward<DofFunction>(f)); }
413
417 template<Detail::Container C>
418 void addCellField(const C& values, const std::string& name)
419 { addCellField(values, name, GridFormat::Precision<GridFormat::MDRangeScalar<C>>{}); }
420
424 template<Detail::Container C, GridFormat::Concepts::Scalar T>
425 void addCellField(const C& values, const std::string& name, const GridFormat::Precision<T>& prec)
426 {
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");
429
430 addCellField_(values, name, prec);
431 }
432
436 std::string write(const std::string& name)
437 {
438 Dune::Timer timer;
439
440 volVarFields_.updateFieldData(gridVariables_, solutionVector_);
441 auto filename = ParentType::write(name);
442 volVarFields_.clearFieldData();
443
444 timer.stop();
445 if (verbosity_ > 0)
446 std::cout << Fmt::format(
447 "Writing output to \"{}\". Took {:.2g} seconds.\n",
448 filename, timer.elapsed()
449 );
450
451 return filename;
452 }
453
457 template<std::floating_point T>
458 std::string write(T time)
459 {
460 Dune::Timer timer;
461
462 volVarFields_.updateFieldData(gridVariables_, solutionVector_);
463 auto filename = ParentType::write(time);
464 volVarFields_.clearFieldData();
465
466 timer.stop();
467 if (verbosity_ > 0)
468 std::cout << Fmt::format(
469 "Writing output to \"{}\". Took {:.2g} seconds.\n",
470 filename, timer.elapsed()
471 );
472
473 return filename;
474 }
475
477 void clear() {
478 ParentType::clear();
479 volVarFields_.clear();
480 }
481
482 void setVerbosity(int verbosity)
483 {
484 if (gridVariables_.gridGeometry().gridView().comm().rank() == 0)
485 verbosity_ = verbosity;
486 }
487
488private:
489 // reproduce behaviour of VtkOutputModule that flattens vectors of size 1
490 template<Detail::Container C, GridFormat::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)
493 {
494 this->setPointField(name, [&] (const auto& vertex) -> T {
495 return values[gridVariables_.gridGeometry().vertexMapper().index(vertex)][0];
496 }, prec);
497 }
498
499 template<Detail::Container C, GridFormat::Concepts::Scalar T>
500 void addPointField_(const C& values, const std::string& name, const GridFormat::Precision<T>& prec)
501 {
502 this->setPointField(name, [&] (const auto& vertex) -> std::ranges::range_value_t<C> {
503 return values[gridVariables_.gridGeometry().vertexMapper().index(vertex)];
504 }, prec);
505 }
506
507 // reproduce behaviour of VtkOutputModule that flattens vectors of size 1
508 template<Detail::Container C, GridFormat::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)
511 {
512 this->setCellField(name, [&] (const auto& element) -> T {
513 return values[gridVariables_.gridGeometry().elementMapper().index(element)][0];
514 }, prec);
515 }
516
517 template<Detail::Container C, GridFormat::Concepts::Scalar T>
518 void addCellField_(const C& values, const std::string& name, const GridFormat::Precision<T>& prec)
519 {
520 this->setCellField(name, [&] (const auto& element) -> std::ranges::range_value_t<C> {
521 return values[gridVariables_.gridGeometry().elementMapper().index(element)];
522 }, prec);
523 }
524
525 template<typename ResultType, typename Id>
526 void setVolVarField_(const std::string& name, Id&& volVarFieldId)
527 {
528 auto dofEntityField = [&, _id=std::move(volVarFieldId)] (const auto& entity) {
529 return volVarFields_.getValue(_id, gridVariables_.gridGeometry().dofMapper().index(entity));
530 };
531 if constexpr (isCVFE)
532 this->setPointField(name, std::move(dofEntityField), GridFormat::Precision<ResultType>{});
533 else
534 this->setCellField(name, std::move(dofEntityField), GridFormat::Precision<ResultType>{});
535 }
536
537 const GridVariables& gridVariables_;
538 const SolutionVector& solutionVector_;
539 VolVarFieldStorage volVarFields_;
540 int verbosity_ = 0;
541};
542
543// Class to store vol var fields; implementation detail of the OutputModule class
544template<typename GridVariables, typename SolutionVector>
545class OutputModule<GridVariables, SolutionVector>::VolVarFieldStorage
546{
547 enum class FieldType
548 { scalar, vector, tensor };
549
550 struct FieldInfo
551 {
552 std::string name;
553 FieldType type;
554 std::size_t index;
555 };
556
557 template<typename T>
558 struct FieldStorage
559 {
560 std::vector<T> data;
561 std::function<T(const VolVar&)> getter;
562 };
563
564public:
565 template<FieldType ft>
566 struct FieldId { std::size_t index; };
567
568 template<std::ranges::range R>
569 static constexpr auto toStorageVector(R&& in)
570 {
571 Vector result;
572 std::ranges::copy(in, result.begin());
573 return result;
574 }
575
576 template<GridFormat::Concepts::MDRange<2> R>
577 static constexpr auto toStorageTensor(R&& in)
578 {
579 Tensor result;
580 std::ranges::for_each(in, [&, i=0] (const auto& row) mutable {
581 std::ranges::copy(row, result[i++].begin());
582 });
583 return result;
584 }
585
586 template<FieldType ft>
587 const auto& getValue(const FieldId<ft>& id, std::size_t idx) const
588 {
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);
593 else
594 return tensorFieldStorage_.at(id.index).data.at(idx);
595 }
596
597 auto registerScalarField(std::string name, std::function<Scalar(const VolVar&)> f)
598 { return register_<FieldType::scalar>(std::move(name), scalarFieldStorage_, std::move(f)); }
599
600 auto registerVectorField(std::string name, std::function<Vector(const VolVar&)> f)
601 { return register_<FieldType::vector>(std::move(name), vectorFieldStorage_, std::move(f)); }
602
603 auto registerTensorField(std::string name, std::function<Tensor(const VolVar&)> f)
604 { return register_<FieldType::tensor>(std::move(name), tensorFieldStorage_, std::move(f)); }
605
606 void updateFieldData(const GridVariables& gridVars, const SolutionVector& x)
607 {
608 resizeFieldData_(gridVars.gridGeometry().numDofs());
609 const auto range = GridFormat::cells(gridVars.gridGeometry().gridView());
610 std::for_each(
611#if __cpp_lib_parallel_algorithm >= 201603L
612 std::execution::par_unseq,
613#endif
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))
620 {
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); }
625 }
626 });
627 }
628
629 void clearFieldData()
630 {
631 for (auto& s : scalarFieldStorage_) { s.data.clear(); }
632 for (auto& s : vectorFieldStorage_) { s.data.clear(); }
633 for (auto& s : tensorFieldStorage_) { s.data.clear(); }
634 }
635
636 void clear()
637 {
638 fields_.clear();
639 scalarFieldStorage_.clear();
640 vectorFieldStorage_.clear();
641 tensorFieldStorage_.clear();
642 }
643
644private:
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)
649 {
650 if (exists_<ft>(name))
651 DUNE_THROW(Dune::InvalidStateException, "Volume variables field '" << name << "' is already defined.");
652
653 FieldId<ft> id{storage.size()};
654 fields_.emplace_back(FieldInfo{std::move(name), ft, id.index});
655 storage.push_back({{}, std::move(f)});
656 return id;
657 }
658
659 template<FieldType ft>
660 bool exists_(const std::string& name) const
661 {
662 return std::ranges::any_of(fields_, [&] (const FieldInfo& info) {
663 return info.type == ft && info.name == name;
664 });
665 }
666
667 void resizeFieldData_(std::size_t size)
668 {
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); });
672 }
673
674 std::vector<FieldInfo> fields_;
675 std::vector<FieldStorage<Scalar>> scalarFieldStorage_;
676 std::vector<FieldStorage<Vector>> vectorFieldStorage_;
677 std::vector<FieldStorage<Tensor>> tensorFieldStorage_;
678};
679
680} // namespace Dumux::IO
681
682#else // DUMUX_HAVE_GRIDFORMAT
683
684namespace Dumux::IO {
685
686template<class... Args>
688{
689public:
690 template<class... _Args>
691 GridWriter(_Args&&...)
692 {
693 static_assert(
694 false,
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)."
698 );
699 }
700};
701
702template<class... Args>
704{
705public:
706 template<class... _Args>
707 OutputModule(_Args&&...)
708 {
709 static_assert(
710 false,
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)."
714 );
715 }
716};
717
718} // namespace Dumux::IO
719
720#endif // DUMUX_HAVE_GRIDFORMAT
721#endif // DUMUX_IO_GRID_HH
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...
Formatting based on the fmt-library which implements std::format of C++20.
GridCache::LocalView localView(const GridCache &gridCache)
Free function to get the local view of a grid cache object.
Definition localview.hh:26
FieldType
Identifier for vtk field types.
Definition fieldtype.hh:22
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.