version 3.11-dev
Loading...
Searching...
No Matches
porousmediumflow/2p/griddatatransfer.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
13#ifndef DUMUX_TWOP_GRIDDATA_TRANSFER_HH
14#define DUMUX_TWOP_GRIDDATA_TRANSFER_HH
15
16#include <memory>
17
18#include <dune/common/fvector.hh>
19#include <dune/grid/common/capabilities.hh>
20#include <dune/grid/common/partitionset.hh>
21#include <dune/grid/utility/persistentcontainer.hh>
22
24
32
33namespace Dumux {
34
39template<class TypeTag>
40class TwoPGridDataTransfer : public GridDataTransfer<GetPropType<TypeTag, Properties::Grid>>
41{
43 using ParentType = GridDataTransfer<Grid>;
47 using GridView = typename GridGeometry::GridView;
48 using FVElementGeometry = typename GridGeometry::LocalView;
49 using SubControlVolume = typename FVElementGeometry::SubControlVolume;
50 using Extrusion = Extrusion_t<GridGeometry>;
55 using Element = typename Grid::template Codim<0>::Entity;
56 using ElementSolution = std::decay_t<decltype(elementSolution(std::declval<Element>(),
57 std::declval<SolutionVector>(),
58 std::declval<GridGeometry>()))>;
61 using Indices = typename ModelTraits::Indices;
62
63 struct AdaptedValues
64 {
65 AdaptedValues() : associatedMass(0.0) {}
66 ElementSolution u;
67 int count = 0;
68 PrimaryVariables associatedMass;
69 bool wasLeaf = false;
70 };
71
72 using PersistentContainer = Dune::PersistentContainer<Grid, AdaptedValues>;
73
74 static constexpr int dim = Grid::dimension;
75 static constexpr int dimWorld = Grid::dimensionworld;
77
78 // saturation primary variable index
79 enum { saturationIdx = Indices::saturationIdx };
80
81 // phase indices
82 enum
83 {
84 phase0Idx = FluidSystem::phase0Idx,
85 phase1Idx = FluidSystem::phase1Idx,
86 };
87
88 // formulations
89 static constexpr auto p0s1 = TwoPFormulation::p0s1;
90 static constexpr auto p1s0 = TwoPFormulation::p1s0;
91
92 // the formulation that is actually used
93 static constexpr auto formulation = ModelTraits::priVarFormulation();
94
95 // This won't work (mass conservative) for compressible fluids
96 static_assert(!FluidSystem::isCompressible(phase0Idx)
97 && !FluidSystem::isCompressible(phase1Idx),
98 "This adaption helper is only mass conservative for incompressible fluids!");
99
100 // check if the used formulation is implemented here
101 static_assert(formulation == p0s1 || formulation == p1s0, "Chosen formulation not known to the TwoPGridDataTransfer");
102
103public:
112 TwoPGridDataTransfer(std::shared_ptr<const Problem> problem,
113 std::shared_ptr<GridGeometry> gridGeometry,
114 std::shared_ptr<const GridVariables> gridVariables,
115 SolutionVector& sol)
116 : ParentType()
117 , problem_(problem)
118 , gridGeometry_(gridGeometry)
119 , gridVariables_(gridVariables)
120 , sol_(sol)
121 , adaptionMap_(gridGeometry->gridView().grid(), 0)
122 {}
123
132 void store(const Grid& grid) override
133 {
134 adaptionMap_.resize();
135
136 for (auto level = grid.maxLevel(); level >= 0; level--)
137 {
138 auto fvGeometry = localView(*gridGeometry_);
139 for (const auto& element : elements(grid.levelGridView(level)))
140 {
141 // get map entry
142 auto& adaptedValues = adaptionMap_[element];
143
144 // put values in the map for leaf elements
145 if (element.isLeaf())
146 {
147 fvGeometry.bindElement(element);
148
149 // store current element solution
150 adaptedValues.u = elementSolution(element, sol_, *gridGeometry_);
151
152 // compute mass in the scvs
153 for (const auto& scv : scvs(fvGeometry))
154 {
155 VolumeVariables volVars;
156 volVars.update(adaptedValues.u, *problem_, element, scv);
157
158 const auto poreVolume = Extrusion::volume(fvGeometry, scv)*volVars.porosity();
159 adaptedValues.associatedMass[phase1Idx] += poreVolume * volVars.density(phase1Idx) * volVars.saturation(phase1Idx);
160 adaptedValues.associatedMass[phase0Idx] += poreVolume * volVars.density(phase0Idx) * volVars.saturation(phase0Idx);
161 }
162
163 // leaf elements always start with count = 1
164 adaptedValues.count = 1;
165 adaptedValues.wasLeaf = true;
166 }
167 // Average in father elements
168 if (element.level() > 0)
169 {
170 auto& adaptedValuesFather = adaptionMap_[element.father()];
171 // For some grids the father element is identical to the son element.
172 // In that case averaging is not necessary.
173 if(&adaptedValues != &adaptedValuesFather)
174 storeAdaptionValues(adaptedValues, adaptedValuesFather);
175 }
176
177 // The vertices of the non-leaf elements exist on the leaf as well
178 // This element solution constructor uses the vertex mapper to obtain
179 // the privars at the vertices, thus, this works for non-leaf elements!
180 if(isBox && !element.isLeaf())
181 adaptedValues.u = elementSolution(element, sol_, *gridGeometry_);
182 }
183 }
184 }
185
197 void reconstruct(const Grid& grid) override
198 {
199 gridGeometry_->update(grid.leafGridView());
200 reconstruct_();
201 }
202
203 private:
204
205 void reconstruct_()
206 {
207 // resize stuff (grid might have changed)
208 adaptionMap_.resize();
209 sol_.resize(gridGeometry_->numDofs());
210
211 // For the box method the mass is lumped to the vertices. For each vertex we store
212 // the associated mass (component 0) and the corresponding mass coefficient
213 // (component 1) in a single block, so that the partial contributions at process
214 // boundaries can be summed up in one communication below.
215 using VertexMass = Dune::FieldVector<Scalar, 2>;
216 std::vector<VertexMass> vertexMass;
217
218 if(isBox)
219 vertexMass.assign(gridGeometry_->numDofs(), VertexMass(0.0));
220
221 // iterate over the interior leaf elements and reconstruct the solution;
222 // data on ghost entities is obtained by communication further below
223 auto fvGeometry = localView(*gridGeometry_);
224 for (const auto& element : elements(gridGeometry_->gridView(), Dune::Partitions::interior))
225 {
226 if (!element.isNew())
227 {
228 const auto& adaptedValues = adaptionMap_[element];
229 fvGeometry.bindElement(element);
230
231 // obtain element solution from map (divide by count!)
232 auto elemSol = adaptedValues.u;
233 if (!isBox)
234 elemSol[0] /= adaptedValues.count;
235
236 const auto elementVolume = volume(element.geometry(), Extrusion{});
237 for (const auto& scv : scvs(fvGeometry))
238 {
239 VolumeVariables volVars;
240 volVars.update(elemSol, *problem_, element, scv);
241
242 // write solution at dof in current solution vector
243 sol_[scv.dofIndex()] = elemSol[scv.localDofIndex()];
244
245 const auto dofIdxGlobal = scv.dofIndex();
246 // For cc schemes, overwrite the saturation by a mass conservative one here
247 if (!isBox)
248 {
249 // only recalculate the saturations if element hasn't been leaf before adaptation
250 if (!adaptedValues.wasLeaf)
251 {
252 if (formulation == p0s1)
253 {
254 sol_[dofIdxGlobal][saturationIdx] = adaptedValues.associatedMass[phase1Idx];
255 sol_[dofIdxGlobal][saturationIdx] /= elementVolume * volVars.density(phase1Idx) * volVars.porosity();
256 }
257 else if (formulation == p1s0)
258 {
259 sol_[dofIdxGlobal][saturationIdx] = adaptedValues.associatedMass[phase0Idx];
260 sol_[dofIdxGlobal][saturationIdx] /= elementVolume * volVars.density(phase0Idx) * volVars.porosity();
261 }
262 }
263 }
264
265 // For the box scheme, add mass & mass coefficient to container (saturations are recalculated at the end)
266 else
267 {
268 const auto scvVolume = Extrusion::volume(fvGeometry, scv);
269 if (formulation == p0s1)
270 {
271 vertexMass[dofIdxGlobal][0] += scvVolume / elementVolume * adaptedValues.associatedMass[phase1Idx];
272 vertexMass[dofIdxGlobal][1] += scvVolume * volVars.density(phase1Idx) * volVars.porosity();
273 }
274 else if (formulation == p1s0)
275 {
276 vertexMass[dofIdxGlobal][0] += scvVolume / elementVolume * adaptedValues.associatedMass[phase0Idx];
277 vertexMass[dofIdxGlobal][1] += scvVolume * volVars.density(phase0Idx) * volVars.porosity();
278 }
279 }
280 }
281 }
282 else
283 {
284 // value is not in map, interpolate from father element
285 assert(element.hasFather() && "new element does not have a father element!");
286
287 // find the ancestor element that existed on the old grid already
288 auto fatherElement = element.father();
289 while (fatherElement.isNew() && fatherElement.level() > 0)
290 fatherElement = fatherElement.father();
291
292 if (!isBox)
293 {
294 const auto& adaptedValuesFather = adaptionMap_[fatherElement];
295
296 // obtain the mass contained in father
297 Scalar massFather = 0.0;
298 if (formulation == p0s1)
299 massFather = adaptedValuesFather.associatedMass[phase1Idx];
300 else if (formulation == p1s0)
301 massFather = adaptedValuesFather.associatedMass[phase0Idx];
302
303 // obtain the element solution through the father
304 auto elemSolSon = adaptedValuesFather.u;
305 elemSolSon[0] /= adaptedValuesFather.count;
306
307 fvGeometry.bindElement(element);
308
309 for (const auto& scv : scvs(fvGeometry))
310 {
311 VolumeVariables volVars;
312 volVars.update(elemSolSon, *problem_, element, scv);
313
314 // store constructed values of son in the current solution
315 sol_[scv.dofIndex()] = elemSolSon[0];
316
317 // overwrite the saturation by a mass conservative one here
318 Scalar massCoeffSon = 0.0;
319 if (formulation == p0s1)
320 massCoeffSon = Extrusion::volume(fvGeometry, scv) * volVars.density(phase1Idx) * volVars.porosity();
321 else if (formulation == p1s0)
322 massCoeffSon = Extrusion::volume(fvGeometry, scv) * volVars.density(phase0Idx) * volVars.porosity();
323 sol_[scv.dofIndex()][saturationIdx] =
324 ( Extrusion::volume(fvGeometry, scv)/volume(fatherElement.geometry(), Extrusion{})*massFather )/massCoeffSon;
325 }
326 }
327 else
328 {
329 auto& adaptedValuesFather = adaptionMap_[fatherElement];
330
331 fvGeometry.bindElement(element);
332
333 // interpolate solution in the father to the vertices of the new son
334 ElementSolution elemSolSon(element, sol_, *gridGeometry_);
335 const auto fatherGeometry = fatherElement.geometry();
336 for (const auto& scv : scvs(fvGeometry))
337 elemSolSon[scv.localDofIndex()] = evalSolution(
338 fatherElement, fatherGeometry, adaptedValuesFather.u, scv.dofPosition()
339 );
340
341 // compute mass & mass coefficients for the scvs (saturations are recalculated at the end)
342 const auto fatherElementVolume = volume(fatherGeometry, Extrusion{});
343 for (const auto& scv : scvs(fvGeometry))
344 {
345 VolumeVariables volVars;
346 volVars.update(elemSolSon, *problem_, element, scv);
347
348 const auto dofIdxGlobal = scv.dofIndex();
349 const auto scvVolume = Extrusion::volume(fvGeometry, scv);
350 if (formulation == p0s1)
351 {
352 vertexMass[dofIdxGlobal][0] += scvVolume / fatherElementVolume * adaptedValuesFather.associatedMass[phase1Idx];
353 vertexMass[dofIdxGlobal][1] += scvVolume * volVars.density(phase1Idx) * volVars.porosity();
354 }
355 else if (formulation == p1s0)
356 {
357 vertexMass[dofIdxGlobal][0] += scvVolume / fatherElementVolume * adaptedValuesFather.associatedMass[phase0Idx];
358 vertexMass[dofIdxGlobal][1] += scvVolume * volVars.density(phase0Idx) * volVars.porosity();
359 }
360
361 // store constructed (pressure) values of son in the current solution (saturation comes later)
362 sol_[dofIdxGlobal] = elemSolSon[scv.localDofIndex()];
363 }
364 }
365 }
366 }
367
368 if(isBox)
369 {
370#if HAVE_MPI
371 // At process-boundary vertices each process only accumulated the mass of its
372 // interior elements. Sum up the partial contributions (associated mass and mass
373 // coefficient together) to obtain the total mass lumped to the vertex before dividing.
374 if constexpr (Dune::Capabilities::canCommunicate<Grid, dim>::v)
375 {
376 const auto& gridView = gridGeometry_->gridView();
377 if (gridView.comm().size() > 1)
378 {
379 using VertexMapper = std::decay_t<decltype(gridGeometry_->vertexMapper())>;
381 gridGeometry_->vertexMapper(), vertexMass
382 );
383 gridView.communicate(
384 massHandle, Dune::InteriorBorder_InteriorBorder_Interface, Dune::ForwardCommunication
385 );
386 }
387 }
388#endif
389 // ghost vertices received no interior contribution (mass coefficient == 0); their
390 // values are obtained by communication of the solution below
391 for(std::size_t dofIdxGlobal = 0; dofIdxGlobal < gridGeometry_->numDofs(); dofIdxGlobal++)
392 if (vertexMass[dofIdxGlobal][1] > 0.0)
393 sol_[dofIdxGlobal][saturationIdx] = vertexMass[dofIdxGlobal][0] / vertexMass[dofIdxGlobal][1];
394 }
395
396#if HAVE_MPI
397 // The reconstruction above only produces valid data on interior/border entities.
398 // Overwrite the values on ghost entities with those of their owner so that fluxes
399 // across the process boundary are evaluated with consistent neighbor values.
400 static constexpr int dofCodim = isBox ? dim : 0;
401 if constexpr (Dune::Capabilities::canCommunicate<Grid, dofCodim>::v)
402 {
403 const auto& gridView = gridGeometry_->gridView();
404 if (gridView.comm().size() > 1)
405 {
406 using DofMapper = std::decay_t<decltype(gridGeometry_->dofMapper())>;
408 gridGeometry_->dofMapper(), sol_
409 );
410 gridView.communicate(
411 dataHandle, Dune::InteriorBorder_All_Interface, Dune::ForwardCommunication
412 );
413 }
414 }
415#endif
416
417 // reset entries in adaptation map
418 adaptionMap_.resize( typename PersistentContainer::Value() );
419 adaptionMap_.shrinkToFit();
420 adaptionMap_.fill( typename PersistentContainer::Value() );
421 }
422
433 static void storeAdaptionValues(AdaptedValues& adaptedValues,
434 AdaptedValues& adaptedValuesFather)
435 {
436 // Add associated mass of the child to the one of the father
437 adaptedValuesFather.associatedMass += adaptedValues.associatedMass;
438
439 if(!isBox)
440 {
441 // add the child's primary variables to the ones of father
442 // we have to divide the child's ones in case it was composed
443 // of several children as well!
444 auto values = adaptedValues.u[0];
445 values /= adaptedValues.count;
446 adaptedValuesFather.u[0] += values;
447
448 // keep track of the number of children that composed this father
449 adaptedValuesFather.count += 1;
450
451 // A father element is never leaf
452 adaptedValuesFather.wasLeaf = false;
453 }
454 else
455 {
456 // For the box scheme, scaling of primary variables by count is obsolete
457 // Thus, we always want count = 1
458 adaptedValuesFather.count = 1;
459
460 // A father element is never leaf
461 adaptedValuesFather.wasLeaf = false;
462 }
463 }
464
465 std::shared_ptr<const Problem> problem_;
466 std::shared_ptr<GridGeometry> gridGeometry_;
467 std::shared_ptr<const GridVariables> gridVariables_;
468 SolutionVector& sol_;
469 PersistentContainer adaptionMap_;
470};
471
472} // end namespace Dumux
473
474#endif
Interface to be used by classes transferring grid data on adaptive grids.
Traits extracting the public Extrusion type from T Defaults to NoExtrusion if no such type is found.
Definition extrusion.hh:246
Interface to be used by classes transferring grid data on adaptive grids.
Definition adaptive/griddatatransfer.hh:23
void store(const Grid &grid) override
Stores primary variables and additional data.
Definition porousmediumflow/2p/griddatatransfer.hh:132
TwoPGridDataTransfer(std::shared_ptr< const Problem > problem, std::shared_ptr< GridGeometry > gridGeometry, std::shared_ptr< const GridVariables > gridVariables, SolutionVector &sol)
Constructor.
Definition porousmediumflow/2p/griddatatransfer.hh:112
void reconstruct(const Grid &grid) override
Reconstruct missing primary variables (where elements are created/deleted).
Definition porousmediumflow/2p/griddatatransfer.hh:197
Defines all properties used in Dumux.
Element solution classes and factory functions.
Helper classes to compute the integration elements.
Defines an enumeration for the formulations accepted by the two-phase model.
GridCache::LocalView localView(const GridCache &gridCache)
Free function to get the local view of a grid cache object.
Definition localview.hh:26
PrimaryVariables evalSolution(const Element &element, const typename Element::Geometry &geometry, const typename FVElementGeometry::GridGeometry &gridGeometry, const CVFEElementSolution< FVElementGeometry, PrimaryVariables > &elemSol, const typename Element::Geometry::GlobalCoordinate &globalPos, bool ignoreState=false)
Interpolates a given box element solution at a given global position. Uses the finite element cache o...
Definition evalsolution.hh:197
auto elementSolution(const Element &element, const SolutionVector &sol, const GridGeometry &gg) -> std::enable_if_t< GridGeometry::discMethod==DiscretizationMethods::cctpfa||GridGeometry::discMethod==DiscretizationMethods::ccmpfa, CCElementSolution< typename GridGeometry::LocalView, std::decay_t< decltype(std::declval< SolutionVector >()[0])> > >
Make an element solution for cell-centered schemes.
Definition cellcentered/elementsolution.hh:101
auto volume(const Geometry &geo, unsigned int integrationOrder=4)
The volume of a given geometry.
Definition volume.hh:159
@ element
Definition fieldtype.hh:23
typename GetProp< TypeTag, Property >::type GetPropType
get the type alias defined in the property
Definition propertysystem.hh:296
@ p1s0
first phase saturation and second phase pressure as primary variables
Definition formulation.hh:25
@ p0s1
first phase pressure and second phase saturation as primary variables
Definition formulation.hh:24
The available discretization methods in Dumux.
constexpr Box box
Definition method.hh:176
Definition adapt.hh:17
VectorCommDataHandle< Mapper, Vector, codim, Detail::Sum, DataType > VectorCommDataHandleSum
Definition vectorcommdatahandle.hh:214
VectorCommDataHandle< Mapper, Vector, codim, Detail::SetEqual, DataType > VectorCommDataHandleEqual
Definition vectorcommdatahandle.hh:211
const Scalar PengRobinsonMixture< Scalar, StaticParameters >::u
Definition pengrobinsonmixture.hh:138
std::ranges::range auto scvs(const FVElementGeometry &fvGeometry, const LocalDof &localDof)
Definition localdof.hh:82
typename Extrusion< T >::type Extrusion_t
Convenience alias for obtaining the extrusion type.
Definition extrusion.hh:257
Contains a class to exchange entries of a vector.
Compute the volume of several common geometry types.