Flow123d  release_2.2.0-914-gf1a3a4f
transport_dg.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 transport_dg.cc
15 * @brief Discontinuous Galerkin method for equation of transport with dispersion.
16 * @author Jan Stebel
17 */
18 
19 #include "system/sys_profiler.hh"
21 
22 #include "io/output_time.hh"
24 #include "fem/mapping_p1.hh"
25 #include "fem/fe_values.hh"
26 #include "fem/fe_p.hh"
27 #include "fem/fe_rt.hh"
28 #include "fields/field_fe.hh"
29 #include "la/linsys_PETSC.hh"
32 #include "transport/heat_model.hh"
33 #include "coupling/balance.hh"
34 
35 #include "fields/multi_field.hh"
36 #include "fields/generic_field.hh"
37 #include "input/factory.hh"
39 
40 FLOW123D_FORCE_LINK_IN_CHILD(concentrationTransportModel);
42 
43 
44 
45 using namespace Input::Type;
46 
47 template<class Model>
49  return Selection("DG_variant", "Type of penalty term.")
50  .add_value(non_symmetric, "non-symmetric", "non-symmetric weighted interior penalty DG method")
51  .add_value(incomplete, "incomplete", "incomplete weighted interior penalty DG method")
52  .add_value(symmetric, "symmetric", "symmetric weighted interior penalty DG method")
53  .close();
54 }
55 
56 /*
57 * Should be removed
58 template<class Model>
59 const Selection & TransportDG<Model>::EqData::get_output_selection() {
60  return Model::ModelEqData::get_output_selection_input_type(
61  "DG",
62  "Implicit in time Discontinuous Galerkin solver")
63  .copy_values(EqData().make_output_field_selection("").close())
64  ConvectionTransport::EqData().output_fields
65  .make_output_field_selection(
66  "ConvectionTransport_output_fields",
67  "Selection of output fields for Convection Solute Transport model.")
68  .close()),
69  .close();
70 }
71 */
72 
73 template<class Model>
75  std::string equation_name = std::string(Model::ModelEqData::name()) + "_DG";
76  return Model::get_input_type("DG", "DG solver")
78  "Linear solver for MH problem.")
79  .declare_key("input_fields", Array(
81  .make_field_descriptor_type(equation_name)),
83  "Input fields of the equation.")
85  "Variant of interior penalty discontinuous Galerkin method.")
86  .declare_key("dg_order", Integer(0,3), Default("1"),
87  "Polynomial order for finite element in DG method (order 0 is suitable if there is no diffusion/dispersion).")
88  .declare_key("output",
89  EqData().output_fields.make_output_type(equation_name, ""),
90  IT::Default("{ \"fields\": [ " + Model::ModelEqData::default_output_field() + "] }"),
91  "Setting of the field output.")
92  .close();
93 }
94 
95 template<class Model>
97  Input::register_class< TransportDG<Model>, Mesh &, const Input::Record>(std::string(Model::ModelEqData::name()) + "_DG") +
99 
100 
101 
102 
103 FEObjects::FEObjects(Mesh *mesh_, unsigned int fe_order)
104 {
105  unsigned int q_order;
106 
107  q_order = 2*fe_order;
108  fe1_ = new FE_P_disc<1,3>(fe_order);
109  fe2_ = new FE_P_disc<2,3>(fe_order);
110  fe3_ = new FE_P_disc<3,3>(fe_order);
111 
112  fe_rt1_ = new FE_RT0<1,3>;
113  fe_rt2_ = new FE_RT0<2,3>;
114  fe_rt3_ = new FE_RT0<3,3>;
115 
116  q0_ = new QGauss<0>(q_order);
117  q1_ = new QGauss<1>(q_order);
118  q2_ = new QGauss<2>(q_order);
119  q3_ = new QGauss<3>(q_order);
120 
121  map1_ = new MappingP1<1,3>;
122  map2_ = new MappingP1<2,3>;
123  map3_ = new MappingP1<3,3>;
124 
125  dh_ = std::make_shared<DOFHandlerMultiDim>(*mesh_);
126 
127  dh_->distribute_dofs(*fe1_, *fe2_, *fe3_);
128 }
129 
130 
132 {
133  delete fe1_;
134  delete fe2_;
135  delete fe3_;
136  delete fe_rt1_;
137  delete fe_rt2_;
138  delete fe_rt3_;
139  delete q0_;
140  delete q1_;
141  delete q2_;
142  delete q3_;
143  delete map1_;
144  delete map2_;
145  delete map3_;
146 }
147 
148 template<> FiniteElement<0,3> *FEObjects::fe<0>() { return 0; }
149 template<> FiniteElement<1,3> *FEObjects::fe<1>() { return fe1_; }
150 template<> FiniteElement<2,3> *FEObjects::fe<2>() { return fe2_; }
151 template<> FiniteElement<3,3> *FEObjects::fe<3>() { return fe3_; }
152 
153 template<> FiniteElement<0,3> *FEObjects::fe_rt<0>() { return 0; }
154 template<> FiniteElement<1,3> *FEObjects::fe_rt<1>() { return fe_rt1_; }
155 template<> FiniteElement<2,3> *FEObjects::fe_rt<2>() { return fe_rt2_; }
156 template<> FiniteElement<3,3> *FEObjects::fe_rt<3>() { return fe_rt3_; }
157 
158 template<> Quadrature<0> *FEObjects::q<0>() { return q0_; }
159 template<> Quadrature<1> *FEObjects::q<1>() { return q1_; }
160 template<> Quadrature<2> *FEObjects::q<2>() { return q2_; }
161 template<> Quadrature<3> *FEObjects::q<3>() { return q3_; }
162 
163 template<> MappingP1<1,3> *FEObjects::mapping<1>() { return map1_; }
164 template<> MappingP1<2,3> *FEObjects::mapping<2>() { return map2_; }
165 template<> MappingP1<3,3> *FEObjects::mapping<3>() { return map3_; }
166 
167 std::shared_ptr<DOFHandlerMultiDim> FEObjects::dh() { return dh_; }
168 
169 
170 template<class Model>
171 TransportDG<Model>::EqData::EqData() : Model::ModelEqData()
172 {
173  *this+=fracture_sigma
174  .name("fracture_sigma")
175  .description(
176  "Coefficient of diffusive transfer through fractures (for each substance).")
178  .input_default("1.0")
180 
181  *this+=dg_penalty
182  .name("dg_penalty")
183  .description(
184  "Penalty parameter influencing the discontinuity of the solution (for each substance). "
185  "Its default value 1 is sufficient in most cases. Higher value diminishes the inter-element jumps.")
187  .input_default("1.0")
189 
190  *this += region_id.name("region_id")
193 
194  *this += subdomain.name("subdomain")
197 
198 
199  // add all input fields to the output list
200  output_fields += *this;
201 
202 }
203 
204 template<class Model>
206  : Model(init_mesh, in_rec),
207  input_rec(in_rec),
208  allocation_done(false)
209 {
210  // Can not use name() + "constructor" here, since START_TIMER only accepts const char *
211  // due to constexpr optimization.
212  START_TIMER(Model::ModelEqData::name());
213  // Check that Model is derived from AdvectionDiffusionModel.
215 
216  this->eq_data_ = &data_;
217 
218 
219  // Set up physical parameters.
220  data_.set_mesh(init_mesh);
221  data_.region_id = GenericField<3>::region_id(*Model::mesh_);
222  data_.subdomain = GenericField<3>::subdomain(*Model::mesh_);
223 
224 
225  // DG variant and order
226  dg_variant = in_rec.val<DGVariant>("dg_variant");
227  dg_order = in_rec.val<unsigned int>("dg_order");
228 
229  Model::init_from_input(in_rec);
230 
231  // create finite element structures and distribute DOFs
232  feo = new FEObjects(Model::mesh_, dg_order);
233  //DebugOut().fmt("TDG: solution size {}\n", feo->dh()->n_global_dofs());
234 
235 }
236 
237 
238 template<class Model>
240 {
241  data_.set_components(Model::substances_.names());
242  data_.set_input_list( input_rec.val<Input::Array>("input_fields") );
243 
244  // DG stabilization parameters on boundary edges
245  gamma.resize(Model::n_substances());
246  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
247  gamma[sbi].resize(Model::mesh_->boundary_.size());
248 
249  // Resize coefficient arrays
250  int qsize = max(feo->q<0>()->size(), max(feo->q<1>()->size(), max(feo->q<2>()->size(), feo->q<3>()->size())));
251  int max_edg_sides = max(Model::mesh_->max_edge_sides(1), max(Model::mesh_->max_edge_sides(2), Model::mesh_->max_edge_sides(3)));
252  mm_coef.resize(qsize);
253  ret_coef.resize(Model::n_substances());
254  ret_sources.resize(Model::n_substances());
255  ret_sources_prev.resize(Model::n_substances());
256  ad_coef.resize(Model::n_substances());
257  dif_coef.resize(Model::n_substances());
258  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
259  {
260  ret_coef[sbi].resize(qsize);
261  ad_coef[sbi].resize(qsize);
262  dif_coef[sbi].resize(qsize);
263  }
264  ad_coef_edg.resize(max_edg_sides);
265  dif_coef_edg.resize(max_edg_sides);
266  for (int sd=0; sd<max_edg_sides; sd++)
267  {
268  ad_coef_edg[sd].resize(Model::n_substances());
269  dif_coef_edg[sd].resize(Model::n_substances());
270  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
271  {
272  ad_coef_edg[sd][sbi].resize(qsize);
273  dif_coef_edg[sd][sbi].resize(qsize);
274  }
275  }
276 
277  output_vec.resize(Model::n_substances());
278  //output_solution.resize(Model::n_substances());
279  int rank;
280  MPI_Comm_rank(PETSC_COMM_WORLD, &rank);
281  unsigned int output_vector_size= (rank==0)?feo->dh()->n_global_dofs():0;
282  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
283  {
284  // for each substance we allocate output array and vector
285  //output_solution[sbi] = new double[feo->dh()->n_global_dofs()];
286  output_vec[sbi].resize(output_vector_size);
287  }
288  data_.output_field.set_components(Model::substances_.names());
289  data_.output_field.set_mesh(*Model::mesh_);
290  data_.output_type(OutputTime::CORNER_DATA);
291 
292  data_.output_field.setup_components();
293  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
294  {
295  // create shared pointer to a FieldFE, pass FE data and push this FieldFE to output_field on all regions
296  std::shared_ptr<FieldFE<3, FieldValue<3>::Scalar> > output_field_ptr(new FieldFE<3, FieldValue<3>::Scalar>);
297  output_field_ptr->set_fe_data(feo->dh(), feo->mapping<1>(), feo->mapping<2>(), feo->mapping<3>(), &output_vec[sbi]);
298  data_.output_field[sbi].set_field(Model::mesh_->region_db().get_region_set("ALL"), output_field_ptr, 0);
299  }
300 
301  // set time marks for writing the output
302  data_.output_fields.initialize(Model::output_stream_, Model::mesh_, input_rec.val<Input::Record>("output"), this->time());
303 
304  // equation default PETSc solver options
305  std::string petsc_default_opts;
306  if (feo->dh()->distr()->np() == 1)
307  petsc_default_opts = "-ksp_type bcgs -pc_type ilu -pc_factor_levels 2 -ksp_diagonal_scale_fix -pc_factor_fill 6.0";
308  else
309  petsc_default_opts = "-ksp_type bcgs -ksp_diagonal_scale_fix -pc_type asm -pc_asm_overlap 4 -sub_pc_type ilu -sub_pc_factor_levels 3 -sub_pc_factor_fill 6.0";
310 
311  // allocate matrix and vector structures
312  ls = new LinSys*[Model::n_substances()];
313  ls_dt = new LinSys*[Model::n_substances()];
314  solution_elem_ = new double*[Model::n_substances()];
315 
316  stiffness_matrix.resize(Model::n_substances(), nullptr);
317  mass_matrix.resize(Model::n_substances(), nullptr);
318  rhs.resize(Model::n_substances(), nullptr);
319  mass_vec.resize(Model::n_substances(), nullptr);
320  ret_vec.resize(Model::n_substances(), nullptr);
321 
322  for (unsigned int sbi = 0; sbi < Model::n_substances(); sbi++) {
323  ls[sbi] = new LinSys_PETSC(feo->dh()->distr(), petsc_default_opts);
324  ( (LinSys_PETSC *)ls[sbi] )->set_from_input( input_rec.val<Input::Record>("solver") );
325  ls[sbi]->set_solution(NULL);
326 
327  ls_dt[sbi] = new LinSys_PETSC(feo->dh()->distr(), petsc_default_opts);
328  ( (LinSys_PETSC *)ls_dt[sbi] )->set_from_input( input_rec.val<Input::Record>("solver") );
329  solution_elem_[sbi] = new double[Model::mesh_->get_el_ds()->lsize()];
330 
331  VecDuplicate(ls[sbi]->get_solution(), &ret_vec[sbi]);
332  }
333 
334 
335  // initialization of balance object
336  Model::balance_->allocate(feo->dh()->distr()->lsize(),
337  max(feo->fe<1>()->n_dofs(), max(feo->fe<2>()->n_dofs(), feo->fe<3>()->n_dofs())));
338 
339 }
340 
341 
342 template<class Model>
344 {
345  delete Model::time_;
346 
347  if (gamma.size() > 0) {
348  // initialize called
349 
350  for (unsigned int i=0; i<Model::n_substances(); i++)
351  {
352  delete ls[i];
353  delete[] solution_elem_[i];
354  delete ls_dt[i];
355 
356  if (stiffness_matrix[i])
357  MatDestroy(&stiffness_matrix[i]);
358  if (mass_matrix[i])
359  MatDestroy(&mass_matrix[i]);
360  if (rhs[i])
361  VecDestroy(&rhs[i]);
362  if (mass_vec[i])
363  VecDestroy(&mass_vec[i]);
364  if (ret_vec[i])
365  VecDestroy(&ret_vec[i]);
366  }
367  delete[] ls;
368  delete[] solution_elem_;
369  delete[] ls_dt;
370  //delete[] stiffness_matrix;
371  //delete[] mass_matrix;
372  //delete[] rhs;
373  //delete[] mass_vec;
374  //delete[] ret_vec;
375  delete feo;
376 
377  }
378 
379 }
380 
381 
382 template<class Model>
384 {
385  VecScatter output_scatter;
386  VecScatterCreateToZero(ls[0]->get_solution(), &output_scatter, PETSC_NULL);
387  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
388  {
389  // gather solution to output_vec[sbi]
390  VecScatterBegin(output_scatter, ls[sbi]->get_solution(), (output_vec[sbi]).get_data_petsc(), INSERT_VALUES, SCATTER_FORWARD);
391  VecScatterEnd(output_scatter, ls[sbi]->get_solution(), (output_vec[sbi]).get_data_petsc(), INSERT_VALUES, SCATTER_FORWARD);
392  }
393  VecScatterDestroy(&(output_scatter));
394 
395 }
396 
397 
398 
399 template<class Model>
401 {
402  START_TIMER(Model::ModelEqData::name());
403  data_.mark_input_times( *(Model::time_) );
404  data_.set_time(Model::time_->step(), LimitSide::left);
405  std::stringstream ss; // print warning message with table of uninitialized fields
406  if ( FieldCommon::print_message_table(ss, "transport DG") ) {
407  WarningOut() << ss.str();
408  }
409 
410 
411  // set initial conditions
413  for (unsigned int sbi = 0; sbi < Model::n_substances(); sbi++)
414  ( (LinSys_PETSC *)ls[sbi] )->set_initial_guess_nonzero();
415 
416  // check first time assembly - needs preallocation
418 
419  // after preallocation we assemble the matrices and vectors required for mass balance
420  for (unsigned int sbi=0; sbi<Model::n_substances(); ++sbi)
421  {
422  Model::balance_->calculate_instant(Model::subst_idx[sbi], ls[sbi]->get_solution());
423 
424  // add sources due to sorption
425  ret_sources_prev[sbi] = 0;
426  }
427 
428  output_data();
429 }
430 
431 
432 template<class Model>
434 {
435  // preallocate system matrix
436  for (unsigned int i=0; i<Model::n_substances(); i++)
437  {
438  // preallocate system matrix
439  ls[i]->start_allocation();
440  stiffness_matrix[i] = NULL;
441  rhs[i] = NULL;
442 
443  // preallocate mass matrix
444  ls_dt[i]->start_allocation();
445  mass_matrix[i] = NULL;
446  VecZeroEntries(ret_vec[i]);
447  }
450  set_sources();
452  for (unsigned int i=0; i<Model::n_substances(); i++)
453  {
454  VecAssemblyBegin(ret_vec[i]);
455  VecAssemblyEnd(ret_vec[i]);
456  }
457 
458  allocation_done = true;
459 }
460 
461 
462 
463 template<class Model>
465 {
466  START_TIMER("DG-ONE STEP");
467 
468  Model::time_->next_time();
469  Model::time_->view("TDG");
470 
471  START_TIMER("data reinit");
472  data_.set_time(Model::time_->step(), LimitSide::left);
473  END_TIMER("data reinit");
474 
475  // assemble mass matrix
476  if (mass_matrix[0] == NULL || data_.subset(FieldFlag::in_time_term).changed() )
477  {
478  for (unsigned int i=0; i<Model::n_substances(); i++)
479  {
481  ls_dt[i]->mat_zero_entries();
482  VecZeroEntries(ret_vec[i]);
483  }
485  for (unsigned int i=0; i<Model::n_substances(); i++)
486  {
487  ls_dt[i]->finish_assembly();
488  VecAssemblyBegin(ret_vec[i]);
489  VecAssemblyEnd(ret_vec[i]);
490  // construct mass_vec for initial time
491  if (mass_matrix[i] == NULL)
492  {
493  VecDuplicate(ls[i]->get_solution(), &mass_vec[i]);
494  MatMult(*(ls_dt[i]->get_matrix()), ls[i]->get_solution(), mass_vec[i]);
495  MatConvert(*( ls_dt[i]->get_matrix() ), MATSAME, MAT_INITIAL_MATRIX, &mass_matrix[i]);
496  }
497  else
498  MatCopy(*( ls_dt[i]->get_matrix() ), mass_matrix[i], DIFFERENT_NONZERO_PATTERN);
499  }
500  }
501 
502  // assemble stiffness matrix
503  if (stiffness_matrix[0] == NULL
504  || data_.subset(FieldFlag::in_main_matrix).changed()
505  || Model::flux_changed)
506  {
507  // new fluxes can change the location of Neumann boundary,
508  // thus stiffness matrix must be reassembled
509  for (unsigned int i=0; i<Model::n_substances(); i++)
510  {
511  ls[i]->start_add_assembly();
512  ls[i]->mat_zero_entries();
513  }
515  for (unsigned int i=0; i<Model::n_substances(); i++)
516  {
517  ls[i]->finish_assembly();
518 
519  if (stiffness_matrix[i] == NULL)
520  MatConvert(*( ls[i]->get_matrix() ), MATSAME, MAT_INITIAL_MATRIX, &stiffness_matrix[i]);
521  else
522  MatCopy(*( ls[i]->get_matrix() ), stiffness_matrix[i], DIFFERENT_NONZERO_PATTERN);
523  }
524  }
525 
526  // assemble right hand side (due to sources and boundary conditions)
527  if (rhs[0] == NULL
528  || data_.subset(FieldFlag::in_rhs).changed()
529  || Model::flux_changed)
530  {
531  for (unsigned int i=0; i<Model::n_substances(); i++)
532  {
533  ls[i]->start_add_assembly();
534  ls[i]->rhs_zero_entries();
535  }
536  set_sources();
538  for (unsigned int i=0; i<Model::n_substances(); i++)
539  {
540  ls[i]->finish_assembly();
541 
542  if (rhs[i] == nullptr) VecDuplicate(*( ls[i]->get_rhs() ), &rhs[i]);
543  VecCopy(*( ls[i]->get_rhs() ), rhs[i]);
544  }
545  }
546 
547  Model::flux_changed = false;
548 
549 
550  /* Apply backward Euler time integration.
551  *
552  * Denoting A the stiffness matrix and M the mass matrix, the algebraic system at the k-th time level reads
553  *
554  * (1/dt M + A)u^k = f + 1/dt M.u^{k-1}
555  *
556  * Hence we modify at each time level the right hand side:
557  *
558  * f^k = f + 1/dt M u^{k-1},
559  *
560  * where f stands for the term stemming from the force and boundary conditions.
561  * Accordingly, we set
562  *
563  * A^k = A + 1/dt M.
564  *
565  */
566  Mat m;
567  START_TIMER("solve");
568  for (unsigned int i=0; i<Model::n_substances(); i++)
569  {
570  MatConvert(stiffness_matrix[i], MATSAME, MAT_INITIAL_MATRIX, &m);
571  MatAXPY(m, 1./Model::time_->dt(), mass_matrix[i], SUBSET_NONZERO_PATTERN);
572  ls[i]->set_matrix(m, DIFFERENT_NONZERO_PATTERN);
573  Vec w;
574  VecDuplicate(rhs[i], &w);
575  VecWAXPY(w, 1./Model::time_->dt(), mass_vec[i], rhs[i]);
576  ls[i]->set_rhs(w);
577 
578  VecDestroy(&w);
579  MatDestroy(&m);
580 
581  ls[i]->solve();
582 
583  // update mass_vec due to possible changes in mass matrix
584  MatMult(*(ls_dt[i]->get_matrix()), ls[i]->get_solution(), mass_vec[i]);
585  }
586  END_TIMER("solve");
587 
589 
590  END_TIMER("DG-ONE STEP");
591 }
592 
593 
594 template<class Model>
596 {
597  // calculate element averages of solution
598  for (unsigned int i_cell=0; i_cell<Model::mesh_->get_el_ds()->lsize(); i_cell++)
599  {
600  typename DOFHandlerBase::CellIterator elem = Model::mesh_->element(feo->dh()->el_index(i_cell));
601 
602  unsigned int n_dofs;
603  switch (elem->dim())
604  {
605  case 1:
606  n_dofs = feo->fe<1>()->n_dofs();
607  break;
608  case 2:
609  n_dofs = feo->fe<2>()->n_dofs();
610  break;
611  case 3:
612  n_dofs = feo->fe<3>()->n_dofs();
613  break;
614  }
615 
616  std::vector<IdxInt> dof_indices(n_dofs);
617  feo->dh()->get_dof_indices(elem, dof_indices);
618 
619  for (unsigned int sbi=0; sbi<Model::n_substances(); ++sbi)
620  {
621  solution_elem_[sbi][i_cell] = 0;
622 
623  for (unsigned int j=0; j<n_dofs; ++j)
624  solution_elem_[sbi][i_cell] += ls[sbi]->get_solution_array()[dof_indices[j]-feo->dh()->distr()->begin()];
625 
626  solution_elem_[sbi][i_cell] = max(solution_elem_[sbi][i_cell]/n_dofs, 0.);
627  }
628  }
629 }
630 
631 
632 
633 
634 template<class Model>
636 {
637  //if (!Model::time_->is_current( Model::time_->marks().type_output() )) return;
638 
639 
640  START_TIMER("DG-OUTPUT");
641 
642  // gather the solution from all processors
643  data_.output_fields.set_time( this->time().step(), LimitSide::left);
644  //if (data_.output_fields.is_field_output_time(data_.output_field, this->time().step()) )
646  data_.output_fields.output(this->time().step());
647 
648  Model::output_data();
649 
650  START_TIMER("TOS-balance");
651  for (unsigned int sbi=0; sbi<Model::n_substances(); ++sbi)
652  Model::balance_->calculate_instant(Model::subst_idx[sbi], ls[sbi]->get_solution());
653  Model::balance_->output();
654  END_TIMER("TOS-balance");
655 
656  END_TIMER("DG-OUTPUT");
657 }
658 
659 
660 template<class Model>
662 {
663  if (Model::balance_->cumulative())
664  {
665  for (unsigned int sbi=0; sbi<Model::n_substances(); ++sbi)
666  {
667  Model::balance_->calculate_cumulative(Model::subst_idx[sbi], ls[sbi]->get_solution());
668 
669  // update source increment due to retardation
670  VecDot(ret_vec[sbi], ls[sbi]->get_solution(), &ret_sources[sbi]);
671 
672  Model::balance_->add_cumulative_source(Model::subst_idx[sbi], (ret_sources[sbi]-ret_sources_prev[sbi])/Model::time_->dt());
673  ret_sources_prev[sbi] = ret_sources[sbi];
674  }
675  }
676 }
677 
678 
679 template<class Model>
681 {
682  START_TIMER("assemble_mass");
683  Model::balance_->start_mass_assembly(Model::subst_idx);
684  assemble_mass_matrix<1>();
685  assemble_mass_matrix<2>();
686  assemble_mass_matrix<3>();
687  Model::balance_->finish_mass_assembly(Model::subst_idx);
688  END_TIMER("assemble_mass");
689 }
690 
691 
692 template<class Model> template<unsigned int dim>
694 {
695  FEValues<dim,3> fe_values(*feo->mapping<dim>(), *feo->q<dim>(), *feo->fe<dim>(), update_values | update_JxW_values | update_quadrature_points);
696  const unsigned int ndofs = feo->fe<dim>()->n_dofs(), qsize = feo->q<dim>()->size();
697  vector<IdxInt> dof_indices(ndofs);
698  PetscScalar local_mass_matrix[ndofs*ndofs], local_retardation_balance_vector[ndofs];
699  vector<PetscScalar> local_mass_balance_vector(ndofs);
700 
701  // assemble integral over elements
702  for (unsigned int i_cell=0; i_cell<Model::mesh_->get_el_ds()->lsize(); i_cell++)
703  {
704  typename DOFHandlerBase::CellIterator cell = Model::mesh_->element(feo->dh()->el_index(i_cell));
705  if (cell->dim() != dim) continue;
706 
707  fe_values.reinit(cell);
708  feo->dh()->get_dof_indices(cell, dof_indices);
709  ElementAccessor<3> ele_acc = cell->element_accessor();
710 
711  Model::compute_mass_matrix_coefficient(fe_values.point_list(), ele_acc, mm_coef);
712  Model::compute_retardation_coefficient(fe_values.point_list(), ele_acc, ret_coef);
713 
714  for (unsigned int sbi=0; sbi<Model::n_substances(); ++sbi)
715  {
716  // assemble the local mass matrix
717  for (unsigned int i=0; i<ndofs; i++)
718  {
719  for (unsigned int j=0; j<ndofs; j++)
720  {
721  local_mass_matrix[i*ndofs+j] = 0;
722  for (unsigned int k=0; k<qsize; k++)
723  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);
724  }
725  }
726 
727  for (unsigned int i=0; i<ndofs; i++)
728  {
729  local_mass_balance_vector[i] = 0;
730  local_retardation_balance_vector[i] = 0;
731  for (unsigned int k=0; k<qsize; k++)
732  {
733  local_mass_balance_vector[i] += mm_coef[k]*fe_values.shape_value(i,k)*fe_values.JxW(k);
734  local_retardation_balance_vector[i] -= ret_coef[sbi][k]*fe_values.shape_value(i,k)*fe_values.JxW(k);
735  }
736  }
737 
738  Model::balance_->add_mass_matrix_values(Model::subst_idx[sbi], ele_acc.region().bulk_idx(), dof_indices, local_mass_balance_vector);
739  ls_dt[sbi]->mat_set_values(ndofs, &(dof_indices[0]), ndofs, &(dof_indices[0]), local_mass_matrix);
740  VecSetValues(ret_vec[sbi], ndofs, &(dof_indices[0]), local_retardation_balance_vector, ADD_VALUES);
741  }
742  }
743 }
744 
745 
746 
747 
748 template<class Model>
750 {
751  START_TIMER("assemble_stiffness");
752  START_TIMER("assemble_volume_integrals");
753  assemble_volume_integrals<1>();
754  assemble_volume_integrals<2>();
755  assemble_volume_integrals<3>();
756  END_TIMER("assemble_volume_integrals");
757 
758  START_TIMER("assemble_fluxes_boundary");
759  assemble_fluxes_boundary<1>();
760  assemble_fluxes_boundary<2>();
761  assemble_fluxes_boundary<3>();
762  END_TIMER("assemble_fluxes_boundary");
763 
764  START_TIMER("assemble_fluxes_elem_elem");
765  assemble_fluxes_element_element<1>();
766  assemble_fluxes_element_element<2>();
767  assemble_fluxes_element_element<3>();
768  END_TIMER("assemble_fluxes_elem_elem");
769 
770  START_TIMER("assemble_fluxes_elem_side");
771  assemble_fluxes_element_side<1>();
772  assemble_fluxes_element_side<2>();
773  assemble_fluxes_element_side<3>();
774  END_TIMER("assemble_fluxes_elem_side");
775  END_TIMER("assemble_stiffness");
776 }
777 
778 
779 
780 template<class Model>
781 template<unsigned int dim>
783 {
784  FEValues<dim,3> fv_rt(*feo->mapping<dim>(), *feo->q<dim>(), *feo->fe_rt<dim>(),
786  FEValues<dim,3> fe_values(*feo->mapping<dim>(), *feo->q<dim>(), *feo->fe<dim>(),
788  const unsigned int ndofs = feo->fe<dim>()->n_dofs(), qsize = feo->q<dim>()->size();
789  std::vector<IdxInt> dof_indices(ndofs);
790  vector<arma::vec3> velocity(qsize);
791  vector<vector<double> > sources_sigma(Model::n_substances(), std::vector<double>(qsize));
792  PetscScalar local_matrix[ndofs*ndofs];
793 
794  // assemble integral over elements
795  for (unsigned int i_cell=0; i_cell<Model::mesh_->get_el_ds()->lsize(); i_cell++)
796  {
797  typename DOFHandlerBase::CellIterator cell = Model::mesh_->element(feo->dh()->el_index(i_cell));
798  if (cell->dim() != dim) continue;
799 
800  fe_values.reinit(cell);
801  fv_rt.reinit(cell);
802  ElementAccessor<3> ele_acc = cell->element_accessor();
803  feo->dh()->get_dof_indices(cell, dof_indices);
804 
805  calculate_velocity(cell, velocity, fv_rt);
806  Model::compute_advection_diffusion_coefficients(fe_values.point_list(), velocity, ele_acc, ad_coef, dif_coef);
807  Model::compute_sources_sigma(fe_values.point_list(), ele_acc, sources_sigma);
808 
809  // assemble the local stiffness matrix
810  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
811  {
812  for (unsigned int i=0; i<ndofs; i++)
813  for (unsigned int j=0; j<ndofs; j++)
814  local_matrix[i*ndofs+j] = 0;
815 
816  for (unsigned int k=0; k<qsize; k++)
817  {
818  for (unsigned int i=0; i<ndofs; i++)
819  {
820  arma::vec3 Kt_grad_i = dif_coef[sbi][k].t()*fe_values.shape_grad(i,k);
821  double ad_dot_grad_i = arma::dot(ad_coef[sbi][k], fe_values.shape_grad(i,k));
822 
823  for (unsigned int j=0; j<ndofs; j++)
824  local_matrix[i*ndofs+j] += (arma::dot(Kt_grad_i, fe_values.shape_grad(j,k))
825  -fe_values.shape_value(j,k)*ad_dot_grad_i
826  +sources_sigma[sbi][k]*fe_values.shape_value(j,k)*fe_values.shape_value(i,k))*fe_values.JxW(k);
827  }
828  }
829  ls[sbi]->mat_set_values(ndofs, &(dof_indices[0]), ndofs, &(dof_indices[0]), local_matrix);
830  }
831  }
832 }
833 
834 
835 template<class Model>
837 {
838  START_TIMER("assemble_sources");
839  Model::balance_->start_source_assembly(Model::subst_idx);
840  set_sources<1>();
841  set_sources<2>();
842  set_sources<3>();
843  Model::balance_->finish_source_assembly(Model::subst_idx);
844  END_TIMER("assemble_sources");
845 }
846 
847 template<class Model>
848 template<unsigned int dim>
850 {
851  FEValues<dim,3> fe_values(*feo->mapping<dim>(), *feo->q<dim>(), *feo->fe<dim>(),
853  const unsigned int ndofs = feo->fe<dim>()->n_dofs(), qsize = feo->q<dim>()->size();
854  vector<std::vector<double> > sources_conc(Model::n_substances(), std::vector<double>(qsize)),
855  sources_density(Model::n_substances(), std::vector<double>(qsize)),
856  sources_sigma(Model::n_substances(), std::vector<double>(qsize));
857  vector<IdxInt> dof_indices(ndofs);
858  PetscScalar local_rhs[ndofs];
859  vector<PetscScalar> local_source_balance_vector(ndofs), local_source_balance_rhs(ndofs);
860  double source;
861 
862  // assemble integral over elements
863  for (unsigned int i_cell=0; i_cell<Model::mesh_->get_el_ds()->lsize(); i_cell++)
864  {
865  typename DOFHandlerBase::CellIterator cell = Model::mesh_->element(feo->dh()->el_index(i_cell));
866  if (cell->dim() != dim) continue;
867 
868  fe_values.reinit(cell);
869  feo->dh()->get_dof_indices(cell, dof_indices);
870 
871  Model::compute_source_coefficients(fe_values.point_list(), cell->element_accessor(), sources_conc, sources_density, sources_sigma);
872 
873  // assemble the local stiffness matrix
874  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
875  {
876  fill_n(local_rhs, ndofs, 0);
877  local_source_balance_vector.assign(ndofs, 0);
878  local_source_balance_rhs.assign(ndofs, 0);
879 
880  // compute sources
881  for (unsigned int k=0; k<qsize; k++)
882  {
883  source = (sources_density[sbi][k] + sources_conc[sbi][k]*sources_sigma[sbi][k])*fe_values.JxW(k);
884 
885  for (unsigned int i=0; i<ndofs; i++)
886  local_rhs[i] += source*fe_values.shape_value(i,k);
887  }
888  ls[sbi]->rhs_set_values(ndofs, &(dof_indices[0]), local_rhs);
889 
890  for (unsigned int i=0; i<ndofs; i++)
891  {
892  for (unsigned int k=0; k<qsize; k++)
893  local_source_balance_vector[i] -= sources_sigma[sbi][k]*fe_values.shape_value(i,k)*fe_values.JxW(k);
894 
895  local_source_balance_rhs[i] += local_rhs[i];
896  }
897  Model::balance_->add_source_matrix_values(Model::subst_idx[sbi], cell->region().bulk_idx(), dof_indices, local_source_balance_vector);
898  Model::balance_->add_source_vec_values(Model::subst_idx[sbi], cell->region().bulk_idx(), dof_indices, local_source_balance_rhs);
899  }
900  }
901 }
902 
903 
904 
905 template<class Model>
906 template<unsigned int dim>
908 {
909  vector<FESideValues<dim,3>*> fe_values;
910  FESideValues<dim,3> fsv_rt(*feo->mapping<dim>(), *feo->q<dim-1>(), *feo->fe_rt<dim>(),
911  update_values);
912  const unsigned int ndofs = feo->fe<dim>()->n_dofs(), qsize = feo->q<dim-1>()->size(),
913  n_max_sides = ad_coef_edg.size();
914  vector< vector<IdxInt> > side_dof_indices;
915  PetscScalar local_matrix[ndofs*ndofs];
916  vector<vector<arma::vec3> > side_velocity(n_max_sides);
917  vector<vector<double> > dg_penalty(n_max_sides);
918  double gamma_l, omega[2], transport_flux;
919 
920  for (unsigned int sid=0; sid<n_max_sides; sid++) // Optimize: SWAP LOOPS
921  {
922  side_dof_indices.push_back( vector<IdxInt>(ndofs) );
923  fe_values.push_back(new FESideValues<dim,3>(*feo->mapping<dim>(), *feo->q<dim-1>(), *feo->fe<dim>(),
925  }
926 
927  // assemble integral over sides
928  for (unsigned int iedg=0; iedg<feo->dh()->n_loc_edges(); iedg++)
929  {
930  Edge *edg = &Model::mesh_->edges[feo->dh()->edge_index(iedg)];
931  if (edg->n_sides < 2 || edg->side(0)->element()->dim() != dim) continue;
932 
933  for (int sid=0; sid<edg->n_sides; sid++)
934  {
935  typename DOFHandlerBase::CellIterator cell = edg->side(sid)->element();
936  ElementAccessor<3> ele_acc = cell->element_accessor();
937  feo->dh()->get_dof_indices(cell, side_dof_indices[sid]);
938  fe_values[sid]->reinit(cell, edg->side(sid)->el_idx());
939  fsv_rt.reinit(cell, edg->side(sid)->el_idx());
940  calculate_velocity(cell, side_velocity[sid], fsv_rt);
941  Model::compute_advection_diffusion_coefficients(fe_values[sid]->point_list(), side_velocity[sid], ele_acc, ad_coef_edg[sid], dif_coef_edg[sid]);
942  dg_penalty[sid].resize(Model::n_substances());
943  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
944  dg_penalty[sid][sbi] = data_.dg_penalty[sbi].value(cell->centre(), ele_acc);
945  }
946 
947  // fluxes and penalty
948  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
949  {
950  vector<double> fluxes(edg->n_sides);
951  for (int sid=0; sid<edg->n_sides; sid++)
952  {
953  fluxes[sid] = 0;
954  for (unsigned int k=0; k<qsize; k++)
955  fluxes[sid] += arma::dot(ad_coef_edg[sid][sbi][k], fe_values[sid]->normal_vector(k))*fe_values[sid]->JxW(k);
956  fluxes[sid] /= edg->side(sid)->measure();
957  }
958 
959  for (int s1=0; s1<edg->n_sides; s1++)
960  {
961  for (int s2=s1+1; s2<edg->n_sides; s2++)
962  {
963  OLD_ASSERT(edg->side(s1)->valid(), "Invalid side of edge.");
964  if (!feo->dh()->el_is_local(edg->side(s1)->element().index())
965  && !feo->dh()->el_is_local(edg->side(s2)->element().index())) continue;
966 
967  arma::vec3 nv = fe_values[s1]->normal_vector(0);
968 
969  // set up the parameters for DG method
970  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);
971 
972  int sd[2];
973  sd[0] = s1;
974  sd[1] = s2;
975 
976 #define AVERAGE(i,k,side_id) (fe_values[sd[side_id]]->shape_value(i,k)*0.5)
977 #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])
978 #define JUMP(i,k,side_id) ((side_id==0?1:-1)*fe_values[sd[side_id]]->shape_value(i,k))
979 
980  // For selected pair of elements:
981  for (int n=0; n<2; n++)
982  {
983  if (!feo->dh()->el_is_local(edg->side(sd[n])->element().index())) continue;
984 
985  for (int m=0; m<2; m++)
986  {
987  for (unsigned int i=0; i<fe_values[sd[n]]->n_dofs(); i++)
988  for (unsigned int j=0; j<fe_values[sd[m]]->n_dofs(); j++)
989  local_matrix[i*fe_values[sd[m]]->n_dofs()+j] = 0;
990 
991  for (unsigned int k=0; k<qsize; k++)
992  {
993  double flux_times_JxW = transport_flux*fe_values[0]->JxW(k);
994  double gamma_times_JxW = gamma_l*fe_values[0]->JxW(k);
995 
996  for (unsigned int i=0; i<fe_values[sd[n]]->n_dofs(); i++)
997  {
998  double flux_JxW_jump_i = flux_times_JxW*JUMP(i,k,n);
999  double gamma_JxW_jump_i = gamma_times_JxW*JUMP(i,k,n);
1000  double JxW_jump_i = fe_values[0]->JxW(k)*JUMP(i,k,n);
1001  double JxW_var_wavg_i = fe_values[0]->JxW(k)*WAVERAGE(i,k,n)*dg_variant;
1002 
1003  for (unsigned int j=0; j<fe_values[sd[m]]->n_dofs(); j++)
1004  {
1005  int index = i*fe_values[sd[m]]->n_dofs()+j;
1006 
1007  // flux due to transport (applied on interior edges) (average times jump)
1008  local_matrix[index] += flux_JxW_jump_i*AVERAGE(j,k,m);
1009 
1010  // penalty enforcing continuity across edges (applied on interior and Dirichlet edges) (jump times jump)
1011  local_matrix[index] += gamma_JxW_jump_i*JUMP(j,k,m);
1012 
1013  // terms due to diffusion
1014  local_matrix[index] -= WAVERAGE(j,k,m)*JxW_jump_i;
1015  local_matrix[index] -= JUMP(j,k,m)*JxW_var_wavg_i;
1016  }
1017  }
1018  }
1019  ls[sbi]->mat_set_values(fe_values[sd[n]]->n_dofs(), &(side_dof_indices[sd[n]][0]), fe_values[sd[m]]->n_dofs(), &(side_dof_indices[sd[m]][0]), local_matrix);
1020  }
1021  }
1022 #undef AVERAGE
1023 #undef WAVERAGE
1024 #undef JUMP
1025  }
1026  }
1027  }
1028  }
1029 
1030  for (unsigned int i=0; i<n_max_sides; i++)
1031  {
1032  delete fe_values[i];
1033  }
1034 }
1035 
1036 
1037 template<class Model>
1038 template<unsigned int dim>
1040 {
1041  FESideValues<dim,3> fe_values_side(*feo->mapping<dim>(), *feo->q<dim-1>(), *feo->fe<dim>(),
1043  FESideValues<dim,3> fsv_rt(*feo->mapping<dim>(), *feo->q<dim-1>(), *feo->fe_rt<dim>(),
1044  update_values);
1045  const unsigned int ndofs = feo->fe<dim>()->n_dofs(), qsize = feo->q<dim-1>()->size();
1046  std::vector<IdxInt> side_dof_indices(ndofs);
1047  PetscScalar local_matrix[ndofs*ndofs];
1048  vector<arma::vec3> side_velocity;
1049  vector<double> robin_sigma(qsize);
1050  vector<double> csection(qsize);
1051  double gamma_l;
1052 
1053  // assemble boundary integral
1054  for (unsigned int iedg=0; iedg<feo->dh()->n_loc_edges(); iedg++)
1055  {
1056  Edge *edg = &Model::mesh_->edges[feo->dh()->edge_index(iedg)];
1057  if (edg->n_sides > 1) continue;
1058  // check spatial dimension
1059  if (edg->side(0)->dim() != dim-1) continue;
1060  // skip edges lying not on the boundary
1061  if (edg->side(0)->cond() == NULL) continue;
1062 
1063  SideIter side = edg->side(0);
1064  typename DOFHandlerBase::CellIterator cell = side->element();
1065  ElementAccessor<3> ele_acc = cell->element_accessor();
1066  feo->dh()->get_dof_indices(cell, side_dof_indices);
1067  fe_values_side.reinit(cell, side->el_idx());
1068  fsv_rt.reinit(cell, side->el_idx());
1069 
1070  calculate_velocity(cell, side_velocity, fsv_rt);
1071  Model::compute_advection_diffusion_coefficients(fe_values_side.point_list(), side_velocity, ele_acc, ad_coef, dif_coef);
1072  arma::uvec bc_type;
1073  Model::get_bc_type(side->cond()->element_accessor(), bc_type);
1074  data_.cross_section.value_list(fe_values_side.point_list(), ele_acc, csection);
1075 
1076  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
1077  {
1078  for (unsigned int i=0; i<ndofs; i++)
1079  for (unsigned int j=0; j<ndofs; j++)
1080  local_matrix[i*ndofs+j] = 0;
1081 
1082  // On Neumann boundaries we have only term from integrating by parts the advective term,
1083  // on Dirichlet boundaries we additionally apply the penalty which enforces the prescribed value.
1084  double side_flux = 0;
1085  for (unsigned int k=0; k<qsize; k++)
1086  side_flux += arma::dot(ad_coef[sbi][k], fe_values_side.normal_vector(k))*fe_values_side.JxW(k);
1087  double transport_flux = side_flux/side->measure();
1088 
1089  if (bc_type[sbi] == AdvectionDiffusionModel::abc_dirichlet)
1090  {
1091  // set up the parameters for DG method
1092  set_DG_parameters_boundary(side, qsize, dif_coef[sbi], transport_flux, fe_values_side.normal_vector(0), data_.dg_penalty[sbi].value(cell->centre(), ele_acc), gamma_l);
1093  gamma[sbi][side->cond_idx()] = gamma_l;
1094  transport_flux += gamma_l;
1095  }
1096 
1097  // fluxes and penalty
1098  for (unsigned int k=0; k<qsize; k++)
1099  {
1100  double flux_times_JxW;
1101  if (bc_type[sbi] == AdvectionDiffusionModel::abc_total_flux)
1102  {
1103  Model::get_flux_bc_sigma(sbi, fe_values_side.point_list(), side->cond()->element_accessor(), robin_sigma);
1104  flux_times_JxW = csection[k]*robin_sigma[k]*fe_values_side.JxW(k);
1105  }
1106  else if (bc_type[sbi] == AdvectionDiffusionModel::abc_diffusive_flux)
1107  {
1108  Model::get_flux_bc_sigma(sbi, fe_values_side.point_list(), side->cond()->element_accessor(), robin_sigma);
1109  flux_times_JxW = (transport_flux + csection[k]*robin_sigma[k])*fe_values_side.JxW(k);
1110  }
1111  else if (bc_type[sbi] == AdvectionDiffusionModel::abc_inflow && side_flux < 0)
1112  flux_times_JxW = 0;
1113  else
1114  flux_times_JxW = transport_flux*fe_values_side.JxW(k);
1115 
1116  for (unsigned int i=0; i<ndofs; i++)
1117  {
1118  for (unsigned int j=0; j<ndofs; j++)
1119  {
1120  // flux due to advection and penalty
1121  local_matrix[i*ndofs+j] += flux_times_JxW*fe_values_side.shape_value(i,k)*fe_values_side.shape_value(j,k);
1122 
1123  // flux due to diffusion (only on dirichlet and inflow boundary)
1124  if (bc_type[sbi] == AdvectionDiffusionModel::abc_dirichlet)
1125  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)
1126  + 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
1127  )*fe_values_side.JxW(k);
1128  }
1129  }
1130  }
1131 
1132  ls[sbi]->mat_set_values(ndofs, &(side_dof_indices[0]), ndofs, &(side_dof_indices[0]), local_matrix);
1133  }
1134  }
1135 }
1136 
1137 
1138 template<class Model>
1139 template<unsigned int dim>
1141 {
1142 
1143  if (dim == 1) return;
1144  FEValues<dim-1,3> fe_values_vb(*feo->mapping<dim-1>(), *feo->q<dim-1>(), *feo->fe<dim-1>(),
1146  FESideValues<dim,3> fe_values_side(*feo->mapping<dim>(), *feo->q<dim-1>(), *feo->fe<dim>(),
1148  FESideValues<dim,3> fsv_rt(*feo->mapping<dim>(), *feo->q<dim-1>(), *feo->fe_rt<dim>(),
1149  update_values);
1150  FEValues<dim-1,3> fv_rt(*feo->mapping<dim-1>(), *feo->q<dim-1>(), *feo->fe_rt<dim-1>(),
1151  update_values);
1152 
1153  vector<FEValuesSpaceBase<3>*> fv_sb(2);
1154  const unsigned int ndofs = feo->fe<dim>()->n_dofs(); // number of local dofs
1155  const unsigned int qsize = feo->q<dim-1>()->size(); // number of quadrature points
1156  int side_dof_indices[2*ndofs];
1157  vector<IdxInt> indices(ndofs);
1158  unsigned int n_dofs[2], n_indices;
1159  vector<arma::vec3> velocity_higher, velocity_lower;
1160  vector<double> frac_sigma(qsize);
1161  vector<double> csection_lower(qsize), csection_higher(qsize);
1162  PetscScalar local_matrix[4*ndofs*ndofs];
1163  double comm_flux[2][2];
1164 
1165  // index 0 = element with lower dimension,
1166  // index 1 = side of element with higher dimension
1167  fv_sb[0] = &fe_values_vb;
1168  fv_sb[1] = &fe_values_side;
1169 
1170  // assemble integral over sides
1171  for (unsigned int inb=0; inb<feo->dh()->n_loc_nb(); inb++)
1172  {
1173  Neighbour *nb = &Model::mesh_->vb_neighbours_[feo->dh()->nb_index(inb)];
1174  // skip neighbours of different dimension
1175  if (nb->element()->dim() != dim-1) continue;
1176 
1177  typename DOFHandlerBase::CellIterator cell_sub = Model::mesh_->element.full_iter(nb->element());
1178  n_indices = feo->dh()->get_dof_indices(cell_sub, indices);
1179  for(unsigned int i=0; i<n_indices; ++i) {
1180  side_dof_indices[i] = indices[i];
1181  }
1182  fe_values_vb.reinit(cell_sub);
1183  n_dofs[0] = fv_sb[0]->n_dofs();
1184 
1185  typename DOFHandlerBase::CellIterator cell = nb->side()->element();
1186  n_indices = feo->dh()->get_dof_indices(cell, indices);
1187  for(unsigned int i=0; i<n_indices; ++i) {
1188  side_dof_indices[i+n_dofs[0]] = indices[i];
1189  }
1190  fe_values_side.reinit(cell, nb->side()->el_idx());
1191  n_dofs[1] = fv_sb[1]->n_dofs();
1192 
1193  // Element id's for testing if they belong to local partition.
1194  int element_id[2];
1195  element_id[0] = cell_sub.index();
1196  element_id[1] = cell.index();
1197 
1198  fsv_rt.reinit(cell, nb->side()->el_idx());
1199  fv_rt.reinit(cell_sub);
1200  calculate_velocity(cell, velocity_higher, fsv_rt);
1201  calculate_velocity(cell_sub, velocity_lower, fv_rt);
1202  Model::compute_advection_diffusion_coefficients(fe_values_vb.point_list(), velocity_lower, cell_sub->element_accessor(), ad_coef_edg[0], dif_coef_edg[0]);
1203  Model::compute_advection_diffusion_coefficients(fe_values_vb.point_list(), velocity_higher, cell->element_accessor(), ad_coef_edg[1], dif_coef_edg[1]);
1204  data_.cross_section.value_list(fe_values_vb.point_list(), cell_sub->element_accessor(), csection_lower);
1205  data_.cross_section.value_list(fe_values_vb.point_list(), cell->element_accessor(), csection_higher);
1206 
1207  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++) // Optimize: SWAP LOOPS
1208  {
1209  for (unsigned int i=0; i<n_dofs[0]+n_dofs[1]; i++)
1210  for (unsigned int j=0; j<n_dofs[0]+n_dofs[1]; j++)
1211  local_matrix[i*(n_dofs[0]+n_dofs[1])+j] = 0;
1212 
1213  data_.fracture_sigma[sbi].value_list(fe_values_vb.point_list(), cell_sub->element_accessor(), frac_sigma);
1214 
1215  // set transmission conditions
1216  for (unsigned int k=0; k<qsize; k++)
1217  {
1218  /* The communication flux has two parts:
1219  * - "diffusive" term containing sigma
1220  * - "advective" term representing usual upwind
1221  *
1222  * The calculation differs from the reference manual, since ad_coef and dif_coef have different meaning
1223  * than b and A in the manual.
1224  * In calculation of sigma there appears one more csection_lower in the denominator.
1225  */
1226  double sigma = frac_sigma[k]*arma::dot(dif_coef_edg[0][sbi][k]*fe_values_side.normal_vector(k),fe_values_side.normal_vector(k))*
1227  2*csection_higher[k]*csection_higher[k]/(csection_lower[k]*csection_lower[k]);
1228 
1229  double transport_flux = arma::dot(ad_coef_edg[1][sbi][k], fe_values_side.normal_vector(k));
1230 
1231  comm_flux[0][0] = (sigma-min(0.,transport_flux))*fv_sb[0]->JxW(k);
1232  comm_flux[0][1] = -(sigma-min(0.,transport_flux))*fv_sb[0]->JxW(k);
1233  comm_flux[1][0] = -(sigma+max(0.,transport_flux))*fv_sb[0]->JxW(k);
1234  comm_flux[1][1] = (sigma+max(0.,transport_flux))*fv_sb[0]->JxW(k);
1235 
1236  for (int n=0; n<2; n++)
1237  {
1238  if (!feo->dh()->el_is_local(element_id[n])) continue;
1239 
1240  for (unsigned int i=0; i<n_dofs[n]; i++)
1241  for (int m=0; m<2; m++)
1242  for (unsigned int j=0; j<n_dofs[m]; j++)
1243  local_matrix[(i+n*n_dofs[0])*(n_dofs[0]+n_dofs[1]) + m*n_dofs[0] + j] +=
1244  comm_flux[m][n]*fv_sb[m]->shape_value(j,k)*fv_sb[n]->shape_value(i,k);
1245  }
1246  }
1247  ls[sbi]->mat_set_values(n_dofs[0]+n_dofs[1], side_dof_indices, n_dofs[0]+n_dofs[1], side_dof_indices, local_matrix);
1248  }
1249  }
1250 
1251 }
1252 
1253 
1254 
1255 
1256 
1257 
1258 template<class Model>
1260 {
1261  START_TIMER("assemble_bc");
1262  Model::balance_->start_flux_assembly(Model::subst_idx);
1263  set_boundary_conditions<1>();
1264  set_boundary_conditions<2>();
1265  set_boundary_conditions<3>();
1266  Model::balance_->finish_flux_assembly(Model::subst_idx);
1267  END_TIMER("assemble_bc");
1268 }
1269 
1270 
1271 template<class Model>
1272 template<unsigned int dim>
1274 {
1275  FESideValues<dim,3> fe_values_side(*feo->mapping<dim>(), *feo->q<dim-1>(), *feo->fe<dim>(),
1277  FESideValues<dim,3> fsv_rt(*feo->mapping<dim>(), *feo->q<dim-1>(), *feo->fe_rt<dim>(),
1278  update_values);
1279  const unsigned int ndofs = feo->fe<dim>()->n_dofs(), qsize = feo->q<dim-1>()->size();
1280  vector<IdxInt> side_dof_indices(ndofs);
1281  unsigned int loc_b=0;
1282  double local_rhs[ndofs];
1283  vector<PetscScalar> local_flux_balance_vector(ndofs);
1284  PetscScalar local_flux_balance_rhs;
1285  vector<double> bc_values(qsize);
1286  vector<double> bc_fluxes(qsize),
1287  bc_sigma(qsize),
1288  bc_ref_values(qsize);
1289  vector<double> csection(qsize);
1290  vector<arma::vec3> velocity;
1291 
1292  for (unsigned int loc_el = 0; loc_el < Model::mesh_->get_el_ds()->lsize(); loc_el++)
1293  {
1294  ElementFullIter elm = Model::mesh_->element(feo->dh()->el_index(loc_el));
1295  if (elm->boundary_idx_ == nullptr) continue;
1296 
1297  FOR_ELEMENT_SIDES(elm,si)
1298  {
1299  Edge *edg = elm->side(si)->edge();
1300  if (edg->n_sides > 1) continue;
1301  // skip edges lying not on the boundary
1302  if (edg->side(0)->cond() == NULL) continue;
1303 
1304  if (edg->side(0)->dim() != dim-1)
1305  {
1306  if (edg->side(0)->cond() != nullptr) ++loc_b;
1307  continue;
1308  }
1309 
1310  SideIter side = edg->side(0);
1311  typename DOFHandlerBase::CellIterator cell = Model::mesh_->element.full_iter(side->element());
1312  ElementAccessor<3> ele_acc = side->cond()->element_accessor();
1313 
1314  arma::uvec bc_type;
1315  Model::get_bc_type(ele_acc, bc_type);
1316 
1317  fe_values_side.reinit(cell, side->el_idx());
1318  fsv_rt.reinit(cell, side->el_idx());
1319  calculate_velocity(cell, velocity, fsv_rt);
1320 
1321  Model::compute_advection_diffusion_coefficients(fe_values_side.point_list(), velocity, side->element()->element_accessor(), ad_coef, dif_coef);
1322  data_.cross_section.value_list(fe_values_side.point_list(), side->element()->element_accessor(), csection);
1323 
1324  feo->dh()->get_dof_indices(cell, side_dof_indices);
1325 
1326  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
1327  {
1328  fill_n(local_rhs, ndofs, 0);
1329  local_flux_balance_vector.assign(ndofs, 0);
1330  local_flux_balance_rhs = 0;
1331 
1332  // The b.c. data are fetched for all possible b.c. types since we allow
1333  // different bc_type for each substance.
1334  data_.bc_dirichlet_value[sbi].value_list(fe_values_side.point_list(), ele_acc, bc_values);
1335 
1336  double side_flux = 0;
1337  for (unsigned int k=0; k<qsize; k++)
1338  side_flux += arma::dot(ad_coef[sbi][k], fe_values_side.normal_vector(k))*fe_values_side.JxW(k);
1339  double transport_flux = side_flux/side->measure();
1340 
1341  if (bc_type[sbi] == AdvectionDiffusionModel::abc_inflow && side_flux < 0)
1342  {
1343  for (unsigned int k=0; k<qsize; k++)
1344  {
1345  double bc_term = -transport_flux*bc_values[k]*fe_values_side.JxW(k);
1346  for (unsigned int i=0; i<ndofs; i++)
1347  local_rhs[i] += bc_term*fe_values_side.shape_value(i,k);
1348  }
1349  for (unsigned int i=0; i<ndofs; i++)
1350  local_flux_balance_rhs -= local_rhs[i];
1351  }
1352  else if (bc_type[sbi] == AdvectionDiffusionModel::abc_dirichlet)
1353  {
1354  for (unsigned int k=0; k<qsize; k++)
1355  {
1356  double bc_term = gamma[sbi][side->cond_idx()]*bc_values[k]*fe_values_side.JxW(k);
1357  arma::vec3 bc_grad = -bc_values[k]*fe_values_side.JxW(k)*dg_variant*(arma::trans(dif_coef[sbi][k])*fe_values_side.normal_vector(k));
1358  for (unsigned int i=0; i<ndofs; i++)
1359  local_rhs[i] += bc_term*fe_values_side.shape_value(i,k)
1360  + arma::dot(bc_grad,fe_values_side.shape_grad(i,k));
1361  }
1362  for (unsigned int k=0; k<qsize; k++)
1363  {
1364  for (unsigned int i=0; i<ndofs; i++)
1365  {
1366  local_flux_balance_vector[i] += (arma::dot(ad_coef[sbi][k], fe_values_side.normal_vector(k))*fe_values_side.shape_value(i,k)
1367  - arma::dot(dif_coef[sbi][k]*fe_values_side.shape_grad(i,k),fe_values_side.normal_vector(k))
1368  + gamma[sbi][side->cond_idx()]*fe_values_side.shape_value(i,k))*fe_values_side.JxW(k);
1369  }
1370  }
1371  if (Model::time_->tlevel() > 0)
1372  for (unsigned int i=0; i<ndofs; i++)
1373  local_flux_balance_rhs -= local_rhs[i];
1374  }
1375  else if (bc_type[sbi] == AdvectionDiffusionModel::abc_total_flux)
1376  {
1377  Model::get_flux_bc_data(sbi, fe_values_side.point_list(), ele_acc, bc_fluxes, bc_sigma, bc_ref_values);
1378  for (unsigned int k=0; k<qsize; k++)
1379  {
1380  double bc_term = csection[k]*(bc_sigma[k]*bc_ref_values[k]+bc_fluxes[k])*fe_values_side.JxW(k);
1381  for (unsigned int i=0; i<ndofs; i++)
1382  local_rhs[i] += bc_term*fe_values_side.shape_value(i,k);
1383  }
1384 
1385  for (unsigned int i=0; i<ndofs; i++)
1386  {
1387  for (unsigned int k=0; k<qsize; k++)
1388  local_flux_balance_vector[i] += csection[k]*bc_sigma[k]*fe_values_side.JxW(k)*fe_values_side.shape_value(i,k);
1389  local_flux_balance_rhs -= local_rhs[i];
1390  }
1391  }
1392  else if (bc_type[sbi] == AdvectionDiffusionModel::abc_diffusive_flux)
1393  {
1394  Model::get_flux_bc_data(sbi, fe_values_side.point_list(), ele_acc, bc_fluxes, bc_sigma, bc_ref_values);
1395  for (unsigned int k=0; k<qsize; k++)
1396  {
1397  double bc_term = csection[k]*(bc_sigma[k]*bc_ref_values[k]+bc_fluxes[k])*fe_values_side.JxW(k);
1398  for (unsigned int i=0; i<ndofs; i++)
1399  local_rhs[i] += bc_term*fe_values_side.shape_value(i,k);
1400  }
1401 
1402  for (unsigned int i=0; i<ndofs; i++)
1403  {
1404  for (unsigned int k=0; k<qsize; k++)
1405  local_flux_balance_vector[i] += csection[k]*(arma::dot(ad_coef[sbi][k], fe_values_side.normal_vector(k)) + bc_sigma[k])*fe_values_side.JxW(k)*fe_values_side.shape_value(i,k);
1406  local_flux_balance_rhs -= local_rhs[i];
1407  }
1408  }
1409  else if (bc_type[sbi] == AdvectionDiffusionModel::abc_inflow && side_flux >= 0)
1410  {
1411  for (unsigned int k=0; k<qsize; k++)
1412  {
1413  for (unsigned int i=0; i<ndofs; i++)
1414  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);
1415  }
1416  }
1417  ls[sbi]->rhs_set_values(ndofs, &(side_dof_indices[0]), local_rhs);
1418 
1419  Model::balance_->add_flux_matrix_values(Model::subst_idx[sbi], loc_b, side_dof_indices, local_flux_balance_vector);
1420  Model::balance_->add_flux_vec_value(Model::subst_idx[sbi], loc_b, local_flux_balance_rhs);
1421  }
1422  ++loc_b;
1423  }
1424  }
1425 }
1426 
1427 
1428 
1429 template<class Model>
1430 template<unsigned int dim>
1432  vector<arma::vec3> &velocity,
1433  FEValuesBase<dim,3> &fv)
1434 {
1435  OLD_ASSERT(cell->dim() == dim, "Element dimension mismatch!");
1436 
1437  velocity.resize(fv.n_points());
1438 
1439  for (unsigned int k=0; k<fv.n_points(); k++)
1440  {
1441  velocity[k].zeros();
1442  for (unsigned int sid=0; sid<cell->n_sides(); sid++)
1443  for (unsigned int c=0; c<3; ++c)
1444  velocity[k][c] += fv.shape_value_component(sid,k,c) * Model::mh_dh->side_flux( *(cell->side(sid)) );
1445  }
1446 }
1447 
1448 
1449 // return the ratio of longest and shortest edge
1451 {
1452  double h_max = 0, h_min = numeric_limits<double>::infinity();
1453  for (unsigned int i=0; i<e->n_nodes(); i++)
1454  for (unsigned int j=i+1; j<e->n_nodes(); j++)
1455  {
1456  h_max = max(h_max, e->node[i]->distance(*e->node[j]));
1457  h_min = min(h_min, e->node[i]->distance(*e->node[j]));
1458  }
1459  return h_max/h_min;
1460 }
1461 
1462 
1463 
1464 template<class Model>
1466  const int s1,
1467  const int s2,
1468  const int K_size,
1469  const vector<arma::mat33> &K1,
1470  const vector<arma::mat33> &K2,
1471  const vector<double> &fluxes,
1472  const arma::vec3 &normal_vector,
1473  const double alpha1,
1474  const double alpha2,
1475  double &gamma,
1476  double *omega,
1477  double &transport_flux)
1478 {
1479  double delta[2];
1480  double h = 0;
1481  double local_alpha = 0;
1482 
1483  OLD_ASSERT(edg.side(s1)->valid(), "Invalid side of an edge.");
1484  SideIter s = edg.side(s1);
1485 
1486  // calculate the side diameter
1487  if (s->dim() == 0)
1488  {
1489  h = 1;
1490  }
1491  else
1492  {
1493  for (unsigned int i=0; i<s->n_nodes(); i++)
1494  for (unsigned int j=i+1; j<s->n_nodes(); j++)
1495  h = max(h, s->node(i)->distance(*s->node(j)));
1496  }
1497 
1498  double aniso1 = elem_anisotropy(edg.side(s1)->element());
1499  double aniso2 = elem_anisotropy(edg.side(s2)->element());
1500 
1501 
1502  // calculate the total in- and out-flux through the edge
1503  double pflux = 0, nflux = 0;
1504  for (int i=0; i<edg.n_sides; i++)
1505  {
1506  if (fluxes[i] > 0)
1507  pflux += fluxes[i];
1508  else
1509  nflux += fluxes[i];
1510  }
1511 
1512  // calculate the flux from s1 to s2
1513  if (fluxes[s2] > 0 && fluxes[s1] < 0 && s1 < s2)
1514  transport_flux = fluxes[s1]*fabs(fluxes[s2]/pflux);
1515  else if (fluxes[s2] < 0 && fluxes[s1] > 0 && s1 < s2)
1516  transport_flux = fluxes[s1]*fabs(fluxes[s2]/nflux);
1517  else if (s1==s2)
1518  transport_flux = fluxes[s1];
1519  else
1520  transport_flux = 0;
1521 
1522  gamma = 0.5*fabs(transport_flux);
1523 
1524 
1525  // determine local DG penalty
1526  local_alpha = max(alpha1, alpha2);
1527 
1528  if (s1 == s2)
1529  {
1530  omega[0] = 1;
1531 
1532  // delta is set to the average value of Kn.n on the side
1533  delta[0] = 0;
1534  for (int k=0; k<K_size; k++)
1535  delta[0] += dot(K1[k]*normal_vector,normal_vector);
1536  delta[0] /= K_size;
1537 
1538  gamma += local_alpha/h*aniso1*aniso2*delta[0];
1539  }
1540  else
1541  {
1542  delta[0] = 0;
1543  delta[1] = 0;
1544  for (int k=0; k<K_size; k++)
1545  {
1546  delta[0] += dot(K1[k]*normal_vector,normal_vector);
1547  delta[1] += dot(K2[k]*normal_vector,normal_vector);
1548  }
1549  delta[0] /= K_size;
1550  delta[1] /= K_size;
1551 
1552  double delta_sum = delta[0] + delta[1];
1553 
1554 // if (delta_sum > numeric_limits<double>::epsilon())
1555  if (fabs(delta_sum) > 0)
1556  {
1557  omega[0] = delta[1]/delta_sum;
1558  omega[1] = delta[0]/delta_sum;
1559  gamma += local_alpha/h*aniso1*aniso2*(delta[0]*delta[1]/delta_sum);
1560  }
1561  else
1562  for (int i=0; i<2; i++) omega[i] = 0;
1563  }
1564 }
1565 
1566 
1567 
1568 
1569 
1570 
1571 template<class Model>
1573  const int K_size,
1574  const vector<arma::mat33> &K,
1575  const double flux,
1576  const arma::vec3 &normal_vector,
1577  const double alpha,
1578  double &gamma)
1579 {
1580  double delta = 0, h = 0;
1581 
1582  // calculate the side diameter
1583  if (side->dim() == 0)
1584  {
1585  h = 1;
1586  }
1587  else
1588  {
1589  for (unsigned int i=0; i<side->n_nodes(); i++)
1590  for (unsigned int j=i+1; j<side->n_nodes(); j++)
1591  h = max(h, side->node(i)->distance( *side->node(j) ));
1592  }
1593 
1594  // delta is set to the average value of Kn.n on the side
1595  for (int k=0; k<K_size; k++)
1596  delta += dot(K[k]*normal_vector,normal_vector);
1597  delta /= K_size;
1598 
1599  gamma = 0.5*fabs(flux) + alpha/h*delta*elem_anisotropy(side->element());
1600 }
1601 
1602 
1603 
1604 
1605 
1606 template<class Model>
1608 {
1609  START_TIMER("set_init_cond");
1610  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
1611  ls[sbi]->start_allocation();
1612  prepare_initial_condition<1>();
1613  prepare_initial_condition<2>();
1614  prepare_initial_condition<3>();
1615 
1616  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
1617  ls[sbi]->start_add_assembly();
1618  prepare_initial_condition<1>();
1619  prepare_initial_condition<2>();
1620  prepare_initial_condition<3>();
1621 
1622  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
1623  {
1624  ls[sbi]->finish_assembly();
1625  ls[sbi]->solve();
1626  }
1627  END_TIMER("set_init_cond");
1628 }
1629 
1630 template<class Model>
1631 template<unsigned int dim>
1633 {
1634  FEValues<dim,3> fe_values(*feo->mapping<dim>(), *feo->q<dim>(), *feo->fe<dim>(),
1636  const unsigned int ndofs = feo->fe<dim>()->n_dofs(), qsize = feo->q<dim>()->size();
1637  std::vector<IdxInt> dof_indices(ndofs);
1638  double matrix[ndofs*ndofs], rhs[ndofs];
1639  std::vector<std::vector<double> > init_values(Model::n_substances());
1640 
1641  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++) // Optimize: SWAP LOOPS
1642  init_values[sbi].resize(qsize);
1643 
1644  for (unsigned int i_cell=0; i_cell<Model::mesh_->get_el_ds()->lsize(); i_cell++)
1645  {
1646  typename DOFHandlerBase::CellIterator elem = Model::mesh_->element(feo->dh()->el_index(i_cell));
1647  if (elem->dim() != dim) continue;
1648 
1649  ElementAccessor<3> ele_acc = elem->element_accessor();
1650  feo->dh()->get_dof_indices(elem, dof_indices);
1651  fe_values.reinit(elem);
1652 
1653  Model::compute_init_cond(fe_values.point_list(), ele_acc, init_values);
1654 
1655  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
1656  {
1657  for (unsigned int i=0; i<ndofs; i++)
1658  {
1659  rhs[i] = 0;
1660  for (unsigned int j=0; j<ndofs; j++)
1661  matrix[i*ndofs+j] = 0;
1662  }
1663 
1664  for (unsigned int k=0; k<qsize; k++)
1665  {
1666  double rhs_term = init_values[sbi][k]*fe_values.JxW(k);
1667 
1668  for (unsigned int i=0; i<ndofs; i++)
1669  {
1670  for (unsigned int j=0; j<ndofs; j++)
1671  matrix[i*ndofs+j] += fe_values.shape_value(i,k)*fe_values.shape_value(j,k)*fe_values.JxW(k);
1672 
1673  rhs[i] += fe_values.shape_value(i,k)*rhs_term;
1674  }
1675  }
1676  ls[sbi]->set_values(ndofs, &(dof_indices[0]), ndofs, &(dof_indices[0]), matrix, rhs);
1677  }
1678  }
1679 }
1680 
1681 
1682 template<class Model>
1684 {
1685  el_4_loc = Model::mesh_->get_el_4_loc();
1686  el_ds = Model::mesh_->get_el_ds();
1687 }
1688 
1689 
1690 template<class Model>
1692 {
1693  if (solution_changed)
1694  {
1695  for (unsigned int i_cell=0; i_cell<Model::mesh_->get_el_ds()->lsize(); i_cell++)
1696  {
1697  typename DOFHandlerBase::CellIterator elem = Model::mesh_->element(feo->dh()->el_index(i_cell));
1698 
1699  unsigned int n_dofs;
1700  switch (elem->dim())
1701  {
1702  case 1:
1703  n_dofs = feo->fe<1>()->n_dofs();
1704  break;
1705  case 2:
1706  n_dofs = feo->fe<2>()->n_dofs();
1707  break;
1708  case 3:
1709  n_dofs = feo->fe<3>()->n_dofs();
1710  break;
1711  }
1712 
1713  std::vector<IdxInt> dof_indices(n_dofs);
1714  feo->dh()->get_dof_indices(elem, dof_indices);
1715 
1716  for (unsigned int sbi=0; sbi<Model::n_substances(); ++sbi)
1717  {
1718  double old_average = 0;
1719  for (unsigned int j=0; j<n_dofs; ++j)
1720  old_average += ls[sbi]->get_solution_array()[dof_indices[j]-feo->dh()->distr()->begin()];
1721  old_average /= n_dofs;
1722 
1723  for (unsigned int j=0; j<n_dofs; ++j)
1724  ls[sbi]->get_solution_array()[dof_indices[j]-feo->dh()->distr()->begin()] += solution_elem_[sbi][i_cell] - old_average;
1725  }
1726  }
1727  }
1728  // update mass_vec for the case that mass matrix changes in next time step
1729  for (unsigned int sbi=0; sbi<Model::n_substances(); ++sbi)
1730  MatMult(*(ls_dt[sbi]->get_matrix()), ls[sbi]->get_solution(), mass_vec[sbi]);
1731 }
1732 
1733 template<class Model>
1735 {
1736  return Model::mesh_->get_row_4_el();
1737 }
1738 
1739 
1740 
1741 
1742 
1743 
1744 
1745 
1747 template class TransportDG<HeatTransferModel>;
1748 
1749 
1750 
1751 
TimeGovernor & time()
Definition: equation.hh:148
Input::Record input_rec
Record with input specification.
Class MappingP1 implements the affine transformation of the unit cell onto the actual cell...
Shape function values.
Definition: update_flags.hh:87
FieldSet * eq_data_
Definition: equation.hh:232
double JxW(const unsigned int point_no)
Return the product of Jacobian determinant and the quadrature weight at given quadrature point...
Definition: fe_values.hh:297
int IdxInt
Define integers that are indices into large arrays (elements, nodes, dofs etc.)
Definition: mesh.h:85
static auto subdomain(Mesh &mesh) -> IndexField
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.
vector< VectorSeqDouble > output_vec
Array for storing the output solution data.
FiniteElement< dim, 3 > * fe()
vector< double > mm_coef
Mass matrix coefficients.
Accessor to input data conforming to declared Array.
Definition: accessors.hh:567
void assemble_fluxes_element_side()
Assembles the fluxes between elements of different dimensions.
static constexpr Mask in_main_matrix
A field is part of main "stiffness matrix" of the equation.
Definition: field_flag.hh:49
Solver based on the original PETSc solver using MPIAIJ matrix and succesive Schur complement construc...
void calculate_concentration_matrix()
Transport with dispersion implemented using discontinuous Galerkin method.
Field< 3, FieldValue< 3 >::Integer > region_id
Class Input::Type::Default specifies default value of keys of a Input::Type::Record.
Definition: type_record.hh:61
FieldCommon & flags_add(FieldFlag::Flags::Mask mask)
double distance(const Node &n2) const
Definition: nodes.hh:86
void set_from_input(const Input::Record in_rec) override
virtual void start_add_assembly()
Definition: linsys.hh:321
void assemble_mass_matrix()
Assembles the mass matrix.
void output(TimeStep step)
Boundary * cond() const
Definition: side_impl.hh:70
virtual PetscErrorCode mat_zero_entries()
Definition: linsys.hh:263
virtual void rhs_set_values(int nrow, int *rows, double *vals)=0
void update_solution() override
Computes the solution in one time instant.
static Default obligatory()
The factory function to make an empty default value which is obligatory.
Definition: type_record.hh:110
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...
Definition: mesh.h:99
Fields computed from the mesh data.
virtual void start_allocation()
Definition: linsys.hh:313
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:157
int index() const
Definition: sys_vector.hh:78
virtual void finish_assembly()=0
int n_sides
Definition: edges.h:36
vector< vector< arma::vec3 > > ad_coef
Advection coefficients.
Definition: edges.h:26
bool valid() const
Definition: side_impl.hh:86
#define AVERAGE(i, k, side_id)
std::shared_ptr< DOFHandlerMultiDim > dh()
void assemble_stiffness_matrix()
Assembles the stiffness matrix.
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:489
const Vec & get_solution(unsigned int sbi)
FieldCommon & units(const UnitSI &units)
Set basic units of the field.
vector< double > ret_sources_prev
MultiField< 3, FieldValue< 3 >::Scalar > dg_penalty
Penalty enforcing inter-element continuity of solution (for each substance).
static const Input::Type::Selection & get_dg_variant_selection_input_type()
Input type for the DG variant selection.
Definition: transport_dg.cc:48
unsigned int n_points()
Returns the number of quadrature points.
Definition: fe_values.hh:358
Class for declaration of inputs sequences.
Definition: type_base.hh:345
EquationOutput output_fields
std::vector< Mat > mass_matrix
The mass matrix.
void calculate_cumulative_balance()
void assemble_fluxes_boundary()
Assembles the fluxes on the boundary.
static constexpr bool value
Definition: json.hpp:87
Symmetric Gauss-Legendre quadrature formulae on simplices.
#define WAVERAGE(i, k, side_id)
TransportDG(Mesh &init_mesh, const Input::Record in_rec)
Constructor.
vector< vector< double > > ret_coef
Retardation coefficient due to sorption.
unsigned int dim() 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.hh:329
#define OLD_ASSERT(...)
Definition: global_defs.h:131
void initialize() override
vector< vector< vector< arma::vec3 > > > ad_coef_edg
Advection coefficients on edges.
Transformed quadrature points.
IdxInt * get_row_4_el()
void output_data()
Postprocesses the solution and writes to output file.
virtual PetscErrorCode set_rhs(Vec &rhs)
Definition: linsys.hh:254
MultiField< 3, FieldValue< 3 >::Scalar > fracture_sigma
Transition parameter for diffusive transfer on fractures (for each substance).
void preallocate()
unsigned int dim() const
Definition: side_impl.hh:37
vector< vector< arma::mat33 > > dif_coef
Diffusion coefficients.
FEObjects * feo
Finite element objects.
static constexpr Mask in_time_term
A field is part of time term of the equation.
Definition: field_flag.hh:47
FieldCommon & input_default(const string &input_default)
const Vec & get_solution()
Definition: linsys.hh:281
Raviart-Thomas element of order 0.
Definition: fe_rt.hh:60
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:58
SideIter side()
Accessor to the data with type Type::Record.
Definition: accessors.hh:292
const Ret val(const string &key) const
static auto region_id(Mesh &mesh) -> IndexField
#define START_TIMER(tag)
Starts a timer with specified tag.
std::vector< Vec > rhs
Vector of right hand side.
Mesh * mesh_
Definition: equation.hh:223
Selection & add_value(const int value, const std::string &key, const std::string &description="", TypeBase::attribute_map attributes=TypeBase::attribute_map())
Adds one new value with name given by key to the Selection.
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:490
FiniteElement< dim, 3 > * fe_rt()
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:51
Shape function gradients.
Definition: update_flags.hh:95
FLOW123D_FORCE_LINK_IN_CHILD(concentrationTransportModel)
void set_solution(double *sol_array)
Definition: linsys.hh:289
ElementIter element()
double measure() const
Definition: sides.cc:29
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:169
double elem_anisotropy(const ElementFullIter &e)
Region region() const
Definition: accessors.hh:98
Normal vectors.
virtual PetscErrorCode rhs_zero_entries()
Definition: linsys.hh:272
void get_par_info(IdxInt *&el_4_loc, Distribution *&el_ds)
Discontinuous Galerkin method for equation of transport with dispersion.
double ** solution_elem_
Element averages of solution (the array is passed to reactions in operator splitting).
#define MPI_Comm_rank
Definition: mpi.h:236
std::vector< Mat > stiffness_matrix
The stiffness matrix.
FieldCommon & description(const string &description)
void set_values(int nrow, int *rows, int ncol, int *cols, PetscScalar *mat_vals, PetscScalar *rhs_vals)
Set values in the system matrix and values in the right-hand side vector on corresponding rows...
Definition: linsys.hh:366
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:75
std::vector< std::vector< double > > gamma
Penalty parameters.
static const Input::Type::Record & get_input_type()
Declare input record type for the equation TransportDG.
Definition: transport_dg.cc:74
void initialize(std::shared_ptr< OutputTime > stream, Mesh *mesh, Input::Record in_rec, const TimeGovernor &tg)
Discontinuous Galerkin method for equation of transport with dispersion.
const Selection & close() const
Close the Selection, no more values can be added.
ElementFullIter element() const
Definition: side_impl.hh:52
void update_after_reactions(bool solution_changed)
void reinit(ElementFullIter &cell, unsigned int sid)
Update cell-dependent data (gradients, Jacobians etc.)
Definition: fe_values.cc:301
MappingP1< dim, 3 > * mapping()
std::vector< Vec > ret_vec
Auxiliary vectors for calculation of sources in balance due to retardation (e.g. sorption).
double shape_value_component(const unsigned int function_no, const unsigned int point_no, const unsigned int comp) const
Return the value of the function_no-th shape function at the point_no-th quadrature point...
Definition: fe_values.cc:187
void reinit(ElementFullIter &cell)
Update cell-dependent data (gradients, Jacobians etc.)
Definition: fe_values.cc:233
vector< double > ret_sources
Temporary values of increments due to retardation (e.g. sorption)
bool set_time(const TimeStep &time, LimitSide limit_side)
Definition: field_set.cc:157
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:178
unsigned int bulk_idx() const
Returns index of the region in the bulk set.
Definition: region.hh:90
Definitions of particular quadrature rules on simplices.
#define WarningOut()
Macro defining &#39;warning&#39; record of log.
Definition: logger.hh:246
FieldCommon & name(const string &name)
Definition: field_common.hh:97
virtual SolveInfo solve()=0
#define END_TIMER(tag)
Ends a timer with specified tag.
Discontinuous Galerkin method for equation of transport with dispersion.
vector< arma::vec::fixed< spacedim > > & point_list()
Return coordinates of all quadrature points in the actual cell system.
Definition: fe_values.hh:318
Quadrature< dim > * q()
static const Input::Type::Record & get_input_type()
Definition: linsys_PETSC.cc:32
unsigned int el_idx() const
Definition: side_impl.hh:81
virtual PetscErrorCode set_matrix(Mat &matrix, MatStructure str)
Definition: linsys.hh:245
std::vector< Vec > mass_vec
Mass from previous time instant (necessary when coefficients of mass matrix change in time)...
Record type proxy class.
Definition: type_record.hh:182
void assemble_volume_integrals()
Assembles the volume integrals into the stiffness matrix.
virtual void mat_set_values(int nrow, int *rows, int ncol, int *cols, double *vals)=0
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:30
Base class for FEValues and FESideValues.
Definition: fe_values.hh:34
Field< 3, FieldValue< 3 >::Integer > subdomain
const Node * node(unsigned int i) const
Definition: side_impl.hh:46
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.
static UnitSI & dimensionless()
Returns dimensionless unit.
Definition: unit_si.cc:55
static bool print_message_table(ostream &stream, std::string equation_name)
Definition: field_common.cc:91
~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)
Calculates the dispersivity (diffusivity) tensor from the velocity field.
SideIter side(const unsigned int i) const
Definition: edges.h:31
LinSys ** ls_dt
Linear algebra system for the time derivative (actually it is used only for handling the matrix struc...
#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.
ElementAccessor< 3 > element_accessor()
Definition: boundaries.cc:47
void zero_time_step() override
Initialize solution in the zero time.
FEObjects(Mesh *mesh_, unsigned int fe_order)
Definition: field.hh:37
unsigned int n_nodes() const
Definition: side_impl.hh:33
Transformed quadrature weights.
Calculates finite element data on a side.
Definition: fe_values.hh:495