Flow123d  release_1.8.2-1603-g0109a2b
darcy_flow_mh.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 darcy_flow_mh.cc
15  * @ingroup flow
16  * @brief Setup and solve linear system of mixed-hybrid discretization of the linear
17  * porous media flow with possible preferential flow in fractures and chanels.
18  */
19 
20 //#include <limits>
21 #include <vector>
22 //#include <iostream>
23 //#include <iterator>
24 //#include <algorithm>
25 #include <armadillo>
26 
27 #include "petscmat.h"
28 #include "petscviewer.h"
29 #include "petscerror.h"
30 #include "mpi.h"
31 
32 #include "system/system.hh"
33 #include "system/sys_profiler.hh"
34 #include "input/factory.hh"
35 
36 #include "mesh/mesh.h"
37 #include "mesh/intersection.hh"
38 #include "mesh/partitioning.hh"
39 #include "la/distribution.hh"
40 #include "la/linsys.hh"
41 #include "la/linsys_PETSC.hh"
42 #include "la/linsys_BDDC.hh"
43 #include "la/schur.hh"
44 #include "la/sparse_graph.hh"
46 
47 #include "flow/darcy_flow_mh.hh"
48 
50 
51 #include "fem/mapping_p1.hh"
52 #include "fem/fe_p.hh"
53 #include "fem/fe_values.hh"
54 #include "fem/fe_rt.hh"
56 
57 #include "tools/time_governor.hh"
59 #include "fields/field.hh"
60 #include "fields/field_values.hh"
62 
63 #include "coupling/balance.hh"
64 
65 #include "fields/vec_seq_double.hh"
66 
67 
68 FLOW123D_FORCE_LINK_IN_CHILD(darcy_flow_mh);
69 
70 
71 
72 
73 namespace it = Input::Type;
74 
76  return it::Selection("MH_MortarMethod")
77  .add_value(NoMortar, "None", "Mortar space: P0 on elements of lower dimension.")
78  .add_value(MortarP0, "P0", "Mortar space: P0 on elements of lower dimension.")
79  .add_value(MortarP1, "P1", "Mortar space: P1 on intersections, using non-conforming pressures.")
80  .close();
81 }
82 
83 
85  return it::Selection("DarcyFlow_BC_Type")
86  .add_value(none, "none",
87  "Homogeneous Neumann boundary condition. Zero flux")
88  .add_value(dirichlet, "dirichlet",
89  "Dirichlet boundary condition. "
90  "Specify the pressure head through the 'bc_pressure' field "
91  "or the piezometric head through the 'bc_piezo_head' field.")
92  .add_value(total_flux, "total_flux", "Flux boundary condition (combines Neumann and Robin type). "
93  "Water inflow equal to (($q^N + \\sigma (h^R - h)$)). "
94  "Specify the water inflow by the 'bc_flux' field, the transition coefficient by 'bc_robin_sigma' "
95  "and the reference pressure head or pieozmetric head through 'bc_pressure' or 'bc_piezo_head' respectively.")
96  .add_value(seepage, "seepage",
97  "Seepage face boundary condition. Pressure and inflow bounded from above. Boundary with potential seepage flow "
98  "is described by the pair of inequalities:"
99  "(($h \\le h_d^D$)) and (($ q \\le q_d^N$)), where the equality holds in at least one of them. Caution! Setting $q_d^N$ strictly negative"
100  "may lead to an ill posed problem since a positive outflow is enforced."
101  "Parameters (($h_d^D$)) and (($q_d^N$)) are given by fields ``bc_pressure`` (or ``bc_piezo_head``) and ``bc_flux`` respectively."
102  )
103  .add_value(river, "river",
104  "River boundary condition. For the water level above the bedrock, (($H > H^S$)), the Robin boundary condition is used with the inflow given by: "
105  "(( $q^N + \\sigma(H^D - H)$)). For the water level under the bedrock, constant infiltration is used "
106  "(( $q^N + \\sigma(H^D - H^S)$)). Parameters: ``bc_pressure``, ``bc_switch_pressure``,"
107  " ``bc_sigma, ``bc_flux``."
108  )
109  .close();
110 }
111 
112 
114  it::Record field_descriptor =
115  it::Record("DarcyFlowMH_Data",FieldCommon::field_descriptor_record_description("DarcyFlowMH_Data") )
116  .copy_keys( DarcyFlowMH_Steady::EqData().make_field_descriptor_type("DarcyFlowMH_Data_aux") )
117  .declare_key("bc_piezo_head", FieldAlgorithmBase< 3, FieldValue<3>::Scalar >::get_input_type_instance(),
118  "Boundary piezometric head for BC types: dirichlet, robin, and river." )
119  .declare_key("bc_switch_piezo_head", FieldAlgorithmBase< 3, FieldValue<3>::Scalar >::get_input_type_instance(),
120  "Boundary switch piezometric head for BC types: seepage, river." )
121  .declare_key("init_piezo_head", FieldAlgorithmBase< 3, FieldValue<3>::Scalar >::get_input_type_instance(),
122  "Initial condition for the pressure given as the piezometric head." )
123  .close();
124 
125  it::Record ns_rec = Input::Type::Record("NonlinearSolver", "Parameters to a non-linear solver.")
127  "Linear solver for MH problem.")
128  .declare_key("tolerance", it::Double(0.0), it::Default("1E-6"),
129  "Residual tolerance.")
130  .declare_key("max_it", it::Integer(0), it::Default("100"),
131  "Maximal number of iterations (linear solves) of the non-linear solver.")
132  .declare_key("converge_on_stagnation", it::Bool(), it::Default("false"),
133  "If a stagnation of the nonlinear solver is detected the solver stops. "
134  "A divergence is reported by default forcing the end of the simulation. Setting this flag to 'true', the solver"
135  "ends with convergence success on stagnation, but report warning about it.")
136  .close();
137 
138  return it::Record("SteadyDarcy_MH", "Mixed-Hybrid solver for STEADY saturated Darcy flow.")
140  .declare_key("input_fields", it::Array( field_descriptor ), it::Default::obligatory(),
141  "Input data for Darcy flow model.")
142  .declare_key("nonlinear_solver", ns_rec, it::Default::obligatory(),
143  "Non-linear solver for MH problem.")
144 
146  "Parameters of output form MH module.")
148  "Settings for computing mass balance.")
150  "Time governor setting for the unsteady Darcy flow model.")
151  .declare_key("n_schurs", it::Integer(0,2), it::Default("2"),
152  "Number of Schur complements to perform when solving MH system.")
153  .declare_key("mortar_method", get_mh_mortar_selection(), it::Default("\"None\""),
154  "Method for coupling Darcy flow between dimensions." )
155  .close();
156 }
157 
158 
160  Input::register_class< DarcyFlowMH_Steady, Mesh &, const Input::Record >("SteadyDarcy_MH") +
162 
163 
164 
166 {
167  ADD_FIELD(anisotropy, "Anisotropy of the conductivity tensor.", "1.0" );
168  anisotropy.units( UnitSI::dimensionless() );
169 
170  ADD_FIELD(cross_section, "Complement dimension parameter (cross section for 1D, thickness for 2D).", "1.0" );
171  cross_section.units( UnitSI().m(3).md() );
172 
173  ADD_FIELD(conductivity, "Isotropic conductivity scalar.", "1.0" );
174  conductivity.units( UnitSI().m().s(-1) );
175 
176  ADD_FIELD(sigma, "Transition coefficient between dimensions.", "1.0");
177  sigma.units( UnitSI::dimensionless() );
178 
179  ADD_FIELD(water_source_density, "Water source density.", "0.0");
180  water_source_density.units( UnitSI().s(-1) );
181 
182  ADD_FIELD(bc_type,"Boundary condition type, possible values:", "\"none\"" );
183  // TODO: temporary solution, we should try to get rid of pointer to the selection after having generic types
184  bc_type.input_selection( &get_bc_type_selection() );
185  bc_type.units( UnitSI::dimensionless() );
186 
187  ADD_FIELD(bc_pressure,"Prescribed pressure value on the boundary. Used for all values of 'bc_type' save the bc_type='none'."
188  "See documentation of 'bc_type' for exact meaning of 'bc_pressure' in individual boundary condition types.", "0.0");
189  bc_pressure.disable_where(bc_type, {none} );
190  bc_pressure.units( UnitSI().m() );
191 
192  ADD_FIELD(bc_flux,"Incoming water boundary flux. Used for bc_types : 'none', 'total_flux', 'seepage', 'river'.", "0.0");
193  bc_flux.disable_where(bc_type, {none, dirichlet} );
194  bc_flux.units( UnitSI().m(4).s(-1).md() );
195 
196  ADD_FIELD(bc_robin_sigma,"Conductivity coefficient in the 'total_flux' or the 'river' boundary condition type.", "0.0");
197  bc_robin_sigma.disable_where(bc_type, {none, dirichlet, seepage} );
198  bc_robin_sigma.units( UnitSI().m(3).s(-1).md() );
199 
200  ADD_FIELD(bc_switch_pressure,
201  "Critical switch pressure for 'seepage' and 'river' boundary conditions.", "0.0");
202  bc_switch_pressure.disable_where(bc_type, {none, dirichlet, total_flux} );
203  bc_switch_pressure.units( UnitSI().m() );
204 
205  //these are for unsteady
206  ADD_FIELD(init_pressure, "Initial condition as pressure", "0.0" );
207  init_pressure.units( UnitSI().m() );
208 
209  ADD_FIELD(storativity,"Storativity.", "0.0" );
210  storativity.units( UnitSI().m(-1) );
211 
212  //time_term_fields = this->subset({"storativity"});
213  //main_matrix_fields = this->subset({"anisotropy", "conductivity", "cross_section", "sigma", "bc_type", "bc_robin_sigma"});
214  //rhs_fields = this->subset({"water_source_density", "bc_pressure", "bc_flux"});
215 
216 }
217 
218 
219 template<unsigned int dim>
221 : quad_(3),
222  fe_values_(map_, quad_, fe_rt_,
224 
225  side_quad_(1),
226  fe_side_values_(map_, side_quad_, fe_p_disc_, update_normal_vectors),
227 
228  velocity_interpolation_quad_(0), // veloctiy values in barycenter
229  velocity_interpolation_fv_(map_,velocity_interpolation_quad_, fe_rt_, update_values | update_quadrature_points),
230 
231  ad_(ad)
232 {
233 }
234 
235 template<unsigned int dim>
237 {
238 }
239 
240 
241 //=============================================================================
242 // CREATE AND FILL GLOBAL MH MATRIX OF THE WATER MODEL
243 // - do it in parallel:
244 // - initial distribution of elements, edges
245 //
246 /*! @brief CREATE AND FILL GLOBAL MH MATRIX OF THE WATER MODEL
247  *
248  * Parameters {Solver,NSchurs} number of performed Schur
249  * complements (0,1,2) for water flow MH-system
250  *
251  */
252 //=============================================================================
254 : DarcyFlowInterface(mesh_in, in_rec),
255  solution(nullptr),
256  schur0(nullptr),
257  edge_ds(nullptr),
258  el_ds(nullptr),
259  side_ds(nullptr),
260  el_4_loc(nullptr)
261 
262 {
263 
264  is_linear_=true;
265 
266  START_TIMER("Darcy constructor");
267  {
268  Input::Record time_record;
269  if ( in_rec.opt_val("time", time_record) )
270  time_ = new TimeGovernor(time_record);
271  else
272  time_ = new TimeGovernor();
273  this->eq_data_ = &data_;
274  }
275 
276  //connecting data fields with mesh
277  {
278  START_TIMER("data init");
279  data_.set_mesh(mesh_in);
280  //data_.gravity_ = arma::vec4( in_rec.val<std::string>("gravity") );
281  data_.gravity_ = arma::vec4(" 0 0 -1 0");
283  std::make_shared<FieldAddPotential<3, FieldValue<3>::Scalar>::FieldFactory>
284  (data_.gravity_, "bc_piezo_head") );
286  std::make_shared<FieldAddPotential<3, FieldValue<3>::Scalar>::FieldFactory>
287  (data_.gravity_, "bc_switch_piezo_head") );
288 
289  data_.set_input_list( in_rec.val<Input::Array>("input_fields") );
291  }
292  output_object = new DarcyFlowMHOutput(this, in_rec.val<Input::Record>("output"));
293 
294 
295  size = mesh_->n_elements() + mesh_->n_sides() + mesh_->n_edges();
296  n_schur_compls = in_rec.val<int>("n_schurs");
297  mortar_method_= in_rec.val<MortarMethod>("mortar_method");
298  if (mortar_method_ != NoMortar) {
300  }
301  mh_dh.reinit(mesh_);
302 
303  AssemblyData assembly_data;
304  assembly_data.data = &data_;
305  assembly_data.mesh = mesh_;
306  assembly_data.mh_dh = &mh_dh;
307  assembly_.push_back(new Assembly<1>(assembly_data));
308  assembly_.push_back(new Assembly<2>(assembly_data));
309  assembly_.push_back(new Assembly<3>(assembly_data));
310 
311  //side_ds->view( std::cout );
312  //el_ds->view( std::cout );
313  //edge_ds->view( std::cout );
314  //rows_ds->view( std::cout );
315 
316 }
317 
318 
319 
321 {
323  // zero_time_term means steady case
324  bool zero_time_term_from_right
326 
329  .val<Input::Record>("nonlinear_solver")
330  .val<Input::AbstractRecord>("linear_solver");
331 
334  // initialization of balance object
336  if (it->val<bool>("balance_on"))
337  {
338  balance_ = boost::make_shared<Balance>("water", mesh_, *it);
339  water_balance_idx_ = balance_->add_quantity("water_volume");
340  balance_->allocate(rows_ds->lsize(), 1);
341  balance_->units(UnitSI().m(3));
342  }
343 
344  /* TODO:
345  * - Allow solution reconstruction (pressure and velocity) from initial condition on user request.
346  * - Steady solution as an intitial condition may be forced by setting inti_time =-1, and set data for the steady solver in that time.
347  * Solver should be able to switch from and to steady case depending on the zero time term.
348  */
349 
350 
351  if (zero_time_term_from_right) {
352  // steady case
353  //read_initial_condition(); // Possible solution guess for steady case.
354  use_steady_assembly_ = true;
355  solve_nonlinear(); // with right limit data
356  } else {
358  assembly_linear_system(); // in particular due to balance
359  // TODO: reconstruction of solution in zero time.
360  }
361  //solution_output(T,right_limit); // data for time T in any case
362  output_data();
363 }
364 
365 //=============================================================================
366 // COMPOSE and SOLVE WATER MH System possibly through Schur complements
367 //=============================================================================
369 {
370  START_TIMER("Solving MH system");
371 
372 
373  time_->next_time();
374 
376  bool zero_time_term_from_left
378  bool jump_time = data_.storativity.is_jump_time();
379  if (! zero_time_term_from_left) {
380  // time term not treated as zero
381  // Unsteady solution up to the T.
382 
383  // this flag is necesssary for switching BC to avoid setting zero neumann on the whole boundary in the steady case
384  use_steady_assembly_ = false;
385  solve_nonlinear(); // with left limit data
386  if (jump_time) {
387  xprintf(Warn, "Output of solution discontinuous in time not supported yet.\n");
388  //solution_output(T, left_limit); // output use time T- delta*dt
389  //output_data();
390  }
391  }
392 
393  if (time_->is_end()) {
394  // output for unsteady case, end_time should not be the jump time
395  // but rether check that
396  if (! zero_time_term_from_left && ! jump_time) output_data();
397  return;
398  }
399 
401  bool zero_time_term_from_right
403  if (zero_time_term_from_right) {
404  // this flag is necesssary for switching BC to avoid setting zero neumann on the whole boundary in the steady case
405  use_steady_assembly_ = true;
406  solve_nonlinear(); // with right limit data
407 
408  } else if (! zero_time_term_from_left && jump_time) {
409  xprintf(Warn, "Discontinuous time term not supported yet.\n");
410  //solution_transfer(); // internally call set_time(T, left) and set_time(T,right) again
411  //solve_nonlinear(); // with right limit data
412  }
413  //solution_output(T,right_limit); // data for time T in any case
414  output_data();
415 
416 }
417 
418 
419 
421 {
423  double residual_norm = schur0->compute_residual();
424  unsigned int l_it=0;
426  xprintf(Msg, "Nonlin initial res: %d %g\n",nonlinear_iteration_, residual_norm);
427 
428  // Reduce is_linear flag.
429  int is_linear_common;
430  MPI_Allreduce(&is_linear_, &is_linear_common,1, MPI_INT ,MPI_MIN,PETSC_COMM_WORLD);
431 
432  Input::Record nl_solver_rec = input_record_.val<Input::Record>("nonlinear_solver");
433  this->tolerance_ = nl_solver_rec.val<double>("tolerance");
434  this->max_n_it_ = nl_solver_rec.val<unsigned int>("max_it");
435 
436  if (! is_linear_common) {
437  // set tolerances of the linear solver unless they are set by user.
438  schur0->set_tolerances(0.1, 0.1*this->tolerance_, 10);
439  }
440  vector<double> convergence_history;
441 
442 
443  while (residual_norm > this->tolerance_ && nonlinear_iteration_ < this->max_n_it_) {
444  OLD_ASSERT_EQUAL( convergence_history.size(), nonlinear_iteration_ );
445  convergence_history.push_back(residual_norm);
446  if (convergence_history.size() >= 5 &&
447  convergence_history[ convergence_history.size() - 1]/convergence_history[ convergence_history.size() - 2] > 0.9 &&
448  convergence_history[ convergence_history.size() - 1]/convergence_history[ convergence_history.size() - 5] > 0.8) {
449  // stagnation
450  if (input_record_.val<bool>("converge_on_stagnation")) {
451  xprintf(Warn, "Accept solution on stagnation. Its: %d Residual: %g\n", nonlinear_iteration_, residual_norm);
452  break;
453  } else {
454  THROW(ExcSolverDiverge() << EI_Reason("Stagnation."));
455  }
456  }
457 
458 
459  int convergedReason = schur0->solve();
460  this -> postprocess();
462 
463  // hack to make BDDC work with empty compute_residual
464  if (is_linear_common) break;
465 
466  //xprintf(MsgLog, "Linear solver ended with reason: %d \n", convergedReason );
467  //OLD_ASSERT( convergedReason >= 0, "Linear solver failed to converge. Convergence reason %d \n", convergedReason );
469  residual_norm = schur0->compute_residual();
470  xprintf(Msg, "Nonlin iter: %d %d (%d) %g\n",nonlinear_iteration_, l_it, convergedReason, residual_norm);
471 
472 
473  }
474 
476 
477 }
478 
480 {
481  START_TIMER("postprocess");
482  //ElementFullIter ele = ELEMENT_FULL_ITER(mesh_, NULL);
483 
484  // modify side fluxes in parallel
485  // for every local edge take time term on digonal and add it to the corresponding flux
486  /*
487  for (unsigned int i_loc = 0; i_loc < el_ds->lsize(); i_loc++) {
488  ele = mesh_->element(el_4_loc[i_loc]);
489  FOR_ELEMENT_SIDES(ele,i) {
490  side_rows[i] = side_row_4_id[ mh_dh.side_dof( ele->side(i) ) ];
491  values[i] = -1.0 * ele->measure() *
492  data.cross_section.value(ele->centre(), ele->element_accessor()) *
493  data.water_source_density.value(ele->centre(), ele->element_accessor()) /
494  ele->n_sides();
495  }
496  VecSetValues(schur0->get_solution(), ele->n_sides(), side_rows, values, ADD_VALUES);
497  }
498  VecAssemblyBegin(schur0->get_solution());
499  VecAssemblyEnd(schur0->get_solution());
500  */
501 }
502 
503 
505  START_TIMER("Darcy output data");
506  time_->view("DARCY"); //time governor information output
507  this->output_object->output();
508 
509 
510  if (balance_ != nullptr)
511  {
512  START_TIMER("Darcy balance output");
513  if (balance_->cumulative() && time_->tlevel() > 0)
514  {
515  balance_->calculate_cumulative_sources(water_balance_idx_, schur0->get_solution(), time_->dt());
516  balance_->calculate_cumulative_fluxes(water_balance_idx_, schur0->get_solution(), time_->dt());
517  }
518 
520  {
521  balance_->calculate_mass(water_balance_idx_, schur0->get_solution());
522  balance_->calculate_source(water_balance_idx_, schur0->get_solution());
523  balance_->calculate_flux(water_balance_idx_, schur0->get_solution());
524  balance_->output(time_->t());
525  }
526  }
527 }
528 
529 
531 {
532  return schur0->get_solution_precision();
533 }
534 
535 
536 
537 void DarcyFlowMH_Steady::get_solution_vector(double * &vec, unsigned int &vec_size)
538 {
539  // TODO: make class for vectors (wrapper for PETSC or other) derived from LazyDependency
540  // and use its mechanism to manage dependency between vectors
542 
543  // scatter solution to all procs
544  VecScatterBegin(par_to_all, schur0->get_solution(), sol_vec, INSERT_VALUES, SCATTER_FORWARD);
545  VecScatterEnd( par_to_all, schur0->get_solution(), sol_vec, INSERT_VALUES, SCATTER_FORWARD);
547  }
548 
549  vec = solution;
550  vec_size = this->size;
551  OLD_ASSERT(vec != NULL, "Requested solution is not allocated!\n");
552 }
553 
555 {
556  vec=schur0->get_solution();
557  OLD_ASSERT(vec != NULL, "Requested solution is not allocated!\n");
558 }
559 
560 
561 template<unsigned int dim>
563 {
564  //START_TIMER("Assembly<dim>::assembly_local_matrix");
565  fe_values_.reinit(ele);
566  const unsigned int ndofs = fe_values_.get_fe()->n_dofs(), qsize = fe_values_.get_quadrature()->size();
567  local_matrix.zeros(ndofs, ndofs);
568 
569  double scale = 1
570  / ad_.data->conductivity.value( ele->centre(), ele->element_accessor() )
571  / ad_.data->cross_section.value( ele->centre(), ele->element_accessor() );
572 
573  for (unsigned int k=0; k<qsize; k++)
574  {
575  for (unsigned int i=0; i<ndofs; i++)
576  {
577  for (unsigned int j=0; j<ndofs; j++)
578  local_matrix[i*ndofs+j] +=
579  scale
580  * arma::dot(fe_values_.shape_vector(i,k),
581  (ad_.data->anisotropy.value(ele->centre(), ele->element_accessor() )).i()
582  * fe_values_.shape_vector(j,k)
583  )
584  * fe_values_.JxW(k);
585  }
586  }
587 }
588 
589 template<unsigned int dim>
591 {
592  //START_TIMER("Assembly<dim>::assembly_local_vb");
593  // compute normal vector to side
594  arma::vec3 nv;
595  ElementFullIter ele_higher = ad_.mesh->element.full_iter(ngh->side()->element());
596  fe_side_values_.reinit(ele_higher, ngh->side()->el_idx());
597  nv = fe_side_values_.normal_vector(0);
598 
599  double value = ad_.data->sigma.value( ele->centre(), ele->element_accessor()) *
600  2*ad_.data->conductivity.value( ele->centre(), ele->element_accessor()) *
601  arma::dot(ad_.data->anisotropy.value( ele->centre(), ele->element_accessor())*nv, nv) *
602  ad_.data->cross_section.value( ngh->side()->centre(), ele_higher->element_accessor() ) * // cross-section of higher dim. (2d)
603  ad_.data->cross_section.value( ngh->side()->centre(), ele_higher->element_accessor() ) /
604  ad_.data->cross_section.value( ele->centre(), ele->element_accessor() ) * // crossection of lower dim.
605  ngh->side()->measure();
606 
607  local_vb[0] = -value; local_vb[1] = value;
608  local_vb[2] = value; local_vb[3] = -value;
609 }
610 
611 
612 
613 template<unsigned int dim>
615 {
616  //START_TIMER("Assembly<dim>::make_element_vector");
617  arma::vec3 flux_in_center;
618  flux_in_center.zeros();
619 
620  velocity_interpolation_fv_.reinit(ele);
621  for (unsigned int li = 0; li < ele->n_sides(); li++) {
622  flux_in_center += ad_.mh_dh->side_flux( *(ele->side( li ) ) )
623  * velocity_interpolation_fv_.shape_vector(li,0);
624  }
625 
626  flux_in_center /= ad_.data->cross_section.value(ele->centre(), ele->element_accessor() );
627  return flux_in_center;
628 }
629 
630 
631 // ===========================================================================================
632 //
633 // MATRIX ASSEMBLY - we use abstract assembly routine, where LS Mat/Vec SetValues
634 // are in fact pointers to allocating or filling functions - this is governed by Linsystem roitunes
635 //
636 // =======================================================================================
637 
639 {
640  START_TIMER("DarcyFlowMH_Steady::assembly_steady_mh_matrix");
641  is_linear_=true;
642 
643  LinSys *ls = schur0;
645 
646  class Boundary *bcd;
647  class Neighbour *ngh;
648 
649  bool fill_matrix = ls->is_preallocated();
650  int el_row, side_row, edge_row, loc_b = 0;
651  int tmp_rows[100];
652  int side_rows[4], edge_rows[4]; // rows for sides and edges of one element
653  double local_vb[4]; // 2x2 matrix
654 
655  // to make space for second schur complement, max. 10 neighbour edges of one el.
656  double zeros[1000];
657  for(int i=0; i<1000; i++) zeros[i]=0.0;
658 
659  double minus_ones[4] = { -1.0, -1.0, -1.0, -1.0 };
660  double loc_side_rhs[4];
661 
662  if (balance_ != nullptr)
663  balance_->start_flux_assembly(water_balance_idx_);
664 
665  for (unsigned int i_loc = 0; i_loc < el_ds->lsize(); i_loc++) {
666  arma::mat local_matrix;
667  ele = mesh_->element(el_4_loc[i_loc]);
668  el_row = row_4_el[el_4_loc[i_loc]];
669  unsigned int nsides = ele->n_sides();
670 
671  if (fill_matrix)
672  assembly_[ele->dim()-1]->assembly_local_matrix(local_matrix, ele);
673 
674  double cross_section = data_.cross_section.value(ele->centre(), ele->element_accessor());
675 
676  for (unsigned int i = 0; i < nsides; i++) {
677  unsigned int idx_side= mh_dh.side_dof( ele->side(i) );
678 /* if (! side_ds->is_local(idx_side)) {
679  cout << el_ds->myp() << " : iside: " << ele.index() << " [" << el_ds->begin() << ", " << el_ds->end() << "]" << endl;
680  cout << el_ds->myp() << " : iside: " << idx_side << " [" << side_ds->begin() << ", " << side_ds->end() << "]" << endl;
681 
682  }*/
683  unsigned int idx_edge= ele->side(i)->edge_idx();
684  side_row = side_rows[i] = side_row_4_id[idx_side];
685  edge_row = edge_rows[i] = row_4_edge[idx_edge];
686  bcd=ele->side(i)->cond();
687 
688  // gravity term on RHS
689  loc_side_rhs[i] = (ele->centre()[ 2 ] - ele->side(i)->centre()[ 2 ]);
690 
691  // set block C and C': side-edge, edge-side
692  double c_val = 1.0;
693 
694  if (bcd) {
695  ElementAccessor<3> b_ele = bcd->element_accessor();
696  EqData::BC_Type type = (EqData::BC_Type)data_.bc_type.value(b_ele.centre(), b_ele);
697 
698  if ( type == EqData::none) {
699  // homogeneous neumann
700  } else if ( type == EqData::dirichlet ) {
701  double bc_pressure = data_.bc_pressure.value(b_ele.centre(), b_ele);
702  c_val = 0.0;
703  loc_side_rhs[i] -= bc_pressure;
704  ls->rhs_set_value(edge_row, -bc_pressure);
705  ls->mat_set_value(edge_row, edge_row, -1.0);
706 
707  } else if ( type == EqData::total_flux) {
708  // internally we work with outward flux
709  double bc_flux = -data_.bc_flux.value(b_ele.centre(), b_ele);
710  double bc_pressure = data_.bc_pressure.value(b_ele.centre(), b_ele);
711  double bc_sigma = data_.bc_robin_sigma.value(b_ele.centre(), b_ele);
712  ls->mat_set_value(edge_row, edge_row, -bcd->element()->measure() * bc_sigma * cross_section );
713  ls->rhs_set_value(edge_row, (bc_flux - bc_sigma * bc_pressure) * bcd->element()->measure() * cross_section);
714 
715  } else if (type==EqData::seepage) {
716  is_linear_=false;
717  //unsigned int loc_edge_idx = edge_row - rows_ds->begin() - side_ds->lsize() - el_ds->lsize();
718  unsigned int loc_edge_idx = bcd->bc_ele_idx_;
719  char & switch_dirichlet = bc_switch_dirichlet[loc_edge_idx];
720  double bc_pressure = data_.bc_switch_pressure.value(b_ele.centre(), b_ele);
721  double bc_flux = -data_.bc_flux.value(b_ele.centre(), b_ele);
722  double side_flux=bc_flux * bcd->element()->measure() * cross_section;
723 
724  // ** Update BC type. **
725  if (switch_dirichlet) {
726  // check and possibly switch to flux BC
727  // The switch raise error on the corresponding edge row.
728  // Magnitude of the error is abs(solution_flux - side_flux).
729  OLD_ASSERT(rows_ds->is_local(side_row), "" );
730  unsigned int loc_side_row = side_row - rows_ds->begin();
731  double & solution_flux = ls->get_solution_array()[loc_side_row];
732 
733  if ( solution_flux < side_flux) {
734  //DBGMSG("x: %g, to neum, p: %g f: %g -> f: %g\n",b_ele.centre()[0], bc_pressure, solution_flux, side_flux);
735  solution_flux = side_flux;
736  switch_dirichlet=0;
737 
738  }
739  } else {
740  // check and possibly switch to pressure BC
741  // TODO: What is the appropriate DOF in not local?
742  // The switch raise error on the corresponding side row.
743  // Magnitude of the error is abs(solution_head - bc_pressure)
744  // Since usually K is very large, this error would be much
745  // higher then error caused by the inverse switch, this
746  // cause that a solution with the flux violating the
747  // flux inequality leading may be accepted, while the error
748  // in pressure inequality is always satisfied.
749  OLD_ASSERT(rows_ds->is_local(edge_row), "" );
750  unsigned int loc_edge_row = edge_row - rows_ds->begin();
751  double & solution_head = ls->get_solution_array()[loc_edge_row];
752 
753  if ( solution_head > bc_pressure) {
754  //DBGMSG("x: %g, to dirich, p: %g -> p: %g f: %g\n",b_ele.centre()[0], solution_head, bc_pressure, bc_flux);
755  solution_head = bc_pressure;
756  switch_dirichlet=1;
757  }
758  }
759 
760  // ** Apply BCUpdate BC type. **
761  // Force Dirichlet type during the first iteration of the unsteady case.
762  if (switch_dirichlet || (use_steady_assembly_ && nonlinear_iteration_ == 0) ) {
763  //DBGMSG("x: %g, dirich, bcp: %g\n",b_ele.centre()[0], bc_pressure);
764  c_val = 0.0;
765  loc_side_rhs[i] -= bc_pressure;
766  ls->rhs_set_value(edge_row, -bc_pressure);
767  ls->mat_set_value(edge_row, edge_row, -1.0);
768  } else {
769  //DBGMSG("x: %g, neuman, q: %g bcq: %g\n",b_ele.centre()[0], side_flux, bc_flux);
770  ls->rhs_set_value(edge_row, side_flux);
771  }
772 
773  } else if (type==EqData::river) {
774  is_linear_=false;
775  //unsigned int loc_edge_idx = edge_row - rows_ds->begin() - side_ds->lsize() - el_ds->lsize();
776  //unsigned int loc_edge_idx = bcd->bc_ele_idx_;
777  //char & switch_dirichlet = bc_switch_dirichlet[loc_edge_idx];
778 
779  double bc_pressure = data_.bc_pressure.value(b_ele.centre(), b_ele);
780  double bc_switch_pressure = data_.bc_switch_pressure.value(b_ele.centre(), b_ele);
781  double bc_flux = -data_.bc_flux.value(b_ele.centre(), b_ele);
782  double bc_sigma = data_.bc_robin_sigma.value(b_ele.centre(), b_ele);
783  OLD_ASSERT(rows_ds->is_local(edge_row), "" );
784  unsigned int loc_edge_row = edge_row - rows_ds->begin();
785  double & solution_head = ls->get_solution_array()[loc_edge_row];
786 
787 
788  // Force Robin type during the first iteration of the unsteady case.
789  if (solution_head > bc_switch_pressure || (use_steady_assembly_ && nonlinear_iteration_ ==0)) {
790  // Robin BC
791  //DBGMSG("x: %g, robin, bcp: %g\n",b_ele.centre()[0], bc_pressure);
792  ls->rhs_set_value(edge_row, bcd->element()->measure() * cross_section * (bc_flux - bc_sigma * bc_pressure) );
793  ls->mat_set_value(edge_row, edge_row, -bcd->element()->measure() * bc_sigma * cross_section );
794  } else {
795  // Neumann BC
796  //DBGMSG("x: %g, neuman, q: %g bcq: %g\n",b_ele.centre()[0], bc_switch_pressure, bc_pressure);
797  double bc_total_flux = bc_flux + bc_sigma*(bc_switch_pressure - bc_pressure);
798  ls->rhs_set_value(edge_row, bc_total_flux * bcd->element()->measure() * cross_section);
799  }
800  } else {
801  xprintf(UsrErr, "BC type not supported.\n");
802  }
803 
804  if (balance_ != nullptr)
805  {
806  balance_->add_flux_matrix_values(water_balance_idx_, loc_b, {side_row}, {1});
807  }
808  ++loc_b;
809  }
810  ls->mat_set_value(side_row, edge_row, c_val);
811  ls->mat_set_value(edge_row, side_row, c_val);
812 
813  // assemble matrix for weights in BDDCML
814  // approximation to diagonal of
815  // S = -C - B*inv(A)*B'
816  // as
817  // diag(S) ~ - diag(C) - 1./diag(A)
818  // the weights form a partition of unity to average a discontinuous solution from neighbouring subdomains
819  // to a continuous one
820  // it is important to scale the effect - if conductivity is low for one subdomain and high for the other,
821  // trust more the one with low conductivity - it will be closer to the truth than an arithmetic average
822  if ( typeid(*ls) == typeid(LinSys_BDDC) ) {
823  double val_side = local_matrix(i,i);
824  double val_edge = -1./local_matrix(i,i);
825 
826  static_cast<LinSys_BDDC*>(ls)->diagonal_weights_set_value( side_row, val_side );
827  static_cast<LinSys_BDDC*>(ls)->diagonal_weights_set_value( edge_row, val_edge );
828  }
829  }
830 
831  ls->rhs_set_values(nsides, side_rows, loc_side_rhs);
832 
833 
834  // set block A: side-side on one element - block diagonal matrix
835  ls->mat_set_values(nsides, side_rows, nsides, side_rows, local_matrix.memptr());
836  // set block B, B': element-side, side-element
837  ls->mat_set_values(1, &el_row, nsides, side_rows, minus_ones);
838  ls->mat_set_values(nsides, side_rows, 1, &el_row, minus_ones);
839 
840 
841  // D block: non-compatible conections and diagonal: element-element
842 
843  ls->mat_set_value(el_row, el_row, 0.0); // maybe this should be in virtual block for schur preallocation
844 
845  if ( typeid(*ls) == typeid(LinSys_BDDC) ) {
846  double val_ele = 1.;
847  static_cast<LinSys_BDDC*>(ls)->diagonal_weights_set_value( el_row, val_ele );
848  }
849 
850  // D, E',E block: compatible connections: element-edge
851 
852  for (unsigned int i = 0; i < ele->n_neighs_vb; i++) {
853  // every compatible connection adds a 2x2 matrix involving
854  // current element pressure and a connected edge pressure
855  ngh= ele->neigh_vb[i];
856  tmp_rows[0]=el_row;
857  tmp_rows[1]=row_4_edge[ ngh->edge_idx() ];
858 
859  assembly_[ngh->side()->dim()]->assembly_local_vb(local_vb, ele, ngh);
860 
861  ls->mat_set_values(2, tmp_rows, 2, tmp_rows, local_vb);
862 
863  // update matrix for weights in BDDCML
864  if ( typeid(*ls) == typeid(LinSys_BDDC) ) {
865  int ind = tmp_rows[1];
866  // there is -value on diagonal in block C!
867  double new_val = local_vb[0];
868  static_cast<LinSys_BDDC*>(ls)->diagonal_weights_set_value( ind, new_val );
869  }
870 
871  if (n_schur_compls == 2) {
872  // for 2. Schur: N dim edge is conected with N dim element =>
873  // there are nz between N dim edge and N-1 dim edges of the element
874 
875  ls->mat_set_values(nsides, edge_rows, 1, tmp_rows+1, zeros);
876  ls->mat_set_values(1, tmp_rows+1, nsides, edge_rows, zeros);
877 
878  // save all global edge indices to higher positions
879  tmp_rows[2+i] = tmp_rows[1];
880  }
881  }
882 
883 
884  // add virtual values for schur complement allocation
885  switch (n_schur_compls) {
886  case 2:
887  // Connections between edges of N+1 dim. elements neighboring with actual N dim element 'ele'
888  OLD_ASSERT(ele->n_neighs_vb*ele->n_neighs_vb<1000, "Too many values in E block.");
889  ls->mat_set_values(ele->n_neighs_vb, tmp_rows+2,
890  ele->n_neighs_vb, tmp_rows+2, zeros);
891 
892  case 1: // included also for case 2
893  // -(C')*(A-)*B block and its transpose conect edge with its elements
894  ls->mat_set_values(1, &el_row, nsides, edge_rows, zeros);
895  ls->mat_set_values(nsides, edge_rows, 1, &el_row, zeros);
896  // -(C')*(A-)*C block conect all edges of every element
897  ls->mat_set_values(nsides, edge_rows, nsides, edge_rows, zeros);
898  }
899  }
900 
901  if (balance_ != nullptr)
902  balance_->finish_flux_assembly(water_balance_idx_);
903 
905 
906 
907  if (mortar_method_ == MortarP0) {
908  P0_CouplingAssembler(*this).assembly(*ls);
909  } else if (mortar_method_ == MortarP1) {
910  P1_CouplingAssembler(*this).assembly(*ls);
911  }
912 }
913 
914 
916 {
917  START_TIMER("assembly source term");
918  if (balance_ != nullptr)
919  balance_->start_source_assembly(water_balance_idx_);
920 
921  for (unsigned int i_loc = 0; i_loc < el_ds->lsize(); i_loc++) {
922 
923  ElementFullIter ele = mesh_->element(el_4_loc[i_loc]);
924  int el_row = row_4_el[el_4_loc[i_loc]];
925 
926  // set sources
927  double source = ele->measure() *
928  data_.cross_section.value(ele->centre(), ele->element_accessor()) *
929  data_.water_source_density.value(ele->centre(), ele->element_accessor());
930  schur0->rhs_set_value(el_row, -1.0 * source );
931 
932  if (balance_ != nullptr)
933  balance_->add_source_rhs_values(water_balance_idx_, ele->region().bulk_idx(), {el_row}, {source});
934  }
935 
936  if (balance_ != nullptr)
937  balance_->finish_source_assembly(water_balance_idx_);
938 }
939 
940 
942  vector<int> &dofs, unsigned int &ele_type, double &delta, arma::vec &dirichlet) {
943 
944  const Element *ele;
945 
946  if (i_ele == (int)(ml_it_->size()) ) { // master element .. 1D
947  ele_type = 0;
948  delta = -delta_0;
949  ele=master_;
950  } else {
951  ele_type = 1;
952  const Intersection &isect=intersections_[ (*ml_it_)[i_ele] ];
953  delta = isect.intersection_true_size();
954  ele = isect.slave_iter();
955  }
956 
957  dofs.resize(ele->n_sides());
958  dirichlet.resize(ele->n_sides());
959  dirichlet.zeros();
960 
961  for(unsigned int i_side=0; i_side < ele->n_sides(); i_side++ ) {
962  dofs[i_side]=darcy_.row_4_edge[ele->side(i_side)->edge_idx()];
963  Boundary * bcd = ele->side(i_side)->cond();
964  if (bcd) {
965  ElementAccessor<3> b_ele = bcd->element_accessor();
966  auto type = (DarcyFlowMH_Steady::EqData::BC_Type)darcy_.data_.bc_type.value(b_ele.centre(), b_ele);
967  //DBGMSG("bcd id: %d sidx: %d type: %d\n", ele->id(), i_side, type);
969  //DBGMSG("Dirichlet: %d\n", ele->index());
970  dofs[i_side] = -dofs[i_side];
971  double bc_pressure = darcy_.data_.bc_pressure.value(b_ele.centre(), b_ele);
972  dirichlet[i_side] = bc_pressure;
973  }
974  }
975  }
976 
977 }
978 
979 /**
980  * Works well but there is large error next to the boundary.
981  */
983  double delta_i, delta_j;
984  arma::mat product;
985  arma::vec dirichlet_i, dirichlet_j;
986  unsigned int ele_type_i, ele_type_j;
987 
988  unsigned int i,j;
989  vector<int> dofs_i,dofs_j;
990 
991  for(ml_it_ = master_list_.begin(); ml_it_ != master_list_.end(); ++ml_it_) {
992 
993  if (ml_it_->size() == 0) continue; // skip empty masters
994 
995 
996  // on the intersection element we consider
997  // * intersection dofs for master and slave
998  // those are dofs of the space into which we interpolate
999  // base functions from individual master and slave elements
1000  // For the master dofs both are usualy eqivalent.
1001  // * original dofs - for master same as intersection dofs, for slave
1002  // all dofs of slave elements
1003 
1004  // form list of intersection dofs, in this case pressures in barycenters
1005  // but we do not use those form MH system in order to allow second schur somplement. We rather map these
1006  // dofs to pressure traces, i.e. we use averages of traces as barycentric values.
1007 
1008 
1009  master_ = intersections_[ml_it_->front()].master_iter();
1010  delta_0 = master_->measure();
1011 
1012  double master_sigma=darcy_.data_.sigma.value( master_->centre(), master_->element_accessor());
1013 
1014  // rows
1015  double check_delta_sum=0;
1016  for(i = 0; i <= ml_it_->size(); ++i) {
1017  pressure_diff(i, dofs_i, ele_type_i, delta_i, dirichlet_i);
1018  check_delta_sum+=delta_i;
1019  //columns
1020  for (j = 0; j <= ml_it_->size(); ++j) {
1021  pressure_diff(j, dofs_j, ele_type_j, delta_j, dirichlet_j);
1022 
1023  double scale = -master_sigma * delta_i * delta_j / delta_0;
1024  product = scale * tensor_average[ele_type_i][ele_type_j];
1025 
1026  arma::vec rhs(dofs_i.size());
1027  rhs.zeros();
1028  ls.set_values( dofs_i, dofs_j, product, rhs, dirichlet_i, dirichlet_j);
1029  auto dofs_i_cp=dofs_i;
1030  auto dofs_j_cp=dofs_j;
1031  ls.set_values( dofs_i_cp, dofs_j_cp, product, rhs, dirichlet_i, dirichlet_j);
1032  }
1033  }
1034  OLD_ASSERT(check_delta_sum < 1E-5*delta_0, "sum err %f > 0\n", check_delta_sum/delta_0);
1035  } // loop over master elements
1036 }
1037 
1038 
1039 
1040  void P1_CouplingAssembler::add_sides(const Element * ele, unsigned int shift, vector<int> &dofs, vector<double> &dirichlet)
1041  {
1042 
1043  for(unsigned int i_side=0; i_side < ele->n_sides(); i_side++ ) {
1044  dofs[shift+i_side] = darcy_.row_4_edge[ele->side(i_side)->edge_idx()];
1045  Boundary * bcd = ele->side(i_side)->cond();
1046 
1047  if (bcd) {
1048  ElementAccessor<3> b_ele = bcd->element_accessor();
1049  auto type = (DarcyFlowMH_Steady::EqData::BC_Type)darcy_.data_.bc_type.value(b_ele.centre(), b_ele);
1050  //DBGMSG("bcd id: %d sidx: %d type: %d\n", ele->id(), i_side, type);
1051  if (type == DarcyFlowMH_Steady::EqData::dirichlet) {
1052  //DBGMSG("Dirichlet: %d\n", ele->index());
1053  dofs[shift + i_side] = -dofs[shift + i_side];
1054  double bc_pressure = darcy_.data_.bc_pressure.value(b_ele.centre(), b_ele);
1055  dirichlet[shift + i_side] = bc_pressure;
1056  }
1057  }
1058  }
1059  }
1060 
1061 
1062 /**
1063  * P1 connection of different dimensions
1064  *
1065  * - 20.11. 2014 - very poor convergence, big error in pressure even at internal part of the fracture
1066  */
1067 
1068 void P1_CouplingAssembler::assembly(LinSys &ls) {
1069 
1070  for (const Intersection &intersec : intersections_) {
1071  const Element * master = intersec.master_iter();
1072  const Element * slave = intersec.slave_iter();
1073 
1074  add_sides(slave, 0, dofs, dirichlet);
1075  add_sides(master, 3, dofs, dirichlet);
1076 
1077  double master_sigma=darcy_.data_.sigma.value( master->centre(), master->element_accessor());
1078 
1079 /*
1080  * Local coordinates on 1D
1081  * t0
1082  * node 0: 0.0
1083  * node 1: 1.0
1084  *
1085  * base fce points
1086  * t0 = 0.0 on side 0 node 0
1087  * t0 = 1.0 on side 1 node 1
1088  *
1089  * Local coordinates on 2D
1090  * t0 t1
1091  * node 0: 0.0 0.0
1092  * node 1: 1.0 0.0
1093  * node 2: 0.0 1.0
1094  *
1095  * base fce points
1096  * t0=0.5, t1=0.0 on side 0 nodes (0,1)
1097  * t0=0.5, t1=0.5 on side 1 nodes (1,2)
1098  * t0=0.0, t1=0.5 on side 2 nodes (2,0)
1099  */
1100 
1101 
1102 
1103  arma::vec point_Y(1);
1104  point_Y.fill(1.0);
1105  arma::vec point_2D_Y(intersec.map_to_slave(point_Y)); // local coordinates of Y on slave (1, t0, t1)
1106  arma::vec point_1D_Y(intersec.map_to_master(point_Y)); // local coordinates of Y on master (1, t0)
1107 
1108  arma::vec point_X(1);
1109  point_X.fill(0.0);
1110  arma::vec point_2D_X(intersec.map_to_slave(point_X)); // local coordinates of X on slave (1, t0, t1)
1111  arma::vec point_1D_X(intersec.map_to_master(point_X)); // local coordinates of X on master (1, t0)
1112 
1113  arma::mat base_2D(3, 3);
1114  // basis functions are numbered as sides
1115  // TODO:
1116  // Use RT finite element to evaluate following matrices.
1117 
1118  // Ravirat - Thomas base functions evaluated in points (0,0), (1,0), (0,1)
1119  // 2D RT_i(t0, t1) = a0 + a1*t0 + a2*t1
1120  // a0 a1 a2
1121  base_2D << 1.0 << 0.0 << -2.0 << arma::endr // RT for side 0
1122  << 1.0 << -2.0 << 0.0 << arma::endr // RT for side 1
1123  << -1.0 << 2.0 << 2.0 << arma::endr;// RT for side 2
1124 
1125 
1126  arma::mat base_1D(2, 2);
1127  // Ravirat - Thomas base functions evaluated in points (0,0), (1,0), (0,1)
1128  // 1D RT_i(t0) = a0 + a1 * t0
1129  // a0 a1
1130  base_1D << 1.0 << -1.0 << arma::endr // RT for side 0,
1131  << 0.0 << 1.0 << arma::endr; // RT for side 1,
1132 
1133 
1134 
1135  // Consider both 2D and 1D value are defined for the test function
1136  // related to the each of 5 DOFs involved in the intersection.
1137  // One of these values is always zero.
1138  // Compute difference of the 2D and 1D value for every DOF.
1139  // Compute value of this difference in both endpoints X,Y of the intersection.
1140 
1141  arma::vec difference_in_Y(5);
1142  arma::vec difference_in_X(5);
1143  // slave sides 0,1,2
1144  difference_in_Y.subvec(0, 2) = -base_2D * point_2D_Y;
1145  difference_in_X.subvec(0, 2) = -base_2D * point_2D_X;
1146  // master sides 3,4
1147  difference_in_Y.subvec(3, 4) = base_1D * point_1D_Y;
1148  difference_in_X.subvec(3, 4) = base_1D * point_1D_X;
1149 
1150  // applying the Simpson's rule
1151  // to the product of two linear functions f, g we get
1152  // (b-a)/6 * ( 3*f(a)*g(a) + 3*f(b)*g(b) + 2*f(a)*g(b) + 2*f(b)*g(a) )
1153  arma::mat A(5, 5);
1154  for (int i = 0; i < 5; ++i) {
1155  for (int j = 0; j < 5; ++j) {
1156  A(i, j) = -master_sigma * intersec.intersection_true_size() *
1157  ( difference_in_Y[i] * difference_in_Y[j]
1158  + difference_in_Y[i] * difference_in_X[j]/2
1159  + difference_in_X[i] * difference_in_Y[j]/2
1160  + difference_in_X[i] * difference_in_X[j]
1161  ) * (1.0 / 3.0);
1162 
1163  }
1164  }
1165  auto dofs_cp=dofs;
1166  ls.set_values( dofs_cp, dofs_cp, A, rhs, dirichlet, dirichlet);
1167 
1168  }
1169 }
1170 
1171 
1172 
1173 /*******************************************************************************
1174  * COMPOSE WATER MH MATRIX WITHOUT SCHUR COMPLEMENT
1175  ******************************************************************************/
1176 
1177 void DarcyFlowMH_Steady::create_linear_system(Input::AbstractRecord in_rec) {
1178 
1179  START_TIMER("preallocation");
1180 
1181  if (schur0 == NULL) { // create Linear System for MH matrix
1182 
1183  if (in_rec.type() == LinSys_BDDC::get_input_type()) {
1184 #ifdef FLOW123D_HAVE_BDDCML
1185  xprintf(Warn, "For BDDC no Schur complements are used.");
1186  prepare_parallel_bddc();
1187  n_schur_compls = 0;
1188  LinSys_BDDC *ls = new LinSys_BDDC(global_row_4_sub_row->size(), &(*rows_ds),
1189  3, // 3 == la::BddcmlWrapper::SPD_VIA_SYMMETRICGENERAL
1190  1, // 1 == number of subdomains per process
1191  true); // swap signs of matrix and rhs to make the matrix SPD
1192  ls->set_from_input(in_rec);
1193  ls->set_solution( NULL );
1194  // possible initialization particular to BDDC
1195  START_TIMER("BDDC set mesh data");
1196  set_mesh_data_for_bddc(ls);
1197  schur0=ls;
1198  END_TIMER("BDDC set mesh data");
1199 #else
1200  xprintf(Err, "Flow123d was not build with BDDCML support.\n");
1201 #endif // FLOW123D_HAVE_BDDCML
1202  }
1203  else if (in_rec.type() == LinSys_PETSC::get_input_type()) {
1204  // use PETSC for serial case even when user wants BDDC
1205  if (n_schur_compls > 2) {
1206  xprintf(Warn, "Invalid number of Schur Complements. Using 2.");
1207  n_schur_compls = 2;
1208  }
1209 
1210  LinSys_PETSC *schur1, *schur2;
1211 
1212  if (n_schur_compls == 0) {
1213  LinSys_PETSC *ls = new LinSys_PETSC( &(*rows_ds) );
1214 
1215  // temporary solution; we have to set precision also for sequantial case of BDDC
1216  // final solution should be probably call of direct solver for oneproc case
1217  if (in_rec.type() != LinSys_BDDC::get_input_type()) ls->set_from_input(in_rec);
1218  else {
1219  ls->LinSys::set_from_input(in_rec); // get only common options
1220  }
1221 
1222  ls->set_solution( NULL );
1223  schur0=ls;
1224  } else {
1225  IS is;
1226  ISCreateStride(PETSC_COMM_WORLD, side_ds->lsize(), rows_ds->begin(), 1, &is);
1227  //OLD_ASSERT(err == 0,"Error in ISCreateStride.");
1228 
1229  SchurComplement *ls = new SchurComplement(is, &(*rows_ds));
1230  ls->set_from_input(in_rec);
1231  ls->set_solution( NULL );
1232 
1233  // make schur1
1234  Distribution *ds = ls->make_complement_distribution();
1235  if (n_schur_compls==1) {
1236  schur1 = new LinSys_PETSC(ds);
1237  schur1->set_positive_definite();
1238  } else {
1239  IS is;
1240  ISCreateStride(PETSC_COMM_WORLD, el_ds->lsize(), ls->get_distribution()->begin(), 1, &is);
1241  //OLD_ASSERT(err == 0,"Error in ISCreateStride.");
1242  SchurComplement *ls1 = new SchurComplement(is, ds); // is is deallocated by SchurComplement
1243  ls1->set_negative_definite();
1244 
1245  // make schur2
1246  schur2 = new LinSys_PETSC( ls1->make_complement_distribution() );
1247  schur2->set_positive_definite();
1248  schur2->set_from_input(in_rec);
1249  ls1->set_complement( schur2 );
1250  schur1 = ls1;
1251  }
1252  ls->set_complement( schur1 );
1253  schur0=ls;
1254  }
1255 
1256  START_TIMER("PETSC PREALLOCATION");
1257  schur0->set_symmetric();
1258  schur0->start_allocation();
1259  assembly_steady_mh_matrix(); // preallocation
1260  VecZeroEntries(schur0->get_solution());
1261  END_TIMER("PETSC PREALLOCATION");
1262  }
1263  else {
1264  xprintf(Err, "Unknown solver type. Internal error.\n");
1265  }
1266  }
1267 
1268  END_TIMER("preallocation");
1269  make_serial_scatter();
1270 }
1271 
1272 
1273 
1274 
1275 void DarcyFlowMH_Steady::assembly_linear_system() {
1277 
1278  bool is_steady = data_.storativity.field_result(mesh_->region_db().get_region_set("BULK")) == result_zeros;
1279  //DBGMSG("Assembly linear system\n");
1280  if (data_.changed()) {
1281  //DBGMSG(" Data changed\n");
1282  // currently we have no optimization for cases when just time term data or RHS data are changed
1283  START_TIMER("full assembly");
1284  if (typeid(*schur0) != typeid(LinSys_BDDC)) {
1285  schur0->start_add_assembly(); // finish allocation and create matrix
1286  }
1287  schur0->mat_zero_entries();
1288  schur0->rhs_zero_entries();
1289  assembly_steady_mh_matrix(); // fill matrix
1290  schur0->finish_assembly();
1291  schur0->set_matrix_changed();
1292  //MatView( *const_cast<Mat*>(schur0->get_matrix()), PETSC_VIEWER_STDOUT_WORLD );
1293  //VecView( *const_cast<Vec*>(schur0->get_rhs()), PETSC_VIEWER_STDOUT_WORLD);
1294 
1295 
1296  if (! is_steady) {
1297  START_TIMER("fix time term");
1298  //DBGMSG(" setup time term\n");
1299  // assembly time term and rhs
1300  setup_time_term();
1301  modify_system();
1302  }
1303  else if (balance_ != nullptr)
1304  {
1305  balance_->start_mass_assembly(water_balance_idx_);
1306  balance_->finish_mass_assembly(water_balance_idx_);
1307  }
1308  END_TIMER("full assembly");
1309  } else {
1310  START_TIMER("modify system");
1311  if (! is_steady) {
1312  modify_system();
1313  } else {
1314  //xprintf(PrgErr, "Planned computation time for steady solver, but data are not changed.\n");
1315  }
1316  END_TIMER("modiffy system");
1317  }
1318 
1319 }
1320 
1321 
1322 
1323 void DarcyFlowMH_Steady::set_mesh_data_for_bddc(LinSys_BDDC * bddc_ls) {
1325  // prepare mesh for BDDCML
1326  // initialize arrays
1327  // auxiliary map for creating coordinates of local dofs and global-to-local numbering
1328  std::map<int,arma::vec3> localDofMap;
1329  // connectivity for the subdomain, i.e. global dof numbers on element, stored element-by-element
1330  // Indices of Nodes on Elements
1331  std::vector<int> inet;
1332  // number of degrees of freedom on elements - determines elementwise chunks of INET array
1333  // Number of Nodes on Elements
1334  std::vector<int> nnet;
1335  // Indices of Subdomain Elements in Global Numbering - for local elements, their global indices
1336  std::vector<int> isegn;
1337 
1338  // This array is currently not used in BDDCML, it was used as an interface scaling alternative to scaling
1339  // by diagonal. It corresponds to the rho-scaling.
1340  std::vector<double> element_permeability;
1341 
1342  // maximal and minimal dimension of elements
1343  int elDimMax = 1;
1344  int elDimMin = 3;
1345  for ( unsigned int i_loc = 0; i_loc < el_ds->lsize(); i_loc++ ) {
1346  // for each element, create local numbering of dofs as fluxes (sides), pressure (element centre), Lagrange multipliers (edges), compatible connections
1347  ElementFullIter el = mesh_->element(el_4_loc[i_loc]);
1348  int e_idx = el.index();
1349 
1350  int elDim = el->dim();
1351  elDimMax = std::max( elDimMax, elDim );
1352  elDimMin = std::min( elDimMin, elDim );
1353 
1354  isegn.push_back( e_idx );
1355  int nne = 0;
1356 
1357  FOR_ELEMENT_SIDES(el,si) {
1358  // insert local side dof
1359  int side_row = side_row_4_id[ mh_dh.side_dof( el->side(si) ) ];
1360  arma::vec3 coord = el->side(si)->centre();
1361 
1362  localDofMap.insert( std::make_pair( side_row, coord ) );
1363  inet.push_back( side_row );
1364  nne++;
1365  }
1366 
1367  // insert local pressure dof
1368  int el_row = row_4_el[ el_4_loc[i_loc] ];
1369  arma::vec3 coord = el->centre();
1370  localDofMap.insert( std::make_pair( el_row, coord ) );
1371  inet.push_back( el_row );
1372  nne++;
1373 
1374  FOR_ELEMENT_SIDES(el,si) {
1375  // insert local edge dof
1376  int edge_row = row_4_edge[ el->side(si)->edge_idx() ];
1377  arma::vec3 coord = el->side(si)->centre();
1378 
1379  localDofMap.insert( std::make_pair( edge_row, coord ) );
1380  inet.push_back( edge_row );
1381  nne++;
1382  }
1383 
1384  // insert dofs related to compatible connections
1385  for ( unsigned int i_neigh = 0; i_neigh < el->n_neighs_vb; i_neigh++) {
1386  int edge_row = row_4_edge[ el->neigh_vb[i_neigh]->edge_idx() ];
1387  arma::vec3 coord = el->neigh_vb[i_neigh]->edge()->side(0)->centre();
1388 
1389  localDofMap.insert( std::make_pair( edge_row, coord ) );
1390  inet.push_back( edge_row );
1391  nne++;
1392  }
1393 
1394  nnet.push_back( nne );
1395 
1396  // version for rho scaling
1397  // trace computation
1398  arma::vec3 centre = el->centre();
1399  double conduct = data_.conductivity.value( centre , el->element_accessor() );
1400  arma::mat33 aniso = data_.anisotropy.value( centre, el->element_accessor() );
1401 
1402  // compute mean on the diagonal
1403  double coef = 0.;
1404  for ( int i = 0; i < 3; i++) {
1405  coef = coef + aniso.at(i,i);
1406  }
1407  // Maybe divide by cs
1408  coef = conduct*coef / 3;
1409 
1410  OLD_ASSERT( coef > 0.,
1411  "Zero coefficient of hydrodynamic resistance %f . \n ", coef );
1412  element_permeability.push_back( 1. / coef );
1413  }
1414  //convert set of dofs to vectors
1415  // number of nodes (= dofs) on the subdomain
1416  int numNodeSub = localDofMap.size();
1417  OLD_ASSERT_EQUAL( (unsigned int)numNodeSub, global_row_4_sub_row->size() );
1418  // Indices of Subdomain Nodes in Global Numbering - for local nodes, their global indices
1419  std::vector<int> isngn( numNodeSub );
1420  // pseudo-coordinates of local nodes (i.e. dofs)
1421  // they need not be exact, they are used just for some geometrical considerations in BDDCML,
1422  // such as selection of corners maximizing area of a triangle, bounding boxes fro subdomains to
1423  // find candidate neighbours etc.
1424  std::vector<double> xyz( numNodeSub * 3 ) ;
1425  int ind = 0;
1426  std::map<int,arma::vec3>::iterator itB = localDofMap.begin();
1427  for ( ; itB != localDofMap.end(); ++itB ) {
1428  isngn[ind] = itB -> first;
1429 
1430  arma::vec3 coord = itB -> second;
1431  for ( int j = 0; j < 3; j++ ) {
1432  xyz[ j*numNodeSub + ind ] = coord[j];
1433  }
1434 
1435  ind++;
1436  }
1437  localDofMap.clear();
1438 
1439  // Number of Nodal Degrees of Freedom
1440  // nndf is trivially one - dofs coincide with nodes
1441  std::vector<int> nndf( numNodeSub, 1 );
1442 
1443  // prepare auxiliary map for renumbering nodes
1444  typedef std::map<int,int> Global2LocalMap_; //! type for storage of global to local map
1445  Global2LocalMap_ global2LocalNodeMap;
1446  for ( unsigned ind = 0; ind < isngn.size(); ++ind ) {
1447  global2LocalNodeMap.insert( std::make_pair( static_cast<unsigned>( isngn[ind] ), ind ) );
1448  }
1449 
1450  // renumber nodes in the inet array to locals
1451  int indInet = 0;
1452  for ( unsigned int iEle = 0; iEle < isegn.size(); iEle++ ) {
1453  int nne = nnet[ iEle ];
1454  for ( int ien = 0; ien < nne; ien++ ) {
1455 
1456  int indGlob = inet[indInet];
1457  // map it to local node
1458  Global2LocalMap_::iterator pos = global2LocalNodeMap.find( indGlob );
1459  OLD_ASSERT( pos != global2LocalNodeMap.end(),
1460  "Cannot remap node index %d to local indices. \n ", indGlob );
1461  int indLoc = static_cast<int> ( pos -> second );
1462 
1463  // store the node
1464  inet[ indInet++ ] = indLoc;
1465  }
1466  }
1467 
1468  int numNodes = size;
1469  int numDofsInt = size;
1470  int spaceDim = 3; // TODO: what is the proper value here?
1471  int meshDim = elDimMax;
1472 
1473  bddc_ls -> load_mesh( spaceDim, numNodes, numDofsInt, inet, nnet, nndf, isegn, isngn, isngn, xyz, element_permeability, meshDim );
1474 }
1475 
1476 
1477 
1478 
1479 //=============================================================================
1480 // DESTROY WATER MH SYSTEM STRUCTURE
1481 //=============================================================================
1482 DarcyFlowMH_Steady::~DarcyFlowMH_Steady() {
1483  if (schur0 != NULL) delete schur0;
1484 
1485  delete edge_ds;
1486 // delete el_ds;
1487  delete side_ds;
1488 
1489 // xfree(el_4_loc);
1490  xfree(row_4_el);
1491  xfree(side_id_4_loc);
1492  xfree(side_row_4_id);
1493  xfree(edge_4_loc);
1494  xfree(row_4_edge);
1495 
1496  if (solution != NULL) {
1497  VecDestroy(&sol_vec);
1498  xfree(solution);
1499  }
1500 
1501  delete output_object;
1502 
1503  VecScatterDestroy(&par_to_all);
1504 
1505 }
1506 
1507 
1508 // ================================================
1509 // PARALLLEL PART
1510 //
1511 
1512 // ========================================================================
1513 // to finish row_4_id arrays we have to convert individual numberings of
1514 // sides/els/edges to whole numbering of rows. To this end we count shifts
1515 // for sides/els/edges on each proc and then we apply them on row_4_id
1516 // arrays.
1517 // we employ macros to avoid code redundancy
1518 // =======================================================================
1519 void DarcyFlowMH_Steady::make_row_numberings() {
1520  int i, shift;
1521  int np = edge_ds->np();
1522  int edge_shift[np], el_shift[np], side_shift[np];
1523  unsigned int rows_starts[np];
1524 
1525  int edge_n_id = mesh_->n_edges(),
1526  el_n_id = mesh_->element.size(),
1527  side_n_id = mesh_->n_sides();
1528 
1529  // compute shifts on every proc
1530  shift = 0; // in this var. we count new starts of arrays chunks
1531  for (i = 0; i < np; i++) {
1532  side_shift[i] = shift - (side_ds->begin(i)); // subtract actual start of the chunk
1533  shift += side_ds->lsize(i);
1534  el_shift[i] = shift - (el_ds->begin(i));
1535  shift += el_ds->lsize(i);
1536  edge_shift[i] = shift - (edge_ds->begin(i));
1537  shift += edge_ds->lsize(i);
1538  rows_starts[i] = shift;
1539  }
1540  // apply shifts
1541  for (i = 0; i < side_n_id; i++) {
1542  int &what = side_row_4_id[i];
1543  if (what >= 0)
1544  what += side_shift[side_ds->get_proc(what)];
1545  }
1546  for (i = 0; i < el_n_id; i++) {
1547  int &what = row_4_el[i];
1548  if (what >= 0)
1549  what += el_shift[el_ds->get_proc(what)];
1550 
1551  }
1552  for (i = 0; i < edge_n_id; i++) {
1553  int &what = row_4_edge[i];
1554  if (what >= 0)
1555  what += edge_shift[edge_ds->get_proc(what)];
1556  }
1557  // make distribution of rows
1558  for (i = np - 1; i > 0; i--)
1559  rows_starts[i] -= rows_starts[i - 1];
1560 
1561  rows_ds = boost::make_shared<Distribution>(&(rows_starts[0]), PETSC_COMM_WORLD);
1562 }
1563 
1564 void DarcyFlowMH_Steady::make_serial_scatter() {
1565  START_TIMER("prepare scatter");
1566  // prepare Scatter form parallel to sequantial in original numbering
1567  {
1568  IS is_loc;
1569  int i, *loc_idx; //, si;
1570 
1571  // create local solution vector
1572  solution = (double *) xmalloc(size * sizeof(double));
1573  VecCreateSeqWithArray(PETSC_COMM_SELF,1, size, solution,
1574  &(sol_vec));
1575 
1576  // create seq. IS to scatter par solutin to seq. vec. in original order
1577  // use essentialy row_4_id arrays
1578  loc_idx = (int *) xmalloc(size * sizeof(int));
1579  i = 0;
1580  FOR_ELEMENTS(mesh_, ele) {
1581  FOR_ELEMENT_SIDES(ele,si) {
1582  loc_idx[i++] = side_row_4_id[ mh_dh.side_dof( ele->side(si) ) ];
1583  }
1584  }
1585  FOR_ELEMENTS(mesh_, ele) {
1586  loc_idx[i++] = row_4_el[ele.index()];
1587  }
1588  for(unsigned int i_edg=0; i_edg < mesh_->n_edges(); i_edg++) {
1589  loc_idx[i++] = row_4_edge[i_edg];
1590  }
1591  OLD_ASSERT( i==size,"Size of array does not match number of fills.\n");
1592  //DBGPRINT_INT("loc_idx",size,loc_idx);
1593  ISCreateGeneral(PETSC_COMM_SELF, size, loc_idx, PETSC_COPY_VALUES, &(is_loc));
1594  xfree(loc_idx);
1595  VecScatterCreate(schur0->get_solution(), is_loc, sol_vec,
1596  PETSC_NULL, &par_to_all);
1597  ISDestroy(&(is_loc));
1598  }
1599  solution_changed_for_scatter=true;
1600 
1601  END_TIMER("prepare scatter");
1602 
1603 }
1604 
1605 // ====================================================================================
1606 // - compute optimal edge partitioning
1607 // - compute appropriate partitioning of elements and sides
1608 // - make arrays: *_id_4_loc and *_row_4_id to allow parallel assembly of the MH matrix
1609 // ====================================================================================
1610 void DarcyFlowMH_Steady::prepare_parallel() {
1611 
1612  START_TIMER("prepare parallel");
1613 
1614  int *loc_part; // optimal (edge,el) partitioning (local chunk)
1615  int *id_4_old; // map from old idx to ids (edge,el)
1616  int loc_i;
1617 
1618  int e_idx;
1619 
1620 
1621  //ierr = MPI_Barrier(PETSC_COMM_WORLD);
1622  //OLD_ASSERT(ierr == 0, "Error in MPI_Barrier.");
1623 
1624  // row_4_el will be modified so we make a copy of the array from mesh
1625  row_4_el = new int[mesh_->n_elements()];
1626  std::copy(mesh_->get_row_4_el(), mesh_->get_row_4_el()+mesh_->n_elements(), row_4_el);
1627  el_4_loc = mesh_->get_el_4_loc();
1628  el_ds = mesh_->get_el_ds();
1629 
1630  //optimal element part; loc. els. id-> new el. numbering
1631  Distribution init_edge_ds(DistributionLocalized(), mesh_->n_edges(), PETSC_COMM_WORLD);
1632  // partitioning of edges, edge belongs to the proc of his first element
1633  // this is not optimal but simple
1634  loc_part = new int[init_edge_ds.lsize()];
1635  id_4_old = new int[mesh_->n_edges()];
1636  {
1637  loc_i = 0;
1638  FOR_EDGES(mesh_, edg ) {
1639  unsigned int i_edg = edg - mesh_->edges.begin();
1640  // partition
1641  e_idx = mesh_->element.index(edg->side(0)->element());
1642  if (init_edge_ds.is_local(i_edg)) {
1643  // find (new) proc of the first element of the edge
1644  loc_part[loc_i++] = el_ds->get_proc(row_4_el[e_idx]);
1645  }
1646  // id array
1647  id_4_old[i_edg] = i_edg;
1648  }
1649  }
1650 
1651  Partitioning::id_maps(mesh_->n_edges(), id_4_old, init_edge_ds, loc_part, edge_ds, edge_4_loc, row_4_edge);
1652  delete[] loc_part;
1653  delete[] id_4_old;
1654 
1655  //optimal side part; loc. sides; id-> new side numbering
1656  Distribution init_side_ds(DistributionBlock(), mesh_->n_sides(), PETSC_COMM_WORLD);
1657  // partitioning of sides follows elements
1658  loc_part = new int[init_side_ds.lsize()];
1659  id_4_old = new int[mesh_->n_sides()];
1660  {
1661  int is = 0;
1662  loc_i = 0;
1663  FOR_SIDES(mesh_, side ) {
1664  // partition
1665  if (init_side_ds.is_local(is)) {
1666  // find (new) proc of the element of the side
1667  loc_part[loc_i++] = el_ds->get_proc(
1668  row_4_el[mesh_->element.index(side->element())]);
1669  }
1670  // id array
1671  id_4_old[is++] = mh_dh.side_dof( side );
1672  }
1673  }
1674 
1675  Partitioning::id_maps(mesh_->n_sides(), id_4_old, init_side_ds, loc_part, side_ds,
1676  side_id_4_loc, side_row_4_id);
1677  delete [] loc_part;
1678  delete [] id_4_old;
1679 
1680  // convert row_4_id arrays from separate numberings to global numbering of rows
1681  make_row_numberings();
1682 
1683  // Initialize bc_switch_dirichlet to size of global boundary.
1684  bc_switch_dirichlet.resize(mesh_->bc_elements.size(), 1);
1685 }
1686 
1687 void DarcyFlowMH_Steady::prepare_parallel_bddc() {
1688 #ifdef FLOW123D_HAVE_BDDCML
1689  // auxiliary
1690  Element *el;
1691  int side_row, edge_row;
1692 
1693  global_row_4_sub_row = boost::make_shared<LocalToGlobalMap>(rows_ds);
1694 
1695  //
1696  // ordering of dofs
1697  // for each subdomain:
1698  // | velocities (at sides) | pressures (at elements) | L. mult. (at edges) |
1699  for (unsigned int i_loc = 0; i_loc < el_ds->lsize(); i_loc++) {
1700  el = mesh_->element(el_4_loc[i_loc]);
1701  int el_row = row_4_el[el_4_loc[i_loc]];
1702 
1703  global_row_4_sub_row->insert( el_row );
1704 
1705  unsigned int nsides = el->n_sides();
1706  for (unsigned int i = 0; i < nsides; i++) {
1707  side_row = side_row_4_id[ mh_dh.side_dof( el->side(i) ) ];
1708  edge_row = row_4_edge[el->side(i)->edge_idx()];
1709 
1710  global_row_4_sub_row->insert( side_row );
1711  global_row_4_sub_row->insert( edge_row );
1712  }
1713 
1714  for (unsigned int i_neigh = 0; i_neigh < el->n_neighs_vb; i_neigh++) {
1715  // mark this edge
1716  edge_row = row_4_edge[el->neigh_vb[i_neigh]->edge_idx() ];
1717  global_row_4_sub_row->insert( edge_row );
1718  }
1719  }
1720  global_row_4_sub_row->finalize();
1721 #endif // FLOW123D_HAVE_BDDCML
1722 }
1723 
1724 /*
1725 void mat_count_off_proc_values(Mat m, Vec v) {
1726  int n, first, last;
1727  const PetscInt *cols;
1728  Distribution distr(v);
1729 
1730  int n_off = 0;
1731  int n_on = 0;
1732  int n_off_rows = 0;
1733  MatGetOwnershipRange(m, &first, &last);
1734  for (int row = first; row < last; row++) {
1735  MatGetRow(m, row, &n, &cols, PETSC_NULL);
1736  bool exists_off = false;
1737  for (int i = 0; i < n; i++)
1738  if (distr.get_proc(cols[i]) != distr.myp())
1739  n_off++, exists_off = true;
1740  else
1741  n_on++;
1742  if (exists_off)
1743  n_off_rows++;
1744  MatRestoreRow(m, row, &n, &cols, PETSC_NULL);
1745  }
1746 }
1747 */
1748 
1749 
1750 
1751 
1752 
1753 
1754 
1755 
1756 
1757 
1758 
1759 // ========================
1760 // unsteady
1761 /*
1762 DarcyFlowMH_Steady::DarcyFlowMH_Unsteady(Mesh &mesh_in, const Input::Record in_rec)
1763  : DarcyFlowMH_Steady(mesh_in, in_rec)
1764 {
1765 
1766  time_ = new TimeGovernor(in_rec.val<Input::Record>("time"));
1767  data_.mark_input_times(this->mark_type());
1768  data_.set_time(time_->step(), LimitSide::right);
1769 
1770  output_object = new DarcyFlowMHOutput(this, in_rec.val<Input::Record>("output"));
1771  //balance_->units(output_object->get_output_fields().field_ele_pressure.units()*data_.cross_section.units()*data_.storativity.units());
1772 
1773  //time_->fix_dt_until_mark();
1774  create_linear_system();
1775 
1776 
1777 
1778  assembly_linear_system();
1779  read_init_condition();
1780 
1781  output_data();
1782 }
1783 */
1784 
1785 void DarcyFlowMH_Steady::read_initial_condition()
1786 {
1787 
1788  VecDuplicate(schur0->get_solution(), &previous_solution);
1789  VecCreateMPI(PETSC_COMM_WORLD,rows_ds->lsize(),PETSC_DETERMINE,&(steady_diagonal));
1790  VecDuplicate(steady_diagonal,& new_diagonal);
1791  VecZeroEntries(new_diagonal);
1792  VecDuplicate(*( schur0->get_rhs()), &steady_rhs);
1793 
1794  // read inital condition
1795  VecZeroEntries(schur0->get_solution());
1796 
1797  double *local_sol = schur0->get_solution_array();
1798 
1799  // cycle over local element rows
1800  ElementFullIter ele = ELEMENT_FULL_ITER(mesh_, NULL);
1801 
1802  DBGMSG("Setup with dt: %f\n",time_->dt());
1803  for (unsigned int i_loc_el = 0; i_loc_el < el_ds->lsize(); i_loc_el++) {
1804  ele = mesh_->element(el_4_loc[i_loc_el]);
1805  int i_loc_row = i_loc_el + side_ds->lsize();
1806 
1807  // set initial condition
1808  local_sol[i_loc_row] = data_.init_pressure.value(ele->centre(),ele->element_accessor());
1809  }
1810 
1811  solution_changed_for_scatter=true;
1812 
1813 }
1814 
1815 void DarcyFlowMH_Steady::setup_time_term() {
1816  // save diagonal of steady matrix
1817  MatGetDiagonal(*( schur0->get_matrix() ), steady_diagonal);
1818  // save RHS
1819  VecCopy(*( schur0->get_rhs()), steady_rhs);
1820 
1821 
1822  PetscScalar *local_diagonal;
1823  VecGetArray(new_diagonal,& local_diagonal);
1824 
1825  ElementFullIter ele = ELEMENT_FULL_ITER(mesh_, NULL);
1826  DBGMSG("Setup with dt: %f\n",time_->dt());
1827 
1828  if (balance_ != nullptr)
1829  balance_->start_mass_assembly(water_balance_idx_);
1830 
1831  for (unsigned int i_loc_el = 0; i_loc_el < el_ds->lsize(); i_loc_el++) {
1832  ele = mesh_->element(el_4_loc[i_loc_el]);
1833  int i_loc_row = i_loc_el + side_ds->lsize();
1834 
1835  // set new diagonal
1836  double diagonal_coeff = data_.cross_section.value(ele->centre(), ele->element_accessor())
1837  * data_.storativity.value(ele->centre(), ele->element_accessor())
1838  * ele->measure();
1839  local_diagonal[i_loc_row]= - diagonal_coeff / time_->dt();
1840 
1841  if (balance_ != nullptr)
1842  balance_->add_mass_matrix_values(water_balance_idx_, ele->region().bulk_idx(), {i_loc_row}, {diagonal_coeff});
1843  }
1844  VecRestoreArray(new_diagonal,& local_diagonal);
1845  MatDiagonalSet(*( schur0->get_matrix() ), new_diagonal, ADD_VALUES);
1846 
1847  solution_changed_for_scatter=true;
1848  schur0->set_matrix_changed();
1849 
1850  if (balance_ != nullptr)
1851  balance_->finish_mass_assembly(water_balance_idx_);
1852 }
1853 
1854 void DarcyFlowMH_Steady::modify_system() {
1855  START_TIMER("modify system");
1856  if (time_->is_changed_dt() && time_->step().index()>0) {
1857  double scale_factor=time_->step(-2).length()/time_->step().length();
1858  if (scale_factor != 1.0) {
1859  // if time step has changed and setup_time_term not called
1860  MatDiagonalSet(*( schur0->get_matrix() ),steady_diagonal, INSERT_VALUES);
1861 
1862  VecScale(new_diagonal, time_->last_dt()/time_->dt());
1863  MatDiagonalSet(*( schur0->get_matrix() ),new_diagonal, ADD_VALUES);
1864  schur0->set_matrix_changed();
1865  }
1866  }
1867 
1868  // modify RHS - add previous solution
1869  VecPointwiseMult(*( schur0->get_rhs()), new_diagonal, schur0->get_solution());
1870  VecAXPY(*( schur0->get_rhs()), 1.0, steady_rhs);
1871  schur0->set_rhs_changed();
1872 
1873  // swap solutions
1874  VecSwap(previous_solution, schur0->get_solution());
1875 }
1876 
1877 
1878 //-----------------------------------------------------------------------------
1879 // vim: set cindent:
TimeGovernor & time()
Definition: equation.hh:146
FieldSet & data()
Definition: equation.hh:191
void set_input_list(Input::Array input_list)
Definition: field_set.hh:180
Class MappingP1 implements the affine transformation of the unit cell onto the actual cell...
Shape function values.
Definition: update_flags.hh:87
FieldSet * eq_data_
Definition: equation.hh:230
static const Input::Type::Record & get_input_type()
Main balance input record type.
Definition: balance.cc:42
void reinit(Mesh *mesh)
BCField< 3, FieldValue< 3 >::Scalar > bc_robin_sigma
#define ADD_FIELD(name,...)
Definition: field_set.hh:275
double measure() const
Definition: elements.cc:89
Output class for darcy_flow_mh model.
Accessor to input data conforming to declared Array.
Definition: accessors.hh:552
unsigned int max_n_it_
Field< 3, FieldValue< 3 >::Scalar > water_source_density
static const int registrar
Registrar of class to factory.
Definition: system.hh:59
unsigned int size() const
Returns number of keys in the Record.
Definition: type_record.hh:578
Solver based on the original PETSc solver using MPIAIJ matrix and succesive Schur complement construc...
void make_intersec_elements()
Definition: mesh.cc:661
int tlevel() const
Class Input::Type::Default specifies default value of keys of a Input::Type::Record.
Definition: type_record.hh:50
void get_parallel_solution_vector(Vec &vector) override
Class for declaration of the input of type Bool.
Definition: type_base.hh:429
unsigned int edge_idx() const
Definition: side_impl.hh:60
Field< 3, FieldValue< 3 >::Scalar > cross_section
void set_time(const TimeStep &time, LimitSide limit_side)
Definition: field_set.hh:195
Boundary * cond() const
Definition: side_impl.hh:69
Wrappers for linear systems based on MPIAIJ and MATIS format.
static const Input::Type::Selection & get_bc_type_selection()
Return a Selection corresponding to enum BC_Type.
virtual void rhs_set_values(int nrow, int *rows, double *vals)=0
friend class P1_CouplingAssembler
bool is_end() const
Returns true if the actual time is greater than or equal to the end time.
void assembly(LinSys &ls)
void next_time()
Proceed to the next time according to current estimated time step.
FieldResult field_result(RegionSet region_set) const
Indicates special field states.
Definition: field.impl.hh:330
arma::vec3 centre() const
Definition: sides.cc:122
TimeMark::Type type_output()
Definition: time_marks.hh:190
static Default obligatory()
The factory function to make an empty default value which is obligatory.
Definition: type_record.hh:99
void zero_time_step() override
friend class P0_CouplingAssembler
RegionSet get_region_set(const string &set_name) const
Definition: region.cc:321
#define ELEMENT_FULL_ITER(_mesh_, i)
Definition: mesh.h:416
Definition: mesh.h:99
Iterator< Ret > find(const string &key) const
Class FEValues calculates finite element data on the actual cells such as shape function values...
virtual void read_initial_condition()
boost::shared_ptr< Distribution > rows_ds
bool is_current(const TimeMark::Type &mask) const
virtual double get_solution_precision()=0
const RegionDB & region_db() const
Definition: mesh.h:156
void pressure_diff(int i_ele, vector< int > &dofs, unsigned int &ele_type, double &delta, arma::vec &dirichlet)
const TimeStep & step(int index=-1) const
static const std::string field_descriptor_record_description(const string &record_name)
Definition: field_common.cc:64
unsigned int water_balance_idx_
index of water balance within the Balance object.
Class for declaration of the integral input data.
Definition: type_base.hh:460
void solve_nonlinear()
Solve method common to zero_time_step and update solution.
double t() const
Basic time management functionality for unsteady (and steady) solvers (class Equation).
unsigned int n_sides()
Definition: mesh.cc:173
static TimeMarks & marks()
Basic time management class.
virtual void set_tolerances(double r_tol, double a_tol, unsigned int max_it)=0
Class for declaration of inputs sequences.
Definition: type_base.hh:316
static const Input::Type::Record & get_input_type()
void view(const char *name="") const
static const Input::Type::Record & get_input_type()
BCField< 3, FieldValue< 3 >::Scalar > bc_pressure
void assembly_local_vb(double *local_vb, ElementFullIter ele, Neighbour *ngh) override
bool is_preallocated()
Definition: linsys.hh:521
Assembly explicit Schur complement for the given linear system. Provides method for resolution of the...
void mat_set_value(int row, int col, double val)
Definition: linsys.hh:324
virtual Record & derive_from(Abstract &parent)
Method to derive new Record from an AbstractRecord parent.
Definition: type_record.cc:193
MortarMethod mortar_method_
#define OLD_ASSERT(...)
Definition: global_defs.h:128
bool opt_val(const string &key, Ret &value) const
Transformed quadrature points.
unsigned int n_elements() const
Definition: mesh.h:142
Class for declaration of the input data that are floating point numbers.
Definition: type_base.hh:513
unsigned int dim() const
Definition: side_impl.hh:36
Definition: system.hh:59
virtual void assembly_source_term()
Source term is implemented differently in LMH version.
double * get_solution_array()
Definition: linsys.hh:280
Mesh & mesh()
Definition: equation.hh:174
#define MPI_MIN
Definition: mpi.h:198
unsigned int edge_idx()
FLOW123D_FORCE_LINK_IN_CHILD(darcy_flow_mh)
const Vec & get_solution()
Definition: linsys.hh:256
BCField< 3, FieldValue< 3 >::Enum > bc_type
Definitions of basic Lagrangean finite elements with polynomial shape functions.
SideIter side()
Selection & add_value(const int value, const std::string &key, const std::string &description="")
Adds one new value with name given by key to the Selection.
Accessor to the data with type Type::Record.
Definition: accessors.hh:277
const Ret val(const string &key) const
unsigned int n_sides() const
#define xprintf(...)
Definition: system.hh:87
const Element * slave_iter() const
#define START_TIMER(tag)
Starts a timer with specified tag.
void assembly_local_matrix(arma::mat &local_matrix, ElementFullIter ele) override
MortarMethod
Type of experimental Mortar-like method for non-compatible 1d-2d interaction.
Mesh * mesh_
Definition: equation.hh:221
Element * element()
Definition: boundaries.cc:35
virtual Value::return_type const & value(const Point &p, const ElementAccessor< spacedim > &elm) const
Definition: field.hh:337
MH_DofHandler mh_dh
unsigned int side_dof(const SideIter side) const
arma::vec3 make_element_vector(ElementFullIter ele) override
Record & declare_key(const string &key, std::shared_ptr< TypeBase > type, const Default &default_value, const string &description, TypeBase::attribute_map key_attributes=TypeBase::attribute_map())
Declares a new key of the Record.
Definition: type_record.cc:459
static Input::Type::Abstract & get_input_type()
unsigned int nonlinear_iteration_
SideIter side(const unsigned int loc_index)
const Record & close() const
Close the Record for further declarations of keys.
Definition: type_record.cc:271
Shape function gradients.
Definition: update_flags.hh:95
virtual double solution_precision() const
DarcyFlowMH_Steady(Mesh &mesh, const Input::Record in_rec)
CREATE AND FILL GLOBAL MH MATRIX OF THE WATER MODEL.
void create_linear_system(Input::AbstractRecord rec)
double measure() const
Definition: sides.cc:29
Accessor to the polymorphic input data of a type given by an AbstracRecord object.
Definition: accessors.hh:444
Normal vectors.
unsigned int bc_ele_idx_
Definition: boundaries.h:76
Record & copy_keys(const Record &other)
Copy keys from other record.
Definition: type_record.cc:211
void mark_input_times(const TimeGovernor &tg)
Definition: field_set.hh:210
Definition: system.hh:59
bool is_jump_time()
std::vector< char > bc_switch_dirichlet
Idicator of dirichlet or neumann type of switch boundary conditions.
Support classes for parallel programing.
#define MPI_Allreduce(sendbuf, recvbuf, count, datatype, op, comm)
Definition: mpi.h:612
std::vector< AssemblyBase * > assembly_
void set_values(int nrow, int *rows, int ncol, int *cols, PetscScalar *mat_vals, PetscScalar *rhs_vals)
Set values in the system matrix and values in the right-hand side vector on corresponding rows...
Definition: linsys.hh:341
Distribution * el_ds
const Selection & close() const
Close the Selection, no more values can be added.
double intersection_true_size() const
virtual void postprocess()
postprocess velocity field (add sources)
Input::Record input_record_
Definition: equation.hh:223
ElementFullIter element() const
Definition: side_impl.hh:51
#define MPI_INT
Definition: mpi.h:160
boost::shared_ptr< Balance > balance_
object for calculation and writing the water balance to file.
double dt() const
EqData()
Creation of all fields.
void assembly_steady_mh_matrix()
void add_factory(std::shared_ptr< FactoryBase > factory)
Definition: field.impl.hh:469
friend class DarcyFlowMHOutput
Distributed sparse graphs, partitioning.
static const Input::Type::Selection & get_mh_mortar_selection()
Selection for enum MortarMethod.
Abstract linear system class.
static const Input::Type::Record & get_input_type()
Definitions of particular quadrature rules on simplices.
arma::vec::fixed< spacedim > centre() const
Definition: accessors.hh:91
#define OLD_ASSERT_EQUAL(a, b)
Definition: global_defs.h:130
void set_mesh_data_for_bddc(LinSys_BDDC *bddc_ls)
mixed-hybrid model of linear Darcy flow, possibly unsteady.
unsigned int el_idx() const
Definition: side_impl.hh:80
void set_mesh(const Mesh &mesh)
Definition: field_set.hh:173
Record type proxy class.
Definition: type_record.hh:171
void update_solution() override
virtual void output_data() override
Write computed fields.
Distribution * side_ds
static Input::Type::Abstract & get_input_type()
Definition: linsys.cc:29
virtual void mat_set_values(int nrow, int *rows, int ncol, int *cols, double *vals)=0
Distribution * edge_ds
unsigned int n_edges() const
Definition: mesh.h:150
Class for representation SI units of Fields.
Definition: unit_si.hh:40
#define MPI_Barrier(comm)
Definition: mpi.h:531
DarcyFlowMHOutput * output_object
static UnitSI & dimensionless()
Returns dimensionless unit.
Definition: unit_si.cc:53
virtual double compute_residual()=0
Field< 3, FieldValue< 3 >::Scalar > storativity
#define THROW(whole_exception_expr)
Wrapper for throw. Saves the throwing point.
Definition: exceptions.hh:44
BCField< 3, FieldValue< 3 >::Scalar > bc_flux
BCField< 3, FieldValue< 3 >::Scalar > bc_switch_pressure
Template for classes storing finite set of named values.
Definitions of Raviart-Thomas finite elements.
virtual int solve()=0
ElementAccessor< 3 > element_accessor()
Definition: boundaries.cc:43
void rhs_set_value(int row, double val)
Definition: linsys.hh:336
Solver based on Multilevel BDDC - using corresponding class of OpenFTL package.
TimeGovernor * time_
Definition: equation.hh:222
ElementVector element
Vector of elements of the mesh.
Definition: mesh.h:225
void output()
Calculate values for output.
Transformed quadrature weights.
void get_solution_vector(double *&vec, unsigned int &vec_size) override
unsigned int lsize(int proc) const
get local size