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