Skip to content

Commit 0da6085

Browse files
committed
Revert "Support dynamic quadrature in Laplacian"
This reverts commit 649c3fb.
1 parent 649c3fb commit 0da6085

File tree

8 files changed

+161
-136
lines changed

8 files changed

+161
-136
lines changed

bindings/pypbat/fem/Laplacian.cpp

Lines changed: 48 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ class Laplacian
1515
public:
1616
Laplacian(
1717
Mesh const& M,
18-
Eigen::Ref<IndexVectorX const> const& eg,
19-
Eigen::Ref<MatrixX const> const& wg,
20-
Eigen::Ref<MatrixX const> const& GNeg,
21-
int dims);
18+
Eigen::Ref<MatrixX const> const& detJe,
19+
Eigen::Ref<MatrixX const> const& GNe,
20+
int dims,
21+
int qOrder);
2222

2323
Laplacian(Laplacian const&) = delete;
2424
Laplacian& operator=(Laplacian const&) = delete;
@@ -40,6 +40,9 @@ class Laplacian
4040
int mMeshDims;
4141
int mMeshOrder;
4242
int mOrder;
43+
int mQuadratureOrder;
44+
45+
static auto constexpr kMaxQuadratureOrder = 4;
4346

4447
private:
4548
void* mLaplacian;
@@ -53,52 +56,58 @@ void BindLaplacian(pybind11::module& m)
5356
.def(
5457
pyb::init<
5558
Mesh const&,
56-
Eigen::Ref<IndexVectorX const> const&,
5759
Eigen::Ref<MatrixX const> const&,
5860
Eigen::Ref<MatrixX const> const&,
61+
int,
5962
int>(),
6063
pyb::arg("mesh"),
61-
pyb::arg("eg"),
62-
pyb::arg("wg"),
64+
pyb::arg("detJe"),
6365
pyb::arg("GNe"),
64-
pyb::arg("dims") = 1,
66+
pyb::arg("dims") = 1,
67+
pyb::arg("quadrature_order") = 1,
6568
"Construct the symmetric part of the Laplacian operator on mesh mesh, using "
66-
"precomputed shape function gradients GNeg evaluated at quadrature points g at "
67-
"elements eg with weights wg. The discretization is based on Galerkin projection. The "
68-
"dimensions dims can be set to accommodate vector-valued functions.")
69+
"precomputed jacobian determinants detJe and shape function gradients GNe evaluated at "
70+
"quadrature points given by the quadrature rule of order quadrature_order. The "
71+
"discretization is based on Galerkin projection. The dimensions dims can be set to "
72+
"accommodate vector-valued functions.")
6973
.def_property(
7074
"dims",
7175
[](Laplacian const& L) { return L.dims(); },
7276
[](Laplacian& L, int dims) { L.dims() = dims; })
7377
.def_readonly("order", &Laplacian::mOrder)
78+
.def_readonly("quadrature_order", &Laplacian::mQuadratureOrder)
7479
.def_property(
75-
"deltag",
80+
"deltaE",
7681
[](Laplacian const& L) { return L.ElementLaplacians(); },
77-
[](Laplacian& L, Eigen::Ref<MatrixX const> const& deltag) {
78-
L.ElementLaplacians() = deltag;
82+
[](Laplacian& L, Eigen::Ref<MatrixX const> const& deltaE) {
83+
L.ElementLaplacians() = deltaE;
7984
},
80-
"|#element nodes|x|#element nodes * #quad.pts.| matrix of element Laplacians")
85+
"|#element nodes|x|#element nodes * #elements| matrix of element Laplacians")
8186
.def_property_readonly("shape", &Laplacian::Shape)
8287
.def("to_matrix", &Laplacian::ToMatrix);
8388
}
8489

