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