Flow123d  jenkins-Flow123d-linux-release-multijob-282
dual_porosity.cc
Go to the documentation of this file.
1 #include <iostream>
2 #include <stdlib.h>
3 #include <math.h>
4 
7 #include "system/system.hh"
8 #include "system/sys_profiler.hh"
9 
10 #include "la/distribution.hh"
11 #include "mesh/mesh.h"
12 #include "mesh/elements.h"
13 #include "mesh/region.hh"
15 
16 #include "reaction/sorption.hh"
20 
21 using namespace Input::Type;
22 
23 
25  = EqData().output_fields.make_output_field_selection("DualPorosity_Output")
26  .close();
27 
29  = Record("DualPorosity",
30  "Dual porosity model in transport problems.\n"
31  "Provides computing the concentration of substances in mobile and immobile zone.\n"
32  )
34  .declare_key("input_fields", Array(DualPorosity::EqData().make_field_descriptor_type("DualPorosity")), Default::obligatory(),
35  "Containes region specific data necessary to construct dual porosity model.")
36  .declare_key("scheme_tolerance", Double(0.0), Default("1e-3"),
37  "Tolerance according to which the explicit Euler scheme is used or not."
38  "Set 0.0 to use analytic formula only (can be slower).")
39 
40  .declare_key("reaction_mobile", ReactionTerm::input_type, Default::optional(), "Reaction model in mobile zone.")
41  .declare_key("reaction_immobile", ReactionTerm::input_type, Default::optional(), "Reaction model in immobile zone.")
42 
43  .declare_key("output_fields", Array(EqData::output_selection),
44  Default("conc_immobile"), "List of fields to write to output stream.");
45 
47 {
48  *this += diffusion_rate_immobile
49  .name("diffusion_rate_immobile")
50  .description("Diffusion coefficient of non-equilibrium linear exchange between mobile and immobile zone.")
51  .input_default("0")
52  .units( UnitSI().s(-1) );
53 
54  *this += porosity_immobile
55  .name("porosity_immobile")
56  .description("Porosity of the immobile zone.")
57  .input_default("0")
58  .units( UnitSI::dimensionless() );
59 
60  *this += init_conc_immobile
61  .name("init_conc_immobile")
62  .description("Initial concentration of substances in the immobile zone.")
63  .units( UnitSI().kg().m(-3) );
64 
65  //creating field for porosity that is set later from the governing equation (transport)
66  *this +=porosity
67  .name("porosity")
68  .units( UnitSI::dimensionless() )
69  .flags( FieldFlag::input_copy );
70 
71  output_fields += *this;
72  output_fields += conc_immobile.name("conc_immobile").units( UnitSI().kg().m(-3) );
73 }
74 
76  : ReactionTerm(init_mesh, in_rec)
77 {
78  //set pointer to equation data fieldset
79  this->eq_data_ = &data_;
80 
81  //reads input and creates possibly other reaction terms
83  //read value from input
84  scheme_tolerance_ = input_record_.val<double>("scheme_tolerance");
85 }
86 
88 {
89  if(reaction_mobile != nullptr) delete reaction_mobile;
90  if(reaction_immobile != nullptr) delete reaction_immobile;
91 
92  VecScatterDestroy(&(vconc_out_scatter));
93  VecDestroy(vconc_immobile);
94 
95  for (unsigned int sbi = 0; sbi < substances_.size(); sbi++)
96  {
97  //no mpi vectors
98  xfree(conc_immobile[sbi]);
99  }
100 
102 }
103 
104 
107  if ( reactions_it )
108  {
109  if (reactions_it->type() == FirstOrderReaction::input_type ) {
110  reaction_mobile = new FirstOrderReaction(*mesh_, *reactions_it);
111 
112  } else
113  if (reactions_it->type() == RadioactiveDecay::input_type) {
114  reaction_mobile = new RadioactiveDecay(*mesh_, *reactions_it);
115  } else
116  if (reactions_it->type() == SorptionMob::input_type ) {
117  reaction_mobile = new SorptionMob(*mesh_, *reactions_it);
118  } else
119  if (reactions_it->type() == DualPorosity::input_type ) {
120  THROW( ReactionTerm::ExcWrongDescendantModel()
121  << ReactionTerm::EI_Model((*reactions_it).type().type_name())
122  << (*reactions_it).ei_address());
123  } else
124  if (reactions_it->type() == Semchem_interface::input_type )
125  { THROW( ReactionTerm::ExcWrongDescendantModel()
126  << ReactionTerm::EI_Model((*reactions_it).type().type_name())
127  << EI_Message("This model is not currently supported!")
128  << (*reactions_it).ei_address());
129  } else
130  { //This point cannot be reached. The TYPE_selection will throw an error first.
131  THROW( ExcMessage()
132  << EI_Message("Descending model type selection failed (SHOULD NEVER HAPPEN).")
133  << (*reactions_it).ei_address());
134  }
135  } else
136  {
137  reaction_mobile = nullptr;
138  }
139 
140  reactions_it = input_record_.find<Input::AbstractRecord>("reaction_immobile");
141  if ( reactions_it )
142  {
143  if (reactions_it->type() == FirstOrderReaction::input_type ) {
144  reaction_immobile = new FirstOrderReaction(*mesh_, *reactions_it);
145 
146  } else
147  if (reactions_it->type() == RadioactiveDecay::input_type) {
148  reaction_immobile = new RadioactiveDecay(*mesh_, *reactions_it);
149  } else
150  if (reactions_it->type() == SorptionImmob::input_type ) {
151  reaction_immobile = new SorptionImmob(*mesh_, *reactions_it);
152  } else
153  if (reactions_it->type() == DualPorosity::input_type ) {
154  THROW( ReactionTerm::ExcWrongDescendantModel()
155  << ReactionTerm::EI_Model((*reactions_it).type().type_name())
156  << (*reactions_it).ei_address());
157  } else
158  if (reactions_it->type() == Semchem_interface::input_type )
159  { THROW( ReactionTerm::ExcWrongDescendantModel()
160  << ReactionTerm::EI_Model((*reactions_it).type().type_name())
161  << EI_Message("This model is not currently supported!")
162  << (*reactions_it).ei_address());
163  } else
164  { //This point cannot be reached. The TYPE_selection will throw an error first.
165  THROW( ExcMessage()
166  << EI_Message("Descending model type selection failed (SHOULD NEVER HAPPEN).")
167  << (*reactions_it).ei_address());
168  }
169  } else
170  {
171  reaction_immobile = nullptr;
172  }
173 
174 }
175 
177 {
178  ASSERT(distribution_ != nullptr, "Distribution has not been set yet.\n");
179  ASSERT(time_ != nullptr, "Time governor has not been set yet.\n");
180  ASSERT(output_stream_,"Null output stream.");
182 
183  //allocating memory for immobile concentration matrix
184  conc_immobile = (double**) xmalloc(substances_.size() * sizeof(double*));
185  conc_immobile_out.clear();
186  conc_immobile_out.resize( substances_.size() );
187  for (unsigned int sbi = 0; sbi < substances_.size(); sbi++)
188  {
189  conc_immobile[sbi] = (double*) xmalloc(distribution_->lsize() * sizeof(double));
190  conc_immobile_out[sbi].resize( distribution_->size() );
191  }
193 
195 
196  if(reaction_mobile != nullptr)
197  {
203  }
204 
205  if(reaction_immobile != nullptr)
206  {
212  }
213 
214 }
215 
217 {
218  //setting fields that are set from input file
221 
222  //setting fields in data
224  data_.set_mesh(*mesh_);
226 
227  //initialization of output
228  output_array = input_record_.val<Input::Array>("output_fields");
234  for (unsigned int sbi=0; sbi<substances_.size(); sbi++)
235  {
236  // create shared pointer to a FieldElementwise and push this Field to output_field on all regions
237  auto output_field_ptr = conc_immobile_out[sbi].create_field<3, FieldValue<3>::Scalar>(substances_.size());
238  data_.conc_immobile[sbi].set_field(mesh_->region_db().get_region_set("ALL"), output_field_ptr, 0);
239  }
241 }
242 
243 
245 {
246  ASSERT(distribution_ != nullptr, "Distribution has not been set yet.\n");
247  ASSERT(time_ != nullptr, "Time governor has not been set yet.\n");
248  ASSERT(output_stream_,"Null output stream.");
250 
251  //coupling - passing fields
252  if(reaction_mobile)
253  if (typeid(*reaction_mobile) == typeid(SorptionMob))
254  {
255  reaction_mobile->data().set_field("porosity", data_["porosity"]);
256  reaction_mobile->data().set_field("porosity_immobile", data_["porosity_immobile"]);
257  }
259  if (typeid(*reaction_immobile) == typeid(SorptionImmob))
260  {
261  reaction_immobile->data().set_field("porosity", data_["porosity"]);
262  reaction_immobile->data().set_field("porosity_immobile", data_["porosity_immobile"]);
263  }
264 
265  data_.set_time(time_->step(0));
267 
268  // write initial condition
272 
273  if(reaction_mobile != nullptr)
275 
276  if(reaction_immobile != nullptr)
278 }
279 
281 {
282  //setting initial condition for immobile concentration matrix
283  for (unsigned int loc_el = 0; loc_el < distribution_->lsize(); loc_el++)
284  {
285  unsigned int index = el_4_loc_[loc_el];
286  ElementAccessor<3> ele_acc = mesh_->element_accessor(index);
287  arma::vec value = data_.init_conc_immobile.value(ele_acc.centre(), ele_acc);
288 
289  for (unsigned int sbi=0; sbi < substances_.size(); sbi++)
290  {
291  conc_immobile[sbi][loc_el] = value(sbi);
292  }
293  }
294 }
295 
297 {
298  data_.set_time(time_->step(-2));
299 
300  START_TIMER("dual_por_exchange_step");
301  for (unsigned int loc_el = 0; loc_el < distribution_->lsize(); loc_el++)
302  {
304  }
305  END_TIMER("dual_por_exchange_step");
306 
309 }
310 
311 
312 double **DualPorosity::compute_reaction(double **concentrations, int loc_el)
313 {
314  unsigned int sbi;
315  double conc_average, // weighted (by porosity) average of concentration
316  conc_mob, conc_immob, // new mobile and immobile concentration
317  previous_conc_mob, previous_conc_immob, // mobile and immobile concentration in previous time step
318  conc_max, //difference between concentration and average concentration
319  por_mob, por_immob; // mobile and immobile porosity
320 
321  // get data from fields
322  ElementFullIter ele = mesh_->element(el_4_loc_[loc_el]);
323  por_mob = data_.porosity.value(ele->centre(),ele->element_accessor());
324  por_immob = data_.porosity_immobile.value(ele->centre(),ele->element_accessor());
325  arma::Col<double> diff_vec = data_.diffusion_rate_immobile.value(ele->centre(), ele->element_accessor());
326 
327  // if porosity_immobile == 0 then mobile concentration stays the same
328  // and immobile concentration cannot change
329  if (por_immob == 0.0) return conc_immobile;
330 
331  double exponent,
332  temp_exponent = (por_mob + por_immob) / (por_mob * por_immob) * time_->dt();
333 
334  for (sbi = 0; sbi < substances_.size(); sbi++) //over all substances
335  {
336  exponent = diff_vec[sbi] * temp_exponent;
337  //previous values
338  previous_conc_mob = concentration_matrix_[sbi][loc_el];
339  previous_conc_immob = conc_immobile[sbi][loc_el];
340 
341  // ---compute average concentration------------------------------------------
342  conc_average = ((por_mob * previous_conc_mob) + (por_immob * previous_conc_immob))
343  / (por_mob + por_immob);
344 
345  conc_max = std::max(previous_conc_mob-conc_average, previous_conc_immob-conc_average);
346 
347  if( conc_max <= (2*scheme_tolerance_/(exponent*exponent)*conc_average) ) // forward euler
348  {
349  double temp = diff_vec[sbi]*(previous_conc_immob - previous_conc_mob) * time_->dt();
350  // ---compute concentration in mobile area
351  conc_mob = temp / por_mob + previous_conc_mob;
352 
353  // ---compute concentration in immobile area
354  conc_immob = -temp / por_immob + previous_conc_immob;
355  }
356  else //analytic solution
357  {
358  double temp = exp(-exponent);
359  // ---compute concentration in mobile area
360  conc_mob = (previous_conc_mob - conc_average) * temp + conc_average;
361 
362  // ---compute concentration in immobile area
363  conc_immob = (previous_conc_immob - conc_average) * temp + conc_average;
364  }
365 
366  concentration_matrix_[sbi][loc_el] = conc_mob;
367  conc_immobile[sbi][loc_el] = conc_immob;
368  }
369 
370  return conc_immobile;
371 }
372 
373 
375 {
376  int sbi, n_subst;
377  n_subst = substances_.size();
378 
379  vconc_immobile = (Vec*) xmalloc(n_subst * (sizeof(Vec)));
380 
381 
382  for (sbi = 0; sbi < n_subst; sbi++) {
383  VecCreateMPIWithArray(PETSC_COMM_WORLD,1, distribution_->lsize(), mesh_->n_elements(), conc_immobile[sbi],
384  &vconc_immobile[sbi]);
385  VecZeroEntries(vconc_immobile[sbi]);
386 
387  // if(rank == 0)
388  VecZeroEntries(conc_immobile_out[sbi].get_data_petsc());
389  }
390 
391  // create output vector scatter
392  IS is;
393  ISCreateGeneral(PETSC_COMM_SELF, mesh_->n_elements(), row_4_el_, PETSC_COPY_VALUES, &is); //WithArray
394  VecScatterCreate(vconc_immobile[0], is, conc_immobile_out[0].get_data_petsc(), PETSC_NULL, &vconc_out_scatter);
395  ISDestroy(&(is));
396 }
397 
398 
400 {
401  unsigned int sbi;
402 
403  for (sbi = 0; sbi < substances_.size(); sbi++) {
404  VecScatterBegin(vconc_out_scatter, vconc_immobile[sbi], conc_immobile_out[sbi].get_data_petsc(), INSERT_VALUES, SCATTER_FORWARD);
405  VecScatterEnd(vconc_out_scatter, vconc_immobile[sbi], conc_immobile_out[sbi].get_data_petsc(), INSERT_VALUES, SCATTER_FORWARD);
406  }
407 }
408 
409 
411 {
413 
414  // Register fresh output data
417 
420 }
421 
FieldSet & data()
Definition: equation.hh:188
void output_type(OutputTime::DiscreteSpace rt)
Definition: field_set.hh:194
unsigned int size() const
get global size
void set_input_list(Input::Array input_list)
Definition: field_set.hh:164
static Input::Type::Record input_type
Definition: sorption.hh:77
Sorption model in immobile zone in case dual porosity is considered.
Definition: sorption.hh:96
virtual void zero_time_step()
Definition: equation.hh:97
ReactionTerm & output_stream(OutputTime &ostream)
Sets the output stream which is given from transport class.
FieldSet * eq_data_
Definition: equation.hh:227
void set_limit_side(LimitSide side)
Definition: field_set.hh:171
OutputTime * output_stream_
Pointer to a transport output stream.
void set_initial_condition()
Sets initial condition from input.
VecScatter vconc_out_scatter
Output vector scatter.
Accessor to input data conforming to declared Array.
Definition: accessors.hh:558
virtual void output_data(void)
Output method.
void output_data(void) override
Main output routine.
double ** concentration_matrix_
const std::vector< std::string > & names()
Definition: substance.hh:97
virtual void initialize()
Initialize fields.
Definition: equation.hh:114
void add_admissible_field_names(const Input::Array &in_array)
Registers names of output fields that can be written using this stream.
Definition: output_time.cc:165
MultiField< 3, FieldValue< 3 >::Scalar > conc_immobile
Calculated concentrations in the immobile zone.
Class Input::Type::Default specifies default value of keys of a Input::Type::Record.
Definition: type_record.hh:39
FieldSet output_fields
Fields indended for output, i.e. all input fields plus those representing solution.
static Default obligatory()
Definition: type_record.hh:89
???
SubstanceList substances_
Names belonging to substances.
static Input::Type::Record input_type
Definition: sorption.hh:99
void set_time(const TimeStep &time)
Definition: field_set.hh:186
RegionSet get_region_set(const string &set_name) const
Definition: region.cc:361
void zero_time_step() override
Definition: mesh.h:109
Field< 3, FieldValue< 3 >::Scalar > porosity_immobile
Immobile porosity field.
Iterator< Ret > find(const string &key) const
int * row_4_el_
Indices of rows belonging to elements.
~DualPorosity(void)
Destructor.
Record & derive_from(AbstractRecord &parent)
Definition: type_record.cc:197
std::vector< VectorSeqDouble > conc_immobile_out
concentration array output for immobile phase (gathered - sequential)
Field< 3, FieldValue< 3 >::Vector > diffusion_rate_immobile
Mass transfer coefficients between mobile and immobile pores.
const RegionDB & region_db() const
Definition: mesh.h:155
static Input::Type::Record input_type
Input record for class FirstOrderReaction.
ElementAccessor< 3 > element_accessor(unsigned int idx, bool boundary=false)
Definition: mesh.cc:670
void allocate_output_mpi(void)
Allocates petsc vectors, prepares them for output and creates output vector scatter.
const TimeStep & step(int index=-1) const
unsigned int size() const
Definition: substance.hh:99
Class for declaration of inputs sequences.
Definition: type_base.hh:239
void update_solution(void) override
Input::Array output_array
static Input::Type::AbstractRecord input_type
double scheme_tolerance_
Dual porosity computational scheme tolerance.
static Default optional()
Definition: type_record.hh:102
static Input::Type::Record input_type
void make_reactions()
Resolves construction of following reactions.
unsigned int n_elements() const
Definition: mesh.h:141
void initialize() override
Prepares the object to usage.
#define ASSERT(...)
Definition: global_defs.h:121
Class for declaration of the input data that are floating point numbers.
Definition: type_base.hh:392
Distribution * distribution_
Pointer to reference to distribution of elements between processors.
static constexpr Mask input_copy
A field that is input of its equation and can not read from input, thus must be set by copy...
Definition: field_flag.hh:29
static Input::Type::Selection output_selection
Class implements the radioactive decay chain.
Accessor to the data with type Type::Record.
Definition: accessors.hh:327
const Ret val(const string &key) const
#define ASSERT_LESS(a, b)
Definition: global_defs.h:164
#define START_TIMER(tag)
Starts a timer with specified tag.
void initialize_fields()
Initializes field sets.
Mesh * mesh_
Definition: equation.hh:218
virtual Value::return_type const & value(const Point &p, const ElementAccessor< spacedim > &elm) const
Definition: field.hh:312
void set_field(const std::string &dest_field_name, FieldCommon &source)
Definition: field_set.cc:101
void set_up_components()
EqData()
Collect all fields.
static Input::Type::Record input_type
void * xmalloc(size_t size)
Memory allocation with checking.
Definition: system.cc:209
Accessor to the polymorphic input data of a type given by an AbstracRecord object.
Definition: accessors.hh:448
double ** conc_immobile
virtual void set_time_governor(TimeGovernor &time)
Definition: equation.cc:80
Support classes for parallel programing.
ReactionTerm * reaction_immobile
Reaction running in immobile zone.
Sorption model in mobile zone in case dual porosity is considered.
Definition: sorption.hh:74
void output_vector_gather(void) override
Gathers all the parallel vectors to enable them to be output.
Field< 3, FieldValue< 3 >::Vector > init_conc_immobile
Initial concentrations in the immobile zone.
virtual void update_solution()
Definition: equation.hh:105
void output(OutputTime *stream)
Definition: field_set.cc:143
const Selection & close() const
Input::Record input_record_
Definition: equation.hh:220
void set_components(const std::vector< string > &names)
Definition: field_set.hh:151
int * el_4_loc_
Indices of elements belonging to local dofs.
double dt() const
double ** compute_reaction(double **concentrations, int loc_el) override
#define END_TIMER(tag)
Ends a timer with specified tag.
static Input::Type::Record input_type
Input record for class RadioactiveDecay.
arma::vec::fixed< spacedim > centre() const
Definition: accessors.hh:81
void set_mesh(const Mesh &mesh)
Definition: field_set.hh:157
ReactionTerm * reaction_mobile
Reaction running in mobile zone.
Record type proxy class.
Definition: type_record.hh:169
Field< 3, FieldValue< 3 >::Scalar > porosity
Porosity field.
Class implements the linear reactions.
#define xfree(p)
Definition: system.hh:108
Class for representation SI units of Fields.
Definition: unit_si.hh:31
static UnitSI & dimensionless()
Returns dimensionless unit.
Definition: unit_si.cc:44
DualPorosity data.
Vec * vconc_immobile
PETSC concentration vector for immobile phase (parallel).
#define THROW(whole_exception_expr)
Wrapper for throw. Saves the throwing point.
Definition: exceptions.hh:34
ReactionTerm & concentration_matrix(double **concentration, Distribution *conc_distr, int *el_4_loc, int *row_4_el)
Template for classes storing finite set of named values.
ReactionTerm & substances(SubstanceList &substances)
Sets the names of substances considered in transport.
TimeGovernor * time_
Definition: equation.hh:219
FieldSet input_data_set_
ElementVector element
Vector of elements of the mesh.
Definition: mesh.h:205
unsigned int lsize(int proc) const
get local size
Record & declare_key(const string &key, const KeyType &type, const Default &default_value, const string &description)
Definition: type_record.cc:430