Flow123d  jenkins-Flow123d-linux-release-multijob-282
transport_dg.cc
Go to the documentation of this file.
1 /*!
2  *
3  * Copyright (C) 2007 Technical University of Liberec. All rights reserved.
4  *
5  * Please make a following refer to Flow123d on your project site if you use the program for any purpose,
6  * especially for academic research:
7  * Flow123d, Research Centre: Advanced Remedial Technologies, Technical University of Liberec, Czech Republic
8  *
9  * This program is free software; you can redistribute it and/or modify it under the terms
10  * of the GNU General Public License version 3 as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
13  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14  * See the GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along with this program; if not,
17  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 021110-1307, USA.
18  *
19  *
20  * $Id$
21  * $Revision$
22  * $LastChangedBy$
23  * $LastChangedDate$
24  *
25  * @file
26  * @brief Discontinuous Galerkin method for equation of transport with dispersion.
27  * @author Jan Stebel
28  */
29 
30 #include "system/sys_profiler.hh"
32 
33 #include "io/output_time.hh"
35 #include "fem/mapping_p1.hh"
36 #include "fem/fe_values.hh"
37 #include "fem/fe_p.hh"
38 #include "fem/fe_rt.hh"
39 #include "fields/field_fe.hh"
40 #include "flow/darcy_flow_mh.hh"
41 #include "la/linsys_PETSC.hh"
44 #include "transport/heat_model.hh"
45 
46 #include "fields/generic_field.hh"
47 
48 using namespace Input::Type;
49 
50 template<class Model>
52  = Selection("DG_variant", "Type of penalty term.")
53  .add_value(non_symmetric, "non-symmetric", "non-symmetric weighted interior penalty DG method")
54  .add_value(incomplete, "incomplete", "incomplete weighted interior penalty DG method")
55  .add_value(symmetric, "symmetric", "symmetric weighted interior penalty DG method");
56 
57 template<class Model>
59  Selection("TransportDG_BC_Type", "Types of boundary condition supported by the transport DG model (solute transport or heat transfer).")
60  .add_value(none, "none", "Homogeneous Neumann boundary condition. Zero flux")
61  .add_value(dirichlet, "dirichlet",
62  "Dirichlet boundary condition."
63  //"Specify the pressure head through the 'bc_pressure' field "
64  //"or the piezometric head through the 'bc_piezo_head' field."
65  )
66  .add_value(neumann, "neumann", "Neumann boundary condition. Prescribe water outflow by the 'bc_flux' field.")
67  .add_value(robin, "robin", "Robin boundary condition. Water outflow equal to $\\sigma (h - h^R)$. "
68  //"Specify the transition coefficient by 'bc_sigma' and the reference pressure head or pieaozmetric head "
69  //"through 'bc_pressure' and 'bc_piezo_head' respectively."
70  )
71  .add_value(inflow, "inflow", "Prescribes the concentration in the inflow water on the inflow part of the boundary.");
72 
73 template<class Model>
75  Model::ModelEqData::get_output_selection_input_type("DG", "DG solver")
76  .copy_values(EqData().make_output_field_selection("").close())
77  .close();
78 
79 template<class Model>
81  = Model::get_input_type("DG", "DG solver")
82  .declare_key("solver", LinSys_PETSC::input_type, Default::obligatory(),
83  "Linear solver for MH problem.")
84  .declare_key("input_fields", Array(TransportDG<Model>::EqData().make_field_descriptor_type(std::string(Model::ModelEqData::name()) + "_DG")), IT::Default::obligatory(), "")
85  .declare_key("dg_variant", TransportDG<Model>::dg_variant_selection_input_type, Default("non-symmetric"),
86  "Variant of interior penalty discontinuous Galerkin method.")
87  .declare_key("dg_order", Integer(0,3), Default("1"),
88  "Polynomial order for finite element in DG method (order 0 is suitable if there is no diffusion/dispersion).")
89  .declare_key("output_fields", Array(EqData::output_selection),
90  Default(Model::ModelEqData::default_output_field()),
91  "List of fields to write to output file.");
92 
93 
94 
95 
96 FEObjects::FEObjects(Mesh *mesh_, unsigned int fe_order)
97 {
98  unsigned int q_order;
99 
100  switch (fe_order)
101  {
102  case 0:
103  q_order = 0;
104  fe1_ = new FE_P_disc<0,1,3>;
105  fe2_ = new FE_P_disc<0,2,3>;
106  fe3_ = new FE_P_disc<0,3,3>;
107  break;
108 
109  case 1:
110  q_order = 2;
111  fe1_ = new FE_P_disc<1,1,3>;
112  fe2_ = new FE_P_disc<1,2,3>;
113  fe3_ = new FE_P_disc<1,3,3>;
114  break;
115 
116  case 2:
117  q_order = 4;
118  fe1_ = new FE_P_disc<2,1,3>;
119  fe2_ = new FE_P_disc<2,2,3>;
120  fe3_ = new FE_P_disc<2,3,3>;
121  break;
122 
123  case 3:
124  q_order = 6;
125  fe1_ = new FE_P_disc<3,1,3>;
126  fe2_ = new FE_P_disc<3,2,3>;
127  fe3_ = new FE_P_disc<3,3,3>;
128  break;
129 
130  default:
131  q_order=0;
132  xprintf(PrgErr, "Unsupported polynomial order %d for finite elements in TransportDG ", fe_order);
133  break;
134  }
135 
136  fe_rt1_ = new FE_RT0<1,3>;
137  fe_rt2_ = new FE_RT0<2,3>;
138  fe_rt3_ = new FE_RT0<3,3>;
139 
140  q0_ = new QGauss<0>(q_order);
141  q1_ = new QGauss<1>(q_order);
142  q2_ = new QGauss<2>(q_order);
143  q3_ = new QGauss<3>(q_order);
144 
145  map0_ = new MappingP1<0,3>;
146  map1_ = new MappingP1<1,3>;
147  map2_ = new MappingP1<2,3>;
148  map3_ = new MappingP1<3,3>;
149 
150  dh_ = new DOFHandlerMultiDim(*mesh_);
151 
152  dh_->distribute_dofs(*fe1_, *fe2_, *fe3_);
153 }
154 
155 
157 {
158  delete fe1_;
159  delete fe2_;
160  delete fe3_;
161  delete fe_rt1_;
162  delete fe_rt2_;
163  delete fe_rt3_;
164  delete q0_;
165  delete q1_;
166  delete q2_;
167  delete q3_;
168  delete map0_;
169  delete map1_;
170  delete map2_;
171  delete map3_;
172  delete dh_;
173 }
174 
175 template<> FiniteElement<0,3> *FEObjects::fe<0>() { return 0; }
176 template<> FiniteElement<1,3> *FEObjects::fe<1>() { return fe1_; }
177 template<> FiniteElement<2,3> *FEObjects::fe<2>() { return fe2_; }
178 template<> FiniteElement<3,3> *FEObjects::fe<3>() { return fe3_; }
179 
180 template<> FiniteElement<0,3> *FEObjects::fe_rt<0>() { return 0; }
181 template<> FiniteElement<1,3> *FEObjects::fe_rt<1>() { return fe_rt1_; }
182 template<> FiniteElement<2,3> *FEObjects::fe_rt<2>() { return fe_rt2_; }
183 template<> FiniteElement<3,3> *FEObjects::fe_rt<3>() { return fe_rt3_; }
184 
185 template<> Quadrature<0> *FEObjects::q<0>() { return q0_; }
186 template<> Quadrature<1> *FEObjects::q<1>() { return q1_; }
187 template<> Quadrature<2> *FEObjects::q<2>() { return q2_; }
188 template<> Quadrature<3> *FEObjects::q<3>() { return q3_; }
189 
190 template<> Mapping<0,3> *FEObjects::mapping<0>() { return map0_; }
191 template<> Mapping<1,3> *FEObjects::mapping<1>() { return map1_; }
192 template<> Mapping<2,3> *FEObjects::mapping<2>() { return map2_; }
193 template<> Mapping<3,3> *FEObjects::mapping<3>() { return map3_; }
194 
196 
197 
198 template<class Model>
199 TransportDG<Model>::EqData::EqData() : Model::ModelEqData()
200 {
201  *this+=fracture_sigma
202  .name("fracture_sigma")
203  .description(
204  "Coefficient of diffusive transfer through fractures (for each substance).")
206  .input_default("1.0")
208 
209  *this+=dg_penalty
210  .name("dg_penalty")
211  .description(
212  "Penalty parameter influencing the discontinuity of the solution (for each substance). "
213  "Its default value 1 is sufficient in most cases. Higher value diminishes the inter-element jumps.")
215  .input_default("1.0")
217 
218  *this+=bc_type
219  .name("bc_type")
220  .description(
221  "Boundary condition type, possible values: inflow, dirichlet, neumann, robin.")
223  .input_default("\"inflow\"")
225  .flags_add(FieldFlag::in_rhs & FieldFlag::in_main_matrix);
226 
227  *this+=bc_flux
228  .name("bc_flux")
229  .description("Flux in Neumann boundary condition.")
230  .units( UnitSI().kg().m().s(-1).md() )
231  .input_default("0.0")
233  *this+=bc_robin_sigma
234  .name("bc_robin_sigma")
235  .description("Conductivity coefficient in Robin boundary condition.")
236  .units( UnitSI().m(4).s(-1).md() )
237  .input_default("0.0")
238  .flags_add(FieldFlag::in_rhs & FieldFlag::in_main_matrix);
239 
240  *this += region_id.name("region_id")
243 
244  // add all input fields to the output list
245 
246 }
247 
248 template<class Model>
250  : TransportBase(init_mesh, in_rec),
251  allocation_done(false)
252 {
253  // Can not use name() + "constructor" here, since START_TIMER only accepts const char *
254  // due to constexpr optimization.
255  START_TIMER(Model::ModelEqData::name());
256  // Check that Model is derived from AdvectionDiffusionModel.
257  static_assert(std::is_base_of<AdvectionDiffusionModel, Model>::value, "");
258 
259  this->eq_data_ = &data_;
260 
261  time_ = new TimeGovernor(in_rec.val<Input::Record>("time"));
262 
263 
264  // Read names of transported substances.
265  // TODO: Substances should be held in TransportOperatorSplitting only.
266  // Class TransportDG requires only names of components,
267  // and it may have no sense for Model to define Substances
268  // (e.g. if Model represents heat transfer). This should be
269  // resolved when transport classes are refactored so that DG method
270  // can be combined with reactions under operator splitting.
271  Model::init_from_input(in_rec, substances_);
273 
274  // Set up physical parameters.
275  data_.set_mesh(init_mesh);
276  data_.set_components(substances_.names());
277  data_.set_input_list( in_rec.val<Input::Array>("input_fields") );
278  data_.set_limit_side(LimitSide::right);
280 
281 
282  // DG variant and order
283  dg_variant = in_rec.val<DGVariant>("dg_variant");
284  dg_order = in_rec.val<unsigned int>("dg_order");
285 
286  // DG stabilization parameters on boundary edges
287  gamma.resize(n_subst_);
288  for (unsigned int sbi=0; sbi<n_subst_; sbi++)
289  gamma[sbi].resize(mesh_->boundary_.size());
290 
291  // create finite element structures and distribute DOFs
292  feo = new FEObjects(mesh_, dg_order);
293  DBGMSG("TDG: solution size %d\n", feo->dh()->n_global_dofs());
294 
295  // Resize coefficient arrays
296  int qsize = max(feo->q<0>()->size(), max(feo->q<1>()->size(), max(feo->q<2>()->size(), feo->q<3>()->size())));
297  int max_edg_sides = max(mesh_->max_edge_sides(1), max(mesh_->max_edge_sides(2), mesh_->max_edge_sides(3)));
298  mm_coef.resize(qsize);
299  ret_coef.resize(n_subst_);
300  sorption_sources.resize(n_subst_);
301  ad_coef.resize(n_subst_);
302  dif_coef.resize(n_subst_);
303  for (unsigned int sbi=0; sbi<n_subst_; sbi++)
304  {
305  ret_coef[sbi].resize(qsize);
306  ad_coef[sbi].resize(qsize);
307  dif_coef[sbi].resize(qsize);
308  }
309  ad_coef_edg.resize(max_edg_sides);
310  dif_coef_edg.resize(max_edg_sides);
311  for (int sd=0; sd<max_edg_sides; sd++)
312  {
313  ad_coef_edg[sd].resize(n_subst_);
314  dif_coef_edg[sd].resize(n_subst_);
315  for (unsigned int sbi=0; sbi<n_subst_; sbi++)
316  {
317  ad_coef_edg[sd][sbi].resize(qsize);
318  dif_coef_edg[sd][sbi].resize(qsize);
319  }
320  }
321 
322  // register output fields
323  output_rec = in_rec.val<Input::Record>("output_stream");
324  output_vec.resize(n_subst_);
325  output_solution.resize(n_subst_);
326  for (unsigned int sbi=0; sbi<n_subst_; sbi++)
327  {
328  // for each substance we allocate output array and vector
329  output_solution[sbi] = new double[feo->dh()->n_global_dofs()];
330  VecCreateSeqWithArray(PETSC_COMM_SELF, 1, feo->dh()->n_global_dofs(), output_solution[sbi], &output_vec[sbi]);
331  }
332  data_.output_field.set_components(substances_.names());
333  data_.output_field.set_mesh(*mesh_);
334  data_.output_type(OutputTime::CORNER_DATA);
335 
336  data_.output_field.set_up_components();
337  for (unsigned int sbi=0; sbi<n_subst_; sbi++)
338  {
339  // create shared pointer to a FieldFE, pass FE data and push this FieldFE to output_field on all regions
340  std::shared_ptr<FieldFE<3, FieldValue<3>::Scalar> > output_field_ptr(new FieldFE<3, FieldValue<3>::Scalar>);
341  output_field_ptr->set_fe_data(feo->dh(), feo->mapping<1>(), feo->mapping<2>(), feo->mapping<3>(), &output_vec[sbi]);
342  data_.output_field[sbi].set_field(mesh_->region_db().get_region_set("ALL"), output_field_ptr, 0);
343  }
344  data_.set_limit_side(LimitSide::left);
346  output_stream->add_admissible_field_names(in_rec.val<Input::Array>("output_fields"));
347 
348  // set time marks for writing the output
350 
351  // allocate matrix and vector structures
352  ls = new LinSys*[n_subst_];
353  ls_dt = new LinSys*[n_subst_];
354  for (unsigned int sbi = 0; sbi < n_subst_; sbi++) {
355  ls[sbi] = new LinSys_PETSC(feo->dh()->distr());
356  ( (LinSys_PETSC *)ls[sbi] )->set_from_input( in_rec.val<Input::Record>("solver") );
357  ls[sbi]->set_solution(NULL);
358 
359  ls_dt[sbi] = new LinSys_PETSC(feo->dh()->distr());
360  ( (LinSys_PETSC *)ls_dt[sbi] )->set_from_input( in_rec.val<Input::Record>("solver") );
361  }
362  stiffness_matrix = new Mat[n_subst_];
363  mass_matrix = new Mat[n_subst_];
364  rhs = new Vec[n_subst_];
365  mass_vec = new Vec[n_subst_];
366 
367 
368  // initialization of balance object
369  Input::Iterator<Input::Record> it = in_rec.find<Input::Record>("balance");
370  if (it->val<bool>("balance_on"))
371  {
372  balance_ = boost::make_shared<Balance>(Model::balance_prefix(), mesh_, feo->dh()->el_ds(), feo->dh()->get_el_4_loc(), *it);
373 
374  // a not very nice workaround for model with single solution component with no name
375  if (typeid(Model) == typeid(HeatTransferModel))
376  subst_idx = {balance_->add_quantity("energy")};
377  else
378  subst_idx = balance_->add_quantities(substances_.names());
379 
380  balance_->allocate(feo->dh()->distr()->lsize(),
381  max(feo->fe<1>()->n_dofs(), max(feo->fe<2>()->n_dofs(), feo->fe<3>()->n_dofs())));
382  }
383 
384 }
385 
386 template<class Model>
388 {
389  delete time_;
390 
391  if (feo->dh()->el_ds()->myp() == 0)
392  {
393  for (unsigned int i=0; i<n_subst_; i++)
394  {
395  VecDestroy(&output_vec[i]);
396  delete[] output_solution[i];
397  }
398  }
399 
400  for (unsigned int i=0; i<n_subst_; i++)
401  {
402  delete ls[i];
403  delete ls_dt[i];
404  MatDestroy(&stiffness_matrix[i]);
405  MatDestroy(&mass_matrix[i]);
406  VecDestroy(&rhs[i]);
407  VecDestroy(&mass_vec[i]);
408  }
409  delete[] ls;
410  delete[] ls_dt;
411  delete[] stiffness_matrix;
412  delete[] mass_matrix;
413  delete[] rhs;
414  delete[] mass_vec;
415  delete feo;
416 
417  gamma.clear();
418  delete output_stream;
419 }
420 
421 
422 template<class Model>
424 {
425  IS is;
426  VecScatter output_scatter;
427  int idx[] = { 0 };
428  for (unsigned int sbi=0; sbi<n_subst_; sbi++)
429  {
430  // gather solution to output_vec[sbi]
431  ISCreateBlock(PETSC_COMM_SELF, ls[sbi]->size(), 1, idx, PETSC_COPY_VALUES, &is);
432  VecScatterCreate(ls[sbi]->get_solution(), is, output_vec[sbi], PETSC_NULL, &output_scatter);
433  VecScatterBegin(output_scatter, ls[sbi]->get_solution(), output_vec[sbi], INSERT_VALUES, SCATTER_FORWARD);
434  VecScatterEnd(output_scatter, ls[sbi]->get_solution(), output_vec[sbi], INSERT_VALUES, SCATTER_FORWARD);
435  VecScatterDestroy(&(output_scatter));
436  ISDestroy(&(is));
437  }
438 }
439 
440 
441 
442 template<class Model>
444 {
445  START_TIMER(Model::ModelEqData::name());
446  data_.mark_input_times(time_->equation_fixed_mark_type());
447  data_.set_time(time_->step());
448 
449 
450  // set initial conditions
451  set_initial_condition();
452  for (unsigned int sbi = 0; sbi < n_subst_; sbi++)
453  ( (LinSys_PETSC *)ls[sbi] )->set_initial_guess_nonzero();
454 
455  // during preallocation we assemble the matrices and vectors required for mass balance
456  if (balance_ != nullptr)
457  {
458  balance_->units(Model::balance_units());
459 
460  if (!allocation_done) preallocate();
461 
462  for (unsigned int sbi=0; sbi<n_subst_; ++sbi)
463  {
464  balance_->calculate_mass(subst_idx[sbi], ls[sbi]->get_solution());
465  balance_->calculate_source(subst_idx[sbi], ls[sbi]->get_solution());
466  balance_->calculate_flux(subst_idx[sbi], ls[sbi]->get_solution());
467 
468  // add sources due to sorption
470  double mass = 0;
471  balance_->calculate_mass(subst_idx[sbi], ls[sbi]->get_solution(), masses);
472  for (auto reg_mass : masses)
473  mass += reg_mass;
474 
475  sorption_sources[sbi] = mass;
476  }
477  }
478 
479  output_data();
480 }
481 
482 
483 template<class Model>
485 {
486  for (unsigned int i=0; i<n_subst_; i++)
487  {
488  // preallocate system matrix
489  ls[i]->start_allocation();
490  stiffness_matrix[i] = NULL;
491  rhs[i] = NULL;
492 
493  // preallocate mass matrix
494  ls_dt[i]->start_allocation();
495  mass_matrix[i] = NULL;
496  }
497  assemble_stiffness_matrix();
498  assemble_mass_matrix();
499  set_sources();
500  set_boundary_conditions();
501 
502  allocation_done = true;
503 }
504 
505 
506 
507 template<class Model>
509 {
510  START_TIMER("DG-ONE STEP");
511 
512  time_->next_time();
513  time_->view("TDG");
514 
515  START_TIMER("data reinit");
516  data_.set_time(time_->step());
517  END_TIMER("data reinit");
518 
519  // check first time assembly - needs preallocation
520  if (!allocation_done) preallocate();
521 
522  // assemble mass matrix
523  if (mass_matrix[0] == NULL || data_.subset(FieldFlag::in_time_term).changed() )
524  {
525  for (unsigned int i=0; i<n_subst_; i++)
526  {
527  ls_dt[i]->start_add_assembly();
528  ls_dt[i]->mat_zero_entries();
529  }
530  assemble_mass_matrix();
531  for (unsigned int i=0; i<n_subst_; i++)
532  {
533  ls_dt[i]->finish_assembly();
534 
535  // construct mass_vec for initial time
536  if (mass_matrix[i] == NULL)
537  {
538  VecDuplicate(ls[i]->get_solution(), &mass_vec[i]);
539  MatMult(*(ls_dt[i]->get_matrix()), ls[i]->get_solution(), mass_vec[i]);
540 
541  if (balance_ != nullptr && balance_->cumulative())
542  {
543  double total_mass = 0;
544  VecSum(mass_vec[i], &total_mass);
545  sorption_sources[i] -= total_mass;
546  }
547  }
548 
549  if (stiffness_matrix[i] == NULL)
550  MatConvert(*( ls_dt[i]->get_matrix() ), MATSAME, MAT_INITIAL_MATRIX, &mass_matrix[i]);
551  else
552  MatCopy(*( ls_dt[i]->get_matrix() ), mass_matrix[i], DIFFERENT_NONZERO_PATTERN);
553  }
554  }
555 
556  // assemble stiffness matrix
557  if (stiffness_matrix[0] == NULL
558  || data_.subset(FieldFlag::in_main_matrix).changed()
559  || Model::flux_changed)
560  {
561  // new fluxes can change the location of Neumann boundary,
562  // thus stiffness matrix must be reassembled
563  for (unsigned int i=0; i<n_subst_; i++)
564  {
565  ls[i]->start_add_assembly();
566  ls[i]->mat_zero_entries();
567  }
568  assemble_stiffness_matrix();
569  for (unsigned int i=0; i<n_subst_; i++)
570  {
571  ls[i]->finish_assembly();
572 
573  if (stiffness_matrix[i] == NULL)
574  MatConvert(*( ls[i]->get_matrix() ), MATSAME, MAT_INITIAL_MATRIX, &stiffness_matrix[i]);
575  else
576  MatCopy(*( ls[i]->get_matrix() ), stiffness_matrix[i], DIFFERENT_NONZERO_PATTERN);
577  }
578  }
579 
580  // assemble right hand side (due to sources and boundary conditions)
581  if (rhs[0] == NULL
582  || data_.subset(FieldFlag::in_rhs).changed()
583  || Model::flux_changed)
584  {
585  for (unsigned int i=0; i<n_subst_; i++)
586  {
587  ls[i]->start_add_assembly();
588  ls[i]->rhs_zero_entries();
589  }
590  set_sources();
591  set_boundary_conditions();
592  for (unsigned int i=0; i<n_subst_; i++)
593  {
594  ls[i]->finish_assembly();
595 
596  VecDuplicate(*( ls[i]->get_rhs() ), &rhs[i]);
597  VecCopy(*( ls[i]->get_rhs() ), rhs[i]);
598  }
599  }
600 
601  Model::flux_changed = false;
602 
603 
604  /* Apply backward Euler time integration.
605  *
606  * Denoting A the stiffness matrix and M the mass matrix, the algebraic system at the k-th time level reads
607  *
608  * (1/dt M + A)u^k = f + 1/dt M.u^{k-1}
609  *
610  * Hence we modify at each time level the right hand side:
611  *
612  * f^k = f + 1/dt M u^{k-1},
613  *
614  * where f stands for the term stemming from the force and boundary conditions.
615  * Accordingly, we set
616  *
617  * A^k = A + 1/dt M.
618  *
619  */
620  Mat m;
621  START_TIMER("solve");
622  for (unsigned int i=0; i<n_subst_; i++)
623  {
624  MatConvert(stiffness_matrix[i], MATSAME, MAT_INITIAL_MATRIX, &m);
625  MatAXPY(m, 1./time_->dt(), mass_matrix[i], SUBSET_NONZERO_PATTERN);
626  ls[i]->set_matrix(m, DIFFERENT_NONZERO_PATTERN);
627  Vec w;
628  VecDuplicate(rhs[i], &w);
629  VecWAXPY(w, 1./time_->dt(), mass_vec[i], rhs[i]);
630  ls[i]->set_rhs(w);
631 
632  VecDestroy(&w);
633  MatDestroy(&m);
634 
635  ls[i]->solve();
636 
637  MatMult(*(ls_dt[i]->get_matrix()), ls[i]->get_solution(), mass_vec[i]);
638  }
639  END_TIMER("solve");
640 
641  if (balance_ != nullptr)
642  {
643  for (unsigned int sbi=0; sbi<n_subst_; ++sbi)
644  {
645  balance_->calculate_mass(subst_idx[sbi], ls[sbi]->get_solution());
646  balance_->calculate_source(subst_idx[sbi], ls[sbi]->get_solution());
647  balance_->calculate_flux(subst_idx[sbi], ls[sbi]->get_solution());
648  if (balance_->cumulative())
649  {
650  balance_->calculate_cumulative_sources(subst_idx[sbi], ls[sbi]->get_solution(), time_->dt());
651  balance_->calculate_cumulative_fluxes(subst_idx[sbi], ls[sbi]->get_solution(), time_->dt());
652 
653  // add sources due to sorption
655  double mass = 0;
656  balance_->calculate_mass(subst_idx[sbi], ls[sbi]->get_solution(), masses);
657  for (auto reg_mass : masses)
658  mass += reg_mass;
659  double total_mass = 0;
660  VecSum(mass_vec[sbi], &total_mass);
661 
662  balance_->add_cumulative_source(subst_idx[sbi], mass-total_mass-sorption_sources[sbi]);
663  sorption_sources[sbi] = mass - total_mass;
664  }
665  }
666  }
667 
668  END_TIMER("DG-ONE STEP");
669 }
670 
671 
672 template<class Model>
674 {
675  // So far the velocity_vector contains zeros, so we ignore it.
676  // Instead we use the value Side.flux.
677 
678  mh_dh = &dh;
679  Model::flux_changed = true;
680 
681 }
682 
683 
684 template<class Model>
686 {
687  if (!time_->is_current( time_->marks().type_output() )) return;
688 
689  START_TIMER("DG-OUTPUT");
690 
691  // gather the solution from all processors
692  output_vector_gather();
693  data_.subset(FieldFlag::allow_output).set_time( time_->step());
694  data_.output(output_stream);
695  output_stream->write_time_frame();
696 
697  if (balance_ != nullptr)
698  balance_->output(time_->t());
699 
700  END_TIMER("DG-OUTPUT");
701 }
702 
703 
704 template<class Model>
706 {
707  START_TIMER("assemble_mass");
708  if (balance_ != nullptr)
709  balance_->start_mass_assembly(subst_idx);
710  assemble_mass_matrix<1>();
711  assemble_mass_matrix<2>();
712  assemble_mass_matrix<3>();
713  if (balance_ != nullptr)
714  balance_->finish_mass_assembly(subst_idx);
715  END_TIMER("assemble_mass");
716 }
717 
718 
719 template<class Model> template<unsigned int dim>
721 {
722  FEValues<dim,3> fe_values(*feo->mapping<dim>(), *feo->q<dim>(), *feo->fe<dim>(), update_values | update_JxW_values | update_quadrature_points);
723  const unsigned int ndofs = feo->fe<dim>()->n_dofs(), qsize = feo->q<dim>()->size();
724  vector<int> dof_indices(ndofs);
725  PetscScalar local_mass_matrix[ndofs*ndofs];
726  vector<PetscScalar> local_mass_balance_vector(ndofs);
727 
728  // assemble integral over elements
729  for (unsigned int i_cell=0; i_cell<feo->dh()->el_ds()->lsize(); i_cell++)
730  {
731  typename DOFHandlerBase::CellIterator cell = mesh_->element(feo->dh()->el_index(i_cell));
732  if (cell->dim() != dim) continue;
733 
734  fe_values.reinit(cell);
735  feo->dh()->get_dof_indices(cell, (unsigned int*)&(dof_indices[0]));
736  ElementAccessor<3> ele_acc = cell->element_accessor();
737 
738  Model::compute_mass_matrix_coefficient(fe_values.point_list(), ele_acc, mm_coef);
739  Model::compute_retardation_coefficient(fe_values.point_list(), ele_acc, ret_coef);
740 
741  for (unsigned int sbi=0; sbi<n_subst_; ++sbi)
742  {
743  // assemble the local mass matrix
744  for (unsigned int i=0; i<ndofs; i++)
745  {
746  for (unsigned int j=0; j<ndofs; j++)
747  {
748  local_mass_matrix[i*ndofs+j] = 0;
749  for (unsigned int k=0; k<qsize; k++)
750  local_mass_matrix[i*ndofs+j] += (mm_coef[k]+ret_coef[sbi][k])*fe_values.shape_value(j,k)*fe_values.shape_value(i,k)*fe_values.JxW(k);
751  }
752 
753  if (balance_ != nullptr)
754  {
755  local_mass_balance_vector[i] = 0;
756  for (unsigned int k=0; k<qsize; k++)
757  local_mass_balance_vector[i] += mm_coef[k]*fe_values.shape_value(i,k)*fe_values.JxW(k);
758  }
759  }
760 
761  if (balance_ != nullptr)
762  balance_->add_mass_matrix_values(subst_idx[sbi], ele_acc.region().bulk_idx(), dof_indices, local_mass_balance_vector);
763 
764  ls_dt[sbi]->mat_set_values(ndofs, &(dof_indices[0]), ndofs, &(dof_indices[0]), local_mass_matrix);
765  }
766  }
767 }
768 
769 
770 
771 
772 template<class Model>
774 {
775  START_TIMER("assemble_stiffness");
776  START_TIMER("assemble_volume_integrals");
777  assemble_volume_integrals<1>();
778  assemble_volume_integrals<2>();
779  assemble_volume_integrals<3>();
780  END_TIMER("assemble_volume_integrals");
781 
782  START_TIMER("assemble_fluxes_boundary");
783  assemble_fluxes_boundary<1>();
784  assemble_fluxes_boundary<2>();
785  assemble_fluxes_boundary<3>();
786  END_TIMER("assemble_fluxes_boundary");
787 
788  START_TIMER("assemble_fluxes_elem_elem");
789  assemble_fluxes_element_element<1>();
790  assemble_fluxes_element_element<2>();
791  assemble_fluxes_element_element<3>();
792  END_TIMER("assemble_fluxes_elem_elem");
793 
794  START_TIMER("assemble_fluxes_elem_side");
795  assemble_fluxes_element_side<1>();
796  assemble_fluxes_element_side<2>();
797  assemble_fluxes_element_side<3>();
798  END_TIMER("assemble_fluxes_elem_side");
799  END_TIMER("assemble_stiffness");
800 }
801 
802 
803 
804 template<class Model>
805 template<unsigned int dim>
807 {
808  FEValues<dim,3> fv_rt(*feo->mapping<dim>(), *feo->q<dim>(), *feo->fe_rt<dim>(),
810  FEValues<dim,3> fe_values(*feo->mapping<dim>(), *feo->q<dim>(), *feo->fe<dim>(),
812  const unsigned int ndofs = feo->fe<dim>()->n_dofs(), qsize = feo->q<dim>()->size();
813  unsigned int dof_indices[ndofs];
814  vector<arma::vec3> velocity(qsize);
815  vector<arma::vec> sources_sigma(qsize, arma::vec(n_substances()));
816  PetscScalar local_matrix[ndofs*ndofs];
817 
818  // assemble integral over elements
819  for (unsigned int i_cell=0; i_cell<feo->dh()->el_ds()->lsize(); i_cell++)
820  {
821  typename DOFHandlerBase::CellIterator cell = mesh_->element(feo->dh()->el_index(i_cell));
822  if (cell->dim() != dim) continue;
823 
824  fe_values.reinit(cell);
825  fv_rt.reinit(cell);
826  ElementAccessor<3> ele_acc = cell->element_accessor();
827  feo->dh()->get_dof_indices(cell, dof_indices);
828 
829  calculate_velocity(cell, velocity, fv_rt);
830  Model::compute_advection_diffusion_coefficients(fe_values.point_list(), velocity, ele_acc, ad_coef, dif_coef);
831  Model::compute_sources_sigma(fe_values.point_list(), ele_acc, sources_sigma);
832 
833  // assemble the local stiffness matrix
834  for (unsigned int sbi=0; sbi<n_subst_; sbi++)
835  {
836  for (unsigned int i=0; i<ndofs; i++)
837  for (unsigned int j=0; j<ndofs; j++)
838  local_matrix[i*ndofs+j] = 0;
839 
840  for (unsigned int k=0; k<qsize; k++)
841  {
842  for (unsigned int i=0; i<ndofs; i++)
843  {
844  arma::vec3 Kt_grad_i = dif_coef[sbi][k].t()*fe_values.shape_grad(i,k);
845  double ad_dot_grad_i = arma::dot(ad_coef[sbi][k], fe_values.shape_grad(i,k));
846 
847  for (unsigned int j=0; j<ndofs; j++)
848  local_matrix[i*ndofs+j] += (arma::dot(Kt_grad_i, fe_values.shape_grad(j,k))
849  -fe_values.shape_value(j,k)*ad_dot_grad_i
850  +sources_sigma[k][sbi]*fe_values.shape_value(j,k)*fe_values.shape_value(i,k))*fe_values.JxW(k);
851  }
852  }
853  ls[sbi]->mat_set_values(ndofs, (int *)dof_indices, ndofs, (int *)dof_indices, local_matrix);
854  }
855  }
856 }
857 
858 
859 template<class Model>
861 {
862  START_TIMER("assemble_sources");
863  if (balance_ != nullptr)
864  balance_->start_source_assembly(subst_idx);
865  set_sources<1>();
866  set_sources<2>();
867  set_sources<3>();
868  if (balance_ != nullptr)
869  balance_->finish_source_assembly(subst_idx);
870  END_TIMER("assemble_sources");
871 }
872 
873 template<class Model>
874 template<unsigned int dim>
876 {
877  FEValues<dim,3> fe_values(*feo->mapping<dim>(), *feo->q<dim>(), *feo->fe<dim>(),
879  const unsigned int ndofs = feo->fe<dim>()->n_dofs(), qsize = feo->q<dim>()->size();
880  vector<arma::vec> sources_conc(qsize, arma::vec(n_substances())),
881  sources_density(qsize, arma::vec(n_substances())),
882  sources_sigma(qsize, arma::vec(n_substances()));
883  vector<int> dof_indices(ndofs);
884  PetscScalar local_rhs[ndofs];
885  vector<PetscScalar> local_source_balance_vector(ndofs), local_source_balance_rhs(ndofs);
886  double source;
887 
888  // assemble integral over elements
889  for (unsigned int i_cell=0; i_cell<feo->dh()->el_ds()->lsize(); i_cell++)
890  {
891  typename DOFHandlerBase::CellIterator cell = mesh_->element(feo->dh()->el_index(i_cell));
892  if (cell->dim() != dim) continue;
893 
894  fe_values.reinit(cell);
895  feo->dh()->get_dof_indices(cell, (unsigned int *)&(dof_indices[0]));
896 
897  Model::compute_source_coefficients(fe_values.point_list(), cell->element_accessor(), sources_conc, sources_density, sources_sigma);
898 
899  // assemble the local stiffness matrix
900  for (unsigned int sbi=0; sbi<n_subst_; sbi++)
901  {
902  fill_n(local_rhs, ndofs, 0);
903  local_source_balance_vector.assign(ndofs, 0);
904  local_source_balance_rhs.assign(ndofs, 0);
905 
906  // compute sources
907  for (unsigned int k=0; k<qsize; k++)
908  {
909  source = (sources_density[k][sbi] + sources_conc[k][sbi]*sources_sigma[k][sbi])*fe_values.JxW(k);
910 
911  for (unsigned int i=0; i<ndofs; i++)
912  {
913  local_rhs[i] += source*fe_values.shape_value(i,k);
914 
915  if (balance_ != nullptr)
916  {
917  local_source_balance_vector[i] -= sources_sigma[k][sbi]*fe_values.shape_value(i,k)*fe_values.JxW(k);
918  local_source_balance_rhs[i] += source*fe_values.shape_value(i,k);
919  }
920  }
921  }
922  ls[sbi]->rhs_set_values(ndofs, &(dof_indices[0]), local_rhs);
923 
924  if (balance_ != nullptr)
925  {
926  balance_->add_source_matrix_values(subst_idx[sbi], cell->region().bulk_idx(), dof_indices, local_source_balance_vector);
927  balance_->add_source_rhs_values(subst_idx[sbi], cell->region().bulk_idx(), dof_indices, local_source_balance_rhs);
928  }
929  }
930  }
931 }
932 
933 
934 
935 template<class Model>
936 template<unsigned int dim>
938 {
939  vector<FESideValues<dim,3>*> fe_values;
940  FESideValues<dim,3> fsv_rt(*feo->mapping<dim>(), *feo->q<dim-1>(), *feo->fe_rt<dim>(),
941  update_values);
942  const unsigned int ndofs = feo->fe<dim>()->n_dofs(), qsize = feo->q<dim-1>()->size(),
943  n_max_sides = ad_coef_edg.size();
944  vector<unsigned int*> side_dof_indices;
945  PetscScalar local_matrix[ndofs*ndofs];
946  vector<vector<arma::vec3> > side_velocity(n_max_sides);
947  vector<arma::vec> dg_penalty(n_max_sides);
948  double gamma_l, omega[2], transport_flux;
949 
950  for (unsigned int sid=0; sid<n_max_sides; sid++)
951  {
952  side_dof_indices.push_back(new unsigned int[ndofs]);
953  fe_values.push_back(new FESideValues<dim,3>(*feo->mapping<dim>(), *feo->q<dim-1>(), *feo->fe<dim>(),
955  }
956 
957  // assemble integral over sides
958  for (unsigned int iedg=0; iedg<feo->dh()->n_loc_edges(); iedg++)
959  {
960  Edge *edg = &mesh_->edges[feo->dh()->edge_index(iedg)];
961  if (edg->n_sides < 2 || edg->side(0)->element()->dim() != dim) continue;
962 
963  for (int sid=0; sid<edg->n_sides; sid++)
964  {
965  typename DOFHandlerBase::CellIterator cell = edg->side(sid)->element();
966  ElementAccessor<3> ele_acc = cell->element_accessor();
967  feo->dh()->get_dof_indices(cell, side_dof_indices[sid]);
968  fe_values[sid]->reinit(cell, edg->side(sid)->el_idx());
969  fsv_rt.reinit(cell, edg->side(sid)->el_idx());
970  calculate_velocity(cell, side_velocity[sid], fsv_rt);
971  Model::compute_advection_diffusion_coefficients(fe_values[sid]->point_list(), side_velocity[sid], ele_acc, ad_coef_edg[sid], dif_coef_edg[sid]);
972  dg_penalty[sid] = data_.dg_penalty.value(cell->centre(), ele_acc);
973  }
974 
975  // fluxes and penalty
976  for (unsigned int sbi=0; sbi<n_subst_; sbi++)
977  {
978  vector<double> fluxes(edg->n_sides);
979  for (int sid=0; sid<edg->n_sides; sid++)
980  {
981  fluxes[sid] = 0;
982  for (unsigned int k=0; k<qsize; k++)
983  fluxes[sid] += arma::dot(ad_coef_edg[sid][sbi][k], fe_values[sid]->normal_vector(k))*fe_values[sid]->JxW(k);
984  fluxes[sid] /= edg->side(sid)->measure();
985  }
986 
987  for (int s1=0; s1<edg->n_sides; s1++)
988  {
989  for (int s2=s1+1; s2<edg->n_sides; s2++)
990  {
991  ASSERT(edg->side(s1)->valid(), "Invalid side of edge.");
992  if (!feo->dh()->el_is_local(edg->side(s1)->element().index())
993  && !feo->dh()->el_is_local(edg->side(s2)->element().index())) continue;
994 
995  arma::vec3 nv = fe_values[s1]->normal_vector(0);
996 
997  // set up the parameters for DG method
998  set_DG_parameters_edge(*edg, s1, s2, qsize, dif_coef_edg[s1][sbi], dif_coef_edg[s2][sbi], fluxes, fe_values[0]->normal_vector(0), dg_penalty[s1][sbi], dg_penalty[s2][sbi], gamma_l, omega, transport_flux);
999 
1000  int sd[2];
1001  sd[0] = s1;
1002  sd[1] = s2;
1003 
1004 #define AVERAGE(i,k,side_id) (fe_values[sd[side_id]]->shape_value(i,k)*0.5)
1005 #define WAVERAGE(i,k,side_id) (arma::dot(dif_coef_edg[sd[side_id]][sbi][k]*fe_values[sd[side_id]]->shape_grad(i,k),nv)*omega[side_id])
1006 #define JUMP(i,k,side_id) ((side_id==0?1:-1)*fe_values[sd[side_id]]->shape_value(i,k))
1007 
1008  // For selected pair of elements:
1009  for (int n=0; n<2; n++)
1010  {
1011  if (!feo->dh()->el_is_local(edg->side(sd[n])->element().index())) continue;
1012 
1013  for (int m=0; m<2; m++)
1014  {
1015  for (unsigned int i=0; i<fe_values[sd[n]]->n_dofs(); i++)
1016  for (unsigned int j=0; j<fe_values[sd[m]]->n_dofs(); j++)
1017  local_matrix[i*fe_values[sd[m]]->n_dofs()+j] = 0;
1018 
1019  for (unsigned int k=0; k<qsize; k++)
1020  {
1021  double flux_times_JxW = transport_flux*fe_values[0]->JxW(k);
1022  double gamma_times_JxW = gamma_l*fe_values[0]->JxW(k);
1023 
1024  for (unsigned int i=0; i<fe_values[sd[n]]->n_dofs(); i++)
1025  {
1026  double flux_JxW_jump_i = flux_times_JxW*JUMP(i,k,n);
1027  double gamma_JxW_jump_i = gamma_times_JxW*JUMP(i,k,n);
1028  double JxW_jump_i = fe_values[0]->JxW(k)*JUMP(i,k,n);
1029  double JxW_var_wavg_i = fe_values[0]->JxW(k)*WAVERAGE(i,k,n)*dg_variant;
1030 
1031  for (unsigned int j=0; j<fe_values[sd[m]]->n_dofs(); j++)
1032  {
1033  int index = i*fe_values[sd[m]]->n_dofs()+j;
1034 
1035  // flux due to transport (applied on interior edges) (average times jump)
1036  local_matrix[index] += flux_JxW_jump_i*AVERAGE(j,k,m);
1037 
1038  // penalty enforcing continuity across edges (applied on interior and Dirichlet edges) (jump times jump)
1039  local_matrix[index] += gamma_JxW_jump_i*JUMP(j,k,m);
1040 
1041  // terms due to diffusion
1042  local_matrix[index] -= WAVERAGE(j,k,m)*JxW_jump_i;
1043  local_matrix[index] -= JUMP(j,k,m)*JxW_var_wavg_i;
1044  }
1045  }
1046  }
1047  ls[sbi]->mat_set_values(fe_values[sd[n]]->n_dofs(), (int *)side_dof_indices[sd[n]], fe_values[sd[m]]->n_dofs(), (int *)side_dof_indices[sd[m]], local_matrix);
1048  }
1049  }
1050 #undef AVERAGE
1051 #undef WAVERAGE
1052 #undef JUMP
1053  }
1054  }
1055  }
1056  }
1057 
1058  for (unsigned int i=0; i<n_max_sides; i++)
1059  {
1060  delete fe_values[i];
1061  delete[] side_dof_indices[i];
1062  }
1063 }
1064 
1065 
1066 template<class Model>
1067 template<unsigned int dim>
1069 {
1070  FESideValues<dim,3> fe_values_side(*feo->mapping<dim>(), *feo->q<dim-1>(), *feo->fe<dim>(),
1072  FESideValues<dim,3> fsv_rt(*feo->mapping<dim>(), *feo->q<dim-1>(), *feo->fe_rt<dim>(),
1073  update_values);
1074  const unsigned int ndofs = feo->fe<dim>()->n_dofs(), qsize = feo->q<dim-1>()->size();
1075  unsigned int side_dof_indices[ndofs];
1076  PetscScalar local_matrix[ndofs*ndofs];
1077  vector<arma::vec3> side_velocity;
1078  vector<arma::vec> robin_sigma(qsize, arma::vec(n_substances()));
1079  arma::vec dg_penalty;
1080  double gamma_l;
1081 
1082  // assemble boundary integral
1083  for (unsigned int iedg=0; iedg<feo->dh()->n_loc_edges(); iedg++)
1084  {
1085  Edge *edg = &mesh_->edges[feo->dh()->edge_index(iedg)];
1086  if (edg->n_sides > 1) continue;
1087  // check spatial dimension
1088  if (edg->side(0)->dim() != dim-1) continue;
1089  // skip edges lying not on the boundary
1090  if (edg->side(0)->cond() == NULL) continue;
1091 
1092  SideIter side = edg->side(0);
1093  typename DOFHandlerBase::CellIterator cell = side->element();
1094  ElementAccessor<3> ele_acc = cell->element_accessor();
1095  feo->dh()->get_dof_indices(cell, side_dof_indices);
1096  fe_values_side.reinit(cell, side->el_idx());
1097  fsv_rt.reinit(cell, side->el_idx());
1098 
1099  calculate_velocity(cell, side_velocity, fsv_rt);
1100  Model::compute_advection_diffusion_coefficients(fe_values_side.point_list(), side_velocity, ele_acc, ad_coef, dif_coef);
1101  dg_penalty = data_.dg_penalty.value(cell->centre(), ele_acc);
1102  arma::uvec bc_type = data_.bc_type.value(side->cond()->element()->centre(), side->cond()->element_accessor());
1103  data_.bc_robin_sigma.value_list(fe_values_side.point_list(), side->cond()->element_accessor(), robin_sigma);
1104 
1105  for (unsigned int sbi=0; sbi<n_subst_; sbi++)
1106  {
1107  for (unsigned int i=0; i<ndofs; i++)
1108  for (unsigned int j=0; j<ndofs; j++)
1109  local_matrix[i*ndofs+j] = 0;
1110 
1111  // On Neumann boundaries we have only term from integrating by parts the advective term,
1112  // on Dirichlet boundaries we additionally apply the penalty which enforces the prescribed value.
1113  double side_flux = 0;
1114  for (unsigned int k=0; k<qsize; k++)
1115  side_flux += arma::dot(ad_coef[sbi][k], fe_values_side.normal_vector(k))*fe_values_side.JxW(k);
1116  double transport_flux = side_flux/side->measure();
1117 
1118  if (bc_type[sbi] == EqData::dirichlet)
1119  {
1120  // set up the parameters for DG method
1121  set_DG_parameters_boundary(side, qsize, dif_coef[sbi], transport_flux, fe_values_side.normal_vector(0), dg_penalty[sbi], gamma_l);
1122  gamma[sbi][side->cond_idx()] = gamma_l;
1123  transport_flux += gamma_l;
1124  }
1125 
1126  // fluxes and penalty
1127  for (unsigned int k=0; k<qsize; k++)
1128  {
1129  double flux_times_JxW;
1130  if (bc_type[sbi] == EqData::robin)
1131  flux_times_JxW = (transport_flux + robin_sigma[k][sbi])*fe_values_side.JxW(k);
1132  else if (bc_type[sbi] == EqData::inflow && side_flux < 0)
1133  flux_times_JxW = 0;
1134  else
1135  flux_times_JxW = transport_flux*fe_values_side.JxW(k);
1136 
1137  for (unsigned int i=0; i<ndofs; i++)
1138  {
1139  for (unsigned int j=0; j<ndofs; j++)
1140  {
1141  // flux due to advection and penalty
1142  local_matrix[i*ndofs+j] += flux_times_JxW*fe_values_side.shape_value(i,k)*fe_values_side.shape_value(j,k);
1143 
1144  // flux due to diffusion (only on dirichlet and inflow boundary)
1145  if (bc_type[sbi] == EqData::dirichlet)
1146  local_matrix[i*ndofs+j] -= (arma::dot(dif_coef[sbi][k]*fe_values_side.shape_grad(j,k),fe_values_side.normal_vector(k))*fe_values_side.shape_value(i,k)
1147  + arma::dot(dif_coef[sbi][k]*fe_values_side.shape_grad(i,k),fe_values_side.normal_vector(k))*fe_values_side.shape_value(j,k)*dg_variant
1148  )*fe_values_side.JxW(k);
1149  }
1150  }
1151  }
1152 
1153  ls[sbi]->mat_set_values(ndofs, (int *)side_dof_indices, ndofs, (int *)side_dof_indices, local_matrix);
1154  }
1155  }
1156 }
1157 
1158 
1159 template<class Model>
1160 template<unsigned int dim>
1162 {
1163 
1164  if (dim == 1) return;
1165  FEValues<dim-1,3> fe_values_vb(*feo->mapping<dim-1>(), *feo->q<dim-1>(), *feo->fe<dim-1>(),
1167  FESideValues<dim,3> fe_values_side(*feo->mapping<dim>(), *feo->q<dim-1>(), *feo->fe<dim>(),
1169  FESideValues<dim,3> fsv_rt(*feo->mapping<dim>(), *feo->q<dim-1>(), *feo->fe_rt<dim>(),
1170  update_values);
1171  FEValues<dim-1,3> fv_rt(*feo->mapping<dim-1>(), *feo->q<dim-1>(), *feo->fe_rt<dim-1>(),
1172  update_values);
1173 
1174  vector<FEValuesSpaceBase<3>*> fv_sb(2);
1175  const unsigned int ndofs = feo->fe<dim>()->n_dofs(); // number of local dofs
1176  const unsigned int qsize = feo->q<dim-1>()->size(); // number of quadrature points
1177  unsigned int side_dof_indices[2*ndofs], n_dofs[2];
1178  vector<arma::vec3> velocity_higher, velocity_lower;
1179  vector<arma::vec> frac_sigma(qsize, arma::vec(n_substances()));
1180  vector<double> csection_lower(qsize), csection_higher(qsize), mm_coef_lower(qsize), mm_coef_higher(qsize);
1181  PetscScalar local_matrix[4*ndofs*ndofs];
1182  double comm_flux[2][2];
1183 
1184  // index 0 = element with lower dimension,
1185  // index 1 = side of element with higher dimension
1186  fv_sb[0] = &fe_values_vb;
1187  fv_sb[1] = &fe_values_side;
1188 
1189  // assemble integral over sides
1190  for (unsigned int inb=0; inb<feo->dh()->n_loc_nb(); inb++)
1191  {
1192  Neighbour *nb = &mesh_->vb_neighbours_[feo->dh()->nb_index(inb)];
1193  // skip neighbours of different dimension
1194  if (nb->element()->dim() != dim-1) continue;
1195 
1196  typename DOFHandlerBase::CellIterator cell_sub = mesh_->element.full_iter(nb->element());
1197  feo->dh()->get_dof_indices(cell_sub, side_dof_indices);
1198  fe_values_vb.reinit(cell_sub);
1199  n_dofs[0] = fv_sb[0]->n_dofs();
1200 
1201  typename DOFHandlerBase::CellIterator cell = nb->side()->element();
1202  feo->dh()->get_dof_indices(cell, side_dof_indices+n_dofs[0]);
1203  fe_values_side.reinit(cell, nb->side()->el_idx());
1204  n_dofs[1] = fv_sb[1]->n_dofs();
1205 
1206  // Element id's for testing if they belong to local partition.
1207  int element_id[2];
1208  element_id[0] = cell_sub.index();
1209  element_id[1] = cell.index();
1210 
1211  fsv_rt.reinit(cell, nb->side()->el_idx());
1212  fv_rt.reinit(cell_sub);
1213  calculate_velocity(cell, velocity_higher, fsv_rt);
1214  calculate_velocity(cell_sub, velocity_lower, fv_rt);
1215  Model::compute_advection_diffusion_coefficients(fe_values_vb.point_list(), velocity_lower, cell_sub->element_accessor(), ad_coef_edg[0], dif_coef_edg[0]);
1216  Model::compute_advection_diffusion_coefficients(fe_values_vb.point_list(), velocity_higher, cell->element_accessor(), ad_coef_edg[1], dif_coef_edg[1]);
1217  Model::compute_mass_matrix_coefficient(fe_values_vb.point_list(), cell_sub->element_accessor(), mm_coef_lower);
1218  Model::compute_mass_matrix_coefficient(fe_values_vb.point_list(), cell->element_accessor(), mm_coef_higher);
1219  data_.cross_section.value_list(fe_values_vb.point_list(), cell_sub->element_accessor(), csection_lower);
1220  data_.cross_section.value_list(fe_values_vb.point_list(), cell->element_accessor(), csection_higher);
1221  data_.fracture_sigma.value_list(fe_values_vb.point_list(), cell_sub->element_accessor(), frac_sigma);
1222 
1223  for (unsigned int sbi=0; sbi<n_subst_; sbi++)
1224  {
1225  for (unsigned int i=0; i<n_dofs[0]+n_dofs[1]; i++)
1226  for (unsigned int j=0; j<n_dofs[0]+n_dofs[1]; j++)
1227  local_matrix[i*(n_dofs[0]+n_dofs[1])+j] = 0;
1228 
1229  // set transmission conditions
1230  for (unsigned int k=0; k<qsize; k++)
1231  {
1232  /* The communication flux has two parts:
1233  * - "diffusive" term containing sigma
1234  * - "advective" term representing usual upwind
1235  *
1236  * The calculation differs from the reference manual, since ad_coef and dif_coef have different meaning
1237  * than b and A in the manual.
1238  * In calculation of sigma there appears one more csection_lower in the denominator.
1239  */
1240  double sigma = frac_sigma[k][sbi]*arma::dot(dif_coef_edg[0][sbi][k]*fe_values_side.normal_vector(k),fe_values_side.normal_vector(k))*
1241  2*csection_higher[k]*csection_higher[k]/(csection_lower[k]*csection_lower[k]);
1242 
1243  // Since mm_coef_* contains cross section, we have to divide by it.
1244  double transport_flux = arma::dot(ad_coef_edg[1][sbi][k], fe_values_side.normal_vector(k));
1245  double por_lower_over_higher = mm_coef_lower[k]*csection_higher[k]/(mm_coef_higher[k]*csection_lower[k]);
1246 
1247  comm_flux[0][0] = (sigma-min(0.,transport_flux*por_lower_over_higher))*fv_sb[0]->JxW(k);
1248  comm_flux[0][1] = -(sigma-min(0.,transport_flux*por_lower_over_higher))*fv_sb[0]->JxW(k);
1249  comm_flux[1][0] = -(sigma+max(0.,transport_flux))*fv_sb[0]->JxW(k);
1250  comm_flux[1][1] = (sigma+max(0.,transport_flux))*fv_sb[0]->JxW(k);
1251 
1252  for (int n=0; n<2; n++)
1253  {
1254  if (!feo->dh()->el_is_local(element_id[n])) continue;
1255 
1256  for (unsigned int i=0; i<n_dofs[n]; i++)
1257  for (int m=0; m<2; m++)
1258  for (unsigned int j=0; j<n_dofs[m]; j++)
1259  local_matrix[(i+n*n_dofs[0])*(n_dofs[0]+n_dofs[1]) + m*n_dofs[0] + j] +=
1260  comm_flux[m][n]*fv_sb[m]->shape_value(j,k)*fv_sb[n]->shape_value(i,k);
1261  }
1262  }
1263  ls[sbi]->mat_set_values(n_dofs[0]+n_dofs[1], (int *)side_dof_indices, n_dofs[0]+n_dofs[1], (int *)side_dof_indices, local_matrix);
1264  }
1265  }
1266 
1267 }
1268 
1269 
1270 
1271 
1272 
1273 
1274 template<class Model>
1276 {
1277  START_TIMER("assemble_bc");
1278  if (balance_ != nullptr)
1279  balance_->start_flux_assembly(subst_idx);
1280  set_boundary_conditions<1>();
1281  set_boundary_conditions<2>();
1282  set_boundary_conditions<3>();
1283  if (balance_ != nullptr)
1284  balance_->finish_flux_assembly(subst_idx);
1285  END_TIMER("assemble_bc");
1286 }
1287 
1288 
1289 template<class Model>
1290 template<unsigned int dim>
1292 {
1293  FESideValues<dim,3> fe_values_side(*feo->mapping<dim>(), *feo->q<dim-1>(), *feo->fe<dim>(),
1295  FESideValues<dim,3> fsv_rt(*feo->mapping<dim>(), *feo->q<dim-1>(), *feo->fe_rt<dim>(),
1296  update_values);
1297  const unsigned int ndofs = feo->fe<dim>()->n_dofs(), qsize = feo->q<dim-1>()->size();
1298  vector<int> side_dof_indices(ndofs);
1299  unsigned int loc_b=0;
1300  double local_rhs[ndofs];
1301  vector<PetscScalar> local_flux_balance_vector(ndofs);
1302  PetscScalar local_flux_balance_rhs;
1303  vector<arma::vec> bc_values(qsize, arma::vec(n_substances())),
1304  bc_fluxes(qsize, arma::vec(n_substances())),
1305  bc_sigma(qsize, arma::vec(n_substances()));
1306  vector<arma::vec3> velocity;
1307 
1308  for (unsigned int loc_el = 0; loc_el < feo->dh()->el_ds()->lsize(); loc_el++)
1309  {
1310  ElementFullIter elm = mesh_->element(feo->dh()->el_index(loc_el));
1311  if (elm->boundary_idx_ == nullptr) continue;
1312 
1313  FOR_ELEMENT_SIDES(elm,si)
1314  {
1315  Edge *edg = elm->side(si)->edge();
1316  if (edg->n_sides > 1) continue;
1317  // skip edges lying not on the boundary
1318  if (edg->side(0)->cond() == NULL) continue;
1319 
1320  if (edg->side(0)->dim() != dim-1)
1321  {
1322  if (edg->side(0)->cond() != nullptr) ++loc_b;
1323  continue;
1324  }
1325 
1326  SideIter side = edg->side(0);
1327  typename DOFHandlerBase::CellIterator cell = mesh().element.full_iter(side->element());
1328  ElementAccessor<3> ele_acc = side->cond()->element_accessor();
1329 
1330  arma::uvec bc_type = data_.bc_type.value(side->cond()->element()->centre(), ele_acc);
1331 
1332  fe_values_side.reinit(cell, side->el_idx());
1333  fsv_rt.reinit(cell, side->el_idx());
1334  calculate_velocity(cell, velocity, fsv_rt);
1335 
1336  Model::compute_advection_diffusion_coefficients(fe_values_side.point_list(), velocity, side->element()->element_accessor(), ad_coef, dif_coef);
1337  Model::compute_dirichlet_bc(fe_values_side.point_list(), ele_acc, bc_values);
1338  data_.bc_flux.value_list(fe_values_side.point_list(), ele_acc, bc_fluxes);
1339  data_.bc_robin_sigma.value_list(fe_values_side.point_list(), ele_acc, bc_sigma);
1340 
1341  feo->dh()->get_dof_indices(cell, (unsigned int *)&(side_dof_indices[0]));
1342 
1343  for (unsigned int sbi=0; sbi<n_subst_; sbi++)
1344  {
1345  fill_n(local_rhs, ndofs, 0);
1346  local_flux_balance_vector.assign(ndofs, 0);
1347  local_flux_balance_rhs = 0;
1348 
1349  double side_flux = 0;
1350  for (unsigned int k=0; k<qsize; k++)
1351  side_flux += arma::dot(ad_coef[sbi][k], fe_values_side.normal_vector(k))*fe_values_side.JxW(k);
1352  double transport_flux = side_flux/side->measure();
1353 
1354  for (unsigned int k=0; k<qsize; k++)
1355  {
1356  double bc_term = 0;
1357  arma::vec3 bc_grad;
1358  bc_grad.zeros();
1359  if (bc_type[sbi] == EqData::inflow && side_flux < 0)
1360  {
1361  bc_term = -transport_flux*bc_values[k][sbi]*fe_values_side.JxW(k);
1362  }
1363  else if (bc_type[sbi] == EqData::dirichlet)
1364  {
1365  bc_term = gamma[sbi][side->cond_idx()]*bc_values[k][sbi]*fe_values_side.JxW(k);
1366  bc_grad = -bc_values[k][sbi]*fe_values_side.JxW(k)*dg_variant*(arma::trans(dif_coef[sbi][k])*fe_values_side.normal_vector(k));
1367  }
1368  else if (bc_type[sbi] == EqData::neumann)
1369  {
1370  bc_term = -bc_fluxes[k][sbi]*fe_values_side.JxW(k);
1371  }
1372  else if (bc_type[sbi] == EqData::robin)
1373  {
1374  bc_term = bc_sigma[k][sbi]*bc_values[k][sbi]*fe_values_side.JxW(k);
1375  }
1376 
1377  for (unsigned int i=0; i<ndofs; i++)
1378  {
1379  local_rhs[i] += bc_term*fe_values_side.shape_value(i,k)
1380  + arma::dot(bc_grad,fe_values_side.shape_grad(i,k));
1381 
1382  if (balance_ != nullptr)
1383  {
1384  if (bc_type[sbi] == EqData::dirichlet)
1385  {
1386  local_flux_balance_vector[i] += (arma::dot(ad_coef[sbi][k], fe_values_side.normal_vector(k))*fe_values_side.shape_value(i,k)
1387  - arma::dot(dif_coef[sbi][k]*fe_values_side.shape_grad(i,k),fe_values_side.normal_vector(k))
1388  + gamma[sbi][side->cond_idx()]*fe_values_side.shape_value(i,k))*fe_values_side.JxW(k);
1389  local_flux_balance_rhs -= (time_->tlevel() == 0?0:1)*bc_term*fe_values_side.shape_value(i,k);
1390  }
1391  else if (bc_type[sbi] == EqData::inflow && side_flux < 0)
1392  {
1393  local_flux_balance_rhs -= bc_term*fe_values_side.shape_value(i,k);
1394  }
1395  else if (bc_type[sbi] == EqData::neumann)
1396  {
1397  local_flux_balance_vector[i] += arma::dot(ad_coef[sbi][k], fe_values_side.normal_vector(k))*fe_values_side.JxW(k)*fe_values_side.shape_value(i,k);
1398  local_flux_balance_rhs -= bc_term*fe_values_side.shape_value(i,k);
1399  }
1400  else if (bc_type[sbi] == EqData::robin)
1401  {
1402  local_flux_balance_vector[i] += (arma::dot(ad_coef[sbi][k], fe_values_side.normal_vector(k)) + bc_sigma[k][sbi])*fe_values_side.JxW(k)*fe_values_side.shape_value(i,k);
1403  local_flux_balance_rhs -= bc_term*fe_values_side.shape_value(i,k);
1404  }
1405  else
1406  {
1407  local_flux_balance_vector[i] += arma::dot(ad_coef[sbi][k], fe_values_side.normal_vector(k))*fe_values_side.JxW(k)*fe_values_side.shape_value(i,k);
1408  }
1409  }
1410  }
1411  }
1412  ls[sbi]->rhs_set_values(ndofs, &(side_dof_indices[0]), local_rhs);
1413 
1414  if (balance_ != nullptr)
1415  {
1416  balance_->add_flux_matrix_values(subst_idx[sbi], loc_b, side_dof_indices, local_flux_balance_vector);
1417  balance_->add_flux_vec_value(subst_idx[sbi], loc_b, local_flux_balance_rhs);
1418  }
1419  }
1420  ++loc_b;
1421  }
1422  }
1423 }
1424 
1425 
1426 
1427 // TODO: The detection of side number from SideIter
1428 // in TransportDG::calculate_velocity()
1429 // should be done with help of class RefElement. This however requires
1430 // that the MH_DofHandler uses the node/side ordering defined in
1431 // the respective RefElement.
1432 template<class Model>
1433 template<unsigned int dim>
1435 {
1436  std::map<const Node*, int> node_nums;
1437  for (unsigned int i=0; i<cell->n_nodes(); i++)
1438  node_nums[cell->node[i]] = i;
1439 
1440  velocity.resize(fv.n_points());
1441 
1442  for (unsigned int k=0; k<fv.n_points(); k++)
1443  {
1444  velocity[k].zeros();
1445  for (unsigned int sid=0; sid<cell->n_sides(); sid++)
1446  {
1447  if (cell->side(sid)->dim() != dim-1) continue;
1448  int num = dim*(dim+1)/2;
1449  for (unsigned int i=0; i<cell->side(sid)->n_nodes(); i++)
1450  num -= node_nums[cell->side(sid)->node(i)];
1451  velocity[k] += fv.shape_vector(num,k) * mh_dh->side_flux( *(cell->side(sid)) );
1452  }
1453  }
1454 }
1455 
1456 
1457 
1458 
1459 
1460 template<class Model>
1462  const int s1,
1463  const int s2,
1464  const int K_size,
1465  const vector<arma::mat33> &K1,
1466  const vector<arma::mat33> &K2,
1467  const vector<double> &fluxes,
1468  const arma::vec3 &normal_vector,
1469  const double alpha1,
1470  const double alpha2,
1471  double &gamma,
1472  double *omega,
1473  double &transport_flux)
1474 {
1475  double delta[2];
1476  double h = 0;
1477  double local_alpha = 0;
1478 
1479  ASSERT(edg.side(s1)->valid(), "Invalid side of an edge.");
1480  SideIter s = edg.side(s1);
1481 
1482  // calculate the side diameter
1483  if (s->dim() == 0)
1484  {
1485  h = 1;
1486  }
1487  else
1488  {
1489  for (unsigned int i=0; i<s->n_nodes(); i++)
1490  for (unsigned int j=i+1; j<s->n_nodes(); j++)
1491  h = max(h, s->node(i)->distance(*s->node(j)));
1492  }
1493 
1494  // calculate the total in- and out-flux through the edge
1495  double pflux = 0, nflux = 0;
1496  for (int i=0; i<edg.n_sides; i++)
1497  {
1498  if (fluxes[i] > 0)
1499  pflux += fluxes[i];
1500  else
1501  nflux += fluxes[i];
1502  }
1503 
1504  // calculate the flux from s1 to s2
1505  if (fluxes[s2] > 0 && fluxes[s1] < 0 && s1 < s2)
1506  transport_flux = fluxes[s1]*fabs(fluxes[s2]/pflux);
1507  else if (fluxes[s2] < 0 && fluxes[s1] > 0 && s1 < s2)
1508  transport_flux = fluxes[s1]*fabs(fluxes[s2]/nflux);
1509  else if (s1==s2)
1510  transport_flux = fluxes[s1];
1511  else
1512  transport_flux = 0;
1513 
1514  gamma = 0.5*fabs(transport_flux);
1515 
1516 
1517  // determine local DG penalty
1518  local_alpha = max(alpha1, alpha2);
1519 
1520  if (s1 == s2)
1521  {
1522  omega[0] = 1;
1523 
1524  // delta is set to the average value of Kn.n on the side
1525  delta[0] = 0;
1526  for (int k=0; k<K_size; k++)
1527  delta[0] += dot(K1[k]*normal_vector,normal_vector);
1528  delta[0] /= K_size;
1529 
1530  gamma += local_alpha/h*(delta[0]);
1531  }
1532  else
1533  {
1534  delta[0] = 0;
1535  delta[1] = 0;
1536  for (int k=0; k<K_size; k++)
1537  {
1538  delta[0] += dot(K1[k]*normal_vector,normal_vector);
1539  delta[1] += dot(K2[k]*normal_vector,normal_vector);
1540  }
1541  delta[0] /= K_size;
1542  delta[1] /= K_size;
1543 
1544  double delta_sum = delta[0] + delta[1];
1545 
1546  if (delta_sum > numeric_limits<double>::epsilon())
1547  {
1548  omega[0] = delta[1]/delta_sum;
1549  omega[1] = delta[0]/delta_sum;
1550  gamma += local_alpha/h*(delta[0]*delta[1]/delta_sum);
1551  }
1552  else
1553  for (int i=0; i<2; i++) omega[i] = 0;
1554  }
1555 }
1556 
1557 
1558 
1559 
1560 
1561 
1562 template<class Model>
1564  const int K_size,
1565  const vector<arma::mat33> &K,
1566  const double flux,
1567  const arma::vec3 &normal_vector,
1568  const double alpha,
1569  double &gamma)
1570 {
1571  double delta = 0, h = 0;
1572 
1573  // calculate the side diameter
1574  if (side->dim() == 0)
1575  {
1576  h = 1;
1577  }
1578  else
1579  {
1580  for (unsigned int i=0; i<side->n_nodes(); i++)
1581  for (unsigned int j=i+1; j<side->n_nodes(); j++)
1582  h = max(h, side->node(i)->distance( *side->node(j) ));
1583  }
1584 
1585  // delta is set to the average value of Kn.n on the side
1586  for (int k=0; k<K_size; k++)
1587  delta += dot(K[k]*normal_vector,normal_vector);
1588  delta /= K_size;
1589 
1590  gamma = 0.5*fabs(flux) + alpha/h*delta;
1591 }
1592 
1593 
1594 
1595 
1596 
1597 template<class Model>
1599 {
1600  START_TIMER("set_init_cond");
1601  for (unsigned int sbi=0; sbi<n_subst_; sbi++)
1602  ls[sbi]->start_allocation();
1603  prepare_initial_condition<1>();
1604  prepare_initial_condition<2>();
1605  prepare_initial_condition<3>();
1606 
1607  for (unsigned int sbi=0; sbi<n_subst_; sbi++)
1608  ls[sbi]->start_add_assembly();
1609  prepare_initial_condition<1>();
1610  prepare_initial_condition<2>();
1611  prepare_initial_condition<3>();
1612 
1613  for (unsigned int sbi=0; sbi<n_subst_; sbi++)
1614  {
1615  ls[sbi]->finish_assembly();
1616  ls[sbi]->solve();
1617  }
1618  END_TIMER("set_init_cond");
1619 }
1620 
1621 template<class Model>
1622 template<unsigned int dim>
1624 {
1625  FEValues<dim,3> fe_values(*feo->mapping<dim>(), *feo->q<dim>(), *feo->fe<dim>(),
1627  const unsigned int ndofs = feo->fe<dim>()->n_dofs(), qsize = feo->q<dim>()->size();
1628  unsigned int dof_indices[ndofs];
1629  double matrix[ndofs*ndofs], rhs[ndofs];
1630  std::vector<arma::vec> init_values(qsize);
1631 
1632  for (unsigned int k=0; k<qsize; k++)
1633  init_values[k].resize(n_subst_);
1634 
1635  for (unsigned int i_cell=0; i_cell<feo->dh()->el_ds()->lsize(); i_cell++)
1636  {
1637  typename DOFHandlerBase::CellIterator elem = mesh_->element(feo->dh()->el_index(i_cell));
1638  if (elem->dim() != dim) continue;
1639 
1640  ElementAccessor<3> ele_acc = elem->element_accessor();
1641  feo->dh()->get_dof_indices(elem, dof_indices);
1642  fe_values.reinit(elem);
1643 
1644  Model::compute_init_cond(fe_values.point_list(), ele_acc, init_values);
1645 
1646  for (unsigned int sbi=0; sbi<n_subst_; sbi++)
1647  {
1648  for (unsigned int i=0; i<ndofs; i++)
1649  {
1650  rhs[i] = 0;
1651  for (unsigned int j=0; j<ndofs; j++)
1652  matrix[i*ndofs+j] = 0;
1653  }
1654 
1655  for (unsigned int k=0; k<qsize; k++)
1656  {
1657  double rhs_term = init_values[k](sbi)*fe_values.JxW(k);
1658 
1659  for (unsigned int i=0; i<ndofs; i++)
1660  {
1661  for (unsigned int j=0; j<ndofs; j++)
1662  matrix[i*ndofs+j] += fe_values.shape_value(i,k)*fe_values.shape_value(j,k)*fe_values.JxW(k);
1663 
1664  rhs[i] += fe_values.shape_value(i,k)*rhs_term;
1665  }
1666  }
1667  ls[sbi]->set_values(ndofs, (int *)dof_indices, ndofs, (int *)dof_indices, matrix, rhs);
1668  }
1669  }
1670 }
1671 
1672 
1673 template<class Model>
1674 void TransportDG<Model>::calc_fluxes(vector<vector<double> > &bcd_balance, vector<vector<double> > &bcd_plus_balance, vector<vector<double> > &bcd_minus_balance)
1675 {
1676  calc_fluxes<1>(bcd_balance, bcd_plus_balance, bcd_minus_balance);
1677  calc_fluxes<2>(bcd_balance, bcd_plus_balance, bcd_minus_balance);
1678  calc_fluxes<3>(bcd_balance, bcd_plus_balance, bcd_minus_balance);
1679 }
1680 
1681 template<class Model>
1682 template<unsigned int dim>
1683 void TransportDG<Model>::calc_fluxes(vector<vector<double> > &bcd_balance, vector<vector<double> > &bcd_plus_balance, vector<vector<double> > &bcd_minus_balance)
1684 {
1685  FESideValues<dim,3> fe_values(*feo->mapping<dim>(), *feo->q<dim-1>(), *feo->fe<dim>(),
1687  FESideValues<dim,3> fsv_rt(*feo->mapping<dim>(), *feo->q<dim-1>(), *feo->fe_rt<dim>(),
1688  update_values);
1689  const unsigned int ndofs = feo->fe<dim>()->n_dofs(), qsize = feo->q<dim-1>()->size();
1690  unsigned int dof_indices[ndofs];
1691  vector<arma::vec3> side_velocity(qsize);
1692  vector<arma::vec> bc_values(qsize, arma::vec(n_subst_)), bc_fluxes(qsize, arma::vec(n_subst_)), bc_sigma(qsize, arma::vec(n_subst_));
1693  arma::vec3 conc_grad;
1694 
1695  for (unsigned int iedg=0; iedg<feo->dh()->n_loc_edges(); iedg++)
1696  {
1697  Edge *edg = &mesh_->edges[feo->dh()->edge_index(iedg)];
1698  if (edg->n_sides > 1) continue;
1699  if (edg->side(0)->dim() != dim-1) continue;
1700  // skip edges lying not on the boundary
1701  if (edg->side(0)->cond() == NULL) continue;
1702 
1703  SideIter side = edg->side(0);
1705  ElementAccessor<3> ele_acc = cell->element_accessor();
1706  Region r = side->cond()->element_accessor().region();
1707  if (! r.is_valid()) xprintf(Msg, "Invalid region, ele % d, edg: % d\n", side->cond()->bc_ele_idx_, side->cond()->edge_idx_);
1708  unsigned int bc_region_idx = r.boundary_idx();
1709 
1710  fe_values.reinit(cell, side->el_idx());
1711  fsv_rt.reinit(cell, side->el_idx());
1712  feo->dh()->get_dof_indices(cell, dof_indices);
1713  calculate_velocity(cell, side_velocity, fsv_rt);
1714  Model::compute_advection_diffusion_coefficients(fe_values.point_list(), side_velocity, ele_acc, ad_coef, dif_coef);
1715  Model::compute_dirichlet_bc(fe_values.point_list(), side->cond()->element_accessor(), bc_values);
1716  data_.bc_flux.value_list(fe_values.point_list(), side->cond()->element_accessor(), bc_fluxes);
1717  data_.bc_robin_sigma.value_list(fe_values.point_list(), side->cond()->element_accessor(), bc_sigma);
1718  arma::uvec bc_type = data_.bc_type.value(side->cond()->element()->centre(), side->cond()->element_accessor());
1719 
1720  for (unsigned int sbi=0; sbi<n_subst_; sbi++)
1721  {
1722  double mass_flux = 0;
1723  double water_flux = 0;
1724  for (unsigned int k=0; k<qsize; k++)
1725  water_flux += arma::dot(ad_coef[sbi][k], fe_values.normal_vector(k))*fe_values.JxW(k);
1726  water_flux /= side->measure();
1727 
1728  for (unsigned int k=0; k<qsize; k++)
1729  {
1730  double conc = 0;
1731  conc_grad.zeros();
1732  for (unsigned int i=0; i<ndofs; i++)
1733  {
1734  conc += fe_values.shape_value(i,k)*ls[sbi]->get_solution_array()[dof_indices[i]-feo->dh()->loffset()];
1735  conc_grad += fe_values.shape_grad(i,k)*ls[sbi]->get_solution_array()[dof_indices[i]-feo->dh()->loffset()];
1736  }
1737 
1738  // flux due to advection
1739  if (bc_type[sbi] == EqData::inflow && water_flux < 0)
1740  mass_flux += water_flux*bc_values[k][sbi]*fe_values.JxW(k);
1741  else
1742  mass_flux += water_flux*conc*fe_values.JxW(k);
1743 
1744  if (bc_type[sbi] == EqData::dirichlet)
1745  {
1746  // flux due to diffusion
1747  mass_flux -= arma::dot(dif_coef[sbi][k]*conc_grad,fe_values.normal_vector(k))*fe_values.JxW(k);
1748 
1749  // the penalty term has to be added otherwise the mass balance will not hold
1750  mass_flux -= gamma[sbi][side->cond_idx()]*(bc_values[k][sbi] - conc)*fe_values.JxW(k);
1751  }
1752  else if (bc_type[sbi] == EqData::neumann)
1753  {
1754  // flux due to Neumann b.c.
1755  mass_flux += bc_fluxes[k][sbi]*fe_values.JxW(k);
1756  }
1757  else if (bc_type[sbi] == EqData::robin)
1758  {
1759  // flux due to Robin b.c.
1760  mass_flux += bc_sigma[k][sbi]*(conc - bc_values[k][sbi])*fe_values.JxW(k);
1761  }
1762 
1763  }
1764  bcd_balance[sbi][bc_region_idx] += mass_flux;
1765  if (mass_flux >= 0) bcd_plus_balance[sbi][bc_region_idx] += mass_flux;
1766  else bcd_minus_balance[sbi][bc_region_idx] += mass_flux;
1767  }
1768  }
1769 
1770 }
1771 
1772 template<class Model>
1774 {
1775  calc_elem_sources<1>(mass, src_balance);
1776  calc_elem_sources<2>(mass, src_balance);
1777  calc_elem_sources<3>(mass, src_balance);
1778 }
1779 
1780 template<class Model>
1781 template<unsigned int dim>
1783 {
1784  FEValues<dim,3> fe_values(*feo->mapping<dim>(), *feo->q<dim>(), *feo->fe<dim>(),
1786  const unsigned int ndofs = feo->fe<dim>()->n_dofs(), qsize = feo->q<dim>()->size();
1787  unsigned int dof_indices[ndofs];
1788  vector<arma::vec> sources_conc(qsize, arma::vec(n_substances())),
1789  sources_density(qsize, arma::vec(n_substances())),
1790  sources_sigma(qsize, arma::vec(n_substances()));
1791  double mass_sum, sources_sum, conc, conc_diff;
1792 
1793  for (unsigned int i_cell=0; i_cell<feo->dh()->el_ds()->lsize(); i_cell++)
1794  {
1795  typename DOFHandlerBase::CellIterator elem = mesh_->element(feo->dh()->el_index(i_cell));
1796  if (elem->dim() != dim) continue;
1797 
1798  Region r = elem->element_accessor().region();
1799  if (! r.is_valid()) xprintf(Msg, "Invalid region, ele % d\n", elem.index());
1800  unsigned int region_idx = r.bulk_idx();
1801 
1802  ElementAccessor<3> ele_acc = elem->element_accessor();
1803  fe_values.reinit(elem);
1804  feo->dh()->get_dof_indices(elem, dof_indices);
1805 
1806  Model::compute_mass_matrix_coefficient(fe_values.point_list(), ele_acc, mm_coef);
1807  Model::compute_source_coefficients(fe_values.point_list(), ele_acc, sources_conc, sources_density, sources_sigma);
1808 
1809  for (unsigned int sbi=0; sbi<n_subst_; sbi++)
1810  {
1811  mass_sum = 0;
1812  sources_sum = 0;
1813 
1814  for (unsigned int k=0; k<qsize; k++)
1815  {
1816  conc = 0;
1817  for (unsigned int i=0; i<ndofs; i++)
1818  conc += fe_values.shape_value(i,k)*ls[sbi]->get_solution_array()[dof_indices[i]-feo->dh()->loffset()];
1819 
1820  mass_sum += mm_coef[k]*conc*fe_values.JxW(k);
1821 
1822  conc_diff = sources_conc[k][sbi] - conc;
1823  sources_sum += (sources_density[k][sbi] + conc_diff*sources_sigma[k][sbi])*fe_values.JxW(k);
1824  }
1825 
1826  mass[sbi][region_idx] += mass_sum;
1827  src_balance[sbi][region_idx] += sources_sum;
1828  }
1829  }
1830 }
1831 
1832 
1833 
1834 
1835 
1837 template class TransportDG<HeatTransferModel>;
1838 
1839 
1840 
1841 
Class MappingP1 implements the affine transformation of the unit cell onto the actual cell...
Shape function values.
Definition: update_flags.hh:98
FieldSet * eq_data_
Definition: equation.hh:227
Distribution * el_ds() const
Returns the distribution of number of element to local processes.
Definition: dofhandler.hh:306
#define DBGMSG(...)
Definition: global_defs.h:196
void assemble_fluxes_element_element()
Assembles the fluxes between elements of the same dimension.
void set_sources()
Assembles the right hand side due to volume sources.
void set_boundary_conditions()
Assembles the r.h.s. components corresponding to the Dirichlet boundary conditions.
Transformed quadrature weight for cell sides.
FiniteElement< dim, 3 > * fe()
vector< double > mm_coef
Mass matrix coefficients.
Accessor to input data conforming to declared Array.
Definition: accessors.hh:558
void assemble_fluxes_element_side()
Assembles the fluxes between elements of different dimensions.
Definition: system.hh:72
static constexpr Mask in_main_matrix
A field is part of main &quot;stiffness matrix&quot; of the equation.
Definition: field_flag.hh:34
Solver based on the original PETSc solver using MPIAIJ matrix and succesive Schur complement construc...
Input::Record output_rec
Record with output specification.
static constexpr Mask allow_output
The field can output. Is part of generated output selection. (default on)
Definition: field_flag.hh:27
int tlevel() const
unsigned int bulk_size() const
Definition: region.cc:252
const std::vector< std::string > & names()
Definition: substance.hh:97
Transport with dispersion implemented using discontinuous Galerkin method.
Field< 3, FieldValue< 3 >::Integer > region_id
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
Class Input::Type::Default specifies default value of keys of a Input::Type::Record.
Definition: type_record.hh:39
FieldCommon & flags_add(FieldFlag::Flags::Mask mask)
double distance(const Node &n2) const
Definition: nodes.hh:98
void assemble_mass_matrix()
Assembles the mass matrix.
Boundary * cond() const
Definition: side_impl.hh:59
OutputTime * output_stream
int * get_el_4_loc() const
Definition: dofhandler.hh:308
void next_time()
Proceed to the next time according to current estimated time step.
void update_solution() override
Computes the solution in one time instant.
const double JxW(const unsigned int point_no)
Return the product of Jacobian determinant and the quadrature weight at given quadrature point...
Definition: fe_values.cc:178
const arma::vec::fixed< spacedim > normal_vector(unsigned int point_no)
Returns the normal vector to a side at given quadrature point.
Definition: fe_values.cc:196
TimeMark::Type type_output()
Definition: time_marks.hh:203
static Default obligatory()
Definition: type_record.hh:89
int dg_variant
DG variant ((non-)symmetric/incomplete.
void prepare_initial_condition()
Assembles the auxiliary linear system to calculate the initial solution as L^2-projection of the pres...
RegionSet get_region_set(const string &set_name) const
Definition: region.cc:361
const MH_DofHandler * mh_dh
Definition: mesh.h:109
Fields computed from the mesh data.
Iterator< Ret > find(const string &key) const
void set_initial_condition()
Sets the initial condition.
Class FEValues calculates finite element data on the actual cells such as shape function values...
#define FOR_ELEMENT_SIDES(i, j)
Definition: elements.h:151
int index() const
Definition: sys_vector.hh:88
bool is_current(const TimeMark::Type &mask) const
int n_sides
Definition: edges.h:48
vector< vector< arma::vec3 > > ad_coef
Advection coefficients.
static Input::Type::Record input_type
Definition: linsys_PETSC.hh:46
Definition: edges.h:38
bool valid() const
Definition: side_impl.hh:75
const RegionDB & region_db() const
Definition: mesh.h:155
void set_from_input(const Input::Record in_rec)
Mat * mass_matrix
The mass matrix.
#define AVERAGE(i, k, side_id)
void assemble_stiffness_matrix()
Assembles the stiffness matrix.
const TimeStep & step(int index=-1) const
vector< vector< vector< arma::mat33 > > > dif_coef_edg
Diffusion coefficients on edges.
Discontinuous Galerkin method for equation of transport with dispersion.
Class for declaration of the integral input data.
Definition: type_base.hh:355
double t() const
vector< Boundary > boundary_
Definition: mesh.h:209
Basic time management functionality for unsteady (and steady) solvers (class Equation).
FieldCommon & units(const UnitSI &units)
Set basic units of the field.
const arma::vec::fixed< spacedim > shape_grad(const unsigned int function_no, const unsigned int point_no)
Return the gradient of the function_no-th shape function at the point_no-th quadrature point...
Definition: fe_values.cc:154
unsigned int size() const
Definition: substance.hh:99
static TimeMarks & marks()
Class for declaration of inputs sequences.
Definition: type_base.hh:239
Specification of transport model interface.
void view(const char *name="") const
void assemble_fluxes_boundary()
Assembles the fluxes on the boundary.
Symmetric Gauss-Legendre quadrature formulae on simplices.
#define WAVERAGE(i, k, side_id)
const unsigned int n_points()
Returns the number of quadrature points.
Definition: fe_values.cc:202
vector< vector< double > > ret_coef
Retardation coefficient due to sorption.
unsigned int boundary_idx() const
Returns index of the region in the boundary set.
Definition: region.hh:75
unsigned int dim() const
vector< double > sorption_sources
Temporary values of source increments.
TransportDG(Mesh &init_mesh, const Input::Record &in_rec)
Constructor.
vector< double * > output_solution
Array for storing the output solution data.
vector< vector< vector< arma::vec3 > > > ad_coef_edg
Advection coefficients on edges.
Discontinuous Lagrangean finite element on dim dimensional simplex.
Definition: fe_p.hh:213
Transformed quadrature points.
void output_data()
Postprocesses the solution and writes to output file.
#define ASSERT(...)
Definition: global_defs.h:121
static OutputTime * create_output_stream(const Input::Record &in_rec)
This method delete all object instances of class OutputTime stored in output_streams vector...
Definition: output_time.cc:141
void preallocate()
unsigned int dim() const
Definition: side_impl.hh:26
vector< unsigned int > subst_idx
List of indices used to call balance methods for a set of quantities.
vector< vector< arma::mat33 > > dif_coef
Diffusion coefficients.
FEObjects * feo
Finite element objects.
Mesh & mesh()
Definition: equation.hh:171
static constexpr Mask in_time_term
A field is part of time term of the equation.
Definition: field_flag.hh:32
FieldCommon & input_default(const string &input_default)
Definition: field_common.hh:92
TimeMark::Type equation_fixed_mark_type() const
Raviart-Thomas element of order 0.
Definition: fe_rt.hh:44
Definitions of basic Lagrangean finite elements with polynomial shape functions.
static constexpr Mask equation_external_output
Match an output field, that can be also copy of other field.
Definition: field_flag.hh:42
SideIter side()
Selection & add_value(const int value, const std::string &key, const std::string &description="")
Accessor to the data with type Type::Record.
Definition: accessors.hh:327
FieldCommon & input_selection(const Input::Type::Selection *element_selection)
const Ret val(const string &key) const
static auto region_id(Mesh &mesh) -> IndexField
#define xprintf(...)
Definition: system.hh:100
#define START_TIMER(tag)
Starts a timer with specified tag.
FiniteElement< dim, spacedim > * fe
The used finite element.
Definition: fe_values.hh:337
Provides the numbering of the finite element degrees of freedom on the computational mesh...
Definition: dofhandler.hh:251
Mat * stiffness_matrix
The stiffness matrix.
Mesh * mesh_
Definition: equation.hh:218
Element * element()
Definition: boundaries.cc:47
Field< 3, FieldValue< 3 >::Vector > fracture_sigma
Transition parameter for diffusive transfer on fractures (for each substance).
boost::shared_ptr< Balance > balance_
(new) object for calculation and writing the mass balance to file.
unsigned int dg_order
Polynomial order of finite elements.
void output_vector_gather()
static constexpr Mask in_rhs
A field is part of the right hand side of the equation.
Definition: field_flag.hh:36
unsigned int n_subst_
Number of transported substances.
Affine mapping between reference and actual cell.
Definition: mapping_p1.hh:64
Shape function gradients.
void mark_output_times(const TimeGovernor &tg)
Definition: output_time.cc:182
Vec * rhs
Vector of right hand side.
void set_solution(double *sol_array)
Definition: linsys.hh:264
unsigned int edge_idx_
Definition: boundaries.h:87
BCField< 3, FieldValue< 3 >::Vector > bc_flux
Flux in Neumann or Robin b.c.
static Input::Type::Selection bc_type_selection
ElementIter element()
double measure() const
Definition: sides.cc:42
DOFHandlerMultiDim * dh()
Region region() const
Definition: accessors.hh:88
Normal vectors.
unsigned int bc_ele_idx_
Definition: boundaries.h:88
Discontinuous Galerkin method for equation of transport with dispersion.
const vector< arma::vec::fixed< spacedim > > & point_list()
Return coordinates of all quadrature points in the actual cell system.
Definition: fe_values.cc:190
const double epsilon
Definition: mathfce.h:6
void output_data() override
Write computed fields.
vector< Neighbour > vb_neighbours_
Definition: mesh.h:235
double side_flux(const Side &side) const
temporary replacement for DofHandler accessor, flux through given side
const arma::vec::fixed< spacedim > shape_vector(const unsigned int function_no, const unsigned int point_no)
Return the value of the function_no-th shape function at the point_no-th quadrature point...
Definition: fe_values.cc:160
FieldCommon & description(const string &description)
Definition: field_common.hh:80
EqData data_
Field data for model parameters.
bool allocation_done
Indicates whether matrices have been preallocated.
unsigned int cond_idx() const
Definition: side_impl.hh:64
std::vector< std::vector< double > > gamma
Penalty parameters.
unsigned int max_edge_sides(unsigned int dim) const
Definition: mesh.h:182
Discontinuous Galerkin method for equation of transport with dispersion.
SubstanceList substances_
Transported substances.
ElementFullIter element() const
Definition: side_impl.hh:41
void calc_fluxes(vector< vector< double > > &bcd_balance, vector< vector< double > > &bcd_plus_balance, vector< vector< double > > &bcd_minus_balance)
Calculates flux through boundary of each region.
double dt() const
arma::vec3 centre() const
Definition: elements.cc:132
Definition: system.hh:72
void reinit(ElementFullIter &cell, unsigned int sid)
Update cell-dependent data (gradients, Jacobians etc.)
Definition: fe_values.cc:312
const double shape_value(const unsigned int function_no, const unsigned int point_no)
Return the value of the function_no-th shape function at the point_no-th quadrature point...
Definition: fe_values.cc:148
BCField< 3, FieldValue< 3 >::EnumVector > bc_type
Type of boundary condition (see also BC_Type)
void reinit(ElementFullIter &cell)
Update cell-dependent data (gradients, Jacobians etc.)
Definition: fe_values.cc:244
Vec * mass_vec
Mass from previous time instant (necessary when coefficients of mass matrix change in time)...
std::vector< Edge > edges
Vector of MH edges, this should not be part of the geometrical mesh.
Definition: mesh.h:216
FullIter full_iter(Iter it)
Definition: sys_vector.hh:438
unsigned int bulk_idx() const
Returns index of the region in the bulk set.
Definition: region.hh:80
Abstract linear system class.
Definitions of particular quadrature rules on simplices.
FieldCommon & name(const string &name)
Definition: field_common.hh:73
void calc_elem_sources(vector< vector< double > > &mass, vector< vector< double > > &src_balance)
Calculates volume sources for each region.
vector< Vec > output_vec
Vector of solution data.
Calculates finite element data on the actual cell.
Definition: fe_values.hh:370
#define END_TIMER(tag)
Ends a timer with specified tag.
Discontinuous Galerkin method for equation of transport with dispersion.
Quadrature< dim > * q()
virtual void set_velocity_field(const MH_DofHandler &dh)
Updates the velocity field which determines some coefficients of the transport equation.
mixed-hybrid model of linear Darcy flow, possibly unsteady.
unsigned int el_idx() const
Definition: side_impl.hh:70
unsigned int n_substances() override
Returns number of trnasported substances.
Record type proxy class.
Definition: type_record.hh:169
void assemble_volume_integrals()
Assembles the volume integrals into the stiffness matrix.
bool is_valid() const
Returns false if the region has undefined/invalid value.
Definition: region.hh:67
FieldCommon & flags(FieldFlag::Flags::Mask mask)
Abstract class for the description of a general finite element on a reference simplex in dim dimensio...
Definition: dofhandler.hh:40
Base class for FEValues and FESideValues.
Definition: fe_values.hh:43
Class for representation SI units of Fields.
Definition: unit_si.hh:31
const Node * node(unsigned int i) const
Definition: side_impl.hh:35
void calculate_velocity(const ElementFullIter &cell, std::vector< arma::vec3 > &velocity, FEValuesBase< dim, 3 > &fv)
Calculates the velocity field on a given dim dimensional cell.
Distribution * distr() const
Definition: dofhandler.hh:90
Mapping< dim, 3 > * mapping()
static UnitSI & dimensionless()
Returns dimensionless unit.
Definition: unit_si.cc:44
~TransportDG()
Destructor.
LinSys ** ls
Linear algebra system for the transport equation.
void set_DG_parameters_edge(const Edge &edg, const int s1, const int s2, const int K_size, const std::vector< arma::mat33 > &K1, const std::vector< arma::mat33 > &K2, const std::vector< double > &fluxes, const arma::vec3 &normal_vector, const double alpha1, const double alpha2, double &gamma, double *omega, double &transport_flux)
Sets up some parameters of the DG method for two sides of an edge.
SideIter side(const unsigned int i) const
Definition: edges.h:43
LinSys ** ls_dt
Linear algebra system for the time derivative (actually it is used only for handling the matrix struc...
Field< 3, FieldValue< 3 >::Vector > dg_penalty
Penalty enforcing inter-element continuity of solution (for each substance).
#define JUMP(i, k, side_id)
Template for classes storing finite set of named values.
void set_DG_parameters_boundary(const SideIter side, const int K_size, const std::vector< arma::mat33 > &K, const double flux, const arma::vec3 &normal_vector, const double alpha, double &gamma)
Sets up parameters of the DG method on a given boundary edge.
Definitions of Raviart-Thomas finite elements.
const unsigned int n_global_dofs() const
Getter for the number of all mesh dofs required by the given finite element.
Definition: dofhandler.hh:72
BCField< 3, FieldValue< 3 >::Vector > bc_robin_sigma
Transition coefficient in Robin b.c.
ElementAccessor< 3 > element_accessor()
Definition: boundaries.cc:55
void zero_time_step() override
Initialize solution in the zero time.
FEObjects(Mesh *mesh_, unsigned int fe_order)
Definition: transport_dg.cc:96
TimeGovernor * time_
Definition: equation.hh:219
ElementVector element
Vector of elements of the mesh.
Definition: mesh.h:205
unsigned int n_nodes() const
Definition: side_impl.hh:22
Transformed quadrature weights.
Calculates finite element data on a side.
Definition: fe_values.hh:415
unsigned int lsize(int proc) const
get local size