[P]arallel [Hi]gh-order [Li]brary for [P]DEs  Latest
Parallel High-Order Library for PDEs through hp-adaptive Discontinuous Galerkin methods
reconstruct_poly.cpp
1 #include <deal.II/hp/fe_collection.h>
2 #include <deal.II/hp/fe_values.h>
3 #include <deal.II/hp/mapping_collection.h>
4 #include <deal.II/hp/q_collection.h>
5 #include <deal.II/dofs/dof_handler.h>
6 #include <deal.II/lac/la_parallel_vector.h>
7 #include <deal.II/base/tensor.h>
8 #include <deal.II/base/polynomial.h>
9 #include <deal.II/base/polynomial_space.h>
10 #include <deal.II/grid/grid_tools.h>
11 
12 #include "reconstruct_poly.h"
13 #include "physics/manufactured_solution.h"
14 
15 namespace PHiLiP {
16 
17 namespace GridRefinement {
18 
19 template <int dim, int nstate, typename real>
21  const dealii::DoFHandler<dim>& dof_handler, // dof_handler
22  const dealii::hp::MappingCollection<dim>& mapping_collection, // mapping collection
23  const dealii::hp::FECollection<dim>& fe_collection, // fe collection
24  const dealii::hp::QCollection<dim>& quadrature_collection, // quadrature collection
25  const dealii::UpdateFlags& update_flags) : // update flags for for volume fe
26  dof_handler(dof_handler),
27  mapping_collection(mapping_collection),
28  fe_collection(fe_collection),
29  quadrature_collection(quadrature_collection),
30  update_flags(update_flags),
31  norm_type(NormType::H1)
32 {
33  reinit(dof_handler.get_triangulation().n_active_cells());
34 }
35 
36 template <int dim, int nstate, typename real>
38 {
39  this->norm_type = norm_type;
40 }
41 
42 template <int dim, int nstate, typename real>
43 void ReconstructPoly<dim,nstate,real>::reinit(const unsigned int n)
44 {
45  derivative_value.resize(n);
46  derivative_direction.resize(n);
47 }
48 
49 // reconstruct the directional derivatives of the reconstructed solution along each of the quad chords
50 template <int dim, int nstate, typename real>
52  const dealii::LinearAlgebra::distributed::Vector<real>& solution, // solution approximation to be reconstructed
53  const unsigned int rel_order) // order of the apporximation
54 {
55  /* based on the dealii numbering, chords defined along nth axis
56  ^ dir[1]
57  |
58  2---+---3
59  | | |
60  +---o---+---> dir[0]
61  | | |
62  0---+---1
63  */
64 
65  for(auto cell = dof_handler.begin_active(); cell != dof_handler.end(); ++cell){
66  if(!cell->is_locally_owned()) continue;
67 
68  // generating the polynomial space
69  unsigned int order = cell->active_fe_index()+rel_order;
70  dealii::PolynomialSpace<dim> poly_space(dealii::Polynomials::Monomial<double>::generate_complete_basis(order));
71 
72  // getting the vector of polynomial coefficients from the p+1 expansion
73  dealii::Vector<real> coeffs_non_hom = reconstruct_norm(
74  norm_type,
75  cell,
76  poly_space,
77  solution);
78 
79  const unsigned int n_poly = poly_space.n();
80  const unsigned int n_degree = poly_space.degree();
81 
82  // assembling a vector of coefficients and indices
83  std::vector<real> coeffs;
84  std::vector<std::array<unsigned int, dim>> indices;
85  unsigned int n_vec = 0;
86 
87  for(unsigned int i = 0; i < n_poly; ++i){
88  std::array<unsigned int, dim> arr = compute_index<dim>(i, n_degree);
89 
90  unsigned int sum = 0;
91  for(int j = 0; j < dim; ++j)
92  sum += arr[j];
93 
94  if(sum == order){
95  // based on expansion of taylor series additional term from expansion (x (+y) (+z))^n
96  // for cross terms, in 1D no such terms. But, after expanding the n^th derivative with
97  // i, j partials in (x,y) we get 1/n! * (n \choose i, j) * i! j! = 1 on the x^i y^j term
98  // (the only one that will be remaining). Also generalizes to n-dimensions.
99  coeffs.push_back(coeffs_non_hom[i]);
100  indices.push_back(arr);
101  n_vec++;
102  }
103  }
104 
105  std::array<real,dim> A_cell;
106  std::array<dealii::Tensor<1,dim,real>,dim> chord_vec;
107 
108  // holds the nodes that form the chord
109  // summing over all the nodes onto each (dim) neighbouring faces/edges
110  std::array<std::pair<dealii::Tensor<1,dim,real>, dealii::Tensor<1,dim,real>>,dim> chord_nodes;
111  for(unsigned int vertex = 0; vertex < dealii::GeometryInfo<dim>::vertices_per_cell; ++vertex){
112 
113  // logic decides what side of each axis vertex is on
114  for(unsigned int i = 0; i < dim; ++i){
115 
116  // chord side equivalent to sign of i^th bit in binary
117  // uses bit-shift and remainder to determine if this bit is 0/1
118  /* example for 2D (see figure above):
119  vertex b0 b1
120  0 0 0
121  1 1 0
122  2 0 1
123  3 1 1
124  */
125  if(vertex>>i % 2 == 0){
126  chord_nodes[i].first += cell->vertex(vertex);
127  }else{
128  chord_nodes[i].second += cell->vertex(vertex);
129  }
130 
131  }
132 
133  }
134 
135  // computing the direction, the chords could also be divided by 2^{i-1} first to get the actual physical coordinates
136  for(unsigned int i = 0; i < dim; ++i)
137  chord_vec[i] = chord_nodes[i].second - chord_nodes[i].first;
138 
139  // normalizing
140  for(unsigned int i = 0; i < dim; ++i)
141  chord_vec[i] /= chord_vec[i].norm();
142 
143  // computing the directional derivative along each vector
144  for(unsigned int i = 0; i < dim; ++i){ // loop over the axes
145  A_cell[i] = 0;
146  for(unsigned int n = 0; n < n_vec; ++n){ // loop over the polynomials
147  real poly_val = coeffs[n];
148 
149  for(unsigned int d = 0; d < dim; ++d) // loop over each poly term, ie x^i y^j z^k
150  poly_val *= pow(chord_vec[i][d], indices[n][d]);
151 
152  // adding polynomial terms contribution to the axis
153  A_cell[i] += poly_val;
154  }
155  }
156 
157  const unsigned int index = cell->active_cell_index();
158  derivative_value[index] = A_cell;
159  derivative_direction[index] = chord_vec;
160  }
161 
162 }
163 
164 // takes an input field and polynomial space and output the largest directional derivative and coresponding normal direction
165 template <int dim, int nstate, typename real>
167  const dealii::LinearAlgebra::distributed::Vector<real>& solution, // solution approximation to be reconstructed
168  const unsigned int rel_order) // order of the apporximation
169 {
170  const real pi = atan(1)*4.0;
171 
172  for(auto cell = dof_handler.begin_active(); cell != dof_handler.end(); ++cell){
173  if(!cell->is_locally_owned()) continue;
174 
175  // generating the polynomial space
176  unsigned int order = cell->active_fe_index()+rel_order;
177  dealii::PolynomialSpace<dim> poly_space(dealii::Polynomials::Monomial<double>::generate_complete_basis(order));
178 
179  // getting the vector of polynomial coefficients from the p+1 expansion
180  dealii::Vector<real> coeffs_non_hom = reconstruct_norm(
181  norm_type,
182  cell,
183  poly_space,
184  solution);
185 
186  const unsigned int n_poly = poly_space.n();
187  const unsigned int n_degree = poly_space.degree();
188 
189  // assembling a vector of coefficients and indices
190  std::vector<real> coeffs;
191  std::vector<std::array<unsigned int, dim>> indices;
192  unsigned int n_vec = 0;
193 
194  for(unsigned int i = 0; i < n_poly; ++i){
195  std::array<unsigned int, dim> arr = compute_index<dim>(i, n_degree);
196 
197  unsigned int sum = 0;
198  for(int j = 0; j < dim; ++j)
199  sum += arr[j];
200 
201  if(sum == order){
202  // based on expansion of taylor series additional term from expansion (x (+y) (+z))^n
203  // for cross terms, in 1D no such terms. But, after expanding the n^th derivative with
204  // i, j partials in (x,y) we get 1/n! * (n \choose i, j) * i! j! = 1 on the x^i y^j term
205  // (the only one that will be remaining). Also generalizes to n-dimensions.
206  coeffs.push_back(coeffs_non_hom[i]);
207  indices.push_back(arr);
208  n_vec++;
209  }
210  }
211 
212  std::array<real,dim> value_cell;
213  std::array<dealii::Tensor<1,dim,real>,dim> direction_cell;
214  if(dim == 1){
215 
216  Assert(n_vec == 1, dealii::ExcInternalError());
217 
218  value_cell[0] = coeffs[0];
219  direction_cell[0][0] = 1.0;
220 
221  }else if(order == 2){
222 
223  // if current order is 2, can be solved by the eigenvalue problem
224  Assert(n_vec == dim*(dim+1)/2, dealii::ExcInternalError());
225 
226  dealii::SymmetricTensor<2,dim,real> hessian;
227  // looping over each term of the homogenous polynomial
228  for(unsigned int n = 0; n < n_vec; ++n){
229  // comparing the indices values at each dimension pair
230  // only thing assumed is indices[n] sums to 2 (order)
231  for(unsigned int i = 0; i < dim; ++i){
232 
233  // case 1: a*xi^2 (diagonal)
234  if((indices[n][i] == 2)){
235  hessian[i][i] = coeffs[n];
236  }
237 
238  // case 2: a*xi*yi (off diagonal)
239  for(unsigned int j = i+1; j < dim; ++j){
240  if((indices[n][i] == 1) && (indices[n][j] == 1)){
241  hessian[i][j] = 0.5 * coeffs[n];
242  }
243  }
244 
245  }
246 
247  }
248 
249  // // debugging for dim = 2
250  // for(unsigned int i = 0; i < n_vec; ++i)
251  // std::cout << "n_vec[" << i << "] = " << coeffs[i] << " * x^"<< indices[i][0] << " * y^" << indices[i][1] << std::endl;
252  // std::cout << "Hessian = [" << hessian[0][0] << ", " << hessian[0][1] << "]" << std::endl;
253  // std::cout << " [" << hessian[1][0] << ", " << hessian[1][1] << "]" << std::endl << std::endl;
254 
255  // https://www.dealii.org/current/doxygen/deal.II/symmetric__tensor_8h.html#aa18a9d623fcd520f022421fd1d6c7a14
256  using eigenpair = std::pair<real,dealii::Tensor<1,dim,real>>;
257  std::array<eigenpair,dim> eig = dealii::eigenvectors(hessian);
258 
259  // resorting the list based on the absolute value of the eigenvalue
260  std::sort(eig.begin(), eig.end(), [](
261  const eigenpair left,
262  const eigenpair right)
263  {
264  return abs(left.first) > abs(right.first);
265  });
266 
267  // storing the values
268  for(int d = 0; d < dim; ++d){
269  value_cell[d] = abs(eig[d].first);
270  direction_cell[d] = eig[d].second;
271  }
272 
273  }else{
274 
275  // evaluating any point requires sum over power of the multindices
276  auto eval = [&](const dealii::Tensor<1,dim,real>& point) -> real{
277  real val = 0.0;
278  for(unsigned int i = 0; i < n_vec; ++i){
279  real val_coeff = coeffs[i];
280  for(int d = 0; d < dim; ++d)
281  val_coeff *= pow(point[d], indices[i][d]);
282  val += val_coeff;
283  }
284  return val;
285  };
286 
287  // looping over the range
288  if(dim == 2){
289 
290  // number of sampling points in each direciton
291  const unsigned int n_sample = 180;
292 
293  // keeping track of largest point and angle
294  real A_max = 0.0, t_max = 0.0;
295 
296  // using polar coordinates theta\in[0, \pi)
297  real r = 1.0, theta, val;
298  dealii::Tensor<1,dim,real> p_sample;
299  for(unsigned int i = 0; i < n_sample; ++i){
300  theta = i*pi/n_sample;
301 
302  p_sample[0] = r*cos(theta);
303  p_sample[1] = r*sin(theta);
304 
305  val = abs(eval(p_sample));
306  if(val > A_max){
307  A_max = val;
308  t_max = theta;
309  }
310  }
311 
312  dealii::Tensor<1,dim,real> p_1;
313  p_1[0] = r*cos(t_max);
314  p_1[1] = r*sin(t_max);
315 
316  // Taking A_2 to be at an angle of 90 degrees relative to first
317  dealii::Tensor<1,dim,real> p_2;
318  p_2[0] = r*cos(t_max+pi/2.0);
319  p_2[1] = r*sin(t_max+pi/2.0);
320 
321  value_cell[0] = A_max;
322  value_cell[1] = abs(eval(p_2));
323 
324  direction_cell[0] = p_1;
325  direction_cell[1] = p_2;
326 
327  }else if(dim == 3){
328 
329  // using fibbonaci sphere algorithm, with ~ n^2/2 points compared to 2d for equal points in theta and phi as before
330  // https://stackoverflow.com/questions/9600801/evenly-distributing-n-points-on-a-sphere/26127012#26127012
331  const unsigned int n_sample = 180, n_sample_3d = 180*90;
332 
333  // keeping track of the largest point and angles
334  real A_1 = 0.0;
335  dealii::Tensor<1,dim,real> p_1;
336 
337  // parameters needed
338  real offset = 1.0/n_sample_3d;
339  real increment = pi * (3 - sqrt(5));
340 
341  // spherical coordinates
342  real y, r, phi, val;
343  dealii::Tensor<1,dim,real> p_sample;
344  for(unsigned int i = 0; i < n_sample_3d; ++i){
345  // calculation of the points
346  y = (i*offset) - 1 + offset/2;
347  r = sqrt(1-pow(y,2));
348 
349  phi = remainder(i, 2*n_sample_3d) * increment;
350 
351  p_sample[0] = r*cos(phi);
352  p_sample[1] = y;
353  p_sample[2] = r*sin(phi);
354 
355  val = abs(eval(p_sample));
356  if(val > A_1){
357  A_1 = val;
358  p_1 = p_sample/p_sample.norm();
359  }
360  }
361 
362  // generating the rest of the basis for p_1 rotation, two orthogonal vectors forming a plane
363  dealii::Tensor<1,dim,real> u;
364  dealii::Tensor<1,dim,real> v;
365 
366  // checking if near the x-axis
367  dealii::Tensor<1,dim,real> px;
368  px[0] = 1.0;
369  if(abs(px*p_1) < 1.0/sqrt(2.0)){ // if further apart than 45 degrees use x-axis
370  // using cross products to generate two vectors orthogonal to p_1
371  u = dealii::cross_product_3d(p_1, px);
372  }else{ // if not, use the y axis instead
373  dealii::Tensor<1,dim,real> py;
374  py[1] = 1.0;
375  u = dealii::cross_product_3d(p_1, py);
376  }
377  // second orthogonal to form the basis
378  v = dealii::cross_product_3d(p_1, u);
379 
380  // normalizing
381  u = u / u.norm();
382  v = v / v.norm();
383 
384  // now performing the 2d analysis in the plane uv
385  real A_2 = 0.0, t_2 = 0.0;
386 
387  // using polar coordinates theta\in[0, \pi)
388  real theta;
389  dealii::Tensor<1,dim,real> p_2;
390  for(unsigned int i = 0; i < n_sample; ++i){
391  theta = i*pi/n_sample;
392  p_2 = cos(theta)*u + sin(theta)*v;
393 
394  val = abs(eval(p_2));
395  if(val > A_2){
396  A_2 = val;
397  t_2 = theta;
398  }
399  }
400 
401  // reassinging the largest value to p_2
402  p_2 = cos(t_2)*u + sin(t_2)*v;
403 
404  // Taking A_2 to be at an angle of 90 degrees relative to first
405  dealii::Tensor<1,dim,real> p_3 = cos(t_2+pi/2.0)*u + sin(t_2+pi/2.0)*v;
406 
407  // assigning the results
408  value_cell[0] = A_1;
409  value_cell[1] = A_2;
410  value_cell[2] = abs(eval(p_3));
411 
412  direction_cell[0] = p_1;
413  direction_cell[1] = p_2;
414  direction_cell[2] = p_3;
415 
416  }else{
417 
418  // no other dimensions should appear
419  Assert(false, dealii::ExcInternalError());
420 
421  }
422  }
423 
424  // storing the tensor of results
425  const unsigned int index = cell->active_cell_index();
426  derivative_value[index] = value_cell;
427  derivative_direction[index] = direction_cell;
428  }
429 }
430 
431 template <int dim, int nstate, typename real>
433  const std::shared_ptr<ManufacturedSolutionFunction<dim,real>>& manufactured_solution,
434  const unsigned int rel_order)
435 {
436  for(auto cell = dof_handler.begin_active(); cell != dof_handler.end(); ++cell){
437  if(!cell->is_locally_owned()) continue;
438 
439  unsigned int order = cell->active_fe_index() + rel_order;
440  assert(order == 2); // hessian based only
441  (void) order;
442 
443  // evaluating the hessian from the manufactured solution
444  dealii::Point<dim,real> center_point = cell->center();
445  dealii::SymmetricTensor<2,dim,real> hessian =
446  manufactured_solution->hessian(center_point);
447 
448  // performing eigenvalue decomposition
449  using eigenpair = std::pair<real,dealii::Tensor<1,dim,real>>;
450  std::array<eigenpair,dim> eig = dealii::eigenvectors(hessian);
451 
452  std::sort(eig.begin(), eig.end(), [](
453  const eigenpair left,
454  const eigenpair right)
455  {
456  return abs(left.first) > abs(right.first);
457  });
458 
459  // storing the values for the cell
460  const unsigned int index = cell->active_cell_index();
461  for(int d = 0; d < dim; ++d){
462  derivative_value[index][d] = abs(eig[d].first);
463  derivative_direction[index][d] = eig[d].second;
464  }
465 
466  }
467 }
468 
469 // from DEALII, https://www.dealii.org/current/doxygen/deal.II/polynomial__space_8cc_source.html
470 // protected function so reimplementing slightly modified form here
471 // computes the multiindex for different dimensions, assuming the index map hasn't been modified
472 template <>
473 std::array<unsigned int, 1> compute_index<1>(
474  const unsigned int i,
475  const unsigned int /*size*/)
476 {
477  return {{i}};
478 }
479 
480 template <>
481 std::array<unsigned int, 2> compute_index<2>(
482  const unsigned int i,
483  const unsigned int size)
484 {
485  unsigned int k = 0;
486  for(unsigned int iy = 0; iy < size; ++iy)
487  if(i < k+size-iy){
488  return {{i-k, iy}};
489  }else{
490  k += size - iy;
491  }
492 
493  Assert(false, dealii::ExcInternalError());
494  return {{dealii::numbers::invalid_unsigned_int, dealii::numbers::invalid_unsigned_int}};
495 }
496 
497 template <>
498 std::array<unsigned int, 3> compute_index<3>(
499  const unsigned int i,
500  const unsigned int size)
501 {
502  unsigned int k = 0;
503  for(unsigned int iz = 0; iz < size; ++iz)
504  for(unsigned int iy = 0; iy < size - iz; ++iy)
505  if(i < k+size-iy){
506  return {{i-k, iy, iz}};
507  }else{
508  k += size - iy - iz;
509  }
510 
511  Assert(false, dealii::ExcInternalError());
512  return {{dealii::numbers::invalid_unsigned_int, dealii::numbers::invalid_unsigned_int, dealii::numbers::invalid_unsigned_int}};
513 }
514 
515 template <int dim, int nstate, typename real>
516 template <typename DoFCellAccessorType>
518  const NormType norm_type,
519  const DoFCellAccessorType & curr_cell,
520  const dealii::PolynomialSpace<dim> ps,
521  const dealii::LinearAlgebra::distributed::Vector<real> &solution)
522 {
523 
524  if(norm_type == NormType::H1){
525 
526  return reconstruct_H1_norm(
527  curr_cell,
528  ps,
529  solution);
530 
531  }else if(norm_type == NormType::L2){
532 
533  return reconstruct_L2_norm(
534  curr_cell,
535  ps,
536  solution);
537 
538  }else{
539 
540  // undefined
541  assert(0);
542  return dealii::Vector<real>(0);
543 
544  }
545 
546 }
547 
548 template <int dim, int nstate, typename real>
549 template <typename DoFCellAccessorType>
551  const DoFCellAccessorType & curr_cell,
552  const dealii::PolynomialSpace<dim> ps,
553  const dealii::LinearAlgebra::distributed::Vector<real> &solution)
554 {
555 
556  // center point of the current cell
557  dealii::Point<dim,real> center_point = curr_cell->center();
558 
559  // things to be extracted
560  std::vector<std::array<real,nstate>> soln_at_q_vec;
561  std::vector<std::array<dealii::Tensor<1,dim,real>,nstate>> grad_at_q_vec; // for the H^1 norm
562  std::vector<dealii::Point<dim,real>> qpoint_vec;
563  std::vector<real> JxW_vec;
564 
565  // and keeping track of vector lengths
566  unsigned int n_vec = 0;
567 
568  // fe_values
569  dealii::hp::FEValues<dim,dim> fe_values_collection(
573  update_flags);
574 
575  // looping over the cell vector and extracting the soln, qpoint and JxW
576  // std::vector<DoFCellAccessorType> cell_patch = dealii::GridTools::get_patch_around_cell(curr_cell);
577  std::vector<DoFCellAccessorType> cell_patch = get_patch_around_dof_cell(curr_cell);
578  for(auto cell : cell_patch){
579  const unsigned int mapping_index = 0;
580  const unsigned int fe_index = cell->active_fe_index();
581  const unsigned int quad_index = fe_index;
582 
583  const unsigned int n_dofs = fe_collection[fe_index].n_dofs_per_cell();
584  const unsigned int n_quad = quadrature_collection[quad_index].size();
585 
586  fe_values_collection.reinit(cell, quad_index, mapping_index, fe_index);
587  const dealii::FEValues<dim,dim> &fe_values = fe_values_collection.get_present_fe_values();
588 
589  std::vector<dealii::types::global_dof_index> dofs_indices(fe_values.dofs_per_cell);
590  cell->get_dof_indices(dofs_indices);
591 
592  // looping over the quadrature points of this cell
593  std::array<real,nstate> soln_at_q;
594  std::array<dealii::Tensor<1,dim,real>,nstate> grad_at_q;
595  for(unsigned int iquad = 0; iquad < n_quad; ++iquad){
596  soln_at_q.fill(0.0);
597  for(unsigned int istate = 0; istate < nstate; ++istate)
598  grad_at_q[istate] = 0.0;
599 
600  // looping over the DoFS to get the solution value
601  for(unsigned int idof = 0; idof < n_dofs; ++idof){
602  const unsigned int istate = fe_values.get_fe().system_to_component_index(idof).first;
603  soln_at_q[istate] += solution[dofs_indices[idof]] * fe_values.shape_value_component(idof, iquad, istate);
604  grad_at_q[istate] += solution[dofs_indices[idof]] * fe_values.shape_grad_component(idof, iquad, istate);
605  }
606 
607  // moving the reference point to the center of the curr_cell
608  dealii::Tensor<1,dim,real> tensor_q = fe_values.quadrature_point(iquad) - center_point;
609  dealii::Point<dim,real> point_q(tensor_q);
610 
611  // push back into the vectors
612  soln_at_q_vec.push_back(soln_at_q);
613  grad_at_q_vec.push_back(grad_at_q);
614  qpoint_vec.push_back(point_q);
615  JxW_vec.push_back(fe_values.JxW(iquad));
616  n_vec++;
617  }
618  }
619 
620  // number of polynomials in the space
621  const unsigned int n_poly = ps.n();
622 
623  // allocating the matrix and vector for the RHS
624  dealii::FullMatrix<real> mat(n_poly);
625  dealii::Vector<real> rhs(n_poly);
626 
627  // looping over to assemble the matrices
628  mat = 0.0;
629  for(unsigned int i_poly = 0; i_poly < n_poly; ++i_poly){
630  for(unsigned int j_poly = 0; j_poly < n_poly; ++j_poly){
631  // taking the inner product between \psi_i and \psi_j
632  // <u,v>_{H^1(\Omega)} = \int_{\Omega} u*v + \sum_i^N {\partial_i u * \partial_i v} dx
633  for(unsigned int i_vec = 0; i_vec < n_vec; ++i_vec)
634  mat.add(i_poly, j_poly,
635  ((ps.compute_value(i_poly, qpoint_vec[i_vec]) * ps.compute_value(j_poly, qpoint_vec[i_vec]))
636  +(ps.compute_grad(i_poly, qpoint_vec[i_vec]) * ps.compute_grad(j_poly, qpoint_vec[i_vec])))
637  *JxW_vec[i_vec]);
638  }
639 
640  // take inner product of \psi_i and u (solution)
641  // if multiple states, taking the 2 norm of the different states
642  dealii::Vector<real> rhs_poly(nstate);
643  for(unsigned int istate = 0; istate < nstate; ++istate)
644  for(unsigned int i_vec = 0; i_vec < n_vec; ++i_vec)
645  rhs[i_poly] += ((ps.compute_value(i_poly, qpoint_vec[i_vec]) * soln_at_q_vec[i_vec][istate])
646  +(ps.compute_grad(i_poly, qpoint_vec[i_vec]) * grad_at_q_vec[i_vec][istate]))
647  *JxW_vec[i_vec];
648  }
649 
650  // solving the system
651  dealii::Vector<real> coeffs(n_poly);
652 
653  mat.gauss_jordan();
654  mat.vmult(coeffs, rhs);
655 
656  return coeffs;
657 }
658 
659 template <int dim, int nstate, typename real>
660 template <typename DoFCellAccessorType>
662  const DoFCellAccessorType & curr_cell,
663  const dealii::PolynomialSpace<dim> ps,
664  const dealii::LinearAlgebra::distributed::Vector<real> &solution)
665 {
666  // center point of the current cell
667  dealii::Point<dim,real> center_point = curr_cell->center();
668 
669  // things to be extracted
670  std::vector<std::array<real,nstate>> soln_at_q_vec;
671  std::vector<dealii::Point<dim,real>> qpoint_vec;
672  std::vector<real> JxW_vec;
673 
674  // and keeping track of vector lengths
675  unsigned int n_vec = 0;
676 
677  // fe_values
678  dealii::hp::FEValues<dim,dim> fe_values_collection(
682  update_flags);
683 
684  // looping over the cell vector and extracting the soln, qpoint and JxW
685  // std::vector<DoFCellAccessorType> cell_patch = dealii::GridTools::get_patch_around_cell(curr_cell);
686  std::vector<DoFCellAccessorType> cell_patch = get_patch_around_dof_cell(curr_cell);
687  for(auto cell : cell_patch){
688  const unsigned int mapping_index = 0;
689  const unsigned int fe_index = cell->active_fe_index();
690  const unsigned int quad_index = fe_index;
691 
692  const unsigned int n_dofs = fe_collection[fe_index].n_dofs_per_cell();
693  const unsigned int n_quad = quadrature_collection[quad_index].size();
694 
695  fe_values_collection.reinit(cell, quad_index, mapping_index, fe_index);
696  const dealii::FEValues<dim,dim> &fe_values = fe_values_collection.get_present_fe_values();
697 
698  std::vector<dealii::types::global_dof_index> dofs_indices(fe_values.dofs_per_cell);
699  cell->get_dof_indices(dofs_indices);
700 
701  // looping over the quadrature points of this cell
702  std::array<real,nstate> soln_at_q;
703  for(unsigned int iquad = 0; iquad < n_quad; ++iquad){
704  soln_at_q.fill(0.0);
705 
706  // looping over the DoFS to get the solution value
707  for(unsigned int idof = 0; idof < n_dofs; ++idof){
708  const unsigned int istate = fe_values.get_fe().system_to_component_index(idof).first;
709  soln_at_q[istate] += solution[dofs_indices[idof]] * fe_values.shape_value_component(idof, iquad, istate);
710  }
711 
712  // moving the reference point to the center of the curr_cell
713  dealii::Tensor<1,dim,real> tensor_q = fe_values.quadrature_point(iquad) - center_point;
714  dealii::Point<dim,real> point_q(tensor_q);
715 
716  // push back into the vectors
717  soln_at_q_vec.push_back(soln_at_q);
718  qpoint_vec.push_back(point_q);
719  JxW_vec.push_back(fe_values.JxW(iquad));
720  n_vec++;
721  }
722  }
723 
724  // number of polynomials in the space
725  const unsigned int n_poly = ps.n();
726 
727  // allocating the matrix and vector for the RHS
728  dealii::FullMatrix<real> mat(n_poly);
729  dealii::Vector<real> rhs(n_poly);
730 
731  // looping over to assemble the matrices
732  mat = 0.0;
733  for(unsigned int i_poly = 0; i_poly < n_poly; ++i_poly){
734  for(unsigned int j_poly = 0; j_poly < n_poly; ++j_poly){
735  // taking the inner product between \psi_i and \psi_j
736  // <u,v>_{L^2(\Omega)} = \int_{\Omega} u*v dx
737  for(unsigned int i_vec = 0; i_vec < n_vec; ++i_vec)
738  mat.add(i_poly, j_poly,
739  (ps.compute_value(i_poly, qpoint_vec[i_vec]) * ps.compute_value(j_poly, qpoint_vec[i_vec]))
740  *JxW_vec[i_vec]);
741  }
742 
743  // take inner product of \psi_i and u (solution)
744  // if multiple states, taking the 2 norm of the different states
745  dealii::Vector<real> rhs_poly(nstate);
746  for(unsigned int istate = 0; istate < nstate; ++istate)
747  for(unsigned int i_vec = 0; i_vec < n_vec; ++i_vec)
748  rhs[i_poly] += (ps.compute_value(i_poly, qpoint_vec[i_vec]) * soln_at_q_vec[i_vec][istate])
749  *JxW_vec[i_vec];
750  }
751 
752  // solving the system
753  dealii::Vector<real> coeffs(n_poly);
754 
755  mat.gauss_jordan();
756  mat.vmult(coeffs, rhs);
757 
758  return coeffs;
759 }
760 
761 // based on DEALII GridTools::get_patch_around_cell
762 // https://www.dealii.org/current/doxygen/deal.II/grid__tools__dof__handlers_8cc_source.html#l01411
763 // modified to work directly on the dof_handler accesor for hp-access rather than casting back and forth
764 template <int dim, int nstate, typename real>
765 template <typename DoFCellAccessorType>
767  const DoFCellAccessorType &cell)
768 {
769  Assert(cell->is_locally_owned(), dealii::ExcInternalError());
770 
771  std::vector<DoFCellAccessorType> patch;
772  patch.push_back(cell);
773 
774  for(unsigned int iface = 0; iface < dealii::GeometryInfo<dim>::faces_per_cell; ++iface){
775  if(cell->face(iface)->at_boundary()) continue;
776 
777  Assert(cell->neighbor(iface).state() == dealii::IteratorState::valid,
778  dealii::ExcInternalError());
779 
780  if(cell->neighbor(iface)->has_children() == false){ // case 1: coarse cell
781  patch.push_back(cell->neighbor(iface));
782  }else{ // has children cells
783  if(dim > 1){ // case 2: (2d/3d) get subface cells
784  for(unsigned int subface = 0; subface < cell->face(iface)->n_children(); ++subface)
785  patch.push_back(cell->neighbor_child_on_subface(iface, subface));
786  }else{ // case 3: (1d) iterate over children to find one on boundary
787  DoFCellAccessorType neighbor = cell->neighbor(iface);
788 
789  // looping over the children
790  while(neighbor->has_children())
791  neighbor = neighbor->child(1-iface);
792 
793  Assert(neighbor->neighbor(1-iface) == cell, dealii::ExcInternalError());
794  patch.push_back(neighbor);
795  }
796  }
797  }
798 
799  return patch;
800 }
801 
802 template <int dim, int nstate, typename real>
804  const unsigned int index)
805 {
806  dealii::Vector<real> vec(derivative_value.size());
807 
808  for(unsigned int i = 0; i < derivative_value.size(); i++){
809  vec[i] = derivative_value[i][index];
810  }
811 
812  return vec;
813 }
814 
820 
821 } // namespace GridRefinement
822 
823 } // namespace PHiLiP
void reconstruct_chord_derivative(const dealii::LinearAlgebra::distributed::Vector< real > &solution, const unsigned int rel_order)
Construct directional derivatives along the chords of the cell.
std::vector< std::array< dealii::Tensor< 1, dim, real >, dim > > derivative_direction
Derivative directions.
Polynomial Reconstruction Class.
Manufactured solution used for grid studies to check convergence orders.
dealii::Vector< real > reconstruct_norm(const NormType norm_type, const DoFCellAccessorType &curr_cell, const dealii::PolynomialSpace< dim > ps, const dealii::LinearAlgebra::distributed::Vector< real > &solution)
Performs polynomial patchwise reconstruction on the current cell in the selected norm.
const dealii::UpdateFlags & update_flags
Update flags used in obtaining local cell representation.
const dealii::hp::MappingCollection< dim > & mapping_collection
Collection of mapping rules for reference element conversion.
Files for the baseline physics.
Definition: ADTypes.hpp:10
void reconstruct_directional_derivative(const dealii::LinearAlgebra::distributed::Vector< real > &solution, const unsigned int rel_order)
Construct the set of largest perpendicular directional derivatives.
void reinit(const unsigned int n)
Reinitialze the internal vectors.
std::vector< std::array< real, dim > > derivative_value
Derivative values.
const dealii::hp::FECollection< dim > & fe_collection
Collection of Finite elements to represent discontinuous solution space.
ReconstructPoly(const dealii::DoFHandler< dim > &dof_handler, const dealii::hp::MappingCollection< dim > &mapping_collection, const dealii::hp::FECollection< dim > &fe_collection, const dealii::hp::QCollection< dim > &quadrature_collection, const dealii::UpdateFlags &update_flags)
Constructor. Stores required information about the mesh and quadrature rules.
void set_norm_type(const NormType norm_type)
Select the Norm to be used in reconstruction.
NormType norm_type
Setting controls the choice of norm used in reconstruction. Set via set_norm_type.
dealii::Vector< real > get_derivative_value_vector_dealii(const unsigned int index)
Gets the i^th largest componet of the directional derivative vector as a dealii::Vector.
const dealii::DoFHandler< dim > & dof_handler
Degree of freedom handler for iteration over mesh elements and their nodes.
dealii::Vector< real > reconstruct_H1_norm(const DoFCellAccessorType &curr_cell, const dealii::PolynomialSpace< dim > ps, const dealii::LinearAlgebra::distributed::Vector< real > &solution)
Performs polynomial patchwise reconstruction on the current cell in the H1 semi-norm.
const dealii::hp::QCollection< dim > & quadrature_collection
Collection of quadrature rules used to evaluate volume integrals.
std::vector< DoFCellAccessorType > get_patch_around_dof_cell(const DoFCellAccessorType &cell)
Get the patch of cells surrounding the current cell of DofCellAccessorType.
void reconstruct_manufactured_derivative(const std::shared_ptr< ManufacturedSolutionFunction< dim, real >> &manufactured_solution, const unsigned int rel_order)
Constructs directional derivates based on the manufactured solution hessian.
dealii::Vector< real > reconstruct_L2_norm(const DoFCellAccessorType &curr_cell, const dealii::PolynomialSpace< dim > ps, const dealii::LinearAlgebra::distributed::Vector< real > &solution)
Performs polynomial patchwise reconstruction on the current cell in the L2 norm.
real1 norm(const dealii::Tensor< 1, dim, real1 > x)
Returns norm of dealii::Tensor<1,dim,real>