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