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") +
194 *
this += field_ele_pressure.name(
"pressure_p0")
197 .description(
"Pressure solution - P0 interpolation.");
199 *
this += field_edge_pressure.name(
"pressure_edge")
202 .description(
"Pressure solution - Crouzeix-Raviart interpolation.");
204 *
this += field_ele_piezo_head.name(
"piezo_head_p0")
207 .description(
"Piezo head solution - P0 interpolation.");
209 *
this += field_ele_velocity.name(
"velocity_p0")
210 .units(
UnitSI().m().s(-1))
212 .description(
"Velocity solution - P0 interpolation.");
214 *
this += flux.name(
"flux")
215 .units(
UnitSI().m().s(-1))
217 .description(
"Darcy flow flux.");
219 *
this += anisotropy.name(
"anisotropy")
220 .description(
"Anisotropy of the conductivity tensor.")
221 .input_default(
"1.0")
224 *
this += cross_section.name(
"cross_section")
225 .description(
"Complement dimension parameter (cross section for 1D, thickness for 2D).")
226 .input_default(
"1.0")
227 .units(
UnitSI().m(3).md() );
229 *
this += conductivity.name(
"conductivity")
230 .description(
"Isotropic conductivity scalar.")
231 .input_default(
"1.0")
232 .units(
UnitSI().m().s(-1) )
235 *
this += sigma.name(
"sigma")
236 .description(
"Transition coefficient between dimensions.")
237 .input_default(
"1.0")
240 *
this += water_source_density.name(
"water_source_density")
241 .description(
"Water source density.")
242 .input_default(
"0.0")
245 *
this += bc_type.name(
"bc_type")
246 .description(
"Boundary condition type.")
247 .input_selection( get_bc_type_selection() )
248 .input_default(
"\"none\"")
252 .disable_where(bc_type, {
none, seepage} )
254 .description(
"Prescribed pressure value on the boundary. Used for all values of ``bc_type`` except ``none`` and ``seepage``. "
255 "See documentation of ``bc_type`` for exact meaning of ``bc_pressure`` in individual boundary condition types.")
256 .input_default(
"0.0")
260 .disable_where(bc_type, {
none, dirichlet} )
262 .description(
"Incoming water boundary flux. Used for bc_types : ``total_flux``, ``seepage``, ``river``.")
263 .input_default(
"0.0")
264 .units(
UnitSI().m().s(-1) );
266 *
this += bc_robin_sigma
267 .disable_where(bc_type, {
none, dirichlet, seepage} )
268 .name(
"bc_robin_sigma")
269 .description(
"Conductivity coefficient in the ``total_flux`` or the ``river`` boundary condition type.")
270 .input_default(
"0.0")
273 *
this += bc_switch_pressure
274 .disable_where(bc_type, {
none, dirichlet, total_flux} )
275 .name(
"bc_switch_pressure")
276 .description(
"Critical switch pressure for ``seepage`` and ``river`` boundary conditions.")
277 .input_default(
"0.0")
282 *
this += init_pressure.name(
"init_pressure")
283 .description(
"Initial condition for pressure in time dependent problems.")
284 .input_default(
"0.0")
287 *
this += storativity.name(
"storativity")
288 .description(
"Storativity (in time dependent problems).")
289 .input_default(
"0.0")
292 *
this += extra_storativity.name(
"extra_storativity")
293 .description(
"Storativity added from upstream equation.")
295 .input_default(
"0.0")
296 .flags( input_copy );
298 *
this += extra_source.name(
"extra_water_source_density")
299 .description(
"Water source density added from upstream equation.")
300 .input_default(
"0.0")
302 .flags( input_copy );
304 *
this += gravity_field.name(
"gravity")
305 .description(
"Gravity vector.")
306 .input_default(
"0.0")
309 *
this += bc_gravity.name(
"bc_gravity")
310 .description(
"Boundary gravity vector.")
311 .input_default(
"0.0")
314 *
this += init_piezo_head.name(
"init_piezo_head")
316 .input_default(
"0.0")
317 .description(
"Init piezo head.");
319 *
this += bc_piezo_head.name(
"bc_piezo_head")
321 .input_default(
"0.0")
322 .description(
"Boundary piezo head.");
324 *
this += bc_switch_piezo_head.name(
"bc_switch_piezo_head")
326 .input_default(
"0.0")
327 .description(
"Boundary switch piezo head.");
371 MessageOut() <<
"Duplicate key 'time', time in flow equation is already initialized from parent class!";
378 data_ = make_shared<EqData>();
381 data_->is_linear=
true;
411 gravity_array.copy_to(gvec);
414 data_->gravity_vec_ =
data_->gravity_.subvec(0,2);
417 auto field_algo=std::make_shared<FieldConstant<3, FieldValue<3>::VectorFixed>>();
418 field_algo->set_value(gvalue);
419 data_->gravity_field.set(field_algo, 0.0);
420 data_->bc_gravity.set(field_algo, 0.0);
422 data_->bc_pressure.add_factory(
425 data_->bc_switch_pressure.add_factory(
428 data_->init_pressure.add_factory(
438 MessageOut() <<
"Missing the key 'time', obligatory for the transient problems." << endl;
451 std::shared_ptr< FiniteElement<1> > fe1_rt = std::make_shared<FE_RT0_disc<1>>();
452 std::shared_ptr< FiniteElement<2> > fe2_rt = std::make_shared<FE_RT0_disc<2>>();
453 std::shared_ptr< FiniteElement<3> > fe3_rt = std::make_shared<FE_RT0_disc<3>>();
454 std::shared_ptr< FiniteElement<0> > fe0_disc = std::make_shared<FE_P_disc<0>>(0);
455 std::shared_ptr< FiniteElement<1> > fe1_disc = std::make_shared<FE_P_disc<1>>(0);
456 std::shared_ptr< FiniteElement<2> > fe2_disc = std::make_shared<FE_P_disc<2>>(0);
457 std::shared_ptr< FiniteElement<3> > fe3_disc = std::make_shared<FE_P_disc<3>>(0);
458 std::shared_ptr< FiniteElement<0> > fe0_cr = std::make_shared<FE_CR<0>>();
459 std::shared_ptr< FiniteElement<1> > fe1_cr = std::make_shared<FE_CR<1>>();
460 std::shared_ptr< FiniteElement<2> > fe2_cr = std::make_shared<FE_CR<2>>();
461 std::shared_ptr< FiniteElement<3> > fe3_cr = std::make_shared<FE_CR<3>>();
463 FESystem<0> fe0_sys( {fe0_disc, fe0_disc, fe0_cr} );
469 std::shared_ptr<DiscreteSpace> ds = std::make_shared<EqualOrderDiscreteSpace>(
mesh_, fe_sys);
470 data_->dh_ = std::make_shared<DOFHandlerMultiDim>(*
mesh_);
471 data_->dh_->distribute_dofs(ds);
475 this->
data_->multidim_assembler = AssemblyBase::create< AssemblyMH >(
data_);
478 data_->add_coords_field();
481 uint rt_component = 0;
482 data_->full_solution =
data_->dh_->create_vector();
483 auto ele_flux_ptr = create_field_fe<3, FieldValue<3>::VectorFixed>(
data_->dh_, &
data_->full_solution, rt_component);
484 data_->flux.set(ele_flux_ptr, 0.0);
488 uint p_ele_component = 1;
489 auto ele_pressure_ptr = create_field_fe<3, FieldValue<3>::Scalar>(
data_->dh_, &
data_->full_solution, p_ele_component);
490 data_->field_ele_pressure.set(ele_pressure_ptr, 0.0);
492 uint p_edge_component = 2;
493 auto edge_pressure_ptr = create_field_fe<3, FieldValue<3>::Scalar>(
data_->dh_, &
data_->full_solution, p_edge_component);
494 data_->field_edge_pressure.set(edge_pressure_ptr, 0.0);
496 data_->field_ele_piezo_head.set(
503 uint p_edge_component = 2;
504 data_->dh_cr_ = std::make_shared<SubDOFHandlerMultiDim>(
data_->dh_,p_edge_component);
509 std::shared_ptr<DiscreteSpace> ds_cr_disc = std::make_shared<EqualOrderDiscreteSpace>(
mesh_, fe_cr_disc);
510 data_->dh_cr_disc_ = std::make_shared<DOFHandlerMultiDim>(*
mesh_);
511 data_->dh_cr_disc_->distribute_dofs(ds_cr_disc);
521 .val<Input::AbstractRecord>(
"linear_solver");
531 VecCreateMPI(PETSC_COMM_WORLD,
data_->dh_->distr()->lsize(),PETSC_DETERMINE,&(
steady_diagonal));
540 data_->water_balance_idx =
balance_->add_quantity(
"water_volume");
570 if (zero_time_term_from_right) {
601 data_->full_solution.local_to_ghost_begin();
602 data_->full_solution.local_to_ghost_end();
612 bool jump_time =
data_->storativity.is_jump_time();
613 if (! zero_time_term_from_left) {
623 WarningOut() <<
"Output of solution discontinuous in time not supported yet.\n";
632 if (! zero_time_term_from_left && ! jump_time && output)
output_data();
638 if (zero_time_term_from_right) {
643 }
else if (! zero_time_term_from_left && jump_time) {
644 WarningOut() <<
"Discontinuous time term not supported yet.\n";
656 return (
data_->storativity.input_list_size() == 0);
669 MessageOut().fmt(
"[nonlinear solver] norm of initial residual: {}\n", residual_norm);
672 int is_linear_common;
677 this->
max_n_it_ = nl_solver_rec.
val<
unsigned int>(
"max_it");
678 this->
min_n_it_ = nl_solver_rec.
val<
unsigned int>(
"min_it");
679 if (this->min_n_it_ > this->max_n_it_) this->min_n_it_ = this->
max_n_it_;
681 if (! is_linear_common) {
689 while (nonlinear_iteration_ < this->
min_n_it_ ||
690 (residual_norm > this->tolerance_ && nonlinear_iteration_ < this->
max_n_it_ )) {
692 convergence_history.push_back(residual_norm);
695 if (convergence_history.size() >= 5 &&
696 convergence_history[ convergence_history.size() - 1]/convergence_history[ convergence_history.size() - 2] > 0.9 &&
697 convergence_history[ convergence_history.size() - 1]/convergence_history[ convergence_history.size() - 5] > 0.8) {
703 THROW(ExcSolverDiverge() << EI_Reason(
"Stagnation."));
707 if (! is_linear_common)
713 if (is_linear_common){
716 MessageOut().fmt(
"[nonlinear solver] lin. it: {}, reason: {}, residual: {}\n",
730 MessageOut().fmt(
"[nonlinear solver] it: {} lin. it: {}, reason: {}, residual: {}\n",
733 chkerr(VecDestroy(&save_solution));
757 if(
data_->mortar_method_ != MortarMethod::NoMortar){
758 auto multidim_assembler = AssemblyBase::create< AssemblyMH >(
data_);
760 unsigned int dim = dh_cell.dim();
761 multidim_assembler[dim-1]->fix_velocity(dh_cell);
828 unsigned int dim = dh_cell.dim();
829 assembler[dim-1]->assemble(dh_cell);
847 double zeros[100000];
848 for(
int i=0; i<100000; i++) zeros[i] = 0.0;
851 tmp_rows.reserve(200);
854 dofs.reserve(
data_->dh_->max_elem_dofs());
855 dofs_ngh.reserve(
data_->dh_->max_elem_dofs());
860 const uint ndofs = dh_cell.n_dofs();
862 dh_cell.get_dof_indices(dofs);
871 for (
DHCellSide neighb_side : dh_cell.neighb_sides() ) {
879 const uint ndofs_ngh = dh_neighb_cell.
n_dofs();
880 dofs_ngh.resize(ndofs_ngh);
886 const unsigned int t = dh_neighb_cell.
n_dofs() - (dh_neighb_cell.
dim()+1) + neighb_side.side().side_idx();
887 tmp_rows.push_back(dofs_ngh[t]);
901 for(
auto &isec : isec_list ) {
905 const uint ndofs_slave = dh_cell_slave.
n_dofs();
906 dofs_ngh.resize(ndofs_slave);
910 for(
unsigned int i_side=0; i_side < dh_cell_slave.
elm()->
n_sides(); i_side++) {
912 tmp_rows.push_back( dofs_ngh[(ndofs_slave+1)/2+i_side] );
922 edge_rows = dofs.
data() + nsides +1;
961 const uint ndofs = dh_cell.n_dofs();
962 global_dofs.resize(ndofs);
963 dh_cell.get_dof_indices(global_dofs);
965 double cs =
data_->cross_section.value(ele.
centre(), ele);
968 double source = ele.
measure() * cs *
969 (
data_->water_source_density.value(ele.
centre(), ele)
975 {dh_cell.get_loc_dof_indices()[ndofs/2]}, {0}, {source});
995 #ifdef FLOW123D_HAVE_BDDCML
996 WarningOut() <<
"For BDDC no Schur complements are used.";
1009 THROW( ExcBddcmlNotSupported() );
1010 #endif // FLOW123D_HAVE_BDDCML
1015 WarningOut() <<
"Invalid number of Schur Complements. Using 2.";
1028 ls->LinSys::set_from_input(in_rec);
1037 ISCreateGeneral(PETSC_COMM_SELF, side_dofs_vec.size(), &(side_dofs_vec[0]), PETSC_COPY_VALUES, &is);
1052 const PetscInt *b_indices;
1053 ISGetIndices(ls->
IsB, &b_indices);
1055 for(
uint i_b=0, i_bb=0; i_b < b_size && i_bb < elem_dofs_vec.size(); i_b++) {
1056 if (b_indices[i_b] == elem_dofs_vec[i_bb])
1057 elem_dofs_vec[i_bb++] = i_b + ds->
begin();
1059 ISRestoreIndices(ls->
IsB, &b_indices);
1062 ISCreateGeneral(PETSC_COMM_SELF, elem_dofs_vec.size(), &(elem_dofs_vec[0]), PETSC_COPY_VALUES, &is);
1090 THROW( ExcUnknownSolver() );
1102 START_TIMER(
"DarcyFlowMH_Steady::assembly_linear_system");
1104 data_->is_linear=
true;
1159 std::string output_file;
1171 PetscViewerASCIIOpen(PETSC_COMM_WORLD, output_file.c_str(), &viewer);
1172 PetscViewerSetFormat(viewer, PETSC_VIEWER_ASCII_MATLAB);
1184 double d_max = std::numeric_limits<double>::max();
1185 double h1 = d_max, h2 = d_max, h3 = d_max;
1186 double he2 = d_max, he3 = d_max;
1189 case 1: h1 = std::min(h1,ele.measure());
break;
1190 case 2: h2 = std::min(h2,ele.measure());
break;
1191 case 3: h3 = std::min(h3,ele.measure());
break;
1194 for (
unsigned int j=0; j<ele->n_sides(); j++) {
1196 case 2: he2 = std::min(he2, ele.side(j)->measure());
break;
1197 case 3: he3 = std::min(he3, ele.side(j)->measure());
break;
1201 if(h1 == d_max) h1 = 0;
1202 if(h2 == d_max) h2 = 0;
1203 if(h3 == d_max) h3 = 0;
1204 if(he2 == d_max) he2 = 0;
1205 if(he3 == d_max) he3 = 0;
1208 file = fopen(output_file.c_str(),
"a");
1209 fprintf(file,
"nA = %d;\n",
data_->dh_cr_disc_->distr()->size());
1210 fprintf(file,
"nB = %d;\n",
data_->dh_->mesh()->get_el_ds()->size());
1211 fprintf(file,
"nBF = %d;\n",
data_->dh_cr_->distr()->size());
1212 fprintf(file,
"h1 = %e;\nh2 = %e;\nh3 = %e;\n", h1, h2, h3);
1213 fprintf(file,
"he2 = %e;\nhe3 = %e;\n", he2, he3);
1231 START_TIMER(
"DarcyFlowMH_Steady::set_mesh_data_for_bddc");
1259 dh_cell.get_dof_indices(cell_dofs_global);
1261 inet.insert(inet.end(), cell_dofs_global.begin(), cell_dofs_global.end());
1262 uint n_inet = cell_dofs_global.size();
1265 uint dim = dh_cell.elm().dim();
1266 elDimMax = std::max( elDimMax, dim );
1267 elDimMin = std::min( elDimMin, dim );
1271 isegn.push_back( dh_cell.elm_idx());
1274 for (
unsigned int si=0; si<dh_cell.elm()->n_sides(); si++) {
1275 arma::vec3 coord = dh_cell.elm().side(si)->centre();
1278 localDofMap.insert( std::make_pair( cell_dofs_global[si], coord ) );
1280 localDofMap.insert( std::make_pair( cell_dofs_global[si+dim+2], coord ) );
1283 arma::vec3 elm_centre = dh_cell.elm().centre();
1284 localDofMap.insert( std::make_pair( cell_dofs_global[dim+1], elm_centre ) );
1288 for(
DHCellSide side : dh_cell.neighb_sides()) {
1289 uint neigh_dim = side.cell().elm().dim();
1290 side.cell().get_dof_indices(cell_dofs_global);
1291 int edge_row = cell_dofs_global[neigh_dim+2+side.side_idx()];
1292 localDofMap.insert( std::make_pair( edge_row, side.centre() ) );
1293 inet.push_back( edge_row );
1296 nnet.push_back(n_inet);
1301 double conduct =
data_->conductivity.value( elm_centre , dh_cell.elm() );
1302 auto aniso =
data_->anisotropy.value( elm_centre , dh_cell.elm() );
1306 for (
int i = 0; i < 3; i++) {
1307 coef = coef + aniso.at(i,i);
1310 coef = conduct*coef / 3;
1313 "Zero coefficient of hydrodynamic resistance %f . \n ", coef );
1314 element_permeability.push_back( 1. / coef );
1324 auto distr =
data_->dh_->distr();
1333 int numNodeSub = localDofMap.size();
1344 for ( ; itB != localDofMap.end(); ++itB ) {
1345 isngn[ind] = itB -> first;
1348 for (
int j = 0; j < 3; j++ ) {
1349 xyz[ j*numNodeSub + ind ] = coord[j];
1354 localDofMap.clear();
1362 Global2LocalMap_ global2LocalNodeMap;
1363 for (
unsigned ind = 0; ind < isngn.size(); ++ind ) {
1364 global2LocalNodeMap.insert( std::make_pair(
static_cast<unsigned>( isngn[ind] ), ind ) );
1369 for (
unsigned int iEle = 0; iEle < isegn.size(); iEle++ ) {
1370 int nne = nnet[ iEle ];
1371 for (
int ien = 0; ien < nne; ien++ ) {
1373 int indGlob = inet[indInet];
1375 Global2LocalMap_::iterator pos = global2LocalNodeMap.find( indGlob );
1376 OLD_ASSERT( pos != global2LocalNodeMap.end(),
1377 "Cannot remap node index %d to local indices. \n ", indGlob );
1378 int indLoc =
static_cast<int> ( pos -> second );
1381 inet[ indInet++ ] = indLoc;
1385 int numNodes =
size;
1386 int numDofsInt =
size;
1388 int meshDim = elDimMax;
1400 bddc_ls -> load_mesh( LinSys_BDDC::BDDCMatrixType::SPD_VIA_SYMMETRICGENERAL, spaceDim, numNodes, numDofsInt, inet, nnet, nndf, isegn, isngn, isngn, xyz, element_permeability, meshDim );
1422 if(
time_ !=
nullptr)
1475 const IntIdx p_ele_dof = dh_cell.get_loc_dof_indices()[dh_cell.n_dofs()/2];
1476 local_sol[p_ele_dof] =
data_->init_pressure.value(ele.
centre(),ele);
1487 PetscScalar *local_diagonal;
1495 dofs.reserve(
data_->dh_->max_elem_dofs());
1499 dofs.resize(dh_cell.n_dofs());
1500 dh_cell.get_dof_indices(dofs);
1503 const uint p_ele_dof = dh_cell.n_dofs() / 2;
1505 double diagonal_coeff =
data_->cross_section.value(ele.
centre(), ele)
1507 +
data_->extra_storativity.value(ele.
centre(), ele))
1509 local_diagonal[dh_cell.get_loc_dof_indices()[p_ele_dof]]= - diagonal_coeff /
time_->
dt();
1511 balance_->add_mass_values(
data_->water_balance_idx, dh_cell,
1512 {dh_cell.get_loc_dof_indices()[p_ele_dof]},
1513 {diagonal_coeff}, 0.0);
1515 VecRestoreArray(new_diagonal,& local_diagonal);
1516 MatDiagonalSet(*( schur0->get_matrix() ), new_diagonal, ADD_VALUES);
1518 schur0->set_matrix_changed();
1520 balance_->finish_mass_assembly(data_->water_balance_idx);
1527 if (scale_factor != 1.0) {
1548 void dofs_range(
unsigned int n_dofs,
unsigned int &min,
unsigned int &max,
unsigned int component) {
1552 }
else if (component==1) {
1564 unsigned int i, n_dofs, min, max;
1568 n_dofs = dh_cell.get_dof_indices(dof_indices);
1570 for (i=min; i<max; ++i) dof_vec.push_back(dof_indices[i]);