3.4
DUNE for Multi-{Phase, Component, Scale, Physics, ...} flow and transport in porous media
fvpressurecompositional.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_FVPRESSURECOMPOSITIONAL_HH
25#define DUMUX_FVPRESSURECOMPOSITIONAL_HH
26
27#include <cmath>
28#include <dune/common/float_cmp.hh>
29
30// dumux environment
31#include <dumux/common/math.hh>
36
37namespace Dumux {
65template<class TypeTag> class FVPressureCompositional
66: public FVPressure<TypeTag>
67{
68 //the model implementation
71
76
78
81
83 enum
84 {
85 dim = GridView::dimension, dimWorld = GridView::dimensionworld
86 };
87 enum
88 {
89 pw = Indices::pressureW,
90 pn = Indices::pressureN
91 };
92 enum
93 {
94 wPhaseIdx = Indices::wPhaseIdx, nPhaseIdx = Indices::nPhaseIdx,
95 wCompIdx = Indices::wCompIdx, nCompIdx = Indices::nCompIdx,
96 contiWEqIdx = Indices::contiWEqIdx, contiNEqIdx = Indices::contiNEqIdx
97 };
98 enum
99 {
100 numPhases = getPropValue<TypeTag, Properties::NumPhases>(),
101 numComponents = getPropValue<TypeTag, Properties::NumComponents>()
102 };
103
104 // using declarations to abbreviate a dune class...
105 using Element = typename GridView::Traits::template Codim<0>::Entity;
106
107 // convenience shortcuts for Vectors/Matrices
108 using GlobalPosition = Dune::FieldVector<Scalar, dimWorld>;
109 using PhaseVector = Dune::FieldVector<Scalar, numPhases>;
110 using ComponentVector = Dune::FieldVector<Scalar, numComponents>;
111
112public:
113 //the variables object is initialized, non-compositional before and compositional after first pressure calculation
114 void initialMaterialLaws(bool compositional);
115
116 //initialization routine to prepare first timestep
117 void initialize(bool solveTwice = false);
118
128 void update()
129 {
130 //pre-transport to estimate update vector
131 Scalar dt_estimate = 0.;
132 Dune::dinfo << "secant guess"<< std::endl;
133 problem_.transportModel().update(problem_.timeManager().time(), dt_estimate, updateEstimate_, false);
134 //last argument false in update() makes sure that this is estimate and no "real" transport step
135
136 // if we just started a new episode, the TS size of the update Estimate is a better
137 // estimate then the size of the last time step
138 if(Dune::FloatCmp::eq<Scalar>(problem_.timeManager().time(), problem_.timeManager().episodeStartTime())
139 && problem_.timeManager().episodeIndex() > 1)
140 problem_.timeManager().setTimeStepSize(dt_estimate*getParam<Scalar>("Impet.CFLFactor"));
141
142 updateEstimate_ *= problem_.timeManager().timeStepSize();
143
144 problem_.pressureModel().assemble(false); Dune::dinfo << "pressure calculation"<< std::endl;
145 problem_.pressureModel().solve();
146
147 return;
148 }
149
150 //constitutive functions are initialized and stored in the variables object
151 void updateMaterialLaws(bool postTimeStep = false);
152
153 //numerical volume derivatives wrt changes in mass, pressure
154 void volumeDerivatives(const GlobalPosition&,const Element& ep);
155
168 template<class MultiWriter>
169 void addOutputVtkFields(MultiWriter &writer)
170 {
171 using ScalarSolutionType = typename GetProp<TypeTag, Properties::SolutionTypes>::ScalarSolution;
172 int size = problem_.gridView().size(0);
173 ScalarSolutionType *pressureW = writer.allocateManagedBuffer(size);
174 ScalarSolutionType *pressureN = writer.allocateManagedBuffer(size);
175 ScalarSolutionType *totalConcentration1 = writer.allocateManagedBuffer (size);
176 ScalarSolutionType *totalConcentration2 = writer.allocateManagedBuffer (size);
177 ScalarSolutionType *pc = writer.allocateManagedBuffer(size);
178 ScalarSolutionType *saturationW = writer.allocateManagedBuffer(size);
179 ScalarSolutionType *densityWetting = writer.allocateManagedBuffer(size);
180 ScalarSolutionType *densityNonwetting = writer.allocateManagedBuffer(size);
181 ScalarSolutionType *mobilityW = writer.allocateManagedBuffer (size);
182 ScalarSolutionType *mobilityNW = writer.allocateManagedBuffer (size);
183 ScalarSolutionType *massfraction1W = writer.allocateManagedBuffer (size);
184 ScalarSolutionType *massfraction1NW = writer.allocateManagedBuffer (size);
185 // numerical stuff
186 ScalarSolutionType *volErr = writer.allocateManagedBuffer (size);
187
188
189 for (int i = 0; i < size; i++)
190 {
191 // basic level 0 output
192 CellData& cellData = problem_.variables().cellData(i);
193 (*pressureW)[i] = cellData.pressure(wPhaseIdx);
194 (*pressureN)[i] = cellData.pressure(nPhaseIdx);
195 (*totalConcentration1)[i] = cellData.massConcentration(wCompIdx);
196 (*totalConcentration2)[i] = cellData.massConcentration(nCompIdx);
197 (*saturationW)[i] = cellData.saturation(wPhaseIdx);
198 // output standard secondary variables
199 if(problem_.vtkOutputLevel()>=1)
200 {
201 (*pc)[i] = cellData.capillaryPressure();
202 (*densityWetting)[i] = cellData.density(wPhaseIdx);
203 (*densityNonwetting)[i] = cellData.density(nPhaseIdx);
204 (*mobilityW)[i] = cellData.mobility(wPhaseIdx);
205 (*mobilityNW)[i] = cellData.mobility(nPhaseIdx);
206 (*massfraction1W)[i] = cellData.massFraction(wPhaseIdx,wCompIdx);
207 (*massfraction1NW)[i] = cellData.massFraction(nPhaseIdx,wCompIdx);
208 (*volErr)[i] = cellData.volumeError();
209 }
210 }
211 writer.attachCellData(*pressureW, "wetting pressure");
212 writer.attachCellData(*pressureN, "nonwetting pressure");
213 writer.attachCellData(*saturationW, "wetting saturation");
214 writer.attachCellData(*totalConcentration1, "C^w from cellData");
215 writer.attachCellData(*totalConcentration2, "C^n from cellData");
216 if(problem_.vtkOutputLevel()>=1)
217 {
218 writer.attachCellData(*pc, "capillary pressure");
219 writer.attachCellData(*densityWetting, "wetting density");
220 writer.attachCellData(*densityNonwetting, "nonwetting density");
221 writer.attachCellData(*mobilityW, "mobility w_phase");
222 writer.attachCellData(*mobilityNW, "mobility nw_phase");
223 std::ostringstream oss1, oss2;
224 oss1 << "mass fraction " << FluidSystem::componentName(0) << " in " << FluidSystem::phaseName(0) << "-phase";
225 writer.attachCellData(*massfraction1W, oss1.str());
226 oss2 << "mass fraction " << FluidSystem::componentName(0) << " in " << FluidSystem::phaseName(1) << "-phase";
227 writer.attachCellData(*massfraction1NW, oss2.str());
228 writer.attachCellData(*volErr, "volume Error");
229 }
230 // verbose output with numerical details
231 if(problem_.vtkOutputLevel()>=2)
232 {
233 // add debug stuff
234 ScalarSolutionType *errorCorrPtr = writer.allocateManagedBuffer (size);
235 ScalarSolutionType *dv_dpPtr = writer.allocateManagedBuffer (size);
236 ScalarSolutionType *dV_dC1Ptr = writer.allocateManagedBuffer (size);
237 ScalarSolutionType *dV_dC2Ptr = writer.allocateManagedBuffer (size);
238 ScalarSolutionType *updEstimate1 = writer.allocateManagedBuffer (size);
239 ScalarSolutionType *updEstimate2 = writer.allocateManagedBuffer (size);
240 for (int i = 0; i < size; i++)
241 {
242 CellData& cellData = problem_.variables().cellData(i);
243 (*errorCorrPtr)[i] = cellData.errorCorrection();
244 (*dv_dpPtr)[i] = cellData.dv_dp();
245 (*dV_dC1Ptr)[i] = cellData.dv(wCompIdx);
246 (*dV_dC2Ptr)[i] = cellData.dv(nCompIdx);
247 (*updEstimate1)[i] = updateEstimate_[0][i];
248 (*updEstimate2)[i] = updateEstimate_[1][i];
249 }
250 writer.attachCellData(*errorCorrPtr, "Error Correction");
251 writer.attachCellData(*dv_dpPtr, "dv_dp");
252 writer.attachCellData(*dV_dC1Ptr, "dV_dC1");
253 writer.attachCellData(*dV_dC2Ptr, "dV_dC2");
254 writer.attachCellData(*updEstimate1, "updEstimate comp 1");
255 writer.attachCellData(*updEstimate2, "updEstimate comp 2");
256 }
257
258 // very verbose output
259 if(problem_.vtkOutputLevel()>=3)
260 {
261 ScalarSolutionType *pressurePV = writer.allocateManagedBuffer(size);
262 ScalarSolutionType *viscosityWetting = writer.allocateManagedBuffer(size);
263 ScalarSolutionType *viscosityNonwetting = writer.allocateManagedBuffer(size);
264 // ScalarSolutionType *nun = writer.allocateManagedBuffer(size);
265 // ScalarSolutionType *nuw = writer.allocateManagedBuffer(size);
266 ScalarSolutionType *faceUpwindW = writer.allocateManagedBuffer(size);
267 ScalarSolutionType *faceUpwindN = writer.allocateManagedBuffer(size);
268 for (int i = 0; i < size; i++)
269 {
270 CellData& cellData = problem_.variables().cellData(i);
271 (*viscosityWetting)[i] = cellData.viscosity(wPhaseIdx);
272 (*viscosityNonwetting)[i] = cellData.viscosity(nPhaseIdx);
273 // (*nun)[i] = cellData.phaseMassFraction(nPhaseIdx);
274 // (*nuw)[i] = cellData.phaseMassFraction(wPhaseIdx);
275 (*faceUpwindW)[i] = 0;
276 (*faceUpwindN)[i] = 0;
277 // run thorugh all local face idx and collect upwind information
278 for(int fIdx = 0; fIdx<cellData.fluxData().size(); fIdx++)
279 {
280 if(cellData.isUpwindCell(fIdx, contiWEqIdx))
281 (*faceUpwindW)[i] += pow(10,static_cast<double>(3-fIdx));
282 if(cellData.isUpwindCell(fIdx, contiNEqIdx))
283 (*faceUpwindN)[i] += pow(10,static_cast<double>(3-fIdx));
284 }
285 }
286 // writer.attachCellData(*nun, "phase mass fraction n-phase");
287 // writer.attachCellData(*nuw, "phase mass fraction w-phase");
288 *pressurePV = this->pressure();
289 writer.attachCellData(*faceUpwindW, "isUpwind w-phase");
290 writer.attachCellData(*faceUpwindN, "isUpwind n-phase");
291 writer.attachCellData(*pressurePV, "pressure (Primary Variable");
292 writer.attachCellData(*viscosityWetting, "wetting viscosity");
293 writer.attachCellData(*viscosityNonwetting, "nonwetting viscosity");
294 }
295 return;
296 }
297
306 void initializationOutput(double pseudoTS = 0.)
307 {
308 std::cout << "Writing debug for current time step\n";
309 initializationOutputWriter_.beginWrite(problem_.timeManager().time() + pseudoTS);
310 asImp_().addOutputVtkFields(initializationOutputWriter_);
311
312#if DUNE_MINIMAL_DEBUG_LEVEL <= 2
313 int size_ = problem_.gridView().size(0);
314 // output porosity, permeability
315 Dune::BlockVector<Dune::FieldVector<double,1> > *poroPtr = initializationOutputWriter_.allocateManagedBuffer (size_);
316 Dune::BlockVector<Dune::FieldVector<double,1> > *permPtr = initializationOutputWriter_.allocateManagedBuffer (size_);
317
318 Dune::BlockVector<Dune::FieldVector<double,1> > poro_(0.), perm_(0.);
319 poro_.resize(size_); perm_.resize(size_);
320 // iterate over all elements of domain
321 for (const auto& element : elements(problem_.gridView()))
322 {
323 // get index
324 int eIdxGlobal = problem_.variables().index(element);
325 poro_[eIdxGlobal] = problem_.spatialParams().porosity(element);
326 perm_[eIdxGlobal] = problem_.spatialParams().intrinsicPermeability(element)[0][0];
327 }
328 *poroPtr = poro_;
329 *permPtr = perm_;
330 initializationOutputWriter_.attachCellData(*poroPtr, "porosity");
331 initializationOutputWriter_.attachCellData(*permPtr, "permeability");
332
333 if(problem_.vtkOutputLevel()>=3)
334 {
335 Dune::BlockVector<Dune::FieldVector<double,1> > *permPtrY = initializationOutputWriter_.allocateManagedBuffer (size_);
336 Dune::BlockVector<Dune::FieldVector<double,1> > *permPtrZ = initializationOutputWriter_.allocateManagedBuffer (size_);
337 Dune::BlockVector<Dune::FieldVector<double,1> > permY_(0.), permZ_(0.);
338 permY_.resize(size_); permZ_.resize(size_);
339 // iterate over all elements of domain
340 for (const auto& element : elements(problem_.gridView()))
341 {
342 // get index
343 int eIdxGlobal = problem_.variables().index(element);
344 if(dim >=2)
345 permY_[eIdxGlobal] = problem_.spatialParams().intrinsicPermeability(element)[1][1];
346 if(dim >=3)
347 permZ_[eIdxGlobal] = problem_.spatialParams().intrinsicPermeability(element)[2][2];
348 }
349 if(dim >=2)
350 {
351 *permPtrY = permY_;
352 initializationOutputWriter_.attachCellData(*permPtrY, "permeability Y");
353 }
354 if(dim >=3)
355 {
356 *permPtrZ = permZ_;
357 initializationOutputWriter_.attachCellData(*permPtrZ, "permeability Z");
358 }
359 }
360#endif
361
363 return;
364 }
366
371 FVPressureCompositional(Problem& problem) : FVPressure<TypeTag>(problem),
372 problem_(problem), initializationOutputWriter_(problem.gridView(),"initOutput2p2c"),
373 maxError_(0.0), incp_(1.0e1)
374 {
375 updateEstimate_.resize(getPropValue<TypeTag, Properties::NumPhases>());
376 for (int i=0; i<getPropValue<TypeTag, Properties::NumPhases>(); i++)
377 updateEstimate_[i].resize(problem.gridView().size(0));
378
379 ErrorTermFactor_ = getParam<Scalar>("Impet.ErrorTermFactor");
380 ErrorTermLowerBound_ = getParam<Scalar>("Impet.ErrorTermLowerBound", 0.2);
381 ErrorTermUpperBound_ = getParam<Scalar>("Impet.ErrorTermUpperBound");
382
383 if (pressureType == Indices::pressureGlobal)
384 {
385 DUNE_THROW(Dune::NotImplemented, "Global Pressure type not supported!");
386 }
387 }
388protected:
389 TransportSolutionType updateEstimate_;
390 Problem& problem_;
391
394
395 Scalar maxError_;
396 Scalar incp_;
401 static constexpr int pressureType = getPropValue<TypeTag, Properties::PressureFormulation>();
402
403private:
404 Problem& problem()
405 {
406 return problem_;
407 }
408 const Problem& problem() const
409 {
410 return problem_;
411 }
412
414 Implementation &asImp_()
415 { return *static_cast<Implementation *>(this);}
416
418 const Implementation &asImp_() const
419 { return *static_cast<const Implementation *>(this);}
420};
421
422
431template<class TypeTag>
433{
434 // inizialize matrix, RHS, and condition Matrix
435 ParentType::initialize();
436
437 // initialguess: set saturations, determine visco and mobility for initial pressure equation
438 // at this moment, the pressure is unknown. Hence, dont regard compositional effects.
439 Dune::dinfo << "first saturation guess"<<std::endl; //=J: initialGuess()
440 problem_.pressureModel().initialMaterialLaws(false);
441 #if DUNE_MINIMAL_DEBUG_LEVEL <= 3
442 // this update produces fluxes on init pressures
443 Scalar dummy;
444 problem_.transportModel().update(0.,dummy, updateEstimate_, false);
445 initializationOutput();
446 #endif
447 Dune::dinfo << "first pressure guess"<<std::endl;
448 problem_.pressureModel().assemble(true);
449 problem_.pressureModel().solve();
450 #if DUNE_MINIMAL_DEBUG_LEVEL <= 3
451 initializationOutput(1e-6);
452 #endif
453 // update the compositional variables (hence true)
454 Dune::dinfo << "first guess for mass fractions"<<std::endl;//= J: transportInitial()
455 problem_.pressureModel().initialMaterialLaws(true);
456
457 // perform concentration update to determine secants
458 Dune::dinfo << "secant guess"<< std::endl;
459 Scalar dt_estimate = 0.;
460 problem_.transportModel().update(0., dt_estimate, updateEstimate_, false);
461 using std::min;
462 dt_estimate = min ( problem_.timeManager().timeStepSize(), dt_estimate);
463 //make sure the right time-step is used by all processes in the parallel case
464 if (problem_.gridView().comm().size() > 1)
465 dt_estimate = problem_.gridView().comm().min(dt_estimate);
466
467 updateEstimate_ *= dt_estimate;
468 //communicate in parallel case
469// problem_.variables().communicateUpdateEstimate();
470 #if DUNE_MINIMAL_DEBUG_LEVEL <= 3
471 initializationOutput(2e-6);
472 #endif
473 // pressure calculation
474 Dune::dinfo << "second pressure guess"<<std::endl;
475 problem_.pressureModel().assemble(false);
476 problem_.pressureModel().solve();
477 #if DUNE_MINIMAL_DEBUG_LEVEL <= 3
478 initializationOutput(3e-6);
479 #endif
480
481 // update the compositional variables
482 problem_.pressureModel().initialMaterialLaws(true);
483
484
485 if (solveTwice)
486 {
487 Dune::BlockVector<Dune::FieldVector<Scalar, 1> > pressureOld(this->pressure());
488 Dune::BlockVector<Dune::FieldVector<Scalar, 1> > pressureDiff;
489 Scalar pressureNorm = 1.; //dummy initialization to perform at least 1 iteration
490 int numIter = 1;
491
492 while (pressureNorm > 1e-5 && numIter < 10)
493 {
494 Scalar dt_dummy=0.; // without this dummy, it will never converge!
495 // update for secants
496 problem_.transportModel().update(0., dt_dummy, updateEstimate_, false); Dune::dinfo << "secant guess"<< std::endl;
497 updateEstimate_ *= dt_estimate;
498
499 //communicate in parallel case
500// problem_.variables().communicateUpdateEstimate();
501
502 // pressure calculation
503 problem_.pressureModel().assemble(false); Dune::dinfo << "pressure guess number "<< numIter <<std::endl;
504 problem_.pressureModel().solve();
505 // update the compositional variables
506 problem_.pressureModel().initialMaterialLaws(true);
507
508 pressureDiff = pressureOld;
509 pressureDiff -= this->pressure();
510 pressureNorm = pressureDiff.infinity_norm();
511 pressureOld = this->pressure();
512 pressureNorm /= pressureOld.infinity_norm();
513
514 numIter++;
515 }
516 }
517 return;
518}
519
528template<class TypeTag>
530{
531 // iterate through leaf grid an evaluate c0 at cell center
532 for (const auto& element : elements(problem_.gridView()))
533 {
534 // get global coordinate of cell center
535 GlobalPosition globalPos = element.geometry().center();
536
537 // assign an Index for convenience
538 int eIdxGlobal = problem_.variables().index(element);
539
540 // get the temperature
541 Scalar temperature_ = problem_.temperatureAtPos(globalPos);
542 CellData& cellData = problem_.variables().cellData(eIdxGlobal);
543 // acess the fluid state and prepare for manipulation
544 FluidState& fluidState = cellData.manipulateFluidState();
546
547 const auto fluidMatrixInteraction = problem_.spatialParams().fluidMatrixInteractionAtPos(element.geometry().center());
548
549 // initial conditions
550 PhaseVector pressure(0.);
551 Scalar sat_0=0.;
552
553 typename Indices::BoundaryFormulation icFormulation;
554 problem_.initialFormulation(icFormulation, element); // get type of initial condition
555
556 if(!compositional) //means that we do the first approximate guess without compositions
557 {
558 // phase pressures are unknown, so start with an exemplary
559 Scalar exemplaryPressure = problem_.referencePressure(element);
560 pressure[wPhaseIdx] = pressure[nPhaseIdx] = this->pressure()[eIdxGlobal] = exemplaryPressure;
561 if (icFormulation == Indices::saturation) // saturation initial condition
562 {
563 sat_0 = problem_.initSat(element);
564 flashSolver.saturationFlash2p2c(fluidState, sat_0, pressure, temperature_);
565 }
566 else if (icFormulation == Indices::concentration) // concentration initial condition
567 {
568 Scalar Z0 = problem_.initConcentration(element);
569 flashSolver.concentrationFlash2p2c(fluidState, Z0, pressure, temperature_);
570 }
571 }
572 else if(compositional) //means we regard compositional effects since we know an estimate pressure field
573 {
574 if (icFormulation == Indices::saturation) // saturation initial condition
575 {
576 //get saturation, determine pc
577 sat_0 = problem_.initSat(element);
578 Scalar pc=0.;
579 if(getPropValue<TypeTag, Properties::EnableCapillarity>())
580 {
581 pc = fluidMatrixInteraction.pc(sat_0);
582 }
583 else
584 pc = 0.;
585
586 //determine phase pressures from primary pressure variable
587 switch (pressureType)
588 {
589 case pw:
590 {
591 pressure[wPhaseIdx] = this->pressure()[eIdxGlobal];
592 pressure[nPhaseIdx] = this->pressure()[eIdxGlobal] +pc;
593 break;
594 }
595 case pn:
596 {
597 pressure[wPhaseIdx] = this->pressure()[eIdxGlobal]-pc;
598 pressure[nPhaseIdx] = this->pressure()[eIdxGlobal];
599 break;
600 }
601 }
602 flashSolver.saturationFlash2p2c(fluidState, sat_0, pressure, temperature_);
603 }
604 else if (icFormulation == Indices::concentration) // concentration initial condition
605 {
606 Scalar Z0 = problem_.initConcentration(element);
607 // If total concentrations are given at the boundary, saturation is unknown.
608 // This may affect pc and hence p_alpha and hence again saturation -> iteration.
609
610 // iterations in case of enabled capillary pressure
611 if(getPropValue<TypeTag, Properties::EnableCapillarity>())
612 {
613 //start with pc from last TS
614 Scalar pc(cellData.capillaryPressure());
615
616 int maxiter = 3;
617 //start iteration loop
618 for(int iter=0; iter < maxiter; iter++)
619 {
620 //determine phase pressures from primary pressure variable
621 switch (pressureType)
622 {
623 case pw:
624 {
625 pressure[wPhaseIdx] = this->pressure()[eIdxGlobal];
626 pressure[nPhaseIdx] = this->pressure()[eIdxGlobal] + pc;
627 break;
628 }
629 case pn:
630 {
631 pressure[wPhaseIdx] = this->pressure()[eIdxGlobal] - pc;
632 pressure[nPhaseIdx] = this->pressure()[eIdxGlobal];
633 break;
634 }
635 }
636
637 //store old pc
638 Scalar oldPc = pc;
639 //update with better pressures
640 flashSolver.concentrationFlash2p2c(fluidState, Z0, pressure, problem_.temperatureAtPos(globalPos));
641 pc = fluidMatrixInteraction.pc(fluidState.saturation(wPhaseIdx));
642 // TODO: get right criterion, do output for evaluation
643 //converge criterion
644 using std::abs;
645 if (abs(oldPc - pc) < 10.0)
646 iter = maxiter;
647
648 pc = fluidMatrixInteraction.pc(fluidState.saturation(wPhaseIdx));
649 }
650 }
651 else // capillary pressure neglected
652 {
653 pressure[wPhaseIdx] = pressure[nPhaseIdx]
654 = this->pressure()[eIdxGlobal];
655 flashSolver.concentrationFlash2p2c(fluidState, Z0, pressure, temperature_);
656 }
657 } //end conc initial condition
658 } //end compositional
659
660 cellData.calculateMassConcentration(problem_.spatialParams().porosity(element));
661
662 problem_.transportModel().totalConcentration(wCompIdx,eIdxGlobal) = cellData.massConcentration(wCompIdx);
663 problem_.transportModel().totalConcentration(nCompIdx,eIdxGlobal) = cellData.massConcentration(nCompIdx);
664
665 // initialize mobilities
666 cellData.setMobility(wPhaseIdx, fluidMatrixInteraction.krw(fluidState.saturation(wPhaseIdx))
667 / cellData.viscosity(wPhaseIdx));
668 cellData.setMobility(nPhaseIdx, fluidMatrixInteraction.krn(fluidState.saturation(wPhaseIdx))
669 / cellData.viscosity(nPhaseIdx));
670
671 // calculate perimeter used as weighting factor
672 if(!compositional)
673 {
674 // run through all intersections with neighbors
675 for (const auto& intersection : intersections(problem_.gridView(), element))
676 {
677 cellData.perimeter()
678 += intersection.geometry().volume();
679 }
680 cellData.globalIdx() = eIdxGlobal;
681
682 // set dv to zero to prevent output errors
683 cellData.dv_dp() = 0.;
684 cellData.dv(wPhaseIdx) = 0.;
685 cellData.dv(nPhaseIdx) = 0.;
686 }
687 // all
688 cellData.reset();
689 }
690 return;
691}
692
700template<class TypeTag>
702{
703 Scalar maxError = 0.;
704 // iterate through leaf grid an evaluate c0 at cell center
705 for (const auto& element : elements(problem().gridView()))
706 {
707 int eIdxGlobal = problem().variables().index(element);
708
709 CellData& cellData = problem().variables().cellData(eIdxGlobal);
710
711 asImp_().updateMaterialLawsInElement(element, postTimeStep);
712
713 using std::max;
714 maxError = max(maxError, fabs(cellData.volumeError()));
715 }
716 if (problem_.gridView().comm().size() > 1)
717 maxError_ = problem_.gridView().comm().max(maxError_);
718
719 maxError_ = maxError/problem().timeManager().timeStepSize();
720 return;
721}
722
735template<class TypeTag>
736void FVPressureCompositional<TypeTag>::volumeDerivatives(const GlobalPosition& globalPos, const Element& element)
737{
738 // cell index
739 int eIdxGlobal = problem_.variables().index(element);
740
741 CellData& cellData = problem_.variables().cellData(eIdxGlobal);
742
743 // get cell temperature
744 Scalar temperature_ = cellData.temperature(wPhaseIdx);
745
746 // initialize an Fluid state and a flash solver
747 FluidState updFluidState;
749
750 /**********************************
751 * a) get necessary variables
752 **********************************/
753 //determine phase pressures for flash calculation
754 PhaseVector pressure(0.);
755 for(int phaseIdx = 0; phaseIdx< numPhases; phaseIdx++)
756 pressure[phaseIdx] = cellData.pressure(phaseIdx);
757
758 // mass of components inside the cell
759 ComponentVector mass(0.);
760 for(int compIdx = 0; compIdx< numComponents; compIdx++)
761 mass[compIdx] = cellData.massConcentration(compIdx);
762
763 // actual fluid volume
764 // see Fritz 2011 (Dissertation) eq.3.76
765 Scalar specificVolume(0.); // = \sum_{\alpha} \nu_{\alpha} / \rho_{\alpha}
766 for(int phaseIdx = 0; phaseIdx< numPhases; phaseIdx++)
767 specificVolume += cellData.phaseMassFraction(phaseIdx) / cellData.density(phaseIdx);
768 Scalar volalt = mass.one_norm() * specificVolume;
769// volalt = cellData.volumeError()+problem_.spatialParams().porosity(element);
770 // = \sum_{\kappa} C^{\kappa} + \sum_{\alpha} \nu_{\alpha} / \rho_{\alpha}
771
772 /**********************************
773 * b) define increments
774 **********************************/
775 // increments for numerical derivatives
776 ComponentVector massIncrement(0.);
777 for(int compIdx = 0; compIdx< numComponents; compIdx++)
778 {
779 massIncrement[compIdx] = updateEstimate_[compIdx][eIdxGlobal];
780 if(fabs(massIncrement[compIdx]) < 1e-8 * cellData.density(compIdx))
781 massIncrement[compIdx] = 1e-8* cellData.density(compIdx); // as phaseIdx = compIdx
782 }
783 Scalar& incp = incp_;
784
785 /**********************************
786 * c) Secant method for derivatives
787 **********************************/
788
789 // numerical derivative of fluid volume with respect to pressure
790 PhaseVector p_(incp);
791 p_ += pressure;
792 Scalar Z0 = mass[0] / mass.one_norm();
793 flashSolver.concentrationFlash2p2c(updFluidState, Z0, p_, temperature_);
794
795 specificVolume=0.; // = \sum_{\alpha} \nu_{\alpha} / \rho_{\alpha}
796 for(int phaseIdx = 0; phaseIdx< numPhases; phaseIdx++)
797 specificVolume += updFluidState.phaseMassFraction(phaseIdx) / updFluidState.density(phaseIdx);
798 Scalar dv_dp = ((mass.one_norm() * specificVolume) - volalt) /incp;
799
800 if (dv_dp>0)
801 {
802 // dV_dp > 0 is unphysical: Try inverse increment for secant
803 Dune::dinfo << "dv_dp larger 0 at Idx " << eIdxGlobal << " , try and invert secant"<< std::endl;
804
805 p_ -= 2*incp;
806 flashSolver.concentrationFlash2p2c(updFluidState, Z0, p_, temperature_);
807
808 specificVolume=0.; // = \sum_{\alpha} \nu_{\alpha} / \rho_{\alpha}
809 for(int phaseIdx = 0; phaseIdx< numPhases; phaseIdx++)
810 specificVolume += updFluidState.phaseMassFraction(phaseIdx) / updFluidState.density(phaseIdx);
811 dv_dp = ((mass.one_norm() * specificVolume) - volalt) /incp;
812
813 // dV_dp > 0 is unphysical: Try inverse increment for secant
814 if (dv_dp>0)
815 {
816 Dune::dwarn << "dv_dp still larger 0 after inverting secant at idx"<< eIdxGlobal<< std::endl;
817 p_ += 2*incp;
818 flashSolver.concentrationFlash2p2c(updFluidState, Z0, p_, temperature_);
819 // neglect effects of phase split, only regard changes in phase densities
820 specificVolume=0.; // = \sum_{\alpha} \nu_{\alpha} / \rho_{\alpha}
821 for(int phaseIdx = 0; phaseIdx< numPhases; phaseIdx++)
822 specificVolume += cellData.phaseMassFraction(phaseIdx) / updFluidState.density(phaseIdx);
823 dv_dp = ((mass.one_norm() * specificVolume) - volalt) /incp;
824 if (dv_dp>0)
825 {
826 std::cout << "dv_dp still larger 0 after both inverts at idx " << eIdxGlobal << std::endl;
827 dv_dp = cellData.dv_dp();
828 }
829 }
830 }
831 cellData.dv_dp()=dv_dp;
832
833 // numerical derivative of fluid volume with respect to mass of components
834 for (int compIdx = 0; compIdx<numComponents; compIdx++)
835 {
836 mass[compIdx] += massIncrement[compIdx];
837 Z0 = mass[0] / mass.one_norm();
838
839 flashSolver.concentrationFlash2p2c(updFluidState, Z0, pressure, temperature_);
840
841 specificVolume=0.; // = \sum_{\alpha} \nu_{\alpha} / \rho_{\alpha}
842 for(int phaseIdx = 0; phaseIdx< numPhases; phaseIdx++)
843 specificVolume += updFluidState.phaseMassFraction(phaseIdx) / updFluidState.density(phaseIdx);
844
845 cellData.dv(compIdx) = ((mass.one_norm() * specificVolume) - volalt) / massIncrement[compIdx];
846 mass[compIdx] -= massIncrement[compIdx];
847
848 //check routines if derivatives are meaningful
849 using std::isnan;
850 using std::isinf;
851 if (isnan(cellData.dv(compIdx)) || isinf(cellData.dv(compIdx)) )
852 {
853 DUNE_THROW(Dune::MathError, "NAN/inf of dV_dm. If that happens in first timestep, try smaller firstDt!");
854 }
855 }
856 cellData.volumeDerivativesAvailable(true);
857}
858
859}//end namespace Dumux
860#endif
Define some often used mathematical functions.
Simplifies writing multi-file VTK datasets.
Determines the pressures and saturations of all fluid phases given the total mass of all components.
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 pressure(int phaseIdx) noexcept
I/O name of pressure for multiphase systems.
Definition: name.hh:34
Simplifies writing multi-file VTK datasets.
Definition: vtkmultiwriter.hh:148
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
The finite volume model for the solution of the compositional pressure equation.
Definition: fvpressurecompositional.hh:67
Scalar incp_
Increment for the volume derivative w.r.t pressure.
Definition: fvpressurecompositional.hh:396
void volumeDerivatives(const GlobalPosition &, const Element &ep)
Partial derivatives of the volumes w.r.t. changes in total concentration and pressure.
Definition: fvpressurecompositional.hh:736
Scalar ErrorTermLowerBound_
Handling of error term: lower bound for error dampening.
Definition: fvpressurecompositional.hh:398
void initializationOutput(double pseudoTS=0.)
Write additional debug info in a special writer.
Definition: fvpressurecompositional.hh:306
Scalar maxError_
Maximum volume error of all cells.
Definition: fvpressurecompositional.hh:395
void addOutputVtkFields(MultiWriter &writer)
Write data files.
Definition: fvpressurecompositional.hh:169
void updateMaterialLaws(bool postTimeStep=false)
Updates secondary variables.
Definition: fvpressurecompositional.hh:701
Problem & problem_
Definition: fvpressurecompositional.hh:390
FVPressureCompositional(Problem &problem)
Constructs a FVPressureCompositional object.
Definition: fvpressurecompositional.hh:371
Scalar ErrorTermFactor_
Handling of error term: relaxation factor.
Definition: fvpressurecompositional.hh:397
static constexpr int pressureType
gives kind of pressure used ( , , )
Definition: fvpressurecompositional.hh:401
Scalar ErrorTermUpperBound_
Definition: fvpressurecompositional.hh:399
void initialMaterialLaws(bool compositional)
initializes the fluid distribution and hereby the variables container
Definition: fvpressurecompositional.hh:529
VtkMultiWriter< GridView > initializationOutputWriter_
output for the initialization procedure
Definition: fvpressurecompositional.hh:393
void update()
Compositional pressure solution routine: update estimate for secants, assemble, solve.
Definition: fvpressurecompositional.hh:128
TransportSolutionType updateEstimate_
Update estimate for changes in volume for the pressure equation.
Definition: fvpressurecompositional.hh:389
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
Defines the properties required for the sequential 2p2c models.
Finite Volume Diffusion Model.