14 using namespace MoFEM;
16 static char help[] =
"...\n\n";
45 static boost::shared_ptr<SetUpSchur>
56 int main(
int argc,
char *argv[]) {
63 DMType dm_name =
"DMMOFEM";
65 DMType dm_name_mg =
"DMMOFEM_MG";
73 auto core_log = logging::core::get();
89 simple->getAddBoundaryFE() =
true;
92 auto add_shared_entities_on_skeleton = [&]() {
94 auto boundary_meshset =
simple->getBoundaryMeshSet();
95 auto skeleton_meshset =
simple->getSkeletonMeshSet();
101 0,
simple->getDim() - 1, skeleton_ents,
true);
102 skeleton_ents = subtract(skeleton_ents, bdy_ents);
104 CHKERR m_field.
get_moab().add_entities(skeleton_meshset, skeleton_ents);
108 CHKERR add_shared_entities_on_skeleton();
118 const char *list_bases[] = {
"ainsworth",
"ainsworth_lobatto",
"demkowicz",
121 PetscInt choice_base_value = AINSWORTH;
123 LASBASETOP, &choice_base_value, &flg);
125 if (flg != PETSC_TRUE)
128 if (choice_base_value == AINSWORTH)
130 if (choice_base_value == AINSWORTH_LOBATTO)
132 else if (choice_base_value == DEMKOWICZ)
134 else if (choice_base_value == BERNSTEIN)
137 enum spaces { hdiv, hcurl, last_space };
138 const char *list_spaces[] = {
"hdiv",
"hcurl"};
139 PetscInt choice_space_value = hdiv;
141 last_space, &choice_space_value, &flg);
142 if (flg != PETSC_TRUE)
145 if (choice_space_value == hdiv)
147 else if (choice_space_value == hcurl)
153 CHKERR simple->addDomainBrokenField(
"BROKEN", space, base, 1);
164 CHKERR bc_mng->removeSideDOFs(
simple->getProblemName(),
"ZERO_FLUX",
169 auto assemble_domain_lhs = [&](
auto &pip) {
176 IT>::OpMixDivTimesScalar<SPACE_DIM>;
182 pip.push_back(
new OpHdivHdiv(
"BROKEN",
"BROKEN", beta));
183 auto unity = []() constexpr {
return 1; };
184 pip.push_back(
new OpHdivU(
"BROKEN",
"U", unity,
true));
192 op_loop_skeleton_side->getOpPtrVector(), {});
196 auto broken_data_ptr =
197 boost::make_shared<std::vector<BrokenBaseSideData>>();
202 op_loop_domain_side->getOpPtrVector(), {HDIV});
203 op_loop_domain_side->getOpPtrVector().push_back(
206 op_loop_skeleton_side->getOpPtrVector().push_back(op_loop_domain_side);
208 IT>::OpBrokenSpaceConstrain<1>;
209 op_loop_skeleton_side->getOpPtrVector().push_back(
210 new OpC(
"HYBRID", broken_data_ptr, 1.,
true,
false));
214 constexpr
int partition = 1;
216 op_print->doWorkRhsHook = [&](
DataOperator *base_op_ptr,
int side,
220 if (
auto op_ptr =
dynamic_cast<BdyEleOp *
>(base_op_ptr)) {
221 auto fe_method = op_ptr->getFEMethod();
222 auto num_fe = fe_method->numeredEntFiniteElementPtr;
225 if (num_fe->getPStatus() & PSTATUS_SHARED)
226 MOFEM_LOG(
"SELF", Sev::inform) <<
"Num FE: " << *num_fe;
231 op_loop_skeleton_side->getOpPtrVector().push_back(op_print);
234 pip.push_back(op_loop_skeleton_side);
239 auto assemble_domain_rhs = [&](
auto &pip) {
243 AT>::LinearForm<IT>::OpSource<1, 1>;
244 auto source = [&](
const double x,
const double y,
245 const double z) constexpr {
254 CHKERR assemble_domain_lhs(pip_mng->getOpDomainLhsPipeline());
255 CHKERR assemble_domain_rhs(pip_mng->getOpDomainRhsPipeline());
262 TetPolynomialBase::switchCacheBaseOn<HDIV>(
263 {pip_mng->getDomainLhsFE().get(), pip_mng->getDomainRhsFE().get()});
264 TetPolynomialBase::switchCacheBaseOn<L2>(
265 {pip_mng->getDomainLhsFE().get(), pip_mng->getDomainRhsFE().get()});
271 auto ksp = pip_mng->createKSP();
273 CHKERR KSPSetFromOptions(ksp);
274 BOOST_LOG_SCOPED_THREAD_ATTR(
"Timeline", attrs::timer());
275 MOFEM_LOG(
"TIMER", Sev::inform) <<
"KSPSetUp";
277 MOFEM_LOG(
"TIMER", Sev::inform) <<
"KSPSetUp <= Done";
279 MOFEM_LOG(
"TIMER", Sev::inform) <<
"KSPSolve";
281 MOFEM_LOG(
"TIMER", Sev::inform) <<
"KSPSolve <= Done";
283 CHKERR VecGhostUpdateBegin(x, INSERT_VALUES, SCATTER_FORWARD);
284 CHKERR VecGhostUpdateEnd(x, INSERT_VALUES, SCATTER_FORWARD);
288 auto ksp = pip_mng->createKSP();
290 BOOST_LOG_SCOPED_THREAD_ATTR(
"Timeline", attrs::timer());
291 MOFEM_LOG(
"TIMER", Sev::inform) <<
"KSPSetUp";
292 CHKERR schur_ptr->setUp(ksp);
293 MOFEM_LOG(
"TIMER", Sev::inform) <<
"KSPSetUp <= Done";
295 MOFEM_LOG(
"TIMER", Sev::inform) <<
"KSPSolve";
297 MOFEM_LOG(
"TIMER", Sev::inform) <<
"KSPSolve <= Done";
299 CHKERR VecGhostUpdateBegin(x, INSERT_VALUES, SCATTER_FORWARD);
300 CHKERR VecGhostUpdateEnd(x, INSERT_VALUES, SCATTER_FORWARD);
305 auto check_residual = [&](
auto x,
auto f) {
311 auto &domain_rhs = pip_mng->getOpDomainRhsPipeline();
317 auto div_flux_ptr = boost::make_shared<VectorDouble>();
319 "BROKEN", div_flux_ptr));
321 AT>::LinearForm<IT>::OpBaseTimesScalarField<1>;
323 domain_rhs.push_back(
new OpUDivFlux(
"U", div_flux_ptr, beta));
324 auto source = [&](
const double x,
const double y,
325 const double z) constexpr {
return 1; };
327 AT>::LinearForm<IT>::OpSource<1, 1>;
331 IT>::OpMixDivTimesU<3, 1, SPACE_DIM>;
333 AT>::LinearForm<IT>::OpBaseTimesVector<3, 3, 1>;
334 auto flux_ptr = boost::make_shared<MatrixDouble>();
335 domain_rhs.push_back(
337 boost::shared_ptr<VectorDouble> u_ptr =
338 boost::make_shared<VectorDouble>();
340 domain_rhs.push_back(
new OpHDivH(
"BROKEN", u_ptr, beta));
341 domain_rhs.push_back(
new OpHdivFlux(
"BROKEN", flux_ptr, beta));
349 op_loop_skeleton_side->getOpPtrVector(), {});
353 auto broken_data_ptr =
354 boost::make_shared<std::vector<BrokenBaseSideData>>();
359 op_loop_domain_side->getOpPtrVector(), {HDIV});
360 op_loop_domain_side->getOpPtrVector().push_back(
362 auto flux_mat_ptr = boost::make_shared<MatrixDouble>();
363 op_loop_domain_side->getOpPtrVector().push_back(
365 op_loop_domain_side->getOpPtrVector().push_back(
369 op_loop_skeleton_side->getOpPtrVector().push_back(op_loop_domain_side);
371 IT>::OpBrokenSpaceConstrainDHybrid<1>;
373 IT>::OpBrokenSpaceConstrainDFlux<1>;
374 op_loop_skeleton_side->getOpPtrVector().push_back(
375 new OpC_dHybrid(
"HYBRID", broken_data_ptr, 1.));
376 auto hybrid_ptr = boost::make_shared<MatrixDouble>();
377 op_loop_skeleton_side->getOpPtrVector().push_back(
379 op_loop_skeleton_side->getOpPtrVector().push_back(
380 new OpC_dBroken(broken_data_ptr, hybrid_ptr, 1.));
383 domain_rhs.push_back(op_loop_skeleton_side);
386 CHKERR VecGhostUpdateBegin(
f, INSERT_VALUES, SCATTER_FORWARD);
387 CHKERR VecGhostUpdateEnd(
f, INSERT_VALUES, SCATTER_FORWARD);
389 pip_mng->getDomainRhsFE()->f =
f;
390 pip_mng->getSkeletonRhsFE()->f =
f;
391 pip_mng->getDomainRhsFE()->x = x;
392 pip_mng->getSkeletonRhsFE()->x = x;
395 simple->getDomainFEName(),
396 pip_mng->getDomainRhsFE());
398 CHKERR VecGhostUpdateBegin(
f, ADD_VALUES, SCATTER_REVERSE);
399 CHKERR VecGhostUpdateEnd(
f, ADD_VALUES, SCATTER_REVERSE);
404 CHKERR VecNorm(
f, NORM_2, &fnrm);
405 MOFEM_LOG_C(
"AT", Sev::inform,
"Residual %3.4e", fnrm);
407 constexpr
double eps = 1e-8;
410 "Residual norm larger than accepted");
415 auto calculate_error = [&]() {
419 auto &domain_rhs = pip_mng->getOpDomainRhsPipeline();
425 auto u_grad_ptr = boost::make_shared<MatrixDouble>();
426 auto flux_val_ptr = boost::make_shared<MatrixDouble>();
427 auto div_val_ptr = boost::make_shared<VectorDouble>();
428 auto source_ptr = boost::make_shared<VectorDouble>();
430 domain_rhs.push_back(
432 domain_rhs.push_back(
435 "BROKEN", div_val_ptr));
436 auto source = [&](
const double x,
const double y,
437 const double z) constexpr {
return -1; };
440 enum { DIV, GRAD,
LAST };
443 domain_rhs.push_back(
446 u_grad_ptr, mpi_vec, GRAD, flux_val_ptr));
449 simple->getDomainFEName(),
450 pip_mng->getDomainRhsFE());
451 CHKERR VecAssemblyBegin(mpi_vec);
452 CHKERR VecAssemblyEnd(mpi_vec);
455 const double *error_ind;
456 CHKERR VecGetArrayRead(mpi_vec, &error_ind);
458 <<
"Approximation error ||div flux - source||: "
459 << std::sqrt(error_ind[DIV]);
460 MOFEM_LOG(
"AT", Sev::inform) <<
"Approximation error ||grad-flux||: "
461 << std::sqrt(error_ind[GRAD]);
462 CHKERR VecRestoreArrayRead(mpi_vec, &error_ind);
468 auto get_post_proc_fe = [&]() {
471 auto post_proc_fe = boost::make_shared<PostProcEle>(m_field);
477 post_proc_fe->getOpPtrVector().push_back(op_loop_side);
480 op_loop_side->getOpPtrVector(), {HDIV});
481 auto u_vec_ptr = boost::make_shared<VectorDouble>();
482 auto flux_mat_ptr = boost::make_shared<MatrixDouble>();
483 op_loop_side->getOpPtrVector().push_back(
485 op_loop_side->getOpPtrVector().push_back(
487 op_loop_side->getOpPtrVector().push_back(
491 post_proc_fe->getPostProcMesh(),
493 post_proc_fe->getMapGaussPts(),
497 {{
"BROKEN", flux_mat_ptr}},
506 auto post_proc_fe = get_post_proc_fe();
508 simple->getBoundaryFEName(), post_proc_fe);
509 CHKERR post_proc_fe->writeFile(
"out_result.h5m");
537 CHKERR KSPSetFromOptions(ksp);
539 CHKERR KSPGetPC(ksp, &pc);
541 PetscBool is_pcfs = PETSC_FALSE;
542 PetscObjectTypeCompare((PetscObject)pc, PCFIELDSPLIT, &is_pcfs);
545 MOFEM_LOG(
"AT", Sev::inform) <<
"Setup Schur pc";
547 auto create_sub_dm = [&]() {
550 auto create_dm = [&](
552 std::string problem_name,
553 std::vector<std::string> fe_names,
554 std::vector<std::string> fields,
560 auto create_dm_imp = [&]() {
564 for (
auto fe : fe_names) {
568 for (
auto field : fields) {
577 "Error in creating schurDM. It is possible that schurDM is "
582 auto schur_dm = create_dm(
586 {
simple->getDomainFEName(),
simple->getSkeletonFEName()},
592 auto block_dm = create_dm(
596 {
simple->getDomainFEName(),
simple->getSkeletonFEName()},
602 return std::make_tuple(schur_dm, block_dm);
605 auto get_nested_mat_data = [&](
auto schur_dm,
auto block_dm) {
613 simple->getDomainFEName(),
617 {
"BROKEN",
"BROKEN"},
628 simple->getSkeletonFEName(),
632 {
"BROKEN",
"HYBRID"}, {
"HYBRID",
"BROKEN"}
644 {schur_dm, block_dm}, block_mat_data,
646 {
"BROKEN",
"U"}, {
nullptr,
nullptr}, true
651 auto set_ops = [&](
auto schur_dm) {
656 boost::shared_ptr<BlockStructure> block_data;
660 pip_mng->getOpDomainLhsPipeline().push_front(
662 pip_mng->getOpDomainLhsPipeline().push_back(
670 auto pre_proc_schur_lhs_ptr = boost::make_shared<FEMethod>();
671 auto post_proc_schur_lhs_ptr = boost::make_shared<FEMethod>();
673 pre_proc_schur_lhs_ptr->preProcessHook = [
this]() {
676 MOFEM_LOG(
"AT", Sev::verbose) <<
"Lhs Assemble Begin";
680 post_proc_schur_lhs_ptr->postProcessHook = [
this, ao_up,
681 post_proc_schur_lhs_ptr]() {
683 MOFEM_LOG(
"AT", Sev::verbose) <<
"Lhs Assemble End";
686 CHKERR MatAssemblyBegin(S, MAT_FINAL_ASSEMBLY);
687 CHKERR MatAssemblyEnd(S, MAT_FINAL_ASSEMBLY);
689 auto S_from_block =
matDuplicate(S, MAT_SHARE_NONZERO_PATTERN);
692 S_from_block, {
"BROKEN",
"U"},
693 {nullptr, nullptr}, ao_up);
694 CHKERR MatAssemblyBegin(S_from_block, MAT_FINAL_ASSEMBLY);
695 CHKERR MatAssemblyEnd(S_from_block, MAT_FINAL_ASSEMBLY);
696 CHKERR MatAYPX(S_from_block, -1, S, DIFFERENT_NONZERO_PATTERN);
698 CHKERR MatNorm(S_from_block, NORM_FROBENIUS, &norm);
699 MOFEM_LOG(
"AT", Sev::inform) <<
"Norm of difference: " << norm;
703 "Norm of difference between Schur and block matrix is larger "
708 {
"BROKEN",
"U"}, {nullptr, nullptr}, ao_up);
709 CHKERR MatAssemblyBegin(S, MAT_FINAL_ASSEMBLY);
710 CHKERR MatAssemblyEnd(S, MAT_FINAL_ASSEMBLY);
713 MOFEM_LOG(
"AT", Sev::verbose) <<
"Lhs Assemble Finish";
718 ksp_ctx_ptr->getPreProcSetOperators().push_front(pre_proc_schur_lhs_ptr);
719 ksp_ctx_ptr->getPostProcSetOperators().push_back(post_proc_schur_lhs_ptr);
724 auto set_pc = [&](
auto pc,
auto block_dm) {
726 auto block_is =
getDMSubData(block_dm)->getSmartRowIs();
727 CHKERR PCFieldSplitSetIS(pc, NULL, block_is);
728 CHKERR PCFieldSplitSetSchurPre(pc, PC_FIELDSPLIT_SCHUR_PRE_USER, S);
732 auto set_diagonal_pc = [&](
auto pc,
auto schur_dm) {
742 CHKERR PCFieldSplitSchurGetSubKSP(pc, PETSC_NULL, &subksp);
743 auto get_pc = [](
auto ksp) {
745 CHKERR KSPGetPC(ksp, &pc_raw);
750 auto set_pc_p_mg = [&](
auto dm,
auto pc) {
754 PetscBool same = PETSC_FALSE;
755 PetscObjectTypeCompare((PetscObject)pc, PCMG, &same);
760 CHKERR PCSetFromOptions(pc);
765 CHKERR set_pc_p_mg(schur_dm, get_pc(subksp[1]));
771 auto [schur_dm, block_dm] = create_sub_dm();
773 auto nested_mat_data = get_nested_mat_data(schur_dm, block_dm);
777 CHKERR MatSetDM(S, PETSC_NULL);
781 CHKERR MatSetBlockSize(S, bs);
784 CHKERR set_pc(pc, block_dm);
786 CHKERR KSPGetDM(ksp, &solver_dm);
788 CHKERR DMSetMatType(solver_dm, MATSHELL);
792 CHKERR set_diagonal_pc(pc, schur_dm);
796 "PC is not set to PCFIELDSPLIT");
801 boost::shared_ptr<SetUpSchur>
803 return boost::shared_ptr<SetUpSchur>(
new SetUpSchurImpl(m_field));