28 #include "petscviewer.h"
29 #include "petscerror.h"
86 .
add_value(
MortarP1,
"P1",
"Mortar space: P1 on intersections, using non-conforming pressures.")
94 "Homogeneous Neumann boundary condition\n(zero normal flux over the boundary).")
96 "Dirichlet boundary condition. "
97 "Specify the pressure head through the ``bc_pressure`` field "
98 "or the piezometric head through the ``bc_piezo_head`` field.")
99 .
add_value(
total_flux,
"total_flux",
"Flux boundary condition (combines Neumann and Robin type). "
100 "Water inflow equal to (($ \\delta_d(q_d^N + \\sigma_d (h_d^R - h_d) )$)). "
101 "Specify the water inflow by the ``bc_flux`` field, the transition coefficient by ``bc_robin_sigma`` "
102 "and the reference pressure head or piezometric head through ``bc_pressure`` or ``bc_piezo_head`` respectively.")
104 "Seepage face boundary condition. Pressure and inflow bounded from above. Boundary with potential seepage flow "
105 "is described by the pair of inequalities: "
106 "(($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. "
107 "Caution: setting (($q_d^N$)) strictly negative "
108 "may lead to an ill posed problem since a positive outflow is enforced. "
109 "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."
112 "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: "
113 "(( $ \\delta_d(q_d^N + \\sigma_d(H_d^D - H_d) )$)). For the water level under the bedrock, constant infiltration is used: "
114 "(( $ \\delta_d(q_d^N + \\sigma_d(H_d^D - H_d^S) )$)). Parameters: ``bc_pressure``, ``bc_switch_pressure``, "
115 " ``bc_sigma``, ``bc_flux``."
126 "Boundary piezometric head for BC types: dirichlet, robin, and river." )
128 "Boundary switch piezometric head for BC types: seepage, river." )
130 "Initial condition for the pressure given as the piezometric head." )
132 return field_descriptor;
139 "Linear solver for MH problem.")
141 "Residual tolerance.")
143 "Minimum number of iterations (linear solutions) to use.\nThis is usefull if the convergence criteria "
144 "does not characterize your goal well enough so it converges prematurely, possibly even without a single linear solution."
145 "If greater then 'max_it' the value is set to 'max_it'.")
147 "Maximum number of iterations (linear solutions) of the non-linear solver.")
149 "If a stagnation of the nonlinear solver is detected the solver stops. "
150 "A divergence is reported by default, forcing the end of the simulation. By setting this flag to 'true', the solver "
151 "ends with convergence success on stagnation, but it reports warning about it.")
156 return it::Record(
"Flow_Darcy_MH",
"Mixed-Hybrid solver for saturated Darcy flow.")
160 "Vector of the gravity force. Dimensionless.")
162 "Input data for Darcy flow model.")
164 "Non-linear solver for MH problem.")
166 "Output stream settings.\n Specify file format, precision etc.")
169 IT::Default(
"{ \"fields\": [ \"pressure_p0\", \"velocity_p0\" ] }"),
170 "Specification of output fields and output times.")
172 "Output settings specific to Darcy flow model.\n"
173 "Includes raw output and some experimental functionality.")
175 "Settings for computing mass balance.")
177 "Number of Schur complements to perform when solving MH system.")
179 "Method for coupling Darcy flow between dimensions on incompatible meshes. [Experimental]" )
185 Input::register_class< DarcyMH, Mesh &, const Input::Record >(
"Flow_Darcy_MH") +
192 *
this += field_ele_pressure.name(
"pressure_p0")
195 .description(
"Pressure solution - P0 interpolation.");
197 *
this += field_edge_pressure.name(
"pressure_edge")
200 .description(
"Pressure solution - Crouzeix-Raviart interpolation.");
202 *
this += field_ele_piezo_head.name(
"piezo_head_p0")
205 .description(
"Piezo head solution - P0 interpolation.");
207 *
this += field_ele_velocity.name(
"velocity_p0")
208 .units(
UnitSI().m().s(-1))
210 .description(
"Velocity solution - P0 interpolation.");
212 *
this += flux.name(
"flux")
213 .units(
UnitSI().m().s(-1))
215 .description(
"Darcy flow flux.");
217 *
this += anisotropy.name(
"anisotropy")
218 .description(
"Anisotropy of the conductivity tensor.")
219 .input_default(
"1.0")
222 *
this += cross_section.name(
"cross_section")
223 .description(
"Complement dimension parameter (cross section for 1D, thickness for 2D).")
224 .input_default(
"1.0")
225 .units(
UnitSI().m(3).md() );
227 *
this += conductivity.name(
"conductivity")
228 .description(
"Isotropic conductivity scalar.")
229 .input_default(
"1.0")
230 .units(
UnitSI().m().s(-1) )
233 *
this += sigma.name(
"sigma")
234 .description(
"Transition coefficient between dimensions.")
235 .input_default(
"1.0")
238 *
this += water_source_density.name(
"water_source_density")
239 .description(
"Water source density.")
240 .input_default(
"0.0")
243 *
this += bc_type.name(
"bc_type")
244 .description(
"Boundary condition type.")
245 .input_selection( get_bc_type_selection() )
246 .input_default(
"\"none\"")
250 .disable_where(bc_type, {
none, seepage} )
252 .description(
"Prescribed pressure value on the boundary. Used for all values of ``bc_type`` except ``none`` and ``seepage``. "
253 "See documentation of ``bc_type`` for exact meaning of ``bc_pressure`` in individual boundary condition types.")
254 .input_default(
"0.0")
258 .disable_where(bc_type, {
none, dirichlet} )
260 .description(
"Incoming water boundary flux. Used for bc_types : ``total_flux``, ``seepage``, ``river``.")
261 .input_default(
"0.0")
262 .units(
UnitSI().m().s(-1) );
264 *
this += bc_robin_sigma
265 .disable_where(bc_type, {
none, dirichlet, seepage} )
266 .name(
"bc_robin_sigma")
267 .description(
"Conductivity coefficient in the ``total_flux`` or the ``river`` boundary condition type.")
268 .input_default(
"0.0")
271 *
this += bc_switch_pressure
272 .disable_where(bc_type, {
none, dirichlet, total_flux} )
273 .name(
"bc_switch_pressure")
274 .description(
"Critical switch pressure for ``seepage`` and ``river`` boundary conditions.")
275 .input_default(
"0.0")
280 *
this += init_pressure.name(
"init_pressure")
281 .description(
"Initial condition for pressure in time dependent problems.")
282 .input_default(
"0.0")
285 *
this += storativity.name(
"storativity")
286 .description(
"Storativity (in time dependent problems).")
287 .input_default(
"0.0")
290 *
this += extra_storativity.name(
"extra_storativity")
291 .description(
"Storativity added from upstream equation.")
293 .input_default(
"0.0")
294 .flags( input_copy );
296 *
this += extra_source.name(
"extra_water_source_density")
297 .description(
"Water source density added from upstream equation.")
298 .input_default(
"0.0")
300 .flags( input_copy );
302 *
this += gravity_field.name(
"gravity")
303 .description(
"Gravity vector.")
304 .input_default(
"0.0")
307 *
this += bc_gravity.name(
"bc_gravity")
308 .description(
"Boundary gravity vector.")
309 .input_default(
"0.0")
312 *
this += init_piezo_head.name(
"init_piezo_head")
314 .input_default(
"0.0")
315 .description(
"Init piezo head.");
317 *
this += bc_piezo_head.name(
"bc_piezo_head")
319 .input_default(
"0.0")
320 .description(
"Boundary piezo head.");
322 *
this += bc_switch_piezo_head.name(
"bc_switch_piezo_head")
324 .input_default(
"0.0")
325 .description(
"Boundary switch piezo head.");
375 MessageOut() <<
"Duplicate key 'time', time in flow equation is already initialized from parent class!";
415 return next_t * (1 - 2*std::numeric_limits<double>::epsilon());
437 gravity_array.copy_to(gvec);
443 auto field_algo=std::make_shared<FieldConstant<3, FieldValue<3>::VectorFixed>>();
444 field_algo->set_value(gvalue);
445 eq_fields_->gravity_field.set(field_algo, 0.0);
464 MessageOut() <<
"Missing the key 'time', obligatory for the transient problems." << endl;
477 std::shared_ptr< FiniteElement<1> > fe1_rt = std::make_shared<FE_RT0_disc<1>>();
478 std::shared_ptr< FiniteElement<2> > fe2_rt = std::make_shared<FE_RT0_disc<2>>();
479 std::shared_ptr< FiniteElement<3> > fe3_rt = std::make_shared<FE_RT0_disc<3>>();
480 std::shared_ptr< FiniteElement<0> > fe0_disc = std::make_shared<FE_P_disc<0>>(0);
481 std::shared_ptr< FiniteElement<1> > fe1_disc = std::make_shared<FE_P_disc<1>>(0);
482 std::shared_ptr< FiniteElement<2> > fe2_disc = std::make_shared<FE_P_disc<2>>(0);
483 std::shared_ptr< FiniteElement<3> > fe3_disc = std::make_shared<FE_P_disc<3>>(0);
484 std::shared_ptr< FiniteElement<0> > fe0_cr = std::make_shared<FE_CR<0>>();
485 std::shared_ptr< FiniteElement<1> > fe1_cr = std::make_shared<FE_CR<1>>();
486 std::shared_ptr< FiniteElement<2> > fe2_cr = std::make_shared<FE_CR<2>>();
487 std::shared_ptr< FiniteElement<3> > fe3_cr = std::make_shared<FE_CR<3>>();
489 FESystem<0> fe0_sys( {fe0_disc, fe0_disc, fe0_cr} );
495 std::shared_ptr<DiscreteSpace> ds = std::make_shared<EqualOrderDiscreteSpace>(
mesh_, fe_sys);
496 eq_data_->dh_ = std::make_shared<DOFHandlerMultiDim>(*
mesh_);
507 uint rt_component = 0;
509 auto ele_flux_ptr = create_field_fe<3, FieldValue<3>::VectorFixed>(
eq_data_->dh_, &
eq_data_->full_solution, rt_component);
514 uint p_ele_component = 1;
515 auto ele_pressure_ptr = create_field_fe<3, FieldValue<3>::Scalar>(
eq_data_->dh_, &
eq_data_->full_solution, p_ele_component);
516 eq_fields_->field_ele_pressure.set(ele_pressure_ptr, 0.0);
518 uint p_edge_component = 2;
519 auto edge_pressure_ptr = create_field_fe<3, FieldValue<3>::Scalar>(
eq_data_->dh_, &
eq_data_->full_solution, p_edge_component);
520 eq_fields_->field_edge_pressure.set(edge_pressure_ptr, 0.0);
529 uint p_edge_component = 2;
530 eq_data_->dh_cr_ = std::make_shared<SubDOFHandlerMultiDim>(
eq_data_->dh_,p_edge_component);
535 std::shared_ptr<DiscreteSpace> ds_cr_disc = std::make_shared<EqualOrderDiscreteSpace>(
mesh_, fe_cr_disc);
536 eq_data_->dh_cr_disc_ = std::make_shared<DOFHandlerMultiDim>(*
mesh_);
537 eq_data_->dh_cr_disc_->distribute_dofs(ds_cr_disc);
547 .val<Input::AbstractRecord>(
"linear_solver");
598 if (zero_time_term_from_right) {
599 MessageOut() <<
"Flow zero time step - steady case\n";
608 MessageOut() <<
"Flow zero time step - unsteady case\n";
620 eq_data_->full_solution.local_to_ghost_begin();
621 eq_data_->full_solution.local_to_ghost_end();
638 eq_data_->full_solution.local_to_ghost_begin();
639 eq_data_->full_solution.local_to_ghost_end();
650 bool jump_time =
eq_fields_->storativity.is_jump_time();
651 if (! zero_time_term_from_left) {
652 MessageOut() <<
"Flow time step - unsteady case\n";
662 WarningOut() <<
"Output of solution discontinuous in time not supported yet.\n";
671 if (! zero_time_term_from_left && ! jump_time && output)
output_data();
678 if (zero_time_term_from_right) {
679 MessageOut() <<
"Flow time step - steady case\n";
684 }
else if (! zero_time_term_from_left && jump_time) {
685 WarningOut() <<
"Discontinuous time term not supported yet.\n";
697 return (
eq_fields_->storativity.input_list_size() == 0);
710 MessageOut().fmt(
"[nonlinear solver] norm of initial residual: {}\n", residual_norm);
713 int is_linear_common;
718 this->
max_n_it_ = nl_solver_rec.
val<
unsigned int>(
"max_it");
719 this->
min_n_it_ = nl_solver_rec.
val<
unsigned int>(
"min_it");
720 if (this->min_n_it_ > this->max_n_it_) this->min_n_it_ = this->
max_n_it_;
722 if (! is_linear_common) {
730 while (nonlinear_iteration_ < this->
min_n_it_ ||
731 (residual_norm > this->tolerance_ && nonlinear_iteration_ < this->
max_n_it_ )) {
733 convergence_history.push_back(residual_norm);
736 if (convergence_history.size() >= 5 &&
737 convergence_history[ convergence_history.size() - 1]/convergence_history[ convergence_history.size() - 2] > 0.9 &&
738 convergence_history[ convergence_history.size() - 1]/convergence_history[ convergence_history.size() - 5] > 0.8) {
744 THROW(ExcSolverDiverge() << EI_Reason(
"Stagnation."));
748 if (! is_linear_common)
754 if (is_linear_common){
757 MessageOut().fmt(
"[nonlinear solver] lin. it: {}, reason: {}, residual: {}\n",
771 MessageOut().fmt(
"[nonlinear solver] it: {} lin. it: {}, reason: {}, residual: {}\n",
774 chkerr(VecDestroy(&save_solution));
798 if(
eq_data_->mortar_method_ != MortarMethod::NoMortar){
799 auto multidim_assembler = AssemblyFlowBase::create< AssemblyMH >(
eq_fields_,
eq_data_);
801 unsigned int dim = dh_cell.dim();
802 multidim_assembler[dim-1]->fix_velocity(dh_cell);
869 unsigned int dim = dh_cell.dim();
870 assembler[dim-1]->assemble(dh_cell);
888 double zeros[100000];
889 for(
int i=0; i<100000; i++) zeros[i] = 0.0;
892 tmp_rows.reserve(200);
895 dofs.reserve(
eq_data_->dh_->max_elem_dofs());
896 dofs_ngh.reserve(
eq_data_->dh_->max_elem_dofs());
901 const uint ndofs = dh_cell.n_dofs();
903 dh_cell.get_dof_indices(dofs);
912 for (
DHCellSide neighb_side : dh_cell.neighb_sides() ) {
920 const uint ndofs_ngh = dh_neighb_cell.
n_dofs();
921 dofs_ngh.resize(ndofs_ngh);
927 const unsigned int t = dh_neighb_cell.
n_dofs() - (dh_neighb_cell.
dim()+1) + neighb_side.side().side_idx();
928 tmp_rows.push_back(dofs_ngh[t]);
942 for(
auto &isec : isec_list ) {
946 const uint ndofs_slave = dh_cell_slave.
n_dofs();
947 dofs_ngh.resize(ndofs_slave);
951 for(
unsigned int i_side=0; i_side < dh_cell_slave.
elm()->
n_sides(); i_side++) {
953 tmp_rows.push_back( dofs_ngh[(ndofs_slave+1)/2+i_side] );
963 edge_rows = dofs.
data() + nsides +1;
1002 const uint ndofs = dh_cell.n_dofs();
1003 global_dofs.resize(ndofs);
1004 dh_cell.get_dof_indices(global_dofs);
1009 double source = ele.
measure() * cs *
1016 {dh_cell.get_loc_dof_indices()[ndofs/2]}, {0}, {source});
1036 #ifdef FLOW123D_HAVE_BDDCML
1037 WarningOut() <<
"For BDDC no Schur complements are used.";
1050 THROW( ExcBddcmlNotSupported() );
1051 #endif // FLOW123D_HAVE_BDDCML
1056 WarningOut() <<
"Invalid number of Schur Complements. Using 2.";
1069 ls->LinSys::set_from_input(in_rec);
1078 ISCreateGeneral(PETSC_COMM_SELF, side_dofs_vec.size(), &(side_dofs_vec[0]), PETSC_COPY_VALUES, &is);
1093 const PetscInt *b_indices;
1094 ISGetIndices(ls->
IsB, &b_indices);
1096 for(
uint i_b=0, i_bb=0; i_b < b_size && i_bb < elem_dofs_vec.size(); i_b++) {
1097 if (b_indices[i_b] == elem_dofs_vec[i_bb])
1098 elem_dofs_vec[i_bb++] = i_b + ds->
begin();
1100 ISRestoreIndices(ls->
IsB, &b_indices);
1103 ISCreateGeneral(PETSC_COMM_SELF, elem_dofs_vec.size(), &(elem_dofs_vec[0]), PETSC_COPY_VALUES, &is);
1131 THROW( ExcUnknownSolver() );
1143 START_TIMER(
"DarcyFlowMH_Steady::assembly_linear_system");
1200 std::string output_file;
1212 PetscViewerASCIIOpen(PETSC_COMM_WORLD, output_file.c_str(), &viewer);
1213 PetscViewerSetFormat(viewer, PETSC_VIEWER_ASCII_MATLAB);
1225 double d_max = std::numeric_limits<double>::max();
1226 double h1 = d_max, h2 = d_max, h3 = d_max;
1227 double he2 = d_max, he3 = d_max;
1230 case 1: h1 = std::min(h1,ele.measure());
break;
1231 case 2: h2 = std::min(h2,ele.measure());
break;
1232 case 3: h3 = std::min(h3,ele.measure());
break;
1235 for (
unsigned int j=0; j<ele->n_sides(); j++) {
1237 case 2: he2 = std::min(he2, ele.side(j)->measure());
break;
1238 case 3: he3 = std::min(he3, ele.side(j)->measure());
break;
1242 if(h1 == d_max) h1 = 0;
1243 if(h2 == d_max) h2 = 0;
1244 if(h3 == d_max) h3 = 0;
1245 if(he2 == d_max) he2 = 0;
1246 if(he3 == d_max) he3 = 0;
1249 file = fopen(output_file.c_str(),
"a");
1251 fprintf(file,
"nB = %d;\n",
eq_data_->dh_->mesh()->get_el_ds()->size());
1253 fprintf(file,
"h1 = %e;\nh2 = %e;\nh3 = %e;\n", h1, h2, h3);
1254 fprintf(file,
"he2 = %e;\nhe3 = %e;\n", he2, he3);
1272 START_TIMER(
"DarcyFlowMH_Steady::set_mesh_data_for_bddc");
1300 dh_cell.get_dof_indices(cell_dofs_global);
1302 inet.insert(inet.end(), cell_dofs_global.begin(), cell_dofs_global.end());
1303 uint n_inet = cell_dofs_global.size();
1306 uint dim = dh_cell.elm().dim();
1307 elDimMax = std::max( elDimMax, dim );
1308 elDimMin = std::min( elDimMin, dim );
1312 isegn.push_back( dh_cell.elm_idx());
1315 for (
unsigned int si=0; si<dh_cell.elm()->n_sides(); si++) {
1316 arma::vec3 coord = dh_cell.elm().side(si)->centre();
1319 localDofMap.insert( std::make_pair( cell_dofs_global[si], coord ) );
1321 localDofMap.insert( std::make_pair( cell_dofs_global[si+dim+2], coord ) );
1324 arma::vec3 elm_centre = dh_cell.elm().centre();
1325 localDofMap.insert( std::make_pair( cell_dofs_global[dim+1], elm_centre ) );
1329 for(
DHCellSide side : dh_cell.neighb_sides()) {
1330 uint neigh_dim = side.cell().elm().dim();
1331 side.cell().get_dof_indices(cell_dofs_global);
1332 int edge_row = cell_dofs_global[neigh_dim+2+side.side_idx()];
1333 localDofMap.insert( std::make_pair( edge_row, side.centre() ) );
1334 inet.push_back( edge_row );
1337 nnet.push_back(n_inet);
1342 double conduct =
eq_fields_->conductivity.value( elm_centre , dh_cell.elm() );
1343 auto aniso =
eq_fields_->anisotropy.value( elm_centre , dh_cell.elm() );
1347 for (
int i = 0; i < 3; i++) {
1348 coef = coef + aniso.at(i,i);
1351 coef = conduct*coef / 3;
1353 ASSERT_GT( coef, 0. ).error(
"Zero coefficient of hydrodynamic resistance. \n");
1354 element_permeability.push_back( 1. / coef );
1364 auto distr =
eq_data_->dh_->distr();
1373 int numNodeSub = localDofMap.size();
1384 for ( ; itB != localDofMap.end(); ++itB ) {
1385 isngn[ind] = itB -> first;
1388 for (
int j = 0; j < 3; j++ ) {
1389 xyz[ j*numNodeSub + ind ] = coord[j];
1394 localDofMap.clear();
1402 Global2LocalMap_ global2LocalNodeMap;
1403 for (
unsigned ind = 0; ind < isngn.size(); ++ind ) {
1404 global2LocalNodeMap.insert( std::make_pair(
static_cast<unsigned>( isngn[ind] ), ind ) );
1409 for (
unsigned int iEle = 0; iEle < isegn.size(); iEle++ ) {
1410 int nne = nnet[ iEle ];
1411 for (
int ien = 0; ien < nne; ien++ ) {
1413 int indGlob = inet[indInet];
1415 Global2LocalMap_::iterator pos = global2LocalNodeMap.find( indGlob );
1416 ASSERT( pos != global2LocalNodeMap.end() )(indGlob).error(
"Cannot remap node global index to local indices.\n");
1417 int indLoc =
static_cast<int> ( pos -> second );
1420 inet[ indInet++ ] = indLoc;
1424 int numNodes =
size;
1425 int numDofsInt =
size;
1427 int meshDim = elDimMax;
1439 bddc_ls -> load_mesh( LinSys_BDDC::BDDCMatrixType::SPD_VIA_SYMMETRICGENERAL, spaceDim, numNodes, numDofsInt, inet, nnet, nndf, isegn, isngn, isngn, xyz, element_permeability, meshDim );
1461 if(
time_ !=
nullptr)
1511 LocDofVec loc_dofs = dh_cell.get_loc_dof_indices();
1512 for (
unsigned int i=0; i<ele->
n_sides(); i++) {
1516 eq_data_->full_solution.add(loc_dofs[edge_dof], init_value/n_sides_of_edge);
1520 eq_data_->full_solution.ghost_to_local_begin();
1521 eq_data_->full_solution.ghost_to_local_end();
1522 eq_data_->full_solution.local_to_ghost_begin();
1523 eq_data_->full_solution.local_to_ghost_end();
1529 START_TIMER(
"DarcyFlowMH::reconstruct_solution_from_schur");
1532 unsigned int dim = dh_cell.dim();
1533 assembler[dim-1]->assemble_reconstruct(dh_cell);
1536 eq_data_->full_solution.local_to_ghost_begin();
1537 eq_data_->full_solution.local_to_ghost_end();
1548 PetscScalar *local_diagonal;
1556 dofs.reserve(
eq_data_->dh_->max_elem_dofs());
1560 dofs.resize(dh_cell.n_dofs());
1561 dh_cell.get_dof_indices(dofs);
1564 const uint p_ele_dof = dh_cell.n_dofs() / 2;
1570 local_diagonal[dh_cell.get_loc_dof_indices()[p_ele_dof]]= - diagonal_coeff /
time_->
dt();
1573 {dh_cell.get_loc_dof_indices()[p_ele_dof]},
1574 {diagonal_coeff}, 0.0);
1576 VecRestoreArray(new_diagonal,& local_diagonal);
1577 MatDiagonalSet(*( schur0->get_matrix() ), new_diagonal, ADD_VALUES);
1579 schur0->set_matrix_changed();
1581 balance_->finish_mass_assembly(eq_data_->water_balance_idx);
1588 if (scale_factor != 1.0) {
1609 void dofs_range(
unsigned int n_dofs,
unsigned int &min,
unsigned int &max,
unsigned int component) {
1613 }
else if (component==1) {
1624 ASSERT_LT(component, 3).error(
"Invalid component!");
1625 unsigned int i, n_dofs, min, max;
1629 n_dofs = dh_cell.get_dof_indices(dof_indices);
1631 for (i=min; i<max; ++i) dof_vec.push_back(dof_indices[i]);