834{
837
838
840
842
844 const auto & lambda_dof_map = lambda_system.
get_dof_map();
845
848 const FEType lambda_fe_type =
850
851
854
855
857
858
859 vector_fe->attach_quadrature_rule(&qrule);
860 scalar_fe->attach_quadrature_rule(&qrule);
861
862
866
867
868
870
871
872 vector_fe_face->attach_quadrature_rule(&qface);
873 scalar_fe_face->attach_quadrature_rule(&qface);
874 lambda_fe_face->attach_quadrature_rule(&qface);
875
876
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();
883
884
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();
891
892
893
894
895
896
897
903
904
906
907
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;
912
913
914
915 std::vector<Number> mu;
916
917
918 auto & matrix = lambda_system.get_system_matrix();
919
920 auto compute_and_invert_K =
921 [&](
const auto vector_n_dofs_in,
const auto scalar_n_dofs_in,
const Elem *
const elem_in)
922 {
923 const auto mixed_size = vector_n_dofs_in + scalar_n_dofs_in;
924
925 K_mixed.setZero(mixed_size, mixed_size);
926
927 for (
const auto qp :
make_range(qrule.n_points()))
928 {
929
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]);
933
934
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]);
938
939
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]);
943 }
944
945
946 bool tau_found = false;
947 for (auto side : elem_in->side_index_range())
948 {
949
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);
954
955 for (
const auto qp :
make_range(qface.n_points()))
956 {
957 const auto normal = normals[qp];
958 const auto normal_sq = normal * normal;
959
960
961
962
963 for (
const auto i :
make_range(scalar_n_dofs_in))
964 {
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);
968
969 if (tau)
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;
973 }
974 }
975 }
976
977 Kinv_mixed = K_mixed.inverse();
978 };
979
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)
984 {
985 const auto mixed_size = vector_n_dofs_in + scalar_n_dofs_in;
986 F_mixed.setZero(mixed_size);
987
988
989
990
992 for (
const auto qp :
make_range(qrule.n_points()))
993 {
994 const Real x = q_point[qp](0);
995 const Real y = q_point[qp](1);
996 const Real z = q_point[qp](2);
997
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];
1005 }
1006
1007
1008 bool tau_found = false;
1009 std::vector<Number> g;
1010
1011 for (auto side : elem_in->side_index_range())
1012 {
1013
1014 vector_fe_face->reinit(elem_in, side);
1015 scalar_fe_face->reinit(elem_in, side);
1016 lambda_fe_face->reinit(elem_in, side);
1017
1018 const auto & qp_mu = [&]()
1019 {
1021 {
1022 if (elem_in->neighbor_ptr(side))
1023 {
1024 compute_qp_soln(lambda_solution_std_vec, qface.n_points(), lambda_phi_face, Lambda);
1025 return lambda_solution_std_vec;
1026 }
1027 else
1028 {
1029 g.resize(qface.n_points());
1030 for (
const auto qp :
make_range(qface.n_points()))
1031 {
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);
1036
1037
1042 }
1043 return g;
1044 }
1045 }
1046 else
1047 {
1048 auto & real_mu = lambda_phi_face[shape_function];
1049 mu.resize(qface.n_points());
1050 for (
const auto qp :
make_range(qface.n_points()))
1051 mu[qp] = real_mu[qp];
1052 return mu;
1053 }
1054 }();
1055
1056 const bool internal_face = elem_in->neighbor_ptr(side);
1057 const auto tau =
compute_tau(internal_face, tau_found, elem_in);
1058
1059 for (
const auto qp :
make_range(qface.n_points()))
1060 {
1061 const auto normal = normals[qp];
1062 const auto normal_sq = normal * normal;
1063
1064
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];
1067
1068
1069
1070
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;
1074 }
1075 }
1076 };
1077
1078 for (
const auto & elem :
mesh.active_local_element_ptr_range())
1079 {
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;
1086
1087
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"));
1091
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;
1096
1097 if (global_solve)
1098 {
1099 K_lm_libmesh.
resize(lambda_n_dofs, lambda_n_dofs);
1100 F_lm_libmesh.
resize(lambda_n_dofs);
1101
1102 for (const auto s : elem->side_index_range())
1103 if (!elem->neighbor_ptr(s))
1104 {
1106 external_boundary_indices.insert(dofs_on_side.begin(), dofs_on_side.end());
1107 }
1108 }
1109
1110
1111 vector_fe->reinit(elem);
1112 scalar_fe->reinit(elem);
1113
1114 libmesh_assert_equal_to(vector_n_dofs, vector_phi.size());
1115 libmesh_assert_equal_to(scalar_n_dofs, scalar_phi.size());
1116
1117 compute_and_invert_K(vector_n_dofs, scalar_n_dofs, elem);
1118 local_solns.resize(n_mu_funcs);
1119
1120 if (global_solve)
1121 {
1122 volumetric_q.resize(lambda_n_dofs);
1123 volumetric_u.resize(lambda_n_dofs);
1124 face_q.resize(lambda_n_dofs);
1125
1126 for (
const auto i :
make_range(lambda_n_dofs))
1127 {
1128 if (external_boundary_indices.count(i))
1129 continue;
1130
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);
1136 compute_qp_soln(volumetric_q[i], qrule.n_points(), vector_phi, local_q_soln);
1137 compute_qp_soln(volumetric_u[i], qrule.n_points(), scalar_phi, local_u_soln);
1138 }
1139
1140
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))
1145 for (const auto qp :
make_range(qrule.n_points()))
1146 K_lm_libmesh(i, j) += JxW[qp] * volumetric_q[i][qp] * volumetric_q[j][qp];
1147
1148
1149 for (
const auto qp :
make_range(qrule.n_points()))
1150 {
1151 const Real x = q_point[qp](0);
1152 const Real y = q_point[qp](1);
1153 const Real z = q_point[qp](2);
1154
1155
1156
1157
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];
1166 }
1167
1168
1169 for (const auto s : elem->side_index_range())
1170 {
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))
1176 for (const auto qp :
make_range(qface.n_points()))
1177 K_lm_libmesh(i, j) +=
1178 JxW_face[qp] * lambda_phi_face[i][qp] * lambda_phi_face[j][qp];
1179
1180 if (!elem->neighbor_ptr(s))
1181 {
1182 vector_fe_face->reinit(elem, s);
1183 for (
const auto i :
make_range(lambda_n_dofs))
1184 {
1185 if (external_boundary_indices.count(i))
1186 continue;
1187
1188 const auto local_q_soln = local_solns[i].head(vector_n_dofs);
1189 compute_qp_soln(face_q[i], qface.n_points(), vector_phi_face, local_q_soln);
1190 }
1191
1192 for (
const auto qp :
make_range(qface.n_points()))
1193 {
1194 const Real xf = qface_point[qp](0);
1195 const Real yf = qface_point[qp](1);
1196 const Real zf = qface_point[qp](2);
1197
1198
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];
1207 }
1208 }
1209 }
1210
1211
1212
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);
1216 }
1217 else
1218 {
1219
1220 Lambda.resize(lambda_n_dofs);
1222 for (
const auto i :
make_range(lambda_n_dofs))
1223 Lambda(i) = lambda_solution_std_vec[i];
1224
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));
1234
1235
1236
1238 dof_map,
1239 system,
1240 elem,
1241 vector_soln,
1242 scalar_soln,
1243 Lambda,
1244 *vector_fe,
1245 *vector_fe_face,
1246 *scalar_fe,
1247 *scalar_fe_face,
1248 *lambda_fe_face,
1249 qrule,
1250 qface);
1251 }
1252 }
1253
1254 if (!global_solve)
1255 {
1256 system.solution->close();
1257
1258 system.update();
1259 }
1260}
Real scalar(Real x, Real y)
Real forcing(Real x, Real y)
Defines a dense matrix for use in Finite Element-type computations.
void resize(const unsigned int new_m, const unsigned int new_n)
Resizes the matrix to the specified size and calls zero().
Defines a dense vector for use in Finite Element-type computations.
void resize(const unsigned int n)
Resize the vector.
This is the base class from which all geometric element types are derived.
const MeshBase & get_mesh() const
const T_sys & get_system(std::string_view name) const
static std::unique_ptr< FEGenericBase > build(const unsigned int dim, const FEType &type)
Builds a specific finite element type.
static void dofs_on_side(const Elem *const elem, const unsigned int dim, const FEType &fe_t, unsigned int s, std::vector< unsigned int > &di, const bool add_p_level=true)
Fills the vector di with the local degree of freedom indices associated with side s of element elem A...
class FEType hides (possibly multiple) FEFamily and approximation orders, thereby enabling specialize...
Order default_quadrature_order() const
Manages consistently variables, degrees of freedom, coefficient vectors, matrices and linear solvers ...
This is the MeshBase class.
unsigned int mesh_dimension() const
This class implements specific orders of Gauss quadrature.
Manages consistently variables, degrees of freedom, and coefficient vectors.
SparseMatrix< Number > & add_matrix(std::string_view mat_name, ParallelType type=PARALLEL, MatrixBuildType mat_build_type=MatrixBuildType::AUTOMATIC)
Adds the additional matrix mat_name to this system.
virtual void reinit()
Reinitializes degrees of freedom and other required data on the current mesh.
std::unique_ptr< NumericVector< Number > > current_local_solution
All the values I need to compute my contribution to the simulation at hand.
const FEType & variable_type(const unsigned int i) const
NumericVector< Number > & add_vector(std::string_view vec_name, const bool projections=true, const ParallelType type=PARALLEL)
Adds the additional vector vec_name to this system.
unsigned int variable_number(std::string_view var) const
const DofMap & get_dof_map() const
const unsigned int invalid_uint
A number which is used quite often to represent an invalid or uninitialized value for an unsigned int...
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
void compute_qp_soln(std::vector< SolnType > &qp_vec, const unsigned int n_qps, const std::vector< std::vector< PhiType > > &phi, const LocalSolutionVector &soln)
void compute_enriched_soln(const MeshBase &mesh, const DofMap &dof_map, System &system, const Elem *const elem, const EigenVector &vector_soln, const EigenVector &scalar_soln, const EigenVector &Lambda, FEVectorBase &vector_fe, FEVectorBase &vector_fe_face, FEBase &scalar_fe, FEBase &scalar_fe_face, FEBase &lambda_fe_face, QBase &qrule, QBase &qface)
Real compute_tau(const bool internal_face, bool &tau_found, const Elem *const elem)