CompositionFromFugacities.hpp
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  Copyright (C) 2011-2013 by Andreas Lauser
5  Copyright (C) 2012 by Bernd Flemisch
6 
7  This file is part of the Open Porous Media project (OPM).
8 
9  OPM is free software: you can redistribute it and/or modify
10  it under the terms of the GNU General Public License as published by
11  the Free Software Foundation, either version 2 of the License, or
12  (at your option) any later version.
13 
14  OPM is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  GNU General Public License for more details.
18 
19  You should have received a copy of the GNU General Public License
20  along with OPM. If not, see <http://www.gnu.org/licenses/>.
21 */
26 #ifndef OPM_COMPOSITION_FROM_FUGACITIES_HPP
27 #define OPM_COMPOSITION_FROM_FUGACITIES_HPP
28 
30 
31 #include <dune/common/fvector.hh>
32 #include <dune/common/fmatrix.hh>
33 
34 #include <opm/common/ErrorMacros.hpp>
35 #include <opm/common/Exceptions.hpp>
37 
38 #include <limits>
39 
40 namespace Opm {
41 
46 template <class Scalar, class FluidSystem, class Evaluation = Scalar>
48 {
49  enum { numComponents = FluidSystem::numComponents };
50 
51  typedef typename FluidSystem::ParameterCache ParameterCache;
52 
53 public:
54  typedef Dune::FieldVector<Evaluation, numComponents> ComponentVector;
55 
59  template <class FluidState>
60  static void guessInitial(FluidState &fluidState,
61  ParameterCache &/*paramCache*/,
62  unsigned phaseIdx,
63  const ComponentVector &/*fugVec*/)
64  {
65  if (FluidSystem::isIdealMixture(phaseIdx))
66  return;
67 
68  // Pure component fugacities
69  for (unsigned i = 0; i < numComponents; ++ i) {
70  //std::cout << f << " -> " << mutParams.fugacity(phaseIdx, i)/f << "\n";
71  fluidState.setMoleFraction(phaseIdx,
72  i,
73  1.0/numComponents);
74  }
75  }
76 
83  template <class FluidState>
84  static void solve(FluidState &fluidState,
85  ParameterCache &paramCache,
86  unsigned phaseIdx,
87  const ComponentVector &targetFug)
88  {
89  typedef MathToolbox<Evaluation> Toolbox;
90 
91  // use a much more efficient method in case the phase is an
92  // ideal mixture
93  if (FluidSystem::isIdealMixture(phaseIdx)) {
94  solveIdealMix_(fluidState, paramCache, phaseIdx, targetFug);
95  return;
96  }
97 
98  //Dune::FMatrixPrecision<Scalar>::set_singular_limit(1e-25);
99 
100  // save initial composition in case something goes wrong
101  Dune::FieldVector<Evaluation, numComponents> xInit;
102  for (unsigned i = 0; i < numComponents; ++i) {
103  xInit[i] = fluidState.moleFraction(phaseIdx, i);
104  }
105 
107  // Newton method
109 
110  // Jacobian matrix
111  Dune::FieldMatrix<Evaluation, numComponents, numComponents> J;
112  // solution, i.e. phase composition
113  Dune::FieldVector<Evaluation, numComponents> x;
114  // right hand side
115  Dune::FieldVector<Evaluation, numComponents> b;
116 
117  paramCache.updatePhase(fluidState, phaseIdx);
118 
119  // maximum number of iterations
120  const int nMax = 25;
121  for (int nIdx = 0; nIdx < nMax; ++nIdx) {
122  // calculate Jacobian matrix and right hand side
123  linearize_(J, b, fluidState, paramCache, phaseIdx, targetFug);
126 
127  /*
128  std::cout << FluidSystem::phaseName(phaseIdx) << "Phase composition: ";
129  for (unsigned i = 0; i < FluidSystem::numComponents; ++i)
130  std::cout << fluidState.moleFraction(phaseIdx, i) << " ";
131  std::cout << "\n";
132  std::cout << FluidSystem::phaseName(phaseIdx) << "Phase phi: ";
133  for (unsigned i = 0; i < FluidSystem::numComponents; ++i)
134  std::cout << fluidState.fugacityCoefficient(phaseIdx, i) << " ";
135  std::cout << "\n";
136  */
137 
138  // Solve J*x = b
139  x = Toolbox::createConstant(0.0);
140  try { J.solve(x, b); }
141  catch (Dune::FMatrixError e)
142  { throw Opm::NumericalProblem(e.what()); }
143 
144  //std::cout << "original delta: " << x << "\n";
145 
147 
148  /*
149  std::cout << FluidSystem::phaseName(phaseIdx) << "Phase composition: ";
150  for (unsigned i = 0; i < FluidSystem::numComponents; ++i)
151  std::cout << fluidState.moleFraction(phaseIdx, i) << " ";
152  std::cout << "\n";
153  std::cout << "J: " << J << "\n";
154  std::cout << "rho: " << fluidState.density(phaseIdx) << "\n";
155  std::cout << "delta: " << x << "\n";
156  std::cout << "defect: " << b << "\n";
157 
158  std::cout << "J: " << J << "\n";
159 
160  std::cout << "---------------------------\n";
161  */
162 
163  // update the fluid composition. b is also used to store
164  // the defect for the next iteration.
165  Scalar relError = update_(fluidState, paramCache, x, b, phaseIdx, targetFug);
166 
167  if (relError < 1e-9) {
168  const Evaluation& rho = FluidSystem::density(fluidState, paramCache, phaseIdx);
169  fluidState.setDensity(phaseIdx, rho);
170 
171  //std::cout << "num iterations: " << nIdx << "\n";
172  return;
173  }
174  }
175 
176  OPM_THROW(Opm::NumericalProblem,
177  "Calculating the " << FluidSystem::phaseName(phaseIdx)
178  << "Phase composition failed. Initial {x} = {"
179  << xInit
180  << "}, {fug_t} = {" << targetFug << "}, p = " << fluidState.pressure(phaseIdx)
181  << ", T = " << fluidState.temperature(phaseIdx));
182  }
183 
184 
185 protected:
186  // update the phase composition in case the phase is an ideal
187  // mixture, i.e. the component's fugacity coefficients are
188  // independent of the phase's composition.
189  template <class FluidState>
190  static void solveIdealMix_(FluidState &fluidState,
191  ParameterCache &paramCache,
192  unsigned phaseIdx,
193  const ComponentVector &fugacities)
194  {
195  for (unsigned i = 0; i < numComponents; ++ i) {
196  const Evaluation& phi = FluidSystem::fugacityCoefficient(fluidState,
197  paramCache,
198  phaseIdx,
199  i);
200  const Evaluation& gamma = phi * fluidState.pressure(phaseIdx);
202  Valgrind::CheckDefined(gamma);
203  Valgrind::CheckDefined(fugacities[i]);
204  fluidState.setFugacityCoefficient(phaseIdx, i, phi);
205  fluidState.setMoleFraction(phaseIdx, i, fugacities[i]/gamma);
206  };
207 
208  paramCache.updatePhase(fluidState, phaseIdx);
209 
210  const Evaluation& rho = FluidSystem::density(fluidState, paramCache, phaseIdx);
211  fluidState.setDensity(phaseIdx, rho);
212  return;
213  }
214 
215  template <class FluidState>
216  static Scalar linearize_(Dune::FieldMatrix<Evaluation, numComponents, numComponents> &J,
217  Dune::FieldVector<Evaluation, numComponents> &defect,
218  FluidState &fluidState,
219  ParameterCache &paramCache,
220  unsigned phaseIdx,
221  const ComponentVector &targetFug)
222  {
223  typedef MathToolbox<Evaluation> Toolbox;
224 
225  // reset jacobian
226  J = 0;
227 
228  Scalar absError = 0;
229  // calculate the defect (deviation of the current fugacities
230  // from the target fugacities)
231  for (unsigned i = 0; i < numComponents; ++ i) {
232  const Evaluation& phi = FluidSystem::fugacityCoefficient(fluidState,
233  paramCache,
234  phaseIdx,
235  i);
236  const Evaluation& f = phi*fluidState.pressure(phaseIdx)*fluidState.moleFraction(phaseIdx, i);
237  fluidState.setFugacityCoefficient(phaseIdx, i, phi);
238 
239  defect[i] = targetFug[i] - f;
240  absError = std::max(absError, std::abs(Toolbox::value(defect[i])));
241  }
242 
243  // assemble jacobian matrix of the constraints for the composition
244  static const Scalar eps = std::numeric_limits<Scalar>::epsilon()*1e6;
245  for (unsigned i = 0; i < numComponents; ++ i) {
247  // approximately calculate partial derivatives of the
248  // fugacity defect of all components in regard to the mole
249  // fraction of the i-th component. This is done via
250  // forward differences
251 
252  // deviate the mole fraction of the i-th component
253  Evaluation xI = fluidState.moleFraction(phaseIdx, i);
254  fluidState.setMoleFraction(phaseIdx, i, xI + eps);
255  paramCache.updateSingleMoleFraction(fluidState, phaseIdx, i);
256 
257  // compute new defect and derivative for all component
258  // fugacities
259  for (unsigned j = 0; j < numComponents; ++j) {
260  // compute the j-th component's fugacity coefficient ...
261  const Evaluation& phi = FluidSystem::fugacityCoefficient(fluidState,
262  paramCache,
263  phaseIdx,
264  j);
265  // ... and its fugacity ...
266  const Evaluation& f =
267  phi *
268  fluidState.pressure(phaseIdx) *
269  fluidState.moleFraction(phaseIdx, j);
270  // as well as the defect for this fugacity
271  const Evaluation& defJPlusEps = targetFug[j] - f;
272 
273  // use forward differences to calculate the defect's
274  // derivative
275  J[j][i] = (defJPlusEps - defect[j])/eps;
276  }
277 
278  // reset composition to original value
279  fluidState.setMoleFraction(phaseIdx, i, xI);
280  paramCache.updateSingleMoleFraction(fluidState, phaseIdx, i);
281 
282  // end forward differences
284  }
285 
286  return absError;
287  }
288 
289  template <class FluidState>
290  static Scalar update_(FluidState &fluidState,
291  ParameterCache &paramCache,
292  Dune::FieldVector<Evaluation, numComponents> &x,
293  Dune::FieldVector<Evaluation, numComponents> &/*b*/,
294  unsigned phaseIdx,
295  const Dune::FieldVector<Evaluation, numComponents> &targetFug)
296  {
297  typedef MathToolbox<Evaluation> Toolbox;
298 
299  // store original composition and calculate relative error
300  Dune::FieldVector<Evaluation, numComponents> origComp;
301  Scalar relError = 0;
302  Evaluation sumDelta = Toolbox::createConstant(0.0);
303  Evaluation sumx = Toolbox::createConstant(0.0);
304  for (unsigned i = 0; i < numComponents; ++i) {
305  origComp[i] = fluidState.moleFraction(phaseIdx, i);
306  relError = std::max(relError, std::abs(Toolbox::value(x[i])));
307 
308  sumx += Toolbox::abs(fluidState.moleFraction(phaseIdx, i));
309  sumDelta += Toolbox::abs(x[i]);
310  }
311 
312  // chop update to at most 20% change in composition
313  const Scalar maxDelta = 0.2;
314  if (sumDelta > maxDelta)
315  x /= (sumDelta/maxDelta);
316 
317  // change composition
318  for (unsigned i = 0; i < numComponents; ++i) {
319  Evaluation newx = origComp[i] - x[i];
320  // only allow negative mole fractions if the target fugacity is negative
321  if (targetFug[i] > 0)
322  newx = Toolbox::max(0.0, newx);
323  // only allow positive mole fractions if the target fugacity is positive
324  else if (targetFug[i] < 0)
325  newx = Toolbox::min(0.0, newx);
326  // if the target fugacity is zero, the mole fraction must also be zero
327  else
328  newx = 0;
329 
330  fluidState.setMoleFraction(phaseIdx, i, newx);
331  }
332 
333  paramCache.updateComposition(fluidState, phaseIdx);
334 
335  return relError;
336  }
337 
338  template <class FluidState>
339  static Scalar calculateDefect_(const FluidState &params,
340  unsigned phaseIdx,
341  const ComponentVector &targetFug)
342  {
343  Scalar result = 0.0;
344  for (unsigned i = 0; i < numComponents; ++i) {
345  // sum of the fugacity defect weighted by the inverse
346  // fugacity coefficient
347  result += std::abs(
348  (targetFug[i] - params.fugacity(phaseIdx, i))
349  /
350  params.fugacityCoefficient(phaseIdx, i) );
351  };
352  return result;
353  }
354 }; // namespace Opm
355 
356 } // end namespace Opm
357 
358 #endif
static Scalar update_(FluidState &fluidState, ParameterCache &paramCache, Dune::FieldVector< Evaluation, numComponents > &x, Dune::FieldVector< Evaluation, numComponents > &, unsigned phaseIdx, const Dune::FieldVector< Evaluation, numComponents > &targetFug)
Definition: CompositionFromFugacities.hpp:290
Calculates the chemical equilibrium from the component fugacities in a phase.
Definition: CompositionFromFugacities.hpp:47
bool CheckDefined(const T &value OPM_UNUSED)
Make valgrind complain if any of the memory occupied by an object is undefined.
Definition: Valgrind.hpp:74
Definition: MathToolbox.hpp:39
Definition: Air_Mesitylene.hpp:31
static Scalar calculateDefect_(const FluidState &params, unsigned phaseIdx, const ComponentVector &targetFug)
Definition: CompositionFromFugacities.hpp:339
Some templates to wrap the valgrind client request macros.
Evaluation< Scalar, VarSetTag, numVars > max(const Evaluation< Scalar, VarSetTag, numVars > &x1, const Evaluation< Scalar, VarSetTag, numVars > &x2)
Definition: Math.hpp:114
static void solve(FluidState &fluidState, ParameterCache &paramCache, unsigned phaseIdx, const ComponentVector &targetFug)
Calculates the chemical equilibrium from the component fugacities in a phase.
Definition: CompositionFromFugacities.hpp:84
Dune::FieldVector< Evaluation, numComponents > ComponentVector
Definition: CompositionFromFugacities.hpp:54
static Scalar linearize_(Dune::FieldMatrix< Evaluation, numComponents, numComponents > &J, Dune::FieldVector< Evaluation, numComponents > &defect, FluidState &fluidState, ParameterCache &paramCache, unsigned phaseIdx, const ComponentVector &targetFug)
Definition: CompositionFromFugacities.hpp:216
Evaluation< Scalar, VarSetTag, numVars > abs(const Evaluation< Scalar, VarSetTag, numVars > &)
Definition: Math.hpp:41
Evaluation< Scalar, VarSetTag, numVars > min(const Evaluation< Scalar, VarSetTag, numVars > &x1, const Evaluation< Scalar, VarSetTag, numVars > &x2)
Definition: Math.hpp:61
static void solveIdealMix_(FluidState &fluidState, ParameterCache &paramCache, unsigned phaseIdx, const ComponentVector &fugacities)
Definition: CompositionFromFugacities.hpp:190
static void guessInitial(FluidState &fluidState, ParameterCache &, unsigned phaseIdx, const ComponentVector &)
Guess an initial value for the composition of the phase.
Definition: CompositionFromFugacities.hpp:60
A traits class which provides basic mathematical functions for arbitrary scalar floating point values...