Flow123d
sorption_base.cc
Go to the documentation of this file.
1 #include <boost/foreach.hpp>
2 
3 #include "reaction/reaction.hh"
8 #include "reaction/isotherm.hh"
9 #include "reaction/sorption.hh"
10 
11 #include "system/system.hh"
12 #include "system/sys_profiler.hh"
13 
14 #include "la/distribution.hh"
15 #include "mesh/mesh.h"
16 #include "mesh/elements.h"
17 #include "input/type_selection.hh"
18 
19 #include "fields/field_set.hh"
21 
22 
23 using namespace std;
24 using namespace Input::Type;
25 
27  .add_value(Isotherm::none,"none", "No adsorption considered.")
28  .add_value(Isotherm::linear, "linear",
29  "Linear isotherm runs the concentration exchange between liquid and solid.")
30  .add_value(Isotherm::langmuir, "langmuir",
31  "Langmuir isotherm runs the concentration exchange between liquid and solid.")
32  .add_value(Isotherm::freundlich, "freundlich",
33  "Freundlich isotherm runs the concentration exchange between liquid and solid.");
34 
35 
36 
38  = Record("Adsorption", "AUXILIARY RECORD. Should not be directly part of the input tree.")
39  .declare_key("substances", Array(String()), Default::obligatory(),
40  "Names of the substances that take part in the adsorption model.")
41  .declare_key("solvent_density", Double(), Default("1.0"),
42  "Density of the solvent.")
43  .declare_key("substeps", Integer(), Default("1000"),
44  "Number of equidistant substeps, molar mass and isotherm intersections")
45  .declare_key("molar_mass", Array(Double()), Default::obligatory(),
46  "Specifies molar masses of all the adsorbing species.")
47  .declare_key("solubility", Array(Double(0.0)), Default::optional(), //("-1.0"), //
48  "Specifies solubility limits of all the adsorbing species.")
49  .declare_key("table_limits", Array(Double(0.0)), Default::optional(), //("-1.0"), //
50  "Specifies highest aqueous concentration in interpolation table.")
51  .declare_key("input_fields", Array(EqData("").input_data_set_.make_field_descriptor_type("Sorption")), Default::obligatory(), //
52  "Containes region specific data necessary to construct isotherms.")//;
53  .declare_key("reaction", ReactionTerm::input_type, Default::optional(), "Reaction model following the sorption.");
54 
55 
56 SorptionBase::EqData::EqData(const string &output_field_name)
57 {
58  ADD_FIELD(rock_density, "Rock matrix density.", "0.0");
59 
60  ADD_FIELD(sorption_type,"Considered adsorption is described by selected isotherm."); //
61  sorption_type.input_selection(&sorption_type_selection);
62 
63  ADD_FIELD(isotherm_mult,"Multiplication parameters (k, omega) in either Langmuir c_s = omega * (alpha*c_a)/(1- alpha*c_a) or in linear c_s = k * c_a isothermal description.","1.0");
64 
65  ADD_FIELD(isotherm_other,"Second parameters (alpha, ...) defining isotherm c_s = omega * (alpha*c_a)/(1- alpha*c_a).","1.0");
66  ADD_FIELD(init_conc_solid, "Initial solid concentration of substances."
67  " Vector, one value for every substance.", "0");
68 
69  rock_density.units("");
70  init_conc_solid.units("M/L^3");
71 
72  input_data_set_ += *this;
73 
74  // porosity field is set from governing equation (transport) later
75  // hence we do not add it to the input_data_set_
76  *this += porosity.name("porosity").units("1").just_copy();
77 
78  output_fields += *this;
79  output_fields += conc_solid.name(output_field_name).units("M/L^3");
80 }
81 
82 
84  : ReactionTerm(init_mesh, in_rec),
85  data_(nullptr)
86 {
87 }
88 
89 
91 {
92  if(reaction != nullptr) delete reaction;
93 
94  if (data_ != nullptr) delete data_;
95 
96 // if(!output_rec.is_empty())
97  {
98  VecDestroy(vconc_solid);
99  VecDestroy(vconc_solid_out);
100  }
101 
102  for (unsigned int sbi = 0; sbi < names_.size(); sbi++)
103  {
104  //no mpi vectors
105  xfree(conc_solid[sbi]);
106  }
107  xfree(conc_solid);
108 }
109 
110 
111 
112 
114 {
115  Input::Array substances_array = in_rec.val<Input::Array>("substances");
116  unsigned int k, global_idx, i_subst = 0;
117  bool found;
118 
119  for(Input::Iterator<string> spec_iter = substances_array.begin<string>(); spec_iter != substances_array.end(); ++spec_iter, i_subst++)
120  {
121  //finding the name of a substance in the global array of names
122  found = false;
123  for(k = 0; k < names.size(); k++)
124  {
125  if (*spec_iter == names[k])
126  {
127  global_idx = k;
128  found = true;
129  break;
130  }
131  }
132 
133  if(!found)
134  xprintf(UsrErr,"Wrong name of %d-th substance - not found in global set of transported substances.\n", i_subst);
135 
136  //finding the global index of substance in the local array
137  found = false;
138  for(k = 0; k < substance_global_idx_.size(); k++)
139  {
140  if(substance_global_idx_[k] == global_idx)
141  {
142  found = true;
143  break;
144  }
145  }
146 
147  if(!found)
148  substance_global_idx_.push_back(global_idx);
149 
150  }
152 }
153 
155 {
160 
161  // read fields from input file
162  data_->input_data_set_.set_input_list(in_rec.val<Input::Array>("input_fields"));
163 
164  data_->set_mesh(*mesh_);
165  data_->set_limit_side(LimitSide::right);
166 
167  // Common data for all the isotherms loaded bellow
168  solvent_density = in_rec.val<double>("solvent_density");
169 
170  Input::Array molar_mass_array = in_rec.val<Input::Array>("molar_mass");
171 
172  if (molar_mass_array.size() == molar_masses.size() ) molar_mass_array.copy_to( molar_masses );
173  else xprintf(UsrErr,"Number of molar masses %d has to match number of adsorbing species %d.\n", molar_mass_array.size(), molar_masses.size());
174 
175  Input::Iterator<Input::Array> solub_iter = in_rec.find<Input::Array>("solubility");
176  if( solub_iter )
177  {
178  solub_iter->copy_to(solubility_vec_);
179  if (solubility_vec_.size() != n_substances_)
180  {
181  xprintf(UsrErr,"Number of given solubility limits %d has to match number of adsorbing species %d.\n", solubility_vec_.size(), n_substances_);
182  }
183  }else{
184  // fill solubility_vec_ with zeros or resize it at least
186  }
187 
188  Input::Iterator<Input::Array> interp_table_limits = in_rec.find<Input::Array>("table_limits");
189  if( interp_table_limits )
190  {
191  interp_table_limits->copy_to(table_limit_);
192  if (table_limit_.size() != n_substances_)
193  {
194  xprintf(UsrErr,"Number of given table limits %d has to match number of adsorbing species %d.\n", table_limit_.size(), n_substances_);
195  }/**/
196  }else{
197  // fill table_limit_ with zeros or resize it at least
198  table_limit_.resize(n_substances_);
199  }
200 }
201 
203 {
204  ASSERT(distribution != nullptr, "Distribution has not been set yet.\n");
205  ASSERT(time_ != nullptr, "Time governor has not been set yet.\n");
206 
207 
208 
209  //DBGMSG("SorptionBase constructor.\n");
211 
212  // for(unsigned int s=0; s<names_.size(); s++)
213  // cout << s << " " << names_[s] << endl;
214  //
215  // for(unsigned int s=0; s<n_substances_; s++)
216  // cout << s << " " << substance_global_idx_[s] << " " << names_[substance_global_idx_[s]] << endl;
217 
219  nr_of_points = input_record_.val<int>("substeps");
220 
221  // Input::Iterator<Input::Record> out_rec = in_rec.find<Input::Record>("output");
222  //output_rec = in_rec.find<Input::Record>("output");
223  // if(out_rec) output_rec = *out_rec;
224  output_array = input_record_.val<Input::Array>("output_fields");
225 
226  //Simple vectors holding common informations.
227  molar_masses.resize( n_substances_ );
228 
229  //isotherms array resized bellow
230  isotherms.resize(nr_of_regions);
231  for(int i_reg = 0; i_reg < nr_of_regions; i_reg++)
232  for(int i_spec = 0; i_spec < n_substances_; i_spec++)
233  {
234  Isotherm iso_mob;
235  isotherms[i_reg].push_back(iso_mob);
236  }
237 
238 
240  data_->set_time(*time_);
241  make_tables();
242 
243  //allocating new array for sorbed concentrations
244  conc_solid = (double**) xmalloc(names_.size() * sizeof(double*));//new double * [n_substances_];
245  conc_solid_out = (double**) xmalloc(names_.size() * sizeof(double*));
246  for (unsigned int sbi = 0; sbi < names_.size(); sbi++)
247  {
248  conc_solid[sbi] = (double*) xmalloc(distribution->lsize() * sizeof(double));//new double[ nr_of_local_elm ];
249  conc_solid_out[sbi] = (double*) xmalloc(distribution->size() * sizeof(double));
250  //zero initialization of solid concentration for all substances
251  for(unsigned int i=0; i < distribution->lsize(); i++)
252  conc_solid[sbi][i] = 0;
253  }
254 
256 
257 
258  //setting initial condition for solid concentrations
259  for (unsigned int loc_el = 0; loc_el < distribution->lsize(); loc_el++)
260  {
261  unsigned int index = el_4_loc[loc_el];
262  ElementAccessor<3> ele_acc = mesh_->element_accessor(index);
263  arma::vec value = data_->init_conc_solid.value(ele_acc.centre(),
264  ele_acc);
265 
266  //setting initial solid concentration for substances involved in adsorption
267  for (int sbi = 0; sbi < n_substances_; sbi++)
268  {
269  int subst_id = substance_global_idx_[sbi];
270  conc_solid[subst_id][loc_el] = value(sbi);
271  }
272  }
273 
274  //initialization of output
278  for (int sbi=0; sbi<names_.size(); sbi++)
279  {
280  // create shared pointer to a FieldElementwise and push this Field to output_field on all regions
281  std::shared_ptr<FieldElementwise<3, FieldValue<3>::Scalar> > output_field_ptr(
283  data_->conc_solid[sbi].set_field(mesh_->region_db().get_region_set("ALL"), output_field_ptr, 0);
284  }
285  data_->output_fields.set_limit_side(LimitSide::right);
287 
288 
289  //DBGMSG("Going to write initial condition.\n");
290  // write initial condition
294 
295  // creating reaction from input and setting their parameters
297 
298  if(reaction != nullptr)
299  {
304  }
305 }
306 
308 {
309  //DBGMSG("SorptionBase init_from_input\n");
310  Input::Iterator<Input::AbstractRecord> reactions_it = in_rec.find<Input::AbstractRecord>("reaction");
311  if ( reactions_it )
312  {
313  if (reactions_it->type() == Linear_reaction::input_type ) {
314  reaction = new Linear_reaction(*mesh_, *reactions_it);
315 
316  } else
317  if (reactions_it->type() == Pade_approximant::input_type) {
318  reaction = new Pade_approximant(*mesh_, *reactions_it);
319 
320  } else
321  if (reactions_it->type() == SorptionBase::input_type ) {
322  xprintf(UsrErr, "Sorption model cannot have another descendant sorption model.\n");
323  } else
324  if (reactions_it->type() == DualPorosity::input_type ) {
325  xprintf(UsrErr, "Sorption model cannot have descendant dual porosity model.\n");
326  } else
327  if (reactions_it->type() == Semchem_interface::input_type )
328  {
329  xprintf(UsrErr, "Semchem chemistry model is not supported at current time.\n");
330  } else
331  {
332  xprintf(UsrErr, "Unknown reactions type in Sorption model.\n");
333  }
334  } else
335  {
336  reaction = nullptr;
337  }
338 }
339 
341 {
342  //DBGMSG("Sorption - update_solution\n");
343  data_->set_time(*time_); // set to the last computed time
344 
345  // if parameters changed during last time step, reinit isotherms and eventualy
346  // update interpolation tables in the case of constant rock matrix parameters
347  if(data_->changed())
348  make_tables();
349 
350 
351  START_TIMER("Sorption");
352  for (int loc_el = 0; loc_el < distribution->lsize(); loc_el++)
353  {
355  }
356  END_TIMER("Sorption");
357 
358  if(reaction != nullptr) reaction->update_solution();
359 }
360 
362 {
363  ElementAccessor<3> elm;
364  BOOST_FOREACH(const Region &reg_iter, this->mesh_->region_db().get_region_set("BULK") )
365  {
366  int reg_idx = reg_iter.bulk_idx();
367 
368  if(data_->is_constant(reg_iter))
369  {
370  ElementAccessor<3> elm(this->mesh_, reg_iter); // constant element accessor
371  isotherm_reinit(isotherms[reg_idx],elm);
372  //xprintf(MsgDbg,"parameters are constant\n");
373  for(int i_subst = 0; i_subst < n_substances_; i_subst++)
374  {
375  isotherms[reg_idx][i_subst].make_table(nr_of_points);
376  }
377  }
378  }
379 }
380 
381 double **SorptionBase::compute_reaction(double **concentrations, int loc_el) // Sorption simulations are realized just for one element.
382 {
383  //DBGMSG("compute_reaction\n");
384  ElementFullIter elem = mesh_->element(el_4_loc[loc_el]);
385  double porosity;
386  double rock_density;
387  Region region = elem->region();
388  int reg_id_nr = region.bulk_idx();
389  int variabl_int = 0;
390  int i_subst, subst_id;
391 
392  std::vector<Isotherm> & isotherms_vec = isotherms[reg_id_nr];
393 
394  //if(reg_id_nr != 0) cout << "region id is " << reg_id_nr << endl;
395 
396  // Constant value of rock density and mobile porosity over the whole region => interpolation_table is precomputed
397  if (isotherms_vec[0].is_precomputed())
398  {
399  for(i_subst = 0; i_subst < n_substances_; i_subst++)
400  {
401  subst_id = substance_global_idx_[i_subst];
402  //DBGMSG("on s_%d precomputed %d\n",subst_id, isotherms_vec[i_subst].is_precomputed());
403 
404  Isotherm & isotherm = this->isotherms[reg_id_nr][i_subst];
405  isotherm.interpolate((concentration_matrix_[subst_id][loc_el]), conc_solid[subst_id][loc_el]);
406  }
407  }
408  else
409  {
410  isotherm_reinit(isotherms_vec, elem->element_accessor());
411 
412  for(i_subst = 0; i_subst < n_substances_; i_subst++)
413  {
414  subst_id = substance_global_idx_[i_subst];
415  Isotherm & isotherm = this->isotherms[reg_id_nr][i_subst];
416  isotherm.compute((concentration_matrix_[subst_id][loc_el]), conc_solid[subst_id][loc_el]);
417  }
418  }
419 
420  return concentrations;
421 }
422 
424 {
425  data_->set_field(data_->porosity.name(),por_m);
426 }
427 
428 
430 {
431  DBGMSG("Not implemented.\n");
432  xprintf(Msg, "\nSorption parameters are defined as follows:\n");
433 }
434 
435 /*
436 void SorptionBase::set_concentration_vector(Vec &vc)
437 {
438  DBGMSG("Not implemented.\n");
439 }
440 */
441 
442 /**************************************** OUTPUT ***************************************************/
443 
445 {
446  int sbi, n_subst, ierr, rank, np; //, i, j, ph;
447  n_subst = names_.size();
448 
449  vconc_solid = (Vec*) xmalloc(n_subst * (sizeof(Vec)));
450  vconc_solid_out = (Vec*) xmalloc(n_subst * (sizeof(Vec))); // extend to all
451 
452  for (sbi = 0; sbi < n_subst; sbi++) {
453  ierr = VecCreateMPIWithArray(PETSC_COMM_WORLD,1, distribution->lsize(), mesh_->n_elements(), conc_solid[sbi],
454  &vconc_solid[sbi]);
455  VecZeroEntries(vconc_solid[sbi]);
456 
457  ierr = VecCreateSeqWithArray(PETSC_COMM_SELF,1, mesh_->n_elements(), conc_solid_out[sbi], &vconc_solid_out[sbi]);
458  VecZeroEntries(vconc_solid_out[sbi]);
459  }
460 }
461 
462 
464 {
465  unsigned int sbi/*, rank, np*/;
466  IS is;
467  VecScatter vconc_out_scatter;
468  //PetscViewer inviewer;
469 
470  ISCreateGeneral(PETSC_COMM_SELF, mesh_->n_elements(), row_4_el, PETSC_COPY_VALUES, &is); //WithArray
471  VecScatterCreate(vconc_solid[0], is, vconc_solid_out[0], PETSC_NULL, &vconc_out_scatter);
472  for (sbi = 0; sbi < names_.size(); sbi++) {
473  VecScatterBegin(vconc_out_scatter, vconc_solid[sbi], vconc_solid_out[sbi], INSERT_VALUES, SCATTER_FORWARD);
474  VecScatterEnd(vconc_out_scatter, vconc_solid[sbi], vconc_solid_out[sbi], INSERT_VALUES, SCATTER_FORWARD);
475  }
476  //VecView(transport->vconc[0],PETSC_VIEWER_STDOUT_WORLD);
477  //VecView(transport->vconc_out[0],PETSC_VIEWER_STDOUT_WORLD);
478  VecScatterDestroy(&(vconc_out_scatter));
479  ISDestroy(&(is));
480 }
481 
482 
484 {
485  //DBGMSG("Sorption output\n");
487 
488  int ierr, rank;
489  MPI_Comm_rank(PETSC_COMM_WORLD, &rank);
490  if (rank == 0)
491  {
492  // Register fresh output data
495  }
496 
497  //it can call only linear reaction which has no output at the moment
498  //if(reaction) reaction->output_data();
499 
500  //for synchronization when measuring time by Profiler
502 }