Hermes2D  2.0
space_hcurl.cpp
1 // This file is part of Hermes2D.
2 //
3 // Hermes2D is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, either version 2 of the License, or
6 // (at your option) any later version.
7 //
8 // Hermes2D is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with Hermes2D. If not, see <http://www.gnu.org/licenses/>.
15 
16 #include "global.h"
17 #include "space_hcurl.h"
18 #include "matrix.h"
19 #include "quad_all.h"
20 #include "shapeset_hc_all.h"
21 #include "essential_boundary_conditions.h"
22 
23 namespace Hermes
24 {
25  namespace Hermes2D
26  {
27  template<typename Scalar>
28  HcurlSpace<Scalar>::HcurlSpace() : Space<Scalar>()
29  {
30  }
31 
32  template<typename Scalar>
33  void HcurlSpace<Scalar>::init(Shapeset* shapeset, int p_init)
34  {
35  if(shapeset == NULL)
36  {
37  this->shapeset = new HcurlShapeset;
38  this->own_shapeset = true;
39  }
40  if(this->shapeset->get_num_components() < 2) throw Hermes::Exceptions::Exception("HcurlSpace requires a vector shapeset.");
41 
42  this->precalculate_projection_matrix(0, this->proj_mat, this->chol_p);
43 
44  // set uniform poly order in elements
45  if(p_init < 0) throw Hermes::Exceptions::Exception("P_INIT must be >= 0 in an Hcurl space.");
46  else this->set_uniform_order_internal(p_init, HERMES_ANY_INT);
47 
48  // enumerate basis functions
49  this->assign_dofs();
50  }
51 
52  template<typename Scalar>
53  HcurlSpace<Scalar>::HcurlSpace(const Mesh* mesh, EssentialBCs<Scalar>* essential_bcs, int p_init, Shapeset* shapeset)
54  : Space<Scalar>(mesh, shapeset, essential_bcs)
55  {
56  init(shapeset, p_init);
57  }
58 
59  template<typename Scalar>
60  HcurlSpace<Scalar>::HcurlSpace(const Mesh* mesh, int p_init, Shapeset* shapeset)
61  : Space<Scalar>(mesh, shapeset, NULL)
62  {
63  init(shapeset, p_init);
64  }
65 
66  template<typename Scalar>
67  HcurlSpace<Scalar>::~HcurlSpace()
68  {
69  if(this->own_shapeset)
70  delete this->shapeset;
71  }
72 
73  template<typename Scalar>
74  void HcurlSpace<Scalar>::copy(const Space<Scalar>* space, Mesh* new_mesh)
75  {
76  Space<Scalar>::copy(space, new_mesh);
77 
78  this->precalculate_projection_matrix(0, this->proj_mat, this->chol_p);
79  }
80 
81  template<typename Scalar>
83  {
84  if(shapeset->get_id() < 20 && shapeset->get_id() > 9)
85  {
86  this->shapeset = shapeset;
87  this->own_shapeset = false;
88  }
89  else
90  throw Hermes::Exceptions::Exception("Wrong shapeset type in HcurlSpace<Scalar>::set_shapeset()");
91  }
92 
93  template<typename Scalar>
95  {
96  Node* en;
97  this->edge_functions_count = 0;
98  for_all_edge_nodes(en, this->mesh)
99  {
100  if(en->ref > 1 || en->bnd || this->mesh->peek_vertex_node(en->p1, en->p2) != NULL)
101  {
102  int ndofs = this->get_edge_order_internal(en) + 1;
103  this->ndata[en->id].n = ndofs;
104  if(en->bnd)
105  if(this->essential_bcs != NULL)
106  if(this->essential_bcs->get_boundary_condition(this->mesh->boundary_markers_conversion.get_user_marker(en->marker).marker) != NULL)
107  this->ndata[en->id].dof = this->H2D_CONSTRAINED_DOF;
108  else
109  {
110  this->ndata[en->id].dof = this->next_dof;
111  this->next_dof += ndofs * this->stride;
112  this->edge_functions_count += ndofs;
113  }
114  else
115  {
116  this->ndata[en->id].dof = this->next_dof;
117  this->next_dof += ndofs * this->stride;
118  this->edge_functions_count += ndofs;
119  }
120  else
121  {
122  this->ndata[en->id].dof = this->next_dof;
123  this->next_dof += ndofs * this->stride;
124  this->edge_functions_count += ndofs;
125  }
126  }
127  else
128  this->ndata[en->id].n = -1;
129  }
130  }
131 
132  template<typename Scalar>
133  void HcurlSpace<Scalar>::assign_bubble_dofs()
134  {
135  Element* e;
136  this->bubble_functions_count = 0;
137  for_all_active_elements(e, this->mesh)
138  {
139  typename Space<Scalar>::ElementData* ed = &this->edata[e->id];
140  ed->bdof = this->next_dof;
141  ed->n = this->shapeset->get_num_bubbles(ed->order, e->get_mode());
142  this->next_dof += ed->n * this->stride;
143  this->bubble_functions_count += ed->n;
144  }
145  }
146 
147  template<typename Scalar>
148  void HcurlSpace<Scalar>::get_boundary_assembly_list_internal(Element* e, int surf_num, AsmList<Scalar>* al) const
149  {
150  Node* en = e->en[surf_num];
151  typename Space<Scalar>::NodeData* nd = &this->ndata[en->id];
152 
153  if(nd->n >= 0) // unconstrained
154  {
155  if(nd->dof >= 0)
156  {
157  int ori = (e->vn[surf_num]->id < e->vn[e->next_vert(surf_num)]->id) ? 0 : 1;
158  for (int j = 0, dof = nd->dof; j < nd->n; j++, dof += this->stride)
159  al->add_triplet(this->shapeset->get_edge_index(surf_num, ori, j, e->get_mode()), dof, 1.0);
160  }
161  else
162  {
163  for (int j = 0; j < nd->n; j++)
164  al->add_triplet(this->shapeset->get_edge_index(surf_num, 0, j, e->get_mode()), -1, nd->edge_bc_proj[j]);
165  }
166  }
167  else // constrained
168  {
169  int part = nd->part;
170  int ori = part < 0 ? 1 : 0;
171  if(part < 0) part ^= ~0;
172 
173  nd = &this->ndata[nd->base->id]; // ccc
174  for (int j = 0, dof = nd->dof; j < nd->n; j++, dof += this->stride)
175  al->add_triplet(this->shapeset->get_constrained_edge_index(surf_num, j, ori, part, e->get_mode()), dof, 1.0);
176  }
177  }
178 
180 
181  template<typename Scalar>
182  Scalar* HcurlSpace<Scalar>::get_bc_projection(SurfPos* surf_pos, int order, EssentialBoundaryCondition<Scalar> *bc)
183  {
184  assert(order >= 0);
185  Scalar* proj = new Scalar[order + 1];
186 
187  Quad1DStd quad1d;
188  Scalar* rhs = proj;
189  int mo = quad1d.get_max_order();
190  double2* pt = quad1d.get_points(mo);
191 
192  Node* vn1 = this->mesh->get_node(surf_pos->v1);
193  Node* vn2 = this->mesh->get_node(surf_pos->v2);
194  double el = sqrt(sqr(vn1->x - vn2->x) + sqr(vn1->y - vn2->y));
195  el *= 0.5 * (surf_pos->hi - surf_pos->lo);
196 
197  // get boundary values at integration points, construct rhs
198  for (int i = 0; i <= order; i++)
199  {
200  rhs[i] = 0.0;
201  int ii = this->shapeset->get_edge_index(0, 0, i, surf_pos->base->get_mode());
202  for (int j = 0; j < quad1d.get_num_points(mo); j++)
203  {
204  double t = (pt[j][0] + 1) * 0.5, s = 1.0 - t;
205  surf_pos->t = surf_pos->lo * s + surf_pos->hi * t;
206 
207  if(bc->get_value_type() == EssentialBoundaryCondition<Scalar>::BC_CONST)
208  {
209  rhs[i] += pt[j][1] * this->shapeset->get_fn_value(ii, pt[j][0], -1.0, 0, surf_pos->base->get_mode())
210  * bc->value_const * el;
211  }
212  // If the BC is not constant.
213  else if(bc->get_value_type() == EssentialBoundaryCondition<Scalar>::BC_FUNCTION)
214  {
215  // Find out the (x, y) coordinate.
216  double x, y, n_x, n_y, t_x, t_y;
217  Nurbs* nurbs = surf_pos->base->is_curved() ? surf_pos->base->cm->nurbs[surf_pos->surf_num] : NULL;
218  CurvMap::nurbs_edge(surf_pos->base, nurbs, surf_pos->surf_num, 2.0*surf_pos->t - 1.0, x, y, n_x, n_y, t_x, t_y);
219  // Calculate.
220  rhs[i] += pt[j][1] * this->shapeset->get_fn_value(ii, pt[j][0], -1.0, 0, surf_pos->base->get_mode())
221  * bc->value(x, y, n_x, n_y, t_x, t_y) * el;
222  }
223  }
224  }
225 
226  // solve the system using a precalculated Cholesky decomposed projection matrix
227  cholsl(this->proj_mat, order + 1, this->chol_p, rhs, rhs);
228  return proj;
229  }
230 
231  template<typename Scalar>
232  void HcurlSpace<Scalar>::update_constrained_nodes(Element* e, EdgeInfo* ei0, EdgeInfo* ei1, EdgeInfo* ei2, EdgeInfo* ei3)
233  {
234  int j;
235  EdgeInfo* ei[4] = { ei0, ei1, ei2, ei3 };
236  typename Space<Scalar>::NodeData* nd;
237 
238  // on non-refined elements all we have to do is update edge nodes lying on constrained edges
239  if(e->active)
240  {
241  for (unsigned int i = 0; i < e->get_nvert(); i++)
242  {
243  if(ei[i] != NULL)
244  {
245  nd = &this->ndata[e->en[i]->id];
246  nd->base = ei[i]->node;
247  nd->part = ei[i]->part;
248  if(ei[i]->ori) nd->part ^= ~0;
249  }
250  }
251  }
252  // the element has sons - update mid-edge constrained vertex nodes
253  else
254  {
255  // create new edge infos where we don't have them yet
256  EdgeInfo ei_data[4];
257  for (unsigned int i = 0; i < e->get_nvert(); i++)
258  {
259  if(ei[i] == NULL)
260  {
261  j = e->next_vert(i);
262  Node* mid_vn = this->get_mid_edge_vertex_node(e, i, j);
263  if(mid_vn != NULL && mid_vn->is_constrained_vertex())
264  {
265  Node* mid_en = this->mesh->peek_edge_node(e->vn[i]->id, e->vn[j]->id);
266  if(mid_en != NULL)
267  {
268  ei[i] = ei_data + i;
269  ei[i]->node = mid_en;
270  ei[i]->part = -1;
271  ei[i]->lo = -1.0;
272  ei[i]->hi = 1.0;
273  ei[i]->ori = (e->vn[i]->id < e->vn[j]->id) ? 0 : 1;
274  }
275  }
276  }
277  }
278 
279  // create edge infos for half-edges
280  EdgeInfo half_ei_data[4][2];
281  EdgeInfo* half_ei[4][2];
282  for (unsigned int i = 0; i < e->get_nvert(); i++)
283  {
284  if(ei[i] == NULL)
285  {
286  half_ei[i][0] = half_ei[i][1] = NULL;
287  }
288  else
289  {
290  EdgeInfo* h0 = half_ei[i][0] = half_ei_data[i];
291  EdgeInfo* h1 = half_ei[i][1] = half_ei_data[i] + 1;
292 
293  h0->node = h1->node = ei[i]->node;
294  h0->part = (ei[i]->part + 1) * 2;
295  h1->part = h0->part + 1;
296  h0->hi = h1->lo = (ei[i]->lo + ei[i]->hi) / 2;
297  h0->lo = ei[i]->lo;
298  h1->hi = ei[i]->hi;
299  h1->ori = h0->ori = ei[i]->ori;
300  }
301  }
302 
303  // recur to sons
304  if(e->is_triangle())
305  {
306  update_constrained_nodes(e->sons[0], half_ei[0][0], NULL, half_ei[2][1], NULL);
307  update_constrained_nodes(e->sons[1], half_ei[0][1], half_ei[1][0], NULL, NULL);
308  update_constrained_nodes(e->sons[2], NULL, half_ei[1][1], half_ei[2][0], NULL);
309  update_constrained_nodes(e->sons[3], NULL, NULL, NULL, NULL);
310  }
311  else if(e->sons[2] == NULL) // 'horizontally' split quad
312  {
313  update_constrained_nodes(e->sons[0], ei[0], half_ei[1][0], NULL, half_ei[3][1]);
314  update_constrained_nodes(e->sons[1], NULL, half_ei[1][1], ei[2], half_ei[3][0]);
315  }
316  else if(e->sons[0] == NULL) // 'vertically' split quad
317  {
318  update_constrained_nodes(e->sons[2], half_ei[0][0], NULL, half_ei[2][1], ei[3]);
319  update_constrained_nodes(e->sons[3], half_ei[0][1], ei[1], half_ei[2][0], NULL);
320  }
321  else // fully split quad
322  {
323  update_constrained_nodes(e->sons[0], half_ei[0][0], NULL, NULL, half_ei[3][1]);
324  update_constrained_nodes(e->sons[1], half_ei[0][1], half_ei[1][0], NULL, NULL);
325  update_constrained_nodes(e->sons[2], NULL, half_ei[1][1], half_ei[2][0], NULL);
326  update_constrained_nodes(e->sons[3], NULL, NULL, half_ei[2][1], half_ei[3][0]);
327  }
328  }
329  }
330 
331  template<typename Scalar>
333  {
334  Element* e;
335  for_all_base_elements(e, this->mesh)
336  update_constrained_nodes(e, NULL, NULL, NULL, NULL);
337  }
338 
339  template class HERMES_API HcurlSpace<double>;
340  template class HERMES_API HcurlSpace<std::complex<double> >;
341  }
342 }