Flow123d  jenkins-Flow123d-linux-release-multijob-198
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  * Complementary functor for isotherm of type "none"
75  */
76 class None {
77 public:
78  /// Constructor to set parameters
79  None(){}
80  /// Isotherm definition.
81  inline double operator()(double x) {
82  return (0.0);
83  }
84 };
85 
86 /**
87  * Functor for linear isotherm
88  */
89 class Linear {
90 public:
91  /// Constructor to set parameters
92  Linear(double mult_coef) : mult_coef_(mult_coef) {}
93  /// Isotherm definition.
94  inline double operator()(double x) {
95  return (mult_coef_*x);
96  }
97 private:
98  /// Parameters of the isotherm.
99  double mult_coef_;
100 };
101 
102 
103 
104 /**
105  * Functor for Langmuir isotherm.
106  */
107 class Langmuir {
108 public:
109  /// Constructor to set parameters
110  Langmuir( double mult_coef, double alpha) : mult_coef_(mult_coef), alpha_(alpha) {}
111  /// Isotherm definition.
112  inline double operator()( double x) {
113  return (mult_coef_*(alpha_ * x)/(alpha_ *x + 1));
114  }
115 
116 private:
117  /// Parameters of the isotherm.
118  double mult_coef_;
119  double alpha_;
120 };
121 
122 
123 
124 /**
125  * Functor for Freundlich isotherm.
126  */
127 class Freundlich {
128 public:
129  /// Constructor to set parameters
130  Freundlich(double mult_coef, double exponent) : mult_coef_(mult_coef), exponent_(exponent){}
131  /// Isotherm definition.
132  inline double operator()(double x) {
133  return (mult_coef_*pow(x, exponent_));
134  }
135 
136 private:
137  /// Parameters of the isotherm.
138  double mult_coef_;
139  double exponent_;
140 };
141 
142 
143 /**
144 * Class describing one isotherm with possibly precalculated interpolation table.
145 */
146 class Isotherm {
147 public:
148  TYPEDEF_ERR_INFO( EI_BoostMessage, std::string);
149  DECLARE_EXCEPTION( ExcBoostSolver, <<
150  "The Boost solver of nonlinear equation did not converge in sorption model.\n"
151  << "Check input at the following address for possible user error: \t" << Input::EI_Address::val << "\n"
152  "Solver message: \n" << EI_BoostMessage::val);
153 
154  TYPEDEF_ERR_INFO( EI_TotalMass, double);
155  DECLARE_EXCEPTION( ExcNegativeTotalMass, <<
156  "The total mass in sorption model became negative during the computation (value: "
157  << EI_TotalMass::val << ").\n"
158  << "Check input at the following address for possible user error: \t" << Input::EI_Address::val << "\n");
159 
160 
161  /// Type of adsorption isotherm.
163  none = 0,
164  linear = 1,
167  };
168 
169  /// Pair of soluted and adsorbed concentration.
170  struct ConcPair {
171  ConcPair(double x, double y) : fluid(x), solid(y) {}
172  double fluid;
173  double solid;
174  };
175 
176  /**
177  * Setting adsorption parameters for general isotherm. These parameters are then used either
178  * for creation of the interpolation table via @p make_table method or just one adsorption is computed
179  * through @p compute method. Provided parameters are:
180  * @param sorption_type - type of isotherm
181  * @param limited_solubility_on - true if @p c_aqua_limit is solubility limit
182  * @param aqua_density - density of the liquid phase
183  * @param scale_aqua - generalized porosity, fraction of the space with liquid phase
184  * @param scale_sorbed - fraction of the space with the solid to which we adsorp
185  * @param c_aqua_limit - limit for interpolation table, possibly solubility limit
186  * @param mult_coef - multiplicative coefficient of the isotherm (all isotherms have one)
187  * @param second_coef - possibly second parameter of the isotherm
188  */
189  inline void reinit(enum SorptionType sorption_type, bool limited_solubility_on,
190  double aqua_density, double scale_aqua, double scale_sorbed,
191  double c_aqua_limit, double mult_coef, double second_coef);
192 
193  /**
194  * Create interpolation table for isotherm in rotated coordinate system with X axes given by total mass in
195  * both phases. Size of the table is the only parameter. Currently we support only linear interpolation.
196  * @p reinit has to be called just before this method.
197  */
198  void make_table(int n_points);
199 
200  /**
201  * Direct calculation of the equilibrium adsorption using a non-linear solver.
202  * @p reinit has to be called just before this method.
203  */
204  inline void compute(double &c_aqua, double &c_sorbed);
205 
206  /**
207  * Use interpolation to determine equilibrium state.
208  * Assumes previous call to @p make_table. If total mass is larger then table limit we either
209  * call @p precipitate (limit_solubility_on) or use direct computation.
210  */
211  inline void interpolate(double &c_aqua, double &c_sorbed);
212 
213  /**
214  * Returns true if interpolation table is created.
215  */
216  inline bool is_precomputed(void) {
217  return interpolation_table.size() != 0;
218  }
219 
220 protected:
221  /**
222  * Implementation of interpolation construction for particular isotherm functor.
223  * Specialized for sorption 'none' - creates empty table.
224  */
225  template<class Func>
226  void make_table(const Func &isotherm, int n_points);
227  /**
228  * Find new values for concentrations in @p c_pair that has same total mass and lies on the
229  * @p isotherm (functor object).
230  * Specialized for sorption 'none' - returns unchanged @p c_pair.
231  */
232  template<class Func>
233  inline ConcPair solve_conc(ConcPair c_pair, const Func &isotherm);
234  /**
235  * Dispatch isotherm type and use appropriate template.
236  */
237  inline ConcPair solve_conc(ConcPair conc);
238  /**
239  * Update concentrations using interopolation.
240  */
241  inline ConcPair compute_projection( ConcPair conc );
242  /**
243  * Modify concentrations after adsorption for limited solubility.
244  */
245  inline ConcPair precipitate( ConcPair conc );
246 
247  /****************************************
248  * Data
249  */
250 
251  /// Type of isotherm
253 
254  /// Multiplication parameter of the isotherm
255  double mult_coef_;
256 
257  /// Optional secod parameter of the isotherm
258  double second_coef_;
259 
260  /* Concentration in liquid phase for limit of the interpolation table, or
261  * solubility limit.
262  */
263  double table_limit_;
264 
265  /// Solubility limit flag
267 
268  /// density of the solvent
269  double rho_aqua_;
270  /// coefficient that convert soluted concentration to mass; porosity = k_W, originally rho_aqua*porosity = k_W
271  double scale_aqua_;
272  /// coefficient that convert adsorbed molar concentration to mass; molar_weight * rho_rock * (1 - porosity) = k_H
274  /// reciprocal values
276  /**
277  * Interpolation table of isotherm in the rotated coordinates.
278  * The X axes of rotated system is total mass, the Y axes is perpendicular.
279  */
281  /**
282  * Step on the rotated X axes (total mass).
283  */
285 
286 };
287 
288 // Sorption none template specializations
289 template<> Isotherm::ConcPair Isotherm::solve_conc(Isotherm::ConcPair c_pair, const None &isotherm);
290 template<> void Isotherm::make_table(const None &isotherm, int n_steps);
291 
292 
293 /**
294  * Functor for solved equation in form F(x) ==0.
295  * Function @p func is an isotherm functor object in concentration based coordinated system.
296  * We solve the equation in modified system (scaled and rotated) for better numerical stability.
297  * The solved equation reads:
298  * F(X) -Y =0, where
299  * X is total mass , Y
300  */
301 template <class Func>
303 {
304 public:
305  CrossFunction(const Func &func_, double total_mass, double scale_aqua, double scale_sorbed, double rho_aqua)
306  : func(func_), total_mass_(total_mass),
307  scale_sorbed_(scale_sorbed), scale_aqua_(scale_aqua), rho_aqua_(rho_aqua)
308  {}
309 
310  double operator()( double conc_aqua)
311  {
312  // that is the selected isotherm // scale_sorbed_ * func( conc_aqua ) + scale_aqua_ * conc_aqua - total_mass_
313  return scale_sorbed_*func( conc_aqua/rho_aqua_) + (scale_aqua_) * conc_aqua - total_mass_;
314  }
315 private:
316  Func func;
318 };
319 
320 
321 /*****************************************************************************************************************
322  * IMPLEMENTATION
323  */
324 
325 
326 inline void Isotherm::reinit(enum SorptionType adsorption_type, bool limited_solubility_on,
327  double rho_aqua, double scale_aqua, double scale_sorbed,
328  double c_aqua_limit, double mult_coef, double second_coef)
329 {
330  adsorption_type_ = adsorption_type;
331  rho_aqua_ = rho_aqua;
332  scale_aqua_ = scale_aqua;
333  scale_sorbed_ = scale_sorbed;
334  limited_solubility_on_ = limited_solubility_on;
335  mult_coef_ = mult_coef;
336  second_coef_ = second_coef;
337 
338  table_limit_ = c_aqua_limit;
341 }
342 
343 
344 inline void Isotherm::compute( double &c_aqua, double &c_sorbed ) {
345  ConcPair c_pair(c_aqua, c_sorbed);
346  ConcPair result(0,0);
347 
348  if (limited_solubility_on_ && (c_pair.fluid > table_limit_)) {
349  result = precipitate( c_pair );
350  } else {
351  result = solve_conc( c_pair );
352  }
353  c_aqua=result.fluid;
354  c_sorbed=result.solid;
355 }
356 
357 
358 inline void Isotherm::interpolate( double &c_aqua, double &c_sorbed ) {
359  ConcPair c_pair(c_aqua, c_sorbed);
360  ConcPair result(0,0);
361 
362  result = compute_projection( c_pair );
363 
364  c_aqua=result.fluid;
365  c_sorbed=result.solid;
366 }
367 
368 
369 
371  double total_mass = (scale_aqua_* c_pair.fluid + scale_sorbed_ * c_pair.solid);
372  if(total_mass < 0.0)
373  THROW( Isotherm::ExcNegativeTotalMass()
374  << EI_TotalMass(total_mass)
375  );
376  // total_mass_step_ is set and checked in make_table
377  double total_mass_steps = total_mass / total_mass_step_;
378  unsigned int total_mass_idx = static_cast <unsigned int>(std::floor(total_mass_steps));
379 
380  if (total_mass_idx < (interpolation_table.size() - 1) ) {
381  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]);
382  return ConcPair( (total_mass * inv_scale_aqua_ - rot_sorbed * inv_scale_sorbed_),
383  (total_mass * inv_scale_sorbed_ + rot_sorbed * inv_scale_aqua_) );
384  } else {
386  return precipitate( c_pair );
387  } else {
388  return solve_conc( c_pair );
389  }
390  }
391 }
392 
393 
394 
396  double total_mass = (scale_aqua_*c_pair.fluid + scale_sorbed_ * c_pair.solid);
397  return ConcPair( table_limit_,
398  (total_mass - scale_aqua_ * table_limit_)/scale_sorbed_ );
399 }
400 
401 
402 
403 template<class Func>
404 inline Isotherm::ConcPair Isotherm::solve_conc(Isotherm::ConcPair c_pair, const Func &isotherm)
405 {
406  boost::uintmax_t max_iter = 20;
407  tolerance<double> toler(30);
408  double total_mass = (scale_aqua_*c_pair.fluid + scale_sorbed_ * c_pair.solid);
409  double mass_limit = table_limit_*scale_aqua_ + const_cast<Func &>(isotherm)(table_limit_ / this->rho_aqua_)*scale_sorbed_;
410  double upper_solution_bound;
411 
412  if(total_mass >= mass_limit)
413  {
414  mass_limit = total_mass;
415  }
416  upper_solution_bound = mass_limit / scale_aqua_;
417  CrossFunction<Func> eq_func(isotherm, total_mass, scale_aqua_, scale_sorbed_, this->rho_aqua_);
418  pair<double,double> solution;
419  if (total_mass > 0) // here should be probably some kind of tolerance instead of "0"
420  {
421  try {
422  solution = boost::math::tools::toms748_solve(eq_func, 0.0, upper_solution_bound, toler, max_iter);
423  }
424  catch(boost::exception const & e)
425  {
426  THROW( Isotherm::ExcBoostSolver()
427  << EI_BoostMessage(boost::diagnostic_information(e))
428  );
429  }
430  }
431  double difference;
432  difference = (solution.second - solution.first)/2;
433  double c_aqua = solution.first + difference;
434  return ConcPair( c_aqua,
435  (total_mass - scale_aqua_ * c_aqua)/scale_sorbed_);
436 }
437 
438 
440 {
441  switch(adsorption_type_)
442  {
443  case 0: // none
444  {
445  None obj_isotherm;
446  return solve_conc( conc, obj_isotherm);
447  }
448  break;
449  case 1: // linear:
450  {
451  Linear obj_isotherm(mult_coef_);
452  return solve_conc( conc, obj_isotherm);
453  }
454  break;
455  case 2: // freundlich
456  {
457  Freundlich obj_isotherm(mult_coef_, second_coef_);
458  return solve_conc( conc, obj_isotherm);
459  }
460  break;
461  case 3: // langmuir:
462  {
463  Langmuir obj_isotherm(mult_coef_, second_coef_);
464  return solve_conc( conc, obj_isotherm);
465  }
466  break;
467  }
468  return conc;
469 }
470 
471 
472 template<class Func>
473 void Isotherm::make_table(const Func &isotherm, int n_steps)
474 {
475  double mass_limit = scale_aqua_ * table_limit_ + scale_sorbed_ * const_cast<Func &>(isotherm)(table_limit_ / this->rho_aqua_);
476 
477  if(mass_limit < 0.0)
478  THROW( Isotherm::ExcNegativeTotalMass()
479  << EI_TotalMass(mass_limit)
480  );
481  total_mass_step_ = mass_limit / n_steps;
482  double mass = 0.0;
483  for(int i=0; i<= n_steps; i++) {
484  // aqueous concentration (original coordinates c_a) corresponding to i-th total_mass_step_
485  ConcPair c_pair( mass/scale_aqua_, 0.0 );
486 
487  ConcPair result = solve_conc( c_pair, isotherm);
488  double c_sorbed_rot = ( result.solid * scale_aqua_ - result.fluid * scale_sorbed_);
489  interpolation_table.push_back(c_sorbed_rot);
490  mass = mass+total_mass_step_;
491  }
492 
493  return;
494 }
495 
496 #endif /* SORPTION_IMPL_HH_ */
double operator()(double x)
Isotherm definition.
Definition: isotherm.hh:81
tolerance(unsigned bits)
Definition: isotherm.hh:58
double mult_coef_
Multiplication parameter of the isotherm.
Definition: isotherm.hh:255
CrossFunction(const Func &func_, double total_mass, double scale_aqua, double scale_sorbed, double rho_aqua)
Definition: isotherm.hh:305
void compute(double &c_aqua, double &c_sorbed)
Definition: isotherm.hh:344
double second_coef_
Optional secod parameter of the isotherm.
Definition: isotherm.hh:258
ConcPair(double x, double y)
Definition: isotherm.hh:171
double operator()(double x)
Isotherm definition.
Definition: isotherm.hh:132
Definition: isotherm.hh:76
ConcPair solve_conc(ConcPair c_pair, const Func &isotherm)
Definition: isotherm.hh:404
Langmuir(double mult_coef, double alpha)
Constructor to set parameters.
Definition: isotherm.hh:110
double total_mass_step_
Definition: isotherm.hh:284
double alpha_
Definition: isotherm.hh:119
double inv_scale_sorbed_
Definition: isotherm.hh:275
double operator()(double x)
Isotherm definition.
Definition: isotherm.hh:94
double mult_coef_
Parameters of the isotherm.
Definition: isotherm.hh:118
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:326
double scale_aqua_
Definition: isotherm.hh:317
Freundlich(double mult_coef, double exponent)
Constructor to set parameters.
Definition: isotherm.hh:130
double mult_coef_
Parameters of the isotherm.
Definition: isotherm.hh:99
vector< double > interpolation_table
Definition: isotherm.hh:280
double scale_sorbed_
coefficient that convert adsorbed molar concentration to mass; molar_weight * rho_rock * (1 - porosit...
Definition: isotherm.hh:273
ConcPair precipitate(ConcPair conc)
Definition: isotherm.hh:395
Linear(double mult_coef)
Constructor to set parameters.
Definition: isotherm.hh:92
bool limited_solubility_on_
Solubility limit flag.
Definition: isotherm.hh:266
Pair of soluted and adsorbed concentration.
Definition: isotherm.hh:170
double operator()(double conc_aqua)
Definition: isotherm.hh:310
double rho_aqua_
Definition: isotherm.hh:317
double mult_coef_
Parameters of the isotherm.
Definition: isotherm.hh:138
SorptionType
Type of adsorption isotherm.
Definition: isotherm.hh:162
TYPEDEF_ERR_INFO(EI_BoostMessage, std::string)
double exponent_
Definition: isotherm.hh:139
bool operator()(const T &a, const T &b)
Definition: isotherm.hh:63
void make_table(int n_points)
Definition: isotherm.cc:11
None()
Constructor to set parameters.
Definition: isotherm.hh:79
void interpolate(double &c_aqua, double &c_sorbed)
Definition: isotherm.hh:358
double inv_scale_aqua_
reciprocal values
Definition: isotherm.hh:275
double total_mass_
Definition: isotherm.hh:317
ConcPair compute_projection(ConcPair conc)
Definition: isotherm.hh:370
double scale_aqua_
coefficient that convert soluted concentration to mass; porosity = k_W, originally rho_aqua*porosity ...
Definition: isotherm.hh:271
double table_limit_
Definition: isotherm.hh:263
enum SorptionType adsorption_type_
Type of isotherm.
Definition: isotherm.hh:252
double scale_sorbed_
Definition: isotherm.hh:317
bool is_precomputed(void)
Definition: isotherm.hh:216
double operator()(double x)
Isotherm definition.
Definition: isotherm.hh:112
double rho_aqua_
density of the solvent
Definition: isotherm.hh:269
#define THROW(whole_exception_expr)
Wrapper for throw. Saves the throwing point.
Definition: exceptions.hh:34
DECLARE_EXCEPTION(ExcBoostSolver,<< "The Boost solver of nonlinear equation did not converge in sorption model.\n"<< "Check input at the following address for possible user error: \t"<< Input::EI_Address::val<< "\n""Solver message: \n"<< EI_BoostMessage::val)