Flow123d  release_2.2.0-914-gf1a3a4f
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/partitioning.hh"
38 #include "la/distribution.hh"
39 #include "la/linsys.hh"
40 #include "la/linsys_PETSC.hh"
41 #include "la/linsys_BDDC.hh"
42 #include "la/schur.hh"
43 //#include "la/sparse_graph.hh"
45 
46 #include "flow/darcy_flow_mh.hh"
47 
49 
50 
51 #include "tools/time_governor.hh"
53 #include "fields/field.hh"
54 #include "fields/field_values.hh"
56 
57 #include "coupling/balance.hh"
58 
59 #include "fields/vec_seq_double.hh"
60 
61 #include "darcy_flow_assembly.hh"
62 
65 
66 
67 FLOW123D_FORCE_LINK_IN_CHILD(darcy_flow_mh);
68 
69 
70 
71 
72 namespace it = Input::Type;
73 
75  return it::Selection("MH_MortarMethod")
76  .add_value(NoMortar, "None", "Mortar space: P0 on elements of lower dimension.")
77  .add_value(MortarP0, "P0", "Mortar space: P0 on elements of lower dimension.")
78  .add_value(MortarP1, "P1", "Mortar space: P1 on intersections, using non-conforming pressures.")
79  .close();
80 }
81 
82 
84  return it::Selection("Flow_Darcy_BC_Type")
85  .add_value(none, "none",
86  "Homogeneous Neumann boundary condition. Zero flux")
87  .add_value(dirichlet, "dirichlet",
88  "Dirichlet boundary condition. "
89  "Specify the pressure head through the ''bc_pressure'' field "
90  "or the piezometric head through the ''bc_piezo_head'' field.")
91  .add_value(total_flux, "total_flux", "Flux boundary condition (combines Neumann and Robin type). "
92  "Water inflow equal to (($ \\delta_d(q_d^N + \\sigma_d (h_d^R - h_d) )$)). "
93  "Specify the water inflow by the 'bc_flux' field, the transition coefficient by 'bc_robin_sigma' "
94  "and the reference pressure head or pieozmetric head through ''bc_pressure'' or ''bc_piezo_head'' respectively.")
95  .add_value(seepage, "seepage",
96  "Seepage face boundary condition. Pressure and inflow bounded from above. Boundary with potential seepage flow "
97  "is described by the pair of inequalities: "
98  "(($h_d \\le h_d^D$)) and (($ -\\boldsymbol q_d\\cdot\\boldsymbol n \\le \\delta q_d^N$)), where the equality holds in at least one of them. "
99  "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_switch_pressure`` (or ``bc_switch_piezo_head``) and ``bc_flux`` respectively."
102  )
103  .add_value(river, "river",
104  "River boundary condition. For the water level above the bedrock, (($H_d > H_d^S$)), the Robin boundary condition is used with the inflow given by: "
105  "(( $ \\delta_d(q_d^N + \\sigma_d(H_d^D - H_d) )$)). For the water level under the bedrock, constant infiltration is used: "
106  "(( $ \\delta_d(q_d^N + \\sigma_d(H_d^D - H_d^S) )$)). Parameters: ``bc_pressure``, ``bc_switch_pressure``, "
107  " ``bc_sigma, ``bc_flux``."
108  )
109  .close();
110 }
111 
113 
114  const it::Record &field_descriptor =
115  it::Record("Flow_Darcy_MH_Data",FieldCommon::field_descriptor_record_description("Flow_Darcy_MH_Data") )
116  .copy_keys( DarcyMH::EqData().make_field_descriptor_type("Flow_Darcy_MH_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  return field_descriptor;
125 }
126 
128 
129  it::Record ns_rec = Input::Type::Record("NonlinearSolver", "Parameters to a non-linear solver.")
130  .declare_key("linear_solver", LinSys::get_input_type(), it::Default("{}"),
131  "Linear solver for MH problem.")
132  .declare_key("tolerance", it::Double(0.0), it::Default("1E-6"),
133  "Residual tolerance.")
134  .declare_key("min_it", it::Integer(0), it::Default("1"),
135  "Minimum number of iterations (linear solves) to use. This is usefull if the convergence criteria "
136  "does not characterize your goal well enough so it converges prematurely possibly without the single linear solve."
137  "If greater then 'max_it' the value is set to 'max_it'.")
138  .declare_key("max_it", it::Integer(0), it::Default("100"),
139  "Maximum number of iterations (linear solves) of the non-linear solver.")
140  .declare_key("converge_on_stagnation", it::Bool(), it::Default("false"),
141  "If a stagnation of the nonlinear solver is detected the solver stops. "
142  "A divergence is reported by default forcing the end of the simulation. Setting this flag to 'true', the solver"
143  "ends with convergence success on stagnation, but report warning about it.")
144  .close();
145 
146  return it::Record("Flow_Darcy_MH", "Mixed-Hybrid solver for STEADY saturated Darcy flow.")
148  .declare_key("gravity", it::Array(it::Double(), 3,3), it::Default("[ 0, 0, -1]"),
149  "Vector of the gravity force. Dimensionless.")
151  "Input data for Darcy flow model.")
152  .declare_key("nonlinear_solver", ns_rec, it::Default("{}"),
153  "Non-linear solver for MH problem.")
154  .declare_key("output_stream", OutputTime::get_input_type(), it::Default("{}"),
155  "Parameters of output stream.")
156 
157  .declare_key("output", DarcyFlowMHOutput::get_input_type(), IT::Default("{ \"fields\": [ \"pressure_p0\", \"velocity_p0\" ] }"),
158  "Parameters of output from MH module.")
160  "Parameters of output form MH module.")
161  .declare_key("balance", Balance::get_input_type(), it::Default("{}"),
162  "Settings for computing mass balance.")
164  "Time governor setting for the unsteady Darcy flow model.")
165  .declare_key("n_schurs", it::Integer(0,2), it::Default("2"),
166  "Number of Schur complements to perform when solving MH system.")
167  .declare_key("mortar_method", get_mh_mortar_selection(), it::Default("\"None\""),
168  "Method for coupling Darcy flow between dimensions." )
169  .close();
170 }
171 
172 
173 const int DarcyMH::registrar =
174  Input::register_class< DarcyMH, Mesh &, const Input::Record >("Flow_Darcy_MH") +
176 
177 
178 
180 {
181  mortar_method_=NoMortar;
182 
183  ADD_FIELD(anisotropy, "Anisotropy of the conductivity tensor.", "1.0" );
184  anisotropy.units( UnitSI::dimensionless() );
185 
186  ADD_FIELD(cross_section, "Complement dimension parameter (cross section for 1D, thickness for 2D).", "1.0" );
187  cross_section.units( UnitSI().m(3).md() );
188 
189  ADD_FIELD(conductivity, "Isotropic conductivity scalar.", "1.0" );
190  conductivity.units( UnitSI().m().s(-1) ).set_limits(0.0);
191 
192  ADD_FIELD(sigma, "Transition coefficient between dimensions.", "1.0");
193  sigma.units( UnitSI::dimensionless() );
194 
195  ADD_FIELD(water_source_density, "Water source density.", "0.0");
196  water_source_density.units( UnitSI().s(-1) );
197 
198  ADD_FIELD(bc_type,"Boundary condition type, possible values:", "\"none\"" );
199  // TODO: temporary solution, we should try to get rid of pointer to the selection after having generic types
200  bc_type.input_selection( get_bc_type_selection() );
201  bc_type.units( UnitSI::dimensionless() );
202 
203  ADD_FIELD(bc_pressure,"Prescribed pressure value on the boundary. Used for all values of 'bc_type' except for 'none' and 'seepage'. "
204  "See documentation of 'bc_type' for exact meaning of 'bc_pressure' in individual boundary condition types.", "0.0");
205  bc_pressure.disable_where(bc_type, {none, seepage} );
206  bc_pressure.units( UnitSI().m() );
207 
208  ADD_FIELD(bc_flux,"Incoming water boundary flux. Used for bc_types : 'total_flux', 'seepage', 'river'.", "0.0");
209  bc_flux.disable_where(bc_type, {none, dirichlet} );
210  bc_flux.units( UnitSI().m(4).s(-1).md() );
211 
212  ADD_FIELD(bc_robin_sigma,"Conductivity coefficient in the 'total_flux' or the 'river' boundary condition type.", "0.0");
213  bc_robin_sigma.disable_where(bc_type, {none, dirichlet, seepage} );
214  bc_robin_sigma.units( UnitSI().m(3).s(-1).md() );
215 
216  ADD_FIELD(bc_switch_pressure,
217  "Critical switch pressure for 'seepage' and 'river' boundary conditions.", "0.0");
218  bc_switch_pressure.disable_where(bc_type, {none, dirichlet, total_flux} );
219  bc_switch_pressure.units( UnitSI().m() );
220 
221  //these are for unsteady
222  ADD_FIELD(init_pressure, "Initial condition for pressure", "0.0" );
223  init_pressure.units( UnitSI().m() );
224 
225  ADD_FIELD(storativity,"Storativity.", "0.0" );
226  storativity.units( UnitSI().m(-1) );
227 
228  //time_term_fields = this->subset({"storativity"});
229  //main_matrix_fields = this->subset({"anisotropy", "conductivity", "cross_section", "sigma", "bc_type", "bc_robin_sigma"});
230  //rhs_fields = this->subset({"water_source_density", "bc_pressure", "bc_flux"});
231 
232 }
233 
234 
235 
236 
237 
238 
239 //=============================================================================
240 // CREATE AND FILL GLOBAL MH MATRIX OF THE WATER MODEL
241 // - do it in parallel:
242 // - initial distribution of elements, edges
243 //
244 /*! @brief CREATE AND FILL GLOBAL MH MATRIX OF THE WATER MODEL
245  *
246  * Parameters {Solver,NSchurs} number of performed Schur
247  * complements (0,1,2) for water flow MH-system
248  *
249  */
250 //=============================================================================
251 DarcyMH::DarcyMH(Mesh &mesh_in, const Input::Record in_rec)
252 : DarcyFlowInterface(mesh_in, in_rec),
253  solution(nullptr),
254  schur0(nullptr),
255  data_changed_(false),
256  output_object(nullptr)
257 {
258 
259  START_TIMER("Darcy constructor");
260  {
261  auto time_record = input_record_.val<Input::Record>("time");
262  //if ( in_rec.opt_val("time", time_record) )
263  time_ = new TimeGovernor(time_record);
264  //else
265  // time_ = new TimeGovernor();
266  }
267 
268  data_ = make_shared<EqData>();
270 
271  data_->is_linear=true;
272 
273  size = mesh_->n_elements() + mesh_->n_sides() + mesh_->n_edges();
274  n_schur_compls = in_rec.val<int>("n_schurs");
275  data_->mortar_method_= in_rec.val<MortarMethod>("mortar_method");
276  if (data_->mortar_method_ != NoMortar) {
278  }
279 
280 
281 
282  //side_ds->view( std::cout );
283  //el_ds->view( std::cout );
284  //edge_ds->view( std::cout );
285  //rows_ds->view( std::cout );
286 
287 }
288 
289 
290 
292 //connecting data fields with mesh
293 {
294 
295  START_TIMER("data init");
296  data_->mesh = mesh_;
297  data_->mh_dh = &mh_dh;
298  data_->set_mesh(*mesh_);
299 
300  auto gravity_array = input_record_.val<Input::Array>("gravity");
301  std::vector<double> gvec;
302  gravity_array.copy_to(gvec);
303  gvec.push_back(0.0); // zero pressure shift
304  data_->gravity_ = arma::vec(gvec);
305  data_->gravity_vec_ = data_->gravity_.subvec(0,2);
306 
307  data_->bc_pressure.add_factory(
308  std::make_shared<FieldAddPotential<3, FieldValue<3>::Scalar>::FieldFactory>
309  (data_->gravity_, "bc_piezo_head") );
310  data_->bc_switch_pressure.add_factory(
311  std::make_shared<FieldAddPotential<3, FieldValue<3>::Scalar>::FieldFactory>
312  (data_->gravity_, "bc_switch_piezo_head") );
313  data_->init_pressure.add_factory(
314  std::make_shared<FieldAddPotential<3, FieldValue<3>::Scalar>::FieldFactory>
315  (data_->gravity_, "init_piezo_head") );
316 
317 
318  data_->set_input_list( this->input_record_.val<Input::Array>("input_fields") );
319  // Check that the time step was set for the transient simulation.
320  if (! zero_time_term(true) && time_->is_default() ) {
321  //THROW(ExcAssertMsg());
322  //THROW(ExcMissingTimeGovernor() << input_record_.ei_address());
323  MessageOut() << "Missing the key 'time', obligatory for the transient problems." << endl;
324  ASSERT(false);
325  }
326 
327  data_->mark_input_times(*time_);
328 }
329 
330 
331 
333 
334  init_eq_data();
336 
337  mh_dh.reinit(mesh_);
338  // Initialize bc_switch_dirichlet to size of global boundary.
339  data_->bc_switch_dirichlet.resize(mesh_->bc_elements.size(), 1);
340 
341 
344  .val<Input::Record>("nonlinear_solver")
345  .val<Input::AbstractRecord>("linear_solver");
346 
347  // auxiliary set_time call since allocation assembly evaluates fields as well
350 
351 
352 
353  // allocate time term vectors
354  VecDuplicate(schur0->get_solution(), &previous_solution);
355  VecCreateMPI(PETSC_COMM_WORLD, mh_dh.rows_ds->lsize(),PETSC_DETERMINE,&(steady_diagonal));
356  VecDuplicate(steady_diagonal,& new_diagonal);
357  VecZeroEntries(new_diagonal);
358  VecDuplicate(steady_diagonal, &steady_rhs);
359 
360 
361  // initialization of balance object
362  balance_ = std::make_shared<Balance>("water", mesh_);
363  balance_->init_from_input(input_record_.val<Input::Record>("balance"), time());
364  data_->water_balance_idx = balance_->add_quantity("water_volume");
365  balance_->allocate(mh_dh.rows_ds->lsize(), 1);
366  balance_->units(UnitSI().m(3));
367 
368 
369  data_->balance = balance_;
370  data_->lin_sys = schur0;
371 
372 
374 }
375 
377 {}
378 
380 {
381 
382  /* TODO:
383  * - Allow solution reconstruction (pressure and velocity) from initial condition on user request.
384  * - Steady solution as an intitial condition may be forced by setting inti_time =-1, and set data for the steady solver in that time.
385  * Solver should be able to switch from and to steady case depending on the zero time term.
386  */
387 
389 
390  // zero_time_term means steady case
391  bool zero_time_term_from_right = zero_time_term();
392 
393 
394  if (zero_time_term_from_right) {
395  // steady case
396  VecZeroEntries(schur0->get_solution());
397  //read_initial_condition(); // Possible solution guess for steady case.
398  use_steady_assembly_ = true;
399  solve_nonlinear(); // with right limit data
400  } else {
401  VecZeroEntries(schur0->get_solution());
402  VecZeroEntries(previous_solution);
404  assembly_linear_system(); // in particular due to balance
405 // print_matlab_matrix("matrix_zero");
406  // TODO: reconstruction of solution in zero time.
407  }
408  //solution_output(T,right_limit); // data for time T in any case
409  output_data();
410 }
411 
412 //=============================================================================
413 // COMPOSE and SOLVE WATER MH System possibly through Schur complements
414 //=============================================================================
416 {
417  START_TIMER("Solving MH system");
418 
419  time_->next_time();
420 
421  time_->view("DARCY"); //time governor information output
423  bool zero_time_term_from_left=zero_time_term();
424 
425  bool jump_time = data_->storativity.is_jump_time();
426  if (! zero_time_term_from_left) {
427  // time term not treated as zero
428  // Unsteady solution up to the T.
429 
430  // this flag is necesssary for switching BC to avoid setting zero neumann on the whole boundary in the steady case
431  use_steady_assembly_ = false;
432  prepare_new_time_step(); //SWAP
433 
434  solve_nonlinear(); // with left limit data
435  if (jump_time) {
436  WarningOut() << "Output of solution discontinuous in time not supported yet.\n";
437  //solution_output(T, left_limit); // output use time T- delta*dt
438  //output_data();
439  }
440  }
441 
442  if (time_->is_end()) {
443  // output for unsteady case, end_time should not be the jump time
444  // but rether check that
445  if (! zero_time_term_from_left && ! jump_time) output_data();
446  return;
447  }
448 
450  bool zero_time_term_from_right=zero_time_term();
451  if (zero_time_term_from_right) {
452  // this flag is necesssary for switching BC to avoid setting zero neumann on the whole boundary in the steady case
453  use_steady_assembly_ = true;
454  solve_nonlinear(); // with right limit data
455 
456  } else if (! zero_time_term_from_left && jump_time) {
457  WarningOut() << "Discontinuous time term not supported yet.\n";
458  //solution_transfer(); // internally call set_time(T, left) and set_time(T,right) again
459  //solve_nonlinear(); // with right limit data
460  }
461  //solution_output(T,right_limit); // data for time T in any case
462  output_data();
463 
464 }
465 
466 bool DarcyMH::zero_time_term(bool time_global) {
467  if (time_global) {
468  return (data_->storativity.input_list_size() == 0);
469  } else {
470  return data_->storativity.field_result(mesh_->region_db().get_region_set("BULK")) == result_zeros;
471  }
472 }
473 
474 
476 {
477 
479  double residual_norm = schur0->compute_residual();
481  MessageOut().fmt("[nonlinear solver] norm of initial residual: {}\n", residual_norm);
482 
483  // Reduce is_linear flag.
484  int is_linear_common;
485  MPI_Allreduce(&(data_->is_linear), &is_linear_common,1, MPI_INT ,MPI_MIN,PETSC_COMM_WORLD);
486 
487  Input::Record nl_solver_rec = input_record_.val<Input::Record>("nonlinear_solver");
488  this->tolerance_ = nl_solver_rec.val<double>("tolerance");
489  this->max_n_it_ = nl_solver_rec.val<unsigned int>("max_it");
490  this->min_n_it_ = nl_solver_rec.val<unsigned int>("min_it");
491  if (this->min_n_it_ > this->max_n_it_) this->min_n_it_ = this->max_n_it_;
492 
493  if (! is_linear_common) {
494  // set tolerances of the linear solver unless they are set by user.
495  schur0->set_tolerances(0.1*this->tolerance_, 0.01*this->tolerance_, 100);
496  }
497  vector<double> convergence_history;
498 
499  Vec save_solution;
500  VecDuplicate(schur0->get_solution(), &save_solution);
501  while (nonlinear_iteration_ < this->min_n_it_ ||
502  (residual_norm > this->tolerance_ && nonlinear_iteration_ < this->max_n_it_ )) {
503  OLD_ASSERT_EQUAL( convergence_history.size(), nonlinear_iteration_ );
504  convergence_history.push_back(residual_norm);
505 
506  // stagnation test
507  if (convergence_history.size() >= 5 &&
508  convergence_history[ convergence_history.size() - 1]/convergence_history[ convergence_history.size() - 2] > 0.9 &&
509  convergence_history[ convergence_history.size() - 1]/convergence_history[ convergence_history.size() - 5] > 0.8) {
510  // stagnation
511  if (input_record_.val<Input::Record>("nonlinear_solver").val<bool>("converge_on_stagnation")) {
512  WarningOut().fmt("Accept solution on stagnation. Its: {} Residual: {}\n", nonlinear_iteration_, residual_norm);
513  break;
514  } else {
515  THROW(ExcSolverDiverge() << EI_Reason("Stagnation."));
516  }
517  }
518 
519  if (! is_linear_common)
520  VecCopy( schur0->get_solution(), save_solution);
523 
524  // hack to make BDDC work with empty compute_residual
525  if (is_linear_common){
526  // we want to print this info in linear (and steady) case
527  residual_norm = schur0->compute_residual();
528  MessageOut().fmt("[nonlinear solver] lin. it: {}, reason: {}, residual: {}\n",
529  si.n_iterations, si.converged_reason, residual_norm);
530  break;
531  }
532  data_changed_=true; // force reassembly for non-linear case
533 
534  double alpha = 1; // how much of new solution
535  VecAXPBY(schur0->get_solution(), (1-alpha), alpha, save_solution);
536 
537  /*
538  double * sol;
539  unsigned int sol_size;
540  get_solution_vector(sol, sol_size);
541  if (mh_dh.el_ds->myp() == 0)
542  VecView(sol_vec, PETSC_VIEWER_STDOUT_SELF);
543  */
544 
545  //LogOut().fmt("Linear solver ended with reason: {} \n", si.converged_reason );
546  //OLD_ASSERT( si.converged_reason >= 0, "Linear solver failed to converge. Convergence reason %d \n", si.converged_reason );
548 
549  residual_norm = schur0->compute_residual();
550  MessageOut().fmt("[nonlinear solver] it: {} lin. it: {}, reason: {}, residual: {}\n",
551  nonlinear_iteration_, si.n_iterations, si.converged_reason, residual_norm);
552  }
553  VecDestroy(&save_solution);
554  this -> postprocess();
555 
556  // adapt timestep
557  if (! this->zero_time_term()) {
558  double mult = 1.0;
559  if (nonlinear_iteration_ < 3) mult = 1.6;
560  if (nonlinear_iteration_ > 7) mult = 0.7;
561  int result = time_->set_upper_constraint(time_->dt() * mult, "Darcy adaptivity.");
562  //DebugOut().fmt("time adaptivity, res: {} it: {} m: {} dt: {} edt: {}\n", result, nonlinear_iteration_, mult, time_->dt(), time_->estimate_dt());
563  }
564 
566 
567 }
568 
569 
571 {
572  //VecSwap(previous_solution, schur0->get_solution());
573 }
574 
576 {
577  START_TIMER("postprocess");
578 
579  //fix velocity when mortar method is used
580  if(data_->mortar_method_ != MortarMethod::NoMortar){
581  auto multidim_assembler = AssemblyBase::create< AssemblyMH >(data_);
582  for (unsigned int i_loc = 0; i_loc < mh_dh.el_ds->lsize(); i_loc++) {
583  auto ele_ac = mh_dh.accessor(i_loc);
584  unsigned int dim = ele_ac.dim();
585  multidim_assembler[dim-1]->fix_velocity(ele_ac);
586  }
587  }
588  //ElementFullIter ele = ELEMENT_FULL_ITER(mesh_, NULL);
589 
590  // modify side fluxes in parallel
591  // for every local edge take time term on digonal and add it to the corresponding flux
592  /*
593  for (unsigned int i_loc = 0; i_loc < el_ds->lsize(); i_loc++) {
594  ele = mesh_->element(el_4_loc[i_loc]);
595  FOR_ELEMENT_SIDES(ele,i) {
596  side_rows[i] = side_row_4_id[ mh_dh.side_dof( ele_ac.side(i) ) ];
597  values[i] = -1.0 * ele_ac.measure() *
598  data.cross_section.value(ele_ac.centre(), ele_ac.element_accessor()) *
599  data.water_source_density.value(ele_ac.centre(), ele_ac.element_accessor()) /
600  ele_ac.n_sides();
601  }
602  VecSetValues(schur0->get_solution(), ele_ac.n_sides(), side_rows, values, ADD_VALUES);
603  }
604  VecAssemblyBegin(schur0->get_solution());
605  VecAssemblyEnd(schur0->get_solution());
606  */
607 }
608 
609 
611  START_TIMER("Darcy output data");
612  //time_->view("DARCY"); //time governor information output
613  this->output_object->output();
614 
615 
616  START_TIMER("Darcy balance output");
617  balance_->calculate_cumulative(data_->water_balance_idx, schur0->get_solution());
618  balance_->calculate_instant(data_->water_balance_idx, schur0->get_solution());
619  balance_->output();
620 }
621 
622 
624 {
625  return schur0->get_solution_precision();
626 }
627 
628 
629 
630 void DarcyMH::get_solution_vector(double * &vec, unsigned int &vec_size)
631 {
632  // TODO: make class for vectors (wrapper for PETSC or other) derived from LazyDependency
633  // and use its mechanism to manage dependency between vectors
635 
636  // scatter solution to all procs
637  VecScatterBegin(par_to_all, schur0->get_solution(), sol_vec, INSERT_VALUES, SCATTER_FORWARD);
638  VecScatterEnd( par_to_all, schur0->get_solution(), sol_vec, INSERT_VALUES, SCATTER_FORWARD);
640  }
641 
642  vec = solution;
643  vec_size = this->size;
644  OLD_ASSERT(vec != NULL, "Requested solution is not allocated!\n");
645 }
646 
648 {
649  vec=schur0->get_solution();
650  OLD_ASSERT(vec != NULL, "Requested solution is not allocated!\n");
651 }
652 
653 
654 // ===========================================================================================
655 //
656 // MATRIX ASSEMBLY - we use abstract assembly routine, where LS Mat/Vec SetValues
657 // are in fact pointers to allocating or filling functions - this is governed by Linsystem roitunes
658 //
659 // =======================================================================================
661 {
662  START_TIMER("DarcyFlowMH_Steady::assembly_steady_mh_matrix");
663 
664  // set auxiliary flag for switchting Dirichlet like BC
665  data_->force_bc_switch = use_steady_assembly_ && (nonlinear_iteration_ == 0);
666  data_->n_schur_compls = n_schur_compls;
667 
668 
669  balance_->start_flux_assembly(data_->water_balance_idx);
670 
671  // TODO: try to move this into balance, or have it in the generic assembler class, that should perform the cell loop
672  // including various pre- and post-actions
673  data_->local_boundary_index=0;
674  for (unsigned int i_loc = 0; i_loc < mh_dh.el_ds->lsize(); i_loc++) {
675  auto ele_ac = mh_dh.accessor(i_loc);
676  unsigned int dim = ele_ac.dim();
677  assembler[dim-1]->assemble(ele_ac);
678  }
679 
680 
681  balance_->finish_flux_assembly(data_->water_balance_idx);
682 
683 }
684 
685 
687 {
688  START_TIMER("DarcyFlowMH_Steady::allocate_mh_matrix");
689 
690  // set auxiliary flag for switchting Dirichlet like BC
691  data_->n_schur_compls = n_schur_compls;
692  LinSys *ls = schur0;
693 
694 
695 
696  int local_dofs[10];
697 
698  // to make space for second schur complement, max. 10 neighbour edges of one el.
699  double zeros[100000];
700  for(int i=0; i<100000; i++) zeros[i] = 0.0;
701 
702  std::vector<int> tmp_rows;
703  tmp_rows.reserve(200);
704 
705  unsigned int nsides, loc_size;
706 
707  for (unsigned int i_loc = 0; i_loc < mh_dh.el_ds->lsize(); i_loc++) {
708  auto ele_ac = mh_dh.accessor(i_loc);
709  nsides = ele_ac.n_sides();
710 
711  //allocate at once matrix [sides,ele,edges]x[sides,ele,edges]
712  loc_size = 1 + 2*nsides;
713  unsigned int i_side = 0;
714 
715  for (; i_side < nsides; i_side++) {
716  local_dofs[i_side] = ele_ac.side_row(i_side);
717  local_dofs[i_side+nsides] = ele_ac.edge_row(i_side);
718  }
719  local_dofs[i_side+nsides] = ele_ac.ele_row();
720  int * edge_rows = local_dofs + nsides;
721  //int ele_row = local_dofs[0];
722 
723  // whole local MH matrix
724  ls->mat_set_values(loc_size, local_dofs, loc_size, local_dofs, zeros);
725 
726 
727  // compatible neighborings rows
728  unsigned int n_neighs = ele_ac.full_iter()->n_neighs_vb;
729  for (unsigned int i = 0; i < n_neighs; i++) {
730  // every compatible connection adds a 2x2 matrix involving
731  // current element pressure and a connected edge pressure
732  Neighbour *ngh = ele_ac.full_iter()->neigh_vb[i];
733  int neigh_edge_row = mh_dh.row_4_edge[ ngh->edge_idx() ];
734  tmp_rows.push_back(neigh_edge_row);
735  //DebugOut() << "CC" << print_var(tmp_rows[i]);
736  }
737 
738  // allocate always also for schur 2
739  ls->mat_set_values(nsides+1, edge_rows, n_neighs, tmp_rows.data(), zeros); // (edges, ele) x (neigh edges)
740  ls->mat_set_values(n_neighs, tmp_rows.data(), nsides+1, edge_rows, zeros); // (neigh edges) x (edges, ele)
741  ls->mat_set_values(n_neighs, tmp_rows.data(), n_neighs, tmp_rows.data(), zeros); // (neigh edges) x (neigh edges)
742 
743  tmp_rows.clear();
744 
745  if (data_->mortar_method_ != NoMortar) {
746  auto &isec_list = mesh_->mixed_intersections().element_intersections_[ele_ac.ele_global_idx()];
747  for(auto &isec : isec_list ) {
748  IntersectionLocalBase *local = isec.second;
749  Element &slave_ele = mesh_->element[local->bulk_ele_idx()];
750  //DebugOut().fmt("Alloc: {} {}", ele_ac.ele_global_idx(), local->bulk_ele_idx());
751  for(unsigned int i_side=0; i_side < slave_ele.n_sides(); i_side++) {
752  tmp_rows.push_back( mh_dh.row_4_edge[ slave_ele.side(i_side)->edge_idx() ] );
753  //DebugOut() << "aedge" << print_var(tmp_rows[i_rows-1]);
754  }
755  }
756  }
757  /*
758  for(unsigned int i_side=0; i_side < ele_ac.full_iter()->n_sides(); i_side++) {
759  DebugOut() << "aedge:" << print_var(edge_rows[i_side]);
760  }*/
761 
762  ls->mat_set_values(nsides, edge_rows, tmp_rows.size(), tmp_rows.data(), zeros); // master edges x neigh edges
763  ls->mat_set_values(tmp_rows.size(), tmp_rows.data(), nsides, edge_rows, zeros); // neigh edges x master edges
764  ls->mat_set_values(tmp_rows.size(), tmp_rows.data(), tmp_rows.size(), tmp_rows.data(), zeros); // neigh edges x neigh edges
765 
766  }
767 /*
768  // alloc edge diagonal entries
769  if(rank == 0)
770  FOR_EDGES(mesh_, edg){
771  int edg_idx = mh_dh.row_4_edge[edg->side(0)->edge_idx()];
772 
773 // FOR_EDGES(mesh_, edg2){
774 // int edg_idx2 = mh_dh.row_4_edge[edg2->side(0)->edge_idx()];
775 // if(edg_idx == edg_idx2){
776 // DBGCOUT(<< "P[ " << rank << " ] " << "edg alloc: " << edg_idx << " " << edg_idx2 << "\n");
777  ls->mat_set_value(edg_idx, edg_idx, 0.0);
778 // }
779 // }
780  }
781  */
782  /*
783  if (mortar_method_ == MortarP0) {
784  P0_CouplingAssembler(*this).assembly(*ls);
785  } else if (mortar_method_ == MortarP1) {
786  P1_CouplingAssembler(*this).assembly(*ls);
787  }*/
788 }
789 
791 {
792  START_TIMER("assembly source term");
793  balance_->start_source_assembly(data_->water_balance_idx);
794 
795  for (unsigned int i_loc = 0; i_loc < mh_dh.el_ds->lsize(); i_loc++) {
796  auto ele_ac = mh_dh.accessor(i_loc);
797 
798  double cs = data_->cross_section.value(ele_ac.centre(), ele_ac.element_accessor());
799 
800  // set sources
801  double source = ele_ac.measure() * cs *
802  data_->water_source_density.value(ele_ac.centre(), ele_ac.element_accessor());
803  schur0->rhs_set_value(ele_ac.ele_row(), -1.0 * source );
804 
805  balance_->add_source_vec_values(data_->water_balance_idx, ele_ac.region().bulk_idx(), {(IdxInt) ele_ac.ele_row()}, {source});
806  }
807 
808  balance_->finish_source_assembly(data_->water_balance_idx);
809 }
810 
811 
812 
813 
814 /*******************************************************************************
815  * COMPOSE WATER MH MATRIX WITHOUT SCHUR COMPLEMENT
816  ******************************************************************************/
817 
819 
820  START_TIMER("preallocation");
821 
822  if (schur0 == NULL) { // create Linear System for MH matrix
823 
824  if (in_rec.type() == LinSys_BDDC::get_input_type()) {
825 #ifdef FLOW123D_HAVE_BDDCML
826  WarningOut() << "For BDDC no Schur complements are used.";
828  n_schur_compls = 0;
830  3, // 3 == la::BddcmlWrapper::SPD_VIA_SYMMETRICGENERAL
831  1, // 1 == number of subdomains per process
832  true); // swap signs of matrix and rhs to make the matrix SPD
833  ls->set_from_input(in_rec);
834  ls->set_solution( NULL );
835  // possible initialization particular to BDDC
836  START_TIMER("BDDC set mesh data");
838  schur0=ls;
839  END_TIMER("BDDC set mesh data");
840 #else
841  xprintf(Err, "Flow123d was not build with BDDCML support.\n");
842 #endif // FLOW123D_HAVE_BDDCML
843  }
844  else if (in_rec.type() == LinSys_PETSC::get_input_type()) {
845  // use PETSC for serial case even when user wants BDDC
846  if (n_schur_compls > 2) {
847  WarningOut() << "Invalid number of Schur Complements. Using 2.";
848  n_schur_compls = 2;
849  }
850 
851  LinSys_PETSC *schur1, *schur2;
852 
853  if (n_schur_compls == 0) {
854  LinSys_PETSC *ls = new LinSys_PETSC( &(*mh_dh.rows_ds) );
855 
856  // temporary solution; we have to set precision also for sequantial case of BDDC
857  // final solution should be probably call of direct solver for oneproc case
858  if (in_rec.type() != LinSys_BDDC::get_input_type()) ls->set_from_input(in_rec);
859  else {
860  ls->LinSys::set_from_input(in_rec); // get only common options
861  }
862 
863  ls->set_solution( NULL );
864  schur0=ls;
865  } else {
866  IS is;
867  ISCreateStride(PETSC_COMM_WORLD, mh_dh.side_ds->lsize(), mh_dh.rows_ds->begin(), 1, &is);
868  //OLD_ASSERT(err == 0,"Error in ISCreateStride.");
869 
870  SchurComplement *ls = new SchurComplement(is, &(*mh_dh.rows_ds));
871 
872  // make schur1
873  Distribution *ds = ls->make_complement_distribution();
874  if (n_schur_compls==1) {
875  schur1 = new LinSys_PETSC(ds);
876  schur1->set_positive_definite();
877  } else {
878  IS is;
879  ISCreateStride(PETSC_COMM_WORLD, mh_dh.el_ds->lsize(), ls->get_distribution()->begin(), 1, &is);
880  //OLD_ASSERT(err == 0,"Error in ISCreateStride.");
881  SchurComplement *ls1 = new SchurComplement(is, ds); // is is deallocated by SchurComplement
882  ls1->set_negative_definite();
883 
884  // make schur2
885  schur2 = new LinSys_PETSC( ls1->make_complement_distribution() );
886  schur2->set_positive_definite();
887  ls1->set_complement( schur2 );
888  schur1 = ls1;
889  }
890  ls->set_complement( schur1 );
891  ls->set_from_input(in_rec);
892  ls->set_solution( NULL );
893  schur0=ls;
894  }
895 
896  START_TIMER("PETSC PREALLOCATION");
899 
901 
902  VecZeroEntries(schur0->get_solution());
903  END_TIMER("PETSC PREALLOCATION");
904  }
905  else {
906  xprintf(Err, "Unknown solver type. Internal error.\n");
907  }
908 
909  END_TIMER("preallocation");
911 
912  }
913 
914 }
915 
916 
917 
918 
920  START_TIMER("DarcyFlowMH_Steady::assembly_linear_system");
921 
922  data_->is_linear=true;
923  bool is_steady = zero_time_term();
924  //DebugOut() << "Assembly linear system\n";
925  if (data_changed_) {
926  data_changed_ = false;
927  //DebugOut() << "Data changed\n";
928  // currently we have no optimization for cases when just time term data or RHS data are changed
929  START_TIMER("full assembly");
930  if (typeid(*schur0) != typeid(LinSys_BDDC)) {
931  schur0->start_add_assembly(); // finish allocation and create matrix
932  }
933 
936 
938 
939  auto multidim_assembler = AssemblyBase::create< AssemblyMH >(data_);
940  assembly_mh_matrix( multidim_assembler ); // fill matrix
941 
943 // print_matlab_matrix("matrix");
945  //MatView( *const_cast<Mat*>(schur0->get_matrix()), PETSC_VIEWER_STDOUT_WORLD );
946  //VecView( *const_cast<Vec*>(schur0->get_rhs()), PETSC_VIEWER_STDOUT_WORLD);
947 
948  if (! is_steady) {
949  START_TIMER("fix time term");
950  //DebugOut() << "setup time term\n";
951  // assembly time term and rhs
952  setup_time_term();
953  modify_system();
954  }
955  else
956  {
957  balance_->start_mass_assembly(data_->water_balance_idx);
958  balance_->finish_mass_assembly(data_->water_balance_idx);
959  }
960  END_TIMER("full assembly");
961  } else {
962  START_TIMER("modify system");
963  if (! is_steady) {
964  modify_system();
965  } else {
966  //xprintf(PrgErr, "Planned computation time for steady solver, but data are not changed.\n");
967  }
968  END_TIMER("modiffy system");
969  }
970 
971 }
972 
973 
974 void DarcyMH::print_matlab_matrix(std::string matlab_file)
975 {
976  std::string output_file;
977 
978  if ( typeid(*schur0) == typeid(LinSys_BDDC) ){
979 // WarningOut() << "Can output matrix only on a single processor.";
980 // output_file = FilePath(matlab_file + "_bddc.m", FilePath::output_file);
981 // ofstream os( output_file );
982 // auto bddc = static_cast<LinSys_BDDC*>(schur0);
983 // bddc->print_matrix(os);
984  }
985  else {//if ( typeid(*schur0) == typeid(LinSys_PETSC) ){
986  output_file = FilePath(matlab_file + ".m", FilePath::output_file);
987  PetscViewer viewer;
988  PetscViewerASCIIOpen(PETSC_COMM_WORLD, output_file.c_str(), &viewer);
989  PetscViewerSetFormat(viewer, PETSC_VIEWER_ASCII_MATLAB);
990  MatView( *const_cast<Mat*>(schur0->get_matrix()), viewer);
991  VecView( *const_cast<Vec*>(schur0->get_rhs()), viewer);
992  }
993 // else{
994 // WarningOut() << "No matrix output available for the current solver.";
995 // return;
996 // }
997 
998  // compute h_min for different dimensions
999  double d_max = std::numeric_limits<double>::max();
1000  double h1 = d_max, h2 = d_max, h3 = d_max;
1001  double he2 = d_max, he3 = d_max;
1002  FOR_ELEMENTS(mesh_, ele){
1003  switch(ele->dim()){
1004  case 1: h1 = std::min(h1,ele->measure()); break;
1005  case 2: h2 = std::min(h2,ele->measure()); break;
1006  case 3: h3 = std::min(h3,ele->measure()); break;
1007  }
1008 
1009  FOR_ELEMENT_SIDES(ele,j){
1010  switch(ele->dim()){
1011  case 2: he2 = std::min(he2, ele->side(j)->measure()); break;
1012  case 3: he3 = std::min(he3, ele->side(j)->measure()); break;
1013  }
1014  }
1015  }
1016  if(h1 == d_max) h1 = 0;
1017  if(h2 == d_max) h2 = 0;
1018  if(h3 == d_max) h3 = 0;
1019  if(he2 == d_max) he2 = 0;
1020  if(he3 == d_max) he3 = 0;
1021 
1022  FILE * file;
1023  file = fopen(output_file.c_str(),"a");
1024  fprintf(file, "nA = %d;\n", mh_dh.side_ds->size());
1025  fprintf(file, "nB = %d;\n", mh_dh.el_ds->size());
1026  fprintf(file, "nBF = %d;\n", mh_dh.edge_ds->size());
1027  fprintf(file, "h1 = %e;\nh2 = %e;\nh3 = %e;\n", h1, h2, h3);
1028  fprintf(file, "he2 = %e;\nhe3 = %e;\n", he2, he3);
1029  fclose(file);
1030 }
1031 
1032 
1034  START_TIMER("DarcyFlowMH_Steady::set_mesh_data_for_bddc");
1035  // prepare mesh for BDDCML
1036  // initialize arrays
1037  // auxiliary map for creating coordinates of local dofs and global-to-local numbering
1038  std::map<int,arma::vec3> localDofMap;
1039  // connectivity for the subdomain, i.e. global dof numbers on element, stored element-by-element
1040  // Indices of Nodes on Elements
1041  std::vector<int> inet;
1042  // number of degrees of freedom on elements - determines elementwise chunks of INET array
1043  // Number of Nodes on Elements
1044  std::vector<int> nnet;
1045  // Indices of Subdomain Elements in Global Numbering - for local elements, their global indices
1046  std::vector<int> isegn;
1047 
1048  // This array is currently not used in BDDCML, it was used as an interface scaling alternative to scaling
1049  // by diagonal. It corresponds to the rho-scaling.
1050  std::vector<double> element_permeability;
1051 
1052  // maximal and minimal dimension of elements
1053  uint elDimMax = 1;
1054  uint elDimMin = 3;
1055  for ( unsigned int i_loc = 0; i_loc < mh_dh.el_ds->lsize(); i_loc++ ) {
1056  auto ele_ac = mh_dh.accessor(i_loc);
1057  // for each element, create local numbering of dofs as fluxes (sides), pressure (element centre), Lagrange multipliers (edges), compatible connections
1058 
1059  elDimMax = std::max( elDimMax, ele_ac.dim() );
1060  elDimMin = std::min( elDimMin, ele_ac.dim() );
1061 
1062  isegn.push_back( ele_ac.ele_global_idx() );
1063  int nne = 0;
1064 
1065  FOR_ELEMENT_SIDES(ele_ac.full_iter(), si) {
1066  // insert local side dof
1067  int side_row = ele_ac.side_row(si);
1068  arma::vec3 coord = ele_ac.side(si)->centre();
1069 
1070  localDofMap.insert( std::make_pair( side_row, coord ) );
1071  inet.push_back( side_row );
1072  nne++;
1073  }
1074 
1075  // insert local pressure dof
1076  int el_row = ele_ac.ele_row();
1077  arma::vec3 coord = ele_ac.centre();
1078  localDofMap.insert( std::make_pair( el_row, coord ) );
1079  inet.push_back( el_row );
1080  nne++;
1081 
1082  FOR_ELEMENT_SIDES(ele_ac.full_iter(), si) {
1083  // insert local edge dof
1084  int edge_row = ele_ac.edge_row(si);
1085  arma::vec3 coord = ele_ac.side(si)->centre();
1086 
1087  localDofMap.insert( std::make_pair( edge_row, coord ) );
1088  inet.push_back( edge_row );
1089  nne++;
1090  }
1091 
1092  // insert dofs related to compatible connections
1093  for ( unsigned int i_neigh = 0; i_neigh < ele_ac.full_iter()->n_neighs_vb; i_neigh++) {
1094  int edge_row = mh_dh.row_4_edge[ ele_ac.full_iter()->neigh_vb[i_neigh]->edge_idx() ];
1095  arma::vec3 coord = ele_ac.full_iter()->neigh_vb[i_neigh]->edge()->side(0)->centre();
1096 
1097  localDofMap.insert( std::make_pair( edge_row, coord ) );
1098  inet.push_back( edge_row );
1099  nne++;
1100  }
1101 
1102  nnet.push_back( nne );
1103 
1104  // version for rho scaling
1105  // trace computation
1106  arma::vec3 centre = ele_ac.centre();
1107  double conduct = data_->conductivity.value( centre , ele_ac.element_accessor() );
1108  auto aniso = data_->anisotropy.value( centre, ele_ac.element_accessor() );
1109 
1110  // compute mean on the diagonal
1111  double coef = 0.;
1112  for ( int i = 0; i < 3; i++) {
1113  coef = coef + aniso.at(i,i);
1114  }
1115  // Maybe divide by cs
1116  coef = conduct*coef / 3;
1117 
1118  OLD_ASSERT( coef > 0.,
1119  "Zero coefficient of hydrodynamic resistance %f . \n ", coef );
1120  element_permeability.push_back( 1. / coef );
1121  }
1122  //convert set of dofs to vectors
1123  // number of nodes (= dofs) on the subdomain
1124  int numNodeSub = localDofMap.size();
1125  OLD_ASSERT_EQUAL( (unsigned int)numNodeSub, mh_dh.global_row_4_sub_row->size() );
1126  // Indices of Subdomain Nodes in Global Numbering - for local nodes, their global indices
1127  std::vector<int> isngn( numNodeSub );
1128  // pseudo-coordinates of local nodes (i.e. dofs)
1129  // they need not be exact, they are used just for some geometrical considerations in BDDCML,
1130  // such as selection of corners maximizing area of a triangle, bounding boxes fro subdomains to
1131  // find candidate neighbours etc.
1132  std::vector<double> xyz( numNodeSub * 3 ) ;
1133  int ind = 0;
1134  std::map<int,arma::vec3>::iterator itB = localDofMap.begin();
1135  for ( ; itB != localDofMap.end(); ++itB ) {
1136  isngn[ind] = itB -> first;
1137 
1138  arma::vec3 coord = itB -> second;
1139  for ( int j = 0; j < 3; j++ ) {
1140  xyz[ j*numNodeSub + ind ] = coord[j];
1141  }
1142 
1143  ind++;
1144  }
1145  localDofMap.clear();
1146 
1147  // Number of Nodal Degrees of Freedom
1148  // nndf is trivially one - dofs coincide with nodes
1149  std::vector<int> nndf( numNodeSub, 1 );
1150 
1151  // prepare auxiliary map for renumbering nodes
1152  typedef std::map<int,int> Global2LocalMap_; //! type for storage of global to local map
1153  Global2LocalMap_ global2LocalNodeMap;
1154  for ( unsigned ind = 0; ind < isngn.size(); ++ind ) {
1155  global2LocalNodeMap.insert( std::make_pair( static_cast<unsigned>( isngn[ind] ), ind ) );
1156  }
1157 
1158  // renumber nodes in the inet array to locals
1159  int indInet = 0;
1160  for ( unsigned int iEle = 0; iEle < isegn.size(); iEle++ ) {
1161  int nne = nnet[ iEle ];
1162  for ( int ien = 0; ien < nne; ien++ ) {
1163 
1164  int indGlob = inet[indInet];
1165  // map it to local node
1166  Global2LocalMap_::iterator pos = global2LocalNodeMap.find( indGlob );
1167  OLD_ASSERT( pos != global2LocalNodeMap.end(),
1168  "Cannot remap node index %d to local indices. \n ", indGlob );
1169  int indLoc = static_cast<int> ( pos -> second );
1170 
1171  // store the node
1172  inet[ indInet++ ] = indLoc;
1173  }
1174  }
1175 
1176  int numNodes = size;
1177  int numDofsInt = size;
1178  int spaceDim = 3; // TODO: what is the proper value here?
1179  int meshDim = elDimMax;
1180 
1181  bddc_ls -> load_mesh( spaceDim, numNodes, numDofsInt, inet, nnet, nndf, isegn, isngn, isngn, xyz, element_permeability, meshDim );
1182 }
1183 
1184 
1185 
1186 
1187 //=============================================================================
1188 // DESTROY WATER MH SYSTEM STRUCTURE
1189 //=============================================================================
1191 
1192  VecDestroy(&previous_solution);
1193  VecDestroy(&steady_diagonal);
1194  VecDestroy(&new_diagonal);
1195  VecDestroy(&steady_rhs);
1196 
1197 
1198  if (schur0 != NULL) {
1199  delete schur0;
1200  VecScatterDestroy(&par_to_all);
1201  }
1202 
1203  if (solution != NULL) {
1204  chkerr(VecDestroy(&sol_vec));
1205  delete [] solution;
1206  }
1207 
1208  if (output_object) delete output_object;
1209 
1210  if(time_ != nullptr)
1211  delete time_;
1212 
1213 }
1214 
1215 
1216 // ================================================
1217 // PARALLLEL PART
1218 //
1219 
1220 
1222  START_TIMER("prepare scatter");
1223  // prepare Scatter form parallel to sequantial in original numbering
1224  {
1225  IS is_loc;
1226  int i, *loc_idx; //, si;
1227 
1228  // create local solution vector
1229  solution = new double[size];
1230  VecCreateSeqWithArray(PETSC_COMM_SELF,1, size, solution,
1231  &(sol_vec));
1232 
1233  // create seq. IS to scatter par solutin to seq. vec. in original order
1234  // use essentialy row_4_id arrays
1235  loc_idx = new int [size];
1236  i = 0;
1237  FOR_ELEMENTS(mesh_, ele) {
1238  FOR_ELEMENT_SIDES(ele,si) {
1239  loc_idx[i++] = mh_dh.side_row_4_id[ mh_dh.side_dof( ele->side(si) ) ];
1240  }
1241  }
1242  FOR_ELEMENTS(mesh_, ele) {
1243  loc_idx[i++] = mh_dh.row_4_el[ele.index()];
1244  }
1245  for(unsigned int i_edg=0; i_edg < mesh_->n_edges(); i_edg++) {
1246  loc_idx[i++] = mh_dh.row_4_edge[i_edg];
1247  }
1248  OLD_ASSERT( i==size,"Size of array does not match number of fills.\n");
1249  //DBGPRINT_INT("loc_idx",size,loc_idx);
1250  ISCreateGeneral(PETSC_COMM_SELF, size, loc_idx, PETSC_COPY_VALUES, &(is_loc));
1251  delete [] loc_idx;
1252  VecScatterCreate(schur0->get_solution(), is_loc, sol_vec,
1253  PETSC_NULL, &par_to_all);
1254  ISDestroy(&(is_loc));
1255  }
1257 
1258  END_TIMER("prepare scatter");
1259 
1260 }
1261 
1262 
1263 /*
1264 void mat_count_off_proc_values(Mat m, Vec v) {
1265  int n, first, last;
1266  const PetscInt *cols;
1267  Distribution distr(v);
1268 
1269  int n_off = 0;
1270  int n_on = 0;
1271  int n_off_rows = 0;
1272  MatGetOwnershipRange(m, &first, &last);
1273  for (int row = first; row < last; row++) {
1274  MatGetRow(m, row, &n, &cols, PETSC_NULL);
1275  bool exists_off = false;
1276  for (int i = 0; i < n; i++)
1277  if (distr.get_proc(cols[i]) != distr.myp())
1278  n_off++, exists_off = true;
1279  else
1280  n_on++;
1281  if (exists_off)
1282  n_off_rows++;
1283  MatRestoreRow(m, row, &n, &cols, PETSC_NULL);
1284  }
1285 }
1286 */
1287 
1288 
1289 
1290 
1291 
1292 
1293 
1294 
1295 
1296 
1297 
1298 
1300 {
1301  double *local_sol = schur0->get_solution_array();
1302 
1303  // cycle over local element rows
1304 
1305  DebugOut().fmt("Setup with dt: {}\n", time_->dt());
1306  for (unsigned int i_loc_el = 0; i_loc_el < mh_dh.el_ds->lsize(); i_loc_el++) {
1307  auto ele_ac = mh_dh.accessor(i_loc_el);
1308  // set initial condition
1309  local_sol[ele_ac.ele_local_row()] = data_->init_pressure.value(ele_ac.centre(),ele_ac.element_accessor());
1310  }
1311 
1313 
1314 }
1315 
1317  // save diagonal of steady matrix
1318  MatGetDiagonal(*( schur0->get_matrix() ), steady_diagonal);
1319  // save RHS
1320  VecCopy(*( schur0->get_rhs()), steady_rhs);
1321 
1322 
1323  PetscScalar *local_diagonal;
1324  VecGetArray(new_diagonal,& local_diagonal);
1325 
1326  DebugOut().fmt("Setup with dt: {}\n", time_->dt());
1327 
1328  balance_->start_mass_assembly(data_->water_balance_idx);
1329 
1330  //DebugOut().fmt("time_term lsize: {} {}\n", mh_dh.el_ds->myp(), mh_dh.el_ds->lsize());
1331  for (unsigned int i_loc_el = 0; i_loc_el < mh_dh.el_ds->lsize(); i_loc_el++) {
1332  auto ele_ac = mh_dh.accessor(i_loc_el);
1333 
1334  // set new diagonal
1335  double diagonal_coeff = data_->cross_section.value(ele_ac.centre(), ele_ac.element_accessor())
1336  * data_->storativity.value(ele_ac.centre(), ele_ac.element_accessor())
1337  * ele_ac.measure();
1338  local_diagonal[ele_ac.ele_local_row()]= - diagonal_coeff / time_->dt();
1339 
1340  //DebugOut().fmt("time_term: {} {} {} {} {}\n", mh_dh.el_ds->myp(), ele_ac.ele_global_idx(), i_loc_row, i_loc_el + mh_dh.side_ds->lsize(), diagonal_coeff);
1341  balance_->add_mass_matrix_values(data_->water_balance_idx,
1342  ele_ac.region().bulk_idx(), { IdxInt(ele_ac.ele_row()) }, {diagonal_coeff});
1343  }
1344  VecRestoreArray(new_diagonal,& local_diagonal);
1345  MatDiagonalSet(*( schur0->get_matrix() ), new_diagonal, ADD_VALUES);
1346 
1349 
1350  balance_->finish_mass_assembly(data_->water_balance_idx);
1351 }
1352 
1354  START_TIMER("modify system");
1355  if (time_->is_changed_dt() && time_->step().index()>0) {
1356  double scale_factor=time_->step(-2).length()/time_->step().length();
1357  if (scale_factor != 1.0) {
1358  // if time step has changed and setup_time_term not called
1359  MatDiagonalSet(*( schur0->get_matrix() ),steady_diagonal, INSERT_VALUES);
1360 
1361  VecScale(new_diagonal, time_->last_dt()/time_->dt());
1362  MatDiagonalSet(*( schur0->get_matrix() ),new_diagonal, ADD_VALUES);
1364  }
1365  }
1366 
1367  // modify RHS - add previous solution
1368  //VecPointwiseMult(*( schur0->get_rhs()), new_diagonal, previous_solution);
1369  VecPointwiseMult(*( schur0->get_rhs()), new_diagonal, schur0->get_solution());
1370  VecAXPY(*( schur0->get_rhs()), 1.0, steady_rhs);
1372 
1373  //VecSwap(previous_solution, schur0->get_solution());
1374 }
1375 
1376 
1377 //-----------------------------------------------------------------------------
1378 // vim: set cindent:
TimeGovernor & time()
Definition: equation.hh:148
unsigned int size() const
get global size
void get_solution_vector(double *&vec, unsigned int &vec_size) override
void make_serial_scatter()
FieldSet * eq_data_
Definition: equation.hh:232
Distribution * side_ds
int IdxInt
Define integers that are indices into large arrays (elements, nodes, dofs etc.)
Definition: mesh.h:85
static const Input::Type::Record & get_input_type()
Main balance input record type.
Definition: balance.cc:42
void reinit(Mesh *mesh)
Output class for darcy_flow_mh model.
IdxInt * row_4_el
virtual void initialize_specific()
void get_parallel_solution_vector(Vec &vector) override
Accessor to input data conforming to declared Array.
Definition: accessors.hh:567
SchurComplement SchurComplement
Definition: linsys.hh:91
void assembly_mh_matrix(MultidimAssembly &assembler)
void set_symmetric(bool flag=true)
Definition: linsys.hh:521
unsigned int uint
Classes with algorithms for computation of intersections of meshes.
unsigned int size() const
Returns number of keys in the Record.
Definition: type_record.hh:598
Solver based on the original PETSc solver using MPIAIJ matrix and succesive Schur complement construc...
IdxInt * row_4_edge
void init_eq_data()
virtual ~DarcyMH() override
MixedMeshIntersections & mixed_intersections()
Definition: mesh.cc:651
static const Input::Type::Record & get_input_type()
The specification of output stream.
Definition: output_time.cc:37
Common base for intersection object.
Class Input::Type::Default specifies default value of keys of a Input::Type::Record.
Definition: type_record.hh:61
#define FOR_ELEMENTS(_mesh_, __i)
Definition: mesh.h:480
Class for declaration of the input of type Bool.
Definition: type_base.hh:458
void set_from_input(const Input::Record in_rec) override
unsigned int edge_idx() const
Definition: side_impl.hh:61
virtual void assembly_source_term()
Source term is implemented differently in LMH version.
friend class DarcyFlowMHOutput
#define MessageOut()
Macro defining &#39;message&#39; record of log.
Definition: logger.hh:243
virtual void start_add_assembly()
Definition: linsys.hh:321
Definition: abscissa.h:25
virtual void output_data() override
Write computed fields.
virtual PetscErrorCode mat_zero_entries()
Definition: linsys.hh:263
Wrappers for linear systems based on MPIAIJ and MATIS format.
bool is_end() const
Returns true if the actual time is greater than or equal to the end time.
void next_time()
Proceed to the next time according to current estimated time step.
void set_rhs_changed()
Definition: linsys.hh:217
static const int registrar
Registrar of class to factory.
unsigned int max_n_it_
VecScatter par_to_all
static Default obligatory()
The factory function to make an empty default value which is obligatory.
Definition: type_record.hh:110
Vec steady_rhs
static const Input::Type::Selection & get_mh_mortar_selection()
Selection for enum MortarMethod.
void initialize() override
RegionSet get_region_set(const string &set_name) const
Definition: region.cc:328
Definition: mesh.h:99
static const Input::Type::Record & type_field_descriptor()
virtual void start_allocation()
Definition: linsys.hh:313
Distribution * el_ds
#define FOR_ELEMENT_SIDES(i, j)
Definition: elements.h:157
void chkerr(unsigned int ierr)
Replacement of new/delete operator in the spirit of xmalloc.
Definition: system.hh:147
virtual void finish_assembly()=0
virtual double get_solution_precision()=0
std::vector< std::vector< ILpair > > element_intersections_
IdxInt * side_row_4_id
const RegionDB & region_db() const
Definition: mesh.h:170
#define ASSERT(expr)
Allow use shorter versions of macro names if these names is not used with external library...
Definition: asserts.hh:346
const TimeStep & step(int index=-1) const
static const std::string field_descriptor_record_description(const string &record_name)
Definition: field_common.cc:68
Class for declaration of the integral input data.
Definition: type_base.hh:489
#define ADD_FIELD(name,...)
Definition: field_set.hh:269
Basic time management functionality for unsteady (and steady) solvers (class Equation).
unsigned int bulk_ele_idx() const
Returns index of bulk element.
unsigned int n_sides()
Definition: mesh.cc:199
bool solution_changed_for_scatter
void solve_nonlinear()
Solve method common to zero_time_step and update solution.
static const Input::Type::Record & get_input_type()
Definition: linsys_BDDC.cc:37
std::shared_ptr< EqData > data_
Record & close() const
Close the Record for further declarations of keys.
Definition: type_record.cc:303
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:345
void view(const char *name="") const
std::shared_ptr< LocalToGlobalMap > global_row_4_sub_row
Necessary only for BDDC solver.
unsigned int min_n_it_
void modify_system()
unsigned int nonlinear_iteration_
void set_mesh_data_for_bddc(LinSys_BDDC *bddc_ls)
Assembly explicit Schur complement for the given linear system. Provides method for resolution of the...
virtual Record & derive_from(Abstract &parent)
Method to derive new Record from an AbstractRecord parent.
Definition: type_record.cc:195
void update_solution() override
unsigned int size() const
Returns size of the container. This is independent of the allocated space.
Definition: sys_vector.hh:391
static Default optional()
The factory function to make an empty default value which is optional.
Definition: type_record.hh:124
#define OLD_ASSERT(...)
Definition: global_defs.h:131
void set_from_input(const Input::Record in_rec) override
Definition: linsys_BDDC.cc:300
unsigned int n_elements() const
Definition: mesh.h:156
virtual void postprocess()
Class for declaration of the input data that are floating point numbers.
Definition: type_base.hh:540
double * get_solution_array()
Definition: linsys.hh:305
std::shared_ptr< Balance > balance_
#define MPI_MIN
Definition: mpi.h:198
unsigned int edge_idx()
FLOW123D_FORCE_LINK_IN_CHILD(darcy_flow_mh)
int n_schur_compls
const Vec & get_solution()
Definition: linsys.hh:281
Input::Type::Record type() const
Definition: accessors.cc:273
FMT_FUNC int fprintf(std::ostream &os, CStringRef format, ArgList args)
Definition: ostream.cc:56
Accessor to the data with type Type::Record.
Definition: accessors.hh:292
const Ret val(const string &key) const
unsigned int n_sides() const
#define xprintf(...)
Definition: system.hh:92
bool is_changed_dt() const
#define START_TIMER(tag)
Starts a timer with specified tag.
Distribution * edge_ds
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.
static const Input::Type::Instance & get_input_type()
bool data_changed_
double * solution
double length() const
unsigned int side_dof(const SideIter side) const
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:490
static Input::Type::Abstract & get_input_type()
ElementVector bc_elements
Definition: mesh.h:268
SideIter side(const unsigned int loc_index)
bool use_steady_assembly_
static const Input::Type::Record & get_input_type_specific()
void allocate_mh_matrix()
static const Input::Type::Selection & get_bc_type_selection()
Return a Selection corresponding to enum BC_Type.
void set_solution(double *sol_array)
Definition: linsys.hh:289
virtual void assembly_linear_system()
virtual const Vec * get_rhs()
Definition: linsys.hh:202
Accessor to the polymorphic input data of a type given by an AbstracRecord object.
Definition: accessors.hh:459
LinSys * schur0
virtual PetscErrorCode rhs_zero_entries()
Definition: linsys.hh:272
Record & copy_keys(const Record &other)
Copy keys from other record.
Definition: type_record.cc:215
Dedicated class for storing path to input and output files.
Definition: file_path.hh:54
DarcyMH(Mesh &mesh, const Input::Record in_rec)
CREATE AND FILL GLOBAL MH MATRIX OF THE WATER MODEL.
Support classes for parallel programing.
#define MPI_Allreduce(sendbuf, recvbuf, count, datatype, op, comm)
Definition: mpi.h:612
int set_upper_constraint(double upper, std::string message)
Sets upper constraint for the next time step estimating.
void create_linear_system(Input::AbstractRecord rec)
Initialize global_row_4_sub_row.
MH_DofHandler mh_dh
MortarMethod
Type of experimental Mortar-like method for non-compatible 1d-2d interaction.
const Selection & close() const
Close the Selection, no more values can be added.
Input::Record input_record_
Definition: equation.hh:225
#define MPI_INT
Definition: mpi.h:160
double dt() const
std::shared_ptr< Distribution > rows_ds
LocalElementAccessorBase< 3 > accessor(uint local_ele_idx)
Vec steady_diagonal
static const Input::Type::Record & get_input_type()
#define WarningOut()
Macro defining &#39;warning&#39; record of log.
Definition: logger.hh:246
virtual SolveInfo solve()=0
#define END_TIMER(tag)
Ends a timer with specified tag.
#define OLD_ASSERT_EQUAL(a, b)
Definition: global_defs.h:133
EqData()
Creation of all fields.
Definition: system.hh:64
void set_matrix_changed()
Definition: linsys.hh:211
static const Input::Type::Record & get_input_type()
Definition: linsys_PETSC.cc:32
void print_matlab_matrix(string matlab_file)
Print darcy flow matrix in matlab format into a file.
mixed-hybrid model of linear Darcy flow, possibly unsteady.
unsigned int index() const
void zero_time_step() override
void set_positive_definite(bool flag=true)
Definition: linsys.hh:536
virtual void read_initial_condition()
Record type proxy class.
Definition: type_record.hh:182
void prepare_parallel_bddc()
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
DarcyFlowMHOutput * output_object
unsigned int n_edges() const
Definition: mesh.h:164
virtual void prepare_new_time_step()
postprocess velocity field (add sources)
Class for representation SI units of Fields.
Definition: unit_si.hh:40
virtual const Mat * get_matrix()
Definition: linsys.hh:186
double last_dt() const
Vec new_diagonal
virtual double solution_precision() const
virtual void setup_time_term()
static UnitSI & dimensionless()
Returns dimensionless unit.
Definition: unit_si.cc:55
#define DebugOut()
Macro defining &#39;debug&#39; record of log.
Definition: logger.hh:252
virtual double compute_residual()=0
Vec previous_solution
#define THROW(whole_exception_expr)
Wrapper for throw. Saves the throwing point.
Definition: exceptions.hh:53
Template for classes storing finite set of named values.
void rhs_set_value(int row, double val)
Definition: linsys.hh:361
double tolerance_
static const Input::Type::Record & get_input_type()
Solver based on Multilevel BDDC - using corresponding class of OpenFTL package.
TimeGovernor * time_
Definition: equation.hh:224
virtual bool zero_time_term(bool time_global=false)
ElementVector element
Vector of elements of the mesh.
Definition: mesh.h:260
void output()
Calculate values for output.
unsigned int lsize(int proc) const
get local size