Plugins
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 
45 namespace ug{
46 namespace Electromagnetism{
47 
54 template <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 
95 template <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 
143 template <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 
236 template <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 
319 template <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);
334 
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 
360 template <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 
396 template <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 
447 template <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 
486 template <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
499  if (elem->reference_object_id () != ROID_TETRAHEDRON)
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])
normal
#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)
int local(bglp_vertex_descriptor p)
ROID_TETRAHEDRON
void VecScaleAdd(double &dest, double alpha1, const double &v1, double alpha2, const double &v2)