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); });
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();
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); });
964 const SCALAR integ1 = (
qr_dual_edge_[0]->Weights().transpose().array() *
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 =
985 (
qr_dual_edge_[1]->Weights().transpose().array() * psidd.array() *
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 =
1006 (
qr_dual_edge_[2]->Weights().transpose().array() * psidd.array() *
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 =
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 =
1070 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> jacd =
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); });
1573 const SCALAR integ1 = (
qr_dual_edge_[0]->Weights().transpose().array() *
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 =
1590 (
qr_dual_edge_[1]->Weights().transpose().array() * psidd.array() *
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 =
1607 (
qr_dual_edge_[2]->Weights().transpose().array() * psidd.array() *
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 =
1625 (
qr_dual_edge_[3]->Weights().transpose().array() * psidd.array() *
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.
Hierarchic Finite Elements of arbitrary degree on quadrilaterals.
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.
Hierarchic Finite Elements of arbitrary degree on triangles.
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_
Interface class for parametric scalar valued finite elements.
Represents a Quadrature Rule over one of the Reference Elements.
const Eigen::VectorXd & Weights() const
All quadrature weights as a vector.
const Eigen::MatrixXd & Points() const
All quadrature points as column vectors.
base::size_type NumPoints() const
Return the total number of quadrature points (num of columns of points/weights)
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...
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
double Jacobi(unsigned n, double alpha, double beta, double x)
Computes the n-th degree shifted Jacobi polynomial.
lf::assemble::dim_t dim_t
lf::base::sub_idx_t sub_idx_t
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.