8590
Laplacian::Laplacian(
8691
Mesh const& M,
87-
Eigen::Ref<IndexVectorX const> const& eg,
88-
Eigen::Ref<MatrixX const> const& wg,
92+
Eigen::Ref<MatrixX const> const& detJe,
8993
Eigen::Ref<MatrixX const> const& GNe,
90-
int dims)
94+
int dims,
95+
int qOrder)
9196
: eMeshElement(M.eElement),
9297
mMeshDims(M.mDims),
9398
mMeshOrder(M.mOrder),
9499
mOrder(),
100+
mQuadratureOrder(),
95101
mLaplacian(nullptr)
96102
{
97-
M.Apply([&]<pbat::fem::CMesh MeshType>(MeshType* mesh) {
98-
using LaplacianType = pbat::fem::SymmetricLaplacianMatrix<MeshType>;
99-
mLaplacian = new LaplacianType(*mesh, eg, wg, GNe, dims);
100-
mOrder = LaplacianType::kOrder;
101-
});
103+
M.ApplyWithQuadrature<kMaxQuadratureOrder>(
104+
[&]<pbat::fem::CMesh MeshType, auto QuadratureOrder>(MeshType* mesh) {
105+
using LaplacianType = pbat::fem::SymmetricLaplacianMatrix<MeshType, QuadratureOrder>;
106+
mLaplacian = new LaplacianType(*mesh, detJe, GNe, dims);
107+
mOrder = LaplacianType::kOrder;
108+
mQuadratureOrder = LaplacianType::kQuadratureOrder;
109+
},
110+
qOrder);
102111
}
103112

104113
CSCMatrix Laplacian::ToMatrix() const
@@ -120,20 +129,20 @@ std::tuple<Index, Index> Laplacian::Shape() const
120129

121130
MatrixX const& Laplacian::ElementLaplacians() const
122131
{
123-
MatrixX* deltagPtr;
132+
MatrixX* deltaEPtr;
124133
Apply([&]<class LaplacianType>(LaplacianType* laplacian) {
125-
deltagPtr = std::addressof(laplacian->deltag);
134+
deltaEPtr = std::addressof(laplacian->deltaE);
126135
});
127-
return *deltagPtr;
136+
return *deltaEPtr;
128137
}
129138

130139
MatrixX& Laplacian::ElementLaplacians()
131140
{
132-
MatrixX* deltagPtr;
141+
MatrixX* deltaEPtr;
133142
Apply([&]<class LaplacianType>(LaplacianType* laplacian) {
134-
deltagPtr = std::addressof(laplacian->deltag);
143+
deltaEPtr = std::addressof(laplacian->deltaE);
135144
});
136-
return *deltagPtr;
145+
return *deltaEPtr;
137146
}
138147

139148
int const& Laplacian::dims() const
@@ -163,11 +172,16 @@ Laplacian::~Laplacian()
163172
template <class Func>
164173
void Laplacian::Apply(Func&& f) const
165174
{
166-
ApplyToMesh(mMeshDims, mMeshOrder, eMeshElement, [&]<pbat::fem::CMesh MeshType>() {
167-
using LaplacianType = pbat::fem::SymmetricLaplacianMatrix<MeshType>;
168-
LaplacianType* laplacian = reinterpret_cast<LaplacianType*>(mLaplacian);
169-
f.template operator()<LaplacianType>(laplacian);
170-
});
175+
ApplyToMeshWithQuadrature<kMaxQuadratureOrder>(
176+
mMeshDims,
177+
mMeshOrder,
178+
eMeshElement,
179+
mQuadratureOrder,
180+
[&]<pbat::fem::CMesh MeshType, auto QuadratureOrder>() {
181+
using LaplacianType = pbat::fem::SymmetricLaplacianMatrix<MeshType, QuadratureOrder>;
182+
LaplacianType* laplacian = reinterpret_cast<LaplacianType*>(mLaplacian);
183+
f.template operator()<LaplacianType>(laplacian);
184+
});
171185
}
172186

173187
} // namespace fem

python/examples/diffusion_smoothing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
# Construct Galerkin laplacian, mass and gradient operators
2323
mesh = pbat.fem.Mesh(
2424
V.T, F.T, element=pbat.fem.Element.Triangle, order=1)
25-
L, egL, wgL, GNegL = pbat.fem.laplacian(mesh)
25+
L, detJeL, GNeL = pbat.fem.laplacian(mesh)
2626
M, detJeM = pbat.fem.mass_matrix(mesh, dims=1)
2727
# Setup heat diffusion
2828
dt = 0.016

