Flow123d  release_1.8.2-1603-g0109a2b
dual_porosity.cc
Go to the documentation of this file.
1 /*!
2  *
3  * Copyright (C) 2015 Technical University of Liberec. All rights reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU General Public License version 3 as published by the
7  * Free Software Foundation. (http://www.gnu.org/licenses/gpl-3.0.en.html)
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12  *
13  *
14  * @file dual_porosity.cc
15  * @brief
16  */
17 
18 #include <iostream>
19 #include <stdlib.h>
20 #include <math.h>
21 
24 #include "system/system.hh"
25 #include "system/sys_profiler.hh"
26 
27 #include "la/distribution.hh"
28 #include "mesh/mesh.h"
29 #include "mesh/elements.h"
30 #include "mesh/region.hh"
32 
33 #include "reaction/sorption.hh"
37 #include "input/factory.hh"
38 
39 FLOW123D_FORCE_LINK_IN_CHILD(dualPorosity)
40 
41 
42 using namespace Input::Type;
43 
44 
45 
46 const Record & DualPorosity::get_input_type() {
47  return Record("DualPorosity",
48  "Dual porosity model in transport problems.\n"
49  "Provides computing the concentration of substances in mobile and immobile zone.\n"
50  )
52  .declare_key("input_fields", Array(DualPorosity::EqData().make_field_descriptor_type("DualPorosity")), Default::obligatory(),
53  "Containes region specific data necessary to construct dual porosity model.")
54  .declare_key("scheme_tolerance", Double(0.0), Default("1e-3"),
55  "Tolerance according to which the explicit Euler scheme is used or not."
56  "Set 0.0 to use analytic formula only (can be slower).")
57 
58  .declare_key("reaction_mobile", ReactionTerm::get_input_type(), Default::optional(), "Reaction model in mobile zone.")
59  .declare_key("reaction_immobile", ReactionTerm::get_input_type(), Default::optional(), "Reaction model in immobile zone.")
60 
61  .declare_key("output_fields",
62  Array(EqData().output_fields
63  .make_output_field_selection(
64  "DualPorosity_output_fields",
65  "Selection of field names of Dual Porosity model available for output.")
66  .close()),
67  Default("\"conc_immobile\""), "List of fields to write to output stream.")
68  .close();
69 }
70 
71 const int DualPorosity::registrar =
72  Input::register_class< DualPorosity, Mesh &, Input::Record >("DualPorosity") +
74 
76 {
78  .name("diffusion_rate_immobile")
79  .description("Diffusion coefficient of non-equilibrium linear exchange between mobile and immobile zone.")
80  .input_default("0")
81  .units( UnitSI().s(-1) );
82 
83  *this += porosity_immobile
84  .name("porosity_immobile")
85  .description("Porosity of the immobile zone.")
86  .input_default("0")
88 
89  *this += init_conc_immobile
90  .name("init_conc_immobile")
91  .description("Initial concentration of substances in the immobile zone.")
92  .units( UnitSI().kg().m(-3) );
93 
94  //creating field for porosity that is set later from the governing equation (transport)
95  *this +=porosity
96  .name("porosity")
99 
100  output_fields += *this;
101  output_fields += conc_immobile.name("conc_immobile").units( UnitSI().kg().m(-3) ).flags(FieldFlag::equation_result);
102 }
103 
105  : ReactionTerm(init_mesh, in_rec)
106 {
107  //set pointer to equation data fieldset
108  this->eq_data_ = &data_;
109 
110  //reads input and creates possibly other reaction terms
111  make_reactions();
112  //read value from input
113  scheme_tolerance_ = input_record_.val<double>("scheme_tolerance");
114 }
115 
117 {
118  VecScatterDestroy(&(vconc_out_scatter));
119  VecDestroy(vconc_immobile);
120 
121  for (unsigned int sbi = 0; sbi < substances_.size(); sbi++)
122  {
123  //no mpi vectors
124  xfree(conc_immobile[sbi]);
125  }
126 
128 }
129 
130 
133  if ( reactions_it )
134  {
135  // TODO: allowed instances in this case are only
136  // FirstOrderReaction, RadioactiveDecay and SorptionMob
137  reaction_mobile = (*reactions_it).factory< ReactionTerm, Mesh &, Input::Record >(*mesh_, *reactions_it);
138  } else
139  {
140  reaction_mobile = nullptr;
141  }
142 
143  reactions_it = input_record_.find<Input::AbstractRecord>("reaction_immobile");
144  if ( reactions_it )
145  {
146  // TODO: allowed instances in this case are only
147  // FirstOrderReaction, RadioactiveDecay and SorptionImmob
148  reaction_immobile = (*reactions_it).factory< ReactionTerm, Mesh &, Input::Record >(*mesh_, *reactions_it);
149  } else
150  {
151  reaction_immobile = nullptr;
152  }
153 
154 }
155 
157 {
158  OLD_ASSERT(distribution_ != nullptr, "Distribution has not been set yet.\n");
159  OLD_ASSERT(time_ != nullptr, "Time governor has not been set yet.\n");
160  OLD_ASSERT(output_stream_,"Null output stream.");
162 
163  //allocating memory for immobile concentration matrix
164  conc_immobile = (double**) xmalloc(substances_.size() * sizeof(double*));
165  conc_immobile_out.clear();
166  conc_immobile_out.resize( substances_.size() );
167  for (unsigned int sbi = 0; sbi < substances_.size(); sbi++)
168  {
169  conc_immobile[sbi] = (double*) xmalloc(distribution_->lsize() * sizeof(double));
170  conc_immobile_out[sbi].resize( distribution_->size() );
171  }
173 
175 
176  if(reaction_mobile)
177  {
178  reaction_mobile->substances(substances_)
179  .output_stream(output_stream_)
180  .concentration_matrix(concentration_matrix_, distribution_, el_4_loc_, row_4_el_)
181  .set_time_governor(*time_);
182  reaction_mobile->initialize();
183  }
184 
186  {
187  reaction_immobile->substances(substances_)
188  .output_stream(output_stream_)
189  .concentration_matrix(conc_immobile, distribution_, el_4_loc_, row_4_el_)
190  .set_time_governor(*time_);
191  reaction_immobile->initialize();
192  }
193 
194 }
195 
197 {
199  //setting fields that are set from input file
202 
203  //setting fields in data
204  data_.set_mesh(*mesh_);
205 
206  //initialization of output
207  output_array = input_record_.val<Input::Array>("output_fields");
212  for (unsigned int sbi=0; sbi<substances_.size(); sbi++)
213  {
214  // create shared pointer to a FieldElementwise and push this Field to output_field on all regions
215  auto output_field_ptr = conc_immobile_out[sbi].create_field<3, FieldValue<3>::Scalar>(substances_.size());
216  data_.conc_immobile[sbi].set_field(mesh_->region_db().get_region_set("ALL"), output_field_ptr, 0);
217  }
218  output_stream_->add_admissible_field_names(output_array);
219 }
220 
221 
223 {
224  OLD_ASSERT(distribution_ != nullptr, "Distribution has not been set yet.\n");
225  OLD_ASSERT(time_ != nullptr, "Time governor has not been set yet.\n");
226  OLD_ASSERT(output_stream_,"Null output stream.");
228 
229  //coupling - passing fields
230  if(reaction_mobile)
231  if (typeid(*reaction_mobile) == typeid(SorptionMob))
232  {
233  reaction_mobile->data().set_field("porosity", data_["porosity"]);
234  reaction_mobile->data().set_field("porosity_immobile", data_["porosity_immobile"]);
235  }
237  if (typeid(*reaction_immobile) == typeid(SorptionImmob))
238  {
239  reaction_immobile->data().set_field("porosity", data_["porosity"]);
240  reaction_immobile->data().set_field("porosity_immobile", data_["porosity_immobile"]);
241  }
242 
245 
246  // write initial condition
250 
251  if(reaction_mobile)
252  reaction_mobile->zero_time_step();
253 
255  reaction_immobile->zero_time_step();
256 }
257 
259 {
260  //setting initial condition for immobile concentration matrix
261  for (unsigned int loc_el = 0; loc_el < distribution_->lsize(); loc_el++)
262  {
263  unsigned int index = el_4_loc_[loc_el];
264  ElementAccessor<3> ele_acc = mesh_->element_accessor(index);
265  arma::vec value = data_.init_conc_immobile.value(ele_acc.centre(), ele_acc);
266 
267  for (unsigned int sbi=0; sbi < substances_.size(); sbi++)
268  {
269  conc_immobile[sbi][loc_el] = value(sbi);
270  }
271  }
272 }
273 
275 {
277 
278  START_TIMER("dual_por_exchange_step");
279  for (unsigned int loc_el = 0; loc_el < distribution_->lsize(); loc_el++)
280  {
282  }
283  END_TIMER("dual_por_exchange_step");
284 
285  if(reaction_mobile) reaction_mobile->update_solution();
286  if(reaction_immobile) reaction_immobile->update_solution();
287 }
288 
289 
290 double **DualPorosity::compute_reaction(double **concentrations, int loc_el)
291 {
292  unsigned int sbi;
293  double conc_average, // weighted (by porosity) average of concentration
294  conc_mob, conc_immob, // new mobile and immobile concentration
295  previous_conc_mob, previous_conc_immob, // mobile and immobile concentration in previous time step
296  conc_max, //difference between concentration and average concentration
297  por_mob, por_immob; // mobile and immobile porosity
298 
299  // get data from fields
300  ElementFullIter ele = mesh_->element(el_4_loc_[loc_el]);
301  por_mob = data_.porosity.value(ele->centre(),ele->element_accessor());
302  por_immob = data_.porosity_immobile.value(ele->centre(),ele->element_accessor());
303  arma::Col<double> diff_vec = data_.diffusion_rate_immobile.value(ele->centre(), ele->element_accessor());
304 
305  // if porosity_immobile == 0 then mobile concentration stays the same
306  // and immobile concentration cannot change
307  if (por_immob == 0.0) return conc_immobile;
308 
309  double exponent,
310  temp_exponent = (por_mob + por_immob) / (por_mob * por_immob) * time_->dt();
311 
312  for (sbi = 0; sbi < substances_.size(); sbi++) //over all substances
313  {
314  exponent = diff_vec[sbi] * temp_exponent;
315  //previous values
316  previous_conc_mob = concentration_matrix_[sbi][loc_el];
317  previous_conc_immob = conc_immobile[sbi][loc_el];
318 
319  // ---compute average concentration------------------------------------------
320  conc_average = ((por_mob * previous_conc_mob) + (por_immob * previous_conc_immob))
321  / (por_mob + por_immob);
322 
323  conc_max = std::max(previous_conc_mob-conc_average, previous_conc_immob-conc_average);
324 
325  if( conc_max <= (2*scheme_tolerance_/(exponent*exponent)*conc_average) ) // forward euler
326  {
327  double temp = diff_vec[sbi]*(previous_conc_immob - previous_conc_mob) * time_->dt();
328  // ---compute concentration in mobile area
329  conc_mob = temp / por_mob + previous_conc_mob;
330 
331  // ---compute concentration in immobile area
332  conc_immob = -temp / por_immob + previous_conc_immob;
333  }
334  else //analytic solution
335  {
336  double temp = exp(-exponent);
337  // ---compute concentration in mobile area
338  conc_mob = (previous_conc_mob - conc_average) * temp + conc_average;
339 
340  // ---compute concentration in immobile area
341  conc_immob = (previous_conc_immob - conc_average) * temp + conc_average;
342  }
343 
344  concentration_matrix_[sbi][loc_el] = conc_mob;
345  conc_immobile[sbi][loc_el] = conc_immob;
346  }
347 
348  return conc_immobile;
349 }
350 
351 
353 {
354  int sbi, n_subst;
355  n_subst = substances_.size();
356 
357  vconc_immobile = (Vec*) xmalloc(n_subst * (sizeof(Vec)));
358 
359 
360  for (sbi = 0; sbi < n_subst; sbi++) {
361  VecCreateMPIWithArray(PETSC_COMM_WORLD,1, distribution_->lsize(), mesh_->n_elements(), conc_immobile[sbi],
362  &vconc_immobile[sbi]);
363  VecZeroEntries(vconc_immobile[sbi]);
364 
365  // if(rank == 0)
366  VecZeroEntries(conc_immobile_out[sbi].get_data_petsc());
367  }
368 
369  // create output vector scatter
370  IS is;
371  ISCreateGeneral(PETSC_COMM_SELF, mesh_->n_elements(), row_4_el_, PETSC_COPY_VALUES, &is); //WithArray
372  VecScatterCreate(vconc_immobile[0], is, conc_immobile_out[0].get_data_petsc(), PETSC_NULL, &vconc_out_scatter);
373  ISDestroy(&(is));
374 }
375 
376 
378 {
379  unsigned int sbi;
380 
381  for (sbi = 0; sbi < substances_.size(); sbi++) {
382  VecScatterBegin(vconc_out_scatter, vconc_immobile[sbi], conc_immobile_out[sbi].get_data_petsc(), INSERT_VALUES, SCATTER_FORWARD);
383  VecScatterEnd(vconc_out_scatter, vconc_immobile[sbi], conc_immobile_out[sbi].get_data_petsc(), INSERT_VALUES, SCATTER_FORWARD);
384  }
385 }
386 
387 
389 {
391 
392  // Register fresh output data
395 
396  if (reaction_mobile) reaction_mobile->output_data();
397  if (reaction_immobile) reaction_immobile->output_data();
398 }
void output_type(OutputTime::DiscreteSpace rt)
Definition: field_set.hh:203
unsigned int size() const
get global size
void set_input_list(Input::Array input_list)
Definition: field_set.hh:180
Sorption model in immobile zone in case dual porosity is considered.
Definition: sorption.hh:123
FieldSet * eq_data_
Definition: equation.hh:230
void set_initial_condition()
Sets initial condition from input.
VecScatter vconc_out_scatter
Output vector scatter.
std::shared_ptr< ReactionTerm > reaction_mobile
Reaction running in mobile zone.
Accessor to input data conforming to declared Array.
Definition: accessors.hh:552
unsigned int size() const
Returns number of keys in the Record.
Definition: type_record.hh:578
void output_data(void) override
Main output routine.
double ** concentration_matrix_
const std::vector< std::string > & names()
Definition: substance.hh:85
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:50
FieldSet output_fields
Fields indended for output, i.e. all input fields plus those representing solution.
static Input::Type::Abstract & get_input_type()
void set_time(const TimeStep &time, LimitSide limit_side)
Definition: field_set.hh:195
SubstanceList substances_
Names belonging to substances.
RegionSet get_region_set(const string &set_name) const
Definition: region.cc:321
void zero_time_step() override
Definition: mesh.h:99
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.
std::vector< VectorSeqDouble > conc_immobile_out
concentration array output for immobile phase (gathered - sequential)
const RegionDB & region_db() const
Definition: mesh.h:156
ElementAccessor< 3 > element_accessor(unsigned int idx, bool boundary=false)
Definition: mesh.cc:700
void allocate_output_mpi(void)
Allocates petsc vectors, prepares them for output and creates output vector scatter.
const TimeStep & step(int index=-1) const
FieldCommon & units(const UnitSI &units)
Set basic units of the field.
unsigned int size() const
Definition: substance.hh:87
Class for declaration of inputs sequences.
Definition: type_base.hh:316
std::shared_ptr< OutputTime > output_stream_
Pointer to a transport output stream.
void update_solution(void) override
ReactionTerm(Mesh &init_mesh, Input::Record in_rec)
Constructor.
Class ReactionTerm is an abstract class representing reaction term in transport.
Input::Array output_array
Class Dual_por_exchange implements the model of dual porosity.
virtual Record & derive_from(Abstract &parent)
Method to derive new Record from an AbstractRecord parent.
Definition: type_record.cc:193
double scheme_tolerance_
Dual porosity computational scheme tolerance.
#define OLD_ASSERT(...)
Definition: global_defs.h:128
void setup_components()
std::shared_ptr< ReactionTerm > reaction_immobile
Reaction running in immobile zone.
void make_reactions()
Resolves construction of following reactions.
unsigned int n_elements() const
Definition: mesh.h:142
void initialize() override
Prepares the object to usage.
Class for declaration of the input data that are floating point numbers.
Definition: type_base.hh:513
Distribution * distribution_
Pointer to reference to distribution of elements between processors.
static constexpr Mask equation_result
Match result fields. These are never given by input or copy of input.
Definition: field_flag.hh:49
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:39
FieldCommon & input_default(const string &input_default)
Accessor to the data with type Type::Record.
Definition: accessors.hh:277
const Ret val(const string &key) const
#define START_TIMER(tag)
Starts a timer with specified tag.
#define OLD_ASSERT_LESS(a, b)
Definition: global_defs.h:131
void initialize_fields()
Initializes field sets.
Mesh * mesh_
Definition: equation.hh:221
virtual Value::return_type const & value(const Point &p, const ElementAccessor< spacedim > &elm) const
Definition: field.hh:337
Record & declare_key(const string &key, std::shared_ptr< TypeBase > type, const Default &default_value, const string &description, TypeBase::attribute_map key_attributes=TypeBase::attribute_map())
Declares a new key of the Record.
Definition: type_record.cc:459
const Record & close() const
Close the Record for further declarations of keys.
Definition: type_record.cc:271
EqData()
Collect all fields.
Class representing dual porosity model in transport.
void * xmalloc(size_t size)
Memory allocation with checking.
Definition: system.cc:198
Accessor to the polymorphic input data of a type given by an AbstracRecord object.
Definition: accessors.hh:444
double ** conc_immobile
Support classes for parallel programing.
Sorption model in mobile zone in case dual porosity is considered.
Definition: sorption.hh:95
void output_vector_gather(void) override
Gathers all the parallel vectors to enable them to be output.
FieldCommon & description(const string &description)
Definition: field_common.hh:90
virtual MultiFieldValue::return_type value(const Point &p, const ElementAccessor< spacedim > &elm) const
Input::Record input_record_
Definition: equation.hh:223
void output(std::shared_ptr< OutputTime > stream)
Definition: field_set.cc:169
void set_components(const std::vector< string > &names)
Definition: field_set.hh:167
int * el_4_loc_
Indices of elements belonging to local dofs.
double dt() const
double ** compute_reaction(double **concentrations, int loc_el) override
FieldCommon & name(const string &name)
Definition: field_common.hh:83
#define END_TIMER(tag)
Ends a timer with specified tag.
MultiField< 3, FieldValue< 3 >::Scalar > diffusion_rate_immobile
Mass transfer coefficients between mobile and immobile pores.
arma::vec::fixed< spacedim > centre() const
Definition: accessors.hh:91
static const int registrar
Registrar of class to factory.
void set_mesh(const Mesh &mesh)
Definition: field_set.hh:173
MultiField< 3, FieldValue< 3 >::Scalar > init_conc_immobile
Initial concentrations in the immobile zone.
Record type proxy class.
Definition: type_record.hh:171
Field< 3, FieldValue< 3 >::Scalar > porosity
Porosity field.
FieldCommon & flags(FieldFlag::Flags::Mask mask)
#define xfree(p)
Definition: system.hh:95
Class for representation SI units of Fields.
Definition: unit_si.hh:40
static const Input::Type::Record & get_input_type()
static UnitSI & dimensionless()
Returns dimensionless unit.
Definition: unit_si.cc:53
DualPorosity data.
Vec * vconc_immobile
PETSC concentration vector for immobile phase (parallel).
This file contains classes representing sorption model. Sorption model can be computed both in case t...
#define FLOW123D_FORCE_LINK_IN_CHILD(x)
Definition: global_defs.h:246
TimeGovernor * time_
Definition: equation.hh:222
FieldSet input_data_set_
ElementVector element
Vector of elements of the mesh.
Definition: mesh.h:225
unsigned int lsize(int proc) const
get local size