3.4
DUNE for Multi-{Phase, Component, Scale, Physics, ...} flow and transport in porous media
fvtransport.hh
Go to the documentation of this file.
1// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2// vi: set et ts=4 sw=4 sts=4:
3/*****************************************************************************
4 * See the file COPYING for full copying permissions. *
5 * *
6 * This program is free software: you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation, either version 3 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
18 *****************************************************************************/
24#ifndef DUMUX_FVTRANSPORT2P2C_HH
25#define DUMUX_FVTRANSPORT2P2C_HH
26
27#include <cmath>
28#include <unordered_map>
29
30#include <dune/grid/common/gridenums.hh>
31#include <dune/common/float_cmp.hh>
32
35#include <dumux/common/math.hh>
37
38namespace Dumux {
58template<class TypeTag>
60{
65
67
70
73
75
77
78 enum
79 {
80 dim = GridView::dimension, dimWorld = GridView::dimensionworld
81 };
82 enum
83 {
84 pw = Indices::pressureW,
85 pn = Indices::pressureN
86 };
87 enum
88 {
89 wPhaseIdx = Indices::wPhaseIdx, nPhaseIdx = Indices::nPhaseIdx,
90 wCompIdx = Indices::wPhaseIdx, nCompIdx = Indices::nPhaseIdx,
91 contiWEqIdx=Indices::contiWEqIdx, contiNEqIdx=Indices::contiNEqIdx,
92 NumPhases = getPropValue<TypeTag, Properties::NumPhases>(),
93 NumComponents = getPropValue<TypeTag, Properties::NumComponents>()
94 };
95
96 using Element = typename GridView::Traits::template Codim<0>::Entity;
97 using Intersection = typename GridView::Intersection;
98
99 using GlobalPosition = Dune::FieldVector<Scalar, dimWorld>;
100 using DimMatrix = Dune::FieldMatrix<Scalar, dim, dim>;
101 using PhaseVector = Dune::FieldVector<Scalar, NumPhases>;
102 using ComponentVector = Dune::FieldVector<Scalar, NumComponents>;
104
105public:
107 using EntryType = Dune::FieldVector<Scalar, 2>;
108 using TimeStepFluxType = Dune::FieldVector<Scalar, 2>;
109
110protected:
112 {
113 Dune::FieldVector<EntryType, 2*dim> faceFluxes;
114 Dune::FieldVector<Scalar, 2*dim> faceTargetDt;
115 Scalar dt;
117 {}
118 };
119
121 Problem& problem()
122 { return problem_; }
123
124 void innerUpdate(TransportSolutionType& updateVec);
125
126public:
127 virtual void update(const Scalar t, Scalar& dt, TransportSolutionType& updateVec, bool impet = false);
128
129 void updateTransportedQuantity(TransportSolutionType& updateVector);
130
131 void updateTransportedQuantity(TransportSolutionType& updateVector, Scalar dt);
132
133 void updateConcentrations(TransportSolutionType& updateVector, Scalar dt);
134
135 // Function which calculates the flux update
136 void getFlux(ComponentVector& fluxEntries, EntryType& timestepFlux,
137 const Intersection& intersection, CellData& cellDataI);
138
139 // Function which calculates the boundary flux update
140 void getFluxOnBoundary(ComponentVector& fluxEntries, EntryType& timestepFlux,
141 const Intersection& intersection, const CellData& cellDataI);
142
143 void evalBoundary(GlobalPosition globalPosFace,
144 const Intersection& intersection,
145 FluidState& BCfluidState,
146 PhaseVector& pressBound);
147
148
154 {
155 // resize update vector and set to zero
156 int transportedQuantities = getPropValue<TypeTag, Properties::NumEq>() - 1; // NumEq - 1 pressure Eq
157 totalConcentration_.resize(transportedQuantities);
158 for (int eqNumber = 0; eqNumber < transportedQuantities; eqNumber++)
159 {
160 totalConcentration_[eqNumber].resize(problem().gridView().size(0));
161 totalConcentration_[eqNumber] = 0;
162 }
163 }
164
169 template<class MultiWriter>
170 void addOutputVtkFields(MultiWriter &writer)
171 {
172 if(problem().vtkOutputLevel()>3)
173 {
174 using ScalarSolutionType = typename GetProp<TypeTag, Properties::SolutionTypes>::ScalarSolution;
175 int size = problem_.gridView().size(0);
176 ScalarSolutionType *totalC1PV = writer.allocateManagedBuffer(size);
177 ScalarSolutionType *totalC2PV = writer.allocateManagedBuffer(size);
178 *totalC1PV = this->totalConcentration_[wCompIdx];
179 *totalC2PV = this->totalConcentration_[nCompIdx];
180 writer.attachCellData(*totalC1PV, "total Concentration w-Comp - vector");
181 writer.attachCellData(*totalC2PV, "total Concentration n-Comp - vector");
182 }
183 }
184
186 void serializeEntity(std::ostream &outstream, const Element &element)
187 {
188 int eIdxGlobal = problem().variables().index(element);
189 outstream << totalConcentration_[wCompIdx][eIdxGlobal]
190 << " " << totalConcentration_[nCompIdx][eIdxGlobal];
191 }
193 void deserializeEntity(std::istream &instream, const Element &element)
194 {
195 int eIdxGlobal = problem().variables().index(element);
196 CellData& cellData = problem().variables().cellData(eIdxGlobal);
197 instream >> totalConcentration_[wCompIdx][eIdxGlobal]
198 >> totalConcentration_[nCompIdx][eIdxGlobal];
199 cellData.setMassConcentration(wCompIdx, totalConcentration_[wCompIdx][eIdxGlobal]);
200 cellData.setMassConcentration(nCompIdx, totalConcentration_[nCompIdx][eIdxGlobal]);
201 }
202
211 void getTransportedQuantity(TransportSolutionType& transportedQuantity)
212 {
213 // resize update vector and set to zero
214 transportedQuantity.resize((getPropValue<TypeTag, Properties::NumEq>() - 1));
215 for(int compIdx = 0; compIdx < (getPropValue<TypeTag, Properties::NumEq>() - 1); compIdx++)
216 transportedQuantity[compIdx].resize(problem_.gridView().size(0));
217
218 transportedQuantity = totalConcentration_;
219 }
220
230 Scalar& totalConcentration(int compIdx, int eIdxGlobal)
231 {
232 return totalConcentration_[compIdx][eIdxGlobal][0];
233 }
234
235 void getSource(Scalar& update, const Element& element, CellData& cellDataI)
236 {}
237
238
244 template<class DataEntry>
245 bool inPhysicalRange(DataEntry& entry)
246 {
247 int numComp = getPropValue<TypeTag, Properties::NumEq>() - 1;
248 for(int compIdx = 0; compIdx < numComp; compIdx++)
249 {
250 if (entry[compIdx] < -1.0e-6)
251 {
252 return false;
253 }
254 }
255 return true;
256 }
257
260 {
261 return localTimeStepping_;
262 }
263
265
274 switchNormals(getParam<bool>("Impet.SwitchNormals")), accumulatedDt_(0),
275 dtThreshold_(1e-6), subCFLFactor_(1.0)
276 {
277 restrictFluxInTransport_ = getParam<int>("Impet.RestrictFluxInTransport", 0);
278 regulateBoundaryPermeability = getPropValue<TypeTag, Properties::RegulateBoundaryPermeability>();
280 minimalBoundaryPermeability = getParam<Scalar>("SpatialParams.MinBoundaryPermeability");
281
282 Scalar cFLFactor = getParam<Scalar>("Impet.CFLFactor");
283 using std::min;
284 subCFLFactor_ = min(getParam<Scalar>("Impet.SubCFLFactor"), cFLFactor);
285 verbosity_ = getParam<int>("TimeManager.SubTimestepVerbosity");
286
288
290 std::cout<<"max CFL-Number of "<<cFLFactor<<", max Sub-CFL-Number of "
291 <<subCFLFactor_<<": Enable local time-stepping!" << std::endl;
292 }
293
295 {
296 }
297
298protected:
299 TransportSolutionType totalConcentration_;
300 Problem& problem_;
301 bool impet_;
303
305 static const int pressureType = getPropValue<TypeTag, Properties::PressureFormulation>();
316 const Scalar dtThreshold_;
318 std::vector<LocalTimesteppingData> timeStepData_;
319
320 void updatedTargetDt_(Scalar &dt);
321
323 {
324 timeStepData_.clear();
325 accumulatedDt_ = 0;
326 }
327
331
332
333private:
335 Implementation &asImp_()
336 { return *static_cast<Implementation *>(this); }
337
339 const Implementation &asImp_() const
340 { return *static_cast<const Implementation *>(this); }
341};
342
343
361template<class TypeTag>
362void FVTransport2P2C<TypeTag>::update(const Scalar t, Scalar& dt,
363 TransportSolutionType& updateVec, bool impet)
364{
365 // initialize dt very large
366 dt = 1E100;
367
368 unsigned int size = problem_.gridView().size(0);
369 if (localTimeStepping_)
370 {
371 if (this->timeStepData_.size() != size)
372 this->timeStepData_.resize(size);
373 }
374 // store if we do update Estimate for flux functions
375 impet_ = impet;
376 averagedFaces_ = 0;
377
378 // resize update vector and set to zero
379 updateVec.resize(getPropValue<TypeTag, Properties::NumComponents>());
380 updateVec[wCompIdx].resize(problem_.gridView().size(0));
381 updateVec[nCompIdx].resize(problem_.gridView().size(0));
382 updateVec[wCompIdx] = 0;
383 updateVec[nCompIdx] = 0;
384
385 // Cell which restricts time step size
386 int restrictingCell = -1;
387
388 ComponentVector entries(0.);
389 EntryType timestepFlux(0.);
390 // compute update vector
391 for (const auto& element : elements(problem().gridView()))
392 {
393 // get cell infos
394 int eIdxGlobalI = problem().variables().index(element);
395 CellData& cellDataI = problem().variables().cellData(eIdxGlobalI);
396
397 // some variables for time step calculation
398 double sumfactorin = 0;
399 double sumfactorout = 0;
400
401 // run through all intersections with neighbors and boundary
402 for (const auto& intersection : intersections(problem().gridView(), element))
403 {
404 int indexInInside = intersection.indexInInside();
405
406 /****** interior face *****************/
407 if (intersection.neighbor())
408 asImp_().getFlux(entries, timestepFlux, intersection, cellDataI);
409
410 /****** Boundary Face *****************/
411 if (intersection.boundary())
412 asImp_().getFluxOnBoundary(entries, timestepFlux, intersection, cellDataI);
413
414
415 if (localTimeStepping_)
416 {
417 LocalTimesteppingData& localData = timeStepData_[eIdxGlobalI];
418 if (localData.faceTargetDt[indexInInside] < accumulatedDt_ + dtThreshold_)
419 {
420 localData.faceFluxes[indexInInside] = entries;
421 }
422 }
423 else
424 {
425 // add to update vector
426 updateVec[wCompIdx][eIdxGlobalI] += entries[wCompIdx];
427 updateVec[nCompIdx][eIdxGlobalI] += entries[nCompIdx];
428 }
429
430 // for time step calculation
431 sumfactorin += timestepFlux[0];
432 sumfactorout += timestepFlux[1];
433
434 }// end all intersections
435
436 if (localTimeStepping_)
437 {
438 LocalTimesteppingData& localData = timeStepData_[eIdxGlobalI];
439 for (int i=0; i < 2*dim; i++)
440 {
441 updateVec[wCompIdx][eIdxGlobalI] += localData.faceFluxes[i][wCompIdx];
442 updateVec[nCompIdx][eIdxGlobalI] += localData.faceFluxes[i][nCompIdx];
443 }
444 }
445
446 /*********** Handle source term ***************/
447 PrimaryVariables q(NAN);
448 problem().source(q, element);
449 updateVec[wCompIdx][eIdxGlobalI] += q[contiWEqIdx];
450 updateVec[nCompIdx][eIdxGlobalI] += q[contiNEqIdx];
451
452 // account for porosity in fluxes for time-step
453 using std::max;
454 sumfactorin = max(sumfactorin,sumfactorout)
455 / problem().spatialParams().porosity(element);
456
457 //calculate time step
458 if (localTimeStepping_)
459 {
460 timeStepData_[eIdxGlobalI].dt = 1./sumfactorin;
461 if ( 1./sumfactorin < dt)
462 {
463 dt = 1./sumfactorin;
464 restrictingCell= eIdxGlobalI;
465 }
466 }
467 else
468 {
469 if ( 1./sumfactorin < dt)
470 {
471 dt = 1./sumfactorin;
472 restrictingCell= eIdxGlobalI;
473 }
474 }
475 } // end grid traversal
476
477#if HAVE_MPI
478 // communicate updated values
480 using ElementMapper = typename SolutionTypes::ElementMapper;
482 for (int i = 0; i < updateVec.size(); i++)
483 {
484 DataHandle dataHandle(problem_.variables().elementMapper(), updateVec[i]);
485 problem_.gridView().template communicate<DataHandle>(dataHandle,
486 Dune::InteriorBorder_All_Interface,
487 Dune::ForwardCommunication);
488 }
489
490 if (localTimeStepping_)
491 {
493
494 TimeDataHandle timeDataHandle(problem_.elementMapper(), timeStepData_);
495 problem_.gridView().template communicate<TimeDataHandle>(timeDataHandle,
496 Dune::InteriorBorder_All_Interface,
497 Dune::ForwardCommunication);
498 }
499
500 dt = problem_.gridView().comm().min(dt);
501#endif
502
503 if(impet)
504 {
505 Dune::dinfo << "Timestep restricted by CellIdx " << restrictingCell << " leads to dt = "
506 <<dt * getParam<Scalar>("Impet.CFLFactor")<< std::endl;
507 if (averagedFaces_ != 0)
508 Dune::dinfo << " Averageing done for " << averagedFaces_ << " faces. "<< std::endl;
509 }
510}
517template<class TypeTag>
518void FVTransport2P2C<TypeTag>::updateTransportedQuantity(TransportSolutionType& updateVector)
519{
520 if (this->enableLocalTimeStepping())
521 this->innerUpdate(updateVector);
522 else
523 updateConcentrations(updateVector, problem().timeManager().timeStepSize());
524}
525
533template<class TypeTag>
534void FVTransport2P2C<TypeTag>::updateTransportedQuantity(TransportSolutionType& updateVector, Scalar dt)
535{
536 updateConcentrations(updateVector, dt);
537}
538
546template<class TypeTag>
547void FVTransport2P2C<TypeTag>::updateConcentrations(TransportSolutionType& updateVector, Scalar dt)
548{
549 // loop thorugh all elements
550 for (int i = 0; i< problem().gridView().size(0); i++)
551 {
552 CellData& cellDataI = problem().variables().cellData(i);
553 for(int compIdx = 0; compIdx < getPropValue<TypeTag, Properties::NumComponents>(); compIdx++)
554 {
555 totalConcentration_[compIdx][i] += (updateVector[compIdx][i]*=dt);
556 cellDataI.setMassConcentration(compIdx, totalConcentration_[compIdx][i]);
557 }
558 }
559}
560
577template<class TypeTag>
578void FVTransport2P2C<TypeTag>::getFlux(ComponentVector& fluxEntries,
579 Dune::FieldVector<Scalar, 2>& timestepFlux,
580 const Intersection& intersection,
581 CellData& cellDataI)
582{
583 fluxEntries = 0.;
584 timestepFlux = 0.;
585 // cell information
586 auto elementI = intersection.inside();
587 int eIdxGlobalI = problem().variables().index(elementI);
588
589 // get position
590 const GlobalPosition globalPos = elementI.geometry().center();
591 const GlobalPosition& gravity_ = problem().gravity();
592 // cell volume, assume linear map here
593 Scalar volume = elementI.geometry().volume();
594
595 // get values of cell I
596 Scalar pressI = problem().pressureModel().pressure(eIdxGlobalI);
597 Scalar pcI = cellDataI.capillaryPressure();
598 DimMatrix K_I(problem().spatialParams().intrinsicPermeability(elementI));
599
600 const auto fluidMatrixInteraction = problem().spatialParams().fluidMatrixInteractionAtPos(elementI.geometry().center());
601 PhaseVector SmobI(0.);
602 using std::max;
603 SmobI[wPhaseIdx] = max((cellDataI.saturation(wPhaseIdx)
604 - fluidMatrixInteraction.pcSwCurve().effToAbsParams().swr())
605 , 1e-2);
606 SmobI[nPhaseIdx] = max((cellDataI.saturation(nPhaseIdx)
607 - fluidMatrixInteraction.pcSwCurve().effToAbsParams().snr())
608 , 1e-2);
609
610 Scalar densityWI (0.), densityNWI(0.);
611 densityWI= cellDataI.density(wPhaseIdx);
612 densityNWI = cellDataI.density(nPhaseIdx);
613
614 // face properties
615 GlobalPosition unitOuterNormal = intersection.centerUnitOuterNormal();
616 if (switchNormals)
617 unitOuterNormal *= -1.0;
618 Scalar faceArea = intersection.geometry().volume();
619
620// // local interface index
621// const int indexInInside = intersection.indexInInside();
622// const int indexInOutside = intersection.indexInOutside();
623
624 // create vector for timestep and for update
625 Dune::FieldVector<Scalar, 2> factor (0.);
626 Dune::FieldVector<Scalar, 2> updFactor (0.);
627
628 PhaseVector potential(0.);
629
630 // access neighbor
631 auto neighbor = intersection.outside();
632 int eIdxGlobalJ = problem().variables().index(neighbor);
633 CellData& cellDataJ = problem().variables().cellData(eIdxGlobalJ);
634
635 // neighbor cell center in global coordinates
636 const GlobalPosition& globalPosNeighbor = neighbor.geometry().center();
637
638 // distance vector between barycenters
639 GlobalPosition distVec = globalPosNeighbor - globalPos;
640 // compute distance between cell centers
641 Scalar dist = distVec.two_norm();
642
643 GlobalPosition unitDistVec(distVec);
644 unitDistVec /= dist;
645
646 // phase densities in neighbor
647 Scalar densityWJ (0.), densityNWJ(0.);
648 densityWJ = cellDataJ.density(wPhaseIdx);
649 densityNWJ = cellDataJ.density(nPhaseIdx);
650
651 // average phase densities with central weighting
652 double densityW_mean = (densityWI + densityWJ) * 0.5;
653 double densityNW_mean = (densityNWI + densityNWJ) * 0.5;
654
655 double pressJ = problem().pressureModel().pressure(eIdxGlobalJ);
656 Scalar pcJ = cellDataJ.capillaryPressure();
657
658 // compute mean permeability
659 DimMatrix meanK_(0.);
660 harmonicMeanMatrix(meanK_,
661 K_I,
662 problem().spatialParams().intrinsicPermeability(neighbor));
663 Dune::FieldVector<Scalar,dim> K(0);
664 meanK_.umv(unitDistVec,K);
665
666 // determine potentials for upwind
667 switch (pressureType)
668 {
669 case pw:
670 {
671 potential[wPhaseIdx] = (pressI - pressJ) / (dist);
672 potential[nPhaseIdx] = (pressI - pressJ + pcI - pcJ) / (dist);
673 break;
674 }
675 case pn:
676 {
677 potential[wPhaseIdx] = (pressI - pressJ - pcI + pcJ) / (dist);
678 potential[nPhaseIdx] = (pressI - pressJ) / (dist);
679 break;
680 }
681 }
682 // add gravity term
683 potential[nPhaseIdx] += (gravity_ * unitDistVec) * densityNW_mean;
684 potential[wPhaseIdx] += (gravity_ * unitDistVec) * densityW_mean;
685
686 potential[wPhaseIdx] *= fabs(K * unitOuterNormal);
687 potential[nPhaseIdx] *= fabs(K * unitOuterNormal);
688
689 // determine upwinding direction, perform upwinding if possible
690 Dune::FieldVector<bool, NumPhases> doUpwinding(true);
691 PhaseVector lambda(0.);
692 for(int phaseIdx = 0; phaseIdx < NumPhases; phaseIdx++)
693 {
694 int contiEqIdx = 0;
695 if(phaseIdx == wPhaseIdx)
696 contiEqIdx = contiWEqIdx;
697 else
698 contiEqIdx = contiNEqIdx;
699
700 if(!impet_ || restrictFluxInTransport_==0) // perform a strict uwpind scheme
701 {
702 if (potential[phaseIdx] > 0.)
703 {
704 lambda[phaseIdx] = cellDataI.mobility(phaseIdx);
705 cellDataI.setUpwindCell(intersection.indexInInside(), contiEqIdx, true);
706
707 }
708 else if(potential[phaseIdx] < 0.)
709 {
710 lambda[phaseIdx] = cellDataJ.mobility(phaseIdx);
711 cellDataI.setUpwindCell(intersection.indexInInside(), contiEqIdx, false);
712 }
713 else
714 {
715 doUpwinding[phaseIdx] = false;
716 cellDataI.setUpwindCell(intersection.indexInInside(), contiEqIdx, false);
717 cellDataJ.setUpwindCell(intersection.indexInOutside(), contiEqIdx, false);
718 }
719 }
720 else // Transport after PE with check on flow direction
721 {
722 bool wasUpwindCell = cellDataI.isUpwindCell(intersection.indexInInside(), contiEqIdx);
723
724 if (potential[phaseIdx] > 0. && wasUpwindCell)
725 lambda[phaseIdx] = cellDataI.mobility(phaseIdx);
726 else if (potential[phaseIdx] < 0. && !wasUpwindCell)
727 lambda[phaseIdx] = cellDataJ.mobility(phaseIdx);
728 // potential direction does not coincide with that of P.E.
729 else if(restrictFluxInTransport_ == 2) // perform central averaging for all direction changes
730 doUpwinding[phaseIdx] = false;
731 else // i.e. restrictFluxInTransport == 1
732 {
733 //check if harmonic weighting is necessary
734 if (potential[phaseIdx] > 0. && (Dune::FloatCmp::ne<Scalar, Dune::FloatCmp::absolute>(cellDataJ.mobility(phaseIdx), 0.0, 1.0e-30) // check if outflow induce neglected (i.e. mob=0) phase flux
735 || (cellDataI.wasRefined() && cellDataJ.wasRefined() && elementI.father() == neighbor.father())))
736 lambda[phaseIdx] = cellDataI.mobility(phaseIdx);
737 else if (potential[phaseIdx] < 0. && (Dune::FloatCmp::ne<Scalar, Dune::FloatCmp::absolute>(cellDataI.mobility(phaseIdx), 0.0, 1.0e-30) // check if inflow induce neglected phase flux
738 || (cellDataI.wasRefined() && cellDataJ.wasRefined() && elementI.father() == neighbor.father())))
739 lambda[phaseIdx] = cellDataJ.mobility(phaseIdx);
740 else
741 doUpwinding[phaseIdx] = false;
742 }
743
744 }
745
746 // do not perform upwinding if so desired
747 if(!doUpwinding[phaseIdx])
748 {
749 //a) no flux if there wont be any flux regardless how to average/upwind
750 if(cellDataI.mobility(phaseIdx)+cellDataJ.mobility(phaseIdx)==0.)
751 {
752 potential[phaseIdx] = 0;
753 continue;
754 }
755
756 //b) perform harmonic averaging
757 fluxEntries[wCompIdx] -= potential[phaseIdx] * faceArea / volume
758 * harmonicMean(cellDataI.massFraction(phaseIdx, wCompIdx) * cellDataI.mobility(phaseIdx) * cellDataI.density(phaseIdx),
759 cellDataJ.massFraction(phaseIdx, wCompIdx) * cellDataJ.mobility(phaseIdx) * cellDataJ.density(phaseIdx));
760 fluxEntries[nCompIdx] -= potential[phaseIdx] * faceArea / volume
761 * harmonicMean(cellDataI.massFraction(phaseIdx, nCompIdx) * cellDataI.mobility(phaseIdx) * cellDataI.density(phaseIdx),
762 cellDataJ.massFraction(phaseIdx, nCompIdx) * cellDataJ.mobility(phaseIdx) * cellDataJ.density(phaseIdx));
763 // c) timestep control
764 // for timestep control : influx
765 using std::max;
766 timestepFlux[0] += max(0.,
767 - potential[phaseIdx] * faceArea / volume
768 * harmonicMean(cellDataI.mobility(phaseIdx),cellDataJ.mobility(phaseIdx)));
769 // outflux
770 timestepFlux[1] += max(0.,
771 potential[phaseIdx] * faceArea / volume
772 * harmonicMean(cellDataI.mobility(phaseIdx),cellDataJ.mobility(phaseIdx))/SmobI[phaseIdx]);
773
774 //d) output
775 if(!(cellDataI.wasRefined() && cellDataJ.wasRefined() && elementI.father() == neighbor.father())
776 && eIdxGlobalI > eIdxGlobalJ) //(only for one side)
777 {
778 averagedFaces_++;
779 #if DUNE_MINIMAL_DEBUG_LEVEL < 3
780 // verbose (only for one side)
781 if(eIdxGlobalI > eIdxGlobalJ)
782 Dune::dinfo << "harmonicMean flux of phase" << phaseIdx <<" used from cell" << eIdxGlobalI<< " into " << eIdxGlobalJ
783 << " ; TE upwind I = "<< cellDataI.isUpwindCell(intersection.indexInInside(), contiEqIdx)
784 << " but pot = "<< potential[phaseIdx] << std::endl;
785 #endif
786 }
787
788 //e) stop further standard calculations
789 potential[phaseIdx] = 0;
790 }
791 }
792
793 // calculate and standardized velocity
794 using std::max;
795 double velocityJIw = max((-lambda[wPhaseIdx] * potential[wPhaseIdx]) * faceArea / volume, 0.0);
796 double velocityIJw = max(( lambda[wPhaseIdx] * potential[wPhaseIdx]) * faceArea / volume, 0.0);
797 double velocityJIn = max((-lambda[nPhaseIdx] * potential[nPhaseIdx]) * faceArea / volume, 0.0);
798 double velocityIJn = max(( lambda[nPhaseIdx] * potential[nPhaseIdx]) * faceArea / volume, 0.0);
799
800 // for timestep control : influx
801 timestepFlux[0] += velocityJIw + velocityJIn;
802
803 double foutw = velocityIJw/SmobI[wPhaseIdx];
804 double foutn = velocityIJn/SmobI[nPhaseIdx];
805 using std::isnan;
806 using std::isinf;
807 if (isnan(foutw) || isinf(foutw) || foutw < 0) foutw = 0;
808 if (isnan(foutn) || isinf(foutn) || foutn < 0) foutn = 0;
809 timestepFlux[1] += foutw + foutn;
810
811 fluxEntries[wCompIdx] +=
812 velocityJIw * cellDataJ.massFraction(wPhaseIdx, wCompIdx) * densityWJ
813 - velocityIJw * cellDataI.massFraction(wPhaseIdx, wCompIdx) * densityWI
814 + velocityJIn * cellDataJ.massFraction(nPhaseIdx, wCompIdx) * densityNWJ
815 - velocityIJn * cellDataI.massFraction(nPhaseIdx, wCompIdx) * densityNWI;
816 fluxEntries[nCompIdx] +=
817 velocityJIw * cellDataJ.massFraction(wPhaseIdx, nCompIdx) * densityWJ
818 - velocityIJw * cellDataI.massFraction(wPhaseIdx, nCompIdx) * densityWI
819 + velocityJIn * cellDataJ.massFraction(nPhaseIdx, nCompIdx) * densityNWJ
820 - velocityIJn * cellDataI.massFraction(nPhaseIdx, nCompIdx) * densityNWI;
821
822
823// //add dispersion
824// for (int phaseIdx = 0; phaseIdx < NumPhases; ++phaseIdx)
825// {
826//
827// //calculate porous media diffusion coefficient
828// // for phases which exist in both finite volumes
829// if (cellDataI.saturation(phaseIdx) <= 0 ||
830// cellDataJ.saturation(phaseIdx) <= 0)
831// {
832// continue;
833// }
834//
835// // calculate tortuosity at the nodes i and j needed
836// // for porous media diffusion coefficient
837// Scalar poroI = problem().spatialParams().porosity(elementI);
838// Scalar poroJ = problem().spatialParams().porosity(neighbor);
839// Scalar tauI =
840// 1.0/(poroI * poroI) *
841// pow(poroI * cellDataI.saturation(phaseIdx), 7.0/3);
842// Scalar tauJ =
843// 1.0/(poroJ * poroJ) *
844// pow(poroJ * cellDataJ.saturation(phaseIdx), 7.0/3);
845// // Diffusion coefficient in the porous medium
846// // -> harmonic mean
847// Scalar porousDiffCoeff
848// = harmonicMean(poroI * cellDataI.saturation(phaseIdx) * tauI
849// * FluidSystem::binaryDiffusionCoefficient(cellDataI.fluidState(), phaseIdx, wCompIdx, nCompIdx),
850// poroJ * cellDataJ.saturation(phaseIdx) * tauJ
851// * FluidSystem::binaryDiffusionCoefficient(cellDataJ.fluidState(), phaseIdx, wCompIdx, nCompIdx));
852// Scalar averagedMolarDensity = (cellDataI.density(phaseIdx) / cellDataI.fluidState().averageMolarMass(phaseIdx)
853// +cellDataJ.density(phaseIdx) / cellDataJ.fluidState().averageMolarMass(phaseIdx))*0.5;
854//
855// for (int compIdx = 0; compIdx < NumComponents; ++compIdx)
856// {
857//
858// Scalar gradx = (cellDataJ.moleFraction(phaseIdx, compIdx)-cellDataI.moleFraction(phaseIdx, compIdx) )/ dist;
859// Scalar gradX = (cellDataJ.massFraction(phaseIdx, compIdx)-cellDataI.massFraction(phaseIdx, compIdx) )/ dist;
860//
861// fluxEntries[compIdx] += gradx * averagedMolarDensity * porousDiffCoeff * faceArea / volume;
862// }
863// }
864
865 return;
866}
867
883template<class TypeTag>
884void FVTransport2P2C<TypeTag>::getFluxOnBoundary(ComponentVector& fluxEntries,
885 Dune::FieldVector<Scalar, 2>& timestepFlux,
886 const Intersection& intersection,
887 const CellData& cellDataI)
888{
889 using std::max;
890 // cell information
891 auto elementI = intersection.inside();
892 int eIdxGlobalI = problem().variables().index(elementI);
893
894 // get position
895 const GlobalPosition globalPos = elementI.geometry().center();
896
897 // cell volume, assume linear map here
898 Scalar volume = elementI.geometry().volume();
899 const GlobalPosition& gravity_ = problem().gravity();
900 // get values of cell I
901 Scalar pressI = problem().pressureModel().pressure(eIdxGlobalI);
902 Scalar pcI = cellDataI.capillaryPressure();
903 DimMatrix K_I(problem().spatialParams().intrinsicPermeability(elementI));
904
905 if(regulateBoundaryPermeability)
906 {
907 int axis = intersection.indexInInside() / 2;
908 if(K_I[axis][axis] < minimalBoundaryPermeability)
909 K_I[axis][axis] = minimalBoundaryPermeability;
910 }
911
912 const auto fluidMatrixInteraction = problem().spatialParams().fluidMatrixInteractionAtPos(elementI.geometry().center());
913 Scalar SwmobI = max((cellDataI.saturation(wPhaseIdx)
914 - fluidMatrixInteraction.pcSwCurve().effToAbsParams().swr())
915 , 1e-2);
916 Scalar SnmobI = max((cellDataI.saturation(nPhaseIdx)
917 - fluidMatrixInteraction.pcSwCurve().effToAbsParams().snr())
918 , 1e-2);
919
920 Scalar densityWI (0.), densityNWI(0.);
921 densityWI= cellDataI.density(wPhaseIdx);
922 densityNWI = cellDataI.density(nPhaseIdx);
923
924 // face properties
925 GlobalPosition unitOuterNormal = intersection.centerUnitOuterNormal();
926 if (switchNormals)
927 unitOuterNormal *= -1.0;
928 Scalar faceArea = intersection.geometry().volume();
929
930 // create vector for timestep and for update
931 Dune::FieldVector<Scalar, 2> factor (0.);
932 Dune::FieldVector<Scalar, 2> updFactor (0.);
933
934 PhaseVector potential(0.);
935 // center of face in global coordinates
936 const GlobalPosition& globalPosFace = intersection.geometry().center();
937
938 // distance vector between barycenters
939 GlobalPosition distVec = globalPosFace - globalPos;
940 // compute distance between cell centers
941 Scalar dist = distVec.two_norm();
942
943 GlobalPosition unitDistVec(distVec);
944 unitDistVec /= dist;
945
946 //instantiate a fluid state
947 FluidState BCfluidState;
948
949 //get boundary type
950 BoundaryTypes bcTypes;
951 problem().boundaryTypes(bcTypes, intersection);
952
953 /********** Dirichlet Boundary *************/
954 if (bcTypes.isDirichlet(contiWEqIdx)) // if contiWEq is Dirichlet, so is contiNEq
955 {
956 //get dirichlet pressure boundary condition
957 PhaseVector pressBound(0.);
958 Scalar pcBound (0.);
959
960 // read boundary values
961 this->evalBoundary(globalPosFace,
962 intersection,
963 BCfluidState,
964 pressBound);
965
966 // determine fluid properties at the boundary
967 Scalar densityWBound = BCfluidState.density(wPhaseIdx);
968 Scalar densityNWBound = BCfluidState.density(nPhaseIdx);
969 Scalar viscosityWBound = FluidSystem::viscosity(BCfluidState, wPhaseIdx);
970 Scalar viscosityNWBound = FluidSystem::viscosity(BCfluidState, nPhaseIdx);
971 if(getPropValue<TypeTag, Properties::EnableCapillarity>())
972 pcBound = (BCfluidState.pressure(nPhaseIdx) - BCfluidState.pressure(wPhaseIdx));
973 // average
974 double densityW_mean = (densityWI + densityWBound) / 2;
975 double densityNW_mean = (densityNWI + densityNWBound) / 2;
976
977 // prepare K
978 Dune::FieldVector<Scalar,dim> K(0);
979 K_I.umv(unitDistVec,K);
980
981 //calculate potential gradient
982 switch (pressureType)
983 {
984 case pw:
985 {
986 potential[wPhaseIdx] = (pressI - pressBound[wPhaseIdx]) / dist;
987 potential[nPhaseIdx] = (pressI + pcI - pressBound[wPhaseIdx] - pcBound)
988 / dist;
989 break;
990 }
991 case pn:
992 {
993 potential[wPhaseIdx] = (pressI - pcI - pressBound[nPhaseIdx] + pcBound)
994 / dist;
995 potential[nPhaseIdx] = (pressI - pressBound[nPhaseIdx]) / dist;
996 break;
997 }
998 }
999 potential[wPhaseIdx] += (gravity_ * unitDistVec) * densityW_mean;
1000 potential[nPhaseIdx] += (gravity_ * unitDistVec) * densityNW_mean;
1001
1002 potential[wPhaseIdx] *= fabs(K * unitOuterNormal);
1003 potential[nPhaseIdx] *= fabs(K * unitOuterNormal);
1004
1005 const auto fluidMatrixInteraction = problem().spatialParams().fluidMatrixInteractionAtPos(elementI.geometry().center());
1006
1007 // do upwinding for lambdas
1008 PhaseVector lambda(0.);
1009 if (potential[wPhaseIdx] >= 0.)
1010 lambda[wPhaseIdx] = cellDataI.mobility(wPhaseIdx);
1011 else
1012 {
1013 if(getPropValue<TypeTag, Properties::BoundaryMobility>()==Indices::satDependent)
1014 lambda[wPhaseIdx] = BCfluidState.saturation(wPhaseIdx) / viscosityWBound;
1015 else
1016 lambda[wPhaseIdx] = fluidMatrixInteraction.krw(BCfluidState.saturation(wPhaseIdx)) / viscosityWBound;
1017 }
1018 if (potential[nPhaseIdx] >= 0.)
1019 lambda[nPhaseIdx] = cellDataI.mobility(nPhaseIdx);
1020 else
1021 {
1022 if(getPropValue<TypeTag, Properties::BoundaryMobility>()==Indices::satDependent)
1023 lambda[nPhaseIdx] = BCfluidState.saturation(nPhaseIdx) / viscosityNWBound;
1024 else
1025 lambda[nPhaseIdx] = fluidMatrixInteraction.krn(BCfluidState.saturation(wPhaseIdx)) / viscosityNWBound;
1026 }
1027 // calculate and standardized velocity
1028
1029 double velocityJIw = max((-lambda[wPhaseIdx] * potential[wPhaseIdx]) * faceArea / volume, 0.0);
1030 double velocityIJw = max(( lambda[wPhaseIdx] * potential[wPhaseIdx]) * faceArea / volume, 0.0);
1031 double velocityJIn = max((-lambda[nPhaseIdx] * potential[nPhaseIdx]) * faceArea / volume, 0.0);
1032 double velocityIJn = max(( lambda[nPhaseIdx] * potential[nPhaseIdx]) * faceArea / volume, 0.0);
1033
1034 // for timestep control
1035 timestepFlux[0] = velocityJIw + velocityJIn;
1036
1037 double foutw = velocityIJw/SwmobI;
1038 double foutn = velocityIJn/SnmobI;
1039 using std::isnan;
1040 using std::isinf;
1041 if (isnan(foutw) || isinf(foutw) || foutw < 0) foutw = 0;
1042 if (isnan(foutn) || isinf(foutn) || foutn < 0) foutn = 0;
1043 timestepFlux[1] = foutw + foutn;
1044
1045 fluxEntries[wCompIdx] =
1046 + velocityJIw * BCfluidState.massFraction(wPhaseIdx, wCompIdx) * densityWBound
1047 - velocityIJw * cellDataI.massFraction(wPhaseIdx, wCompIdx) * densityWI
1048 + velocityJIn * BCfluidState.massFraction(nPhaseIdx, wCompIdx) * densityNWBound
1049 - velocityIJn * cellDataI.massFraction(nPhaseIdx, wCompIdx) * densityNWI ;
1050 fluxEntries[nCompIdx] =
1051 velocityJIw * BCfluidState.massFraction(wPhaseIdx, nCompIdx) * densityWBound
1052 - velocityIJw * cellDataI.massFraction(wPhaseIdx, nCompIdx) * densityWI
1053 + velocityJIn * BCfluidState.massFraction(nPhaseIdx, nCompIdx) * densityNWBound
1054 - velocityIJn * cellDataI.massFraction(nPhaseIdx, nCompIdx) * densityNWI ;
1055 }//end dirichlet boundary
1056 else if (bcTypes.isNeumann(contiWEqIdx))
1057 {
1058 // Convention: outflow => positive sign : has to be subtracted from update vec
1059 PrimaryVariables J(NAN);
1060 problem().neumann(J, intersection);
1061 fluxEntries[wCompIdx] = - J[contiWEqIdx] * faceArea / volume;
1062 fluxEntries[nCompIdx] = - J[contiNEqIdx] * faceArea / volume;
1063
1064 // for timestep control
1065 #define cflIgnoresNeumann
1066 #ifdef cflIgnoresNeumann
1067 timestepFlux[0] = 0;
1068 timestepFlux[1] = 0;
1069 #else
1070 double inflow = updFactor[wCompIdx] / densityW + updFactor[nCompIdx] / densityNW;
1071 if (inflow>0)
1072 {
1073 timestepFlux[0] = updFactor[wCompIdx] / densityW
1074 + updFactor[nCompIdx] / densityNW; // =factor in
1075 timestepFlux[1] = -(updFactor[wCompIdx] / densityW /SwmobI
1076 + updFactor[nCompIdx] / densityNW / SnmobI); // =factor out
1077 }
1078 else
1079 {
1080 timestepFlux[0] = -(updFactor[wCompIdx] / densityW
1081 + updFactor[nCompIdx] / densityNW); // =factor in
1082 timestepFlux[1] = updFactor[wCompIdx] / densityW /SwmobI
1083 + updFactor[nCompIdx] / densityNW / SnmobI; // =factor out
1084 }
1085 #endif
1086 }//end neumann boundary
1087 return;
1088}
1089
1090
1105template<class TypeTag>
1106void FVTransport2P2C<TypeTag>::evalBoundary(GlobalPosition globalPosFace,
1107 const Intersection& intersection,
1108 FluidState &BCfluidState,
1109 PhaseVector &pressBound)
1110{
1111 // prepare a flash solver
1113
1114 auto element = intersection.inside();
1115 // read boundary values
1116 PrimaryVariables primaryVariablesOnBoundary(0.);
1117 problem().dirichlet(primaryVariablesOnBoundary, intersection);
1118
1119 const auto fluidMatrixInteraction = problem().spatialParams().fluidMatrixInteractionAtPos(element.geometry().center());
1120
1121 // read boundary type
1122 typename Indices::BoundaryFormulation bcType;
1123 problem().boundaryFormulation(bcType, intersection);
1124 if (bcType == Indices::saturation)
1125 {
1126 Scalar satBound = primaryVariablesOnBoundary[contiWEqIdx];
1127
1128 if(getPropValue<TypeTag, Properties::EnableCapillarity>())
1129 {
1130 Scalar pcBound = fluidMatrixInteraction.pc(satBound);
1131 switch (pressureType)
1132 {
1133 case pw:
1134 {
1135 pressBound[wPhaseIdx] = primaryVariablesOnBoundary[Indices::pressureEqIdx];
1136 pressBound[nPhaseIdx] = primaryVariablesOnBoundary[Indices::pressureEqIdx] + pcBound;
1137 break;
1138 }
1139 case pn:
1140 {
1141 pressBound[wPhaseIdx] = primaryVariablesOnBoundary[Indices::pressureEqIdx] - pcBound;
1142 pressBound[nPhaseIdx] = primaryVariablesOnBoundary[Indices::pressureEqIdx];
1143 break;
1144 }
1145 }
1146 }
1147 else // capillarity neglected
1148 pressBound[wPhaseIdx] = pressBound[nPhaseIdx] = primaryVariablesOnBoundary[Indices::pressureEqIdx];
1149
1150 flashSolver.saturationFlash2p2c(BCfluidState, satBound, pressBound,
1151 problem().temperatureAtPos(globalPosFace));
1152 }
1153 else if (bcType == Indices::concentration)
1154 {
1155 // saturation and hence pc and hence corresponding pressure unknown
1156 pressBound[wPhaseIdx] = pressBound[nPhaseIdx] = primaryVariablesOnBoundary[Indices::pressureEqIdx];
1157 Scalar Z0Bound = primaryVariablesOnBoundary[contiWEqIdx];
1158 flashSolver.concentrationFlash2p2c(BCfluidState, Z0Bound, pressBound,
1159 problem().temperatureAtPos(globalPosFace));
1160
1161 if(getPropValue<TypeTag, Properties::EnableCapillarity>())
1162 {
1163 Scalar pcBound = fluidMatrixInteraction.pc(BCfluidState.saturation(wPhaseIdx));
1164 int maxiter = 3;
1165 //start iteration loop
1166 for(int iter=0; iter < maxiter; iter++)
1167 {
1168 //prepare pressures to enter flash calculation
1169 switch (pressureType)
1170 {
1171 case pw:
1172 {
1173 pressBound[wPhaseIdx] = primaryVariablesOnBoundary[Indices::pressureEqIdx];
1174 pressBound[nPhaseIdx] = primaryVariablesOnBoundary[Indices::pressureEqIdx]
1175 + pcBound;
1176 break;
1177 }
1178 case pn:
1179 {
1180 pressBound[wPhaseIdx] = primaryVariablesOnBoundary[Indices::pressureEqIdx]
1181 - pcBound;
1182 pressBound[nPhaseIdx] = primaryVariablesOnBoundary[Indices::pressureEqIdx];
1183 break;
1184 }
1185 }
1186
1187 //store old pc
1188 Scalar oldPc = pcBound;
1189 //update with better pressures
1190 flashSolver.concentrationFlash2p2c(BCfluidState, Z0Bound, pressBound,
1191 problem().temperatureAtPos(globalPosFace));
1192 pcBound = fluidMatrixInteraction.pc(BCfluidState.saturation(wPhaseIdx));
1193 // TODO: get right criterion, do output for evaluation
1194 //converge criterion
1195 using std::abs;
1196 if (abs(oldPc - pcBound) < 10.0)
1197 iter = maxiter;
1198 }
1199 }
1200 }
1201 else
1202 DUNE_THROW(Dune::NotImplemented, "Boundary Formulation neither Concentration nor Saturation??");
1203}
1204
1205template<class TypeTag>
1207{
1208 dt = std::numeric_limits<Scalar>::max();
1209
1210 // update target time-step-sizes
1211 for (const auto& element : elements(problem_.gridView()))
1212 {
1213#if HAVE_MPI
1214 if (element.partitionType() != Dune::InteriorEntity)
1215 {
1216 continue;
1217 }
1218#endif
1219
1220 // cell index
1221 int eIdxGlobalI = problem_.variables().index(element);
1222
1223 LocalTimesteppingData& localDataI = timeStepData_[eIdxGlobalI];
1224
1225
1226 using FaceDt = std::unordered_map<int, Scalar>;
1227 FaceDt faceDt;
1228
1229 // run through all intersections with neighbors and boundary
1230 for (const auto& intersection : intersections(problem_.gridView(), element))
1231 {
1232 int indexInInside = intersection.indexInInside();
1233 using std::min;
1234 if (intersection.neighbor())
1235 {
1236 auto neighbor = intersection.outside();
1237 int eIdxGlobalJ = problem_.variables().index(neighbor);
1238
1239 int levelI = element.level();
1240 int levelJ = neighbor.level();
1241
1242
1243 if (eIdxGlobalI < eIdxGlobalJ && levelI <= levelJ)
1244 {
1245 LocalTimesteppingData& localDataJ = timeStepData_[eIdxGlobalJ];
1246
1247 int indexInOutside = intersection.indexInOutside();
1248
1249 if (localDataI.faceTargetDt[indexInInside] < accumulatedDt_ + dtThreshold_
1250 || localDataJ.faceTargetDt[indexInOutside] < accumulatedDt_ + dtThreshold_)
1251 {
1252 Scalar timeStep = min(localDataI.dt, localDataJ.dt);
1253
1254 if (levelI < levelJ)
1255 {
1256 typename FaceDt::iterator it = faceDt.find(indexInInside);
1257 if (it != faceDt.end())
1258 {
1259 it->second = min(it->second, timeStep);
1260 }
1261 else
1262 {
1263 faceDt.insert(std::make_pair(indexInInside, timeStep));
1264 }
1265 }
1266 else
1267 {
1268 localDataI.faceTargetDt[indexInInside] += subCFLFactor_ * timeStep;
1269 localDataJ.faceTargetDt[indexInOutside] += subCFLFactor_ * timeStep;
1270 }
1271
1272 dt = min(dt, timeStep);
1273 }
1274 }
1275 }
1276 else if (intersection.boundary())
1277 {
1278 if (localDataI.faceTargetDt[indexInInside] < accumulatedDt_ + dtThreshold_)
1279 {
1280 localDataI.faceTargetDt[indexInInside] += subCFLFactor_ * localDataI.dt;
1281 dt = min(dt, subCFLFactor_ * localDataI.dt);
1282 }
1283 }
1284 }
1285 if (faceDt.size() > 0)
1286 {
1287 typename FaceDt::iterator it = faceDt.begin();
1288 for (;it != faceDt.end();++it)
1289 {
1290 localDataI.faceTargetDt[it->first] += subCFLFactor_ * it->second;
1291 }
1292
1293 for (const auto& intersection : intersections(problem_.gridView(), element))
1294 {
1295 if (intersection.neighbor())
1296 {
1297 int indexInInside = intersection.indexInInside();
1298
1299 it = faceDt.find(indexInInside);
1300 if (it != faceDt.end())
1301 {
1302 auto neighbor = intersection.outside();
1303 int eIdxGlobalJ = problem_.variables().index(neighbor);
1304
1305 LocalTimesteppingData& localDataJ = timeStepData_[eIdxGlobalJ];
1306
1307 int indexInOutside = intersection.indexInOutside();
1308
1309 localDataJ.faceTargetDt[indexInOutside] += subCFLFactor_ * it->second;
1310 }
1311 }
1312 }
1313 }
1314 }
1315
1316#if HAVE_MPI
1317 // communicate updated values
1318 using SolutionTypes = GetProp<TypeTag, Properties::SolutionTypes>;
1319 using ElementMapper = typename SolutionTypes::ElementMapper;
1320 using TimeDataHandle = VectorCommDataHandleEqual<ElementMapper, std::vector<LocalTimesteppingData>, 0/*elementCodim*/>;
1321
1322 TimeDataHandle timeDataHandle(problem_.elementMapper(), timeStepData_);
1323 problem_.gridView().template communicate<TimeDataHandle>(timeDataHandle,
1324 Dune::InteriorBorder_All_Interface,
1325 Dune::ForwardCommunication);
1326
1327 dt = problem_.gridView().comm().min(dt);
1328#endif
1329}
1330
1331template<class TypeTag>
1332void FVTransport2P2C<TypeTag>::innerUpdate(TransportSolutionType& updateVec)
1333{
1334 if (localTimeStepping_)
1335 {
1336 Scalar realDt = problem_.timeManager().timeStepSize();
1337
1338 Scalar subDt = realDt;
1339
1340 updatedTargetDt_(subDt);
1341
1342 Scalar accumulatedDtOld = accumulatedDt_;
1343 accumulatedDt_ += subDt;
1344
1345 Scalar t = problem_.timeManager().time();
1346
1347 if (accumulatedDt_ < realDt)
1348 {
1349 using std::min;
1350 while(true)
1351 {
1352 Scalar dtCorrection = min(0.0, realDt - accumulatedDt_);
1353 subDt += dtCorrection;
1354
1355 if (verbosity_ > 0)
1356 std::cout<<" Sub-time-step size: "<<subDt<< std::endl;
1357
1358 bool stopTimeStep = false;
1359 int size = problem_.gridView().size(0);
1360 for (int i = 0; i < size; i++)
1361 {
1362 EntryType newVal(0);
1363 int transportedQuantities = getPropValue<TypeTag, Properties::NumEq>() - 1; // NumEq - 1 pressure Eq
1364 for (int eqNumber = 0; eqNumber < transportedQuantities; eqNumber++)
1365 {
1366 newVal[eqNumber] = totalConcentration_[eqNumber][i];
1367 newVal[eqNumber] += updateVec[eqNumber][i] * subDt;
1368 }
1369 if (!asImp_().inPhysicalRange(newVal))
1370 {
1371 stopTimeStep = true;
1372
1373 break;
1374 }
1375 }
1376
1377#if HAVE_MPI
1378 int rank = 0;
1379 if (stopTimeStep)
1380 rank = problem_.gridView().comm().rank();
1381
1382 rank = problem_.gridView().comm().max(rank);
1383 problem_.gridView().comm().broadcast(&stopTimeStep,1,rank);
1384#endif
1385
1386
1387 if (stopTimeStep && accumulatedDtOld > dtThreshold_)
1388 {
1389 problem_.timeManager().setTimeStepSize(accumulatedDtOld);
1390 break;
1391 }
1392 else
1393 {
1394 asImp_().updateTransportedQuantity(updateVec, subDt);
1395 }
1396
1397
1398 if (accumulatedDt_ >= realDt)
1399 {
1400 break;
1401 }
1402
1403 problem_.pressureModel().updateMaterialLaws();
1404 problem_.model().updateTransport(t, subDt, updateVec);
1405
1406 updatedTargetDt_(subDt);
1407
1408 accumulatedDtOld = accumulatedDt_;
1409 accumulatedDt_ += subDt;
1410 }
1411 }
1412 else
1413 {
1414 asImp_().updateTransportedQuantity(updateVec, realDt);
1415 }
1416
1417 resetTimeStepData_();
1418 }
1419}
1420
1421} // end namespace Dumux
1422#endif
Define some often used mathematical functions.
Determines the pressures and saturations of all fluid phases given the total mass of all components.
Contains a class to exchange entries of a vector.
constexpr Scalar harmonicMean(Scalar x, Scalar y, Scalar wx=1.0, Scalar wy=1.0) noexcept
Calculate the (weighted) harmonic mean of two scalar values.
Definition: math.hh:69
void harmonicMeanMatrix(Dune::FieldMatrix< Scalar, m, n > &K, const Dune::FieldMatrix< Scalar, m, n > &Ki, const Dune::FieldMatrix< Scalar, m, n > &Kj)
Calculate the harmonic mean of a fixed-size matrix.
Definition: math.hh:109
T getParam(Args &&... args)
A free function to get a parameter from the parameter tree singleton.
Definition: parameters.hh:348
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 saturation(int phaseIdx) noexcept
I/O name of saturation for multiphase systems.
Definition: name.hh:43
std::string viscosity(int phaseIdx) noexcept
I/O name of viscosity for multiphase systems.
Definition: name.hh:74
Scalar volume(Shape shape, Scalar inscribedRadius)
Returns the volume of a given geometry based on the inscribed radius.
Definition: poreproperties.hh:73
Flash calculation routines for compositional sequential models.
Definition: compositionalflash.hh:51
static void saturationFlash2p2c(FluidState &fluidState, const Scalar saturation, const PhaseVector &phasePressure, const Scalar temperature)
Definition: compositionalflash.hh:219
static void concentrationFlash2p2c(FluidState &fluidState, const Scalar Z0, const PhaseVector &phasePressure, const Scalar temperature)
Definition: compositionalflash.hh:91
A data handle class to exchange entries of a vector.
Definition: vectorcommdatahandle.hh:78
Compositional transport step in a Finite Volume discretization.
Definition: fvtransport.hh:60
virtual void update(const Scalar t, Scalar &dt, TransportSolutionType &updateVec, bool impet=false)
Calculate the update vector and determine timestep size This method calculates the update vector of ...
Definition: fvtransport.hh:362
void evalBoundary(GlobalPosition globalPosFace, const Intersection &intersection, FluidState &BCfluidState, PhaseVector &pressBound)
Evaluate the boundary conditions.
Definition: fvtransport.hh:1106
void deserializeEntity(std::istream &instream, const Element &element)
Function needed for restart option of the transport model: Read in.
Definition: fvtransport.hh:193
bool regulateBoundaryPermeability
Enables regulation of permeability in the direction of a Dirichlet Boundary Condition.
Definition: fvtransport.hh:309
void updateConcentrations(TransportSolutionType &updateVector, Scalar dt)
Updates the concentrations once an update is calculated.
Definition: fvtransport.hh:547
bool localTimeStepping_
Definition: fvtransport.hh:329
Scalar subCFLFactor_
Definition: fvtransport.hh:328
int restrictFluxInTransport_
Restriction of flux on new pressure field if direction reverses from the pressure equation.
Definition: fvtransport.hh:307
FVTransport2P2C(Problem &problem)
Constructs a FVTransport2P2C object Currently, the compositional transport scheme can not be applied ...
Definition: fvtransport.hh:272
const Scalar dtThreshold_
Threshold for sub-time-stepping routine.
Definition: fvtransport.hh:316
void initialize()
Set the initial values before the first pressure equation This method is called before first pressure...
Definition: fvtransport.hh:153
void addOutputVtkFields(MultiWriter &writer)
Write transport variables into the output files.
Definition: fvtransport.hh:170
void getSource(Scalar &update, const Element &element, CellData &cellDataI)
Definition: fvtransport.hh:235
Scalar & totalConcentration(int compIdx, int eIdxGlobal)
Return the the total concentration stored in the transport vector To get real cell values,...
Definition: fvtransport.hh:230
Problem & problem_
Definition: fvtransport.hh:300
void innerUpdate(TransportSolutionType &updateVec)
Definition: fvtransport.hh:1332
int averagedFaces_
number of faces were flux was restricted
Definition: fvtransport.hh:302
void updatedTargetDt_(Scalar &dt)
Definition: fvtransport.hh:1206
void resetTimeStepData_()
Definition: fvtransport.hh:322
Dune::FieldVector< Scalar, 2 > EntryType
Definition: fvtransport.hh:107
bool impet_
indicating if we are in an estimate (false) or real impet (true) step.
Definition: fvtransport.hh:301
void serializeEntity(std::ostream &outstream, const Element &element)
Function needed for restart option of the transport model: Write out.
Definition: fvtransport.hh:186
void getTransportedQuantity(TransportSolutionType &transportedQuantity)
Return the vector of the transported quantity For an immiscible IMPES scheme, this is the saturation....
Definition: fvtransport.hh:211
void getFlux(ComponentVector &fluxEntries, EntryType &timestepFlux, const Intersection &intersection, CellData &cellDataI)
Get flux at an interface between two cells The flux through is calculated according to the underlyin...
Definition: fvtransport.hh:578
Problem & problem()
Acess function for the current problem.
Definition: fvtransport.hh:121
std::vector< LocalTimesteppingData > timeStepData_
Stores data for sub-time-stepping.
Definition: fvtransport.hh:318
int verbosity_
Definition: fvtransport.hh:330
bool inPhysicalRange(DataEntry &entry)
Function to control the abort of the transport-sub-time-stepping depending on a physical parameter ra...
Definition: fvtransport.hh:245
TransportSolutionType totalConcentration_
private vector of transported primary variables
Definition: fvtransport.hh:299
Scalar accumulatedDt_
Current time-interval in sub-time-stepping routine.
Definition: fvtransport.hh:314
virtual ~FVTransport2P2C()
Definition: fvtransport.hh:294
void updateTransportedQuantity(TransportSolutionType &updateVector)
Updates the transported quantity once an update is calculated.
Definition: fvtransport.hh:518
void getFluxOnBoundary(ComponentVector &fluxEntries, EntryType &timestepFlux, const Intersection &intersection, const CellData &cellDataI)
Get flux on Boundary.
Definition: fvtransport.hh:884
static const int pressureType
gives kind of pressure used ( , , )
Definition: fvtransport.hh:305
bool switchNormals
Definition: fvtransport.hh:312
bool enableLocalTimeStepping()
Function to check if local time stepping is activated.
Definition: fvtransport.hh:259
Dune::FieldVector< Scalar, 2 > TimeStepFluxType
Definition: fvtransport.hh:108
Scalar minimalBoundaryPermeability
Minimal limit for the boundary permeability.
Definition: fvtransport.hh:311
Definition: fvtransport.hh:112
Dune::FieldVector< EntryType, 2 *dim > faceFluxes
Definition: fvtransport.hh:113
Dune::FieldVector< Scalar, 2 *dim > faceTargetDt
Definition: fvtransport.hh:114
LocalTimesteppingData()
Definition: fvtransport.hh:116
Scalar dt
Definition: fvtransport.hh:115
Defines the properties required for the sequential 2p2c models.