python/examples/laplace.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ def harmonic_field(V: np.ndarray, C: np.ndarray, order: int, eps: float = 0.1):
1111
mesh = pbat.fem.Mesh(
1212
V.T, C.T, element=pbat.fem.Element.Tetrahedron, order=order)
1313
quadrature_order = max(int(2*(order-1)), 1)
14-
L, egL, wgL, GNegL = pbat.fem.laplacian(
14+
L, detJeL, GNeL = pbat.fem.laplacian(
1515
mesh, quadrature_order=quadrature_order)
1616
# Set Dirichlet boundary conditions at bottom and top of the model
1717
Xmin = mesh.X.min(axis=1)

python/pbatoolkit/fem.py

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ def divergence(mesh, quadrature_order: int = 1, eg: np.ndarray = None, wg: np.nd
3131
Args:
3232
mesh (pbat.fem.Mesh):
3333
quadrature_order (int, optional): Polynomial order to use for operator construction. Defaults to 1.
34-
eg (np.ndarray, optional): |#quad.pts.| array of elements corresponding to quadrature points. Defaults to None.
35-
wg (np.ndarray, optional): |#quad.pts.| array of quadrature weights. Defaults to None.
34+
eg (np.ndarray, optional): |#quad.pts.|x1 array of elements corresponding to quadrature points. Defaults to None.
35+
wg (np.ndarray, optional): |#quad.pts.|x1 array of quadrature weights. Defaults to None.
3636
GNeg (np.ndarray, optional): |#elem. nodes|x|#dims * #quad.pts.| array of shape function gradients at quadrature points. Defaults to None.
3737
3838
Returns:
39-
(scipy.sparse.csc_matrix, np.ndarray, np.ndarray):
39+
(scipy.sparse.csc_matrix, np.ndarray):
4040
"""
4141
should_compute_quadrature = eg is None or wg is None or GNeg is None
4242
G, eg, GNeg = gradient(mesh, quadrature_order=quadrature_order, as_matrix=True) if should_compute_quadrature else gradient(
@@ -60,7 +60,7 @@ def gradient(mesh, quadrature_order: int = 1, eg: np.ndarray = None, GNeg: np.nd
6060
as_matrix (bool, optional): Return the operator as a sparse matrix. Defaults to True.
6161
6262
Returns:
63-
(scipy.sparse.csc_matrix | pbat.fem.Gradient, np.ndarray, np.ndarray):
63+
(scipy.sparse.csc_matrix | pbat.fem.Gradient, np.ndarray):
6464
"""
6565

6666
if GNeg is None:
@@ -100,7 +100,7 @@ def hyper_elastic_potential(
100100
GNeg (np.ndarray, optional): Shape function gradients at quadrature points. Defaults to None.
101101
102102
Returns:
103-
(pbat.fem.HyperElasticPotential, np.ndarray, np.ndarray, np.ndarray):
103+
(pbat.fem.HyperElasticPotential, np.ndarray, np.ndarray):
104104
"""
105105
if eg is None or wg is None or GNeg is None:
106106
wg = _fem.inner_product_weights(
@@ -122,37 +122,32 @@ def laplacian(
122122
mesh,
123123
dims: int = 1,
124124
quadrature_order: int = 1,
125-
eg: np.ndarray = None,
126-
wg: np.ndarray = None,
127-
GNeg: np.ndarray = None,
125+
detJe: np.ndarray = None,
126+
GNe: np.ndarray = None,
128127
as_matrix: bool = True):
129128
"""Construct an FEM Laplacian operator
130129
131130
Args:
132131
mesh (pbat.fem.Mesh):
133132
dims (int, optional): Solution space dimensions. Defaults to 1.
134133
quadrature_order (int, optional): Polynomial order to use for operator construction. Defaults to 1.
135-
eg (np.ndarray, optional): |#quad.pts.| array of elements associated with quadrature points. Defaults to None.
136-
wg (np.ndarray, optional): |#quad.pts.| array of quadrature weights. Defaults to None.
137-
GNeg (np.ndarray, optional): Shape function gradients at quadrature points. Defaults to None.
134+
detJe (np.ndarray, optional): Jacobian determinants at quadrature points. Defaults to None.
135+
GNe (np.ndarray, optional): Shape function gradients at quadrature points. Defaults to None.
138136
as_matrix (bool, optional): Return the operator as a sparse matrix. Defaults to True.
139137
140138
Returns:
141-
(pbat.fem.Laplacian | scipy.sparse.csc_matrix, np.ndarray, np.ndarray, np.ndarray):
139+
(pbat.fem.Laplacian | scipy.sparse.csc_matrix, np.ndarray, np.ndarray):
142140
"""
143-
if eg is None or wg is None or GNeg is None:
144-
wg = _fem.inner_product_weights(
141+
if detJe is None:
142+
detJe = _fem.jacobian_determinants(
145143
mesh, quadrature_order=quadrature_order)
146-
eg = np.linspace(0, mesh.E.shape[1]-1,
147-
num=mesh.E.shape[1], dtype=np.int64)
148-
eg = np.repeat(eg, wg.shape[0])
149-
wg = wg.flatten(order="F")
150-
GNeg = _fem.shape_function_gradients(
144+
if GNe is None:
145+
GNe = _fem.shape_function_gradients(
151146
mesh, quadrature_order=quadrature_order)
152-
L = _fem.Laplacian(mesh, eg, wg, GNeg, dims=dims,
147+
L = _fem.Laplacian(mesh, detJe, GNe, dims=dims,
153148
quadrature_order=quadrature_order)
154149
L = L.to_matrix() if as_matrix else L
155-
return L, eg, wg, GNeg
150+
return L, detJe, GNe
156151

157152

158153
def mass_matrix(

source/pbat/fem/Gradient.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@ TEST_CASE("[fem] Gradient")
8686
CSCMatrix Lhat = -GMT * Ihat.asDiagonal() * GM;
8787
VectorX const LhatValues = Lhat.coeffs();
8888
Lhat.coeffs().setOnes();
89-
using LaplacianType = fem::SymmetricLaplacianMatrix<Mesh>;
90-
VectorX const wg = fem::InnerProductWeights<kQuadratureOrder>(mesh).reshaped();
91-
CSCMatrix L = LaplacianType(mesh, eg, wg, GNe).ToMatrix();
89+
using LaplacianType = fem::SymmetricLaplacianMatrix<Mesh, kQuadratureOrder>;
90+
MatrixX const detJe = fem::DeterminantOfJacobian<kQuadratureOrder>(mesh);
91+
CSCMatrix L = LaplacianType(mesh, detJe, GNe).ToMatrix();
9292
VectorX const Lvalues = L.coeffs();
9393
L.coeffs().setOnes();
9494
Scalar const LsparsityError = (L - Lhat).squaredNorm();

source/pbat/fem/HyperElasticPotential.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,11 @@ TEST_CASE("[fem] HyperElasticPotential")
4747

4848
MeshType const M(V, C);
4949
VectorX const x = M.X.reshaped();
50-
MatrixX const wg = fem::InnerProductWeights<kQuadratureOrder>(M).reshaped();
50+
MatrixX const WG = fem::InnerProductWeights<kQuadratureOrder>(M);
51+
VectorX const wg = WG.reshaped();
5152
MatrixX const GNeg = fem::ShapeFunctionGradients<kQuadratureOrder>(M);
5253
IndexVectorX const eg = IndexVectorX::LinSpaced(M.E.cols(), Index(0), M.E.cols() - 1)
53-
.replicate(1, wg.size() / M.E.cols())
54+
.replicate(1, WG.rows())
5455
.transpose()
5556
.reshaped();
5657
ElasticPotentialType U(M, eg, wg, GNeg, x, Y, nu);

source/pbat/fem/LaplacianMatrix.cpp

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,11 @@ TEST_CASE("[fem] LaplacianMatrix")
4444
return (kOrder - 1) + (kOrder - 1);
4545
}();
4646

47-
using LaplacianMatrix = fem::SymmetricLaplacianMatrix<Mesh>;
48-
VectorX const wg = fem::InnerProductWeights<kQuadratureOrder>(mesh).reshaped();
49-
IndexVectorX const eg =
50-
IndexVectorX::LinSpaced(mesh.E.cols(), Index(0), mesh.E.cols() - 1)
51-
.replicate(1, wg.size() / mesh.E.cols())
52-
.transpose()
53-
.reshaped();
54-
MatrixX const GNeg = fem::ShapeFunctionGradients<kQuadratureOrder>(mesh);
47+
using LaplacianMatrix = fem::SymmetricLaplacianMatrix<Mesh, kQuadratureOrder>;
48+
MatrixX const detJe = fem::DeterminantOfJacobian<kQuadratureOrder>(mesh);
49+
MatrixX const GNe = fem::ShapeFunctionGradients<kQuadratureOrder>(mesh);
5550
CHECK(math::CLinearOperator<LaplacianMatrix>);
56-
LaplacianMatrix matrixFreeLaplacian(mesh, eg, wg, GNeg, outDims);
51+
LaplacianMatrix matrixFreeLaplacian(mesh, detJe, GNe, outDims);
5752

5853
CSCMatrix const L = matrixFreeLaplacian.ToMatrix();
5954
CHECK_EQ(L.rows(), n);

0 commit comments

Comments
 (0)