Flow123d  release_1.8.2-1603-g0109a2b
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 #include "fields/generic_field.hh"
35 #include "input/factory.hh"
36 
37 FLOW123D_FORCE_LINK_IN_CHILD(concentrationTransportModel);
39 
40 
41 
42 using namespace Input::Type;
43 
44 template<class Model>
46  return Selection("DG_variant", "Type of penalty term.")
47  .add_value(non_symmetric, "non-symmetric", "non-symmetric weighted interior penalty DG method")
48  .add_value(incomplete, "incomplete", "incomplete weighted interior penalty DG method")
49  .add_value(symmetric, "symmetric", "symmetric weighted interior penalty DG method")
50  .close();
51 }
52 
53 /*
54  * Should be removed
55 template<class Model>
56 const Selection & TransportDG<Model>::EqData::get_output_selection() {
57  return Model::ModelEqData::get_output_selection_input_type(
58  "DG",
59  "Implicit in time Discontinuous Galerkin solver")
60  .copy_values(EqData().make_output_field_selection("").close())
61  ConvectionTransport::EqData().output_fields
62  .make_output_field_selection(
63  "ConvectionTransport_output_fields",
64  "Selection of output fields for Convection Solute Transport model.")
65  .close()),
66  .close();
67 }
68 */
69 
70 template<class Model>
72  return Model::get_input_type("DG", "DG solver")
74  "Linear solver for MH problem.")
75  .declare_key("input_fields", Array(
77  .make_field_descriptor_type(std::string(Model::ModelEqData::name()) + "_DG")),
79  "Input fields of the equation.")
81  "Variant of interior penalty discontinuous Galerkin method.")
82  .declare_key("dg_order", Integer(0,3), Default("1"),
83  "Polynomial order for finite element in DG method (order 0 is suitable if there is no diffusion/dispersion).")
84 
85  .declare_key("output_fields",
86  Array(
87  // Get selection name and description from the model
88  Model::ModelEqData::get_output_selection()
89  // EqData contains both TransportDG and model specific fields.
90  .copy_values(
91  EqData().make_output_field_selection("DG_output_fields","Auxiliary Selection")
92  .close())
93  .close()),
94  Default(Model::ModelEqData::default_output_field()),
95  "List of fields to write to output file.")
96  .close();
97 }
98 
99 template<class Model>
101  Input::register_class< TransportDG<Model>, Mesh &, const Input::Record>(std::string(Model::ModelEqData::name()) + "_DG") +
103 
104 
105 
106 
107 FEObjects::FEObjects(Mesh *mesh_, unsigned int fe_order)
108 {
109  unsigned int q_order;
110 
111  switch (fe_order)
112  {
113  case 0:
114  q_order = 0;
115  fe1_ = new FE_P_disc<0,1,3>;
116  fe2_ = new FE_P_disc<0,2,3>;
117  fe3_ = new FE_P_disc<0,3,3>;
118  break;
119 
120  case 1:
121  q_order = 2;
122  fe1_ = new FE_P_disc<1,1,3>;
123  fe2_ = new FE_P_disc<1,2,3>;
124  fe3_ = new FE_P_disc<1,3,3>;
125  break;
126 
127  case 2:
128  q_order = 4;
129  fe1_ = new FE_P_disc<2,1,3>;
130  fe2_ = new FE_P_disc<2,2,3>;
131  fe3_ = new FE_P_disc<2,3,3>;
132  break;
133 
134  case 3:
135  q_order = 6;
136  fe1_ = new FE_P_disc<3,1,3>;
137  fe2_ = new FE_P_disc<3,2,3>;
138  fe3_ = new FE_P_disc<3,3,3>;
139  break;
140 
141  default:
142  q_order=0;
143  xprintf(PrgErr, "Unsupported polynomial order %d for finite elements in TransportDG ", fe_order);
144  break;
145  }
146 
147  fe_rt1_ = new FE_RT0<1,3>;
148  fe_rt2_ = new FE_RT0<2,3>;
149  fe_rt3_ = new FE_RT0<3,3>;
150 
151  q0_ = new QGauss<0>(q_order);
152  q1_ = new QGauss<1>(q_order);
153  q2_ = new QGauss<2>(q_order);
154  q3_ = new QGauss<3>(q_order);
155 
156  map0_ = new MappingP1<0,3>;
157  map1_ = new MappingP1<1,3>;
158  map2_ = new MappingP1<2,3>;
159  map3_ = new MappingP1<3,3>;
160 
161  dh_ = new DOFHandlerMultiDim(*mesh_);
162 
163  dh_->distribute_dofs(*fe1_, *fe2_, *fe3_);
164 }
165 
166 
168 {
169  delete fe1_;
170  delete fe2_;
171  delete fe3_;
172  delete fe_rt1_;
173  delete fe_rt2_;
174  delete fe_rt3_;
175  delete q0_;
176  delete q1_;
177  delete q2_;
178  delete q3_;
179  delete map0_;
180  delete map1_;
181  delete map2_;
182  delete map3_;
183  delete dh_;
184 }
185 
186 template<> FiniteElement<0,3> *FEObjects::fe<0>() { return 0; }
187 template<> FiniteElement<1,3> *FEObjects::fe<1>() { return fe1_; }
188 template<> FiniteElement<2,3> *FEObjects::fe<2>() { return fe2_; }
189 template<> FiniteElement<3,3> *FEObjects::fe<3>() { return fe3_; }
190 
191 template<> FiniteElement<0,3> *FEObjects::fe_rt<0>() { return 0; }
192 template<> FiniteElement<1,3> *FEObjects::fe_rt<1>() { return fe_rt1_; }
193 template<> FiniteElement<2,3> *FEObjects::fe_rt<2>() { return fe_rt2_; }
194 template<> FiniteElement<3,3> *FEObjects::fe_rt<3>() { return fe_rt3_; }
195 
196 template<> Quadrature<0> *FEObjects::q<0>() { return q0_; }
197 template<> Quadrature<1> *FEObjects::q<1>() { return q1_; }
198 template<> Quadrature<2> *FEObjects::q<2>() { return q2_; }
199 template<> Quadrature<3> *FEObjects::q<3>() { return q3_; }
200 
201 template<> Mapping<0,3> *FEObjects::mapping<0>() { return map0_; }
202 template<> Mapping<1,3> *FEObjects::mapping<1>() { return map1_; }
203 template<> Mapping<2,3> *FEObjects::mapping<2>() { return map2_; }
204 template<> Mapping<3,3> *FEObjects::mapping<3>() { return map3_; }
205 
207 
208 
209 template<class Model>
210 TransportDG<Model>::EqData::EqData() : Model::ModelEqData()
211 {
212  *this+=fracture_sigma
213  .name("fracture_sigma")
214  .description(
215  "Coefficient of diffusive transfer through fractures (for each substance).")
217  .input_default("1.0")
219 
220  *this+=dg_penalty
221  .name("dg_penalty")
222  .description(
223  "Penalty parameter influencing the discontinuity of the solution (for each substance). "
224  "Its default value 1 is sufficient in most cases. Higher value diminishes the inter-element jumps.")
226  .input_default("1.0")
228 
229  *this += region_id.name("region_id")
232 
233  // add all input fields to the output list
234 
235 }
236 
237 template<class Model>
239  : Model(init_mesh, in_rec),
240  mass_matrix(0),
241  input_rec(in_rec),
242  allocation_done(false)
243 {
244  // Can not use name() + "constructor" here, since START_TIMER only accepts const char *
245  // due to constexpr optimization.
246  START_TIMER(Model::ModelEqData::name());
247  // Check that Model is derived from AdvectionDiffusionModel.
248  static_assert(std::is_base_of<AdvectionDiffusionModel, Model>::value, "");
249 
250  this->eq_data_ = &data_;
251 
252 
253  // Set up physical parameters.
254  data_.set_mesh(init_mesh);
255  data_.region_id = GenericField<3>::region_id(*Model::mesh_);
256 
257 
258  // DG variant and order
259  dg_variant = in_rec.val<DGVariant>("dg_variant");
260  dg_order = in_rec.val<unsigned int>("dg_order");
261 
262  // create finite element structures and distribute DOFs
263  feo = new FEObjects(Model::mesh_, dg_order);
264  DBGMSG("TDG: solution size %d\n", feo->dh()->n_global_dofs());
265 
266 }
267 
268 
269 template<class Model>
271 {
272  data_.set_components(Model::substances_.names());
273  data_.set_input_list( input_rec.val<Input::Array>("input_fields") );
274 
275  // DG stabilization parameters on boundary edges
276  gamma.resize(Model::n_substances());
277  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
278  gamma[sbi].resize(Model::mesh_->boundary_.size());
279 
280  // Resize coefficient arrays
281  int qsize = max(feo->q<0>()->size(), max(feo->q<1>()->size(), max(feo->q<2>()->size(), feo->q<3>()->size())));
282  int max_edg_sides = max(Model::mesh_->max_edge_sides(1), max(Model::mesh_->max_edge_sides(2), Model::mesh_->max_edge_sides(3)));
283  mm_coef.resize(qsize);
284  ad_coef.resize(Model::n_substances());
285  dif_coef.resize(Model::n_substances());
286  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
287  {
288  ad_coef[sbi].resize(qsize);
289  dif_coef[sbi].resize(qsize);
290  }
291  ad_coef_edg.resize(max_edg_sides);
292  dif_coef_edg.resize(max_edg_sides);
293  for (int sd=0; sd<max_edg_sides; sd++)
294  {
295  ad_coef_edg[sd].resize(Model::n_substances());
296  dif_coef_edg[sd].resize(Model::n_substances());
297  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
298  {
299  ad_coef_edg[sd][sbi].resize(qsize);
300  dif_coef_edg[sd][sbi].resize(qsize);
301  }
302  }
303 
304  output_vec.resize(Model::n_substances());
305  //output_solution.resize(Model::n_substances());
306  int rank;
307  MPI_Comm_rank(PETSC_COMM_WORLD, &rank);
308  unsigned int output_vector_size= (rank==0)?feo->dh()->n_global_dofs():0;
309  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
310  {
311  // for each substance we allocate output array and vector
312  //output_solution[sbi] = new double[feo->dh()->n_global_dofs()];
313  VecCreateSeq(PETSC_COMM_SELF, output_vector_size, &output_vec[sbi]);
314  }
315  data_.output_field.set_components(Model::substances_.names());
316  data_.output_field.set_mesh(*Model::mesh_);
317  data_.output_type(OutputTime::CORNER_DATA);
318 
319  data_.output_field.setup_components();
320  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
321  {
322  // create shared pointer to a FieldFE, pass FE data and push this FieldFE to output_field on all regions
323  std::shared_ptr<FieldFE<3, FieldValue<3>::Scalar> > output_field_ptr(new FieldFE<3, FieldValue<3>::Scalar>);
324  output_field_ptr->set_fe_data(feo->dh(), feo->mapping<1>(), feo->mapping<2>(), feo->mapping<3>(), &output_vec[sbi]);
325  data_.output_field[sbi].set_field(Model::mesh_->region_db().get_region_set("ALL"), output_field_ptr, 0);
326  }
327 
328  // set time marks for writing the output
329  Model::output_stream_->add_admissible_field_names(input_rec.val<Input::Array>("output_fields"));
330  Model::output_stream_->mark_output_times(*Model::time_);
331 
332 
333  // allocate matrix and vector structures
334  ls = new LinSys*[Model::n_substances()];
335  ls_dt = new LinSys_PETSC(feo->dh()->distr());
337  solution_elem_ = new double*[Model::n_substances()];
338  for (unsigned int sbi = 0; sbi < Model::n_substances(); sbi++) {
339  ls[sbi] = new LinSys_PETSC(feo->dh()->distr());
340  ( (LinSys_PETSC *)ls[sbi] )->set_from_input( input_rec.val<Input::Record>("solver") );
341  ls[sbi]->set_solution(NULL);
342  solution_elem_[sbi] = new double[Model::mesh_->get_el_ds()->lsize()];
343  }
344  stiffness_matrix = new Mat[Model::n_substances()];
345  rhs = new Vec[Model::n_substances()];
346  mass_vec = new Vec[Model::n_substances()];
347 
348 
349  // initialization of balance object
350  if (Model::balance_ != nullptr)
351  {
352  Model::balance_->allocate(feo->dh()->distr()->lsize(),
353  max(feo->fe<1>()->n_dofs(), max(feo->fe<2>()->n_dofs(), feo->fe<3>()->n_dofs())));
354  }
355 
356 }
357 
358 
359 template<class Model>
361 {
362  delete Model::time_;
363  delete ls_dt;
364 
365  if (Model::mesh_->get_el_ds()->myp() == 0)
366  {
367  for (unsigned int i=0; i<Model::n_substances(); i++)
368  {
369  VecDestroy(&output_vec[i]);
370  //delete[] output_solution[i];
371  }
372  }
373 
374  for (unsigned int i=0; i<Model::n_substances(); i++)
375  {
376  delete ls[i];
377  delete[] solution_elem_[i];
378  MatDestroy(&stiffness_matrix[i]);
379  VecDestroy(&rhs[i]);
380  VecDestroy(&mass_vec[i]);
381  }
382  delete[] ls;
383  delete[] solution_elem_;
384  delete[] stiffness_matrix;
385  delete[] rhs;
386  delete[] mass_vec;
387  delete feo;
388 
389  gamma.clear();
390 }
391 
392 
393 template<class Model>
395 {
396  IS is;
397  VecScatter output_scatter;
398  int idx[] = { 0 };
399  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
400  {
401  // gather solution to output_vec[sbi]
402  //ISCreateBlock(PETSC_COMM_SELF, ls[sbi]->size(),1 , idx, PETSC_COPY_VALUES, &is);
403  //VecScatterCreate(ls[sbi]->get_solution(), is, output_vec[sbi], PETSC_NULL, &output_scatter);
404  VecScatterCreateToZero(ls[sbi]->get_solution(), &output_scatter, PETSC_NULL);
405  VecScatterBegin(output_scatter, ls[sbi]->get_solution(), output_vec[sbi], INSERT_VALUES, SCATTER_FORWARD);
406  VecScatterEnd(output_scatter, ls[sbi]->get_solution(), output_vec[sbi], INSERT_VALUES, SCATTER_FORWARD);
407  VecScatterDestroy(&(output_scatter));
408  //ISDestroy(&(is));
409  }
410 }
411 
412 
413 
414 template<class Model>
416 {
417  START_TIMER(Model::ModelEqData::name());
418  data_.mark_input_times( *(Model::time_) );
419  data_.set_time(Model::time_->step(), LimitSide::left);
420 
421 
422  // set initial conditions
424  for (unsigned int sbi = 0; sbi < Model::n_substances(); sbi++)
425  ( (LinSys_PETSC *)ls[sbi] )->set_initial_guess_nonzero();
426 
427  // check first time assembly - needs preallocation
429 
430  // after preallocation we assemble the matrices and vectors required for mass balance
431  if (Model::balance_ != nullptr)
432  {
433  for (unsigned int sbi=0; sbi<Model::n_substances(); ++sbi)
434  {
435  Model::balance_->calculate_mass(Model::subst_idx[sbi], ls[sbi]->get_solution());
436  Model::balance_->calculate_source(Model::subst_idx[sbi], ls[sbi]->get_solution());
437  Model::balance_->calculate_flux(Model::subst_idx[sbi], ls[sbi]->get_solution());
438  }
439  }
440 
441  output_data();
442 }
443 
444 
445 template<class Model>
447 {
448  // preallocate mass matrix
451  mass_matrix = NULL;
452 
453  // preallocate system matrix
454  for (unsigned int i=0; i<Model::n_substances(); i++)
455  {
456  ls[i]->start_allocation();
457  stiffness_matrix[i] = NULL;
458  rhs[i] = NULL;
459  }
461  set_sources();
463 
464  allocation_done = true;
465 }
466 
467 
468 
469 template<class Model>
471 {
472  START_TIMER("DG-ONE STEP");
473 
474  Model::time_->next_time();
475  Model::time_->view("TDG");
476 
477  START_TIMER("data reinit");
478  data_.set_time(Model::time_->step(), LimitSide::left);
479  END_TIMER("data reinit");
480 
481  // assemble mass matrix
482  if (mass_matrix == NULL || data_.subset(FieldFlag::in_time_term).changed() )
483  {
488 
489  // construct mass_vec for initial time
490  if (mass_matrix == NULL)
491  {
492  for (unsigned int i=0; i<Model::n_substances(); i++)
493  {
494  VecDuplicate(ls[i]->get_solution(), &mass_vec[i]);
495  MatMult(*(ls_dt->get_matrix()), ls[i]->get_solution(), mass_vec[i]);
496  }
497  }
498 
499  mass_matrix = *(ls_dt->get_matrix());
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, 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->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  unsigned int 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  START_TIMER("DG-OUTPUT");
640 
641  // gather the solution from all processors
643  data_.subset(FieldFlag::allow_output).set_time( Model::time_->step(), LimitSide::left);
644  data_.output(Model::output_stream_);
645 
646  Model::output_data();
647 
648  END_TIMER("DG-OUTPUT");
649 }
650 
651 
652 template<class Model>
654 {
655  if (Model::balance_ != nullptr && Model::balance_->cumulative())
656  {
657  for (unsigned int sbi=0; sbi<Model::n_substances(); ++sbi)
658  {
659  Model::balance_->calculate_cumulative_sources(Model::subst_idx[sbi], ls[sbi]->get_solution(), Model::time_->dt());
660  Model::balance_->calculate_cumulative_fluxes(Model::subst_idx[sbi], ls[sbi]->get_solution(), Model::time_->dt());
661  }
662  }
663 }
664 
665 
666 template<class Model>
668 {
669  if (Model::balance_ != nullptr)
670  {
671  for (unsigned int sbi=0; sbi<Model::n_substances(); ++sbi)
672  {
673  Model::balance_->calculate_mass(Model::subst_idx[sbi], ls[sbi]->get_solution());
674  Model::balance_->calculate_source(Model::subst_idx[sbi], ls[sbi]->get_solution());
675  Model::balance_->calculate_flux(Model::subst_idx[sbi], ls[sbi]->get_solution());
676  }
677  }
678 }
679 
680 
681 template<class Model>
683 {
684  START_TIMER("assemble_mass");
685  if (Model::balance_ != nullptr)
686  Model::balance_->start_mass_assembly(Model::subst_idx);
687  assemble_mass_matrix<1>();
688  assemble_mass_matrix<2>();
689  assemble_mass_matrix<3>();
690  if (Model::balance_ != nullptr)
691  Model::balance_->finish_mass_assembly(Model::subst_idx);
692  END_TIMER("assemble_mass");
693 }
694 
695 
696 template<class Model> template<unsigned int dim>
698 {
699  FEValues<dim,3> fe_values(*feo->mapping<dim>(), *feo->q<dim>(), *feo->fe<dim>(), update_values | update_JxW_values | update_quadrature_points);
700  const unsigned int ndofs = feo->fe<dim>()->n_dofs(), qsize = feo->q<dim>()->size();
701  vector<int> dof_indices(ndofs);
702  PetscScalar local_mass_matrix[ndofs*ndofs];
703  vector<PetscScalar> local_mass_balance_vector(ndofs);
704 
705  // assemble integral over elements
706  for (unsigned int i_cell=0; i_cell<Model::mesh_->get_el_ds()->lsize(); i_cell++)
707  {
708  typename DOFHandlerBase::CellIterator cell = Model::mesh_->element(feo->dh()->el_index(i_cell));
709  if (cell->dim() != dim) continue;
710 
711  fe_values.reinit(cell);
712  feo->dh()->get_dof_indices(cell, (unsigned int*)&(dof_indices[0]));
713  ElementAccessor<3> ele_acc = cell->element_accessor();
714 
715  Model::compute_mass_matrix_coefficient(fe_values.point_list(), ele_acc, mm_coef);
716 
717  // assemble the local mass matrix
718  for (unsigned int i=0; i<ndofs; i++)
719  {
720  for (unsigned int j=0; j<ndofs; j++)
721  {
722  local_mass_matrix[i*ndofs+j] = 0;
723  for (unsigned int k=0; k<qsize; k++)
724  local_mass_matrix[i*ndofs+j] += mm_coef[k]*fe_values.shape_value(j,k)*fe_values.shape_value(i,k)*fe_values.JxW(k);
725  }
726  }
727 
728  if (Model::balance_ != nullptr)
729  {
730  for (unsigned int i=0; i<ndofs; i++)
731  {
732  local_mass_balance_vector[i] = 0;
733  for (unsigned int k=0; k<qsize; k++)
734  local_mass_balance_vector[i] += mm_coef[k]*fe_values.shape_value(i,k)*fe_values.JxW(k);
735  }
736 
737  for (unsigned int sbi=0; sbi<Model::n_substances(); ++sbi)
738  Model::balance_->add_mass_matrix_values(Model::subst_idx[sbi], ele_acc.region().bulk_idx(), dof_indices, local_mass_balance_vector);
739  }
740 
741  ls_dt->mat_set_values(ndofs, &(dof_indices[0]), ndofs, &(dof_indices[0]), local_mass_matrix);
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  unsigned int dof_indices[ndofs];
790  vector<arma::vec3> velocity(qsize);
791  vector<arma::vec> sources_sigma(qsize, arma::vec(Model::n_substances()));
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[k][sbi]*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, (int *)dof_indices, ndofs, (int *)dof_indices, local_matrix);
830  }
831  }
832 }
833 
834 
835 template<class Model>
837 {
838  START_TIMER("assemble_sources");
839  if (Model::balance_ != nullptr)
840  Model::balance_->start_source_assembly(Model::subst_idx);
841  set_sources<1>();
842  set_sources<2>();
843  set_sources<3>();
844  if (Model::balance_ != nullptr)
845  Model::balance_->finish_source_assembly(Model::subst_idx);
846  END_TIMER("assemble_sources");
847 }
848 
849 template<class Model>
850 template<unsigned int dim>
852 {
853  FEValues<dim,3> fe_values(*feo->mapping<dim>(), *feo->q<dim>(), *feo->fe<dim>(),
855  const unsigned int ndofs = feo->fe<dim>()->n_dofs(), qsize = feo->q<dim>()->size();
856  vector<arma::vec> sources_conc(qsize, arma::vec(Model::n_substances())),
857  sources_density(qsize, arma::vec(Model::n_substances())),
858  sources_sigma(qsize, arma::vec(Model::n_substances()));
859  vector<int> dof_indices(ndofs);
860  PetscScalar local_rhs[ndofs];
861  vector<PetscScalar> local_source_balance_vector(ndofs), local_source_balance_rhs(ndofs);
862  double source;
863 
864  // assemble integral over elements
865  for (unsigned int i_cell=0; i_cell<Model::mesh_->get_el_ds()->lsize(); i_cell++)
866  {
867  typename DOFHandlerBase::CellIterator cell = Model::mesh_->element(feo->dh()->el_index(i_cell));
868  if (cell->dim() != dim) continue;
869 
870  fe_values.reinit(cell);
871  feo->dh()->get_dof_indices(cell, (unsigned int *)&(dof_indices[0]));
872 
873  Model::compute_source_coefficients(fe_values.point_list(), cell->element_accessor(), sources_conc, sources_density, sources_sigma);
874 
875  // assemble the local stiffness matrix
876  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
877  {
878  fill_n(local_rhs, ndofs, 0);
879  local_source_balance_vector.assign(ndofs, 0);
880  local_source_balance_rhs.assign(ndofs, 0);
881 
882  // compute sources
883  for (unsigned int k=0; k<qsize; k++)
884  {
885  source = (sources_density[k][sbi] + sources_conc[k][sbi]*sources_sigma[k][sbi])*fe_values.JxW(k);
886 
887  for (unsigned int i=0; i<ndofs; i++)
888  local_rhs[i] += source*fe_values.shape_value(i,k);
889  }
890  ls[sbi]->rhs_set_values(ndofs, &(dof_indices[0]), local_rhs);
891 
892  if (Model::balance_ != nullptr)
893  {
894  for (unsigned int i=0; i<ndofs; i++)
895  {
896  for (unsigned int k=0; k<qsize; k++)
897  local_source_balance_vector[i] -= sources_sigma[k][sbi]*fe_values.shape_value(i,k)*fe_values.JxW(k);
898 
899  local_source_balance_rhs[i] += local_rhs[i];
900  }
901  Model::balance_->add_source_matrix_values(Model::subst_idx[sbi], cell->region().bulk_idx(), dof_indices, local_source_balance_vector);
902  Model::balance_->add_source_rhs_values(Model::subst_idx[sbi], cell->region().bulk_idx(), dof_indices, local_source_balance_rhs);
903  }
904  }
905  }
906 }
907 
908 
909 
910 template<class Model>
911 template<unsigned int dim>
913 {
914  vector<FESideValues<dim,3>*> fe_values;
915  FESideValues<dim,3> fsv_rt(*feo->mapping<dim>(), *feo->q<dim-1>(), *feo->fe_rt<dim>(),
916  update_values);
917  const unsigned int ndofs = feo->fe<dim>()->n_dofs(), qsize = feo->q<dim-1>()->size(),
918  n_max_sides = ad_coef_edg.size();
919  vector<unsigned int*> side_dof_indices;
920  PetscScalar local_matrix[ndofs*ndofs];
921  vector<vector<arma::vec3> > side_velocity(n_max_sides);
922  vector<arma::vec> dg_penalty(n_max_sides);
923  double gamma_l, omega[2], transport_flux;
924 
925  for (unsigned int sid=0; sid<n_max_sides; sid++)
926  {
927  side_dof_indices.push_back(new unsigned int[ndofs]);
928  fe_values.push_back(new FESideValues<dim,3>(*feo->mapping<dim>(), *feo->q<dim-1>(), *feo->fe<dim>(),
930  }
931 
932  // assemble integral over sides
933  for (unsigned int iedg=0; iedg<feo->dh()->n_loc_edges(); iedg++)
934  {
935  Edge *edg = &Model::mesh_->edges[feo->dh()->edge_index(iedg)];
936  if (edg->n_sides < 2 || edg->side(0)->element()->dim() != dim) continue;
937 
938  for (int sid=0; sid<edg->n_sides; sid++)
939  {
940  typename DOFHandlerBase::CellIterator cell = edg->side(sid)->element();
941  ElementAccessor<3> ele_acc = cell->element_accessor();
942  feo->dh()->get_dof_indices(cell, side_dof_indices[sid]);
943  fe_values[sid]->reinit(cell, edg->side(sid)->el_idx());
944  fsv_rt.reinit(cell, edg->side(sid)->el_idx());
945  calculate_velocity(cell, side_velocity[sid], fsv_rt);
946  Model::compute_advection_diffusion_coefficients(fe_values[sid]->point_list(), side_velocity[sid], ele_acc, ad_coef_edg[sid], dif_coef_edg[sid]);
947  dg_penalty[sid] = data_.dg_penalty.value(cell->centre(), ele_acc);
948  }
949 
950  // fluxes and penalty
951  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
952  {
953  vector<double> fluxes(edg->n_sides);
954  for (int sid=0; sid<edg->n_sides; sid++)
955  {
956  fluxes[sid] = 0;
957  for (unsigned int k=0; k<qsize; k++)
958  fluxes[sid] += arma::dot(ad_coef_edg[sid][sbi][k], fe_values[sid]->normal_vector(k))*fe_values[sid]->JxW(k);
959  fluxes[sid] /= edg->side(sid)->measure();
960  }
961 
962  for (int s1=0; s1<edg->n_sides; s1++)
963  {
964  for (int s2=s1+1; s2<edg->n_sides; s2++)
965  {
966  OLD_ASSERT(edg->side(s1)->valid(), "Invalid side of edge.");
967  if (!feo->dh()->el_is_local(edg->side(s1)->element().index())
968  && !feo->dh()->el_is_local(edg->side(s2)->element().index())) continue;
969 
970  arma::vec3 nv = fe_values[s1]->normal_vector(0);
971 
972  // set up the parameters for DG method
973  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);
974 
975  int sd[2];
976  sd[0] = s1;
977  sd[1] = s2;
978 
979 #define AVERAGE(i,k,side_id) (fe_values[sd[side_id]]->shape_value(i,k)*0.5)
980 #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])
981 #define JUMP(i,k,side_id) ((side_id==0?1:-1)*fe_values[sd[side_id]]->shape_value(i,k))
982 
983  // For selected pair of elements:
984  for (int n=0; n<2; n++)
985  {
986  if (!feo->dh()->el_is_local(edg->side(sd[n])->element().index())) continue;
987 
988  for (int m=0; m<2; m++)
989  {
990  for (unsigned int i=0; i<fe_values[sd[n]]->n_dofs(); i++)
991  for (unsigned int j=0; j<fe_values[sd[m]]->n_dofs(); j++)
992  local_matrix[i*fe_values[sd[m]]->n_dofs()+j] = 0;
993 
994  for (unsigned int k=0; k<qsize; k++)
995  {
996  double flux_times_JxW = transport_flux*fe_values[0]->JxW(k);
997  double gamma_times_JxW = gamma_l*fe_values[0]->JxW(k);
998 
999  for (unsigned int i=0; i<fe_values[sd[n]]->n_dofs(); i++)
1000  {
1001  double flux_JxW_jump_i = flux_times_JxW*JUMP(i,k,n);
1002  double gamma_JxW_jump_i = gamma_times_JxW*JUMP(i,k,n);
1003  double JxW_jump_i = fe_values[0]->JxW(k)*JUMP(i,k,n);
1004  double JxW_var_wavg_i = fe_values[0]->JxW(k)*WAVERAGE(i,k,n)*dg_variant;
1005 
1006  for (unsigned int j=0; j<fe_values[sd[m]]->n_dofs(); j++)
1007  {
1008  int index = i*fe_values[sd[m]]->n_dofs()+j;
1009 
1010  // flux due to transport (applied on interior edges) (average times jump)
1011  local_matrix[index] += flux_JxW_jump_i*AVERAGE(j,k,m);
1012 
1013  // penalty enforcing continuity across edges (applied on interior and Dirichlet edges) (jump times jump)
1014  local_matrix[index] += gamma_JxW_jump_i*JUMP(j,k,m);
1015 
1016  // terms due to diffusion
1017  local_matrix[index] -= WAVERAGE(j,k,m)*JxW_jump_i;
1018  local_matrix[index] -= JUMP(j,k,m)*JxW_var_wavg_i;
1019  }
1020  }
1021  }
1022  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);
1023  }
1024  }
1025 #undef AVERAGE
1026 #undef WAVERAGE
1027 #undef JUMP
1028  }
1029  }
1030  }
1031  }
1032 
1033  for (unsigned int i=0; i<n_max_sides; i++)
1034  {
1035  delete fe_values[i];
1036  delete[] side_dof_indices[i];
1037  }
1038 }
1039 
1040 
1041 template<class Model>
1042 template<unsigned int dim>
1044 {
1045  FESideValues<dim,3> fe_values_side(*feo->mapping<dim>(), *feo->q<dim-1>(), *feo->fe<dim>(),
1047  FESideValues<dim,3> fsv_rt(*feo->mapping<dim>(), *feo->q<dim-1>(), *feo->fe_rt<dim>(),
1048  update_values);
1049  const unsigned int ndofs = feo->fe<dim>()->n_dofs(), qsize = feo->q<dim-1>()->size();
1050  unsigned int side_dof_indices[ndofs];
1051  PetscScalar local_matrix[ndofs*ndofs];
1052  vector<arma::vec3> side_velocity;
1053  vector<double> robin_sigma(qsize);
1054  vector<double> csection(qsize);
1055  arma::vec dg_penalty;
1056  double gamma_l;
1057 
1058  // assemble boundary integral
1059  for (unsigned int iedg=0; iedg<feo->dh()->n_loc_edges(); iedg++)
1060  {
1061  Edge *edg = &Model::mesh_->edges[feo->dh()->edge_index(iedg)];
1062  if (edg->n_sides > 1) continue;
1063  // check spatial dimension
1064  if (edg->side(0)->dim() != dim-1) continue;
1065  // skip edges lying not on the boundary
1066  if (edg->side(0)->cond() == NULL) continue;
1067 
1068  SideIter side = edg->side(0);
1069  typename DOFHandlerBase::CellIterator cell = side->element();
1070  ElementAccessor<3> ele_acc = cell->element_accessor();
1071  feo->dh()->get_dof_indices(cell, side_dof_indices);
1072  fe_values_side.reinit(cell, side->el_idx());
1073  fsv_rt.reinit(cell, side->el_idx());
1074 
1075  calculate_velocity(cell, side_velocity, fsv_rt);
1076  Model::compute_advection_diffusion_coefficients(fe_values_side.point_list(), side_velocity, ele_acc, ad_coef, dif_coef);
1077  dg_penalty = data_.dg_penalty.value(cell->centre(), ele_acc);
1078  arma::uvec bc_type;
1079  Model::get_bc_type(side->cond()->element_accessor(), bc_type);
1080  data_.cross_section.value_list(fe_values_side.point_list(), ele_acc, csection);
1081 
1082  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
1083  {
1084  for (unsigned int i=0; i<ndofs; i++)
1085  for (unsigned int j=0; j<ndofs; j++)
1086  local_matrix[i*ndofs+j] = 0;
1087 
1088  // On Neumann boundaries we have only term from integrating by parts the advective term,
1089  // on Dirichlet boundaries we additionally apply the penalty which enforces the prescribed value.
1090  double side_flux = 0;
1091  for (unsigned int k=0; k<qsize; k++)
1092  side_flux += arma::dot(ad_coef[sbi][k], fe_values_side.normal_vector(k))*fe_values_side.JxW(k);
1093  double transport_flux = side_flux/side->measure();
1094 
1095  if (bc_type[sbi] == AdvectionDiffusionModel::abc_dirichlet)
1096  {
1097  // set up the parameters for DG method
1098  set_DG_parameters_boundary(side, qsize, dif_coef[sbi], transport_flux, fe_values_side.normal_vector(0), dg_penalty[sbi], gamma_l);
1099  gamma[sbi][side->cond_idx()] = gamma_l;
1100  transport_flux += gamma_l;
1101  }
1102 
1103  // fluxes and penalty
1104  for (unsigned int k=0; k<qsize; k++)
1105  {
1106  double flux_times_JxW;
1107  if (bc_type[sbi] == AdvectionDiffusionModel::abc_total_flux)
1108  {
1109  Model::get_flux_bc_sigma(sbi, fe_values_side.point_list(), side->cond()->element_accessor(), robin_sigma);
1110  flux_times_JxW = csection[k]*robin_sigma[k]*fe_values_side.JxW(k);
1111  }
1112  else if (bc_type[sbi] == AdvectionDiffusionModel::abc_diffusive_flux)
1113  {
1114  Model::get_flux_bc_sigma(sbi, fe_values_side.point_list(), side->cond()->element_accessor(), robin_sigma);
1115  flux_times_JxW = (transport_flux + csection[k]*robin_sigma[k])*fe_values_side.JxW(k);
1116  }
1117  else if (bc_type[sbi] == AdvectionDiffusionModel::abc_inflow && side_flux < 0)
1118  flux_times_JxW = 0;
1119  else
1120  flux_times_JxW = transport_flux*fe_values_side.JxW(k);
1121 
1122  for (unsigned int i=0; i<ndofs; i++)
1123  {
1124  for (unsigned int j=0; j<ndofs; j++)
1125  {
1126  // flux due to advection and penalty
1127  local_matrix[i*ndofs+j] += flux_times_JxW*fe_values_side.shape_value(i,k)*fe_values_side.shape_value(j,k);
1128 
1129  // flux due to diffusion (only on dirichlet and inflow boundary)
1130  if (bc_type[sbi] == AdvectionDiffusionModel::abc_dirichlet)
1131  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)
1132  + 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
1133  )*fe_values_side.JxW(k);
1134  }
1135  }
1136  }
1137 
1138  ls[sbi]->mat_set_values(ndofs, (int *)side_dof_indices, ndofs, (int *)side_dof_indices, local_matrix);
1139  }
1140  }
1141 }
1142 
1143 
1144 template<class Model>
1145 template<unsigned int dim>
1147 {
1148 
1149  if (dim == 1) return;
1150  FEValues<dim-1,3> fe_values_vb(*feo->mapping<dim-1>(), *feo->q<dim-1>(), *feo->fe<dim-1>(),
1152  FESideValues<dim,3> fe_values_side(*feo->mapping<dim>(), *feo->q<dim-1>(), *feo->fe<dim>(),
1154  FESideValues<dim,3> fsv_rt(*feo->mapping<dim>(), *feo->q<dim-1>(), *feo->fe_rt<dim>(),
1155  update_values);
1156  FEValues<dim-1,3> fv_rt(*feo->mapping<dim-1>(), *feo->q<dim-1>(), *feo->fe_rt<dim-1>(),
1157  update_values);
1158 
1159  vector<FEValuesSpaceBase<3>*> fv_sb(2);
1160  const unsigned int ndofs = feo->fe<dim>()->n_dofs(); // number of local dofs
1161  const unsigned int qsize = feo->q<dim-1>()->size(); // number of quadrature points
1162  unsigned int side_dof_indices[2*ndofs], n_dofs[2];
1163  vector<arma::vec3> velocity_higher, velocity_lower;
1164  vector<arma::vec> frac_sigma(qsize, arma::vec(Model::n_substances()));
1165  vector<double> csection_lower(qsize), csection_higher(qsize), mm_coef_lower(qsize), mm_coef_higher(qsize);
1166  PetscScalar local_matrix[4*ndofs*ndofs];
1167  double comm_flux[2][2];
1168 
1169  // index 0 = element with lower dimension,
1170  // index 1 = side of element with higher dimension
1171  fv_sb[0] = &fe_values_vb;
1172  fv_sb[1] = &fe_values_side;
1173 
1174  // assemble integral over sides
1175  for (unsigned int inb=0; inb<feo->dh()->n_loc_nb(); inb++)
1176  {
1177  Neighbour *nb = &Model::mesh_->vb_neighbours_[feo->dh()->nb_index(inb)];
1178  // skip neighbours of different dimension
1179  if (nb->element()->dim() != dim-1) continue;
1180 
1181  typename DOFHandlerBase::CellIterator cell_sub = Model::mesh_->element.full_iter(nb->element());
1182  feo->dh()->get_dof_indices(cell_sub, side_dof_indices);
1183  fe_values_vb.reinit(cell_sub);
1184  n_dofs[0] = fv_sb[0]->n_dofs();
1185 
1186  typename DOFHandlerBase::CellIterator cell = nb->side()->element();
1187  feo->dh()->get_dof_indices(cell, side_dof_indices+n_dofs[0]);
1188  fe_values_side.reinit(cell, nb->side()->el_idx());
1189  n_dofs[1] = fv_sb[1]->n_dofs();
1190 
1191  // Element id's for testing if they belong to local partition.
1192  int element_id[2];
1193  element_id[0] = cell_sub.index();
1194  element_id[1] = cell.index();
1195 
1196  fsv_rt.reinit(cell, nb->side()->el_idx());
1197  fv_rt.reinit(cell_sub);
1198  calculate_velocity(cell, velocity_higher, fsv_rt);
1199  calculate_velocity(cell_sub, velocity_lower, fv_rt);
1200  Model::compute_advection_diffusion_coefficients(fe_values_vb.point_list(), velocity_lower, cell_sub->element_accessor(), ad_coef_edg[0], dif_coef_edg[0]);
1201  Model::compute_advection_diffusion_coefficients(fe_values_vb.point_list(), velocity_higher, cell->element_accessor(), ad_coef_edg[1], dif_coef_edg[1]);
1202  Model::compute_mass_matrix_coefficient(fe_values_vb.point_list(), cell_sub->element_accessor(), mm_coef_lower);
1203  Model::compute_mass_matrix_coefficient(fe_values_vb.point_list(), cell->element_accessor(), mm_coef_higher);
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  data_.fracture_sigma.value_list(fe_values_vb.point_list(), cell_sub->element_accessor(), frac_sigma);
1207 
1208  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
1209  {
1210  for (unsigned int i=0; i<n_dofs[0]+n_dofs[1]; i++)
1211  for (unsigned int j=0; j<n_dofs[0]+n_dofs[1]; j++)
1212  local_matrix[i*(n_dofs[0]+n_dofs[1])+j] = 0;
1213 
1214  // set transmission conditions
1215  for (unsigned int k=0; k<qsize; k++)
1216  {
1217  /* The communication flux has two parts:
1218  * - "diffusive" term containing sigma
1219  * - "advective" term representing usual upwind
1220  *
1221  * The calculation differs from the reference manual, since ad_coef and dif_coef have different meaning
1222  * than b and A in the manual.
1223  * In calculation of sigma there appears one more csection_lower in the denominator.
1224  */
1225  double sigma = frac_sigma[k][sbi]*arma::dot(dif_coef_edg[0][sbi][k]*fe_values_side.normal_vector(k),fe_values_side.normal_vector(k))*
1226  2*csection_higher[k]*csection_higher[k]/(csection_lower[k]*csection_lower[k]);
1227 
1228  // Since mm_coef_* contains cross section, we have to divide by it.
1229  double transport_flux = arma::dot(ad_coef_edg[1][sbi][k], fe_values_side.normal_vector(k));
1230  double por_lower_over_higher = mm_coef_lower[k]*csection_higher[k]/(mm_coef_higher[k]*csection_lower[k]);
1231 
1232  comm_flux[0][0] = (sigma-min(0.,transport_flux*por_lower_over_higher))*fv_sb[0]->JxW(k);
1233  comm_flux[0][1] = -(sigma-min(0.,transport_flux*por_lower_over_higher))*fv_sb[0]->JxW(k);
1234  comm_flux[1][0] = -(sigma+max(0.,transport_flux))*fv_sb[0]->JxW(k);
1235  comm_flux[1][1] = (sigma+max(0.,transport_flux))*fv_sb[0]->JxW(k);
1236 
1237  for (int n=0; n<2; n++)
1238  {
1239  if (!feo->dh()->el_is_local(element_id[n])) continue;
1240 
1241  for (unsigned int i=0; i<n_dofs[n]; i++)
1242  for (int m=0; m<2; m++)
1243  for (unsigned int j=0; j<n_dofs[m]; j++)
1244  local_matrix[(i+n*n_dofs[0])*(n_dofs[0]+n_dofs[1]) + m*n_dofs[0] + j] +=
1245  comm_flux[m][n]*fv_sb[m]->shape_value(j,k)*fv_sb[n]->shape_value(i,k);
1246  }
1247  }
1248  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);
1249  }
1250  }
1251 
1252 }
1253 
1254 
1255 
1256 
1257 
1258 
1259 template<class Model>
1261 {
1262  START_TIMER("assemble_bc");
1263  if (Model::balance_ != nullptr)
1264  Model::balance_->start_flux_assembly(Model::subst_idx);
1265  set_boundary_conditions<1>();
1266  set_boundary_conditions<2>();
1267  set_boundary_conditions<3>();
1268  if (Model::balance_ != nullptr)
1269  Model::balance_->finish_flux_assembly(Model::subst_idx);
1270  END_TIMER("assemble_bc");
1271 }
1272 
1273 
1274 template<class Model>
1275 template<unsigned int dim>
1277 {
1278  FESideValues<dim,3> fe_values_side(*feo->mapping<dim>(), *feo->q<dim-1>(), *feo->fe<dim>(),
1280  FESideValues<dim,3> fsv_rt(*feo->mapping<dim>(), *feo->q<dim-1>(), *feo->fe_rt<dim>(),
1281  update_values);
1282  const unsigned int ndofs = feo->fe<dim>()->n_dofs(), qsize = feo->q<dim-1>()->size();
1283  vector<int> side_dof_indices(ndofs);
1284  unsigned int loc_b=0;
1285  double local_rhs[ndofs];
1286  vector<PetscScalar> local_flux_balance_vector(ndofs);
1287  PetscScalar local_flux_balance_rhs;
1288  vector<arma::vec> bc_values(qsize, arma::vec(Model::n_substances()));
1289  vector<double> bc_fluxes(qsize),
1290  bc_sigma(qsize),
1291  bc_ref_values(qsize);
1292  vector<double> csection(qsize);
1293  vector<arma::vec3> velocity;
1294 
1295  for (unsigned int loc_el = 0; loc_el < Model::mesh_->get_el_ds()->lsize(); loc_el++)
1296  {
1297  ElementFullIter elm = Model::mesh_->element(feo->dh()->el_index(loc_el));
1298  if (elm->boundary_idx_ == nullptr) continue;
1299 
1300  FOR_ELEMENT_SIDES(elm,si)
1301  {
1302  Edge *edg = elm->side(si)->edge();
1303  if (edg->n_sides > 1) continue;
1304  // skip edges lying not on the boundary
1305  if (edg->side(0)->cond() == NULL) continue;
1306 
1307  if (edg->side(0)->dim() != dim-1)
1308  {
1309  if (edg->side(0)->cond() != nullptr) ++loc_b;
1310  continue;
1311  }
1312 
1313  SideIter side = edg->side(0);
1314  typename DOFHandlerBase::CellIterator cell = Model::mesh_->element.full_iter(side->element());
1315  ElementAccessor<3> ele_acc = side->cond()->element_accessor();
1316 
1317  arma::uvec bc_type;
1318  Model::get_bc_type(ele_acc, bc_type);
1319 
1320  fe_values_side.reinit(cell, side->el_idx());
1321  fsv_rt.reinit(cell, side->el_idx());
1322  calculate_velocity(cell, velocity, fsv_rt);
1323 
1324  Model::compute_advection_diffusion_coefficients(fe_values_side.point_list(), velocity, side->element()->element_accessor(), ad_coef, dif_coef);
1325  data_.cross_section.value_list(fe_values_side.point_list(), side->element()->element_accessor(), csection);
1326  // The b.c. data are fetched for all possible b.c. types since we allow
1327  // different bc_type for each substance.
1328  data_.bc_dirichlet_value.value_list(fe_values_side.point_list(), ele_acc, bc_values);
1329 
1330  feo->dh()->get_dof_indices(cell, (unsigned int *)&(side_dof_indices[0]));
1331 
1332  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
1333  {
1334  fill_n(local_rhs, ndofs, 0);
1335  local_flux_balance_vector.assign(ndofs, 0);
1336  local_flux_balance_rhs = 0;
1337 
1338  double side_flux = 0;
1339  for (unsigned int k=0; k<qsize; k++)
1340  side_flux += arma::dot(ad_coef[sbi][k], fe_values_side.normal_vector(k))*fe_values_side.JxW(k);
1341  double transport_flux = side_flux/side->measure();
1342 
1343  if (bc_type[sbi] == AdvectionDiffusionModel::abc_inflow && side_flux < 0)
1344  {
1345  for (unsigned int k=0; k<qsize; k++)
1346  {
1347  double bc_term = -transport_flux*bc_values[k][sbi]*fe_values_side.JxW(k);
1348  for (unsigned int i=0; i<ndofs; i++)
1349  local_rhs[i] += bc_term*fe_values_side.shape_value(i,k);
1350  }
1351  if (Model::balance_ != nullptr)
1352  for (unsigned int i=0; i<ndofs; i++)
1353  local_flux_balance_rhs -= local_rhs[i];
1354  }
1355  else if (bc_type[sbi] == AdvectionDiffusionModel::abc_dirichlet)
1356  {
1357  for (unsigned int k=0; k<qsize; k++)
1358  {
1359  double bc_term = gamma[sbi][side->cond_idx()]*bc_values[k][sbi]*fe_values_side.JxW(k);
1360  arma::vec3 bc_grad = -bc_values[k][sbi]*fe_values_side.JxW(k)*dg_variant*(arma::trans(dif_coef[sbi][k])*fe_values_side.normal_vector(k));
1361  for (unsigned int i=0; i<ndofs; i++)
1362  local_rhs[i] += bc_term*fe_values_side.shape_value(i,k)
1363  + arma::dot(bc_grad,fe_values_side.shape_grad(i,k));
1364  }
1365  if (Model::balance_ != nullptr)
1366  {
1367  for (unsigned int k=0; k<qsize; k++)
1368  {
1369  for (unsigned int i=0; i<ndofs; i++)
1370  {
1371  local_flux_balance_vector[i] += (arma::dot(ad_coef[sbi][k], fe_values_side.normal_vector(k))*fe_values_side.shape_value(i,k)
1372  - arma::dot(dif_coef[sbi][k]*fe_values_side.shape_grad(i,k),fe_values_side.normal_vector(k))
1373  + gamma[sbi][side->cond_idx()]*fe_values_side.shape_value(i,k))*fe_values_side.JxW(k);
1374  }
1375  }
1376  if (Model::time_->tlevel() > 0)
1377  for (unsigned int i=0; i<ndofs; i++)
1378  local_flux_balance_rhs -= local_rhs[i];
1379  }
1380  }
1381  else if (bc_type[sbi] == AdvectionDiffusionModel::abc_total_flux)
1382  {
1383  Model::get_flux_bc_data(sbi, fe_values_side.point_list(), ele_acc, bc_fluxes, bc_sigma, bc_ref_values);
1384  for (unsigned int k=0; k<qsize; k++)
1385  {
1386  double bc_term = csection[k]*(bc_sigma[k]*bc_ref_values[k]+bc_fluxes[k])*fe_values_side.JxW(k);
1387  for (unsigned int i=0; i<ndofs; i++)
1388  local_rhs[i] += bc_term*fe_values_side.shape_value(i,k);
1389  }
1390 
1391  if (Model::balance_ != nullptr)
1392  {
1393  for (unsigned int i=0; i<ndofs; i++)
1394  {
1395  for (unsigned int k=0; k<qsize; k++)
1396  local_flux_balance_vector[i] += csection[k]*bc_sigma[k]*fe_values_side.JxW(k)*fe_values_side.shape_value(i,k);
1397  local_flux_balance_rhs -= local_rhs[i];
1398  }
1399  }
1400  }
1401  else if (bc_type[sbi] == AdvectionDiffusionModel::abc_diffusive_flux)
1402  {
1403  Model::get_flux_bc_data(sbi, fe_values_side.point_list(), ele_acc, bc_fluxes, bc_sigma, bc_ref_values);
1404  for (unsigned int k=0; k<qsize; k++)
1405  {
1406  double bc_term = csection[k]*(bc_sigma[k]*bc_ref_values[k]+bc_fluxes[k])*fe_values_side.JxW(k);
1407  for (unsigned int i=0; i<ndofs; i++)
1408  local_rhs[i] += bc_term*fe_values_side.shape_value(i,k);
1409  }
1410 
1411  if (Model::balance_ != nullptr)
1412  {
1413  for (unsigned int i=0; i<ndofs; i++)
1414  {
1415  for (unsigned int k=0; k<qsize; k++)
1416  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);
1417  local_flux_balance_rhs -= local_rhs[i];
1418  }
1419  }
1420  }
1421  else if (bc_type[sbi] == AdvectionDiffusionModel::abc_inflow && side_flux >= 0)
1422  {
1423  if (Model::balance_ != nullptr)
1424  {
1425  for (unsigned int k=0; k<qsize; k++)
1426  {
1427  for (unsigned int i=0; i<ndofs; i++)
1428  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);
1429  }
1430  }
1431  }
1432  ls[sbi]->rhs_set_values(ndofs, &(side_dof_indices[0]), local_rhs);
1433 
1434  if (Model::balance_ != nullptr)
1435  {
1436  Model::balance_->add_flux_matrix_values(Model::subst_idx[sbi], loc_b, side_dof_indices, local_flux_balance_vector);
1437  Model::balance_->add_flux_vec_value(Model::subst_idx[sbi], loc_b, local_flux_balance_rhs);
1438  }
1439  }
1440  ++loc_b;
1441  }
1442  }
1443 }
1444 
1445 
1446 
1447 template<class Model>
1448 template<unsigned int dim>
1450 {
1451  OLD_ASSERT(cell->dim() == dim, "Element dimension mismatch!");
1452 
1453  velocity.resize(fv.n_points());
1454 
1455  for (unsigned int k=0; k<fv.n_points(); k++)
1456  {
1457  velocity[k].zeros();
1458  for (unsigned int sid=0; sid<cell->n_sides(); sid++)
1459  velocity[k] += fv.shape_vector(sid,k) * Model::mh_dh->side_flux( *(cell->side(sid)) );
1460  }
1461 }
1462 
1463 
1464 
1465 
1466 
1467 template<class Model>
1469  const int s1,
1470  const int s2,
1471  const int K_size,
1472  const vector<arma::mat33> &K1,
1473  const vector<arma::mat33> &K2,
1474  const vector<double> &fluxes,
1475  const arma::vec3 &normal_vector,
1476  const double alpha1,
1477  const double alpha2,
1478  double &gamma,
1479  double *omega,
1480  double &transport_flux)
1481 {
1482  double delta[2];
1483  double h = 0;
1484  double local_alpha = 0;
1485 
1486  OLD_ASSERT(edg.side(s1)->valid(), "Invalid side of an edge.");
1487  SideIter s = edg.side(s1);
1488 
1489  // calculate the side diameter
1490  if (s->dim() == 0)
1491  {
1492  h = 1;
1493  }
1494  else
1495  {
1496  for (unsigned int i=0; i<s->n_nodes(); i++)
1497  for (unsigned int j=i+1; j<s->n_nodes(); j++)
1498  h = max(h, s->node(i)->distance(*s->node(j)));
1499  }
1500 
1501  // calculate the total in- and out-flux through the edge
1502  double pflux = 0, nflux = 0;
1503  for (int i=0; i<edg.n_sides; i++)
1504  {
1505  if (fluxes[i] > 0)
1506  pflux += fluxes[i];
1507  else
1508  nflux += fluxes[i];
1509  }
1510 
1511  // calculate the flux from s1 to s2
1512  if (fluxes[s2] > 0 && fluxes[s1] < 0 && s1 < s2)
1513  transport_flux = fluxes[s1]*fabs(fluxes[s2]/pflux);
1514  else if (fluxes[s2] < 0 && fluxes[s1] > 0 && s1 < s2)
1515  transport_flux = fluxes[s1]*fabs(fluxes[s2]/nflux);
1516  else if (s1==s2)
1517  transport_flux = fluxes[s1];
1518  else
1519  transport_flux = 0;
1520 
1521  gamma = 0.5*fabs(transport_flux);
1522 
1523 
1524  // determine local DG penalty
1525  local_alpha = max(alpha1, alpha2);
1526 
1527  if (s1 == s2)
1528  {
1529  omega[0] = 1;
1530 
1531  // delta is set to the average value of Kn.n on the side
1532  delta[0] = 0;
1533  for (int k=0; k<K_size; k++)
1534  delta[0] += dot(K1[k]*normal_vector,normal_vector);
1535  delta[0] /= K_size;
1536 
1537  gamma += local_alpha/h*(delta[0]);
1538  }
1539  else
1540  {
1541  delta[0] = 0;
1542  delta[1] = 0;
1543  for (int k=0; k<K_size; k++)
1544  {
1545  delta[0] += dot(K1[k]*normal_vector,normal_vector);
1546  delta[1] += dot(K2[k]*normal_vector,normal_vector);
1547  }
1548  delta[0] /= K_size;
1549  delta[1] /= K_size;
1550 
1551  double delta_sum = delta[0] + delta[1];
1552 
1553  if (delta_sum > numeric_limits<double>::epsilon())
1554  {
1555  omega[0] = delta[1]/delta_sum;
1556  omega[1] = delta[0]/delta_sum;
1557  gamma += local_alpha/h*(delta[0]*delta[1]/delta_sum);
1558  }
1559  else
1560  for (int i=0; i<2; i++) omega[i] = 0;
1561  }
1562 }
1563 
1564 
1565 
1566 
1567 
1568 
1569 template<class Model>
1571  const int K_size,
1572  const vector<arma::mat33> &K,
1573  const double flux,
1574  const arma::vec3 &normal_vector,
1575  const double alpha,
1576  double &gamma)
1577 {
1578  double delta = 0, h = 0;
1579 
1580  // calculate the side diameter
1581  if (side->dim() == 0)
1582  {
1583  h = 1;
1584  }
1585  else
1586  {
1587  for (unsigned int i=0; i<side->n_nodes(); i++)
1588  for (unsigned int j=i+1; j<side->n_nodes(); j++)
1589  h = max(h, side->node(i)->distance( *side->node(j) ));
1590  }
1591 
1592  // delta is set to the average value of Kn.n on the side
1593  for (int k=0; k<K_size; k++)
1594  delta += dot(K[k]*normal_vector,normal_vector);
1595  delta /= K_size;
1596 
1597  gamma = 0.5*fabs(flux) + alpha/h*delta;
1598 }
1599 
1600 
1601 
1602 
1603 
1604 template<class Model>
1606 {
1607  START_TIMER("set_init_cond");
1608  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
1609  ls[sbi]->start_allocation();
1610  prepare_initial_condition<1>();
1611  prepare_initial_condition<2>();
1612  prepare_initial_condition<3>();
1613 
1614  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
1615  ls[sbi]->start_add_assembly();
1616  prepare_initial_condition<1>();
1617  prepare_initial_condition<2>();
1618  prepare_initial_condition<3>();
1619 
1620  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
1621  {
1622  ls[sbi]->finish_assembly();
1623  ls[sbi]->solve();
1624  }
1625  END_TIMER("set_init_cond");
1626 }
1627 
1628 template<class Model>
1629 template<unsigned int dim>
1631 {
1632  FEValues<dim,3> fe_values(*feo->mapping<dim>(), *feo->q<dim>(), *feo->fe<dim>(),
1634  const unsigned int ndofs = feo->fe<dim>()->n_dofs(), qsize = feo->q<dim>()->size();
1635  unsigned int dof_indices[ndofs];
1636  double matrix[ndofs*ndofs], rhs[ndofs];
1637  std::vector<arma::vec> init_values(qsize);
1638 
1639  for (unsigned int k=0; k<qsize; k++)
1640  init_values[k].resize(Model::n_substances());
1641 
1642  for (unsigned int i_cell=0; i_cell<Model::mesh_->get_el_ds()->lsize(); i_cell++)
1643  {
1644  typename DOFHandlerBase::CellIterator elem = Model::mesh_->element(feo->dh()->el_index(i_cell));
1645  if (elem->dim() != dim) continue;
1646 
1647  ElementAccessor<3> ele_acc = elem->element_accessor();
1648  feo->dh()->get_dof_indices(elem, dof_indices);
1649  fe_values.reinit(elem);
1650 
1651  Model::compute_init_cond(fe_values.point_list(), ele_acc, init_values);
1652 
1653  for (unsigned int sbi=0; sbi<Model::n_substances(); sbi++)
1654  {
1655  for (unsigned int i=0; i<ndofs; i++)
1656  {
1657  rhs[i] = 0;
1658  for (unsigned int j=0; j<ndofs; j++)
1659  matrix[i*ndofs+j] = 0;
1660  }
1661 
1662  for (unsigned int k=0; k<qsize; k++)
1663  {
1664  double rhs_term = init_values[k](sbi)*fe_values.JxW(k);
1665 
1666  for (unsigned int i=0; i<ndofs; i++)
1667  {
1668  for (unsigned int j=0; j<ndofs; j++)
1669  matrix[i*ndofs+j] += fe_values.shape_value(i,k)*fe_values.shape_value(j,k)*fe_values.JxW(k);
1670 
1671  rhs[i] += fe_values.shape_value(i,k)*rhs_term;
1672  }
1673  }
1674  ls[sbi]->set_values(ndofs, (int *)dof_indices, ndofs, (int *)dof_indices, matrix, rhs);
1675  }
1676  }
1677 }
1678 
1679 
1680 template<class Model>
1681 void TransportDG<Model>::get_par_info(int * &el_4_loc, Distribution * &el_ds)
1682 {
1683  el_4_loc = Model::mesh_->get_el_4_loc();
1684  el_ds = Model::mesh_->get_el_ds();
1685 }
1686 
1687 
1688 template<class Model>
1690 {
1691  if (solution_changed)
1692  {
1693  for (unsigned int i_cell=0; i_cell<Model::mesh_->get_el_ds()->lsize(); i_cell++)
1694  {
1695  typename DOFHandlerBase::CellIterator elem = Model::mesh_->element(feo->dh()->el_index(i_cell));
1696 
1697  unsigned int n_dofs;
1698  switch (elem->dim())
1699  {
1700  case 1:
1701  n_dofs = feo->fe<1>()->n_dofs();
1702  break;
1703  case 2:
1704  n_dofs = feo->fe<2>()->n_dofs();
1705  break;
1706  case 3:
1707  n_dofs = feo->fe<3>()->n_dofs();
1708  break;
1709  }
1710 
1711  unsigned int dof_indices[n_dofs];
1712  feo->dh()->get_dof_indices(elem, dof_indices);
1713 
1714  for (unsigned int sbi=0; sbi<Model::n_substances(); ++sbi)
1715  {
1716  double old_average = 0;
1717  for (unsigned int j=0; j<n_dofs; ++j)
1718  old_average += ls[sbi]->get_solution_array()[dof_indices[j]-feo->dh()->distr()->begin()];
1719  old_average /= n_dofs;
1720 
1721  for (unsigned int j=0; j<n_dofs; ++j)
1722  ls[sbi]->get_solution_array()[dof_indices[j]-feo->dh()->distr()->begin()] += solution_elem_[sbi][i_cell] - old_average;
1723  }
1724  }
1725  }
1726  // update mass_vec for the case that mass matrix changes in next time step
1727  for (unsigned int sbi=0; sbi<Model::n_substances(); ++sbi)
1728  MatMult(*(ls_dt->get_matrix()), ls[sbi]->get_solution(), mass_vec[sbi]);
1729 }
1730 
1731 template<class Model>
1733 {
1734  return Model::mesh_->get_row_4_el();
1735 }
1736 
1737 
1738 
1739 
1740 
1741 
1742 
1743 
1745 template class TransportDG<HeatTransferModel>;
1746 
1747 
1748 
1749 
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:230
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:291
#define DBGMSG(...)
Definition: global_defs.h:229
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.
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
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:552
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:44
Solver based on the original PETSc solver using MPIAIJ matrix and succesive Schur complement construc...
static constexpr Mask allow_output
The field can output. Is part of generated output selection. (default on)
Definition: field_flag.hh:37
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:50
FieldCommon & flags_add(FieldFlag::Flags::Mask mask)
double distance(const Node &n2) const
Definition: nodes.hh:86
virtual void start_add_assembly()
Definition: linsys.hh:296
void assemble_mass_matrix()
Assembles the mass matrix.
Boundary * cond() const
Definition: side_impl.hh:69
virtual PetscErrorCode mat_zero_entries()
Definition: linsys.hh:238
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:99
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:288
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:140
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:85
unsigned int n_loc_nb() const
Returns number of local neighbours.
Definition: dofhandler.hh:337
void set_from_input(const Input::Record in_rec)
#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:460
const Vec & get_solution(unsigned int sbi)
FieldCommon & units(const UnitSI &units)
Set basic units of the field.
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:45
unsigned int n_points()
Returns the number of quadrature points.
Definition: fe_values.hh:329
Class for declaration of inputs sequences.
Definition: type_base.hh:316
void calculate_cumulative_balance()
void assemble_fluxes_boundary()
Assembles the fluxes on the boundary.
Symmetric Gauss-Legendre quadrature formulae on simplices.
#define WAVERAGE(i, k, side_id)
TransportDG(Mesh &init_mesh, const Input::Record in_rec)
Constructor.
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:321
#define OLD_ASSERT(...)
Definition: global_defs.h:128
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:229
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:36
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:42
FieldCommon & input_default(const string &input_default)
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:52
SideIter side()
unsigned int begin(int proc) const
get starting local index
Selection & add_value(const int value, const std::string &key, const std::string &description="")
Adds one new value with name given by key to the Selection.
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:277
const Ret val(const string &key) const
static auto region_id(Mesh &mesh) -> IndexField
#define xprintf(...)
Definition: system.hh:87
Mat mass_matrix
The mass matrix.
#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
Mat * stiffness_matrix
The stiffness matrix.
Mesh * mesh_
Definition: equation.hh:221
Record & declare_key(const string &key, std::shared_ptr< TypeBase > type, const Default &default_value, const string &description, TypeBase::attribute_map key_attributes=TypeBase::attribute_map())
Declares a new key of the Record.
Definition: type_record.cc:459
const Record & close() const
Close the Record for further declarations of keys.
Definition: type_record.cc:271
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:46
Affine mapping between reference and actual cell.
Definition: mapping_p1.hh:53
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:253
FLOW123D_FORCE_LINK_IN_CHILD(concentrationTransportModel)
Vec * rhs
Vector of right hand side.
void set_solution(double *sol_array)
Definition: linsys.hh:264
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:225
DOFHandlerMultiDim * dh()
Region region() const
Definition: accessors.hh:98
Normal vectors.
virtual PetscErrorCode rhs_zero_entries()
Definition: linsys.hh:247
LinSys * ls_dt
Linear algebra system for the time derivative (actually it is used only for handling the matrix struc...
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
const double epsilon
Definition: mathfce.h:23
FieldCommon & description(const string &description)
Definition: field_common.hh:90
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:341
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:74
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:71
virtual MultiFieldValue::return_type value(const Point &p, const ElementAccessor< spacedim > &elm) const
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:51
void update_after_reactions(bool solution_changed)
Definition: system.hh:59
void reinit(ElementFullIter &cell, unsigned int sid)
Update cell-dependent data (gradients, Jacobians etc.)
Definition: fe_values.cc:225
void reinit(ElementFullIter &cell)
Update cell-dependent data (gradients, Jacobians etc.)
Definition: fe_values.cc:157
Vec * mass_vec
Mass from previous time instant (necessary when coefficients of mass matrix change in time)...
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.
FieldCommon & name(const string &name)
Definition: field_common.hh:83
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:310
Quadrature< dim > * q()
static const Input::Type::Record & get_input_type()
Definition: linsys_PETSC.cc:31
unsigned int el_idx() const
Definition: side_impl.hh:80
void calculate_instant_balance()
virtual PetscErrorCode set_matrix(Mat &matrix, MatStructure str)
Definition: linsys.hh:220
Record type proxy class.
Definition: type_record.hh:171
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
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
virtual const Mat * get_matrix()
Definition: linsys.hh:161
const Node * node(unsigned int i) const
Definition: side_impl.hh:45
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:238
Distribution * distr() const
Definition: dofhandler.hh:79
Mapping< dim, 3 > * mapping()
static UnitSI & dimensionless()
Returns dimensionless unit.
Definition: unit_si.cc:53
~TransportDG()
Destructor.
LinSys ** ls
Linear algebra system for the transport equation.
#define JUMP(i, k, side_id)
void set_DG_parameters_edge(const Edge &edg, const int s1, const int s2, const int K_size, const std::vector< arma::mat33 > &K1, const std::vector< arma::mat33 > &K2, const std::vector< double > &fluxes, const arma::vec3 &normal_vector, const double alpha1, const double alpha2, double &gamma, double *omega, double &transport_flux)
Sets up some parameters of the DG method for two sides of an edge.
SideIter side(const unsigned int i) const
Definition: edges.h:31
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:43
virtual void value_list(const std::vector< Point > &point_list, const ElementAccessor< spacedim > &elm, std::vector< typename MultiFieldValue::return_type > &value_list) const
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:32
Transformed quadrature weights.
unsigned int lsize(int proc) const
get local size