Hermes2D  2.0
space_h1.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 "space_h1.h"
17 namespace Hermes
18 {
19  namespace Hermes2D
20  {
21  template<typename Scalar>
22  H1Space<Scalar>::H1Space() : Space<Scalar>()
23  {
24  }
25 
26  template<typename Scalar>
27  void H1Space<Scalar>::init(Shapeset* shapeset, int p_init)
28  {
29  if(shapeset == NULL)
30  {
31  this->shapeset = new H1Shapeset;
32  this->own_shapeset = true;
33  }
34 
35  this->precalculate_projection_matrix(2, this->proj_mat, this->chol_p);
36 
37  // set uniform poly order in elements
38  if(p_init < 1)
39  throw Hermes::Exceptions::Exception("P_INIT must be >= 1 in an H1 space.");
40 
41  else this->set_uniform_order_internal(p_init, HERMES_ANY_INT);
42 
43  // enumerate basis functions
44  this->assign_dofs();
45  }
46 
47  template<typename Scalar>
48  H1Space<Scalar>::H1Space(const Mesh* mesh, EssentialBCs<Scalar>* essential_bcs, int p_init, Shapeset* shapeset)
49  : Space<Scalar>(mesh, shapeset, essential_bcs)
50  {
51  init(shapeset, p_init);
52  }
53 
54  template<typename Scalar>
55  H1Space<Scalar>::H1Space(const Mesh* mesh, int p_init, Shapeset* shapeset)
56  : Space<Scalar>(mesh, shapeset, NULL)
57  {
58  init(shapeset, p_init);
59  }
60 
61  template<typename Scalar>
62  H1Space<Scalar>::~H1Space()
63  {
64  if(this->own_shapeset)
65  delete this->shapeset;
66  }
67 
68  template<typename Scalar>
69  void H1Space<Scalar>::copy(const Space<Scalar>* space, Mesh* new_mesh)
70  {
71  Space<Scalar>::copy(space, new_mesh);
72 
73  this->precalculate_projection_matrix(2, this->proj_mat, this->chol_p);
74 
75  this->assign_dofs();
76  }
77 
78  template<typename Scalar>
80  {
81  if(shapeset->get_id() < 10)
82  {
83  this->shapeset = shapeset;
84  this->own_shapeset = false;
85  }
86  else
87  throw Hermes::Exceptions::Exception("Wrong shapeset type in H1Space<Scalar>::set_shapeset()");
88  }
89 
90  template<typename Scalar>
92  {
93  // Before assigning vertex DOFs, we must know which boundary vertex nodes are part of
94  // a natural BC and which are part of an essential BC. The critical are those which
95  // lie at an interface of both types of BCs and which must be treated as belonging
96  // to the essential part. Unfortunately this has to be done on a per-space basis, as
97  // the markers may have different meanings in different spaces. There is no way to
98  // look at the adjacent edge nodes given a vertex node, thus we have to walk through
99  // all elements in the mesh.
100 
101  // Vertex dofs.
102  Element* e;
103  this->vertex_functions_count = 0;
104  for_all_active_elements(e, this->mesh)
105  {
106  int order = this->get_element_order(e->id);
107  if(order > 0)
108  {
109  for (unsigned int i = 0; i < e->get_nvert(); i++)
110  {
111  Node* vn = e->vn[i];
112  typename Space<Scalar>::NodeData* nd = this->ndata + vn->id;
113  if(!vn->is_constrained_vertex() && nd->dof == this->H2D_UNASSIGNED_DOF)
114  {
115  if(nd->n == 0 || is_fixed_vertex(vn->id))
116  {
117  nd->dof = this->H2D_CONSTRAINED_DOF;
118  }
119  else
120  {
121  nd->dof = this->next_dof;
122  this->next_dof += this->stride;
123  this->vertex_functions_count++;
124  }
125  nd->n = 1;
126  }
127  }
128  }
129  }
130  }
131 
132  template<typename Scalar>
133  void H1Space<Scalar>::assign_edge_dofs()
134  {
135  // Edge dofs.
136  Element* e;
137  this->edge_functions_count = 0;
138  for_all_active_elements(e, this->mesh)
139  {
140  int order = this->get_element_order(e->id);
141  if(order > 0)
142  {
143  for (unsigned int i = 0; i < e->get_nvert(); i++)
144  {
145  Node* vn = e->vn[i];
146  typename Space<Scalar>::NodeData* nd = this->ndata + vn->id;
147  Node* en = e->en[i];
148  nd = this->ndata + en->id;
149  if(nd->dof == this->H2D_UNASSIGNED_DOF)
150  {
151  // If the edge node is not constrained, assign it dofs.
152  if(en->ref > 1 || en->bnd || this->mesh->peek_vertex_node(en->p1, en->p2) != NULL)
153  {
154  int ndofs = this->get_edge_order_internal(en) - 1;
155  nd->n = ndofs;
156 
157  if(en->bnd)
158  if(this->essential_bcs != NULL)
159  if(this->essential_bcs->get_boundary_condition(this->mesh->boundary_markers_conversion.get_user_marker(e->en[i]->marker).marker) != NULL)
160  nd->dof = this->H2D_CONSTRAINED_DOF;
161  else
162  {
163  nd->dof = this->next_dof;
164  this->next_dof += ndofs * this->stride;
165  this->edge_functions_count += ndofs;
166  }
167  else
168  {
169  nd->dof = this->next_dof;
170  this->next_dof += ndofs * this->stride;
171  this->edge_functions_count += ndofs;
172  }
173  else
174  {
175  nd->dof = this->next_dof;
176  this->next_dof += ndofs * this->stride;
177  this->edge_functions_count += ndofs;
178  }
179  }
180  else // Constrained edge node.
181  nd->n = -1;
182  }
183  }
184  }
185  }
186 
187  }
188  template<typename Scalar>
189  void H1Space<Scalar>::assign_bubble_dofs()
190  {
191  // Bubble dofs.
192  Element* e;
193  this->bubble_functions_count = 0;
194  for_all_active_elements(e, this->mesh)
195  {
196  int order = this->get_element_order(e->id);
197  if(order > 0)
198  {
199  typename Space<Scalar>::ElementData* ed = &this->edata[e->id];
200  ed->bdof = this->next_dof;
201  ed->n = order ? this->shapeset->get_num_bubbles(ed->order, e->get_mode()) : 0;
202  this->next_dof += ed->n * this->stride;
203  this->bubble_functions_count += ed->n;
204  }
205  }
206  }
207 
208  template<typename Scalar>
209  void H1Space<Scalar>::get_vertex_assembly_list(Element* e, int iv, AsmList<Scalar>* al) const
210  {
211  Node* vn = e->vn[iv];
212  typename Space<Scalar>::NodeData* nd = &this->ndata[vn->id];
213  int index = this->shapeset->get_vertex_index(iv, e->get_mode());
214  if(this->get_element_order(e->id) == 0) return;
215 
216  if(!vn->is_constrained_vertex()) // unconstrained
217  {
218  al->add_triplet(index, nd->dof, (nd->dof >= 0) ? 1.0 : *(nd->vertex_bc_coef));
219  }
220  else // constrained
221  {
222  //debug_log("! B cause of the triplet\n");
223  for (int j = 0; j < nd->ncomponents; j++)
224  if(nd->baselist[j].coef != (Scalar) 0)
225  {
226  al->add_triplet(index, nd->baselist[j].dof, nd->baselist[j].coef);
227  }
228  }
229  }
230 
231  template<typename Scalar>
232  void H1Space<Scalar>::get_boundary_assembly_list_internal(Element* e, int surf_num, AsmList<Scalar>* al) const
233  {
234  Node* en = e->en[surf_num];
235  typename Space<Scalar>::NodeData* nd = &this->ndata[en->id];
236  if(this->get_element_order(e->id) == 0)
237  return;
238 
239  if(nd->n >= 0) // unconstrained
240  {
241  if(nd->dof >= 0)
242  {
243  int ori = (e->vn[surf_num]->id < e->vn[e->next_vert(surf_num)]->id) ? 0 : 1;
244  for (int j = 0, dof = nd->dof; j < nd->n; j++, dof += this->stride)
245  al->add_triplet(this->shapeset->get_edge_index(surf_num, ori, j + 2, e->get_mode()), dof, 1.0);
246  }
247  else
248  {
249  for (int j = 0; j < nd->n; j++)
250  {
251  al->add_triplet(this->shapeset->get_edge_index(surf_num, 0, j + 2, e->get_mode()), -1, nd->edge_bc_proj[j + 2]);
252  }
253  }
254  }
255  else // constrained
256  {
257  int part = nd->part;
258  int ori = part < 0 ? 1 : 0;
259  if(part < 0) part ^= ~0;
260 
261  nd = &this->ndata[nd->base->id];
262  for (int j = 0, dof = nd->dof; j < nd->n; j++, dof += this->stride)
263  al->add_triplet(this->shapeset->get_constrained_edge_index(surf_num, j + 2, ori, part, e->get_mode()), dof, 1.0);
264  }
265  }
266 
267  template<typename Scalar>
268  Scalar* H1Space<Scalar>::get_bc_projection(SurfPos* surf_pos, int order, EssentialBoundaryCondition<Scalar> *bc)
269  {
270  assert(order >= 1);
271  Scalar* proj = new Scalar[order + 1];
272 
273  if(bc->get_value_type() == EssentialBoundaryCondition<Scalar>::BC_CONST)
274  {
275  proj[0] = proj[1] = bc->value_const;
276  } // If the BC is not constant.
277  else if(bc->get_value_type() == EssentialBoundaryCondition<Scalar>::BC_FUNCTION)
278  {
279  surf_pos->t = surf_pos->lo;
280  // Find out the (x, y) coordinates for the first endpoint.
281  double x, y, n_x, n_y, t_x, t_y;
282  Nurbs* nurbs = surf_pos->base->is_curved() ? surf_pos->base->cm->nurbs[surf_pos->surf_num] : NULL;
283  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);
284  // Calculate.
285  proj[0] = bc->value(x, y, n_x, n_y, t_x, t_y);
286  surf_pos->t = surf_pos->hi;
287  // Find out the (x, y) coordinates for the second endpoint.
288  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);
289  // Calculate.
290  proj[1] = bc->value(x, y, n_x, n_y, t_x, t_y);
291  }
292 
293  if(order-- > 1)
294  {
295  Quad1DStd quad1d;
296  Scalar* rhs = proj + 2;
297  int mo = quad1d.get_max_order();
298  double2* pt = quad1d.get_points(mo);
299 
300  // get boundary values at integration points, construct rhs
301  for (int i = 0; i < order; i++)
302  {
303  rhs[i] = 0.0;
304  int ii = this->shapeset->get_edge_index(0, 0, i + 2, surf_pos->base->get_mode());
305  for (int j = 0; j < quad1d.get_num_points(mo); j++)
306  {
307  double t = (pt[j][0] + 1) * 0.5, s = 1.0 - t;
308  Scalar l = proj[0] * s + proj[1] * t;
309  surf_pos->t = surf_pos->lo * s + surf_pos->hi * t;
310 
311  if(bc->get_value_type() == EssentialBoundaryCondition<Scalar>::BC_CONST)
312  rhs[i] += pt[j][1] * this->shapeset->get_fn_value(ii, pt[j][0], -1.0, 0, surf_pos->base->get_mode())
313  * (bc->value_const - l);
314  // If the BC is not constant.
315  else if(bc->get_value_type() == EssentialBoundaryCondition<Scalar>::BC_FUNCTION)
316  {
317  // Find out the (x, y) coordinate.
318  double x, y, n_x, n_y, t_x, t_y;
319  Nurbs* nurbs = surf_pos->base->is_curved() ? surf_pos->base->cm->nurbs[surf_pos->surf_num] : NULL;
320  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);
321  // Calculate.
322  rhs[i] += pt[j][1] * this->shapeset->get_fn_value(ii, pt[j][0], -1.0, 0, surf_pos->base->get_mode())
323  * (bc->value(x, y, n_x, n_y, t_x, t_y) - l);
324  }
325  }
326  }
327 
328  // solve the system using a precalculated Cholesky decomposed projection matrix
329  cholsl(this->proj_mat, order, this->chol_p, rhs, rhs);
330  }
331 
332  return proj;
333  }
334 
335  template<typename Scalar>
336  inline void H1Space<Scalar>::output_component(typename Space<Scalar>::BaseComponent*& current, typename Space<Scalar>::BaseComponent*& last, typename Space<Scalar>::BaseComponent* min,
337  Node*& edge, typename Space<Scalar>::BaseComponent*& edge_dofs)
338  {
339  // if the dof is already in the list, just add half of the other coef
340  if(last != NULL && last->dof == min->dof)
341  {
342  last->coef += min->coef * 0.5;
343  return;
344  }
345 
346  // leave space for edge node dofs if they belong in front of the current minimum dof
347  if(edge != NULL && this->ndata[edge->id].dof <= min->dof)
348  {
349  edge_dofs = current;
350 
351  // (reserve space only if the edge dofs are not in the list yet)
352  if(this->ndata[edge->id].dof != min->dof)
353  {
354  current += this->ndata[edge->id].n;
355  }
356  edge = NULL;
357  }
358 
359  // output new dof
360  current->dof = min->dof;
361  current->coef = min->coef * 0.5;
362  last = current++;
363  }
364 
365  template<typename Scalar>
366  typename Space<Scalar>::BaseComponent* H1Space<Scalar>::merge_baselists(typename Space<Scalar>::BaseComponent* l1, int n1, typename Space<Scalar>::BaseComponent* l2, int n2,
367  Node* edge, typename Space<Scalar>::BaseComponent*& edge_dofs, int& ncomponents)
368  {
369  // estimate the upper bound of the result size
370  int max_result = n1 + n2;
371  if(edge != NULL) max_result += this->ndata[edge->id].n;
372 
373  typename Space<Scalar>::BaseComponent* result = (typename Space<Scalar>::BaseComponent*) malloc(max_result * sizeof(typename Space<Scalar>::BaseComponent));
374  typename Space<Scalar>::BaseComponent* current = result;
375  typename Space<Scalar>::BaseComponent* last = NULL;
376 
377  // main loop - always output the component with smaller dof so that we get a sorted array
378  int i1 = 0, i2 = 0;
379  while (i1 < n1 && i2 < n2)
380  {
381  if(l1[i1].dof < l2[i2].dof)
382  output_component(current, last, l1 + i1++, edge, edge_dofs);
383  else
384  output_component(current, last, l2 + i2++, edge, edge_dofs);
385  }
386 
387  // finish the longer baselist
388  while (i1 < n1) output_component(current, last, l1 + i1++, edge, edge_dofs);
389  while (i2 < n2) output_component(current, last, l2 + i2++, edge, edge_dofs);
390 
391  // don't forget to reserve space for edge dofs if we haven't done that already
392  if(edge != NULL)
393  {
394  edge_dofs = current;
395  current += this->ndata[edge->id].n;
396  }
397 
398  // if we produced less components than we expected, reallocate the resulting array
399  // ...this should be OK as we are always shrinking the array so no copying should occur
400  ncomponents = current - result;
401  if(ncomponents < max_result)
402  {
403  typename Space<Scalar>::BaseComponent* reallocated_result = (typename Space<Scalar>::BaseComponent*) realloc(result, ncomponents * sizeof(typename Space<Scalar>::BaseComponent));
404  if(edge_dofs != NULL)
405  {
406  edge_dofs = reallocated_result + (edge_dofs - result);
407  }
408  return reallocated_result;
409  }
410  else
411  return result;
412  }
413 
414  template<typename Scalar>
415  void H1Space<Scalar>::update_constrained_nodes(Element* e, EdgeInfo* ei0, EdgeInfo* ei1, EdgeInfo* ei2, EdgeInfo* ei3)
416  {
417  int j, k;
418  EdgeInfo* ei[4] = { ei0, ei1, ei2, ei3 };
419  typename Space<Scalar>::NodeData* nd;
420 
421  if(this->get_element_order(e->id) == 0) return;
422 
423  // on non-refined elements all we have to do is update edge nodes lying on constrained edges
424  if(e->active)
425  {
426  for (unsigned int i = 0; i < e->get_nvert(); i++)
427  {
428  if(ei[i] != NULL)
429  {
430  nd = &this->ndata[e->en[i]->id];
431  nd->base = ei[i]->node;
432  nd->part = ei[i]->part;
433  if(ei[i]->ori) nd->part ^= ~0;
434  }
435  }
436  }
437  // the element has sons - update mid-edge constrained vertex nodes
438  else
439  {
440  // create new edge infos where we don't have them yet
441  EdgeInfo ei_data[4];
442  for (unsigned int i = 0; i < e->get_nvert(); i++)
443  {
444  if(ei[i] == NULL)
445  {
446  j = e->next_vert(i);
447  Node* mid_vn = this->get_mid_edge_vertex_node(e, i, j);
448  if(mid_vn != NULL && mid_vn->is_constrained_vertex())
449  {
450  Node* mid_en = this->mesh->peek_edge_node(e->vn[i]->id, e->vn[j]->id);
451  if(mid_en != NULL)
452  {
453  ei[i] = ei_data + i;
454  ei[i]->node = mid_en;
455  ei[i]->part = -1;
456  ei[i]->lo = -1.0;
457  ei[i]->hi = 1.0;
458  ei[i]->ori = (e->vn[i]->id < e->vn[j]->id) ? 0 : 1;
459  }
460  }
461  }
462  }
463 
464  // create a baselist for each mid-edge vertex node
465  for (unsigned int i = 0; i < e->get_nvert(); i++)
466  {
467  if(ei[i] == NULL) continue;
468  j = e->next_vert(i);
469 
470  Node* mid_vn = this->get_mid_edge_vertex_node(e, i, j);
471  if(mid_vn == NULL) continue;
472 
473  Node* vn[2] = { e->vn[i], e->vn[j] }; // endpoint vertex nodes
474  Node* en = ei[i]->node; // constraining edge node
475  typename Space<Scalar>::BaseComponent *bl[2], dummy_bl[2]; // base lists of v[0] and v[1]
476  int nc[2] = { 0, 0 }; // number of components of bl[0] and bl[1]
477 
478  // get baselists of vn[0] and vn[1] - pretend we have them even if they are unconstrained
479  for (k = 0; k < 2; k++)
480  {
481  nd = &this->ndata[vn[k]->id];
482  if(vn[k]->is_constrained_vertex())
483  {
484  bl[k] = nd->baselist;
485  nc[k] = nd->ncomponents;
486  }
487  else // make up an artificial baselist
488  {
489  dummy_bl[k].dof = nd->dof;
490  dummy_bl[k].coef = (nd->dof >= 0) ? 1.0 : *nd->vertex_bc_coef;
491  bl[k] = &dummy_bl[k];
492  nc[k] = 1;
493  }
494  }
495 
496  // merge the baselists
497  typename Space<Scalar>::BaseComponent* edge_dofs;
498  nd = &this->ndata[mid_vn->id];
499  nd->baselist = merge_baselists(bl[0], nc[0], bl[1], nc[1], en, edge_dofs, nd->ncomponents);
500  this->bc_data.push_back(nd->baselist);
501 
502  // set edge node coeffs to function values of the edge functions
503  double mid = (ei[i]->lo + ei[i]->hi) * 0.5;
504  nd = &this->ndata[en->id];
505  for (k = 0; k < nd->n; k++, edge_dofs++)
506  {
507  edge_dofs->dof = nd->dof + k*this->stride;
508  edge_dofs->coef = this->shapeset->get_fn_value(this->shapeset->get_edge_index(0, ei[i]->ori, k + 2, e->get_mode()), mid, -1.0, 0, e->get_mode());
509  }
510 
511  //dump_baselist(ndata[mid_vn->id]);
512  }
513 
514  // create edge infos for half-edges
515  EdgeInfo half_ei_data[4][2];
516  EdgeInfo* half_ei[4][2];
517  for (unsigned int i = 0; i < e->get_nvert(); i++)
518  {
519  if(ei[i] == NULL)
520  {
521  half_ei[i][0] = half_ei[i][1] = NULL;
522  }
523  else
524  {
525  EdgeInfo* h0 = half_ei[i][0] = half_ei_data[i];
526  EdgeInfo* h1 = half_ei[i][1] = half_ei_data[i] + 1;
527 
528  h0->node = h1->node = ei[i]->node;
529  h0->part = (ei[i]->part + 1) * 2;
530  h1->part = h0->part + 1;
531  h0->hi = h1->lo = (ei[i]->lo + ei[i]->hi) / 2;
532  h0->lo = ei[i]->lo;
533  h1->hi = ei[i]->hi;
534  h1->ori = h0->ori = ei[i]->ori;
535  }
536  }
537 
538  // recur to sons
539  if(e->is_triangle())
540  {
541  update_constrained_nodes(e->sons[0], half_ei[0][0], NULL, half_ei[2][1], NULL);
542  update_constrained_nodes(e->sons[1], half_ei[0][1], half_ei[1][0], NULL, NULL);
543  update_constrained_nodes(e->sons[2], NULL, half_ei[1][1], half_ei[2][0], NULL);
544  update_constrained_nodes(e->sons[3], NULL, NULL, NULL, NULL);
545  }
546  else if(e->sons[2] == NULL) // 'horizontally' split quad
547  {
548  update_constrained_nodes(e->sons[0], ei[0], half_ei[1][0], NULL, half_ei[3][1]);
549  update_constrained_nodes(e->sons[1], NULL, half_ei[1][1], ei[2], half_ei[3][0]);
550  }
551  else if(e->sons[0] == NULL) // 'vertically' split quad
552  {
553  update_constrained_nodes(e->sons[2], half_ei[0][0], NULL, half_ei[2][1], ei[3]);
554  update_constrained_nodes(e->sons[3], half_ei[0][1], ei[1], half_ei[2][0], NULL);
555  }
556  else // fully split quad
557  {
558  update_constrained_nodes(e->sons[0], half_ei[0][0], NULL, NULL, half_ei[3][1]);
559  update_constrained_nodes(e->sons[1], half_ei[0][1], half_ei[1][0], NULL, NULL);
560  update_constrained_nodes(e->sons[2], NULL, half_ei[1][1], half_ei[2][0], NULL);
561  update_constrained_nodes(e->sons[3], NULL, NULL, half_ei[2][1], half_ei[3][0]);
562  }
563  }
564  }
565 
566  template<typename Scalar>
568  {
569  Element* e;
570  for_all_base_elements(e, this->mesh)
571  update_constrained_nodes(e, NULL, NULL, NULL, NULL);
572  }
573 
574  template<typename Scalar>
575  void H1Space<Scalar>::fix_vertex(int id, Scalar value)
576  {
577  FixedVertex fv = { id, value };
578  fixed_vertices.push_back(fv);
579  }
580 
581  template<typename Scalar>
582  bool H1Space<Scalar>::is_fixed_vertex(int id) const
583  {
584  for (unsigned int i = 0; i < fixed_vertices.size(); i++)
585  if(fixed_vertices[i].id == id)
586  return true;
587 
588  return false;
589  }
590 
591  template<typename Scalar>
593  {
594  // process fixed vertices -- put their values into nd->vertex_bc_coef
595  for (unsigned int i = 0; i < fixed_vertices.size(); i++)
596  {
597  Scalar* fixv = new Scalar[1];
598  *fixv = fixed_vertices[i].value;
599  typename Space<Scalar>::NodeData* nd = &this->ndata[fixed_vertices[i].id];
600  nd->vertex_bc_coef = fixv;
601  this->bc_data.push_back(fixv);
602  }
603  }
604 
605  template HERMES_API class H1Space<double>;
606  template HERMES_API class H1Space<std::complex<double> >;
607  }
608 }