Flow123d  jenkins-Flow123d-windows32-release-multijob-51
isotherm.hh
Go to the documentation of this file.
1 /*
2  * isotherm.hh
3  *
4  * Created on: Mar 7, 2013
5  * Author: jb
6  *
7  *
8  * Other possible transformation of coordinates:
9  *
10  * c_l - conc. liquid
11  * c_s - conc. solid
12  * c_t = h_l * c_l + h_s * c_s = X' + Y'
13  * X = c_t
14  * Y = X' - Y' = h_l * c_l - h_s * c_s
15  *
16  * A) make table for function c_t -> Y
17  * 1) for given c_t solve nonlinear eq.
18  * h_l * c_l + h_s * f(c_l) = c_t
19  *
20  * 2) from c_l compute
21  * Y = c_t - 2* h_l * c_l
22  *
23  *
24  * B) calculation of new c_l, c_s from c_t using table:
25  *
26  * 1) use table to get value of Y for given c_t
27  * 2) compute:
28  * c_l = (c_t - Y) / (2 * h_l)
29  * c_s = (c_t + Y) / (2 * h_s)
30  *
31  * ========================
32  * The transformation currently in use transforms
33  * pair (c_l, c_s) directly to (c_t, W) in ortogonal way.
34  * Proposed transformation first scale (c_l, c_s) to (X',Y')
35  * and then transform scaled coordinates in ortogonal way.
36  *
37  */
38 
39 #ifndef SORPTION_IMPL_HH_
40 #define SORPTION_IMPL_HH_
41 
42 #include <vector>
43 #include <input/input_type.hh>
44 #include <boost/math/tools/roots.hpp>
45 #include "fields/field.hh"
46 
47 
48 
49 /**
50  * Convergence criteria for interval based nonlinear solver. It is functor, that
51  * returns true if bounds a,b of the solution are close enough.
52  * We use relative criteria.
53  */
54 template <class T>
55 class tolerance
56 {
57 public:
58  tolerance(unsigned bits)
59  {
60  BOOST_MATH_STD_USING
61  eps = T(ldexp(1.0F, 1-bits));
62  }
63  bool operator()(const T& a, const T& b)
64  {
65  BOOST_MATH_STD_USING
66  return fabs(a - b) <= (eps * (std::max)(fabs(a), fabs(b)));
67  }
68 private:
69  T eps;
70 };
71 
72 
73 
74 /**
75  * Functor for linear isotherm
76  */
77 class Linear {
78 public:
79  /// Constructor to set parameters
80  Linear(double mult_coef) : mult_coef_(mult_coef) {}
81  /// Isotherm definition.
82  inline double operator()(double x) {
83  return (mult_coef_*x);
84  }
85 private:
86  /// Parameters of the isotherm.
87  double mult_coef_;
88 };
89 
90 
91 
92 /**
93  * Functor for Langmuir isotherm.
94  */
95 class Langmuir {
96 public:
97  /// Constructor to set parameters
98  Langmuir( double mult_coef, double alpha) : mult_coef_(mult_coef), alpha_(alpha) {}
99  /// Isotherm definition.
100  inline double operator()( double x) {
101  return (mult_coef_*(alpha_ * x)/(alpha_ *x + 1));
102  }
103 
104 private:
105  /// Parameters of the isotherm.
106  double mult_coef_;
107  double alpha_;
108 };
109 
110 
111 
112 /**
113  * Functor for Freundlich isotherm.
114  */
115 class Freundlich {
116 public:
117  /// Constructor to set parameters
118  Freundlich(double mult_coef, double exponent) : mult_coef_(mult_coef), exponent_(exponent){}
119  /// Isotherm definition.
120  inline double operator()(double x) {
121  return (mult_coef_*pow(x, exponent_));
122  }
123 
124 private:
125  /// Parameters of the isotherm.
126  double mult_coef_;
127  double exponent_;
128 };
129 
130 
131 /**
132 * Class describing one isotherm with possibly precalculated interpolation table.
133 */
134 class Isotherm {
135 public:
136 
137  /// Type of adsorption isotherm.
139  none = 0,
140  linear = 1,
143  };
144 
145  /// Pair of soluted and adsorbed concentration.
146  struct ConcPair {
147  ConcPair(double x, double y) : fluid(x), solid(y) {}
148  double fluid;
149  double solid;
150  };
151 
152  /**
153  * Setting adsorption parameters for general isotherm. These parameters are then used either
154  * for creation of the interpolation table via @p make_table method or just one adsorption is computed
155  * through @p compute method. Provided parameters are:
156  * @param sorption_type - type of isotherm
157  * @param limited_solubility_on - true if @p c_aqua_limit is solubility limit
158  * @param aqua_density - density of the liquid phase
159  * @param scale_aqua - generalized porosity, fraction of the space with liquid phase
160  * @param scale_sorbed - fraction of the space with the solid to which we adsorp
161  * @param c_aqua_limit - limit for interpolation table, possibly solubility limit
162  * @param mult_coef - multiplicative coefficient of the isotherm (all isotherms have one)
163  * @param secodn_coef - possibly second parameter of the isotherm
164  */
165  inline void reinit(enum SorptionType sorption_type, bool limited_solubility_on,
166  double aqua_density, double scale_aqua, double scale_sorbed,
167  double c_aqua_limit, double mult_coef, double second_coef);
168  /**
169  * Create interpolation table for isotherm in rotated coordinate system with X axes given by total mass in
170  * both phases. Size of the table is the only parameter. Currently we support only linear interpolation.
171  * @p reinit has to be called just before this method.
172  */
173  void make_table(int n_points);
174 
175  /**
176  * Direct calculation of the equilibrium adsorption using a non-linear solver.
177  * @p reinit has to be called just before this method.
178  */
179  inline void compute(double &c_aqua, double &c_sorbed);
180 
181  /**
182  * Use interpolation to determine equilibrium state.
183  * Assumes previous call to @p make_table. If total mass is larger then table limit we either
184  * call @p precipitate (limit_solubility_on) or use direct computation.
185  */
186  inline void interpolate(double &c_aqua, double &c_sorbed);
187 
188  /**
189  * Returns true if interpolation table is created.
190  */
191  inline bool is_precomputed(void) {
192  return interpolation_table.size() != 0;
193  }
194 
195 protected:
196  /**
197  * Implementation of interpolation construction for particular isotherm functor.
198  */
199  template<class Func>
200  void make_table(const Func &isotherm, int n_points);
201  /**
202  * Find new values for concentrations in @p c_pair that has same total mass and lies on the
203  * @p isotherm (functor object).
204  */
205  template<class Func>
206  inline ConcPair solve_conc(ConcPair c_pair, const Func &isotherm);
207  /**
208  * Dispatch isotherm type and use appropriate template.
209  */
210  inline ConcPair solve_conc(ConcPair conc);
211  /**
212  * Update concentrations using interopolation.
213  */
214  inline ConcPair compute_projection( ConcPair conc );
215  /**
216  * Modify concentrations after adsorption for limited solubility.
217  */
218  inline ConcPair precipitate( ConcPair conc );
219 
220  /****************************************
221  * Data
222  */
223 
224  /// Type of isotherm
226 
227  /// Multiplication parameter of the isotherm
228  double mult_coef_;
229 
230  /// Optional secod parameter of the isotherm
231  double second_coef_;
232 
233  /* Concentration in liquid phase for limit of the interpolation table, or
234  * solubility limit.
235  */
236  double table_limit_;
237 
238  /// Solubility limit flag
240 
241  /// density of the solvent
242  double rho_aqua_;
243  /// coefficient that convert soluted concentration to mass; porosity = k_W, originally rho_aqua*porosity = k_W
244  double scale_aqua_;
245  /// coefficient that convert adsorbed molar concentration to mass; molar_weight * rho_rock * (1 - porosity) = k_H
247  /// reciprocal values
249  /**
250  * Interpolation table of isotherm in the rotated coordinates.
251  * The X axes of rotated system is total mass, the Y axes is perpendicular.
252  */
254  /**
255  * Step on the rotated X axes (total mass).
256  */
258 
259 };
260 
261 
262 /**
263  * Functor for solved equation in form F(x) ==0.
264  * Function @p func is an isotherm functor object in concentration based coordinated system.
265  * We solve the equation in modified system (scaled and rotated) for better numerical stability.
266  * The solved equation reads:
267  * F(X) -Y =0, where
268  * X is total mass , Y
269  */
270 template <class Func>
272 {
273 public:
274  CrossFunction(const Func &func_, double total_mass, double scale_aqua, double scale_sorbed, double rho_aqua)
275  : func(func_), total_mass_(total_mass),
276  scale_sorbed_(scale_sorbed), scale_aqua_(scale_aqua), rho_aqua_(rho_aqua)
277  {}
278 
279  double operator()( double conc_aqua)
280  {
281  // that is the selected isotherm // scale_sorbed_ * func( conc_aqua ) + scale_aqua_ * conc_aqua - total_mass_
282  return scale_sorbed_*func( conc_aqua/rho_aqua_) + (scale_aqua_) * conc_aqua - total_mass_;
283  }
284 private:
285  Func func;
287 };
288 
289 
290 /*****************************************************************************************************************
291  * IMPLEMENTATION
292  */
293 
294 
295 inline void Isotherm::reinit(enum SorptionType adsorption_type, bool limited_solubility_on,
296  double rho_aqua, double scale_aqua, double scale_sorbed,
297  double c_aqua_limit, double mult_coef, double second_coef)
298 {
299  adsorption_type_ = adsorption_type;
300  rho_aqua_ = rho_aqua;
301  scale_aqua_ = scale_aqua;
302  scale_sorbed_ = scale_sorbed;
305  table_limit_ = c_aqua_limit;
306  limited_solubility_on_ = limited_solubility_on;
307  mult_coef_ = mult_coef;
308  second_coef_ = second_coef;
309 
310 }
311 
312 
313 
314 inline void Isotherm::compute( double &c_aqua, double &c_sorbed ) {
315  ConcPair c_pair(c_aqua, c_sorbed);
316  ConcPair result(0,0);
317 
318  if (limited_solubility_on_ && (c_pair.fluid > table_limit_)) {
319  result = precipitate( c_pair );
320  } else {
321  result = solve_conc( c_pair );
322  }
323  c_aqua=result.fluid;
324  c_sorbed=result.solid;
325 }
326 
327 
328 inline void Isotherm::interpolate( double &c_aqua, double &c_sorbed ) {
329  ConcPair c_pair(c_aqua, c_sorbed);
330  ConcPair result(0,0);
331 
332  result = compute_projection( c_pair );
333 
334  c_aqua=result.fluid;
335  c_sorbed=result.solid;
336 }
337 
338 
339 
341  double total_mass = (scale_aqua_* c_pair.fluid + scale_sorbed_ * c_pair.solid);
342  double total_mass_steps = total_mass / total_mass_step_;
343  int total_mass_idx = static_cast <int>(std::floor(total_mass_steps));
344 
345  if ( total_mass_idx < 0 ) {xprintf(UsrErr,"total_mass %f seems to have negative value.\n", total_mass); }
346  if ((unsigned int)(total_mass_idx) < (interpolation_table.size() - 1) ) {
347  double rot_sorbed = interpolation_table[total_mass_idx] + (total_mass_steps - total_mass_idx)*(interpolation_table[total_mass_idx+1] - interpolation_table[total_mass_idx]);
348  return ConcPair( (total_mass * inv_scale_aqua_ - rot_sorbed * inv_scale_sorbed_),
349  (total_mass * inv_scale_sorbed_ + rot_sorbed * inv_scale_aqua_) );
350  } else {
352  return precipitate( c_pair );
353  } else {
354  return solve_conc( c_pair );
355  }
356  }
357 }
358 
359 
360 
362  double total_mass = (scale_aqua_*c_pair.fluid + scale_sorbed_ * c_pair.solid);
363  return ConcPair( table_limit_,
364  (total_mass - scale_aqua_ * table_limit_)/scale_sorbed_ );
365 }
366 
367 
368 
369 template<class Func>
370 inline Isotherm::ConcPair Isotherm::solve_conc(Isotherm::ConcPair c_pair, const Func &isotherm)
371 {
372  boost::uintmax_t max_iter = 20;
373  tolerance<double> toler(30);
374  double total_mass = (scale_aqua_*c_pair.fluid + scale_sorbed_ * c_pair.solid);
375  double mass_limit = table_limit_*scale_aqua_ + const_cast<Func &>(isotherm)(table_limit_ / this->rho_aqua_)*scale_sorbed_;
376  double upper_solution_bound;
377 
378  if(total_mass >= mass_limit)
379  {
380  mass_limit = total_mass;
381  }
382  upper_solution_bound = mass_limit / scale_aqua_;
383  CrossFunction<Func> eq_func(isotherm, total_mass, scale_aqua_, scale_sorbed_, this->rho_aqua_);
384  pair<double,double> solution;
385  if (total_mass > 0) // here should be probably some kind of tolerance instead of "0"
386  solution = boost::math::tools::toms748_solve(eq_func, 0.0, upper_solution_bound, toler, max_iter);
387  double difference;
388  difference = (solution.second - solution.first)/2;
389  double c_aqua = solution.first + difference;
390  return ConcPair( c_aqua,
391  (total_mass - scale_aqua_ * c_aqua)/scale_sorbed_);
392 }
393 
394 
396 {
397  switch(adsorption_type_)
398  {
399  case 0: // none
400  {
401  Linear obj_isotherm(0.0);
402  return solve_conc( conc, obj_isotherm);
403  }
404  break;
405  case 1: // linear:
406  {
407  Linear obj_isotherm(mult_coef_);
408  return solve_conc( conc, obj_isotherm);
409  }
410  break;
411  case 2: // freundlich
412  {
413  Freundlich obj_isotherm(mult_coef_, second_coef_);
414  return solve_conc( conc, obj_isotherm);
415  }
416  break;
417  case 3: // langmuir:
418  {
419  Langmuir obj_isotherm(mult_coef_, second_coef_);
420  return solve_conc( conc, obj_isotherm);
421  }
422  break;
423  }
424  return conc;
425 }
426 
427 
428 template<class Func>
429 void Isotherm::make_table(const Func &isotherm, int n_steps)
430 {
431  double mass_limit = scale_aqua_ * table_limit_ + scale_sorbed_ * const_cast<Func &>(isotherm)(table_limit_ / this->rho_aqua_);
432  if(mass_limit < 0.0)
433  {
434  xprintf(UsrErr,"Isotherm mass_limit has negative value.\n");
435  }
436  total_mass_step_ = mass_limit / n_steps;
437  double mass = 0.0;
438  for(int i=0; i<= n_steps; i++) {
439  // aqueous concentration (original coordinates c_a) corresponding to i-th total_mass_step_
440  ConcPair c_pair( mass/scale_aqua_, 0.0 );
441 
442  ConcPair result = solve_conc( c_pair, isotherm);
443  double c_sorbed_rot = ( result.solid * scale_aqua_ - result.fluid * scale_sorbed_);
444  interpolation_table.push_back(c_sorbed_rot);
445  mass = mass+total_mass_step_;
446  }
447 
448  return;
449 }
450 
451 
452 #endif /* SORPTION_IMPL_HH_ */
tolerance(unsigned bits)
Definition: isotherm.hh:58
double mult_coef_
Multiplication parameter of the isotherm.
Definition: isotherm.hh:228
CrossFunction(const Func &func_, double total_mass, double scale_aqua, double scale_sorbed, double rho_aqua)
Definition: isotherm.hh:274
void compute(double &c_aqua, double &c_sorbed)
Definition: isotherm.hh:314
double second_coef_
Optional secod parameter of the isotherm.
Definition: isotherm.hh:231
ConcPair(double x, double y)
Definition: isotherm.hh:147
double operator()(double x)
Isotherm definition.
Definition: isotherm.hh:120
ConcPair solve_conc(ConcPair c_pair, const Func &isotherm)
Definition: isotherm.hh:370
Langmuir(double mult_coef, double alpha)
Constructor to set parameters.
Definition: isotherm.hh:98
double total_mass_step_
Definition: isotherm.hh:257
double alpha_
Definition: isotherm.hh:107
double inv_scale_sorbed_
Definition: isotherm.hh:248
double operator()(double x)
Isotherm definition.
Definition: isotherm.hh:82
double mult_coef_
Parameters of the isotherm.
Definition: isotherm.hh:106
void reinit(enum SorptionType sorption_type, bool limited_solubility_on, double aqua_density, double scale_aqua, double scale_sorbed, double c_aqua_limit, double mult_coef, double second_coef)
Definition: isotherm.hh:295
double scale_aqua_
Definition: isotherm.hh:286
Freundlich(double mult_coef, double exponent)
Constructor to set parameters.
Definition: isotherm.hh:118
double mult_coef_
Parameters of the isotherm.
Definition: isotherm.hh:87
vector< double > interpolation_table
Definition: isotherm.hh:253
double scale_sorbed_
coefficient that convert adsorbed molar concentration to mass; molar_weight * rho_rock * (1 - porosit...
Definition: isotherm.hh:246
ConcPair precipitate(ConcPair conc)
Definition: isotherm.hh:361
#define xprintf(...)
Definition: system.hh:100
Linear(double mult_coef)
Constructor to set parameters.
Definition: isotherm.hh:80
bool limited_solubility_on_
Solubility limit flag.
Definition: isotherm.hh:239
Pair of soluted and adsorbed concentration.
Definition: isotherm.hh:146
double operator()(double conc_aqua)
Definition: isotherm.hh:279
double rho_aqua_
Definition: isotherm.hh:286
double mult_coef_
Parameters of the isotherm.
Definition: isotherm.hh:126
SorptionType
Type of adsorption isotherm.
Definition: isotherm.hh:138
Definition: system.hh:72
double exponent_
Definition: isotherm.hh:127
bool operator()(const T &a, const T &b)
Definition: isotherm.hh:63
void make_table(int n_points)
Definition: isotherm.cc:11
void interpolate(double &c_aqua, double &c_sorbed)
Definition: isotherm.hh:328
double inv_scale_aqua_
reciprocal values
Definition: isotherm.hh:248
double total_mass_
Definition: isotherm.hh:286
ConcPair compute_projection(ConcPair conc)
Definition: isotherm.hh:340
double scale_aqua_
coefficient that convert soluted concentration to mass; porosity = k_W, originally rho_aqua*porosity ...
Definition: isotherm.hh:244
double table_limit_
Definition: isotherm.hh:236
enum SorptionType adsorption_type_
Type of isotherm.
Definition: isotherm.hh:225
double scale_sorbed_
Definition: isotherm.hh:286
bool is_precomputed(void)
Definition: isotherm.hh:191
double operator()(double x)
Isotherm definition.
Definition: isotherm.hh:100
double rho_aqua_
density of the solvent
Definition: isotherm.hh:242