3.4
DUNE for Multi-{Phase, Component, Scale, Physics, ...} flow and transport in porous media
lmethod/2dpressure.hh
Go to the documentation of this file.
1/*****************************************************************************
2 * See the file COPYING for full copying permissions. *
3 * *
4 * This program is free software: you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation, either version 3 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
16 *****************************************************************************/
22#ifndef DUMUX_FVMPFAL2DPRESSURE2P_HH
23#define DUMUX_FVMPFAL2DPRESSURE2P_HH
24
25// dumux environment
31
32namespace Dumux {
33
70template<class TypeTag>
71class FvMpfaL2dPressure2p: public FVPressure<TypeTag>
72{
75
76 enum
77 {
78 dim = GridView::dimension, dimWorld = GridView::dimensionworld
79 };
80
83
85
87
90
92
95
96 using PrimaryVariables = typename SolutionTypes::PrimaryVariables;
97 using ScalarSolutionType = typename SolutionTypes::ScalarSolution;
98
100
101 enum
102 {
103 pw = Indices::pressureW,
104 pn = Indices::pressureNw,
105 sw = Indices::saturationW,
106 sn = Indices::saturationNw
107 };
108 enum
109 {
110 wPhaseIdx = Indices::wPhaseIdx,
111 nPhaseIdx = Indices::nPhaseIdx,
112 pressureIdx = Indices::pressureIdx,
113 saturationIdx = Indices::saturationIdx,
114 pressEqIdx = Indices::pressureEqIdx,
115 satEqIdx = Indices::satEqIdx,
116 numPhases = getPropValue<TypeTag, Properties::NumPhases>()
117 };
118 enum
119 {
120 globalCorner = 2,
121 globalEdge = 3,
122 neumannNeumann = 0,
123 dirichletDirichlet = 1,
124 dirichletNeumann = 2,
125 neumannDirichlet = 3
126 };
127
128 using Element = typename GridView::Traits::template Codim<0>::Entity;
129 using IntersectionIterator = typename GridView::IntersectionIterator;
130 using Intersection = typename GridView::Intersection;
131
132 using LocalPosition = Dune::FieldVector<Scalar, dim>;
133 using GravityVector = Dune::FieldVector<Scalar, dimWorld>;
134
135 using GlobalPosition = typename Element::Geometry::GlobalCoordinate;
136
137 using DimMatrix = Dune::FieldMatrix<Scalar, dim, dim>;
138
139 using DimVector = Dune::FieldVector<Scalar, dim>;
140
141public:
150private:
151
152 using GlobalInteractionVolumeVector = std::vector<InteractionVolume>;
153 using InnerBoundaryVolumeFaces = std::vector<Dune::FieldVector<bool, 2*dim> >;
154
156 Intersection getNextIntersection_(const Element&, const IntersectionIterator&);
157
159 friend class FVPressure<TypeTag>;
160 void initializeMatrix();
161
162 void storeInteractionVolumeInfo();
163
165 void assemble();
166
167public:
168
170 void updateMaterialLaws();
171
178 {
179 interactionVolumes_.clear();
181
182 interactionVolumes_.resize(problem_.gridView().size(dim), InteractionVolume(problem_.gridView().grid()));
183 innerBoundaryVolumeFaces_.resize(problem_.gridView().size(0), Dune::FieldVector<bool, 2 * dim>(false));
184
185 storeInteractionVolumeInfo();
186 }
187
192 {
194
195 const auto element = *problem_.gridView().template begin<0>();
196 FluidState fluidState;
197 fluidState.setPressure(wPhaseIdx, problem_.referencePressure(element));
198 fluidState.setPressure(nPhaseIdx, problem_.referencePressure(element));
199 fluidState.setTemperature(problem_.temperature(element));
200 fluidState.setSaturation(wPhaseIdx, 1.);
201 fluidState.setSaturation(nPhaseIdx, 0.);
202 density_[wPhaseIdx] = FluidSystem::density(fluidState, wPhaseIdx);
203 density_[nPhaseIdx] = FluidSystem::density(fluidState, nPhaseIdx);
204 viscosity_[wPhaseIdx] = FluidSystem::viscosity(fluidState, wPhaseIdx);
205 viscosity_[nPhaseIdx] = FluidSystem::viscosity(fluidState, nPhaseIdx);
206
208
209 interactionVolumes_.resize(problem_.gridView().size(dim), InteractionVolume(problem_.gridView().grid()));
210 innerBoundaryVolumeFaces_.resize(problem_.gridView().size(0), Dune::FieldVector<bool, 2 * dim>(false));
211
212 storeInteractionVolumeInfo();
213
214 assemble();
215 this->solve();
216
218
219 return;
220 }
221
226 {
227 for (const auto& element : elements(problem_.gridView()))
228 {
229 storePressureSolution(element);
230 }
231 }
232
238 void storePressureSolution(const Element& element)
239 {
240 int eIdxGlobal = problem_.variables().index(element);
241 CellData& cellData = problem_.variables().cellData(eIdxGlobal);
242
243 switch (pressureType_)
244 {
245 case pw:
246 {
247 Scalar potW = this->pressure()[eIdxGlobal];
248
249 Scalar gravityDiff = (problem_.bBoxMax() - element.geometry().center()) * gravity_;
250 Scalar potPc = cellData.capillaryPressure()
251 + gravityDiff * (density_[nPhaseIdx] - density_[wPhaseIdx]);
252
253 cellData.setPotential(wPhaseIdx, potW);
254 cellData.setPotential(nPhaseIdx, potW + potPc);
255
256 Scalar pressW = potW - gravityDiff * density_[wPhaseIdx];
257
258 cellData.setPressure(wPhaseIdx, pressW);
259 cellData.setPressure(nPhaseIdx, pressW + cellData.capillaryPressure());
260
261 break;
262 }
263 case pn:
264 {
265 Scalar potNw = this->pressure()[eIdxGlobal];
266
267 Scalar gravityDiff = (problem_.bBoxMax() - element.geometry().center()) * gravity_;
268 Scalar potPc = cellData.capillaryPressure()
269 + gravityDiff * (density_[nPhaseIdx] - density_[wPhaseIdx]);
270
271 cellData.setPotential(nPhaseIdx, potNw);
272 cellData.setPotential(wPhaseIdx, potNw - potPc);
273
274 Scalar pressNw = potNw - gravityDiff * density_[nPhaseIdx];
275
276 cellData.setPressure(wPhaseIdx, pressNw - cellData.capillaryPressure());
277 cellData.setPressure(nPhaseIdx, pressNw);
278
279 break;
280 }
281 }
282 cellData.fluxData().resetVelocity();
283 }
284
288 void update()
289 {
290 //error bounds for error term for incompressible models to correct unphysical saturation
291 //over/undershoots due to saturation transport
292 timeStep_ = problem_.timeManager().timeStepSize();
293 maxError_ = 0.0;
294 int size = problem_.gridView().size(0);
295 for (int i = 0; i < size; i++)
296 {
297 Scalar sat = 0;
298 using std::max;
299 switch (saturationType_)
300 {
301 case sw:
302 sat = problem_.variables().cellData(i).saturation(wPhaseIdx);
303 break;
304 case sn:
305 sat = problem_.variables().cellData(i).saturation(nPhaseIdx);
306 break;
307 }
308 if (sat > 1.0)
309 {
310 maxError_ = max(maxError_, (sat - 1.0) / timeStep_);
311 }
312 if (sat < 0.0)
313 {
314 maxError_ = max(maxError_, (-sat) / timeStep_);
315 }
316 }
317
318 assemble();
319 this->solve();
320
322
323 return;
324 }
325
336 template<class MultiWriter>
337 void addOutputVtkFields(MultiWriter &writer)
338 {
339 int size = problem_.gridView().size(0);
340 ScalarSolutionType *potential = writer.allocateManagedBuffer(size);
341
342 (*potential) = this->pressure();
343
344 if (pressureType_ == pw)
345 {
346 writer.attachCellData(*potential, "wetting potential");
347 }
348
349 if (pressureType_ == pn)
350 {
351 writer.attachCellData(*potential, "nonwetting potential");
352 }
353
354 if (vtkOutputLevel_ > 0)
355 {
356 ScalarSolutionType *pressure = writer.allocateManagedBuffer(size);
357 ScalarSolutionType *pressureSecond = writer.allocateManagedBuffer(size);
358 ScalarSolutionType *potentialSecond = writer.allocateManagedBuffer(size);
359 ScalarSolutionType *pc = writer.allocateManagedBuffer(size);
360
361 for (const auto& element : elements(problem_.gridView()))
362 {
363 int idx = problem_.variables().index(element);
364 CellData& cellData = problem_.variables().cellData(idx);
365
366 (*pc)[idx] = cellData.capillaryPressure();
367
368 if (pressureType_ == pw)
369 {
370 (*pressure)[idx] = cellData.pressure(wPhaseIdx);
371 (*potentialSecond)[idx] = cellData.potential(nPhaseIdx);
372 (*pressureSecond)[idx] = cellData.pressure(nPhaseIdx);
373 }
374
375 if (pressureType_ == pn)
376 {
377 (*pressure)[idx] = cellData.pressure(nPhaseIdx);
378 (*potentialSecond)[idx] = cellData.potential(wPhaseIdx);
379 (*pressureSecond)[idx] = cellData.pressure(wPhaseIdx);
380 }
381 }
382
383 if (pressureType_ == pw)
384 {
385 writer.attachCellData(*pressure, "wetting pressure");
386 writer.attachCellData(*pressureSecond, "nonwetting pressure");
387 writer.attachCellData(*potentialSecond, "nonwetting potential");
388 }
389
390 if (pressureType_ == pn)
391 {
392 writer.attachCellData(*pressure, "nonwetting pressure");
393 writer.attachCellData(*pressureSecond, "wetting pressure");
394 writer.attachCellData(*potentialSecond, "wetting potential");
395 }
396
397 writer.attachCellData(*pc, "capillary pressure");
398 }
399
400 return;
401 }
402
408 FvMpfaL2dPressure2p(Problem& problem) :
409 ParentType(problem), problem_(problem), transmissibilityCalculator_(problem),
410 gravity_(problem.gravity()),
411 maxError_(0.), timeStep_(1.)
412 {
413 if (pressureType_ != pw && pressureType_ != pn)
414 {
415 DUNE_THROW(Dune::NotImplemented, "Pressure type not supported!");
416 }
417 if (saturationType_ != sw && saturationType_ != sn)
418 {
419 DUNE_THROW(Dune::NotImplemented, "Saturation type not supported!");
420 }
421 if (getPropValue<TypeTag, Properties::EnableCompressibility>())
422 {
423 DUNE_THROW(Dune::NotImplemented, "Compressibility not supported!");
424 }
425 if (dim != 2)
426 {
427 DUNE_THROW(Dune::NotImplemented, "Dimension not supported!");
428 }
429
430 ErrorTermFactor_ = getParam<Scalar>("Impet.ErrorTermFactor");
431 ErrorTermLowerBound_ = getParam<Scalar>("Impet.ErrorTermLowerBound");
432 ErrorTermUpperBound_ = getParam<Scalar>("Impet.ErrorTermUpperBound");
433
434 density_[wPhaseIdx] = 0.;
435 density_[nPhaseIdx] = 0.;
436 viscosity_[wPhaseIdx] = 0.;
437 viscosity_[nPhaseIdx] = 0.;
438
439 vtkOutputLevel_ = getParam<int>("Vtk.OutputLevel");
440 }
441
442private:
443 Problem& problem_;
444 TransmissibilityCalculator transmissibilityCalculator_;
445
446protected:
447
448 GlobalInteractionVolumeVector interactionVolumes_;
449 InnerBoundaryVolumeFaces innerBoundaryVolumeFaces_;
450
451private:
452 const GravityVector& gravity_;
453
454 Scalar maxError_;
455 Scalar timeStep_;
456 Scalar ErrorTermFactor_;
457 Scalar ErrorTermLowerBound_;
458 Scalar ErrorTermUpperBound_;
459
460 Scalar density_[numPhases];
461 Scalar viscosity_[numPhases];
462
463 int vtkOutputLevel_;
464
465 static constexpr Scalar threshold_ = 1e-15;
467 static const int pressureType_ = getPropValue<TypeTag, Properties::PressureFormulation>();
469 static const int saturationType_ = getPropValue<TypeTag, Properties::SaturationFormulation>();
471 static const int velocityType_ = getPropValue<TypeTag, Properties::VelocityFormulation>();
472
492 Scalar evaluateErrorTerm_(CellData& cellData)
493 {
494 //error term for incompressible models to correct unphysical saturation over/undershoots due to saturation transport
495 // error reduction routine: volumetric error is damped and inserted to right hand side
496 Scalar sat = 0;
497 switch (saturationType_)
498 {
499 case sw:
500 sat = cellData.saturation(wPhaseIdx);
501 break;
502 case sn:
503 sat = cellData.saturation(nPhaseIdx);
504 break;
505 }
506
507 Scalar error = (sat > 1.0) ? sat - 1.0 : 0.0;
508 if (sat < 0.0)
509 {
510 error = sat;
511 }
512 error /= timeStep_;
513
514 using std::abs;
515 Scalar errorAbs = abs(error);
516
517 if ((errorAbs * timeStep_ > 1e-6) && (errorAbs > ErrorTermLowerBound_ * maxError_)
518 && (!problem_.timeManager().willBeFinished()))
519 {
520 return ErrorTermFactor_ * error;
521 }
522 return 0.0;
523 }
524
525};
526
527// TODO doc me!
528template<class TypeTag>
529typename FvMpfaL2dPressure2p<TypeTag>::Intersection
530 FvMpfaL2dPressure2p<TypeTag>::getNextIntersection_(const Element& element,
531 const IntersectionIterator& isIt)
532{
533 auto isItBegin = problem_.gridView().ibegin(element);
534 const auto isEndIt = problem_.gridView().iend(element);
535
536 auto tempIsIt = isIt;
537 auto nextIsIt = ++tempIsIt;
538
539 // get 'nextIsIt'
540 switch (getPropValue<TypeTag, Properties::GridImplementation>())
541 {
542 // for YaspGrid
543 case GridTypeIndices::yaspGrid:
544 {
545 if (nextIsIt == isEndIt)
546 {
547 nextIsIt = isItBegin;
548 }
549 else
550 {
551 nextIsIt = ++tempIsIt;
552
553 if (nextIsIt == isEndIt)
554 {
555 auto tempIsItBegin = isItBegin;
556 nextIsIt = ++tempIsItBegin;
557 }
558 }
559
560 break;
561 }
562 // for ALUGrid and UGGrid
563 case GridTypeIndices::aluGrid:
564 case GridTypeIndices::ugGrid:
565 {
566 if (nextIsIt == isEndIt)
567 nextIsIt = isItBegin;
568
569 break;
570 }
571 default:
572 {
573 DUNE_THROW(Dune::NotImplemented, "GridType can not be used with MPFAO implementation!");
574 break;
575 }
576 }
577
578 return *nextIsIt;
579}
580
581// TODO doc me!
582template<class TypeTag>
583void FvMpfaL2dPressure2p<TypeTag>::initializeMatrix()
584{
585 // determine matrix row sizes
586 for (const auto& element : elements(problem_.gridView()))
587 {
588 // cell index
589 int eIdxGlobalI = problem_.variables().index(element);
590
591 // initialize row size
592 int rowSize = 1;
593
594 // run through all intersections with neighbors
595 const auto isEndIt = problem_.gridView().iend(element);
596 for (auto isIt = problem_.gridView().ibegin(element); isIt != isEndIt; ++isIt)
597 {
598 const auto& intersection = *isIt;
599 auto nextIntersection = getNextIntersection_(element, isIt);
600
601 if (intersection.neighbor())
602 rowSize++;
603
604 // try to find the common neighbor of intersection's and nextIntersection's outside
605 if (intersection.neighbor() && nextIntersection.neighbor())
606 {
607 for (const auto& innerIntersection
608 : intersections(problem_.gridView(), intersection.outside()))
609 for (const auto& innerNextIntersection
610 : intersections(problem_.gridView(), nextIntersection.outside()))
611 {
612 if (innerIntersection.neighbor() && innerNextIntersection.neighbor())
613 {
614 if (innerIntersection.outside() == innerNextIntersection.outside()
615 && innerIntersection.outside() != intersection.inside())
616 {
617 rowSize++;
618 }
619 }
620 }
621 }
622 } // end of intersection loop
623
624 // set number of indices in row eIdxGlobalI to rowSize
625 this->A_.setrowsize(eIdxGlobalI, rowSize);
626
627 } // end of element loop
628
629 // indicate that size of all rows is defined
630 this->A_.endrowsizes();
631
632 // determine position of matrix entries
633 for (const auto& element : elements(problem_.gridView()))
634 {
635 // cell index
636 int eIdxGlobalI = problem_.variables().index(element);
637
638 // add diagonal index
639 this->A_.addindex(eIdxGlobalI, eIdxGlobalI);
640
641 // run through all intersections with neighbors
642 const auto isEndIt = problem_.gridView().iend(element);
643 for (auto isIt = problem_.gridView().ibegin(element); isIt != isEndIt; ++isIt)
644 {
645 const auto& intersection = *isIt;
646 auto nextIntersection = getNextIntersection_(element, isIt);
647
648 if (intersection.neighbor())
649 {
650 // access neighbor
651 int eIdxGlobalJ = problem_.variables().index(intersection.outside());
652
653 // add off diagonal index
654 // add index (row,col) to the matrix
655 this->A_.addindex(eIdxGlobalI, eIdxGlobalJ);
656 }
657
658 if (intersection.neighbor() && nextIntersection.neighbor())
659 {
660 for (const auto& innerIntersection
661 : intersections(problem_.gridView(), intersection.outside()))
662 for (const auto& innerNextIntersection
663 : intersections(problem_.gridView(), nextIntersection.outside()))
664 {
665 if (innerIntersection.neighbor() && innerNextIntersection.neighbor())
666 {
667 auto innerOutside = innerIntersection.outside();
668
669 if (innerOutside == innerNextIntersection.outside()
670 && innerOutside != intersection.inside())
671 {
672 int eIdxGlobalJ = problem_.variables().index(innerOutside);
673
674 this->A_.addindex(eIdxGlobalI, eIdxGlobalJ);
675 }
676 }
677 }
678 }
679 } // end of intersection loop
680 } // end of element loop
681
682 // indicate that all indices are defined, check consistency
683 this->A_.endindices();
684
685 return;
686}
687// Indices used in a interaction volume of the MPFA-o method
688// ___________________________________________________
689// | | |
690// | nuxy: cell geometry | nxy: face normal |
691// | vectors (see MPFA) | |
692// | | |
693// | 4-----------3-----------3 |
694// | | --> nu43 | nu34 <-- | |
695// | | |nu41 1|--> n43 ||nu32 |
696// | | v ^ |0 ^ v| |
697// |____________4__0__|n14__|__n23_|_1__2____________|
698// | | 1 0 | 0 |
699// | | ^ |1 nu23 ^ | |
700// | | |nu14 0|--> n12 | | |
701// | | -->nu12 | nu21<-- | |
702// | 1-----------1-----------2 |
703// | elementnumber |inter- |
704// | |face- |
705// | |number |
706// |________________________|________________________|
707
708// only for 2-D general quadrilateral
709// TODO doc me!
710template<class TypeTag>
711void FvMpfaL2dPressure2p<TypeTag>::storeInteractionVolumeInfo()
712{
713 BoundaryTypes bcType;
714
715 // run through all elements
716 for (const auto& element : elements(problem_.gridView()))
717 {
718 // get common geometry information for the following computation
719
720 int eIdxGlobal1 = problem_.variables().index(element);
721
722 const auto isIt12End = problem_.gridView().iend(element);
723 for (auto isIt12 = problem_.gridView().ibegin(element); isIt12 != isIt12End; ++isIt12)
724 {
725 const auto& intersection12 = *isIt12;
726 auto intersection14 = getNextIntersection_(element, isIt12);
727
728 int indexInInside12 = intersection12.indexInInside();
729 int indexInInside14 = intersection14.indexInInside();
730
731 // get the intersection node /bar^{x_3} between 'intersection12'
732 // and 'intersection14', denoted as 'corner1234'
733 const auto refElement = referenceElement(element);
734
735 GlobalPosition corner1234(0);
736
737 int globalVertIdx1234 = 0;
738
739 // get the global coordinate and global vertex index of corner1234
740 for (int i = 0; i < intersection12.geometry().corners(); ++i)
741 {
742 bool finished = false;
743
744 int localVertIdx12corner = refElement.subEntity(indexInInside12, 1, i, dim);
745
746 int globalVertIdx12corner = problem_.variables().vertexMapper().subIndex(element, localVertIdx12corner, dim);
747
748 for (int j = 0; j < intersection14.geometry().corners(); ++j)
749 {
750 int localVertIdx14corner = refElement.subEntity(indexInInside14, 1, j, dim);
751
752 int globalVertIdx14corner = problem_.variables().vertexMapper().subIndex(element, localVertIdx14corner, dim);
753
754 if (globalVertIdx12corner == globalVertIdx14corner)
755 {
756 corner1234 = element.geometry().corner(localVertIdx12corner);
757
758 globalVertIdx1234 = globalVertIdx12corner;
759
760 finished = true;
761 break;
762 }
763 }
764
765 if (finished)
766 {
767 break;
768 }
769 }
770
771 if (interactionVolumes_[globalVertIdx1234].isStored())
772 {
773 continue;
774 }
775 else
776 {
777 interactionVolumes_[globalVertIdx1234].setStored();
778 }
779
780 interactionVolumes_[globalVertIdx1234].setCenterPosition(corner1234);
781
782 //store pointer 1
783 interactionVolumes_[globalVertIdx1234].setSubVolumeElement(element, 0);
784 interactionVolumes_[globalVertIdx1234].setIndexOnElement(intersection12.indexInInside(), 0, 0);
785 interactionVolumes_[globalVertIdx1234].setIndexOnElement(intersection14.indexInInside(), 0, 1);
786
787 // center of face in global coordinates, i.e., the midpoint of edge 'intersection12'
788 const GlobalPosition& globalPosFace12 = intersection12.geometry().center();
789
790 // get face volume
791 Scalar faceVol12 = intersection12.geometry().volume() / 2.0;
792
793 // get outer normal vector scaled with half volume of face 'intersection12'
794 DimVector unitOuterNormal12 = intersection12.centerUnitOuterNormal();
795
796 // center of face in global coordinates, i.e., the midpoint of edge 'intersection14'
797 const GlobalPosition& globalPosFace41 = intersection14.geometry().center();
798
799 // get face volume
800 Scalar faceVol41 = intersection14.geometry().volume() / 2.0;
801
802 // get outer normal vector scaled with half volume of face 'intersection14': for numbering of n see Aavatsmark, Eigestad
803 DimVector unitOuterNormal14 = intersection14.centerUnitOuterNormal();
804
805 interactionVolumes_[globalVertIdx1234].setNormal(unitOuterNormal12, 0, 0);
806 interactionVolumes_[globalVertIdx1234].setNormal(unitOuterNormal14, 0, 1);
807 //get the normals of from cell 2 and 4
808 unitOuterNormal14 *= -1;
809 unitOuterNormal12 *= -1;
810 interactionVolumes_[globalVertIdx1234].setFaceArea(faceVol12, 0, 0);
811 interactionVolumes_[globalVertIdx1234].setFaceArea(faceVol41, 0, 1);
812 interactionVolumes_[globalVertIdx1234].setFacePosition(globalPosFace12, 0, 0);
813 interactionVolumes_[globalVertIdx1234].setFacePosition(globalPosFace41, 0, 1);
814
815 // handle interior face
816 if (intersection12.neighbor())
817 {
818 // access neighbor cell 2 of 'intersection12'
819 auto element2 = intersection12.outside();
820
821 int eIdxGlobal2 = problem_.variables().index(element2);
822
823 //store pointer 2
824 interactionVolumes_[globalVertIdx1234].setSubVolumeElement(element2, 1);
825 interactionVolumes_[globalVertIdx1234].setIndexOnElement(intersection12.indexInOutside(), 1, 1);
826 interactionVolumes_[globalVertIdx1234].setNormal(unitOuterNormal12, 1, 1);
827 interactionVolumes_[globalVertIdx1234].setFaceArea(faceVol12, 1, 1);
828 interactionVolumes_[globalVertIdx1234].setFacePosition(globalPosFace12, 1, 1);
829
830 // 'intersection14' is an interior face
831 if (intersection14.neighbor())
832 {
833 // neighbor cell 3
834 // access neighbor cell 3
835 auto element4 = intersection14.outside();
836
837 //store pointer 4
838 interactionVolumes_[globalVertIdx1234].setSubVolumeElement(element4, 3);
839 interactionVolumes_[globalVertIdx1234].setIndexOnElement(intersection14.indexInOutside(), 3, 0);
840
841 interactionVolumes_[globalVertIdx1234].setNormal(unitOuterNormal14, 3, 0);
842 interactionVolumes_[globalVertIdx1234].setFaceArea(faceVol41, 3, 0);
843 interactionVolumes_[globalVertIdx1234].setFacePosition(globalPosFace41, 3, 0);
844
845 // cell 3
846 GlobalPosition globalPos3(0);
847
848 GlobalPosition globalPosFace23(0);
849 GlobalPosition globalPosFace34(0);
850
851 for (const auto& intersection23
852 : intersections(problem_.gridView(), element2))
853 {
854 bool finished = false;
855
856 for (const auto& intersection43
857 : intersections(problem_.gridView(), element4))
858 {
859 if (intersection23.neighbor() && intersection43.neighbor())
860 {
861 auto element32 = intersection23.outside();
862 auto element34 = intersection43.outside();
863
864 // find the common neighbor cell between cell 2 and cell 3, except cell 1
865 if (element32 == element34 && element32 != element)
866 {
867 //store pointer 3
868 interactionVolumes_[globalVertIdx1234].setSubVolumeElement(element32, 2);
869
870 interactionVolumes_[globalVertIdx1234].setIndexOnElement(intersection23.indexInInside(), 1,
871 0);
872 interactionVolumes_[globalVertIdx1234].setIndexOnElement(intersection23.indexInOutside(), 2,
873 1);
874 interactionVolumes_[globalVertIdx1234].setIndexOnElement(intersection43.indexInInside(), 3,
875 1);
876 interactionVolumes_[globalVertIdx1234].setIndexOnElement(intersection43.indexInOutside(), 2,
877 0);
878
879 // get global coordinate of neighbor cell 4 center
880 globalPos3 = element32.geometry().center();
881
882 globalPosFace23 = intersection23.geometry().center();
883 globalPosFace34 = intersection43.geometry().center();
884
885 Scalar faceVol23 = intersection23.geometry().volume() / 2.0;
886 Scalar faceVol34 = intersection43.geometry().volume() / 2.0;
887
888 // get outer normal vector scaled with half volume of face : for numbering of n see Aavatsmark, Eigestad
889 DimVector unitOuterNormal23 = intersection23.centerUnitOuterNormal();
890
891 DimVector unitOuterNormal43 = intersection43.centerUnitOuterNormal();
892
893 interactionVolumes_[globalVertIdx1234].setNormal(unitOuterNormal23, 1, 0);
894 unitOuterNormal23 *= -1;
895 interactionVolumes_[globalVertIdx1234].setNormal(unitOuterNormal23, 2, 1);
896 interactionVolumes_[globalVertIdx1234].setNormal(unitOuterNormal43, 3, 1);
897 unitOuterNormal43 *= -1;
898 interactionVolumes_[globalVertIdx1234].setNormal(unitOuterNormal43, 2, 0);
899 interactionVolumes_[globalVertIdx1234].setFaceArea(faceVol23, 1, 0);
900 interactionVolumes_[globalVertIdx1234].setFaceArea(faceVol23, 2, 1);
901 interactionVolumes_[globalVertIdx1234].setFaceArea(faceVol34, 2, 0);
902 interactionVolumes_[globalVertIdx1234].setFaceArea(faceVol34, 3, 1);
903 interactionVolumes_[globalVertIdx1234].setFacePosition(globalPosFace23, 1, 0);
904 interactionVolumes_[globalVertIdx1234].setFacePosition(globalPosFace23, 2, 1);
905 interactionVolumes_[globalVertIdx1234].setFacePosition(globalPosFace34, 2, 0);
906 interactionVolumes_[globalVertIdx1234].setFacePosition(globalPosFace34, 3, 1);
907
908 finished = true;
909
910 break;
911 }
912 }
913 }
914 if (finished)
915 {
916 break;
917 }
918 }
919 }
920
921 // 'intersection14' is on the boundary
922 else
923 {
924 problem_.boundaryTypes(bcType, intersection14);
925 PrimaryVariables boundValues(0.0);
926
927 interactionVolumes_[globalVertIdx1234].setBoundary(bcType, 3);
928 if (bcType.isNeumann(pressEqIdx))
929 {
930 problem_.neumann(boundValues, intersection14);
931 boundValues *= faceVol41;
932 interactionVolumes_[globalVertIdx1234].setNeumannCondition(boundValues, 3);
933 }
934 if (bcType.hasDirichlet())
935 {
936 problem_.dirichlet(boundValues, intersection14);
937 interactionVolumes_[globalVertIdx1234].setDirichletCondition(boundValues, 3);
938 }
939
940 // get common geometry information for the following computation
941 // get the information of the face 'isIt23' between cell2 and cell4 (locally numbered)
942
943 // center of face in global coordinates, i.e., the midpoint of edge 'isIt23'
944 GlobalPosition globalPosFace23(0);
945
946 // get face volume
947 Scalar faceVol23 = 0;
948
949 // get outer normal vector scaled with half volume of face 'isIt23'
950 DimVector unitOuterNormal23(0);
951
952 bool finished = false;
953
954 for (const auto& intersection2
955 : intersections(problem_.gridView(), element2))
956 {
957 if (intersection2.boundary())
958 {
959 for (int i = 0; i < intersection2.geometry().corners(); ++i)
960 {
961 int localVertIdx2corner = refElement.subEntity(intersection2.indexInInside(), dim - 1, i,
962 dim);
963
964 int globalVertIdx2corner = problem_.variables().index(
965 element2.template subEntity < dim > (localVertIdx2corner));
966
967 if (globalVertIdx2corner == globalVertIdx1234)
968 {
969 interactionVolumes_[globalVertIdx1234].setIndexOnElement(intersection2.indexInInside(), 1,
970 0);
971
972 globalPosFace23 = intersection2.geometry().center();
973
974 faceVol23 = intersection2.geometry().volume() / 2.0;
975
976 unitOuterNormal23 = intersection2.centerUnitOuterNormal();
977
978 interactionVolumes_[globalVertIdx1234].setNormal(unitOuterNormal23, 1, 0);
979 interactionVolumes_[globalVertIdx1234].setFaceArea(faceVol23, 1, 0);
980 interactionVolumes_[globalVertIdx1234].setFacePosition(globalPosFace23, 1, 0);
981
982 problem_.boundaryTypes(bcType, intersection2);
983 boundValues = 0.0;
984
985 interactionVolumes_[globalVertIdx1234].setBoundary(bcType, 1);
986 if (bcType.isNeumann(pressEqIdx))
987 {
988 problem_.neumann(boundValues, intersection2);
989 boundValues *= faceVol23;
990 interactionVolumes_[globalVertIdx1234].setNeumannCondition(boundValues, 1);
991 }
992 if (bcType.hasDirichlet())
993 {
994 problem_.dirichlet(boundValues, intersection2);
995 interactionVolumes_[globalVertIdx1234].setDirichletCondition(boundValues, 1);
996 }
997
998 interactionVolumes_[globalVertIdx1234].setOutsideFace(2);
999
1000 innerBoundaryVolumeFaces_[eIdxGlobal1][intersection12.indexInInside()] = true;
1001 innerBoundaryVolumeFaces_[eIdxGlobal2][intersection12.indexInOutside()] = true;
1002
1003 finished = true;
1004
1005 break;
1006 }
1007 }
1008 }
1009 if (finished)
1010 {
1011 break;
1012 }
1013 }
1014 if (!finished)
1015 {
1016 DUNE_THROW(
1017 Dune::NotImplemented,
1018 "fvmpfao2pfaboundpressure2p.hh, l. 997: boundary shape not available as interaction volume shape");
1019 }
1020 }
1021 }
1022
1023 // handle boundary face 'intersection12'
1024 else
1025 {
1026 problem_.boundaryTypes(bcType, intersection12);
1027 PrimaryVariables boundValues(0.0);
1028
1029 interactionVolumes_[globalVertIdx1234].setBoundary(bcType, 0);
1030 if (bcType.isNeumann(pressEqIdx))
1031 {
1032 problem_.neumann(boundValues, intersection12);
1033 boundValues *= faceVol12;
1034 interactionVolumes_[globalVertIdx1234].setNeumannCondition(boundValues, 0);
1035 }
1036 if (bcType.hasDirichlet())
1037 {
1038 problem_.dirichlet(boundValues, intersection12);
1039 interactionVolumes_[globalVertIdx1234].setDirichletCondition(boundValues, 0);
1040 }
1041
1042 // 'intersection14' is on boundary
1043 if (intersection14.boundary())
1044 {
1045 problem_.boundaryTypes(bcType, intersection14);
1046 boundValues = 0.0;
1047
1048 interactionVolumes_[globalVertIdx1234].setBoundary(bcType, 3);
1049 if (bcType.isNeumann(pressEqIdx))
1050 {
1051 problem_.neumann(boundValues, intersection14);
1052 boundValues *= faceVol41;
1053 interactionVolumes_[globalVertIdx1234].setNeumannCondition(boundValues, 3);
1054 }
1055 if (bcType.hasDirichlet())
1056 {
1057 problem_.dirichlet(boundValues, intersection14);
1058 interactionVolumes_[globalVertIdx1234].setDirichletCondition(boundValues, 3);
1059 }
1060
1061 interactionVolumes_[globalVertIdx1234].setOutsideFace(1);
1062 interactionVolumes_[globalVertIdx1234].setOutsideFace(2);
1063 }
1064
1065 // 'intersection14' is inside
1066 else
1067 {
1068 // neighbor cell 3
1069 // access neighbor cell 3
1070 auto element4 = intersection14.outside();
1071 interactionVolumes_[globalVertIdx1234].setIndexOnElement(intersection14.indexInOutside(), 3, 0);
1072
1073 //store pointer 4
1074 interactionVolumes_[globalVertIdx1234].setSubVolumeElement(element4, 3);
1075
1076 interactionVolumes_[globalVertIdx1234].setNormal(unitOuterNormal14, 3, 0);
1077 interactionVolumes_[globalVertIdx1234].setFaceArea(faceVol41, 3, 0);
1078 interactionVolumes_[globalVertIdx1234].setFacePosition(globalPosFace41, 3, 0);
1079
1080 int eIdxGlobal4 = problem_.variables().index(element4);
1081
1082 bool finished = false;
1083
1084 // get the information of the face 'isIt34' between cell3 and cell4 (locally numbered)
1085 for (const auto& intersection4
1086 : intersections(problem_.gridView(), element4))
1087 {
1088 if (intersection4.boundary())
1089 {
1090 for (int i = 0; i < intersection4.geometry().corners(); ++i)
1091 {
1092 int localVertIdx4corner = refElement.subEntity(intersection4.indexInInside(), dim - 1, i,
1093 dim);
1094
1095 int globalVertIdx4corner = problem_.variables().index(
1096 (element4).template subEntity < dim > (localVertIdx4corner));
1097
1098 if (globalVertIdx4corner == globalVertIdx1234)
1099 {
1100 interactionVolumes_[globalVertIdx1234].setIndexOnElement(intersection4.indexInInside(), 3,
1101 1);
1102
1103 const GlobalPosition& globalPosFace34 = intersection4.geometry().center();
1104
1105 Scalar faceVol34 = intersection4.geometry().volume() / 2.0;
1106
1107 DimVector unitOuterNormal43 = intersection4.centerUnitOuterNormal();
1108
1109 interactionVolumes_[globalVertIdx1234].setNormal(unitOuterNormal43, 3, 1);
1110 interactionVolumes_[globalVertIdx1234].setFaceArea(faceVol34, 3, 1);
1111 interactionVolumes_[globalVertIdx1234].setFacePosition(globalPosFace34, 3, 1);
1112
1113 problem_.boundaryTypes(bcType, intersection4);
1114 boundValues = 0.0;
1115
1116 interactionVolumes_[globalVertIdx1234].setBoundary(bcType, 2);
1117 if (bcType.isNeumann(pressEqIdx))
1118 {
1119 problem_.neumann(boundValues, intersection4);
1120 boundValues *= faceVol34;
1121 interactionVolumes_[globalVertIdx1234].setNeumannCondition(boundValues, 2);
1122 }
1123 if (bcType.hasDirichlet())
1124 {
1125 problem_.dirichlet(boundValues, intersection4);
1126 interactionVolumes_[globalVertIdx1234].setDirichletCondition(boundValues, 2);
1127 }
1128
1129 interactionVolumes_[globalVertIdx1234].setOutsideFace(1);
1130
1131 innerBoundaryVolumeFaces_[eIdxGlobal1][intersection14.indexInInside()] = true;
1132 innerBoundaryVolumeFaces_[eIdxGlobal4][intersection14.indexInOutside()] = true;
1133
1134 finished = true;
1135
1136 break;
1137 }
1138 }
1139 }
1140 if (finished)
1141 {
1142 break;
1143 }
1144 }
1145 if (!finished)
1146 {
1147 DUNE_THROW(
1148 Dune::NotImplemented,
1149 "fvmpfao2pfaboundpressure2p.hh, l. 1164: boundary shape not available as interaction volume shape");
1150 }
1151 }
1152 }
1153
1154 } // end all intersections
1155 } // end grid traversal
1156
1157 return;
1158}
1159
1160// only for 2-D general quadrilateral
1161// TODO doc me!
1162template<class TypeTag>
1163void FvMpfaL2dPressure2p<TypeTag>::assemble()
1164{
1165 // initialization: set global matrix this->A_ to zero
1166 this->A_ = 0;
1167 this->f_ = 0;
1168
1169 // run through all vertices
1170 for (const auto& vertex : vertices(problem_.gridView()))
1171 {
1172 int vIdxGlobal = problem_.variables().index(vertex);
1173
1174 InteractionVolume& interactionVolume = interactionVolumes_[vIdxGlobal];
1175
1176 if (interactionVolume.isInnerVolume())
1177 {
1178
1179 auto element1 = interactionVolume.getSubVolumeElement(0);
1180 auto element2 = interactionVolume.getSubVolumeElement(1);
1181 auto element3 = interactionVolume.getSubVolumeElement(2);
1182 auto element4 = interactionVolume.getSubVolumeElement(3);
1183
1184 // get global coordinate of cell centers
1185 const GlobalPosition& globalPos1 = element1.geometry().center();
1186 const GlobalPosition& globalPos2 = element2.geometry().center();
1187 const GlobalPosition& globalPos3 = element3.geometry().center();
1188 const GlobalPosition& globalPos4 = element4.geometry().center();
1189
1190 // cell volumes
1191 Scalar volume1 = element1.geometry().volume();
1192 Scalar volume2 = element2.geometry().volume();
1193 Scalar volume3 = element3.geometry().volume();
1194 Scalar volume4 = element4.geometry().volume();
1195
1196 // cell index
1197 int eIdxGlobal1 = problem_.variables().index(element1);
1198 int eIdxGlobal2 = problem_.variables().index(element2);
1199 int eIdxGlobal3 = problem_.variables().index(element3);
1200 int eIdxGlobal4 = problem_.variables().index(element4);
1201
1202 //get the cell Data
1203 CellData& cellData1 = problem_.variables().cellData(eIdxGlobal1);
1204 CellData& cellData2 = problem_.variables().cellData(eIdxGlobal2);
1205 CellData& cellData3 = problem_.variables().cellData(eIdxGlobal3);
1206 CellData& cellData4 = problem_.variables().cellData(eIdxGlobal4);
1207
1208 // evaluate right hand side
1209 PrimaryVariables source(0.0);
1210 problem_.source(source, element1);
1211 this->f_[eIdxGlobal1] += volume1 / (4.0) * (source[wPhaseIdx] / density_[wPhaseIdx] + source[nPhaseIdx] / density_[nPhaseIdx]);
1212 problem_.source(source, element2);
1213 this->f_[eIdxGlobal2] += volume2 / (4.0) * (source[wPhaseIdx] / density_[wPhaseIdx] + source[nPhaseIdx] / density_[nPhaseIdx]);
1214 problem_.source(source, element3);
1215 this->f_[eIdxGlobal3] += volume3 / (4.0) * (source[wPhaseIdx] / density_[wPhaseIdx] + source[nPhaseIdx] / density_[nPhaseIdx]);
1216 problem_.source(source, element4);
1217 this->f_[eIdxGlobal4] += volume4 / (4.0) * (source[wPhaseIdx] / density_[wPhaseIdx] + source[nPhaseIdx] / density_[nPhaseIdx]);
1218
1219 this->f_[eIdxGlobal1] += evaluateErrorTerm_(cellData1) * volume1 / (4.0);
1220 this->f_[eIdxGlobal2] += evaluateErrorTerm_(cellData2) * volume2 / (4.0);
1221 this->f_[eIdxGlobal3] += evaluateErrorTerm_(cellData3) * volume3 / (4.0);
1222 this->f_[eIdxGlobal4] += evaluateErrorTerm_(cellData4) * volume4 / (4.0);
1223
1224 //get mobilities of the phases
1225 Dune::FieldVector<Scalar, numPhases> lambda1(cellData1.mobility(wPhaseIdx));
1226 lambda1[nPhaseIdx] = cellData1.mobility(nPhaseIdx);
1227
1228 //compute total mobility of cell 1
1229 Scalar lambdaTotal1 = lambda1[wPhaseIdx] + lambda1[nPhaseIdx];
1230
1231 //get mobilities of the phases
1232 Dune::FieldVector<Scalar, numPhases> lambda2(cellData2.mobility(wPhaseIdx));
1233 lambda2[nPhaseIdx] = cellData2.mobility(nPhaseIdx);
1234
1235 //compute total mobility of cell 1
1236 Scalar lambdaTotal2 = lambda2[wPhaseIdx] + lambda2[nPhaseIdx];
1237
1238 //get mobilities of the phases
1239 Dune::FieldVector<Scalar, numPhases> lambda3(cellData3.mobility(wPhaseIdx));
1240 lambda3[nPhaseIdx] = cellData3.mobility(nPhaseIdx);
1241
1242 //compute total mobility of cell 1
1243 Scalar lambdaTotal3 = lambda3[wPhaseIdx] + lambda3[nPhaseIdx];
1244
1245 //get mobilities of the phases
1246 Dune::FieldVector<Scalar, numPhases> lambda4(cellData4.mobility(wPhaseIdx));
1247 lambda4[nPhaseIdx] = cellData4.mobility(nPhaseIdx);
1248
1249 //compute total mobility of cell 1
1250 Scalar lambdaTotal4 = lambda4[wPhaseIdx] + lambda4[nPhaseIdx];
1251
1252 std::vector<DimVector > lambda(2*dim);
1253
1254 lambda[0][0] = lambdaTotal1;
1255 lambda[0][1] = lambdaTotal1;
1256 lambda[1][0] = lambdaTotal2;
1257 lambda[1][1] = lambdaTotal2;
1258 lambda[2][0] = lambdaTotal3;
1259 lambda[2][1] = lambdaTotal3;
1260 lambda[3][0] = lambdaTotal4;
1261 lambda[3][1] = lambdaTotal4;
1262
1263 //add capillary pressure and gravity terms to right-hand-side
1264 //calculate capillary pressure velocity
1265 Dune::FieldVector<Scalar, 2 * dim> pc(0);
1266 pc[0] = cellData1.capillaryPressure();
1267 pc[1] = cellData2.capillaryPressure();
1268 pc[2] = cellData3.capillaryPressure();
1269 pc[3] = cellData4.capillaryPressure();
1270
1271 Dune::FieldVector<Scalar, 2 * dim> gravityDiff(0);
1272
1273// std::cout<<"maxPos = "<<problem_.bBoxMax()<<"\n";
1274
1275 gravityDiff[0] = (problem_.bBoxMax() - globalPos1) * gravity_ * (density_[nPhaseIdx] - density_[wPhaseIdx]);
1276 gravityDiff[1] = (problem_.bBoxMax() - globalPos2) * gravity_ * (density_[nPhaseIdx] - density_[wPhaseIdx]);
1277 gravityDiff[2] = (problem_.bBoxMax() - globalPos3) * gravity_ * (density_[nPhaseIdx] - density_[wPhaseIdx]);
1278 gravityDiff[3] = (problem_.bBoxMax() - globalPos4) * gravity_ * (density_[nPhaseIdx] - density_[wPhaseIdx]);
1279
1280 pc += gravityDiff;
1281
1282 Dune::FieldVector<Scalar, 2 * dim> pcFlux(0);
1283
1284 Scalar pcPotential12 = 0;
1285 Scalar pcPotential14 = 0;
1286 Scalar pcPotential32 = 0;
1287 Scalar pcPotential34 = 0;
1288
1289 DimVector Tu(0);
1290 Dune::FieldVector<Scalar, 2 * dim - dim + 1> u(0);
1291 Dune::FieldMatrix<Scalar,dim,2*dim-dim+1> T(0);
1292
1293 int lType = transmissibilityCalculator_.calculateTransmissibility(T, interactionVolume, lambda, 0, 1, 2, 3);
1294
1295 if (lType == TransmissibilityCalculator::rightTriangle)
1296 {
1297 if (innerBoundaryVolumeFaces_[eIdxGlobal1][interactionVolume.getIndexOnElement(0, 0)])
1298 {
1299 T *= 2;
1300 }
1301 this->A_[eIdxGlobal1][eIdxGlobal2] += T[1][0];
1302 this->A_[eIdxGlobal1][eIdxGlobal3] += T[1][1];
1303 this->A_[eIdxGlobal1][eIdxGlobal1] += T[1][2];
1304
1305 this->A_[eIdxGlobal2][eIdxGlobal2] -= T[1][0];
1306 this->A_[eIdxGlobal2][eIdxGlobal3] -= T[1][1];
1307 this->A_[eIdxGlobal2][eIdxGlobal1] -= T[1][2];
1308
1309 u[0] = pc[1];
1310 u[1] = pc[2];
1311 u[2] = pc[0];
1312
1313 T.mv(u, Tu);
1314
1315 pcFlux[0] = Tu[1];
1316 pcPotential12 = Tu[1];
1317 }
1318 else
1319 {
1320 if (innerBoundaryVolumeFaces_[eIdxGlobal1][interactionVolume.getIndexOnElement(0, 0)])
1321 {
1322 T *= 2;
1323 }
1324 this->A_[eIdxGlobal1][eIdxGlobal1] += T[1][0];
1325 this->A_[eIdxGlobal1][eIdxGlobal4] += T[1][1];
1326 this->A_[eIdxGlobal1][eIdxGlobal2] += T[1][2];
1327
1328 this->A_[eIdxGlobal2][eIdxGlobal1] -= T[1][0];
1329 this->A_[eIdxGlobal2][eIdxGlobal4] -= T[1][1];
1330 this->A_[eIdxGlobal2][eIdxGlobal2] -= T[1][2];
1331
1332 u[0] = pc[0];
1333 u[1] = pc[3];
1334 u[2] = pc[1];
1335
1336 T.mv(u, Tu);
1337
1338 pcFlux[0] = Tu[1];
1339 pcPotential12 = Tu[1];
1340 }
1341
1342 lType = transmissibilityCalculator_.calculateTransmissibility(T, interactionVolume, lambda, 1, 2, 3, 0);
1343
1344 if (lType == TransmissibilityCalculator::rightTriangle)
1345 {
1346 if (innerBoundaryVolumeFaces_[eIdxGlobal2][interactionVolume.getIndexOnElement(1, 0)])
1347 {
1348 T *= 2;
1349 }
1350 this->A_[eIdxGlobal2][eIdxGlobal3] += T[1][0];
1351 this->A_[eIdxGlobal2][eIdxGlobal4] += T[1][1];
1352 this->A_[eIdxGlobal2][eIdxGlobal2] += T[1][2];
1353
1354 this->A_[eIdxGlobal3][eIdxGlobal3] -= T[1][0];
1355 this->A_[eIdxGlobal3][eIdxGlobal4] -= T[1][1];
1356 this->A_[eIdxGlobal3][eIdxGlobal2] -= T[1][2];
1357
1358 u[0] = pc[2];
1359 u[1] = pc[3];
1360 u[2] = pc[1];
1361
1362 T.mv(u, Tu);
1363
1364 pcFlux[1] = Tu[1];
1365 pcPotential32 = -Tu[1];
1366 }
1367 else
1368 {
1369 if (innerBoundaryVolumeFaces_[eIdxGlobal2][interactionVolume.getIndexOnElement(1, 0)])
1370 {
1371 T *= 2;
1372 }
1373 this->A_[eIdxGlobal2][eIdxGlobal2] += T[1][0];
1374 this->A_[eIdxGlobal2][eIdxGlobal1] += T[1][1];
1375 this->A_[eIdxGlobal2][eIdxGlobal3] += T[1][2];
1376
1377 this->A_[eIdxGlobal3][eIdxGlobal2] -= T[1][0];
1378 this->A_[eIdxGlobal3][eIdxGlobal1] -= T[1][1];
1379 this->A_[eIdxGlobal3][eIdxGlobal3] -= T[1][2];
1380
1381 u[0] = pc[1];
1382 u[1] = pc[0];
1383 u[2] = pc[2];
1384
1385 T.mv(u, Tu);
1386
1387 pcFlux[1] = Tu[1];
1388 pcPotential32 = -Tu[1];
1389 }
1390
1391 lType = transmissibilityCalculator_.calculateTransmissibility(T, interactionVolume, lambda, 2, 3, 0, 1);
1392
1393 if (lType == TransmissibilityCalculator::rightTriangle)
1394 {
1395 if (innerBoundaryVolumeFaces_[eIdxGlobal3][interactionVolume.getIndexOnElement(2, 0)])
1396 {
1397 T *= 2;
1398 }
1399 this->A_[eIdxGlobal3][eIdxGlobal4] += T[1][0];
1400 this->A_[eIdxGlobal3][eIdxGlobal1] += T[1][1];
1401 this->A_[eIdxGlobal3][eIdxGlobal3] += T[1][2];
1402
1403 this->A_[eIdxGlobal4][eIdxGlobal4] -= T[1][0];
1404 this->A_[eIdxGlobal4][eIdxGlobal1] -= T[1][1];
1405 this->A_[eIdxGlobal4][eIdxGlobal3] -= T[1][2];
1406
1407 u[0] = pc[3];
1408 u[1] = pc[0];
1409 u[2] = pc[2];
1410
1411 T.mv(u, Tu);
1412
1413 pcFlux[2] = Tu[1];
1414 pcPotential34 = Tu[1];
1415 }
1416 else
1417 {
1418 if (innerBoundaryVolumeFaces_[eIdxGlobal3][interactionVolume.getIndexOnElement(2, 0)])
1419 {
1420 T *= 2;
1421 }
1422 this->A_[eIdxGlobal3][eIdxGlobal3] += T[1][0];
1423 this->A_[eIdxGlobal3][eIdxGlobal2] += T[1][1];
1424 this->A_[eIdxGlobal3][eIdxGlobal4] += T[1][2];
1425
1426 this->A_[eIdxGlobal4][eIdxGlobal3] -= T[1][0];
1427 this->A_[eIdxGlobal4][eIdxGlobal2] -= T[1][1];
1428 this->A_[eIdxGlobal4][eIdxGlobal4] -= T[1][2];
1429
1430 u[0] = pc[2];
1431 u[1] = pc[1];
1432 u[2] = pc[3];
1433
1434 T.mv(u, Tu);
1435
1436 pcFlux[2] = Tu[1];
1437 pcPotential34 = Tu[1];
1438 }
1439
1440 lType = transmissibilityCalculator_.calculateTransmissibility(T, interactionVolume, lambda, 3, 0, 1, 2);
1441
1442 if (lType == TransmissibilityCalculator::rightTriangle)
1443 {
1444 if (innerBoundaryVolumeFaces_[eIdxGlobal4][interactionVolume.getIndexOnElement(3, 0)])
1445 {
1446 T *= 2;
1447 }
1448 this->A_[eIdxGlobal4][eIdxGlobal1] += T[1][0];
1449 this->A_[eIdxGlobal4][eIdxGlobal2] += T[1][1];
1450 this->A_[eIdxGlobal4][eIdxGlobal4] += T[1][2];
1451
1452 this->A_[eIdxGlobal1][eIdxGlobal1] -= T[1][0];
1453 this->A_[eIdxGlobal1][eIdxGlobal2] -= T[1][1];
1454 this->A_[eIdxGlobal1][eIdxGlobal4] -= T[1][2];
1455
1456 u[0] = pc[0];
1457 u[1] = pc[1];
1458 u[2] = pc[3];
1459
1460 T.mv(u, Tu);
1461
1462 pcFlux[3] = Tu[1];
1463 pcPotential14 = -Tu[1];
1464 }
1465 else
1466 {
1467 if (innerBoundaryVolumeFaces_[eIdxGlobal4][interactionVolume.getIndexOnElement(3, 0)])
1468 {
1469 T *= 2;
1470 }
1471 this->A_[eIdxGlobal4][eIdxGlobal4] += T[1][0];
1472 this->A_[eIdxGlobal4][eIdxGlobal3] += T[1][1];
1473 this->A_[eIdxGlobal4][eIdxGlobal1] += T[1][2];
1474
1475 this->A_[eIdxGlobal1][eIdxGlobal4] -= T[1][0];
1476 this->A_[eIdxGlobal1][eIdxGlobal3] -= T[1][1];
1477 this->A_[eIdxGlobal1][eIdxGlobal1] -= T[1][2];
1478
1479 u[0] = pc[3];
1480 u[1] = pc[2];
1481 u[2] = pc[0];
1482
1483 T.mv(u, Tu);
1484
1485 pcFlux[3] = Tu[1];
1486 pcPotential14 = -Tu[1];
1487 }
1488
1489 if (pc[0] == 0 && pc[1] == 0 && pc[2] == 0 && pc[3] == 0)
1490 {
1491 continue;
1492 }
1493
1494 //compute mobilities of face 1
1495 Dune::FieldVector<Scalar, numPhases> lambda12Upw(0.0);
1496 lambda12Upw[wPhaseIdx] = (pcPotential12 >= 0) ? lambda1[wPhaseIdx] : lambda2[wPhaseIdx];
1497 lambda12Upw[nPhaseIdx] = (pcPotential12 >= 0) ? lambda1[nPhaseIdx] : lambda2[nPhaseIdx];
1498
1499 //compute mobilities of face 4
1500 Dune::FieldVector<Scalar, numPhases> lambda14Upw(0.0);
1501 lambda14Upw[wPhaseIdx] = (pcPotential14 >= 0) ? lambda1[wPhaseIdx] : lambda4[wPhaseIdx];
1502 lambda14Upw[nPhaseIdx] = (pcPotential14 >= 0) ? lambda1[nPhaseIdx] : lambda4[nPhaseIdx];
1503
1504 //compute mobilities of face 2
1505 Dune::FieldVector<Scalar, numPhases> lambda32Upw(0.0);
1506 lambda32Upw[wPhaseIdx] = (pcPotential32 >= 0) ? lambda3[wPhaseIdx] : lambda2[wPhaseIdx];
1507 lambda32Upw[nPhaseIdx] = (pcPotential32 >= 0) ? lambda3[nPhaseIdx] : lambda2[nPhaseIdx];
1508
1509 //compute mobilities of face 3
1510 Dune::FieldVector<Scalar, numPhases> lambda34Upw(0.0);
1511 lambda34Upw[wPhaseIdx] = (pcPotential34 >= 0) ? lambda3[wPhaseIdx] : lambda4[wPhaseIdx];
1512 lambda34Upw[nPhaseIdx] = (pcPotential34 >= 0) ? lambda3[nPhaseIdx] : lambda4[nPhaseIdx];
1513
1514 for (int i = 0; i < numPhases; i++)
1515 {
1516 Scalar lambdaT12 = lambda12Upw[wPhaseIdx] + lambda12Upw[nPhaseIdx];
1517 Scalar lambdaT14 = lambda14Upw[wPhaseIdx] + lambda14Upw[nPhaseIdx];
1518 Scalar lambdaT32 = lambda32Upw[wPhaseIdx] + lambda32Upw[nPhaseIdx];
1519 Scalar lambdaT34 = lambda34Upw[wPhaseIdx] + lambda34Upw[nPhaseIdx];
1520 Scalar fracFlow12 = (lambdaT12 > threshold_) ? lambda12Upw[i] / (lambdaT12) : 0.0;
1521 Scalar fracFlow14 = (lambdaT14 > threshold_) ? lambda14Upw[i] / (lambdaT14) : 0.0;
1522 Scalar fracFlow32 = (lambdaT32 > threshold_) ? lambda32Upw[i] / (lambdaT32) : 0.0;
1523 Scalar fracFlow34 = (lambdaT34 > threshold_) ? lambda34Upw[i] / (lambdaT34) : 0.0;
1524
1525 Dune::FieldVector<Scalar, 2 * dim> pcFluxReal(pcFlux);
1526
1527 pcFluxReal[0] *= fracFlow12;
1528 pcFluxReal[1] *= fracFlow32;
1529 pcFluxReal[2] *= fracFlow34;
1530 pcFluxReal[3] *= fracFlow14;
1531
1532// if (std::isnan(pcFluxReal.two_norm()))
1533// std::cout<<"pcFlux = "<<pcFlux<<"\n";
1534
1535 switch (pressureType_)
1536 {
1537 case pw:
1538 {
1539 if (i == nPhaseIdx)
1540 {
1541 //add capillary pressure term to right hand side
1542 this->f_[eIdxGlobal1] -= (pcFluxReal[0] - pcFluxReal[3]);
1543 this->f_[eIdxGlobal2] -= (pcFluxReal[1] - pcFluxReal[0]);
1544 this->f_[eIdxGlobal3] -= (pcFluxReal[2] - pcFluxReal[1]);
1545 this->f_[eIdxGlobal4] -= (pcFluxReal[3] - pcFluxReal[2]);
1546
1547 }
1548 break;
1549 }
1550 case pn:
1551 {
1552 if (i == wPhaseIdx)
1553 {
1554 //add capillary pressure term to right hand side
1555 this->f_[eIdxGlobal1] += (pcFluxReal[0] - pcFluxReal[3]);
1556 this->f_[eIdxGlobal2] += (pcFluxReal[1] - pcFluxReal[0]);
1557 this->f_[eIdxGlobal3] += (pcFluxReal[2] - pcFluxReal[1]);
1558 this->f_[eIdxGlobal4] += (pcFluxReal[3] - pcFluxReal[2]);
1559 }
1560 break;
1561 }
1562 }
1563 }
1564 }
1565
1566 // at least one face on boundary!
1567 else
1568 {
1569 for (int elemIdx = 0; elemIdx < 2 * dim; elemIdx++)
1570 {
1571 bool isOutside = false;
1572 for (int fIdx = 0; fIdx < dim; fIdx++)
1573 {
1574 int intVolFaceIdx = interactionVolume.getFaceIndexFromSubVolume(elemIdx, fIdx);
1575 if (interactionVolume.isOutsideFace(intVolFaceIdx))
1576 {
1577 isOutside = true;
1578 break;
1579 }
1580 }
1581 if (isOutside)
1582 {
1583 continue;
1584 }
1585
1586 auto element = interactionVolume.getSubVolumeElement(elemIdx);
1587
1588 // get global coordinate of cell centers
1589 const GlobalPosition& globalPos = element.geometry().center();
1590
1591 // cell volumes
1592 Scalar volume = element.geometry().volume();
1593
1594 // cell index
1595 int eIdxGlobal = problem_.variables().index(element);
1596
1597 //get the cell Data
1598 CellData& cellData = problem_.variables().cellData(eIdxGlobal);
1599
1600 //permeability vector at boundary
1601 DimMatrix permeability(problem_.spatialParams().intrinsicPermeability(element));
1602
1603 // evaluate right hand side
1604 PrimaryVariables source(0);
1605 problem_.source(source, element);
1606 this->f_[eIdxGlobal] += volume / (4.0) * (source[wPhaseIdx] / density_[wPhaseIdx] + source[nPhaseIdx] / density_[nPhaseIdx]);
1607
1608 this->f_[eIdxGlobal] += evaluateErrorTerm_(cellData) * volume / (4.0);
1609
1610 //get mobilities of the phases
1611 Dune::FieldVector<Scalar, numPhases> lambda(cellData.mobility(wPhaseIdx));
1612 lambda[nPhaseIdx] = cellData.mobility(nPhaseIdx);
1613
1614 Scalar pc = cellData.capillaryPressure();
1615
1616 Scalar gravityDiff = (problem_.bBoxMax() - globalPos) * gravity_ * (density_[nPhaseIdx] - density_[wPhaseIdx]);
1617
1618 pc += gravityDiff; //minus because of gravity definition!
1619
1620 for (int fIdx = 0; fIdx < dim; fIdx++)
1621 {
1622 int intVolFaceIdx = interactionVolume.getFaceIndexFromSubVolume(elemIdx, fIdx);
1623
1624 if (interactionVolume.isBoundaryFace(intVolFaceIdx))
1625 {
1626
1627 if (interactionVolume.getBoundaryType(intVolFaceIdx).isDirichlet(pressEqIdx))
1628 {
1629 int boundaryFaceIdx = interactionVolume.getIndexOnElement(elemIdx, fIdx);
1630
1631 const auto refElement = referenceElement(element);
1632
1633 const LocalPosition& localPos = refElement.position(boundaryFaceIdx, 1);
1634
1635 const GlobalPosition& globalPosFace = element.geometry().global(localPos);
1636
1637 DimVector distVec(globalPosFace - globalPos);
1638 Scalar dist = distVec.two_norm();
1639 DimVector unitDistVec(distVec);
1640 unitDistVec /= dist;
1641
1642 Scalar faceArea = interactionVolume.getFaceArea(elemIdx, fIdx);
1643
1644 // get pc and lambda at the boundary
1645 Scalar satWBound = cellData.saturation(wPhaseIdx);
1646 //check boundary sat at face 1
1647 if (interactionVolume.getBoundaryType(intVolFaceIdx).isDirichlet(satEqIdx))
1648 {
1649 Scalar satBound = interactionVolume.getDirichletValues(intVolFaceIdx)[saturationIdx];
1650 switch (saturationType_)
1651 {
1652 case sw:
1653 {
1654 satWBound = satBound;
1655 break;
1656 }
1657 case sn:
1658 {
1659 satWBound = 1 - satBound;
1660 break;
1661 }
1662 }
1663
1664 }
1665
1666 const auto fluidMatrixInteraction = problem_.spatialParams().fluidMatrixInteractionAtPos(element.geometry().center());
1667 Scalar pcBound = fluidMatrixInteraction.pc(satWBound);
1668
1669 Scalar gravityDiffBound = (problem_.bBoxMax() - globalPosFace) * gravity_
1670 * (density_[nPhaseIdx] - density_[wPhaseIdx]);
1671
1672 pcBound += gravityDiffBound;
1673
1674 Dune::FieldVector<Scalar, numPhases> lambdaBound(fluidMatrixInteraction.krw(satWBound));
1675 lambdaBound[nPhaseIdx] = fluidMatrixInteraction.krn(satWBound);
1676 lambdaBound[wPhaseIdx] /= viscosity_[wPhaseIdx];
1677 lambdaBound[nPhaseIdx] /= viscosity_[nPhaseIdx];
1678
1679 Scalar potentialBound = interactionVolume.getDirichletValues(intVolFaceIdx)[pressureIdx];
1680 Scalar gdeltaZ = (problem_.bBoxMax()-globalPosFace) * gravity_;
1681
1682 //calculate potential gradients
1683 Scalar potentialDiffW = 0;
1684 Scalar potentialDiffNw = 0;
1685 switch (pressureType_)
1686 {
1687 case pw:
1688 {
1689 potentialBound += density_[wPhaseIdx]*gdeltaZ;
1690 potentialDiffW = (cellData.potential(wPhaseIdx) - potentialBound) / dist;
1691 potentialDiffNw = (cellData.potential(nPhaseIdx) - potentialBound - pcBound) / dist;
1692 break;
1693 }
1694 case pn:
1695 {
1696 potentialBound += density_[nPhaseIdx]*gdeltaZ;
1697 potentialDiffW = (cellData.potential(wPhaseIdx) - potentialBound + pcBound) / dist;
1698 potentialDiffNw = (cellData.potential(nPhaseIdx) - potentialBound) / dist;
1699 break;
1700 }
1701 }
1702
1703 Scalar lambdaTotal = (potentialDiffW >= 0.) ? lambda[wPhaseIdx] : lambdaBound[wPhaseIdx];
1704 lambdaTotal += (potentialDiffNw >= 0.) ? lambda[nPhaseIdx] : lambdaBound[nPhaseIdx];
1705
1706 DimVector permTimesNormal(0);
1707 permeability.mv(unitDistVec, permTimesNormal);
1708
1709 //calculate current matrix entry
1710 Scalar entry = lambdaTotal * (unitDistVec * permTimesNormal) / dist * faceArea;
1711
1712 //calculate right hand side
1713 Scalar pcFlux = 0;
1714
1715 switch (pressureType_)
1716 {
1717 case pw:
1718 {
1719 // calculate capillary pressure gradient
1720 DimVector pcGradient = unitDistVec;
1721 pcGradient *= (pc - pcBound) / dist;
1722
1723 //add capillary pressure term to right hand side
1724 pcFlux = 0.5 * (lambda[nPhaseIdx] + lambdaBound[nPhaseIdx])
1725 * (permTimesNormal * pcGradient) * faceArea;
1726
1727 break;
1728 }
1729 case pn:
1730 {
1731 // calculate capillary pressure gradient
1732 DimVector pcGradient = unitDistVec;
1733 pcGradient *= (pc - pcBound) / dist;
1734
1735 //add capillary pressure term to right hand side
1736 pcFlux = 0.5 * (lambda[wPhaseIdx] + lambdaBound[wPhaseIdx])
1737 * (permTimesNormal * pcGradient) * faceArea;
1738
1739 break;
1740
1741 }
1742 }
1743
1744 // set diagonal entry and right hand side entry
1745 this->A_[eIdxGlobal][eIdxGlobal] += entry;
1746 this->f_[eIdxGlobal] += entry * potentialBound;
1747
1748 if (pc == 0 && pcBound == 0)
1749 {
1750 continue;
1751 }
1752
1753 for (int i = 0; i < numPhases; i++)
1754 {
1755 switch (pressureType_)
1756 {
1757 case pw:
1758 {
1759 if (i == nPhaseIdx)
1760 {
1761 //add capillary pressure term to right hand side
1762 this->f_[eIdxGlobal] -= pcFlux;
1763 }
1764 break;
1765 }
1766 case pn:
1767 {
1768 if (i == wPhaseIdx)
1769 {
1770 //add capillary pressure term to right hand side
1771 this->f_[eIdxGlobal] += pcFlux;
1772 }
1773 break;
1774 }
1775 }
1776 }
1777
1778 }
1779 else if (interactionVolume.getBoundaryType(intVolFaceIdx).isNeumann(pressEqIdx))
1780 {
1781 Scalar J = interactionVolume.getNeumannValues(intVolFaceIdx)[wPhaseIdx] / density_[wPhaseIdx];
1782 J += interactionVolume.getNeumannValues(intVolFaceIdx)[nPhaseIdx] / density_[nPhaseIdx];
1783
1784 this->f_[eIdxGlobal] -= J;
1785 }
1786 else
1787 {
1788 std::cout << "interactionVolume.getBoundaryType(intVolFaceIdx).isNeumann(pressEqIdx)"
1789 << interactionVolume.getBoundaryType(intVolFaceIdx).isNeumann(pressEqIdx) << "\n";
1790 DUNE_THROW(Dune::NotImplemented,
1791 "No valid boundary condition type defined for pressure equation!");
1792 }
1793 }
1794 }
1795 }
1796
1797 } // end boundaries
1798
1799 } // end vertex iterator
1800
1801 // only do more if we have more than one process
1802 if (problem_.gridView().comm().size() > 1)
1803 {
1804 // set ghost and overlap element entries
1805 for (const auto& element : elements(problem_.gridView()))
1806 {
1807 if (element.partitionType() == Dune::InteriorEntity)
1808 continue;
1809
1810 // get the global index of the cell
1811 int eIdxGlobalI = problem_.variables().index(element);
1812
1813 this->A_[eIdxGlobalI] = 0.0;
1814 this->A_[eIdxGlobalI][eIdxGlobalI] = 1.0;
1815 this->f_[eIdxGlobalI] = this->pressure()[eIdxGlobalI];
1816 }
1817 }
1818
1819 return;
1820}
1821
1827template<class TypeTag>
1829{
1830 // iterate through leaf grid an evaluate c0 at cell center
1831 for (const auto& element : elements(problem_.gridView()))
1832 {
1833 int eIdxGlobal = problem_.variables().index(element);
1834
1835 CellData& cellData = problem_.variables().cellData(eIdxGlobal);
1836
1837 Scalar satW = cellData.saturation(wPhaseIdx);
1838
1839 const auto fluidMatrixInteraction = problem_.spatialParams().fluidMatrixInteractionAtPos(element.geometry().center());
1840 const Scalar pc = fluidMatrixInteraction.pc(satW);
1841
1842 cellData.setCapillaryPressure(pc);
1843
1844 // initialize mobilities
1845 const Scalar mobilityW = fluidMatrixInteraction.krw(satW) / viscosity_[wPhaseIdx];
1846 const Scalar mobilityNw = fluidMatrixInteraction.krn(satW) / viscosity_[nPhaseIdx];
1847
1848 // initialize mobilities
1849 cellData.setMobility(wPhaseIdx, mobilityW);
1850 cellData.setMobility(nPhaseIdx, mobilityNw);
1851
1852 //initialize fractional flow functions
1853 cellData.setFracFlowFunc(wPhaseIdx, mobilityW / (mobilityW + mobilityNw));
1854 cellData.setFracFlowFunc(nPhaseIdx, mobilityNw / (mobilityW + mobilityNw));
1855 }
1856 return;
1857}
1858
1859} // end namespace Dumux
1860#endif
Provides methods for transmissibility calculation 2-d.
Class including the information of an interaction volume of a MPFA L-method that does not change with...
Definition: adapt.hh:29
typename Properties::Detail::GetPropImpl< TypeTag, Property >::type GetProp
get the type of a property
Definition: propertysystem.hh:141
typename Properties::Detail::GetPropImpl< TypeTag, Property >::type::type GetPropType
get the type alias defined in the property
Definition: propertysystem.hh:150
std::string viscosity(int phaseIdx) noexcept
I/O name of viscosity for multiphase systems.
Definition: name.hh:74
std::string permeability() noexcept
I/O name of permeability.
Definition: name.hh:143
std::string pressure(int phaseIdx) noexcept
I/O name of pressure for multiphase systems.
Definition: name.hh:34
std::string density(int phaseIdx) noexcept
I/O name of density for multiphase systems.
Definition: name.hh:65
Scalar volume(Shape shape, Scalar inscribedRadius)
Returns the volume of a given geometry based on the inscribed radius.
Definition: poreproperties.hh:73
Finite volume MPFA L-method discretization of a two-phase flow pressure equation of the sequential IM...
Definition: lmethod/2dpressure.hh:72
InnerBoundaryVolumeFaces innerBoundaryVolumeFaces_
Vector marking faces which intersect the boundary.
Definition: lmethod/2dpressure.hh:449
void storePressureSolution(const Element &element)
Stores the pressure solution of a cell.
Definition: lmethod/2dpressure.hh:238
void update()
Pressure update.
Definition: lmethod/2dpressure.hh:288
void initialize()
Initializes the pressure model.
Definition: lmethod/2dpressure.hh:191
void updateInteractionVolumeInfo()
Updates interaction volumes.
Definition: lmethod/2dpressure.hh:177
FvMpfaL2dPressure2p(Problem &problem)
Constructs a FvMpfaL2dPressure2p object.
Definition: lmethod/2dpressure.hh:408
GlobalInteractionVolumeVector interactionVolumes_
Global Vector of interaction volumes.
Definition: lmethod/2dpressure.hh:448
void storePressureSolution()
Globally stores the pressure solution.
Definition: lmethod/2dpressure.hh:225
void addOutputVtkFields(MultiWriter &writer)
Adds pressure output to the output file.
Definition: lmethod/2dpressure.hh:337
FvMpfaL2dTransmissibilityCalculator< TypeTag > TransmissibilityCalculator
Definition: lmethod/2dpressure.hh:149
void updateMaterialLaws()
constitutive functions are initialized and stored in the variables object
Definition: lmethod/2dpressure.hh:1828
Provides methods for transmissibility calculation in 2-d.
Definition: 2dtransmissibilitycalculator.hh:44
Class including the information of an interaction volume of a MPFA L-method that does not change with...
Definition: linteractionvolume.hh:41
The finite volume base class for the solution of a pressure equation.
Definition: sequential/cellcentered/pressure.hh:49
void initialize()
Initialize pressure model.
Definition: sequential/cellcentered/pressure.hh:213
PressureSolution & pressure()
Returns the vector containing the pressure solution.
Definition: sequential/cellcentered/pressure.hh:120
void solve()
Solves the global system of equations to get the spatial distribution of the pressure.
Definition: sequential/cellcentered/pressure.hh:527
Specifies the properties for immiscible 2p diffusion/pressure models.
Properties for a MPFA method.
Finite Volume Diffusion Model.