1#ifndef LF_USCALFE_HP_FE_H_
2#define LF_USCALFE_HP_FE_H_
12#define _USE_MATH_DEFINES
14#include <lf/assemble/assemble.h>
15#include <lf/mesh/mesh.h>
16#include <lf/quad/quad.h>
23#include "scalar_reference_finite_element.h"
41double legendre(
unsigned n,
double x,
double t = 1);
58double ilegendre(
unsigned n,
double x,
double t = 1);
99double legendre_dx(
unsigned n,
double x,
double t = 1);
123double jacobi(
unsigned n,
double alpha,
double beta,
double x);
132double jacobi(
unsigned n,
double alpha,
double x);
155double ijacobi(
unsigned n,
double alpha,
double x);
167double ijacobi_dx(
unsigned n,
double alpha,
double x);
181double jacobi_dx(
unsigned n,
double alpha,
double x);
221template <
typename SCALAR>
255 dim_t codim)
const override {
256 LF_ASSERT_MSG(codim >= 0 && codim <= 1,
"codim out of range.");
257 return codim == 0 ?
degree_ - 1 : 2;
269 LF_ASSERT_MSG((codim == 0 && subidx == 0) || (codim == 1 && subidx < 2),
270 "codim/subidx out of range.");
274 [[nodiscard]] Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic>
276 LF_ASSERT_MSG(refcoords.rows() == 1,
"refcoords must be a row vector");
277 Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> result(
281 refcoords.unaryExpr([&](
double x) -> SCALAR {
return 1 - x; });
282 result.row(1) = refcoords;
284 for (
int i = 0; i <
degree_ - 1; ++i) {
285 result.row(i + 2) = refcoords.unaryExpr(
286 [&](
double x) -> SCALAR {
return ilegendre(i + 2, x); });
291 [[nodiscard]] Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic>
293 const Eigen::MatrixXd &refcoords)
const override {
294 LF_ASSERT_MSG(refcoords.rows() == 1,
"refcoords must be a row vector");
295 Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> result(
299 refcoords.unaryExpr([&](
double ) -> SCALAR {
return -1; });
301 refcoords.unaryExpr([&](
double ) -> SCALAR {
return 1; });
303 for (
int i = 0; i <
degree_ - 1; ++i) {
304 result.row(i + 2) = refcoords.unaryExpr(
305 [&](
double x) -> SCALAR {
return ilegendre_dx(i + 2, x); });
346 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> &nodevals)
const override {
349 dofs[0] = nodevals[0];
350 dofs[1] = nodevals[1];
355 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> psidd =
357 [&](
double x) -> SCALAR {
return legendre_dx(i - 1, x); });
364 dofs[i] = (P1 * nodevals[1] - P0 * nodevals[0] - integ) * (2 * i - 1);
449template <
typename SCALAR>
459 std::array<
unsigned, 3> edge_degrees,
460 const quad::QuadRuleCache &qr_cache,
461 nonstd::span<const
lf::mesh::Orientation> rel_orient)
474 LF_ASSERT_MSG(
edge_degrees_[0] >= 0,
"illegal degree for edge 0");
475 LF_ASSERT_MSG(
edge_degrees_[1] >= 0,
"illegal degree for edge 1");
476 LF_ASSERT_MSG(
edge_degrees_[2] >= 0,
"illegal degree for edge 2");
483 [[nodiscard]]
unsigned Degree()
const override {
503 dim_t codim)
const override {
515 "You cannot call this method with codim=1 if the edges of the "
516 "triangle have a differing number of shape functions.");
521 LF_ASSERT_MSG(
false,
"Illegal codim " << codim);
538 LF_ASSERT_MSG(subidx == 0,
"illegal codim and subidx.");
541 LF_ASSERT_MSG(subidx >= 0 && subidx <= 2,
542 "illegal codim/subidx combination.");
545 LF_ASSERT_MSG(subidx >= 0 && subidx <= 2,
546 "illegal codim/subidx combination.");
549 LF_VERIFY_MSG(
false,
"Illegal codim " << codim);
555 [[nodiscard]] Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic>
557 Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> result(
560 const Eigen::RowVectorXd l1 = Eigen::RowVectorXd::Ones(refcoords.cols()) -
561 refcoords.row(0) - refcoords.row(1);
562 const Eigen::RowVectorXd l2 = refcoords.row(0);
563 const Eigen::RowVectorXd l3 = refcoords.row(1);
574 for (
long j = 0; j < refcoords.cols(); ++j) {
575 result(3 + i, j) =
ilegendre(i + 2, l2[j], l1[j] + l2[j]);
579 for (
long j = 0; j < refcoords.cols(); ++j) {
591 for (
long j = 0; j < refcoords.cols(); ++j) {
592 result(current_dof + i, j) =
ilegendre(i + 2, l3[j], l2[j] + l3[j]);
596 for (
long j = 0; j < refcoords.cols(); ++j) {
608 for (
long j = 0; j < refcoords.cols(); ++j) {
609 result(current_dof + i, j) =
ilegendre(i + 2, l1[j], l3[j] + l1[j]);
613 for (
long j = 0; j < refcoords.cols(); ++j) {
628 Eigen::Array<SCALAR, 1, Eigen::Dynamic> edge(refcoords.cols());
629 for (Eigen::Index j = 0; j < refcoords.cols(); ++j) {
631 edge(j) =
ilegendre(i + 2, l2[j], l1[j] + l2[j]);
633 edge(j) =
ilegendre(i + 2, l1[j], l1[j] + l2[j]);
643 result.row(current_dof++) =
644 (edge * l3.array().unaryExpr([&](
double x) -> SCALAR {
645 return ijacobi(j + 1, 2 * i + 4, x);
650 result.row(current_dof++) =
651 (edge * l3.array().unaryExpr([&](
double x) -> SCALAR {
652 return ijacobi(j + 1, 2 * i + 4, x);
658 LF_ASSERT_MSG(current_dof == result.rows(),
659 "Something's wrong, not all rows have been filled.");
663 [[nodiscard]] Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic>
665 const Eigen::MatrixXd &refcoords)
const override {
666 Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> result(
669 const Eigen::RowVectorXd l1 = Eigen::RowVectorXd::Ones(refcoords.cols()) -
670 refcoords.row(0) - refcoords.row(1);
671 const Eigen::RowVectorXd l2 = refcoords.row(0);
672 const Eigen::RowVectorXd l3 = refcoords.row(1);
674 const Eigen::RowVectorXd l1_dx =
675 Eigen::RowVectorXd::Constant(refcoords.cols(), -1);
676 const Eigen::RowVectorXd l1_dy =
677 Eigen::RowVectorXd::Constant(refcoords.cols(), -1);
678 const Eigen::RowVectorXd l2_dx =
679 Eigen::RowVectorXd::Constant(refcoords.cols(), 1);
680 const Eigen::RowVectorXd l2_dy =
681 Eigen::RowVectorXd::Constant(refcoords.cols(), 0);
682 const Eigen::RowVectorXd l3_dx =
683 Eigen::RowVectorXd::Constant(refcoords.cols(), 0);
684 const Eigen::RowVectorXd l3_dy =
685 Eigen::RowVectorXd::Constant(refcoords.cols(), 1);
687 for (Eigen::Index i = 0; i < refcoords.cols(); ++i) {
690 result(0, 2 * i + 0) = l1_dx[i];
691 result(0, 2 * i + 1) = l1_dy[i];
692 result(1, 2 * i + 0) = l2_dx[i];
693 result(1, 2 * i + 1) = l2_dy[i];
694 result(2, 2 * i + 0) = l3_dx[i];
695 result(2, 2 * i + 1) = l3_dy[i];
699 result(current_dof + j, 2 * i + 0) =
701 ilegendre_dt(j + 2, l2[i], l1[i] + l2[i]) * (l1_dx[i] + l2_dx[i]);
702 result(current_dof + j, 2 * i + 1) =
704 ilegendre_dt(j + 2, l2[i], l1[i] + l2[i]) * (l1_dy[i] + l2_dy[i]);
708 ilegendre_dt(j + 2, l1[i], l1[i] + l2[i]) * (l1_dx[i] + l2_dx[i]);
711 ilegendre_dt(j + 2, l1[i], l1[i] + l2[i]) * (l1_dy[i] + l2_dy[i]);
719 result(current_dof + j, 2 * i + 0) =
721 ilegendre_dt(j + 2, l3[i], l2[i] + l3[i]) * (l2_dx[i] + l3_dx[i]);
722 result(current_dof + j, 2 * i + 1) =
724 ilegendre_dt(j + 2, l3[i], l2[i] + l3[i]) * (l2_dy[i] + l3_dy[i]);
728 ilegendre_dt(j + 2, l2[i], l2[i] + l3[i]) * (l2_dx[i] + l3_dx[i]);
731 ilegendre_dt(j + 2, l2[i], l2[i] + l3[i]) * (l2_dy[i] + l3_dy[i]);
739 result(current_dof + j, 2 * i + 0) =
741 ilegendre_dt(j + 2, l1[i], l3[i] + l1[i]) * (l3_dx[i] + l1_dx[i]);
742 result(current_dof + j, 2 * i + 1) =
744 ilegendre_dt(j + 2, l1[i], l3[i] + l1[i]) * (l3_dy[i] + l1_dy[i]);
748 ilegendre_dt(j + 2, l3[i], l3[i] + l1[i]) * (l3_dx[i] + l1_dx[i]);
751 ilegendre_dt(j + 2, l3[i], l3[i] + l1[i]) * (l3_dy[i] + l1_dy[i]);
764 edge_eval =
ilegendre(j + 2, l2[i], l1[i] + l2[i]);
765 edge_dx =
ilegendre_dx(j + 2, l2[i], l1[i] + l2[i]) * l2_dx[i] +
767 (l1_dx[i] + l2_dx[i]);
768 edge_dy =
ilegendre_dx(j + 2, l2[i], l1[i] + l2[i]) * l2_dy[i] +
770 (l1_dy[i] + l2_dy[i]);
772 edge_eval =
ilegendre(j + 2, l1[i], l1[i] + l2[i]);
773 edge_dx =
ilegendre_dx(j + 2, l1[i], l1[i] + l2[i]) * l1_dx[i] +
775 (l1_dx[i] + l2_dx[i]);
776 edge_dy =
ilegendre_dx(j + 2, l1[i], l1[i] + l2[i]) * l1_dy[i] +
778 (l1_dy[i] + l2_dy[i]);
781 SCALAR jackinte =
ijacobi(k + 1, 2 * j + 4, l3[i]);
782 SCALAR jackeval =
ijacobi_dx(k + 1, 2 * j + 4, l3[i]);
783 result(current_dof, 2 * i + 0) =
784 jackinte * edge_dx + edge_eval * jackeval * l3_dx[i];
785 result(current_dof++, 2 * i + 1) =
786 jackinte * edge_dy + edge_eval * jackeval * l3_dy[i];
791 "internal error, not all rows have been filled.");
807 Eigen::MatrixXd nodes(2, 3 + Ns0 + Ns1 + Ns2 + Nt);
820 nodes.block(0, 3, 1, Ns0) =
823 nodes.block(1, 3, 1, Ns0).setZero();
828 nodes.block(0, 3 + Ns0, 1, Ns1) =
830 nodes.block(1, 3 + Ns0, 1, Ns1) =
qr_dual_edge_[1]->Points();
832 nodes.block(0, 3 + Ns0, 1, Ns1) =
qr_dual_edge_[1]->Points();
833 nodes.block(1, 3 + Ns0, 1, Ns1) =
839 nodes.block(0, 3 + Ns0 + Ns1, 1, Ns2).setZero();
841 nodes.block(1, 3 + Ns0 + Ns1, 1, Ns2) =
844 nodes.block(1, 3 + Ns0 + Ns1, 1, Ns2) =
qr_dual_edge_[2]->Points();
862 return 3 + Ns0 + Ns1 + Ns2 + Nt;
866 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> &nodevals)
const override {
881 const Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic>
883 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> boundary_function =
884 dofs.segment(0, 3 + d0 + d1 + d2) *
885 basis_functions.block(0, 0, 3 + d0 + d1 + d2, basis_functions.cols());
886 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> boundary_face_dofs =
896 std::array<const quad::QuadRule *, 3>
905 [[nodiscard]] Eigen::Matrix<SCALAR, 1, Eigen::Dynamic>
907 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> &nodevals)
const {
908 Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> dofs(3);
909 dofs[0] = nodevals[0];
910 dofs[1] = nodevals[1];
911 dofs[2] = nodevals[2];
920 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> &nodevals)
const {
925 Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> dofs(
934 Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> psidd =
936 [&](
double x) -> SCALAR {
return legendre_dx(i - 1, x); });
938 const SCALAR integ1 = (
qr_dual_edge_[0]->Weights().transpose().array() *
939 psidd.array() * nodevals.segment(3, Ns0).array())
943 (P1 * nodevals[1] - P0 * nodevals[0] - integ1) * (2 * i - 1);
946 (P1 * nodevals[0] - P0 * nodevals[1] - integ1) * (2 * i - 1);
954 Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> psidd =
956 [&](
double x) -> SCALAR {
return legendre_dx(i - 1, x); });
958 const SCALAR integ2 =
959 (
qr_dual_edge_[1]->Weights().transpose().array() * psidd.array() *
960 nodevals.segment(3 + Ns0, Ns1).array())
964 (P1 * nodevals[2] - P0 * nodevals[1] - integ2) * (2 * i - 1);
967 (P1 * nodevals[1] - P0 * nodevals[2] - integ2) * (2 * i - 1);
975 Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> psidd =
977 [&](
double x) -> SCALAR {
return legendre_dx(i - 1, x); });
979 const SCALAR integ3 =
980 (
qr_dual_edge_[2]->Weights().transpose().array() * psidd.array() *
981 nodevals.segment(3 + Ns0 + Ns1, Ns2).array())
985 (P1 * nodevals[0] - P0 * nodevals[2] - integ3) * (2 * i - 1);
989 (P1 * nodevals[2] - P0 * nodevals[0] - integ3) * (2 * i - 1);
1001 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> &nodevals)
const {
1010 const Eigen::Matrix<double, 1, Eigen::Dynamic> xnorm =
1017 const Eigen::Matrix<double, 1, Eigen::Dynamic> xnorm_adj =
1020 : Eigen::RowVectorXd::Ones(Nt) - xnorm;
1021 const Eigen::Matrix<double, 1, Eigen::Dynamic> y =
1025 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> ypow =
1028 [&](
double y) -> SCALAR {
return std::pow(1 - y, i); })
1030 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> ypowp1 =
1033 [&](
double y) -> SCALAR {
return std::pow(1 - y, i + 1); })
1035 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> psidd =
1036 xnorm_adj.unaryExpr(
1037 [&](
double x) -> SCALAR {
return legendre_dx(i + 1, x); });
1040 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> jacdd =
1044 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> jacd =
1050 nodevals.block(0, 3 + Ns, 1, Nt).array() * psidd.array() *
1051 (ypowp1.array() * jacdd.array() -
1052 (2 * i + 4) * ypow.array() * jacd.array()))
1056 dofs[idx] *= 2 * j + 2 * i +
1088template <
typename SCALAR>
1098 std::array<
unsigned, 4> edge_degrees,
1099 const quad::QuadRuleCache &qr_cache,
1100 nonstd::span<const
lf::mesh::Orientation> rel_orient)
1117 LF_ASSERT_MSG(
edge_degrees_[0] >= 0,
"illegal degree for edge 0");
1118 LF_ASSERT_MSG(
edge_degrees_[1] >= 0,
"illegal degree for edge 1");
1119 LF_ASSERT_MSG(
edge_degrees_[2] >= 0,
"illegal degree for edge 2");
1120 LF_ASSERT_MSG(
edge_degrees_[3] >= 0,
"illegal degree for edge 3");
1127 [[nodiscard]]
unsigned Degree()
const override {
1148 dim_t codim)
const override {
1157 "You cannot call this method with codim=1 if the edges of the "
1158 "quad have a differing number of shape functions.");
1163 LF_ASSERT_MSG(
false,
"Illegal codim " << codim);
1180 LF_ASSERT_MSG(subidx == 0,
"illegal codim and subidex.");
1183 LF_ASSERT_MSG(subidx >= 0 && subidx < 4,
"illegal codim and subidx.");
1186 LF_ASSERT_MSG(subidx >= 0 && subidx < 4,
"illegal codim and subidx.");
1189 LF_VERIFY_MSG(
false,
"Illegal codim " << codim);
1195 [[nodiscard]] Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic>
1197 Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> result(
1200 const Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> sf1d_x =
1201 fe1d_.EvalReferenceShapeFunctions(refcoords.row(0));
1202 const Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> sf1d_y =
1203 fe1d_.EvalReferenceShapeFunctions(refcoords.row(1));
1204 const Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> sf1df_x =
1205 fe1d_.EvalReferenceShapeFunctions(
1206 Eigen::RowVectorXd::Constant(refcoords.cols(), 1) -
1208 const Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> sf1df_y =
1209 fe1d_.EvalReferenceShapeFunctions(
1210 Eigen::RowVectorXd::Constant(refcoords.cols(), 1) -
1213 result.row(0) = (sf1d_x.row(0).array() * sf1d_y.row(0).array()).matrix();
1214 result.row(1) = (sf1d_x.row(1).array() * sf1d_y.row(0).array()).matrix();
1215 result.row(2) = (sf1d_x.row(1).array() * sf1d_y.row(1).array()).matrix();
1216 result.row(3) = (sf1d_x.row(0).array() * sf1d_y.row(1).array()).matrix();
1218 int current_dof = 4;
1224 result.row(current_dof + i) =
1225 (sf1d_x.row(2 + i).array() * sf1d_y.row(0).array()).matrix();
1228 (sf1df_x.row(2 + i).array() * sf1d_y.row(0).array()).matrix();
1236 result.row(current_dof + i) =
1237 (sf1d_x.row(1).array() * sf1d_y.row(2 + i).array()).matrix();
1240 (sf1d_x.row(1).array() * sf1df_y.row(2 + i).array()).matrix();
1248 result.row(current_dof + i) =
1249 (sf1df_x.row(2 + i).array() * sf1d_y.row(1).array()).matrix();
1252 (sf1d_x.row(2 + i).array() * sf1d_y.row(1).array()).matrix();
1260 result.row(current_dof + i) =
1261 (sf1d_x.row(0).array() * sf1df_y.row(2 + i).array()).matrix();
1264 (sf1d_x.row(0).array() * sf1d_y.row(2 + i).array()).matrix();
1272 result.row(current_dof++) =
1273 (sf1d_x.row(j + 2).array() * sf1d_y.row(i + 2).array()).matrix();
1276 LF_ASSERT_MSG(current_dof == result.rows(),
1277 "Not all rows have been filled.");
1281 [[nodiscard]] Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic>
1283 const Eigen::MatrixXd &refcoords)
const override {
1284 Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> result(
1287 const Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> sf1d_x =
1288 fe1d_.EvalReferenceShapeFunctions(refcoords.row(0));
1289 const Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> sf1d_y =
1290 fe1d_.EvalReferenceShapeFunctions(refcoords.row(1));
1291 const Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> sf1d_dx =
1292 fe1d_.GradientsReferenceShapeFunctions(refcoords.row(0));
1293 const Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> sf1d_dy =
1294 fe1d_.GradientsReferenceShapeFunctions(refcoords.row(1));
1297 const Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> sf1df_x =
1298 fe1d_.EvalReferenceShapeFunctions(
1299 Eigen::RowVectorXd::Constant(refcoords.cols(), 1) -
1301 const Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> sf1df_y =
1302 fe1d_.EvalReferenceShapeFunctions(
1303 Eigen::RowVectorXd::Constant(refcoords.cols(), 1) -
1305 const Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> sf1df_dx =
1306 fe1d_.GradientsReferenceShapeFunctions(
1307 Eigen::RowVectorXd::Constant(refcoords.cols(), 1) -
1309 const Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> sf1df_dy =
1310 fe1d_.GradientsReferenceShapeFunctions(
1311 Eigen::RowVectorXd::Constant(refcoords.cols(), 1) -
1313 for (Eigen::Index i = 0; i < refcoords.cols(); ++i) {
1315 result(0, 2 * i + 0) = sf1d_dx(0, i) * sf1d_y(0, i);
1316 result(0, 2 * i + 1) = sf1d_x(0, i) * sf1d_dy(0, i);
1317 result(1, 2 * i + 0) = sf1d_dx(1, i) * sf1d_y(0, i);
1318 result(1, 2 * i + 1) = sf1d_x(1, i) * sf1d_dy(0, i);
1319 result(2, 2 * i + 0) = sf1d_dx(1, i) * sf1d_y(1, i);
1320 result(2, 2 * i + 1) = sf1d_x(1, i) * sf1d_dy(1, i);
1321 result(3, 2 * i + 0) = sf1d_dx(0, i) * sf1d_y(1, i);
1322 result(3, 2 * i + 1) = sf1d_x(0, i) * sf1d_dy(1, i);
1324 int current_dof = 4;
1328 result(current_dof + j, 2 * i + 0) = sf1d_dx(2 + j, i) * sf1d_y(0, i);
1329 result(current_dof + j, 2 * i + 1) = sf1d_x(2 + j, i) * sf1d_dy(0, i);
1332 -sf1df_dx(2 + j, i) * sf1d_y(0, i);
1334 sf1df_x(2 + j, i) * sf1d_dy(0, i);
1341 result(current_dof + j, 2 * i + 0) = sf1d_dx(1, i) * sf1d_y(2 + j, i);
1342 result(current_dof + j, 2 * i + 1) = sf1d_x(1, i) * sf1d_dy(2 + j, i);
1345 sf1d_dx(1, i) * sf1df_y(2 + j, i);
1347 sf1d_x(1, i) * -sf1df_dy(2 + j, i);
1354 result(current_dof + j, 2 * i + 0) =
1355 -sf1df_dx(2 + j, i) * sf1d_y(1, i);
1356 result(current_dof + j, 2 * i + 1) =
1357 sf1df_x(2 + j, i) * sf1d_dy(1, i);
1360 sf1d_dx(2 + j, i) * sf1d_y(1, i);
1362 sf1d_x(2 + j, i) * sf1d_dy(1, i);
1369 result(current_dof + j, 2 * i + 0) =
1370 sf1d_dx(0, i) * sf1df_y(2 + j, i);
1371 result(current_dof + j, 2 * i + 1) =
1372 sf1d_x(0, i) * -sf1df_dy(2 + j, i);
1375 sf1d_dx(0, i) * sf1d_y(2 + j, i);
1377 sf1d_x(0, i) * sf1d_dy(2 + j, i);
1384 result(current_dof, 2 * i + 0) = sf1d_dx(k + 2, i) * sf1d_y(j + 2, i);
1385 result(current_dof++, 2 * i + 1) =
1386 sf1d_x(k + 2, i) * sf1d_dy(j + 2, i);
1410 Eigen::MatrixXd nodes(2, 4 + Ne0 + Ne1 + Ne2 + Ne3 + Nq * Nq);
1425 nodes.block(0, 4, 1, Ne0) =
1428 nodes.block(1, 4, 1, Ne0).setZero();
1433 nodes.block(0, 4 + Ne0, 1, Ne1).setOnes();
1435 nodes.block(1, 4 + Ne0, 1, Ne1) =
qr_dual_edge_[1]->Points();
1437 nodes.block(1, 4 + Ne0, 1, Ne1) =
1444 nodes.block(0, 4 + Ne0 + Ne1, 1, Ne2) =
1447 nodes.block(0, 4 + Ne0 + Ne1, 1, Ne2) =
qr_dual_edge_[2]->Points();
1449 nodes.block(1, 4 + Ne0 + Ne1, 1, Ne2).setOnes();
1453 nodes.block(0, 4 + Ne0 + Ne1 + Ne2, 1, Ne3).setZero();
1455 nodes.block(1, 4 + Ne0 + Ne1 + Ne2, 1, Ne3) =
1458 nodes.block(1, 4 + Ne0 + Ne1 + Ne2, 1, Ne3) =
1464 auto points =
fe1d_.EvaluationNodes();
1466 nodes.block(0, 4 + Ne0 + Ne1 + Ne2 + Ne3 + i * Nq, 1, Nq) = points;
1467 nodes.block(1, 4 + Ne0 + Ne1 + Ne2 + Ne3 + i * Nq, 1, Nq)
1468 .setConstant(points(0, i));
1488 return 4 + Ne0 + Ne1 + Ne2 + Ne3 + Nq * Nq;
1492 const Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> &nodevals)
const override {
1504 dofs[0] = nodevals[0];
1505 dofs[1] = nodevals[1];
1506 dofs[2] = nodevals[2];
1507 dofs[3] = nodevals[3];
1516 Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> psidd =
1518 [&](
double x) -> SCALAR {
return legendre_dx(i - 1, x); });
1519 const SCALAR integ1 = (
qr_dual_edge_[0]->Weights().transpose().array() *
1520 psidd.array() * nodevals.segment(4, Ne0).array())
1524 (P1 * nodevals[1] - P0 * nodevals[0] - integ1) * (2 * i - 1);
1527 (P1 * nodevals[0] - P0 * nodevals[1] - integ1) * (2 * i - 1);
1532 Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> psidd =
1534 [&](
double x) -> SCALAR {
return legendre_dx(i - 1, x); });
1535 const SCALAR integ2 =
1536 (
qr_dual_edge_[1]->Weights().transpose().array() * psidd.array() *
1537 nodevals.segment(4 + Ne0, Ne1).array())
1541 (P1 * nodevals[2] - P0 * nodevals[1] - integ2) * (2 * i - 1);
1544 (P1 * nodevals[1] - P0 * nodevals[2] - integ2) * (2 * i - 1);
1549 Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> psidd =
1551 [&](
double x) -> SCALAR {
return legendre_dx(i - 1, x); });
1552 const SCALAR integ3 =
1553 (
qr_dual_edge_[2]->Weights().transpose().array() * psidd.array() *
1554 nodevals.segment(4 + Ne0 + Ne1, Ne2).array())
1558 (P1 * nodevals[3] - P0 * nodevals[2] - integ3) * (2 * i - 1);
1562 (P1 * nodevals[2] - P0 * nodevals[3] - integ3) * (2 * i - 1);
1567 Eigen::Matrix<SCALAR, 1, Eigen::Dynamic> psidd =
1569 [&](
double x) -> SCALAR {
return legendre_dx(i - 1, x); });
1570 const SCALAR integ4 =
1571 (
qr_dual_edge_[3]->Weights().transpose().array() * psidd.array() *
1572 nodevals.segment(4 + Ne0 + Ne1 + Ne2, Ne3).array())
1577 (P1 * nodevals[0] - P0 * nodevals[3] - integ4) * (2 * i - 1);
1582 (P1 * nodevals[3] - P0 * nodevals[0] - integ4) * (2 * i - 1);
1588 auto Nq =
fe1d_.NumEvaluationNodes();
1589 Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> dof_temp(
1591 for (
unsigned i = 0; i < Nq; ++i) {
1592 dof_temp.row(i) =
fe1d_
1593 .NodalValuesToDofs(nodevals.segment(
1594 4 + Ne0 + Ne1 + Ne2 + Ne3 + Nq * i, Nq))
1597 Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> dof_temp2(
1603 fe1d_.NodalValuesToDofs(dof_temp.col(i))
1609 Eigen::Map<Eigen::Matrix<SCALAR, 1, Eigen::Dynamic>>(
1610 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.
nonstd::span< const lf::mesh::Orientation > rel_orient_
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_
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.
nonstd::span< const lf::mesh::Orientation > rel_orient_
unsigned interior_degree_
const lf::quad::QuadRule * qr_dual_tria_
std::array< const quad::QuadRule *, 3 > qr_dual_edge_
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 ilegendre_dt(unsigned n, double x, double t)
Computes .
double ilegendre_dx(unsigned n, double x, double t)
Computes .
lf::assemble::dim_t dim_t
double ijacobi(unsigned n, double alpha, double x)
Evaluate the integral of the (n-1)-th degree Jacobi Polynomial for .
double jacobi_dx(unsigned n, double alpha, double x)
Computes the derivative of the n-th degree Jacobi Polynomial for .
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 legendre_dx(unsigned n, double x, double t)
Computes the derivative of the n-th degree scaled Legendre polynomial.
double jacobi(unsigned n, double alpha, double beta, double x)
Computes the n-th degree shifted Jacobi polynomial.
double legendre(unsigned n, double x, double t)
computes the n-th degree scaled Legendre Polynomial
double ijacobi_dx(unsigned n, double alpha, double x)
Computes the derivative of the n-th integrated scaled Jacobi polynomial.