Plugins
Loading...
Searching...
No Matches
nedelec_local_ass_impl.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2013-2014: G-CSC, Goethe University Frankfurt
3 * Author: Dmitry Logashenko
4 *
5 * This file is part of UG4.
6 *
7 * UG4 is free software: you can redistribute it and/or modify it under the
8 * terms of the GNU Lesser General Public License version 3 (as published by the
9 * Free Software Foundation) with the following additional attribution
10 * requirements (according to LGPL/GPL v3 §7):
11 *
12 * (1) The following notice must be displayed in the Appropriate Legal Notices
13 * of covered and combined works: "Based on UG4 (www.ug4.org/license)".
14 *
15 * (2) The following notice must be displayed at a prominent place in the
16 * terminal output of covered works: "Based on UG4 (www.ug4.org/license)".
17 *
18 * (3) The following bibliography is recommended for citation and must be
19 * preserved in all covered files:
20 * "Reiter, S., Vogel, A., Heppner, I., Rupp, M., and Wittum, G. A massively
21 * parallel geometric multigrid solver on hierarchically distributed grids.
22 * Computing and visualization in science 16, 4 (2013), 151-164"
23 * "Vogel, A., Reiter, S., Rupp, M., Nägel, A., and Wittum, G. UG4 -- a novel
24 * flexible software system for simulating pde based models on high performance
25 * computers. Computing and visualization in science 16, 4 (2013), 165-179"
26 *
27 * This program is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 * GNU Lesser General Public License for more details.
31 */
32
33/*
34 * nedelec_local_ass_impl.cpp
35 * Implementation of the functions for assembling the local stiffness, mass and
36 * weak divergence matrices of the Nedelec-type-1 (Whitney-1) based discretization
37 * of the Maxwell equations, as well as the shape functions and their curls
38 * of the Nedelec element.
39 */
40
41/* UG4 headers: */
44
45namespace ug{
46namespace Electromagnetism{
47
54template <typename TDomain, typename TElem>
56(
57 const position_type * corners,
58 MathVector<WDim> grad [numCorners]
59)
60{
61// We assume here that TElem is a simplex (triangle or tetrahedron):
63 "Not a simplex (" << ref_elem_type::REFERENCE_OBJECT_ID
64 << ") as a template param. of NedelecT1_LDisc_forSimplex: Only triangles and tetrahedra accepted.");
65
66// Whitney-0 shapes:
67 const W0_shapes_type & W0_shapes = Provider<W0_shapes_type>::get ();
68
69// Reference mapping
71 ref_mapping.update (corners);
72
73// Compute the gradients
74
75 MathVector<dim> local_pos;
76 MathVector<dim> local_grad [numCorners];
78
79 local_pos = 0; // we compute the gradients at this point (remember: the mapping is linear)
80 W0_shapes.grads (local_grad, local_pos);
81 ref_mapping.jacobian_transposed_inverse (JtInv, local_pos);
82 for (size_t co = 0; co < numCorners; co++)
83 MatVecMult (grad[co], JtInv, local_grad[co]);
84}
85
95template <typename TDomain, typename TElem>
97(
98 const TDomain * domain,
99 TElem * elem,
100 size_t edge_corner [numEdges] [2]
101)
102{
104 const ref_elem_type & rRefElem = Provider<ref_elem_type>::get ();
105
106 const grid_type * grid = domain->grid().get ();
107 UG_ASSERT ((grid != 0), "No grid in the domain.");
108
109 for (size_t e = 0; e < numEdges; e++)
110 {
111 int co_0 = rRefElem.id (1, e, 0, 0);
112 int co_1 = rRefElem.id (1, e, 0, 1);
113 UG_ASSERT ((co_0 >= 0 && co_1 >= 0), "NedelecT1_LDisc_forSimplex::get_edge_corners: Internal error.");
114
115 const Edge * edge = ((grid_type *) grid)->get_edge (elem, e);
116
117 if (elem->vertex (co_0) == edge->vertex (0))
118 {
119 UG_ASSERT ((elem->vertex (co_0) == edge->vertex (0)), "NedelecT1_LDisc_forSimplex::get_edge_corners: Internal error.")
120 edge_corner[e][0] = co_0;
121 edge_corner[e][1] = co_1;
122 }
123 else
124 {
125 UG_ASSERT ((elem->vertex (co_0) == edge->vertex (1) && elem->vertex (co_1) == edge->vertex (0)),
126 "NedelecT1_LDisc_forSimplex::get_edge_corners: Internal error.")
127 edge_corner[e][0] = co_1;
128 edge_corner[e][1] = co_0;
129 }
130 }
131}
132
143template <typename TDomain, typename TElem>
145(
146 const TDomain * domain,
147 TElem * elem,
148 const position_type * corners,
149 number S [maxNumEdges][maxNumEdges],
150 number M [maxNumEdges][maxNumEdges]
151)
152{
153// clear the local matrices:
154 memset (S, 0, maxNumEdges * maxNumEdges * sizeof (number));
155 memset (M, 0, maxNumEdges * maxNumEdges * sizeof (number));
156
157// get volume of the grid element
158 number V = ElementSize<ref_elem_type, WDim> (corners);
159
160/* 1. Computation of the stiffness matrix: */
161
162// get the gradients of the Whitney-0 elements
163 MathVector<WDim> grad_w0 [numCorners];
164 compute_W0_grads (corners, grad_w0);
165
166// get the correspondence of the edges and the corners:
167 size_t edge_corner [numEdges] [2];
168 get_edge_corners (domain, elem, edge_corner);
169
170// compute \f$ \mathbf{rot} w^{(1)}_e / 2 = \mathbf{grad} w^{(0)}_m \times \mathbf{grad} w^{(0)}_n \f$
171// for every edge \f$ e = (m, n) \f$
172 MathVector<WDim> half_rot_w1 [numEdges];
173 for (size_t e = 0; e < numEdges; e++)
174 GenVecCross (half_rot_w1[e], grad_w0[edge_corner[e][0]], grad_w0[edge_corner[e][1]]);
175
176// compute the entries of the local stiffness matrix using its symmetry
177 for (size_t e_1 = 0; e_1 < numEdges; e_1++)
178 for (size_t e_2 = 0; e_2 <= e_1; e_2++)
179 S[e_1][e_2] = VecDot (half_rot_w1[e_1], half_rot_w1[e_2]) * 4 * V;
180 for (size_t e_1 = 0; e_1 < numEdges-1; e_1++)
181 for (size_t e_2 = e_1 + 1; e_2 < numEdges; e_2++) S[e_1][e_2] = S[e_2][e_1];
182
183/* 2. Computation of the mass matrix: */
184
185// REMARK: Below we compute the integrals of (w^{(1)}_e1, w^{(1)}_e2) over the
186// element by a generalization of the Simpson's quadrature rule for simplexes, cf.
187// A. Horwitz, A version of Simpson’s rule for multiple integrals,
188// Journal of Computational and Applied Mathematics 134 (2001), pp. 1-11,
189// DOI: 10.1016/S0377-0427(00)00444-1
190
191 static const number lambda = ((number) (WDim + 1)) / (WDim + 2);
192
193// compute the values of the w^{(1)}_e-functions at the center of the element
194 MathVector<WDim> w1_at_center[numEdges];
195 for (size_t e = 0; e < numEdges; e++)
196 {
197 // All the w^{(0)} (i.e. Lagrange) functions have the same value at the center
198 // of the element. (This can be considered as a definition of the 'center'.)
199 // This value is 1.0 / numCorners:
200 VecSubtract (w1_at_center[e], grad_w0[edge_corner[e][0]], grad_w0[edge_corner[e][1]]);
201 w1_at_center[e] /= numCorners;
202 }
203// compute the values of w^{(1)}_e-functions at the corners
204 MathVector<WDim> w1_at_co[numEdges][numCorners];
205 for (size_t e = 0; e < numEdges; e++)
206 {
207 // Every w^{(1)} is non-zero at only two corners of the element: the ends
208 // of the edge: At all the other corners the corresponding w^{(0)} are zero.
209 for (size_t co = 0; co < numCorners; co++) w1_at_co[e][co] = 0;
210 w1_at_co[e][edge_corner[e][0]] = grad_w0[edge_corner[e][1]];
211 w1_at_co[e][edge_corner[e][1]] -= grad_w0[edge_corner[e][0]];
212 }
213// assemble the mass matrix
214 for (size_t e_1 = 0; e_1 < numEdges; e_1++)
215 for (size_t e_2 = 0; e_2 <= e_1; e_2++)
216 {
217 number t = 0;
218 for (size_t co = 0; co < numCorners; co++)
219 t += VecDot (w1_at_co[e_1][co], w1_at_co[e_2][co]);
220 t /= numCorners;
221
222 M[e_1][e_2] = (lambda * VecDot (w1_at_center[e_1], w1_at_center[e_2])
223 + (1 - lambda) * t) * V;
224 }
225 for (size_t e_1 = 0; e_1 < numEdges-1; e_1++)
226 for (size_t e_2 = e_1 + 1; e_2 < numEdges; e_2++) M[e_1][e_2] = M[e_2][e_1];
227}
228
236template <typename TDomain, typename TElem>
238(
239 const TDomain * domain,
240 TElem * elem,
241 const position_type * corners,
242 number M [maxNumEdges][maxNumEdges]
243)
244{
245// clear the local matrix:
246 memset (M, 0, maxNumEdges * maxNumEdges * sizeof (number));
247
248// get volume of the grid element
249 number V = ElementSize<ref_elem_type, WDim> (corners);
250
251/* Computation of the mass matrix: */
252
253// get the gradients of the Whitney-0 elements
254 MathVector<WDim> grad_w0 [numCorners];
255 compute_W0_grads (corners, grad_w0);
256
257// get the correspondence of the edges and the corners:
258 size_t edge_corner [numEdges] [2];
259 get_edge_corners (domain, elem, edge_corner);
260
261// REMARK: Below we compute the integrals of (w^{(1)}_e1, w^{(1)}_e2) over the
262// element by a generalization of the Simpson's quadrature rule for simplexes, cf.
263// A. Horwitz, A version of Simpson’s rule for multiple integrals,
264// Journal of Computational and Applied Mathematics 134 (2001), pp. 1-11,
265// DOI: 10.1016/S0377-0427(00)00444-1
266
267 static const number lambda = ((number) (WDim + 1)) / (WDim + 2);
268
269// compute the values of the w^{(1)}_e-functions at the center of the element
270 MathVector<WDim> w1_at_center[numEdges];
271 for (size_t e = 0; e < numEdges; e++)
272 {
273 // All the w^{(0)} (i.e. Lagrange) functions have the same value at the center
274 // of the element. (This can be considered as a definition of the 'center'.)
275 // This value is 1.0 / numCorners:
276 VecSubtract (w1_at_center[e], grad_w0[edge_corner[e][0]], grad_w0[edge_corner[e][1]]);
277 w1_at_center[e] /= numCorners;
278 }
279// compute the values of w^{(1)}_e-functions at the corners
280 MathVector<WDim> w1_at_co[numEdges][numCorners];
281 for (size_t e = 0; e < numEdges; e++)
282 {
283 // Every w^{(1)} is non-zero at only two corners of the element: the ends
284 // of the edge: At all the other corners the corresponding w^{(0)} are zero.
285 for (size_t co = 0; co < numCorners; co++) w1_at_co[e][co] = 0;
286 w1_at_co[e][edge_corner[e][0]] = grad_w0[edge_corner[e][1]];
287 w1_at_co[e][edge_corner[e][1]] -= grad_w0[edge_corner[e][0]];
288 }
289// assemble the mass matrix
290 for (size_t e_1 = 0; e_1 < numEdges; e_1++)
291 for (size_t e_2 = 0; e_2 <= e_1; e_2++)
292 {
293 number t = 0;
294 for (size_t co = 0; co < numCorners; co++)
295 t += VecDot (w1_at_co[e_1][co], w1_at_co[e_2][co]);
296 t /= numCorners;
297
298 M[e_1][e_2] = (lambda * VecDot (w1_at_center[e_1], w1_at_center[e_2])
299 + (1 - lambda) * t) * V;
300 }
301 for (size_t e_1 = 0; e_1 < numEdges-1; e_1++)
302 for (size_t e_2 = e_1 + 1; e_2 < numEdges; e_2++) M[e_1][e_2] = M[e_2][e_1];
303}
304
319template <typename TDomain, typename TElem>
321(
322 const TDomain * domain,
323 TElem * elem,
324 const position_type * corners,
325 number B [][numEdges]
326)
327{
328// clear the local matrix:
329 memset (B, 0, numCorners * numEdges * sizeof (number));
330
331// get the gradients of the Whitney-0 elements
332 MathVector<WDim> grad_w0 [numCorners];
333 compute_W0_grads (corners, grad_w0);
335// get the correspondence of the edges and the corners:
336 size_t edge_corner [numEdges] [2];
337 get_edge_corners (domain, elem, edge_corner);
338
339// compute the values of the (- |T| w^{(1)}_e)-functions at the center of the element
340 number V = - ElementSize<ref_elem_type, WDim> (corners) / numCorners;
341 MathVector<WDim> T_w1_at_center[numEdges];
342 for (size_t e = 0; e < numEdges; e++)
343 {
344 VecSubtract (T_w1_at_center[e], grad_w0[edge_corner[e][0]], grad_w0[edge_corner[e][1]]);
345 T_w1_at_center[e] *= V;
346 }
347
348// compute the weak div matrix
349 for (size_t v = 0; v < numCorners; v++)
350 for (size_t e = 0; e < numEdges; e++)
351 B[v][e] = VecDot (T_w1_at_center[e], grad_w0[v]);
352}
353
360template <typename TDomain, typename TElem>
362(
363 const TDomain * domain,
364 TElem * elem,
365 const position_type * corners,
366 const MathVector<dim> local,
367 MathVector<WDim> shapes []
368)
369{
370// get the gradients of the Whitney-0 elements
371 MathVector<WDim> grad_w0 [numCorners];
372 compute_W0_grads (corners, grad_w0);
373
374// get the correspondence of the edges and the corners:
375 size_t edge_corner [numEdges] [2];
376 get_edge_corners (domain, elem, edge_corner);
377
378// Whitney-0 shapes:
379 const W0_shapes_type& W0_shapes = Provider<W0_shapes_type>::get ();
380
381// compute the shapes:
382 number w0 [numCorners];
383 W0_shapes.shapes (w0, local);
384 for (size_t e = 0; e < numEdges; e++)
385 {
386 size_t m = edge_corner[e][0], n = edge_corner[e][1];
387 VecScaleAdd (shapes[e], w0[m], grad_w0[n], - w0[n], grad_w0[m]);
388 }
389}
390
396template <typename TDomain, typename TElem>
398(
399 const TDomain * domain,
400 TElem * elem,
401 const position_type * corners,
402 const number dof [],
403 const MathVector<dim> local [],
404 const size_t n_pnt,
405 MathVector<WDim> values []
406)
407{
408// get the gradients of the Whitney-0 elements
409 MathVector<WDim> grad_w0 [numCorners];
410 compute_W0_grads (corners, grad_w0);
411
412// get the correspondence of the edges and the corners:
413 size_t edge_corner [numEdges] [2];
414 get_edge_corners (domain, elem, edge_corner);
415
416// Whitney-0 shapes:
417 const W0_shapes_type & W0_shapes = Provider<W0_shapes_type>::get ();
418
419// compute the values at all the points
420 for (size_t pnt = 0; pnt < n_pnt; pnt++)
421 {
422 // get Whitney-0 shapes
423 number w0 [numCorners];
424 W0_shapes.shapes (w0, local[pnt]);
425
426 // loop the shape functions
427 MathVector<WDim>& value = values[pnt];
428 value = 0.0;
429 for (size_t e = 0; e < numEdges; e++)
430 {
431 MathVector<WDim> shape;
432 size_t m = edge_corner[e][0], n = edge_corner[e][1];
433 VecScaleAdd (shape, w0[m], grad_w0[n], - w0[n], grad_w0[m]);
434 VecScaleAppend (value, dof[e], shape);
435 }
436 }
437}
438
447template <typename TDomain, typename TElem>
449(
450 const TDomain * domain,
451 TElem * elem,
452 const position_type * corners,
453 const number dof [],
454 MathVector<WDim> & curl_vec
455)
456{
457// get the gradients of the Whitney-0 elements
458 MathVector<WDim> grad_w0 [numCorners];
459 compute_W0_grads (corners, grad_w0);
460
461// get the correspondence of the edges and the corners:
462 size_t edge_corner [numEdges] [2];
463 get_edge_corners (domain, elem, edge_corner);
464
465// compute \f$ \mathbf{rot} w^{(1)}_e / 2 = \mathbf{grad} w^{(0)}_m \times \mathbf{grad} w^{(0)}_n \f$
466// for every edge \f$ e = (m, n) \f$
467 MathVector<WDim> half_rot_w1 [numEdges];
468 for (size_t e = 0; e < numEdges; e++)
469 GenVecCross (half_rot_w1[e], grad_w0[edge_corner[e][0]], grad_w0[edge_corner[e][1]]);
470
471// compute the curl
472 curl_vec = 0.0;
473 for (size_t e = 0; e < numEdges; e++)
474 VecScaleAppend (curl_vec, dof[e], half_rot_w1[e]);
475 curl_vec *= 2;
476}
477
486template <typename TDomain>
488(
489 const TDomain * domain,
490 GridObject * elem,
491 const position_type corners [],
492 const number dofs [],
493 const MathVector<3> & normal,
494 const position_type pnt,
495 number & flux
496)
497{
498// current implementation considers only tetrahedra
500 UG_THROW ("NedelecInterpolation::curl_flux:"
501 " No implementation of the Nedelec elements for"
502 " Reference Object " << elem->reference_object_id () <<
503 " of reference dim. 3 in a 3d domain. (This must be a tetrahedron.)");
504
505// compute the flux
506 number area;
507 MathVector<3> sect_corners [4];
508 size_t n_intersect = IntersectPlaneWithTetrahedron (sect_corners, pnt, normal, corners);
509 switch (n_intersect)
510 {
511 case 0:
512 flux = 0.0;
513 return 0.0;
514
515 case 3:
516 area = TriangleArea (sect_corners[0], sect_corners[1], sect_corners[2]);
517 break;
518 case 4:
519 area = TriangleArea (sect_corners[0], sect_corners[1], sect_corners[2])
520 + TriangleArea (sect_corners[0], sect_corners[2], sect_corners[3]);
521 break;
522
523 default:
524 UG_THROW ("NedelecInterpolation::curl_flux:"
525 " Illegal number of intersections of a plane with a tetrahedron.");
526 }
527
528// compute the curl (as a vector); note that it is constant in the element
529 MathVector<3> curl_vec;
531 corners, dofs, curl_vec);
532 flux = VecDot (curl_vec, normal) * area;
533
534 return area;
535};
536
537} // end namespace Electromagnetism
538} // end namespace ug
539
540/* End of File */
T * get()
void shapes(shape_type *vShape, const MathVector< dim > &x) const
void grads(grad_type *vGrad, const MathVector< dim > &x) const
virtual Vertex * vertex(size_t index) const
TDomain::position_type position_type
type of the geometric positions (WDim-vectors)
Definition nedelec_local_ass.h:565
static number curl_flux(const TDomain *domain, GridObject *elem, const position_type corners[], const number dofs[], const MathVector< WDim > &normal, const position_type pnt, number &flux)
computes flux of the curl through a given plane in an element
Definition nedelec_local_ass.h:457
static void local_mass(const TDomain *domain, TElem *elem, const position_type *corners, number M[maxNumEdges][maxNumEdges])
assembles the local mass matrix of the Nedelec elements
Definition nedelec_local_ass_impl.h:238
static void interpolate(const TDomain *domain, TElem *elem, const position_type *corners, const number dofs[], const MathVector< dim > local[], const size_t n_pnt, MathVector< WDim > values[])
computes of the values of the grid functions
Definition nedelec_local_ass_impl.h:398
TDomain::position_type position_type
type of the geometric positions (WDim-vectors)
Definition nedelec_local_ass.h:266
static void local_div_matrix(const TDomain *domain, TElem *elem, const position_type *corners, number B[][numEdges])
assembles the discrete weak div operator
Definition nedelec_local_ass_impl.h:321
static void compute_W0_grads(const position_type *corners, MathVector< WDim > grad[numCorners])
computes the gradients of the Whitney-0 (Lagrange P1) shape functions
Definition nedelec_local_ass_impl.h:56
static void get_shapes(const TDomain *domain, TElem *elem, const position_type *corners, const MathVector< dim > local, MathVector< WDim > shapes[])
computes the Nedelec shapes at a given point
Definition nedelec_local_ass_impl.h:362
static void curl(const TDomain *domain, TElem *elem, const position_type *corners, const number dofs[], MathVector< WDim > &curl_vec)
computes the curl of the grid functions (in 2d represented as instead of )
Definition nedelec_local_ass_impl.h:449
static void local_stiffness_and_mass(const TDomain *domain, TElem *elem, const position_type *corners, number S[maxNumEdges][maxNumEdges], number M[maxNumEdges][maxNumEdges])
assembles the local stiffness and mass matrices of the rot-rot operator
Definition nedelec_local_ass_impl.h:145
static void get_edge_corners(const TDomain *domain, TElem *elem, size_t edge_corner[numEdges][2])
gets the correspondence between the edge dof indices and the corners of the element
Definition nedelec_local_ass_impl.h:97
reference_element_traits< TElem >::reference_element_type ref_elem_type
type of reference element
Definition nedelec_local_ass.h:269
TDomain::grid_type grid_type
type of the grid
Definition nedelec_local_ass.h:263
static void curl(const TDomain *domain, TElem *elem, const position_type *corners, const number dofs[], MathVector< WDim > &curl_vec)
computes the curl of the grid functions (in 2d represented as instead of )
Definition nedelec_local_ass.h:184
virtual ReferenceObjectID reference_object_id() const=0
static TClass & get()
number jacobian_transposed_inverse(MathMatrix< worldDim, dim > &JTInv, const MathVector< dim > &locPos) const
void update(const MathVector< worldDim > *vCornerCoord)
SmartPtr< TGrid > grid()
size_t IntersectPlaneWithTetrahedron(vector3 intsOut[4], const vector3 &planePoint, const vector3 &planeNormal, const vector3 t[4])
#define UG_ASSERT(expr, msg)
#define UG_THROW(msg)
double number
void MatVecMult(vector_t_out &vOut, const matrix_t &m, const vector_t_in &v)
number TriangleArea(const vector_t &p1, const vector_t &p2, const vector_t &p3)
void VecScaleAppend(vector_t &vOut, typename vector_t::value_type s1, const vector_t &v1)
void GenVecCross(MathVector< dim > &result, const MathVector< dim > &v_1, const MathVector< dim > &v_2)
void VecSubtract(vector_t &vOut, const vector_t &v, typename vector_t::value_type s)
vector_t::value_type VecDot(const vector_t &v1, const vector_t &v2)
ROID_TETRAHEDRON
void VecScaleAdd(double &dest, double alpha1, const double &v1, double alpha2, const double &v2)