LehrFEM++ 1.0.0
A simple Finite Element Library for teaching
ultraweak_dpg.h
1
9#ifndef PROJECTS_DPG_CS_ULTRAWEAK_DPG_H
10#define PROJECTS_DPG_CS_ULTRAWEAK_DPG_H
11
12#include <lf/base/base.h>
13#include <lf/fe/fe.h>
14#include <lf/mesh/test_utils/test_meshes.h>
15#include <lf/mesh/utils/utils.h>
16#include <lf/refinement/refinement.h>
17#include <lf/uscalfe/uscalfe.h>
18
19#include <cmath>
20
21#include "../dpg_element_matrix_provider.h"
22#include "../dpg_element_vector_provider.h"
23#include "../dpg_tools.h"
24#include "../product_element_matrix_provider_builder.h"
25#include "../product_element_vector_provider_builder.h"
26#include "../product_fe_space.h"
27#include "../product_fe_space_factory.h"
28#include "../test/convection_diffusion_ell_bvp.h"
29
30// utility type to return the reported energy norms:
31//[dofs, error in u-component,error in sigma-component, error estimate.]
32using energy_vector_ultraweak =
33 std::vector<std::tuple<int, double, double, double>>;
34
35namespace projects::dpg::test {
36
59template <typename SOLFUNC_U, typename SOLFUNC_SIGMA, typename ALPHAFUNC,
60 typename BETAFUNC, typename FFUNC, typename GFUNC>
61energy_vector_ultraweak
62TestConververgenceUltraWeakDPGConvectionDiffusionDirichletBVP(
63 size_type reflevels, SOLFUNC_U solution_u, SOLFUNC_SIGMA solution_sigma,
64 ALPHAFUNC alpha, BETAFUNC beta, FFUNC f, GFUNC g, int degree,
65 int enrichement, lf::base::RefEl ref_el) {
66 energy_vector_ultraweak errnorms{};
67
68 // generate a tensor product mesh based on the provided reference element.
69 std::shared_ptr<lf::mesh::Mesh> top_mesh;
70 std::unique_ptr<lf::mesh::MeshFactory> mesh_factory_ptr =
71 std::make_unique<lf::mesh::hybrid2d::MeshFactory>(2);
72 switch (ref_el) {
74 // construct a triangular tensor product mesh
75 lf::mesh::utils::TPTriagMeshBuilder builder(std::move(mesh_factory_ptr));
76 builder.setBottomLeftCorner(Eigen::Vector2d{0.0, 0.0})
77 .setTopRightCorner(Eigen::Vector2d{1.0, 1.0})
78 .setNumXCells(2)
79 .setNumYCells(2);
80 top_mesh = builder.Build();
81 break;
82 }
83
85 lf::mesh::utils::TPQuadMeshBuilder builder(std::move(mesh_factory_ptr));
86 builder.setBottomLeftCorner(Eigen::Vector2d{0.0, 0.0})
87 .setTopRightCorner(Eigen::Vector2d{1.0, 1.0})
88 .setNumXCells(2)
89 .setNumYCells(2);
90 top_mesh = builder.Build();
91 break;
92 }
93 default: {
94 LF_ASSERT_MSG(false, "unsupported reference element:" << ref_el);
95 break;
96 }
97 }
98
99 // Generate a hierarchy of test meshes by uniform refinement
100 std::shared_ptr<lf::refinement::MeshHierarchy> multi_mesh_p =
102 reflevels);
103 lf::refinement::MeshHierarchy& multi_mesh{*multi_mesh_p};
104 multi_mesh.PrintInfo(std::cout);
105
106 // get number of levels:
107 size_type L = multi_mesh.NumLevels();
108
109 // perform computations on all levels:
110 for (size_type l = 0; l < L; ++l) {
111 // retrive mesh for current level
112 std::shared_ptr<const lf::mesh::Mesh> mesh_p{multi_mesh.getMesh(l)};
113
114 // construct trial space:
115 ProductUniformFESpaceFactory<double> factory_trial(mesh_p);
116 auto u = factory_trial.AddL2Component(degree);
117 auto sigma_x = factory_trial.AddL2Component(degree);
118 auto sigma_y = factory_trial.AddL2Component(degree);
119 auto u_hat = factory_trial.AddTraceComponent(degree + 1);
120 auto q_n = factory_trial.AddFluxComponent(degree);
121 auto fe_space_trial = factory_trial.Build();
122
123 // construct test space:
124 ProductUniformFESpaceFactory<double> factory_test(mesh_p);
125 auto v = factory_test.AddL2Component(degree + enrichement);
126 auto tau_x = factory_test.AddL2Component(degree + enrichement);
127 auto tau_y = factory_test.AddL2Component(degree + enrichement);
128 auto fe_space_test = factory_test.Build();
129
130 // wrap functions into a mesh function:
131 auto alpha_mf = lf::mesh::utils::MeshFunctionGlobal(alpha);
132 auto alpha_inf_mf = lf::mesh::utils::MeshFunctionGlobal(
133 [&alpha](const Eigen::Vector2d& x) -> double {
134 return 1.0 / alpha(x);
135 });
136 auto beta_mf = lf::mesh::utils::MeshFunctionGlobal(beta);
138 auto one_mf = lf::mesh::utils::MeshFunctionConstant(1.0);
139 auto zero_mf =
140 lf::mesh::utils::MeshFunctionConstant(Eigen::Vector2d(0.0, 0.0));
141 auto x_selector_mf = lf::mesh::utils::MeshFunctionGlobal(
142 [](const Eigen::Vector2d & /*x*/) -> Eigen::Matrix2d {
143 return (Eigen::MatrixXd(2, 2) << 1.0, 0.0, 0.0, 0.0).finished();
144 });
145 auto y_selector_mf = lf::mesh::utils::MeshFunctionGlobal(
146 [](const Eigen::Vector2d & /*x*/) -> Eigen::Matrix2d {
147 return (Eigen::MatrixXd(2, 2) << 0.0, 0.0, 0.0, 1.0).finished();
148 });
149
150 auto x_selector_mf_vector =
151 lf::mesh::utils::MeshFunctionConstant(Eigen::Vector2d(1.0, 0.0));
152 auto y_selector_mf_vector =
153 lf::mesh::utils::MeshFunctionConstant(Eigen::Vector2d(0.0, 1.0));
154
155 // construct extended stiffness provider
156 ProductElementMatrixProviderBuilder stiffness_builder(fe_space_trial,
157 fe_space_test);
158 stiffness_builder.AddConvectionElementMatrixProvider(u, v, -beta_mf,
159 zero_mf);
160 stiffness_builder.AddConvectionElementMatrixProvider(
161 sigma_x, v, x_selector_mf_vector, zero_mf);
162 stiffness_builder.AddConvectionElementMatrixProvider(
163 sigma_y, v, y_selector_mf_vector, zero_mf);
164
165 // stiffness_builder.AddTraceElementMatrixProvider(u_hat, v, beta_mf);
166 stiffness_builder.AddFluxElementMatrixProvider(q_n, v, -one_mf);
167
168 stiffness_builder.AddConvectionElementMatrixProvider(
169 u, tau_x, x_selector_mf_vector, zero_mf);
170 stiffness_builder.AddConvectionElementMatrixProvider(
171 u, tau_y, y_selector_mf_vector, zero_mf);
172
173 stiffness_builder.AddReactionElementMatrixProvider(sigma_x, tau_x,
174 alpha_inf_mf);
175 stiffness_builder.AddReactionElementMatrixProvider(sigma_y, tau_y,
176 alpha_inf_mf);
177
178 stiffness_builder.AddTraceElementMatrixProvider(u_hat, tau_x,
179 -x_selector_mf_vector);
180 stiffness_builder.AddTraceElementMatrixProvider(u_hat, tau_y,
181 -y_selector_mf_vector);
182 auto stiffness_provider = stiffness_builder.Build();
183
184 // construct gram matrix provider
185 ProductElementMatrixProviderBuilder gramian_builder(fe_space_test,
186 fe_space_test);
187 gramian_builder.AddDiffusionElementMatrixProvider(v, v, one_mf);
188 gramian_builder.AddReactionElementMatrixProvider(v, v, one_mf);
189
190 gramian_builder.AddDiffusionElementMatrixProvider(tau_x, tau_x,
191 x_selector_mf);
192 gramian_builder.AddDiffusionElementMatrixProvider(tau_y, tau_y,
193 y_selector_mf);
194
195 gramian_builder.AddDiffusionElementMatrixProvider(
196 tau_y, tau_x,
198 [](const Eigen::Vector2d & /*x*/) -> Eigen::Matrix2d {
199 return (Eigen::MatrixXd(2, 2) << 0.0, 1.0, 0.0, 0.0).finished();
200 }));
201 gramian_builder.AddDiffusionElementMatrixProvider(
202 tau_x, tau_y,
204 [](const Eigen::Vector2d & /*x*/) -> Eigen::Matrix2d {
205 return (Eigen::MatrixXd(2, 2) << 0.0, 0.0, 1.0, 0.0).finished();
206 }));
207
208 gramian_builder.AddReactionElementMatrixProvider(tau_x, tau_x, one_mf);
209 gramian_builder.AddReactionElementMatrixProvider(tau_y, tau_y, one_mf);
210 auto gramian_provider = gramian_builder.Build();
211
212 // construct the rhs provider:
213 ProductElementVectorProviderBuilder rhs_builder(fe_space_test);
214 rhs_builder.AddLoadElementVectorProvider(v, f_mf);
215 auto rhs_provider = rhs_builder.Build();
216
217 // initialize the dpg providers:
218 auto element_matrix_provider =
219 std::make_shared<DpgElementMatrixProvider<double>>(stiffness_provider,
220 gramian_provider);
221 auto element_vector_provider =
222 std::make_shared<DpgElementVectorProvider<double>>(
223 rhs_provider, stiffness_provider, gramian_provider);
224
225 // initialize the boundary value problem
226 auto h = [](const Eigen::Vector2d & /*x*/) -> double { return 0.0; };
227 auto dirichlet_selector = lf::mesh::utils::flagEntitiesOnBoundary(mesh_p);
228 auto bvp = std::make_shared<FullConvectionDiffusionBVP<
229 decltype(alpha), decltype(beta), decltype(f), decltype(g), decltype(h),
230 decltype(dirichlet_selector)>>(
231 FullConvectionDiffusionBVP(alpha, beta, f, g, h,
232 std::move(dirichlet_selector)));
233
234 // Assemble full system:
235 auto [A, phi] = ConvectionDiffusionDPGLinSys<double>(
236 fe_space_trial, element_matrix_provider, element_vector_provider, bvp,
237 u_hat, q_n);
238
239 // calculate full solution
240 Eigen::SimplicialLDLT<Eigen::SparseMatrix<double>> solver;
241 solver.compute(A);
242 Eigen::VectorXd sol_vec = solver.solve(phi);
243
244 // extract u component:
245 auto& dofh = fe_space_trial->LocGlobMap();
246 auto fe_space_u = fe_space_trial->ComponentFESpace(u);
247 Eigen::VectorXd sol_vec_u =
248 sol_vec.segment(dofh.Offset(u), dofh.NumDofs(u));
249
250 // Extract sigma_x component:
251 auto fe_space_sigma_x = fe_space_trial->ComponentFESpace(sigma_x);
252 Eigen::VectorXd sol_vec_sigma_x =
253 sol_vec.segment(dofh.Offset(sigma_x), dofh.NumDofs(sigma_x));
254
255 // Extract sigma_y component:
256 auto fe_space_sigma_y = fe_space_trial->ComponentFESpace(sigma_y);
257 Eigen::VectorXd sol_vec_sigma_y =
258 sol_vec.segment(dofh.Offset(sigma_y), dofh.NumDofs(sigma_y));
259
260 // wrap finite element solution and exact solution into a mesh function:
261 auto mf_fe_u = lf::fe::MeshFunctionFE(fe_space_u, sol_vec_u);
262 auto mf_solution_u = lf::mesh::utils::MeshFunctionGlobal(solution_u);
263
264 auto mf_fe_sigma_x =
265 lf::fe::MeshFunctionFE(fe_space_sigma_x, sol_vec_sigma_x);
266 auto mf_solution_sigma_x = lf::mesh::utils::MeshFunctionGlobal(
267 [&solution_sigma](Eigen::Vector2d x) -> double {
268 return solution_sigma(x)[0];
269 });
270
271 auto mf_fe_sigma_y =
272 lf::fe::MeshFunctionFE(fe_space_sigma_y, sol_vec_sigma_y);
273 auto mf_solution_sigma_y = lf::mesh::utils::MeshFunctionGlobal(
274 [&solution_sigma](Eigen::Vector2d x) -> double {
275 return solution_sigma(x)[1];
276 });
277
278 double L2err_u = std::sqrt(lf::fe::IntegrateMeshFunction(
279 *mesh_p, lf::mesh::utils::squaredNorm(mf_fe_u - mf_solution_u), 10));
280
281 double L2err_sigma_x = std::sqrt(lf::fe::IntegrateMeshFunction(
282 *mesh_p,
283 lf::mesh::utils::squaredNorm(mf_fe_sigma_x - mf_solution_sigma_x), 10));
284 double L2err_sigma_y = std::sqrt(lf::fe::IntegrateMeshFunction(
285 *mesh_p,
286 lf::mesh::utils::squaredNorm(mf_fe_sigma_y - mf_solution_sigma_y), 10));
287
288 double L2err_sigma = std::sqrt(lf::fe::IntegrateMeshFunction(
289 *mesh_p,
290 lf::mesh::utils::squaredNorm(mf_fe_sigma_x - mf_solution_sigma_x) +
291 lf::mesh::utils::squaredNorm(mf_fe_sigma_y - mf_solution_sigma_y),
292 10));
293
294 auto local_errors =
295 ElementErrorEstimators(fe_space_trial->LocGlobMap(), stiffness_provider,
296 gramian_provider, rhs_provider, sol_vec);
297 double posteriorError = EvalPosteriorError(mesh_p, local_errors);
298 std::cout << "Level " << l << "(" << dofh.NumDofs()
299 << ") , L2 Error u: " << L2err_u
300 << ", L2 error sigma: " << L2err_sigma << "(x:" << L2err_sigma_x
301 << ", y: " << L2err_sigma_y << ")"
302 << ", posterior error: " << posteriorError << std::endl;
303 errnorms.emplace_back(dofh.NumDofs(), L2err_u, L2err_sigma, posteriorError);
304 }
305 return errnorms;
306}
307
308} // namespace projects::dpg::test
309
310#endif // PROJECTS_DPG_CS_ULTRAWEAK_DPG_H
Represents a reference element with all its properties.
Definition: ref_el.h:106
static constexpr RefEl kTria()
Returns the reference triangle.
Definition: ref_el.h:158
static constexpr RefEl kQuad()
Returns the reference quadrilateral.
Definition: ref_el.h:166
A MeshFunction which takes the same constant value on the whole mesh.
MeshFunction wrapper for a simple function of physical coordinates.
Implements a Builder for a tensor product grid (with rectangular cells)
Implements a MeshBuilder that generates a triangular structured mesh.
A hierarchy of nested 2D hybrid meshes created by refinement.
std::ostream & PrintInfo(std::ostream &o, unsigned ctrl=0) const
Output of information about the mesh hierarchy.
MeshFunctionFE(std::shared_ptr< T >, const Eigen::Matrix< SCALAR_COEFF, Eigen::Dynamic, 1 > &) -> MeshFunctionFE< typename T::Scalar, SCALAR_COEFF >
auto IntegrateMeshFunction(const lf::mesh::Mesh &mesh, const MF &mf, const QR_SELECTOR &qr_selector, const ENTITY_PREDICATE &ep=base::PredicateTrue{}, int codim=0) -> mesh::utils::MeshFunctionReturnType< MF >
Integrate a MeshFunction over a mesh (with quadrature rules)
Definition: fe_tools.h:111
CodimMeshDataSet< bool > flagEntitiesOnBoundary(const std::shared_ptr< const Mesh > &mesh_p, lf::base::dim_t codim)
flag entities of a specific co-dimension located on the boundary
std::shared_ptr< MeshHierarchy > GenerateMeshHierarchyByUniformRefinemnt(const std::shared_ptr< lf::mesh::Mesh > &mesh_p, lf::base::size_type ref_lev, RefPat ref_pat)
Generated a sequence of nested 2D hybrid mehes by regular or barycentric refinement.
lf::mesh::utils::CodimMeshDataSet< SCALAR > ElementErrorEstimators(const ProductUniformFEDofHandler &trial_dofh, std::shared_ptr< ProductElementMatrixProvider< SCALAR > > stiffness_provider, std::shared_ptr< ProductElementMatrixProvider< SCALAR > > gramian_provider, std::shared_ptr< ProductElementVectorProvider< SCALAR > > load_provider, const Eigen::Matrix< SCALAR, Eigen::Dynamic, 1 > &sol_vec)
Evaluation of the local DPG error estimators on a mesh.
Definition: dpg_tools.h:327
SCALAR EvalPosteriorError(const std::shared_ptr< const lf::mesh::Mesh > &mesh_p, const lf::mesh::utils::CodimMeshDataSet< SCALAR > &local_errors)
Evaluation of the DPG error estimator.
Definition: dpg_tools.h:374