PRAgMaTIc  master
MetricField.h
Go to the documentation of this file.
1 /* Copyright (C) 2010 Imperial College London and others.
2  *
3  * Please see the AUTHORS file in the main source directory for a
4  * full list of copyright holders.
5  *
6  * Gerard Gorman
7  * Applied Modelling and Computation Group
8  * Department of Earth Science and Engineering
9  * Imperial College London
10  *
11  * g.gorman@imperial.ac.uk
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  * notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above
19  * copyright notice, this list of conditions and the following
20  * disclaimer in the documentation and/or other materials provided
21  * with the distribution.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
24  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
25  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
28  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
30  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
32  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
33  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
34  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37 
38 #ifndef METRICFIELD_H
39 #define METRICFIELD_H
40 
41 #include <cmath>
42 #include <iostream>
43 #include <map>
44 #include <set>
45 #include <vector>
46 #include <cfloat>
47 
48 #include <Eigen/Core>
49 #include <Eigen/Dense>
50 
51 #include "MetricTensor.h"
52 #include "Mesh.h"
53 #include "ElementProperty.h"
54 
56 
57 #ifdef HAVE_MPI
58 #include <mpi.h>
59 #endif
60 
61 #ifdef _OPENMP
62 #include <omp.h>
63 #endif
64 
73 template<typename real_t, int dim> class MetricField{
74 public:
78  _NNodes = mesh.get_number_nodes();
79  _NElements = mesh.get_number_elements();
80  _mesh = &mesh;
81  _metric = NULL;
82 
83  rank = 0;
84  nprocs = 1;
85 #ifdef HAVE_MPI
86  MPI_Comm_size(_mesh->get_mpi_comm(), &nprocs);
87  MPI_Comm_rank(_mesh->get_mpi_comm(), &rank);
88 #endif
89 
90  double bbox[dim*2];
91  for(int i=0;i<dim;i++){
92  bbox[i*2] = DBL_MAX;
93  bbox[i*2+1] = -DBL_MAX;
94  }
95 #pragma omp parallel
96  {
97  double lbbox[dim*2];
98  for(int i=0;i<dim;i++){
99  lbbox[i*2] = DBL_MAX;
100  lbbox[i*2+1] = -DBL_MAX;
101  }
102 #pragma omp for schedule(static)
103  for(int i=0; i<_NNodes; i++){
104  const real_t *x = _mesh->get_coords(i);
105 
106  for(int j=0;j<dim;j++){
107  lbbox[j*2] = std::min(lbbox[j*2], x[j]);
108  lbbox[j*2+1] = std::max(lbbox[j*2+1], x[j]);
109  }
110  }
111 
112 #pragma omp critical
113  {
114  for(int j=0;j<dim;j++){
115  bbox[j*2] = std::min(lbbox[j*2], bbox[j*2]);
116  bbox[j*2+1] = std::max(lbbox[j*2+1], bbox[j*2+1]);
117  }
118  }
119  }
120  double max_extent = bbox[1]-bbox[0];
121  for(int j=1;j<dim;j++)
122  max_extent = std::max(max_extent, bbox[j*2+1]-bbox[j*2]);
123 
124  min_eigenvalue = 1.0/pow(max_extent, 2);
125  }
126 
130  if(_metric!=NULL)
131  delete [] _metric;
132  }
133 
134  void generate_mesh_metric(double resolution_scaling_factor){
135  if(_metric==NULL)
136  _metric = new MetricTensor<real_t,dim>[_NNodes];
137 
138  if(dim==2){
139  std::cerr<<"ERROR: void generate_mesh_metric() not yet implemented in 2D.\n";
140  exit(-1);
141  }else{
142 #pragma omp parallel
143  {
144  double alpha = pow(1.0/resolution_scaling_factor, 2);
145 #pragma omp for schedule(static)
146  for(int i=0; i<_NNodes; i++){
147  real_t m[6];
148 
149  fit_ellipsoid(i, m);
150 
151  for(int j=0;j<6;j++)
152  m[j]*=alpha;
153 
154  _metric[i].set_metric(m);
155  }
156  }
157  }
158  }
159 
160 /* Start of code generated by fit_ellipsoid_3d.py. Warning - be careful about modifying
161  any of the generated code directly. Any changes/fixes should be done
162  in the code generation script generation.
163  */
164 
165 void fit_ellipsoid(int i, real_t *sm){
166  Eigen::Matrix<double, 6, 6> A = Eigen::Matrix<real_t, 6, 6>::Zero(6,6);
167  Eigen::Matrix<double, 6, 1> b = Eigen::Matrix<real_t, 6, 1>::Zero(6);
168 
169  std::vector<index_t> nodes = _mesh->NNList[i];
170  nodes.push_back(i);
171 
172  for(typename std::vector<index_t>::const_iterator it=nodes.begin();it!=nodes.end();++it){
173  const real_t *X0=_mesh->get_coords(*it);
174  real_t x0=X0[0], y0=X0[1], z0=X0[2];
175  assert(std::isfinite(x0));
176  assert(std::isfinite(y0));
177  assert(std::isfinite(z0));
178 
179  for(typename std::vector<index_t>::const_iterator n=_mesh->NNList[*it].begin();n!=_mesh->NNList[*it].end();++n){
180  if(*n<=*it)
181  continue;
182 
183  const real_t *X=_mesh->get_coords(*n);
184  real_t x=X[0]-x0, y=X[1]-y0, z=X[2]-z0;
185 
186  assert(std::isfinite(x));
187  assert(std::isfinite(y));
188  assert(std::isfinite(z));
189  if(x<0){
190  x*=-1;
191  y*=-1;
192  z*=-1;
193  }
194  A[0]+=pow(x, 4); A[1]+=pow(x, 2)*pow(y, 2); A[2]+=pow(x, 2)*pow(z, 2); A[3]+=pow(x, 2)*y*z; A[4]+=pow(x, 3)*z; A[5]+=pow(x, 3)*y;
195  A[6]+=pow(x, 2)*pow(y, 2); A[7]+=pow(y, 4); A[8]+=pow(y, 2)*pow(z, 2); A[9]+=pow(y, 3)*z; A[10]+=x*pow(y, 2)*z; A[11]+=x*pow(y, 3);
196  A[12]+=pow(x, 2)*pow(z, 2); A[13]+=pow(y, 2)*pow(z, 2); A[14]+=pow(z, 4); A[15]+=y*pow(z, 3); A[16]+=x*pow(z, 3); A[17]+=x*y*pow(z, 2);
197  A[18]+=pow(x, 2)*y*z; A[19]+=pow(y, 3)*z; A[20]+=y*pow(z, 3); A[21]+=pow(y, 2)*pow(z, 2); A[22]+=x*y*pow(z, 2); A[23]+=x*pow(y, 2)*z;
198  A[24]+=pow(x, 3)*z; A[25]+=x*pow(y, 2)*z; A[26]+=x*pow(z, 3); A[27]+=x*y*pow(z, 2); A[28]+=pow(x, 2)*pow(z, 2); A[29]+=pow(x, 2)*y*z;
199  A[30]+=pow(x, 3)*y; A[31]+=x*pow(y, 3); A[32]+=x*y*pow(z, 2); A[33]+=x*pow(y, 2)*z; A[34]+=pow(x, 2)*y*z; A[35]+=pow(x, 2)*pow(y, 2);
200 
201  b[0]+=pow(x, 2);
202  b[1]+=pow(y, 2);
203  b[2]+=pow(z, 2);
204  b[3]+=y*z;
205  b[4]+=x*z;
206  b[5]+=x*y;
207  }
208  }
209 
210  Eigen::Matrix<double, 6, 1> S = Eigen::Matrix<real_t, 6, 1>::Zero(6);
211 
212  A.svd().solve(b, &S);
213 
214  if(_mesh->NNList[i].size()>=6){
215  sm[0] = S[0]; sm[1] = S[5]; sm[2] = S[4];
216  sm[3] = S[1]; sm[4] = S[3];
217  sm[5] = S[2];
218  }else{
219  sm[0] = S[0]; sm[1] = 0; sm[2] = 0;
220  sm[3] = S[1]; sm[4] = 0;
221  sm[5] = S[2];
222  }
223 
224  return;
225 }
226 
227 /* End of code generated by fit_ellipsoid_3d.py. Warning - be careful about
228  modifying any of the generated code directly. Any changes/fixes
229  should be done in the code generation script generation.*/
230 
231  void generate_Steiner_ellipse(double resolution_scaling_factor){
232  if(_metric==NULL)
233  _metric = new MetricTensor<real_t,dim>[_NNodes];
234 
235  if(dim==2){
236  std::cerr<<"ERROR: void generate_Steiner_ellipse() not yet implemented in 2D.\n";
237  exit(-1);
238  }else{
239  std::vector<double> SteinerMetricField(_NElements*6);
240 #pragma omp parallel
241  {
242 #pragma omp for schedule(static)
243  for(int i=0; i<_NElements; i++){
244  const index_t *n=_mesh->get_element(i);
245 
246  const real_t *x0 = _mesh->get_coords(n[0]);
247  const real_t *x1 = _mesh->get_coords(n[1]);
248  const real_t *x2 = _mesh->get_coords(n[2]);
249  const real_t *x3 = _mesh->get_coords(n[3]);
250 
251  pragmatic::generate_Steiner_ellipse(x0, x1, x2, x3, SteinerMetricField.data()+i*6);
252  }
253 
254  double alpha = pow(1.0/resolution_scaling_factor, 2);
255 #pragma omp for schedule(static)
256  for(int i=0; i<_NNodes; i++){
257  double sm[6];
258  for(int j=0;j<6;j++)
259  sm[j] = 0.0;
260 
261  for(typename std::set<index_t>::const_iterator ie=_mesh->NEList[i].begin();ie!=_mesh->NEList[i].end();++ie){
262  for(int j=0;j<6;j++)
263  sm[j]+=SteinerMetricField[(*ie)*6+j];
264  }
265 
266  double scale = alpha/_mesh->NEList[i].size();
267  for(int j=0;j<6;j++)
268  sm[j]*=scale;
269 
270  _metric[i].set_metric(sm);
271  }
272  }
273  }
274  }
275 
279  void get_metric(real_t* metric){
280  // Enforce first-touch policy.
281 #pragma omp parallel
282  {
283 #pragma omp for schedule(static)
284  for(int i=0; i<_NNodes; i++){
285  _metric[i].get_metric(metric+i*(dim==2?3:6));
286  }
287  }
288  }
289 
293  void set_metric(const real_t* metric){
294  if(_metric==NULL)
295  _metric = new MetricTensor<real_t,dim>[_NNodes];
296 
297  for(int i=0; i<_NNodes; i++){
298  _metric[i].set_metric(metric+i*(dim==2?3:6));
299  }
300  }
301 
305  void set_metric_full(const real_t* metric){
306  if(_metric==NULL)
307  _metric = new MetricTensor<real_t,dim>[_NNodes];
308 
309  real_t m[dim==2?3:6];
310  for(int i=0; i<_NNodes; i++){
311  if(dim==2){
312  m[0] = metric[i*4]; m[1] = metric[i*4+1];
313  m[2] = metric[i*4+3];
314  }else{
315  m[0] = metric[i*9]; m[1] = metric[i*9+1]; m[2] = metric[i*9+2];
316  m[3] = metric[i*9+4]; m[4] = metric[i*9+5];
317  m[5] = metric[i*9+8];
318  }
319  _metric[i].set_metric(m);
320  }
321  }
322 
323 
328  void set_metric(const real_t* metric, int id){
329  if(_metric==NULL)
330  _metric = new MetricTensor<real_t,dim>[_NNodes];
331 
332  _metric[id].set_metric(metric);
333  }
334 
336  void relax_mesh(double omega){
337  assert(_metric!=NULL);
338 
339  size_t pNElements = (size_t)predict_nelements_part();
340 
341  // We don't really know how much addition space we'll need so this was set after some experimentation.
342  size_t fudge = 5;
343 
344  if(pNElements > _mesh->NElements){
345  // Let's leave a safety margin.
346  pNElements *= fudge;
347  }else{
348  /* The mesh can contain more elements than the predicted number, however
349  * some elements may still need to be refined, therefore until the mesh
350  * is coarsened and defraged we need extra space for the new vertices and
351  * elements that will be created during refinement.
352  */
353  pNElements = _mesh->NElements * fudge;
354  }
355 
356  // In 2D, the number of nodes is ~ 1/2 the number of elements.
357  // In 3D, the number of nodes is ~ 1/6 the number of elements.
358  size_t pNNodes = pNElements/(dim==2?2:6);
359 
360  _mesh->_ENList.resize(pNElements*(dim+1));
361  _mesh->boundary.resize(pNElements*(dim+1));
362  _mesh->_coords.resize(pNNodes*dim);
363  _mesh->metric.resize(pNNodes*(dim==2?3:6));
364  _mesh->NNList.resize(pNNodes);
365  _mesh->NEList.resize(pNNodes);
366  _mesh->node_owner.resize(pNNodes, -1);
367  _mesh->lnn2gnn.resize(pNNodes, -1);
368 
369 #ifdef HAVE_MPI
370  // At this point we can establish a new, gappy global numbering system
371  if(nprocs>1)
372  _mesh->create_gappy_global_numbering(pNElements);
373 #endif
374 
375  // Enforce first-touch policy
376 #pragma omp parallel
377  {
378 #pragma omp for schedule(static)
379  for(int i=0; i<_NNodes; i++){
380  double M[dim==2?3:6];
381  _metric[i].get_metric(M);
382  for(int j=0; j<(dim==2?3:6); j++)
383  _mesh->metric[i*(dim==2?3:6)+j] = (1.0-omega)*_mesh->metric[i*(dim==2?3:6)+j] + omega*M[j];
384  MetricTensor<real_t,dim>::positive_definiteness(&(_mesh->metric[i*(dim==2?3:6)]));
385  }
386  }
387 
388  // Halo update if parallel
389  halo_update<double, (dim==2?3:6)>(_mesh->get_mpi_comm(), _mesh->send, _mesh->recv, _mesh->metric);
390  }
391 
392 
394  void update_mesh(){
395  assert(_metric!=NULL);
396 
397  size_t pNElements = (size_t)predict_nelements_part();
398 
399  // We don't really know how much addition space we'll need so this was set after some experimentation.
400  size_t fudge = 5;
401 
402  if(pNElements > _mesh->NElements){
403  // Let's leave a safety margin.
404  pNElements *= fudge;
405  }else{
406  /* The mesh can contain more elements than the predicted number, however
407  * some elements may still need to be refined, therefore until the mesh
408  * is coarsened and defraged we need extra space for the new vertices and
409  * elements that will be created during refinement.
410  */
411  pNElements = _mesh->NElements * fudge;
412  }
413 
414  // In 2D, the number of nodes is ~ 1/2 the number of elements.
415  // In 3D, the number of nodes is ~ 1/6 the number of elements.
416  size_t pNNodes = std::max(pNElements/(dim==2?2:6), _mesh->get_number_nodes());
417 
418  _mesh->_ENList.resize(pNElements*(dim+1));
419  _mesh->boundary.resize(pNElements*(dim+1));
420  _mesh->_coords.resize(pNNodes*dim);
421  _mesh->metric.resize(pNNodes*(dim==2?3:6));
422  _mesh->NNList.resize(pNNodes);
423  _mesh->NEList.resize(pNNodes);
424  _mesh->node_owner.resize(pNNodes, -1);
425  _mesh->lnn2gnn.resize(pNNodes, -1);
426 
427 #ifdef HAVE_MPI
428  // At this point we can establish a new, gappy global numbering system
429  if(nprocs>1)
430  _mesh->create_gappy_global_numbering(pNElements);
431 #endif
432 
433  // Enforce first-touch policy
434 #pragma omp parallel
435  {
436 #pragma omp for schedule(static)
437  for(int i=0; i<_NNodes; i++){
438  _metric[i].get_metric(&(_mesh->metric[i*(dim==2?3:6)]));
439  }
440  }
441 
442  // Halo update if parallel
443  halo_update<double, (dim==2?3:6)>(_mesh->get_mpi_comm(), _mesh->send, _mesh->recv, _mesh->metric);
444  }
445 
454  void add_field(const real_t* psi, const real_t target_error, int p_norm=-1){
455  bool add_to=true;
456  if(_metric==NULL){
457  add_to = false;
458  _metric = new MetricTensor<real_t,dim>[_NNodes];
459  }
460 
461  real_t eta = 1.0/target_error;
462 #pragma omp parallel
463  {
464  // Calculate Hessian at each point.
465  double h[dim==2?3:6];
466 
467  if(p_norm>0){
468 #pragma omp for schedule(static) nowait
469  for(int i=0; i<_NNodes; i++){
470  hessian_qls_kernel(psi, i, h);
471 
472  double m_det;
473  if(dim==2){
474  /*|h[0] h[1]|
475  |h[1] h[2]|*/
476  m_det = fabs(h[0]*h[2]-h[1]*h[1]);
477  }else if(dim==3){
478  /*|h[0] h[1] h[2]|
479  |h[1] h[3] h[4]|
480  |h[2] h[4] h[5]|
481 
482  sympy
483  h0,h1,h2,h3,h4,h5 = symbols("h[0], h[1], h[2], h[3], h[4], h[5]")
484  M = Matrix([[h0, h1, h2],
485  [h1, h3, h4],
486  [h2, h4, h5]])
487  print_ccode(det(M))
488  */
489  m_det = fabs(h[0]*h[3]*h[5] - h[0]*pow(h[4], 2) - pow(h[1], 2)*h[5] + 2*h[1]*h[2]*h[4] - pow(h[2], 2)*h[3]);
490  }
491 
492  double scaling_factor = eta * pow(m_det+DBL_EPSILON, -1.0 / (2.0 * p_norm + dim));
493 
494  if(std::isnormal(scaling_factor)){
495  for(int j=0;j<(dim==2?3:6);j++)
496  h[j] *= scaling_factor;
497  }else{
498  if(dim==2){
499  h[0] = min_eigenvalue; h[1] = 0.0;
500  h[2] = min_eigenvalue;
501  }else{
502  h[0] = min_eigenvalue; h[1] = 0.0; h[2] = 0.0;
503  h[3] = min_eigenvalue; h[4] = 0.0;
504  h[5] = min_eigenvalue;
505  }
506  }
507 
508  if(add_to){
509  // Merge this metric with the existing metric field.
510  _metric[i].constrain(h);
511  }else{
512  _metric[i].set_metric(h);
513  }
514  }
515  }else{
516 #pragma omp for schedule(static)
517  for(int i=0; i<_NNodes; i++){
518  hessian_qls_kernel(psi, i, h);
519 
520  for(int j=0; j<(dim==2?3:6); j++)
521  h[j] *= eta;
522 
523  if(add_to){
524  // Merge this metric with the existing metric field.
525  _metric[i].constrain(h);
526  }else{
527  _metric[i].set_metric(h);
528  }
529  }
530  }
531  }
532  }
533 
537  void apply_max_edge_length(real_t max_len){
538  real_t M[dim==2?3:6];
539  real_t m = 1.0/(max_len*max_len);
540 
541  if(dim==2){
542  M[0] = m;
543  M[1] = 0.0;
544  M[2] = m;
545  }else if(dim==3){
546  M[0] = m;
547  M[1] = 0.0;
548  M[2] = 0.0;
549  M[3] = m;
550  M[4] = 0.0;
551  M[5] = m;
552  }
553 
554 #pragma omp parallel
555  {
556 #pragma omp for schedule(static)
557  for(int i=0;i<_NNodes;i++)
558  _metric[i].constrain(&(M[0]));
559  }
560  }
561 
565  void apply_min_edge_length(real_t min_len){
566  real_t M[dim==2?3:6];
567  double m = 1.0/(min_len*min_len);
568 
569  if(dim==2){
570  M[0] = m;
571  M[1] = 0.0;
572  M[2] = m;
573  }else if(dim==3){
574  M[0] = m;
575  M[1] = 0.0;
576  M[2] = 0.0;
577  M[3] = m;
578  M[4] = 0.0;
579  M[5] = m;
580  }
581 
582 #pragma omp parallel
583  {
584 #pragma omp for schedule(static)
585  for(int i=0;i<_NNodes;i++)
586  _metric[i].constrain(M, false);
587  }
588  }
589 
593  void apply_min_edge_length(const real_t *min_len){
594 #pragma omp parallel
595  {
596  real_t M[dim==2?3:6];
597 #pragma omp for schedule(static)
598  for(int n=0;n<_NNodes;n++){
599  double m = 1.0/(min_len[n]*min_len[n]);
600 
601  if(dim==2){
602  M[0] = m;
603  M[1] = 0.0;
604  M[2] = m;
605  }else if(dim==3){
606  M[0] = m;
607  M[1] = 0.0;
608  M[2] = 0.0;
609  M[3] = m;
610  M[4] = 0.0;
611  M[5] = m;
612  }
613 
614  _metric[n].constrain(M, false);
615  }
616  }
617  }
618 
622  void apply_max_aspect_ratio(real_t max_aspect_ratio){
623 #pragma omp parallel
624  {
625 #pragma omp for schedule(static)
626  for(int i=0;i<_NNodes;i++)
627  _metric[i].limit_aspect_ratio(max_aspect_ratio);
628  }
629  }
630 
634  void apply_max_nelements(real_t nelements){
635  int predicted = predict_nelements();
636  if(predicted>nelements)
637  apply_nelements(nelements);
638  }
639 
643  void apply_min_nelements(real_t nelements){
644  int predicted = predict_nelements();
645  if(predicted<nelements)
646  apply_nelements(nelements);
647  }
648 
652  void apply_nelements(real_t nelements){
653  double scale_factor = nelements/predict_nelements();
654  if(dim==3)
655  scale_factor = pow(scale_factor, 2.0/3.0);
656 
657 #pragma omp parallel
658  {
659 #pragma omp for schedule(static)
660  for(int i=0;i<_NNodes;i++)
661  _metric[i].scale(scale_factor);
662  }
663  }
664 
668  real_t predicted;
669 
670  if(dim==2){
671  real_t total_area_metric = 0.0;
672 
673  const real_t inv3=1.0/3.0;
674 
675  const real_t *refx0 = _mesh->get_coords(_mesh->get_element(0)[0]);
676  const real_t *refx1 = _mesh->get_coords(_mesh->get_element(0)[1]);
677  const real_t *refx2 = _mesh->get_coords(_mesh->get_element(0)[2]);
678  ElementProperty<real_t> property(refx0, refx1, refx2);
679 
680 #pragma omp parallel for reduction(+:total_area_metric)
681  for(int i=0;i<_NElements;i++){
682  const index_t *n=_mesh->get_element(i);
683 
684  const real_t *x0 = _mesh->get_coords(n[0]);
685  const real_t *x1 = _mesh->get_coords(n[1]);
686  const real_t *x2 = _mesh->get_coords(n[2]);
687  real_t area = property.area(x0, x1, x2);
688 
689  const real_t *m0=_metric[n[0]].get_metric();
690  const real_t *m1=_metric[n[1]].get_metric();
691  const real_t *m2=_metric[n[2]].get_metric();
692 
693  real_t m00 = (m0[0]+m1[0]+m2[0])*inv3;
694  real_t m01 = (m0[1]+m1[1]+m2[1])*inv3;
695  real_t m11 = (m0[2]+m1[2]+m2[2])*inv3;
696 
697  real_t det = m00*m11-m01*m01;
698 
699  total_area_metric += area*sqrt(det);
700  }
701 
702  /* Ideal area of equilateral triangle in metric space:
703  s^2*sqrt(3)/4 where s is the length of the side. However, algorithm
704  allows lengths from 1/sqrt(2) up to sqrt(2) in metric
705  space. Therefore:*/
706  const real_t smallest_ideal_area = 0.5*(sqrt(3.0)/4.0);
707  predicted = total_area_metric/smallest_ideal_area;
708 
709  }else if(dim==3){
710  real_t total_volume_metric = 0.0;
711 
712  const real_t *refx0 = _mesh->get_coords(_mesh->get_element(0)[0]);
713  const real_t *refx1 = _mesh->get_coords(_mesh->get_element(0)[1]);
714  const real_t *refx2 = _mesh->get_coords(_mesh->get_element(0)[2]);
715  const real_t *refx3 = _mesh->get_coords(_mesh->get_element(0)[3]);
716  ElementProperty<real_t> property(refx0, refx1, refx2, refx3);
717 
718 #pragma omp parallel for reduction(+:total_volume_metric)
719  for(int i=0;i<_NElements;i++){
720  const index_t *n=_mesh->get_element(i);
721 
722  const real_t *x0 = _mesh->get_coords(n[0]);
723  const real_t *x1 = _mesh->get_coords(n[1]);
724  const real_t *x2 = _mesh->get_coords(n[2]);
725  const real_t *x3 = _mesh->get_coords(n[3]);
726  real_t volume = property.volume(x0, x1, x2, x3);
727 
728  const real_t *m0=_metric[n[0]].get_metric();
729  const real_t *m1=_metric[n[1]].get_metric();
730  const real_t *m2=_metric[n[2]].get_metric();
731  const real_t *m3=_metric[n[3]].get_metric();
732 
733  real_t m00 = (m0[0]+m1[0]+m2[0]+m3[0])*0.25;
734  real_t m01 = (m0[1]+m1[1]+m2[1]+m3[1])*0.25;
735  real_t m02 = (m0[2]+m1[2]+m2[2]+m3[2])*0.25;
736  real_t m11 = (m0[3]+m1[3]+m2[3]+m3[3])*0.25;
737  real_t m12 = (m0[4]+m1[4]+m2[4]+m3[4])*0.25;
738  real_t m22 = (m0[5]+m1[5]+m2[5]+m3[5])*0.25;
739 
740  real_t det = (m11*m22 - m12*m12)*m00 - (m01*m22 - m02*m12)*m01 + (m01*m12 - m02*m11)*m02;
741 
742  assert(det>-DBL_EPSILON);
743  total_volume_metric += volume*sqrt(det);
744  }
745 
746  // Ideal volume of triangle in metric space.
747  real_t ideal_volume = 1.0/sqrt(72.0);
748  predicted = total_volume_metric/ideal_volume;
749  }
750 
751  return predicted;
752  }
753 
757  double predicted=predict_nelements_part();
758 
759 #ifdef HAVE_MPI
760  if(nprocs>1){
761  MPI_Allreduce(MPI_IN_PLACE, &predicted, 1, _mesh->MPI_REAL_T, MPI_SUM, _mesh->get_mpi_comm());
762  }
763 #endif
764 
765  return predicted;
766  }
767 
768  private:
769 
771  void hessian_qls_kernel(const real_t *psi, int i, real_t *Hessian){
772  int min_patch_size = (dim==2?6:15); // In 3D, 10 is the minimum but can give crappy results.
773 
774  std::set<index_t> patch = _mesh->get_node_patch(i, min_patch_size);
775  patch.insert(i);
776 
777  if(dim==2){
778  // Form quadratic system to be solved. The quadratic fit is:
779  // P = a0*y^2+a1*x^2+a2*x*y+a3*y+a4*x+a5
780  // A = P^TP
781  Eigen::Matrix<real_t, 6, 6> A = Eigen::Matrix<real_t, 6, 6>::Zero(6,6);
782  Eigen::Matrix<real_t, 6, 1> b = Eigen::Matrix<real_t, 6, 1>::Zero(6);
783 
784  real_t x0=_mesh->_coords[i*2], y0=_mesh->_coords[i*2+1];
785 
786  for(typename std::set<index_t>::const_iterator n=patch.begin(); n!=patch.end(); n++){
787  real_t x=_mesh->_coords[(*n)*2]-x0, y=_mesh->_coords[(*n)*2+1]-y0;
788 
789  A[0]+=y*y*y*y;
790  A[6]+=x*x*y*y; A[7]+=x*x*x*x;
791  A[12]+=x*y*y*y; A[13]+=x*x*x*y; A[14]+=x*x*y*y;
792  A[18]+=y*y*y; A[19]+=x*x*y; A[20]+=x*y*y; A[21]+=y*y;
793  A[24]+=x*y*y; A[25]+=x*x*x; A[26]+=x*x*y; A[27]+=x*y; A[28]+=x*x;
794  A[30]+=y*y; A[31]+=x*x; A[32]+=x*y; A[33]+=y; A[34]+=x; A[35]+=1;
795 
796  b[0]+=psi[*n]*y*y; b[1]+=psi[*n]*x*x; b[2]+=psi[*n]*x*y; b[3]+=psi[*n]*y; b[4]+=psi[*n]*x; b[5]+=psi[*n];
797  }
798  A[1] = A[6]; A[2] = A[12]; A[3] = A[18]; A[4] = A[24]; A[5] = A[30];
799  A[8] = A[13]; A[9] = A[19]; A[10]= A[25]; A[11]= A[31];
800  A[15]= A[20]; A[16]= A[26]; A[17]= A[32];
801  A[22]= A[27]; A[23]= A[33];
802  A[29]= A[34];
803 
804  Eigen::Matrix<real_t, 6, 1> a = Eigen::Matrix<real_t, 6, 1>::Zero(6);
805  A.svd().solve(b, &a);
806 
807  Hessian[0] = 2*a[1]; // d2/dx2
808  Hessian[1] = a[2]; // d2/dxdy
809  Hessian[2] = 2*a[0]; // d2/dy2
810 
811  }else if(dim==3){
812  // Form quadratic system to be solved. The quadratic fit is:
813  // P = 1 + x + y + z + x^2 + y^2 + z^2 + xy + xz + yz
814  // A = P^TP
815  Eigen::Matrix<real_t, 10, 10> A = Eigen::Matrix<real_t, 10, 10>::Zero(10,10);
816  Eigen::Matrix<real_t, 10, 1> b = Eigen::Matrix<real_t, 10, 1>::Zero(10);
817 
818  real_t x0=_mesh->_coords[i*3], y0=_mesh->_coords[i*3+1], z0=_mesh->_coords[i*3+2];
819  assert(std::isfinite(x0));
820  assert(std::isfinite(y0));
821  assert(std::isfinite(z0));
822 
823  for(typename std::set<index_t>::const_iterator n=patch.begin(); n!=patch.end(); n++){
824  real_t x=_mesh->_coords[(*n)*3]-x0, y=_mesh->_coords[(*n)*3+1]-y0, z=_mesh->_coords[(*n)*3+2]-z0;
825  assert(std::isfinite(x));
826  assert(std::isfinite(y));
827  assert(std::isfinite(z));
828  assert(std::isfinite(psi[*n]));
829 
830  A[0]+=1;
831  A[10]+=x; A[11]+=x*x;
832  A[20]+=y; A[21]+=x*y; A[22]+=y*y;
833  A[30]+=z; A[31]+=x*z; A[32]+=y*z; A[33]+=z*z;
834  A[40]+=x*x; A[41]+=x*x*x; A[42]+=x*x*y; A[43]+=x*x*z; A[44]+=x*x*x*x;
835  A[50]+=x*y; A[51]+=x*x*y; A[52]+=x*y*y; A[53]+=x*y*z; A[54]+=x*x*x*y; A[55]+=x*x*y*y;
836  A[60]+=x*z; A[61]+=x*x*z; A[62]+=x*y*z; A[63]+=x*z*z; A[64]+=x*x*x*z; A[65]+=x*x*y*z; A[66]+=x*x*z*z;
837  A[70]+=y*y; A[71]+=x*y*y; A[72]+=y*y*y; A[73]+=y*y*z; A[74]+=x*x*y*y; A[75]+=x*y*y*y; A[76]+=x*y*y*z; A[77]+=y*y*y*y;
838  A[80]+=y*z; A[81]+=x*y*z; A[82]+=y*y*z; A[83]+=y*z*z; A[84]+=x*x*y*z; A[85]+=x*y*y*z; A[86]+=x*y*z*z; A[87]+=y*y*y*z; A[88]+=y*y*z*z;
839  A[90]+=z*z; A[91]+=x*z*z; A[92]+=y*z*z; A[93]+=z*z*z; A[94]+=x*x*z*z; A[95]+=x*y*z*z; A[96]+=x*z*z*z; A[97]+=y*y*z*z; A[98]+=y*z*z*z; A[99]+=z*z*z*z;
840 
841  b[0]+=psi[*n]*1; b[1]+=psi[*n]*x; b[2]+=psi[*n]*y; b[3]+=psi[*n]*z; b[4]+=psi[*n]*x*x; b[5]+=psi[*n]*x*y; b[6]+=psi[*n]*x*z; b[7]+=psi[*n]*y*y; b[8]+=psi[*n]*y*z; b[9]+=psi[*n]*z*z;
842  }
843 
844  A[1] = A[10]; A[2] = A[20]; A[3] = A[30]; A[4] = A[40]; A[5] = A[50]; A[6] = A[60]; A[7] = A[70]; A[8] = A[80]; A[9] = A[90];
845  A[12] = A[21]; A[13] = A[31]; A[14] = A[41]; A[15] = A[51]; A[16] = A[61]; A[17] = A[71]; A[18] = A[81]; A[19] = A[91];
846  A[23] = A[32]; A[24] = A[42]; A[25] = A[52]; A[26] = A[62]; A[27] = A[72]; A[28] = A[82]; A[29] = A[92];
847  A[34] = A[43]; A[35] = A[53]; A[36] = A[63]; A[37] = A[73]; A[38] = A[83]; A[39] = A[93];
848  A[45] = A[54]; A[46] = A[64]; A[47] = A[74]; A[48] = A[84]; A[49] = A[94];
849  A[56] = A[65]; A[57] = A[75]; A[58] = A[85]; A[59] = A[95];
850  A[67] = A[76]; A[68] = A[86]; A[69] = A[96];
851  A[78] = A[87]; A[79] = A[97];
852  A[89] = A[98];
853 
854  Eigen::Matrix<real_t, 10, 1> a = Eigen::Matrix<real_t, 10, 1>::Zero(10);
855  A.svd().solve(b, &a);
856 
857  Hessian[0] = a[4]*2.0; // d2/dx2
858  Hessian[1] = a[5]; // d2/dxdy
859  Hessian[2] = a[6]; // d2/dxdz
860  Hessian[3] = a[7]*2.0; // d2/dy2
861  Hessian[4] = a[8]; // d2/dydz
862  Hessian[5] = a[9]*2.0; // d2/dz2
863  }
864  }
865 
866 private:
867  int rank, nprocs;
868  int _NNodes, _NElements;
869  MetricTensor<real_t,dim>* _metric;
870  Mesh<real_t>* _mesh;
871  double min_eigenvalue;
872 };
873 
874 #endif
void update_mesh()
Update the metric field on the mesh.
Definition: MetricField.h:394
void set_metric_full(const real_t *metric)
Definition: MetricField.h:305
void apply_min_edge_length(const real_t *min_len)
Definition: MetricField.h:593
MetricField(Mesh< real_t > &mesh)
Definition: MetricField.h:77
void fit_ellipsoid(int i, real_t *sm)
Definition: MetricField.h:165
int index_t
Calculates a number of element properties.
Manages mesh data.
Definition: Mesh.h:70
real_t predict_nelements_part()
Definition: MetricField.h:667
void apply_max_edge_length(real_t max_len)
Definition: MetricField.h:537
Constructs metric tensor field which encodes anisotropic edge size information.
Definition: MetricField.h:73
void add_field(const real_t *psi, const real_t target_error, int p_norm=-1)
Definition: MetricField.h:454
size_t get_number_elements() const
Return the number of elements in the mesh.
Definition: Mesh.h:374
void apply_nelements(real_t nelements)
Definition: MetricField.h:652
static void positive_definiteness(treal_t *metric)
Definition: MetricTensor.h:143
real_t predict_nelements()
Definition: MetricField.h:756
void relax_mesh(double omega)
Update the metric field on the mesh.
Definition: MetricField.h:336
void get_metric(real_t *metric)
Definition: MetricField.h:279
void get_metric(treal_t *metric)
Definition: MetricTensor.h:111
void generate_mesh_metric(double resolution_scaling_factor)
Definition: MetricField.h:134
void apply_min_edge_length(real_t min_len)
Definition: MetricField.h:565
void set_metric(const real_t *metric)
Definition: MetricField.h:293
void generate_Steiner_ellipse(double resolution_scaling_factor)
Definition: MetricField.h:231
void set_metric(const treal_t *metric)
Definition: MetricTensor.h:135
void generate_Steiner_ellipse(const double *x1, const double *x2, const double *x3, const double *x4, double *sm)
void apply_min_nelements(real_t nelements)
Definition: MetricField.h:643
size_t get_number_nodes() const
Return the number of nodes in the mesh.
Definition: Mesh.h:369
void apply_max_aspect_ratio(real_t max_aspect_ratio)
Definition: MetricField.h:622
void apply_max_nelements(real_t nelements)
Definition: MetricField.h:634
void constrain(const treal_t *M_in, bool perserved_small_edges=true)
Definition: MetricTensor.h:186
void set_metric(const real_t *metric, int id)
Definition: MetricField.h:328