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