1#ifndef LF_USCALFE_HP_FE_H_
2#define LF_USCALFE_HP_FE_H_
12#include <lf/assemble/assemble.h>
13#include <lf/mesh/mesh.h>
14#include <lf/quad/quad.h>
21#include "scalar_reference_finite_element.h"
41double Legendre(
unsigned n,
double x,
double t = 1);
60double ILegendre(
unsigned n,
double x,
double t = 1);
73double ILegendreDx(
unsigned n,
double x,
double t = 1);
90double ILegendreDt(
unsigned n,
double x,
double t = 1);
107double LegendreDx(
unsigned n,
double x,
double t = 1);
133double Jacobi(
unsigned n,
double alpha,
double beta,
double x);
144double Jacobi(
unsigned n,
double alpha,
double x);
169double IJacobi(
unsigned n,
double alpha,
double x);
183double IJacobiDx(
unsigned n,
double alpha,
double x);
199double JacobiDx(
unsigned n,
double alpha,
double x);
241template <
typename SCALAR>
275 dim_t codim)
const override {
276 LF_ASSERT_MSG(codim >= 0 && codim <= 1,
"codim out of range.");
277 return codim == 0 ?
degree_ - 1 : 2;
289 LF_ASSERT_MSG((codim == 0 && subidx == 0) || (codim == 1 && subidx < 2),
290 "codim/subidx out of range.");
294 [[nodiscard]] Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic>
296 LF_ASSERT_MSG(refcoords.rows() == 1,
"refcoords must be a row vector");
297 Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> result(
301 refcoords.unaryExpr([&](
double x) -> SCALAR {
return 1 - x; });
302 result.row(1) = refcoords;
304 for (
int i = 0; i <
degree_ - 1; ++i) {
305 result.row(i + 2) = refcoords.unaryExpr(
306 [&](
double x) -> SCALAR {
return ILegendre(i + 2, x); });
311 [[nodiscard]] Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic>
313 const Eigen::MatrixXd &refcoords)
const override {
314 LF_ASSERT_MSG(refcoords.rows() == 1,
"refcoords must be a row vector");
315 Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> result(
319 refcoords.unaryExpr([&](
double ) -> SCALAR {
return -1; });
321 refcoords.unaryExpr([&](
double ) -> SCALAR {
return 1; });
323 for (
int i = 0; i <
degree_ - 1; ++i) {
324 result.row(i + 2) = refcoords.unaryExpr(
325 [&](
double x) -> SCALAR {
return ILegendreDx(i + 2, x); });
368 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> &nodevals)
const override {
371 dofs[0] = nodevals[0];
372 dofs[1] = nodevals[1];
377 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> psidd =
379 [&](
double x) -> SCALAR {
return LegendreDx(i - 1, x); });
383 nodevals.tail(
qr_dual_->NumPoints()).array())
386 dofs[i] = (P1 * nodevals[1] - P0 * nodevals[0] - integ) * (2 * i - 1);
473template <
typename SCALAR>
483 std::array<
unsigned, 3> edge_degrees,
484 const
quad::QuadRuleCache &qr_cache,
485 std::span<const
lf::
mesh::Orientation> rel_orient)
498 LF_ASSERT_MSG(
edge_degrees_[0] >= 0,
"illegal degree for edge 0");
499 LF_ASSERT_MSG(
edge_degrees_[1] >= 0,
"illegal degree for edge 1");
500 LF_ASSERT_MSG(
edge_degrees_[2] >= 0,
"illegal degree for edge 2");
507 [[nodiscard]]
unsigned Degree()
const override {
527 dim_t codim)
const override {
539 "You cannot call this method with codim=1 if the edges of the "
540 "triangle have a differing number of shape functions.");
545 LF_ASSERT_MSG(
false,
"Illegal codim " << codim);
562 LF_ASSERT_MSG(subidx == 0,
"illegal codim and subidx.");
565 LF_ASSERT_MSG(subidx >= 0 && subidx <= 2,
566 "illegal codim/subidx combination.");
569 LF_ASSERT_MSG(subidx >= 0 && subidx <= 2,
570 "illegal codim/subidx combination.");
573 LF_VERIFY_MSG(
false,
"Illegal codim " << codim);
579 [[nodiscard]] Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic>
582 Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> result(
585 const Eigen::RowVectorXd l1 = Eigen::RowVectorXd::Ones(refcoords.cols()) -
586 refcoords.row(0) - refcoords.row(1);
587 const Eigen::RowVectorXd l2 = refcoords.row(0);
588 const Eigen::RowVectorXd l3 = refcoords.row(1);
599 for (
long j = 0; j < refcoords.cols(); ++j) {
600 result(3 + i, j) =
ILegendre(i + 2, l2[j], l1[j] + l2[j]);
604 for (
long j = 0; j < refcoords.cols(); ++j) {
616 for (
long j = 0; j < refcoords.cols(); ++j) {
617 result(current_dof + i, j) =
ILegendre(i + 2, l3[j], l2[j] + l3[j]);
621 for (
long j = 0; j < refcoords.cols(); ++j) {
633 for (
long j = 0; j < refcoords.cols(); ++j) {
634 result(current_dof + i, j) =
ILegendre(i + 2, l1[j], l3[j] + l1[j]);
638 for (
long j = 0; j < refcoords.cols(); ++j) {
653 Eigen::Array<SCALAR, 1, Eigen::Dynamic> edge(refcoords.cols());
654 for (Eigen::Index j = 0; j < refcoords.cols(); ++j) {
656 edge(j) =
ILegendre(i + 2, l2[j], l1[j] + l2[j]);
658 edge(j) =
ILegendre(i + 2, l1[j], l1[j] + l2[j]);
668 result.row(current_dof++) =
669 (edge * l3.array().unaryExpr([&](
double x) -> SCALAR {
670 return IJacobi(j + 1, 2 * i + 4, x);
675 result.row(current_dof++) =
676 (edge * l3.array().unaryExpr([&](
double x) -> SCALAR {
677 return IJacobi(j + 1, 2 * i + 4, x);
683 LF_ASSERT_MSG(current_dof == result.rows(),
684 "Something's wrong, not all rows have been filled.");
688 [[nodiscard]] Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic>
691 const Eigen::MatrixXd &refcoords)
const override {
692 Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> result(
695 const Eigen::RowVectorXd l1 = Eigen::RowVectorXd::Ones(refcoords.cols()) -
696 refcoords.row(0) - refcoords.row(1);
697 const Eigen::RowVectorXd l2 = refcoords.row(0);
698 const Eigen::RowVectorXd l3 = refcoords.row(1);
700 const Eigen::RowVectorXd l1_dx =
701 Eigen::RowVectorXd::Constant(refcoords.cols(), -1);
702 const Eigen::RowVectorXd l1_dy =
703 Eigen::RowVectorXd::Constant(refcoords.cols(), -1);
704 const Eigen::RowVectorXd l2_dx =
705 Eigen::RowVectorXd::Constant(refcoords.cols(), 1);
706 const Eigen::RowVectorXd l2_dy =
707 Eigen::RowVectorXd::Constant(refcoords.cols(), 0);
708 const Eigen::RowVectorXd l3_dx =
709 Eigen::RowVectorXd::Constant(refcoords.cols(), 0);
710 const Eigen::RowVectorXd l3_dy =
711 Eigen::RowVectorXd::Constant(refcoords.cols(), 1);
713 for (Eigen::Index i = 0; i < refcoords.cols(); ++i) {
716 result(0, 2 * i + 0) = l1_dx[i];
717 result(0, 2 * i + 1) = l1_dy[i];
718 result(1, 2 * i + 0) = l2_dx[i];
719 result(1, 2 * i + 1) = l2_dy[i];
720 result(2, 2 * i + 0) = l3_dx[i];
721 result(2, 2 * i + 1) = l3_dy[i];
725 result(current_dof + j, 2 * i + 0) =
726 ILegendreDx(j + 2, l2[i], l1[i] + l2[i]) * l2_dx[i] +
727 ILegendreDt(j + 2, l2[i], l1[i] + l2[i]) * (l1_dx[i] + l2_dx[i]);
728 result(current_dof + j, 2 * i + 1) =
729 ILegendreDx(j + 2, l2[i], l1[i] + l2[i]) * l2_dy[i] +
730 ILegendreDt(j + 2, l2[i], l1[i] + l2[i]) * (l1_dy[i] + l2_dy[i]);
733 ILegendreDx(j + 2, l1[i], l1[i] + l2[i]) * l1_dx[i] +
734 ILegendreDt(j + 2, l1[i], l1[i] + l2[i]) * (l1_dx[i] + l2_dx[i]);
736 ILegendreDx(j + 2, l1[i], l1[i] + l2[i]) * l1_dy[i] +
737 ILegendreDt(j + 2, l1[i], l1[i] + l2[i]) * (l1_dy[i] + l2_dy[i]);
745 result(current_dof + j, 2 * i + 0) =
746 ILegendreDx(j + 2, l3[i], l2[i] + l3[i]) * l3_dx[i] +
747 ILegendreDt(j + 2, l3[i], l2[i] + l3[i]) * (l2_dx[i] + l3_dx[i]);
748 result(current_dof + j, 2 * i + 1) =
749 ILegendreDx(j + 2, l3[i], l2[i] + l3[i]) * l3_dy[i] +
750 ILegendreDt(j + 2, l3[i], l2[i] + l3[i]) * (l2_dy[i] + l3_dy[i]);
753 ILegendreDx(j + 2, l2[i], l2[i] + l3[i]) * l2_dx[i] +
754 ILegendreDt(j + 2, l2[i], l2[i] + l3[i]) * (l2_dx[i] + l3_dx[i]);
756 ILegendreDx(j + 2, l2[i], l2[i] + l3[i]) * l2_dy[i] +
757 ILegendreDt(j + 2, l2[i], l2[i] + l3[i]) * (l2_dy[i] + l3_dy[i]);
765 result(current_dof + j, 2 * i + 0) =
766 ILegendreDx(j + 2, l1[i], l3[i] + l1[i]) * l1_dx[i] +
767 ILegendreDt(j + 2, l1[i], l3[i] + l1[i]) * (l3_dx[i] + l1_dx[i]);
768 result(current_dof + j, 2 * i + 1) =
769 ILegendreDx(j + 2, l1[i], l3[i] + l1[i]) * l1_dy[i] +
770 ILegendreDt(j + 2, l1[i], l3[i] + l1[i]) * (l3_dy[i] + l1_dy[i]);
773 ILegendreDx(j + 2, l3[i], l3[i] + l1[i]) * l3_dx[i] +
774 ILegendreDt(j + 2, l3[i], l3[i] + l1[i]) * (l3_dx[i] + l1_dx[i]);
776 ILegendreDx(j + 2, l3[i], l3[i] + l1[i]) * l3_dy[i] +
777 ILegendreDt(j + 2, l3[i], l3[i] + l1[i]) * (l3_dy[i] + l1_dy[i]);
790 edge_eval =
ILegendre(j + 2, l2[i], l1[i] + l2[i]);
791 edge_dx =
ILegendreDx(j + 2, l2[i], l1[i] + l2[i]) * l2_dx[i] +
793 (l1_dx[i] + l2_dx[i]);
794 edge_dy =
ILegendreDx(j + 2, l2[i], l1[i] + l2[i]) * l2_dy[i] +
796 (l1_dy[i] + l2_dy[i]);
798 edge_eval =
ILegendre(j + 2, l1[i], l1[i] + l2[i]);
799 edge_dx =
ILegendreDx(j + 2, l1[i], l1[i] + l2[i]) * l1_dx[i] +
801 (l1_dx[i] + l2_dx[i]);
802 edge_dy =
ILegendreDx(j + 2, l1[i], l1[i] + l2[i]) * l1_dy[i] +
804 (l1_dy[i] + l2_dy[i]);
807 SCALAR jackinte =
IJacobi(k + 1, 2 * j + 4, l3[i]);
808 SCALAR jackeval =
IJacobiDx(k + 1, 2 * j + 4, l3[i]);
809 result(current_dof, 2 * i + 0) =
810 jackinte * edge_dx + edge_eval * jackeval * l3_dx[i];
811 result(current_dof++, 2 * i + 1) =
812 jackinte * edge_dy + edge_eval * jackeval * l3_dy[i];
817 "internal error, not all rows have been filled.");
833 Eigen::MatrixXd nodes(2, 3 + Ns0 + Ns1 + Ns2 + Nt);
846 nodes.block(0, 3, 1, Ns0) =
849 nodes.block(1, 3, 1, Ns0).setZero();
854 nodes.block(0, 3 + Ns0, 1, Ns1) =
856 nodes.block(1, 3 + Ns0, 1, Ns1) =
qr_dual_edge_[1]->Points();
858 nodes.block(0, 3 + Ns0, 1, Ns1) =
qr_dual_edge_[1]->Points();
859 nodes.block(1, 3 + Ns0, 1, Ns1) =
865 nodes.block(0, 3 + Ns0 + Ns1, 1, Ns2).setZero();
867 nodes.block(1, 3 + Ns0 + Ns1, 1, Ns2) =
870 nodes.block(1, 3 + Ns0 + Ns1, 1, Ns2) =
qr_dual_edge_[2]->Points();
875 nodes.block(0, 3 + Ns0 + Ns1 + Ns2, 2, Nt) =
qr_dual_tria_->Points();
888 return 3 + Ns0 + Ns1 + Ns2 + Nt;
892 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> &nodevals)
const override {
907 const Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic>
909 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> boundary_function =
910 dofs.segment(0, 3 + d0 + d1 + d2) *
911 basis_functions.block(0, 0, 3 + d0 + d1 + d2, basis_functions.cols());
912 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> boundary_face_dofs =
922 std::array<const quad::QuadRule *, 3>
931 [[nodiscard]] Eigen::Matrix<SCALAR, 1, Eigen::Dynamic>
933 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> &nodevals)
const {
934 Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> dofs(3);
935 dofs[0] = nodevals[0];
936 dofs[1] = nodevals[1];
937 dofs[2] = nodevals[2];
946 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> &nodevals)
const {
951 Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> dofs(
960 Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> psidd =
962 [&](
double x) -> SCALAR {
return LegendreDx(i - 1, x); });
965 psidd.array() * nodevals.segment(3, Ns0).array())
969 (P1 * nodevals[1] - P0 * nodevals[0] - integ1) * (2 * i - 1);
972 (P1 * nodevals[0] - P0 * nodevals[1] - integ1) * (2 * i - 1);
980 Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> psidd =
982 [&](
double x) -> SCALAR {
return LegendreDx(i - 1, x); });
984 const SCALAR integ2 =
986 nodevals.segment(3 + Ns0, Ns1).array())
990 (P1 * nodevals[2] - P0 * nodevals[1] - integ2) * (2 * i - 1);
993 (P1 * nodevals[1] - P0 * nodevals[2] - integ2) * (2 * i - 1);
1001 Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> psidd =
1003 [&](
double x) -> SCALAR {
return LegendreDx(i - 1, x); });
1005 const SCALAR integ3 =
1007 nodevals.segment(3 + Ns0 + Ns1, Ns2).array())
1011 (P1 * nodevals[0] - P0 * nodevals[2] - integ3) * (2 * i - 1);
1015 (P1 * nodevals[2] - P0 * nodevals[0] - integ3) * (2 * i - 1);
1027 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> &nodevals)
const {
1036 const Eigen::Matrix<double, 1, Eigen::Dynamic> xnorm =
1038 qr_dual_tria_->Points().row(1).array().unaryExpr([&](
double y) {
1043 const Eigen::Matrix<double, 1, Eigen::Dynamic> xnorm_adj =
1046 : Eigen::RowVectorXd::Ones(Nt) - xnorm;
1047 const Eigen::Matrix<double, 1, Eigen::Dynamic> y =
1051 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> ypow =
1054 [&](
double y) -> SCALAR {
return std::pow(1 - y, i); })
1056 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> ypowp1 =
1059 [&](
double y) -> SCALAR {
return std::pow(1 - y, i + 1); })
1061 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> psidd =
1062 xnorm_adj.unaryExpr(
1063 [&](
double x) -> SCALAR {
return LegendreDx(i + 1, x); });
1066 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> jacdd =
1067 qr_dual_tria_->Points().row(1).unaryExpr([&](
double y) -> SCALAR {
1070 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> jacd =
1071 qr_dual_tria_->Points().row(1).unaryExpr([&](
double y) -> SCALAR {
1076 nodevals.block(0, 3 + Ns, 1, Nt).array() * psidd.array() *
1077 (ypowp1.array() * jacdd.array() -
1078 (2 * i + 4) * ypow.array() * jacd.array()))
1082 dofs[idx] *= 2 * j + 2 * i +
1140template <
typename SCALAR>
1150 std::array<
unsigned, 4> edge_degrees,
1151 const
quad::QuadRuleCache &qr_cache,
1152 std::span<const
lf::
mesh::Orientation> rel_orient)
1169 LF_ASSERT_MSG(
edge_degrees_[0] >= 0,
"illegal degree for edge 0");
1170 LF_ASSERT_MSG(
edge_degrees_[1] >= 0,
"illegal degree for edge 1");
1171 LF_ASSERT_MSG(
edge_degrees_[2] >= 0,
"illegal degree for edge 2");
1172 LF_ASSERT_MSG(
edge_degrees_[3] >= 0,
"illegal degree for edge 3");
1179 [[nodiscard]]
unsigned Degree()
const override {
1200 dim_t codim)
const override {
1209 "You cannot call this method with codim=1 if the edges of the "
1210 "quad have a differing number of shape functions.");
1215 LF_ASSERT_MSG(
false,
"Illegal codim " << codim);
1232 LF_ASSERT_MSG(subidx == 0,
"illegal codim and subidex.");
1235 LF_ASSERT_MSG(subidx >= 0 && subidx < 4,
"illegal codim and subidx.");
1238 LF_ASSERT_MSG(subidx >= 0 && subidx < 4,
"illegal codim and subidx.");
1241 LF_VERIFY_MSG(
false,
"Illegal codim " << codim);
1247 [[nodiscard]] Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic>
1249 Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> result(
1252 const Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> sf1d_x =
1253 fe1d_.EvalReferenceShapeFunctions(refcoords.row(0));
1254 const Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> sf1d_y =
1255 fe1d_.EvalReferenceShapeFunctions(refcoords.row(1));
1256 const Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> sf1df_x =
1257 fe1d_.EvalReferenceShapeFunctions(
1258 Eigen::RowVectorXd::Constant(refcoords.cols(), 1) -
1260 const Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> sf1df_y =
1261 fe1d_.EvalReferenceShapeFunctions(
1262 Eigen::RowVectorXd::Constant(refcoords.cols(), 1) -
1265 result.row(0) = (sf1d_x.row(0).array() * sf1d_y.row(0).array()).matrix();
1266 result.row(1) = (sf1d_x.row(1).array() * sf1d_y.row(0).array()).matrix();
1267 result.row(2) = (sf1d_x.row(1).array() * sf1d_y.row(1).array()).matrix();
1268 result.row(3) = (sf1d_x.row(0).array() * sf1d_y.row(1).array()).matrix();
1270 int current_dof = 4;
1276 result.row(current_dof + i) =
1277 (sf1d_x.row(2 + i).array() * sf1d_y.row(0).array()).matrix();
1280 (sf1df_x.row(2 + i).array() * sf1d_y.row(0).array()).matrix();
1288 result.row(current_dof + i) =
1289 (sf1d_x.row(1).array() * sf1d_y.row(2 + i).array()).matrix();
1292 (sf1d_x.row(1).array() * sf1df_y.row(2 + i).array()).matrix();
1300 result.row(current_dof + i) =
1301 (sf1df_x.row(2 + i).array() * sf1d_y.row(1).array()).matrix();
1304 (sf1d_x.row(2 + i).array() * sf1d_y.row(1).array()).matrix();
1312 result.row(current_dof + i) =
1313 (sf1d_x.row(0).array() * sf1df_y.row(2 + i).array()).matrix();
1316 (sf1d_x.row(0).array() * sf1d_y.row(2 + i).array()).matrix();
1324 result.row(current_dof++) =
1325 (sf1d_x.row(j + 2).array() * sf1d_y.row(i + 2).array()).matrix();
1328 LF_ASSERT_MSG(current_dof == result.rows(),
1329 "Not all rows have been filled.");
1333 [[nodiscard]] Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic>
1336 const Eigen::MatrixXd &refcoords)
const override {
1337 Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> result(
1340 const Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> sf1d_x =
1341 fe1d_.EvalReferenceShapeFunctions(refcoords.row(0));
1342 const Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> sf1d_y =
1343 fe1d_.EvalReferenceShapeFunctions(refcoords.row(1));
1344 const Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> sf1d_dx =
1345 fe1d_.GradientsReferenceShapeFunctions(refcoords.row(0));
1346 const Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> sf1d_dy =
1347 fe1d_.GradientsReferenceShapeFunctions(refcoords.row(1));
1350 const Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> sf1df_x =
1351 fe1d_.EvalReferenceShapeFunctions(
1352 Eigen::RowVectorXd::Constant(refcoords.cols(), 1) -
1354 const Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> sf1df_y =
1355 fe1d_.EvalReferenceShapeFunctions(
1356 Eigen::RowVectorXd::Constant(refcoords.cols(), 1) -
1358 const Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> sf1df_dx =
1359 fe1d_.GradientsReferenceShapeFunctions(
1360 Eigen::RowVectorXd::Constant(refcoords.cols(), 1) -
1362 const Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> sf1df_dy =
1363 fe1d_.GradientsReferenceShapeFunctions(
1364 Eigen::RowVectorXd::Constant(refcoords.cols(), 1) -
1366 for (Eigen::Index i = 0; i < refcoords.cols(); ++i) {
1368 result(0, 2 * i + 0) = sf1d_dx(0, i) * sf1d_y(0, i);
1369 result(0, 2 * i + 1) = sf1d_x(0, i) * sf1d_dy(0, i);
1370 result(1, 2 * i + 0) = sf1d_dx(1, i) * sf1d_y(0, i);
1371 result(1, 2 * i + 1) = sf1d_x(1, i) * sf1d_dy(0, i);
1372 result(2, 2 * i + 0) = sf1d_dx(1, i) * sf1d_y(1, i);
1373 result(2, 2 * i + 1) = sf1d_x(1, i) * sf1d_dy(1, i);
1374 result(3, 2 * i + 0) = sf1d_dx(0, i) * sf1d_y(1, i);
1375 result(3, 2 * i + 1) = sf1d_x(0, i) * sf1d_dy(1, i);
1377 int current_dof = 4;
1381 result(current_dof + j, 2 * i + 0) = sf1d_dx(2 + j, i) * sf1d_y(0, i);
1382 result(current_dof + j, 2 * i + 1) = sf1d_x(2 + j, i) * sf1d_dy(0, i);
1385 -sf1df_dx(2 + j, i) * sf1d_y(0, i);
1387 sf1df_x(2 + j, i) * sf1d_dy(0, i);
1394 result(current_dof + j, 2 * i + 0) = sf1d_dx(1, i) * sf1d_y(2 + j, i);
1395 result(current_dof + j, 2 * i + 1) = sf1d_x(1, i) * sf1d_dy(2 + j, i);
1398 sf1d_dx(1, i) * sf1df_y(2 + j, i);
1400 sf1d_x(1, i) * -sf1df_dy(2 + j, i);
1407 result(current_dof + j, 2 * i + 0) =
1408 -sf1df_dx(2 + j, i) * sf1d_y(1, i);
1409 result(current_dof + j, 2 * i + 1) =
1410 sf1df_x(2 + j, i) * sf1d_dy(1, i);
1413 sf1d_dx(2 + j, i) * sf1d_y(1, i);
1415 sf1d_x(2 + j, i) * sf1d_dy(1, i);
1422 result(current_dof + j, 2 * i + 0) =
1423 sf1d_dx(0, i) * sf1df_y(2 + j, i);
1424 result(current_dof + j, 2 * i + 1) =
1425 sf1d_x(0, i) * -sf1df_dy(2 + j, i);
1428 sf1d_dx(0, i) * sf1d_y(2 + j, i);
1430 sf1d_x(0, i) * sf1d_dy(2 + j, i);
1437 result(current_dof, 2 * i + 0) = sf1d_dx(k + 2, i) * sf1d_y(j + 2, i);
1438 result(current_dof++, 2 * i + 1) =
1439 sf1d_x(k + 2, i) * sf1d_dy(j + 2, i);
1463 Eigen::MatrixXd nodes(2, 4 + Ne0 + Ne1 + Ne2 + Ne3 + Nq * Nq);
1478 nodes.block(0, 4, 1, Ne0) =
1481 nodes.block(1, 4, 1, Ne0).setZero();
1486 nodes.block(0, 4 + Ne0, 1, Ne1).setOnes();
1488 nodes.block(1, 4 + Ne0, 1, Ne1) =
qr_dual_edge_[1]->Points();
1490 nodes.block(1, 4 + Ne0, 1, Ne1) =
1497 nodes.block(0, 4 + Ne0 + Ne1, 1, Ne2) =
1500 nodes.block(0, 4 + Ne0 + Ne1, 1, Ne2) =
qr_dual_edge_[2]->Points();
1502 nodes.block(1, 4 + Ne0 + Ne1, 1, Ne2).setOnes();
1506 nodes.block(0, 4 + Ne0 + Ne1 + Ne2, 1, Ne3).setZero();
1508 nodes.block(1, 4 + Ne0 + Ne1 + Ne2, 1, Ne3) =
1511 nodes.block(1, 4 + Ne0 + Ne1 + Ne2, 1, Ne3) =
1517 auto points =
fe1d_.EvaluationNodes();
1519 nodes.block(0, 4 + Ne0 + Ne1 + Ne2 + Ne3 + i * Nq, 1, Nq) = points;
1520 nodes.block(1, 4 + Ne0 + Ne1 + Ne2 + Ne3 + i * Nq, 1, Nq)
1521 .setConstant(points(0, i));
1541 return 4 + Ne0 + Ne1 + Ne2 + Ne3 + Nq * Nq;
1546 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> &nodevals)
const override {
1558 dofs[0] = nodevals[0];
1559 dofs[1] = nodevals[1];
1560 dofs[2] = nodevals[2];
1561 dofs[3] = nodevals[3];
1570 Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> psidd =
1572 [&](
double x) -> SCALAR {
return LegendreDx(i - 1, x); });
1574 psidd.array() * nodevals.segment(4, Ne0).array())
1578 (P1 * nodevals[1] - P0 * nodevals[0] - integ1) * (2 * i - 1);
1581 (P1 * nodevals[0] - P0 * nodevals[1] - integ1) * (2 * i - 1);
1586 Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> psidd =
1588 [&](
double x) -> SCALAR {
return LegendreDx(i - 1, x); });
1589 const SCALAR integ2 =
1591 nodevals.segment(4 + Ne0, Ne1).array())
1595 (P1 * nodevals[2] - P0 * nodevals[1] - integ2) * (2 * i - 1);
1598 (P1 * nodevals[1] - P0 * nodevals[2] - integ2) * (2 * i - 1);
1603 Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> psidd =
1605 [&](
double x) -> SCALAR {
return LegendreDx(i - 1, x); });
1606 const SCALAR integ3 =
1608 nodevals.segment(4 + Ne0 + Ne1, Ne2).array())
1612 (P1 * nodevals[3] - P0 * nodevals[2] - integ3) * (2 * i - 1);
1616 (P1 * nodevals[2] - P0 * nodevals[3] - integ3) * (2 * i - 1);
1621 Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> psidd =
1623 [&](
double x) -> SCALAR {
return LegendreDx(i - 1, x); });
1624 const SCALAR integ4 =
1626 nodevals.segment(4 + Ne0 + Ne1 + Ne2, Ne3).array())
1631 (P1 * nodevals[0] - P0 * nodevals[3] - integ4) * (2 * i - 1);
1636 (P1 * nodevals[3] - P0 * nodevals[0] - integ4) * (2 * i - 1);
1642 auto Nq =
fe1d_.NumEvaluationNodes();
1643 Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> dof_temp(
1645 for (
unsigned i = 0; i < Nq; ++i) {
1646 dof_temp.row(i) =
fe1d_
1647 .NodalValuesToDofs(nodevals.segment(
1648 4 + Ne0 + Ne1 + Ne2 + Ne3 + Nq * i, Nq))
1651 Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> dof_temp2(
1657 fe1d_.NodalValuesToDofs(dof_temp.col(i))
1663 Eigen::Map<Eigen::Matrix<SCALAR, 1, Eigen::Dynamic>>(
1664 dof_temp2.data(), 1,
Represents a reference element with all its properties.
static constexpr RefEl kSegment()
Returns the (1-dimensional) reference segment.
static constexpr RefEl kTria()
Returns the reference triangle.
static constexpr RefEl kQuad()
Returns the reference quadrilateral.
FeHierarchicQuad(const FeHierarchicQuad &)=default
lf::base::size_type NumRefShapeFunctions() const override
The local shape functions.
FeHierarchicQuad(FeHierarchicQuad &&) noexcept=default
FeHierarchicSegment< SCALAR > fe1d_
lf::base::size_type NumRefShapeFunctions(dim_t codim) const override
One shape function for each vertex, p-1 shape functions on the edges and (p-1)^2 shape functions on t...
Eigen::Matrix< SCALAR, Eigen::Dynamic, Eigen::Dynamic > EvalReferenceShapeFunctions(const Eigen::MatrixXd &refcoords) const override
Evaluation of all reference shape functions in a number of points.
Eigen::MatrixXd EvaluationNodes() const override
Evaluation nodes are the vertices, the points of a quadrature rule on each edge and the points of a q...
lf::base::RefEl RefEl() const override
Tells the type of reference cell underlying the parametric finite element.
lf::base::size_type NumRefShapeFunctions(dim_t codim, sub_idx_t subidx) const override
One shape function for each vertex, p-1 shape functions on the edges and (p-1)^2 shape functions on t...
Eigen::Matrix< SCALAR, Eigen::Dynamic, Eigen::Dynamic > GradientsReferenceShapeFunctions(const Eigen::MatrixXd &refcoords) const override
Computation of the gradients of all reference shape functions in a number of points.
unsigned Degree() const override
Request the maximal polynomial degree of the basis functions in this finite element.
lf::base::size_type NumEvaluationNodes() const override
Tell the number of evaluation (interpolation) nodes.
std::array< unsigned, 4 > edge_degrees_
std::array< const quad::QuadRule *, 4 > qr_dual_edge_
unsigned interior_degree_
std::span< const lf::mesh::Orientation > rel_orient_
Eigen::Matrix< SCALAR, 1, Eigen::Dynamic > NodalValuesToDofs(const Eigen::Matrix< SCALAR, 1, Eigen::Dynamic > &nodevals) const override
Computes the linear combination of reference shape functions matching function values at evaluation n...
Hierarchic Finite Elements of arbitrary degree on segments.
lf::base::RefEl RefEl() const override
Tells the type of reference cell underlying the parametric finite element.
Eigen::Matrix< SCALAR, Eigen::Dynamic, Eigen::Dynamic > GradientsReferenceShapeFunctions(const Eigen::MatrixXd &refcoords) const override
Computation of the gradients of all reference shape functions in a number of points.
lf::base::size_type NumRefShapeFunctions() const override
The local shape functions.
lf::base::size_type NumRefShapeFunctions(dim_t codim, sub_idx_t subidx) const override
One shape function for each vertex, p-1 shape functions for the segment.
lf::base::size_type NumRefShapeFunctions(dim_t codim) const override
One shape function for each vertex, p-1 shape functions for the segment.
const lf::quad::QuadRule * qr_dual_
FeHierarchicSegment(const FeHierarchicSegment &)=default
FeHierarchicSegment(FeHierarchicSegment &&) noexcept=default
Eigen::Matrix< SCALAR, 1, Eigen::Dynamic > NodalValuesToDofs(const Eigen::Matrix< SCALAR, 1, Eigen::Dynamic > &nodevals) const override
Maps function evaluations to basis function coefficients.
lf::base::size_type NumEvaluationNodes() const override
Tell the number of evaluation (interpolation) nodes.
unsigned Degree() const override
Request the maximal polynomial degree of the basis functions in this finite element.
Eigen::MatrixXd EvaluationNodes() const override
Evaluation nodes are the endpoints of the segment and the Chebyshev nodes of degree p-1 on the segmen...
Eigen::Matrix< SCALAR, Eigen::Dynamic, Eigen::Dynamic > EvalReferenceShapeFunctions(const Eigen::MatrixXd &refcoords) const override
Evaluation of all reference shape functions in a number of points.
lf::base::RefEl RefEl() const override
Tells the type of reference cell underlying the parametric finite element.
Eigen::Matrix< SCALAR, Eigen::Dynamic, Eigen::Dynamic > EvalReferenceShapeFunctions(const Eigen::MatrixXd &refcoords) const override
Evaluation of all reference shape functions in a number of points.
unsigned interior_degree_
const lf::quad::QuadRule * qr_dual_tria_
std::array< const quad::QuadRule *, 3 > qr_dual_edge_
std::span< const lf::mesh::Orientation > rel_orient_
Eigen::Matrix< SCALAR, 1, Eigen::Dynamic > NodalValuesToEdgeDofs(const Eigen::Matrix< SCALAR, 1, Eigen::Dynamic > &nodevals) const
FeHierarchicTria(const FeHierarchicTria &)=default
Eigen::MatrixXd EvaluationNodes() const override
Evaluation nodes are the vertices, the points of a Gauss quadrature rule for each edge and the points...
Eigen::Matrix< SCALAR, 1, Eigen::Dynamic > NodalValuesToFaceDofs(const Eigen::Matrix< SCALAR, 1, Eigen::Dynamic > &nodevals) const
Eigen::Matrix< SCALAR, 1, Eigen::Dynamic > NodalValuesToDofs(const Eigen::Matrix< SCALAR, 1, Eigen::Dynamic > &nodevals) const override
Computes the linear combination of reference shape functions matching function values at evaluation n...
lf::base::size_type NumRefShapeFunctions(dim_t codim, sub_idx_t subidx) const override
One shape function for each vertex, p-1 shape functions on the edges and max(0, (p-2)*(p-1)/2) shape ...
lf::base::size_type NumRefShapeFunctions() const override
The local shape functions.
lf::base::size_type NumRefShapeFunctions(dim_t codim) const override
One shape function for each vertex, p-1 shape functions on the edges and max(0, (p-2)*(p-1)/2) shape ...
Eigen::Matrix< SCALAR, Eigen::Dynamic, Eigen::Dynamic > GradientsReferenceShapeFunctions(const Eigen::MatrixXd &refcoords) const override
Computation of the gradients of all reference shape functions in a number of points.
unsigned Degree() const override
Request the maximal polynomial degree of the basis functions in this finite element.
Eigen::Matrix< SCALAR, 1, Eigen::Dynamic > NodalValuesToVertexDofs(const Eigen::Matrix< SCALAR, 1, Eigen::Dynamic > &nodevals) const
FeHierarchicTria(FeHierarchicTria &&) noexcept=default
lf::base::size_type NumEvaluationNodes() const override
Tell the number of evaluation (interpolation) nodes.
std::array< unsigned, 3 > edge_degrees_
ScalarReferenceFiniteElement()=default
Represents a Quadrature Rule over one of the Reference Elements.
unsigned int size_type
general type for variables related to size of arrays
Collects data structures and algorithms designed for scalar finite element methods primarily meant fo...
lf::assemble::dim_t dim_t
double Legendre(unsigned n, double x, double t)
computes the n-th degree scaled Legendre Polynomial
double ILegendreDx(unsigned n, double x, double t)
Computes .
double IJacobiDx(unsigned n, double alpha, double x)
Computes the derivative of the n-th integrated scaled Jacobi polynomial.
double ILegendre(unsigned n, double x, double t)
computes the integral of the (n-1)-th degree scaled Legendre Polynomial
lf::base::sub_idx_t sub_idx_t
double Jacobi(unsigned n, double alpha, double beta, double x)
Computes the n-th degree shifted Jacobi polynomial.
auto transpose(const A &a) -> MeshFunctionUnary< internal::UnaryOpTranspose, A >
Pointwise transpose of an Eigen::Matrix or Eigen::Array
double IJacobi(unsigned n, double alpha, double x)
Evaluate the integral of the (n-1)-th degree Jacobi Polynomial for .
double JacobiDx(unsigned n, double alpha, double x)
Computes the derivative of the n-th degree Jacobi Polynomial for .
double ILegendreDt(unsigned n, double x, double t)
Computes .
double LegendreDx(unsigned n, double x, double t)
Computes the derivative of the n-th degree scaled Legendre polynomial.
Defines a set of interface classes that define a mesh manager and provides mesh-related tools that bu...
Rules for numerical quadrature on reference entity shapes.