294 const Elem *
const elem,
309 const FEType enriched_scalar_fe_type =
311 std::unique_ptr<FEBase> enriched_scalar_fe(
FEBase::build(
dim, enriched_scalar_fe_type));
312 std::unique_ptr<FEBase> enriched_scalar_fe_face(
FEBase::build(
dim, enriched_scalar_fe_type));
315 enriched_scalar_fe->attach_quadrature_rule(&qrule);
316 enriched_scalar_fe_face->attach_quadrature_rule(&qface);
319 const auto & JxW = vector_fe.
get_JxW();
320 const auto & q_point = vector_fe.
get_xyz();
321 const auto & scalar_phi = scalar_fe.
get_phi();
322 const auto & enriched_scalar_phi = enriched_scalar_fe->get_phi();
323 const auto & enriched_scalar_dphi = enriched_scalar_fe->get_dphi();
326 const auto & vector_phi_face = vector_fe_face.
get_phi();
327 const auto & scalar_phi_face = scalar_fe_face.
get_phi();
328 const auto & enriched_scalar_phi_face = enriched_scalar_fe_face->get_phi();
329 const auto & lambda_phi_face = lambda_fe_face.
get_phi();
330 const auto & JxW_face = scalar_fe_face.
get_JxW();
331 const auto & normals = vector_fe_face.
get_normals();
336 std::vector<dof_id_type> enriched_scalar_dof_indices;
339 std::vector<Number> lambda_qps;
341 std::vector<Number> scalar_qps;
343 std::vector<Gradient> vector_qps;
346 const auto enriched_scalar_n_dofs = enriched_scalar_dof_indices.size();
348 const auto m = enriched_scalar_n_dofs + 1;
349 const auto n = enriched_scalar_n_dofs + 1;
351 K_enriched_scalar.
resize(m, n);
352 F_enriched_scalar.
resize(m);
353 U_enriched_scalar.
resize(n);
355 enriched_scalar_fe->reinit(elem);
367 for (
const auto i :
make_range(enriched_scalar_n_dofs))
368 for (
const auto j :
make_range(enriched_scalar_n_dofs))
369 K_enriched_scalar(i, j) +=
370 JxW[qp] * (enriched_scalar_dphi[i][qp] * enriched_scalar_dphi[j][qp]);
374 const Real x = q_point[qp](0);
375 const Real y = q_point[qp](1);
376 const Real z = q_point[qp](2);
388 for (
const auto i :
make_range(enriched_scalar_n_dofs))
389 F_enriched_scalar(i) += JxW[qp] * enriched_scalar_phi[i][qp] * f;
395 for (
const auto i :
make_range(enriched_scalar_n_dofs))
396 K_enriched_scalar(i, enriched_scalar_n_dofs) += JxW[qp] * enriched_scalar_phi[i][qp];
399 for (
const auto j :
make_range(enriched_scalar_n_dofs))
400 K_enriched_scalar(enriched_scalar_n_dofs, j) += JxW[qp] * enriched_scalar_phi[j][qp];
403 F_enriched_scalar(enriched_scalar_n_dofs) += JxW[qp] * scalar_qps[qp];
407 bool tau_found =
false;
411 vector_fe_face.
reinit(elem, side);
414 enriched_scalar_fe_face->reinit(elem, side);
423 for (
const auto i :
make_range(enriched_scalar_n_dofs))
424 F_enriched_scalar(i) -=
425 JxW_face[qp] * enriched_scalar_phi_face[i][qp] * vector_qps[qp] * normals[qp];
428 scalar_fe_face.
reinit(elem, side);
430 lambda_fe_face.
reinit(elem, side);
435 const Real tau = tau_found ? 0 : 1 / elem->
hmin();
440 const auto normal = normals[qp];
441 const auto qhat = vector_qps[qp] + tau * (scalar_qps[qp] - lambda_qps[qp]) * normal;
444 for (
const auto i :
make_range(enriched_scalar_n_dofs))
445 F_enriched_scalar(i) -= JxW_face[qp] * enriched_scalar_phi_face[i][qp] * qhat * normal;
450 K_enriched_scalar.
lu_solve(F_enriched_scalar, U_enriched_scalar);
455 for (
const auto i :
make_range(enriched_scalar_n_dofs))
456 U_insertion(i) = U_enriched_scalar(i);
457 system.
solution->insert(U_insertion, enriched_scalar_dof_indices);
475 const auto & lambda_dof_map = lambda_system.
get_dof_map();
479 const FEType lambda_fe_type =
490 vector_fe->attach_quadrature_rule(&qrule);
491 scalar_fe->attach_quadrature_rule(&qrule);
503 vector_fe_face->attach_quadrature_rule(&qface);
504 scalar_fe_face->attach_quadrature_rule(&qface);
505 lambda_fe_face->attach_quadrature_rule(&qface);
508 const auto & JxW = vector_fe->get_JxW();
509 const auto & q_point = vector_fe->get_xyz();
510 const auto & vector_phi = vector_fe->get_phi();
511 const auto & scalar_phi = scalar_fe->get_phi();
512 const auto & grad_scalar_phi = scalar_fe->get_dphi();
513 const auto & div_vector_phi = vector_fe->get_div_phi();
516 const auto & vector_phi_face = vector_fe_face->get_phi();
517 const auto & scalar_phi_face = scalar_fe_face->get_phi();
518 const auto & lambda_phi_face = lambda_fe_face->get_phi();
519 const auto & JxW_face = scalar_fe_face->get_JxW();
520 const auto & qface_point = vector_fe_face->get_xyz();
521 const auto & normals = vector_fe_face->get_normals();
538 EigenMatrix A, Ainv,
B, Bt, C, Ct, Sinv, D, E, Et, H;
545 std::vector<dof_id_type> vector_dof_indices;
546 std::vector<dof_id_type> scalar_dof_indices;
547 std::vector<dof_id_type> lambda_dof_indices;
548 std::vector<Number> lambda_solution_std_vec;
553 for (
const auto & elem :
mesh.active_local_element_ptr_range())
556 dof_map.dof_indices(elem, vector_dof_indices, system.variable_number(
"q"));
557 dof_map.dof_indices(elem, scalar_dof_indices, system.variable_number(
"u"));
558 lambda_dof_map.dof_indices(elem, lambda_dof_indices, lambda_system.
variable_number(
"lambda"));
560 const auto vector_n_dofs = vector_dof_indices.size();
561 const auto scalar_n_dofs = scalar_dof_indices.size();
562 const auto lambda_n_dofs = lambda_dof_indices.size();
566 scalar_fe->reinit(elem);
568 libmesh_assert_equal_to(vector_n_dofs, vector_phi.size());
569 libmesh_assert_equal_to(scalar_n_dofs, scalar_phi.size());
572 A.setZero(vector_n_dofs, vector_n_dofs);
573 B.setZero(vector_n_dofs, scalar_n_dofs);
574 C.setZero(vector_n_dofs, lambda_n_dofs);
575 G.setZero(vector_n_dofs);
577 Bt.setZero(scalar_n_dofs, vector_n_dofs);
578 D.setZero(scalar_n_dofs, scalar_n_dofs);
579 E.setZero(scalar_n_dofs, lambda_n_dofs);
580 F.setZero(scalar_n_dofs);
582 Ct.setZero(lambda_n_dofs, vector_n_dofs);
583 Et.setZero(lambda_n_dofs, scalar_n_dofs);
584 H.setZero(lambda_n_dofs, lambda_n_dofs);
589 for (
const auto i :
make_range(vector_n_dofs))
590 for (
const auto j :
make_range(vector_n_dofs))
591 A(i, j) -= JxW[qp] * (vector_phi[i][qp] * vector_phi[j][qp]);
594 for (
const auto i :
make_range(vector_n_dofs))
595 for (
const auto j :
make_range(scalar_n_dofs))
596 B(i, j) += JxW[qp] * (div_vector_phi[i][qp] * scalar_phi[j][qp]);
599 for (
const auto i :
make_range(scalar_n_dofs))
600 for (
const auto j :
make_range(vector_n_dofs))
601 Bt(i, j) += JxW[qp] * (grad_scalar_phi[i][qp] * vector_phi[j][qp]);
608 const Real x = q_point[qp](0);
609 const Real y = q_point[qp](1);
610 const Real z = q_point[qp](2);
622 for (
const auto i :
make_range(scalar_n_dofs))
623 F(i) -= JxW[qp] * scalar_phi[i][qp] * f;
628 bool tau_found =
false;
629 for (
auto side : elem->side_index_range())
632 vector_fe_face->reinit(elem, side);
633 scalar_fe_face->reinit(elem, side);
634 lambda_fe_face->reinit(elem, side);
636 if (elem->neighbor_ptr(side) ==
nullptr)
639 const Real xf = qface_point[qp](0);
640 const Real yf = qface_point[qp](1);
641 const Real zf = qface_point[qp](2);
644 Real scalar_value = 0;
651 for (
const auto i :
make_range(vector_n_dofs))
652 G(i) += JxW_face[qp] * (vector_phi_face[i][qp] * normals[qp]) * scalar_value;
659 for (
const auto i :
make_range(scalar_n_dofs))
660 for (
const auto j :
make_range(vector_n_dofs))
662 JxW_face[qp] * scalar_phi_face[i][qp] * (vector_phi_face[j][qp] * normals[qp]);
667 for (
const auto i :
make_range(lambda_n_dofs))
668 for (
const auto j :
make_range(lambda_n_dofs))
669 H(i, j) += JxW_face[qp] * lambda_phi_face[i][qp] * lambda_phi_face[j][qp];
676 const Real tau = tau_found ? 0 : 1 / elem->hmin();
681 const auto normal = normals[qp];
682 const auto normal_sq = normal * normal;
685 for (
const auto i :
make_range(vector_n_dofs))
686 for (
const auto j :
make_range(lambda_n_dofs))
688 JxW_face[qp] * (vector_phi_face[i][qp] * normals[qp]) * lambda_phi_face[j][qp];
693 for (
const auto i :
make_range(scalar_n_dofs))
695 for (
const auto j :
make_range(vector_n_dofs))
696 Bt(i, j) -= JxW_face[qp] * scalar_phi_face[i][qp] * (vector_phi_face[j][qp] * normal);
698 for (
const auto j :
make_range(scalar_n_dofs))
700 JxW_face[qp] * scalar_phi_face[i][qp] * tau * scalar_phi_face[j][qp] * normal_sq;
702 for (
const auto j :
make_range(lambda_n_dofs))
704 JxW_face[qp] * scalar_phi_face[i][qp] * tau * lambda_phi_face[j][qp] * normal_sq;
710 for (
const auto i :
make_range(lambda_n_dofs))
712 for (
const auto j :
make_range(vector_n_dofs))
713 Ct(i, j) -= JxW_face[qp] * lambda_phi_face[i][qp] * (vector_phi_face[j][qp] * normal);
715 for (
const auto j :
make_range(scalar_n_dofs))
717 JxW_face[qp] * lambda_phi_face[i][qp] * tau * scalar_phi_face[j][qp] * normal_sq;
719 for (
const auto j :
make_range(lambda_n_dofs))
721 JxW_face[qp] * lambda_phi_face[i][qp] * tau * lambda_phi_face[j][qp] * normal_sq;
729 Sinv = (D - Bt * Ainv *
B).inverse();
730 const auto BtAinvCMinusE = Bt * Ainv * C - E;
731 const auto BtAinvGMinusF = Bt * Ainv * G - F;
732 const auto CtAinv = Ct * Ainv;
733 const auto BSinv =
B * Sinv;
734 const auto EtSinv = Et * Sinv;
735 K = -CtAinv * (BSinv * BtAinvCMinusE + C) + EtSinv * BtAinvCMinusE + H;
736 P = -CtAinv * (BSinv * BtAinvGMinusF + G) + EtSinv * BtAinvGMinusF;
739 K_libmesh.
resize(K.rows(), K.cols());
740 P_libmesh.
resize(P.size());
746 K_libmesh(i, j) = K(i, j);
753 dof_map.constrain_element_matrix_and_vector(K_libmesh, P_libmesh, lambda_dof_indices);
754 matrix.
add_matrix(K_libmesh, lambda_dof_indices);
755 lambda_system.rhs->
add_vector(P_libmesh, lambda_dof_indices);
765 Lambda.resize(lambda_n_dofs);
767 for (
const auto i :
make_range(lambda_n_dofs))
768 Lambda(i) = lambda_solution_std_vec[i];
769 const auto scalar_soln = Sinv * (BtAinvCMinusE * Lambda - BtAinvGMinusF);
770 const auto vector_soln = Ainv * (G -
B * scalar_soln - C * Lambda);
771 for (
const auto i :
make_range(vector_n_dofs))
772 system.solution->set(vector_dof_indices[i], vector_soln(i));
773 for (
const auto i :
make_range(scalar_n_dofs))
774 system.solution->set(scalar_dof_indices[i], scalar_soln(i));
797 system.solution->close();
844 const auto & lambda_dof_map = lambda_system.
get_dof_map();
848 const FEType lambda_fe_type =
859 vector_fe->attach_quadrature_rule(&qrule);
860 scalar_fe->attach_quadrature_rule(&qrule);
872 vector_fe_face->attach_quadrature_rule(&qface);
873 scalar_fe_face->attach_quadrature_rule(&qface);
874 lambda_fe_face->attach_quadrature_rule(&qface);
877 const auto & JxW = vector_fe->get_JxW();
878 const auto & q_point = vector_fe->get_xyz();
879 const auto & vector_phi = vector_fe->get_phi();
880 const auto & scalar_phi = scalar_fe->get_phi();
881 const auto & grad_scalar_phi = scalar_fe->get_dphi();
882 const auto & div_vector_phi = vector_fe->get_div_phi();
885 const auto & vector_phi_face = vector_fe_face->get_phi();
886 const auto & scalar_phi_face = scalar_fe_face->get_phi();
887 const auto & lambda_phi_face = lambda_fe_face->get_phi();
888 const auto & JxW_face = scalar_fe_face->get_JxW();
889 const auto & qface_point = vector_fe_face->get_xyz();
890 const auto & normals = vector_fe_face->get_normals();
908 std::vector<dof_id_type> vector_dof_indices;
909 std::vector<dof_id_type> scalar_dof_indices;
910 std::vector<dof_id_type> lambda_dof_indices;
911 std::vector<Number> lambda_solution_std_vec;
915 std::vector<Number> mu;
918 auto & matrix = lambda_system.get_system_matrix();
920 auto compute_and_invert_K =
921 [&](
const auto vector_n_dofs_in,
const auto scalar_n_dofs_in,
const Elem *
const elem_in)
923 const auto mixed_size = vector_n_dofs_in + scalar_n_dofs_in;
925 K_mixed.setZero(mixed_size, mixed_size);
930 for (
const auto i :
make_range(vector_n_dofs_in))
931 for (
const auto j :
make_range(vector_n_dofs_in))
932 K_mixed(i, j) += JxW[qp] * (vector_phi[i][qp] * vector_phi[j][qp]);
935 for (
const auto i :
make_range(vector_n_dofs_in))
936 for (
const auto j :
make_range(scalar_n_dofs_in))
937 K_mixed(i, j + vector_n_dofs_in) -= JxW[qp] * (div_vector_phi[i][qp] * scalar_phi[j][qp]);
940 for (
const auto i :
make_range(scalar_n_dofs_in))
941 for (
const auto j :
make_range(vector_n_dofs_in))
942 K_mixed(i + vector_n_dofs_in, j) -= JxW[qp] * (grad_scalar_phi[i][qp] * vector_phi[j][qp]);
946 bool tau_found =
false;
947 for (
auto side : elem_in->side_index_range())
950 vector_fe_face->
reinit(elem_in, side);
951 scalar_fe_face->reinit(elem_in, side);
952 const bool internal_face = elem_in->neighbor_ptr(side);
953 const auto tau =
compute_tau(internal_face, tau_found, elem_in);
957 const auto normal = normals[qp];
958 const auto normal_sq = normal * normal;
963 for (
const auto i :
make_range(scalar_n_dofs_in))
965 for (
const auto j :
make_range(vector_n_dofs_in))
966 K_mixed(i + vector_n_dofs_in, j) +=
967 JxW_face[qp] * scalar_phi_face[i][qp] * (vector_phi_face[j][qp] * normal);
970 for (
const auto j :
make_range(scalar_n_dofs_in))
971 K_mixed(i + vector_n_dofs_in, j + vector_n_dofs_in) +=
972 JxW_face[qp] * scalar_phi_face[i][qp] * tau * scalar_phi_face[j][qp] * normal_sq;
977 Kinv_mixed = K_mixed.inverse();
980 auto compute_rhs = [&](
const auto vector_n_dofs_in,
981 const auto scalar_n_dofs_in,
982 const Elem *
const elem_in,
983 const unsigned int shape_function)
985 const auto mixed_size = vector_n_dofs_in + scalar_n_dofs_in;
986 F_mixed.setZero(mixed_size);
994 const Real x = q_point[qp](0);
995 const Real y = q_point[qp](1);
996 const Real z = q_point[qp](2);
1003 for (
const auto ii :
make_range(scalar_n_dofs_in))
1004 F_mixed(ii + vector_n_dofs_in) += JxW[qp] * f * scalar_phi[ii][qp];
1008 bool tau_found =
false;
1009 std::vector<Number> g;
1011 for (
auto side : elem_in->side_index_range())
1014 vector_fe_face->reinit(elem_in, side);
1015 scalar_fe_face->reinit(elem_in, side);
1016 lambda_fe_face->reinit(elem_in, side);
1018 const auto & qp_mu = [&]()
1022 if (elem_in->neighbor_ptr(side))
1025 return lambda_solution_std_vec;
1032 auto & g_qp = g[qp];
1033 const Real xf = qface_point[qp](0);
1034 const Real yf = qface_point[qp](1);
1035 const Real zf = qface_point[qp](2);
1048 auto & real_mu = lambda_phi_face[shape_function];
1051 mu[qp] = real_mu[qp];
1056 const bool internal_face = elem_in->neighbor_ptr(side);
1057 const auto tau =
compute_tau(internal_face, tau_found, elem_in);
1061 const auto normal = normals[qp];
1062 const auto normal_sq = normal * normal;
1065 for (
const auto ii :
make_range(vector_n_dofs_in))
1066 F_mixed(ii) -= JxW_face[qp] * (vector_phi_face[ii][qp] * normal) * qp_mu[qp];
1071 for (
const auto ii :
make_range(scalar_n_dofs_in))
1072 F_mixed(ii + vector_n_dofs_in) +=
1073 JxW_face[qp] * scalar_phi_face[ii][qp] * tau * qp_mu[qp] * normal_sq;
1078 for (
const auto & elem :
mesh.active_local_element_ptr_range())
1080 std::vector<EigenVector> local_solns;
1081 std::vector<unsigned int> dofs_on_side;
1082 std::unordered_set<unsigned int> external_boundary_indices;
1083 std::vector<std::vector<Gradient>> volumetric_q;
1084 std::vector<std::vector<Number>> volumetric_u;
1085 std::vector<std::vector<Gradient>> face_q;
1088 dof_map.dof_indices(elem, vector_dof_indices, system.variable_number(
"q"));
1089 dof_map.dof_indices(elem, scalar_dof_indices, system.variable_number(
"u"));
1090 lambda_dof_map.dof_indices(elem, lambda_dof_indices, lambda_system.
variable_number(
"lambda"));
1092 const auto vector_n_dofs = vector_dof_indices.size();
1093 const auto scalar_n_dofs = scalar_dof_indices.size();
1094 const auto lambda_n_dofs = lambda_dof_indices.size();
1095 const std::size_t n_mu_funcs = global_solve ? lambda_n_dofs : 1;
1099 K_lm_libmesh.
resize(lambda_n_dofs, lambda_n_dofs);
1100 F_lm_libmesh.
resize(lambda_n_dofs);
1102 for (
const auto s : elem->side_index_range())
1103 if (!elem->neighbor_ptr(s))
1106 external_boundary_indices.insert(dofs_on_side.begin(), dofs_on_side.end());
1111 vector_fe->reinit(elem);
1112 scalar_fe->reinit(elem);
1114 libmesh_assert_equal_to(vector_n_dofs, vector_phi.size());
1115 libmesh_assert_equal_to(scalar_n_dofs, scalar_phi.size());
1117 compute_and_invert_K(vector_n_dofs, scalar_n_dofs, elem);
1118 local_solns.resize(n_mu_funcs);
1122 volumetric_q.resize(lambda_n_dofs);
1123 volumetric_u.resize(lambda_n_dofs);
1124 face_q.resize(lambda_n_dofs);
1126 for (
const auto i :
make_range(lambda_n_dofs))
1128 if (external_boundary_indices.count(i))
1131 compute_rhs(vector_n_dofs, scalar_n_dofs, elem, i);
1132 auto & local_soln = local_solns[i];
1133 local_soln = Kinv_mixed * F_mixed;
1134 const auto local_q_soln = local_soln.head(vector_n_dofs);
1135 const auto local_u_soln = local_soln.tail(scalar_n_dofs);
1141 for (
const auto i :
make_range(lambda_n_dofs))
1142 if (!external_boundary_indices.count(i))
1143 for (
const auto j :
make_range(lambda_n_dofs))
1144 if (!external_boundary_indices.count(j))
1146 K_lm_libmesh(i, j) += JxW[qp] * volumetric_q[i][qp] * volumetric_q[j][qp];
1151 const Real x = q_point[qp](0);
1152 const Real y = q_point[qp](1);
1153 const Real z = q_point[qp](2);
1163 for (
const auto i :
make_range(lambda_n_dofs))
1164 if (!external_boundary_indices.count(i))
1165 F_lm_libmesh(i) += JxW[qp] * f * volumetric_u[i][qp];
1169 for (
const auto s : elem->side_index_range())
1171 lambda_fe_face->reinit(elem, s);
1172 for (
const auto i :
make_range(lambda_n_dofs))
1173 if (external_boundary_indices.count(i))
1174 for (
const auto j :
make_range(lambda_n_dofs))
1175 if (external_boundary_indices.count(j))
1177 K_lm_libmesh(i, j) +=
1178 JxW_face[qp] * lambda_phi_face[i][qp] * lambda_phi_face[j][qp];
1180 if (!elem->neighbor_ptr(s))
1182 vector_fe_face->reinit(elem, s);
1183 for (
const auto i :
make_range(lambda_n_dofs))
1185 if (external_boundary_indices.count(i))
1188 const auto local_q_soln = local_solns[i].head(vector_n_dofs);
1194 const Real xf = qface_point[qp](0);
1195 const Real yf = qface_point[qp](1);
1196 const Real zf = qface_point[qp](2);
1199 Real scalar_value = 0;
1204 for (
const auto i :
make_range(lambda_n_dofs))
1205 if (!external_boundary_indices.count(i))
1206 F_lm_libmesh(i) += JxW_face[qp] * scalar_value * face_q[i][qp] * normals[qp];
1213 dof_map.constrain_element_matrix_and_vector(K_lm_libmesh, F_lm_libmesh, lambda_dof_indices);
1214 matrix.
add_matrix(K_lm_libmesh, lambda_dof_indices);
1215 lambda_system.rhs->
add_vector(F_lm_libmesh, lambda_dof_indices);
1220 Lambda.resize(lambda_n_dofs);
1222 for (
const auto i :
make_range(lambda_n_dofs))
1223 Lambda(i) = lambda_solution_std_vec[i];
1226 auto & local_soln = local_solns[0];
1227 local_soln = Kinv_mixed * F_mixed;
1228 const auto vector_soln = local_soln.head(vector_n_dofs);
1229 const auto scalar_soln = local_soln.tail(scalar_n_dofs);
1230 for (
const auto i :
make_range(vector_n_dofs))
1231 system.solution->set(vector_dof_indices[i], vector_soln(i));
1232 for (
const auto i :
make_range(scalar_n_dofs))
1233 system.solution->set(scalar_dof_indices[i], scalar_soln(i));
1256 system.solution->close();