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