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