28 #include "petscviewer.h"
29 #include "petscerror.h"
51 #include "flow/assembly_mh_old.hh"
52 #include "flow/darcy_flow_mh.hh"
85 .
add_value(
MortarP1,
"P1",
"Mortar space: P1 on intersections, using non-conforming pressures.")
93 "Homogeneous Neumann boundary condition\n(zero normal flux over the boundary).")
95 "Dirichlet boundary condition. "
96 "Specify the pressure head through the ``bc_pressure`` field "
97 "or the piezometric head through the ``bc_piezo_head`` field.")
98 .
add_value(
total_flux,
"total_flux",
"Flux boundary condition (combines Neumann and Robin type). "
99 "Water inflow equal to (($ \\delta_d(q_d^N + \\sigma_d (h_d^R - h_d) )$)). "
100 "Specify the water inflow by the ``bc_flux`` field, the transition coefficient by ``bc_robin_sigma`` "
101 "and the reference pressure head or piezometric head through ``bc_pressure`` or ``bc_piezo_head`` respectively.")
103 "Seepage face boundary condition. Pressure and inflow bounded from above. Boundary with potential seepage flow "
104 "is described by the pair of inequalities: "
105 "(($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. "
106 "Caution: setting (($q_d^N$)) strictly negative "
107 "may lead to an ill posed problem since a positive outflow is enforced. "
108 "Parameters (($h_d^D$)) and (($q_d^N$)) are given by the fields ``bc_switch_pressure`` (or ``bc_switch_piezo_head``) and ``bc_flux`` respectively."
111 "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: "
112 "(( $ \\delta_d(q_d^N + \\sigma_d(H_d^D - H_d) )$)). For the water level under the bedrock, constant infiltration is used: "
113 "(( $ \\delta_d(q_d^N + \\sigma_d(H_d^D - H_d^S) )$)). Parameters: ``bc_pressure``, ``bc_switch_pressure``, "
114 " ``bc_sigma``, ``bc_flux``."
125 "Boundary piezometric head for BC types: dirichlet, robin, and river." )
127 "Boundary switch piezometric head for BC types: seepage, river." )
129 "Initial condition for the pressure given as the piezometric head." )
131 return field_descriptor;
138 "Linear solver for MH problem.")
140 "Residual tolerance.")
142 "Minimum number of iterations (linear solutions) to use.\nThis is usefull if the convergence criteria "
143 "does not characterize your goal well enough so it converges prematurely, possibly even without a single linear solution."
144 "If greater then 'max_it' the value is set to 'max_it'.")
146 "Maximum number of iterations (linear solutions) of the non-linear solver.")
148 "If a stagnation of the nonlinear solver is detected the solver stops. "
149 "A divergence is reported by default, forcing the end of the simulation. By setting this flag to 'true', the solver "
150 "ends with convergence success on stagnation, but it reports warning about it.")
155 return it::Record(
"Flow_Darcy_MH",
"Mixed-Hybrid solver for saturated Darcy flow.")
159 "Vector of the gravity force. Dimensionless.")
161 "Input data for Darcy flow model.")
163 "Non-linear solver for MH problem.")
165 "Output stream settings.\n Specify file format, precision etc.")
168 IT::Default(
"{ \"fields\": [ \"pressure_p0\", \"velocity_p0\" ] }"),
169 "Specification of output fields and output times.")
171 "Output settings specific to Darcy flow model.\n"
172 "Includes raw output and some experimental functionality.")
174 "Settings for computing mass balance.")
176 "Number of Schur complements to perform when solving MH system.")
178 "Method for coupling Darcy flow between dimensions on incompatible meshes. [Experimental]" )
184 Input::register_class< DarcyMH, Mesh &, const Input::Record >(
"Flow_Darcy_MH") +
191 *
this += field_ele_pressure.name(
"pressure_p0")
194 .description(
"Pressure solution - P0 interpolation.");
196 *
this += field_edge_pressure.name(
"pressure_edge")
199 .description(
"Pressure solution - Crouzeix-Raviart interpolation.");
201 *
this += field_ele_piezo_head.name(
"piezo_head_p0")
204 .description(
"Piezo head solution - P0 interpolation.");
206 *
this += field_ele_velocity.name(
"velocity_p0")
207 .units(
UnitSI().m().s(-1))
209 .description(
"Velocity solution - P0 interpolation.");
211 *
this += flux.name(
"flux")
212 .units(
UnitSI().m().s(-1))
214 .description(
"Darcy flow flux.");
216 *
this += anisotropy.name(
"anisotropy")
217 .description(
"Anisotropy of the conductivity tensor.")
218 .input_default(
"1.0")
221 *
this += cross_section.name(
"cross_section")
222 .description(
"Complement dimension parameter (cross section for 1D, thickness for 2D).")
223 .input_default(
"1.0")
224 .units(
UnitSI().m(3).md() );
226 *
this += conductivity.name(
"conductivity")
227 .description(
"Isotropic conductivity scalar.")
228 .input_default(
"1.0")
229 .units(
UnitSI().m().s(-1) )
232 *
this += sigma.name(
"sigma")
233 .description(
"Transition coefficient between dimensions.")
234 .input_default(
"1.0")
237 *
this += water_source_density.name(
"water_source_density")
238 .description(
"Water source density.")
239 .input_default(
"0.0")
242 *
this += bc_type.name(
"bc_type")
243 .description(
"Boundary condition type.")
244 .input_selection( get_bc_type_selection() )
245 .input_default(
"\"none\"")
249 .disable_where(bc_type, {
none, seepage} )
251 .description(
"Prescribed pressure value on the boundary. Used for all values of ``bc_type`` except ``none`` and ``seepage``. "
252 "See documentation of ``bc_type`` for exact meaning of ``bc_pressure`` in individual boundary condition types.")
253 .input_default(
"0.0")
257 .disable_where(bc_type, {
none, dirichlet} )
259 .description(
"Incoming water boundary flux. Used for bc_types : ``total_flux``, ``seepage``, ``river``.")
260 .input_default(
"0.0")
261 .units(
UnitSI().m().s(-1) );
263 *
this += bc_robin_sigma
264 .disable_where(bc_type, {
none, dirichlet, seepage} )
265 .name(
"bc_robin_sigma")
266 .description(
"Conductivity coefficient in the ``total_flux`` or the ``river`` boundary condition type.")
267 .input_default(
"0.0")
270 *
this += bc_switch_pressure
271 .disable_where(bc_type, {
none, dirichlet, total_flux} )
272 .name(
"bc_switch_pressure")
273 .description(
"Critical switch pressure for ``seepage`` and ``river`` boundary conditions.")
274 .input_default(
"0.0")
279 *
this += init_pressure.name(
"init_pressure")
280 .description(
"Initial condition for pressure in time dependent problems.")
281 .input_default(
"0.0")
284 *
this += storativity.name(
"storativity")
285 .description(
"Storativity (in time dependent problems).")
286 .input_default(
"0.0")
289 *
this += extra_storativity.name(
"extra_storativity")
290 .description(
"Storativity added from upstream equation.")
292 .input_default(
"0.0")
293 .flags( input_copy );
295 *
this += extra_source.name(
"extra_water_source_density")
296 .description(
"Water source density added from upstream equation.")
297 .input_default(
"0.0")
299 .flags( input_copy );
301 *
this += gravity_field.name(
"gravity")
302 .description(
"Gravity vector.")
303 .input_default(
"0.0")
306 *
this += bc_gravity.name(
"bc_gravity")
307 .description(
"Boundary gravity vector.")
308 .input_default(
"0.0")
311 *
this += init_piezo_head.name(
"init_piezo_head")
313 .input_default(
"0.0")
314 .description(
"Init piezo head.");
316 *
this += bc_piezo_head.name(
"bc_piezo_head")
318 .input_default(
"0.0")
319 .description(
"Boundary piezo head.");
321 *
this += bc_switch_piezo_head.name(
"bc_switch_piezo_head")
323 .input_default(
"0.0")
324 .description(
"Boundary switch piezo head.");
374 MessageOut() <<
"Duplicate key 'time', time in flow equation is already initialized from parent class!";
414 return next_t * (1 - 2*std::numeric_limits<double>::epsilon());
436 gravity_array.copy_to(gvec);
442 auto field_algo=std::make_shared<FieldConstant<3, FieldValue<3>::VectorFixed>>();
443 field_algo->set_value(gvalue);
444 eq_fields_->gravity_field.set(field_algo, 0.0);
463 MessageOut() <<
"Missing the key 'time', obligatory for the transient problems." << endl;
476 std::shared_ptr< FiniteElement<1> > fe1_rt = std::make_shared<FE_RT0_disc<1>>();
477 std::shared_ptr< FiniteElement<2> > fe2_rt = std::make_shared<FE_RT0_disc<2>>();
478 std::shared_ptr< FiniteElement<3> > fe3_rt = std::make_shared<FE_RT0_disc<3>>();
479 std::shared_ptr< FiniteElement<0> > fe0_disc = std::make_shared<FE_P_disc<0>>(0);
480 std::shared_ptr< FiniteElement<1> > fe1_disc = std::make_shared<FE_P_disc<1>>(0);
481 std::shared_ptr< FiniteElement<2> > fe2_disc = std::make_shared<FE_P_disc<2>>(0);
482 std::shared_ptr< FiniteElement<3> > fe3_disc = std::make_shared<FE_P_disc<3>>(0);
483 std::shared_ptr< FiniteElement<0> > fe0_cr = std::make_shared<FE_CR<0>>();
484 std::shared_ptr< FiniteElement<1> > fe1_cr = std::make_shared<FE_CR<1>>();
485 std::shared_ptr< FiniteElement<2> > fe2_cr = std::make_shared<FE_CR<2>>();
486 std::shared_ptr< FiniteElement<3> > fe3_cr = std::make_shared<FE_CR<3>>();
488 FESystem<0> fe0_sys( {fe0_disc, fe0_disc, fe0_cr} );
494 std::shared_ptr<DiscreteSpace> ds = std::make_shared<EqualOrderDiscreteSpace>(
mesh_, fe_sys);
495 eq_data_->dh_ = std::make_shared<DOFHandlerMultiDim>(*
mesh_);
506 uint rt_component = 0;
508 auto ele_flux_ptr = create_field_fe<3, FieldValue<3>::VectorFixed>(
eq_data_->dh_, &
eq_data_->full_solution, rt_component);
513 uint p_ele_component = 1;
514 auto ele_pressure_ptr = create_field_fe<3, FieldValue<3>::Scalar>(
eq_data_->dh_, &
eq_data_->full_solution, p_ele_component);
515 eq_fields_->field_ele_pressure.set(ele_pressure_ptr, 0.0);
517 uint p_edge_component = 2;
518 auto edge_pressure_ptr = create_field_fe<3, FieldValue<3>::Scalar>(
eq_data_->dh_, &
eq_data_->full_solution, p_edge_component);
519 eq_fields_->field_edge_pressure.set(edge_pressure_ptr, 0.0);
528 uint p_edge_component = 2;
529 eq_data_->dh_cr_ = std::make_shared<SubDOFHandlerMultiDim>(
eq_data_->dh_,p_edge_component);
534 std::shared_ptr<DiscreteSpace> ds_cr_disc = std::make_shared<EqualOrderDiscreteSpace>(
mesh_, fe_cr_disc);
535 eq_data_->dh_cr_disc_ = std::make_shared<DOFHandlerMultiDim>(*
mesh_);
536 eq_data_->dh_cr_disc_->distribute_dofs(ds_cr_disc);
546 .val<Input::AbstractRecord>(
"linear_solver");
597 if (zero_time_term_from_right) {
598 MessageOut() <<
"Flow zero time step - steady case\n";
607 MessageOut() <<
"Flow zero time step - unsteady case\n";
619 eq_data_->full_solution.local_to_ghost_begin();
620 eq_data_->full_solution.local_to_ghost_end();
637 eq_data_->full_solution.local_to_ghost_begin();
638 eq_data_->full_solution.local_to_ghost_end();
649 bool jump_time =
eq_fields_->storativity.is_jump_time();
650 if (! zero_time_term_from_left) {
651 MessageOut() <<
"Flow time step - unsteady case\n";
661 WarningOut() <<
"Output of solution discontinuous in time not supported yet.\n";
670 if (! zero_time_term_from_left && ! jump_time && output)
output_data();
677 if (zero_time_term_from_right) {
678 MessageOut() <<
"Flow time step - steady case\n";
683 }
else if (! zero_time_term_from_left && jump_time) {
684 WarningOut() <<
"Discontinuous time term not supported yet.\n";
696 return (
eq_fields_->storativity.input_list_size() == 0);
709 MessageOut().fmt(
"[nonlinear solver] norm of initial residual: {}\n", residual_norm);
712 int is_linear_common;
717 this->
max_n_it_ = nl_solver_rec.
val<
unsigned int>(
"max_it");
718 this->
min_n_it_ = nl_solver_rec.
val<
unsigned int>(
"min_it");
719 if (this->min_n_it_ > this->max_n_it_) this->min_n_it_ = this->
max_n_it_;
721 if (! is_linear_common) {
729 while (nonlinear_iteration_ < this->
min_n_it_ ||
730 (residual_norm > this->tolerance_ && nonlinear_iteration_ < this->
max_n_it_ )) {
732 convergence_history.push_back(residual_norm);
735 if (convergence_history.size() >= 5 &&
736 convergence_history[ convergence_history.size() - 1]/convergence_history[ convergence_history.size() - 2] > 0.9 &&
737 convergence_history[ convergence_history.size() - 1]/convergence_history[ convergence_history.size() - 5] > 0.8) {
743 THROW(ExcSolverDiverge() << EI_Reason(
"Stagnation."));
747 if (! is_linear_common)
753 if (is_linear_common){
756 MessageOut().fmt(
"[nonlinear solver] lin. it: {}, reason: {}, residual: {}\n",
770 MessageOut().fmt(
"[nonlinear solver] it: {} lin. it: {}, reason: {}, residual: {}\n",
773 chkerr(VecDestroy(&save_solution));
797 if(
eq_data_->mortar_method_ != MortarMethod::NoMortar){
798 auto multidim_assembler = AssemblyFlowBase::create< AssemblyMH >(
eq_fields_,
eq_data_);
800 unsigned int dim = dh_cell.dim();
801 multidim_assembler[dim-1]->fix_velocity(dh_cell);
868 unsigned int dim = dh_cell.dim();
869 assembler[dim-1]->assemble(dh_cell);
887 double zeros[100000];
888 for(
int i=0; i<100000; i++) zeros[i] = 0.0;
891 tmp_rows.reserve(200);
894 dofs.reserve(
eq_data_->dh_->max_elem_dofs());
895 dofs_ngh.reserve(
eq_data_->dh_->max_elem_dofs());
900 const uint ndofs = dh_cell.n_dofs();
902 dh_cell.get_dof_indices(dofs);
911 for (
DHCellSide neighb_side : dh_cell.neighb_sides() ) {
919 const uint ndofs_ngh = dh_neighb_cell.
n_dofs();
920 dofs_ngh.resize(ndofs_ngh);
926 const unsigned int t = dh_neighb_cell.
n_dofs() - (dh_neighb_cell.
dim()+1) + neighb_side.side().side_idx();
927 tmp_rows.push_back(dofs_ngh[t]);
941 for(
auto &isec : isec_list ) {
945 const uint ndofs_slave = dh_cell_slave.
n_dofs();
946 dofs_ngh.resize(ndofs_slave);
950 for(
unsigned int i_side=0; i_side < dh_cell_slave.
elm()->
n_sides(); i_side++) {
952 tmp_rows.push_back( dofs_ngh[(ndofs_slave+1)/2+i_side] );
962 edge_rows = dofs.
data() + nsides +1;
1001 const uint ndofs = dh_cell.n_dofs();
1002 global_dofs.resize(ndofs);
1003 dh_cell.get_dof_indices(global_dofs);
1008 double source = ele.
measure() * cs *
1015 {dh_cell.get_loc_dof_indices()[ndofs/2]}, {0}, {source});
1035 #ifdef FLOW123D_HAVE_BDDCML
1036 WarningOut() <<
"For BDDC no Schur complements are used.";
1049 THROW( ExcBddcmlNotSupported() );
1050 #endif // FLOW123D_HAVE_BDDCML
1055 WarningOut() <<
"Invalid number of Schur Complements. Using 2.";
1068 ls->LinSys::set_from_input(in_rec);
1077 ISCreateGeneral(PETSC_COMM_SELF, side_dofs_vec.size(), &(side_dofs_vec[0]), PETSC_COPY_VALUES, &is);
1092 const PetscInt *b_indices;
1093 ISGetIndices(ls->
IsB, &b_indices);
1095 for(
uint i_b=0, i_bb=0; i_b < b_size && i_bb < elem_dofs_vec.size(); i_b++) {
1096 if (b_indices[i_b] == elem_dofs_vec[i_bb])
1097 elem_dofs_vec[i_bb++] = i_b + ds->
begin();
1099 ISRestoreIndices(ls->
IsB, &b_indices);
1102 ISCreateGeneral(PETSC_COMM_SELF, elem_dofs_vec.size(), &(elem_dofs_vec[0]), PETSC_COPY_VALUES, &is);
1130 THROW( ExcUnknownSolver() );
1142 START_TIMER(
"DarcyFlowMH_Steady::assembly_linear_system");
1199 std::string output_file;
1211 PetscViewerASCIIOpen(PETSC_COMM_WORLD, output_file.c_str(), &viewer);
1212 PetscViewerSetFormat(viewer, PETSC_VIEWER_ASCII_MATLAB);
1224 double d_max = std::numeric_limits<double>::max();
1225 double h1 = d_max, h2 = d_max, h3 = d_max;
1226 double he2 = d_max, he3 = d_max;
1229 case 1: h1 = std::min(h1,ele.measure());
break;
1230 case 2: h2 = std::min(h2,ele.measure());
break;
1231 case 3: h3 = std::min(h3,ele.measure());
break;
1234 for (
unsigned int j=0; j<ele->n_sides(); j++) {
1236 case 2: he2 = std::min(he2, ele.side(j)->measure());
break;
1237 case 3: he3 = std::min(he3, ele.side(j)->measure());
break;
1241 if(h1 == d_max) h1 = 0;
1242 if(h2 == d_max) h2 = 0;
1243 if(h3 == d_max) h3 = 0;
1244 if(he2 == d_max) he2 = 0;
1245 if(he3 == d_max) he3 = 0;
1248 file = fopen(output_file.c_str(),
"a");
1250 fprintf(file,
"nB = %d;\n",
eq_data_->dh_->mesh()->get_el_ds()->size());
1252 fprintf(file,
"h1 = %e;\nh2 = %e;\nh3 = %e;\n", h1, h2, h3);
1253 fprintf(file,
"he2 = %e;\nhe3 = %e;\n", he2, he3);
1271 START_TIMER(
"DarcyFlowMH_Steady::set_mesh_data_for_bddc");
1299 dh_cell.get_dof_indices(cell_dofs_global);
1301 inet.insert(inet.end(), cell_dofs_global.begin(), cell_dofs_global.end());
1302 uint n_inet = cell_dofs_global.size();
1305 uint dim = dh_cell.elm().dim();
1306 elDimMax = std::max( elDimMax, dim );
1307 elDimMin = std::min( elDimMin, dim );
1311 isegn.push_back( dh_cell.elm_idx());
1314 for (
unsigned int si=0; si<dh_cell.elm()->n_sides(); si++) {
1315 arma::vec3 coord = dh_cell.elm().side(si)->centre();
1318 localDofMap.insert( std::make_pair( cell_dofs_global[si], coord ) );
1320 localDofMap.insert( std::make_pair( cell_dofs_global[si+dim+2], coord ) );
1323 arma::vec3 elm_centre = dh_cell.elm().centre();
1324 localDofMap.insert( std::make_pair( cell_dofs_global[dim+1], elm_centre ) );
1328 for(
DHCellSide side : dh_cell.neighb_sides()) {
1329 uint neigh_dim = side.cell().elm().dim();
1330 side.cell().get_dof_indices(cell_dofs_global);
1331 int edge_row = cell_dofs_global[neigh_dim+2+side.side_idx()];
1332 localDofMap.insert( std::make_pair( edge_row, side.centre() ) );
1333 inet.push_back( edge_row );
1336 nnet.push_back(n_inet);
1341 double conduct =
eq_fields_->conductivity.value( elm_centre , dh_cell.elm() );
1342 auto aniso =
eq_fields_->anisotropy.value( elm_centre , dh_cell.elm() );
1346 for (
int i = 0; i < 3; i++) {
1347 coef = coef + aniso.at(i,i);
1350 coef = conduct*coef / 3;
1352 ASSERT_GT( coef, 0. ).error(
"Zero coefficient of hydrodynamic resistance. \n");
1353 element_permeability.push_back( 1. / coef );
1363 auto distr =
eq_data_->dh_->distr();
1372 int numNodeSub = localDofMap.size();
1383 for ( ; itB != localDofMap.end(); ++itB ) {
1384 isngn[ind] = itB -> first;
1387 for (
int j = 0; j < 3; j++ ) {
1388 xyz[ j*numNodeSub + ind ] = coord[j];
1393 localDofMap.clear();
1401 Global2LocalMap_ global2LocalNodeMap;
1402 for (
unsigned ind = 0; ind < isngn.size(); ++ind ) {
1403 global2LocalNodeMap.insert( std::make_pair(
static_cast<unsigned>( isngn[ind] ), ind ) );
1408 for (
unsigned int iEle = 0; iEle < isegn.size(); iEle++ ) {
1409 int nne = nnet[ iEle ];
1410 for (
int ien = 0; ien < nne; ien++ ) {
1412 int indGlob = inet[indInet];
1414 Global2LocalMap_::iterator pos = global2LocalNodeMap.find( indGlob );
1415 ASSERT( pos != global2LocalNodeMap.end() )(indGlob).error(
"Cannot remap node global index to local indices.\n");
1416 int indLoc =
static_cast<int> ( pos -> second );
1419 inet[ indInet++ ] = indLoc;
1423 int numNodes =
size;
1424 int numDofsInt =
size;
1426 int meshDim = elDimMax;
1438 bddc_ls -> load_mesh( LinSys_BDDC::BDDCMatrixType::SPD_VIA_SYMMETRICGENERAL, spaceDim, numNodes, numDofsInt, inet, nnet, nndf, isegn, isngn, isngn, xyz, element_permeability, meshDim );
1460 if(
time_ !=
nullptr)
1510 LocDofVec loc_dofs = dh_cell.get_loc_dof_indices();
1511 for (
unsigned int i=0; i<ele->
n_sides(); i++) {
1515 eq_data_->full_solution.add(loc_dofs[edge_dof], init_value/n_sides_of_edge);
1519 eq_data_->full_solution.ghost_to_local_begin();
1520 eq_data_->full_solution.ghost_to_local_end();
1521 eq_data_->full_solution.local_to_ghost_begin();
1522 eq_data_->full_solution.local_to_ghost_end();
1528 START_TIMER(
"DarcyFlowMH::reconstruct_solution_from_schur");
1531 unsigned int dim = dh_cell.dim();
1532 assembler[dim-1]->assemble_reconstruct(dh_cell);
1535 eq_data_->full_solution.local_to_ghost_begin();
1536 eq_data_->full_solution.local_to_ghost_end();
1547 PetscScalar *local_diagonal;
1555 dofs.reserve(
eq_data_->dh_->max_elem_dofs());
1559 dofs.resize(dh_cell.n_dofs());
1560 dh_cell.get_dof_indices(dofs);
1563 const uint p_ele_dof = dh_cell.n_dofs() / 2;
1569 local_diagonal[dh_cell.get_loc_dof_indices()[p_ele_dof]]= - diagonal_coeff /
time_->
dt();
1572 {dh_cell.get_loc_dof_indices()[p_ele_dof]},
1573 {diagonal_coeff}, 0.0);
1575 VecRestoreArray(new_diagonal,& local_diagonal);
1576 MatDiagonalSet(*( schur0->get_matrix() ), new_diagonal, ADD_VALUES);
1578 schur0->set_matrix_changed();
1580 balance_->finish_mass_assembly(eq_data_->water_balance_idx);
1587 if (scale_factor != 1.0) {
1608 void dofs_range(
unsigned int n_dofs,
unsigned int &min,
unsigned int &max,
unsigned int component) {
1612 }
else if (component==1) {
1623 ASSERT_LT(component, 3).error(
"Invalid component!");
1624 unsigned int i, n_dofs, min, max;
1628 n_dofs = dh_cell.get_dof_indices(dof_indices);
1630 for (i=min; i<max; ++i) dof_vec.push_back(dof_indices[i]);