1 /* *****************************************************************
2     MESQUITE -- The Mesh Quality Improvement Toolkit
3 
4     Copyright 2004 Sandia Corporation and Argonne National
5     Laboratory.  Under the terms of Contract DE-AC04-94AL85000
6     with Sandia Corporation, the U.S. Government retains certain
7     rights in this software.
8 
9     This library is free software; you can redistribute it and/or
10     modify it under the terms of the GNU Lesser General Public
11     License as published by the Free Software Foundation; either
12     version 2.1 of the License, or (at your option) any later version.
13 
14     This library is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17     Lesser General Public License for more details.
18 
19     You should have received a copy of the GNU Lesser General Public License
20     (lgpl.txt) along with this library; if not, write to the Free Software
21     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 
23     diachin2@llnl.gov, djmelan@sandia.gov, mbrewer@sandia.gov,
24     pknupp@sandia.gov, tleurent@mcs.anl.gov, tmunson@mcs.anl.gov
25     kraftche@cae.wisc.edu
26 
27   ***************************************************************** */
28 /*!
29   \file   QualityMetric.cpp
30   \brief
31 
32   \author Michael Brewer
33   \author Thomas Leurent
34   \date   2002-05-14
35   \author Jason Kraftcheck
36   \date   2006-04-20
37 */
38 
39 #include "QualityMetric.hpp"
40 #include "MsqVertex.hpp"
41 #include "PatchData.hpp"
42 
43 namespace MBMesquite {
44 
initialize_queue(MeshDomainAssoc *,const Settings *,MsqError &)45 void QualityMetric::initialize_queue( MeshDomainAssoc* ,
46                                       const Settings* ,
47                                       MsqError&  )
48 {}
49 
get_single_pass(PatchData & pd,std::vector<size_t> & handles,bool free_vertices_only,MsqError & err)50 void QualityMetric::get_single_pass( PatchData& pd,
51                         std::vector<size_t>& handles,
52                         bool free_vertices_only,
53                         MsqError& err )
54 {
55   get_evaluations( pd, handles, free_vertices_only, err );
56 }
57 
58 
59 static inline
get_delta_C(const PatchData & pd,const std::vector<size_t> & indices,MsqError & err)60 double get_delta_C( const PatchData& pd,
61                     const std::vector<size_t>& indices,
62                     MsqError& err )
63 {
64   if (indices.empty()) {
65     MSQ_SETERR(err)(MsqError::INVALID_ARG);
66     return 0.0;
67   }
68 
69   std::vector<size_t>::const_iterator beg, iter, iter2, end;
70 
71   std::vector<size_t> tmp_vect;
72   if (indices.size() == 1u) {
73     pd.get_adjacent_vertex_indices( indices.front(), tmp_vect, err );
74     MSQ_ERRZERO(err);
75     assert(!tmp_vect.empty());
76     tmp_vect.push_back( indices.front() );
77     beg = tmp_vect.begin();
78     end = tmp_vect.end();
79   }
80   else {
81     beg = indices.begin();
82     end = indices.end();
83   }
84 
85   double min_dist_sqr = HUGE_VAL, sum_dist_sqr = 0.0;
86   for (iter = beg; iter != end; ++iter) {
87     for (iter2 = iter+1; iter2 != end; ++iter2) {
88       Vector3D diff = pd.vertex_by_index(*iter);
89       diff -= pd.vertex_by_index(*iter2);
90       double dist_sqr = diff % diff;
91       if (dist_sqr < min_dist_sqr)
92         min_dist_sqr = dist_sqr;
93       sum_dist_sqr += dist_sqr;
94     }
95   }
96 
97   return 3e-6*sqrt(min_dist_sqr) + 5e-7*sqrt(sum_dist_sqr/(end-beg));
98 }
99 
100 
evaluate_with_gradient(PatchData & pd,size_t handle,double & value,std::vector<size_t> & indices,std::vector<Vector3D> & gradient,MsqError & err)101 bool QualityMetric::evaluate_with_gradient( PatchData& pd,
102                               size_t handle,
103                               double& value,
104                               std::vector<size_t>& indices,
105                               std::vector<Vector3D>& gradient,
106                               MsqError& err )
107 {
108   indices.clear();
109   bool valid = evaluate_with_indices( pd, handle, value, indices, err);
110   if (MSQ_CHKERR(err) || !valid)
111     return false;
112   if (indices.empty())
113     return true;
114 
115     // get initial pertubation amount
116   double delta_C = finiteDiffEps;
117   if (!haveFiniteDiffEps) {
118     delta_C = get_delta_C( pd, indices, err ); MSQ_ERRZERO(err);
119     if (keepFiniteDiffEps) {
120       finiteDiffEps = delta_C;
121       haveFiniteDiffEps = true;
122     }
123   }
124   const double delta_inv_C = 1.0/delta_C;
125   const int reduction_limit = 15;
126 
127   gradient.resize( indices.size() );
128   for (size_t v=0; v<indices.size(); ++v)
129   {
130     const Vector3D pos = pd.vertex_by_index(indices[v]);
131 
132     /* gradient in the x, y, z direction */
133     for (int j=0;j<3;++j)
134     {
135       double delta = delta_C;
136       double delta_inv = delta_inv_C;
137       double metric_value;
138       Vector3D delta_v( 0, 0, 0 );
139 
140         //perturb the node and calculate gradient.  The while loop is a
141         //safety net to make sure the epsilon perturbation does not take
142         //the element out of the feasible region.
143       int counter = 0;
144       for (;;) {
145           // perturb the coordinates of the free vertex in the j direction
146           // by delta
147         delta_v[j] = delta;
148         pd.set_vertex_coordinates( pos+delta_v, indices[v], err ); MSQ_ERRZERO(err);
149 
150           //compute the function at the perturbed point location
151         valid = evaluate( pd, handle, metric_value, err);
152         if (!MSQ_CHKERR(err) && valid)
153           break;
154 
155         if (++counter >= reduction_limit) {
156           MSQ_SETERR(err)("Perturbing vertex by delta caused an inverted element.",
157                           MsqError::INTERNAL_ERROR);
158           return false;
159         }
160 
161         delta*=0.1;
162         delta_inv*=10.;
163       }
164         // put the coordinates back where they belong
165       pd.set_vertex_coordinates( pos, indices[v], err );
166         // compute the numerical gradient
167       gradient[v][j] = (metric_value - value) * delta_inv;
168     } // for(j)
169   } // for(indices)
170   return true;
171 }
172 
173 
evaluate_with_Hessian(PatchData & pd,size_t handle,double & value,std::vector<size_t> & indices,std::vector<Vector3D> & gradient,std::vector<Matrix3D> & Hessian,MsqError & err)174 bool QualityMetric::evaluate_with_Hessian( PatchData& pd,
175                               size_t handle,
176                               double& value,
177                               std::vector<size_t>& indices,
178                               std::vector<Vector3D>& gradient,
179                               std::vector<Matrix3D>& Hessian,
180                               MsqError& err )
181 {
182   indices.clear();
183   gradient.clear();
184   keepFiniteDiffEps = true;
185   bool valid = evaluate_with_gradient( pd, handle, value, indices, gradient, err );
186   keepFiniteDiffEps = false;
187   if (MSQ_CHKERR(err) || !valid) {
188     haveFiniteDiffEps = false;
189     return false;
190   }
191   if (indices.empty()){
192     haveFiniteDiffEps = false;
193     return true;
194   }
195 
196     // get initial pertubation amount
197   double delta_C;
198   if (haveFiniteDiffEps) {
199     delta_C = finiteDiffEps;
200   }
201   else {
202     delta_C = get_delta_C( pd, indices, err ); MSQ_ERRZERO(err);
203   }
204   assert(delta_C < 1e30);
205   const double delta_inv_C = 1.0/delta_C;
206   const int reduction_limit = 15;
207 
208   std::vector<Vector3D> temp_gradient( indices.size() );
209   const int num_hess = indices.size() * (indices.size() + 1) / 2;
210   Hessian.resize( num_hess );
211 
212   for (unsigned v = 0; v < indices.size(); ++v) {
213     const Vector3D pos = pd.vertex_by_index(indices[v]);
214     for (int j = 0; j < 3; ++j ) { // x, y, and z
215       double delta = delta_C;
216       double delta_inv = delta_inv_C;
217       double metric_value;
218       Vector3D delta_v(0,0,0);
219 
220         // find finite difference for gradient
221       int counter = 0;
222       for (;;) {
223         delta_v[j] = delta;
224         pd.set_vertex_coordinates( pos+delta_v, indices[v], err ); MSQ_ERRZERO(err);
225         valid = evaluate_with_gradient( pd, handle, metric_value, indices, temp_gradient, err );
226         if (!MSQ_CHKERR(err) && valid)
227           break;
228 
229         if (++counter >= reduction_limit) {
230           MSQ_SETERR(err)("Algorithm did not successfully compute element's "
231                            "Hessian.\n",MsqError::INTERNAL_ERROR);
232           haveFiniteDiffEps = false;
233           return false;
234         }
235 
236         delta *= 0.1;
237         delta_inv *= 10.0;
238       }
239       pd.set_vertex_coordinates( pos, indices[v], err ); MSQ_ERRZERO(err);
240 
241         //compute the numerical Hessian
242       for (unsigned w = 0; w <= v; ++w) {
243           //finite difference to get some entries of the Hessian
244         Vector3D fd( temp_gradient[w] );
245         fd -= gradient[w];
246         fd *= delta_inv;
247           // For the block at position w,v in a matrix, we need the corresponding index
248           // (mat_index) in a 1D array containing only upper triangular blocks.
249         unsigned sum_w = w*(w+1)/2; // 1+2+3+...+w
250         unsigned mat_index = w*indices.size() + v - sum_w;
251         Hessian[mat_index][0][j] = fd[0];
252         Hessian[mat_index][1][j] = fd[1];
253         Hessian[mat_index][2][j] = fd[2];
254       }
255     }  // for(j)
256   } // for(indices)
257   haveFiniteDiffEps = false;
258   return true;
259 }
260 
evaluate_with_Hessian_diagonal(PatchData & pd,size_t handle,double & value,std::vector<size_t> & indices,std::vector<Vector3D> & gradient,std::vector<SymMatrix3D> & Hessian_diagonal,MsqError & err)261 bool QualityMetric::evaluate_with_Hessian_diagonal( PatchData& pd,
262                                 size_t handle,
263                                 double& value,
264                                 std::vector<size_t>& indices,
265                                 std::vector<Vector3D>& gradient,
266                                 std::vector<SymMatrix3D>& Hessian_diagonal,
267                                 MsqError& err )
268 {
269   bool rval = evaluate_with_Hessian( pd, handle, value, indices, gradient, tmpHess, err );
270   if (MSQ_CHKERR(err) || !rval)
271     return rval;
272   size_t s = indices.size();
273   Hessian_diagonal.resize( s );
274   std::vector<Matrix3D>::const_iterator h = tmpHess.begin();
275   for (size_t i = 0; i < indices.size(); ++i) {
276     Hessian_diagonal[i] = h->upper();
277     h += s--;
278   }
279   return rval;
280 }
281 
282 
fixed_vertex_bitmap(PatchData & pd,const MsqMeshEntity * elem,std::vector<size_t> & indices)283 uint32_t QualityMetric::fixed_vertex_bitmap( PatchData& pd,
284                                            const MsqMeshEntity* elem,
285                                            std::vector<size_t>& indices )
286 {
287   indices.clear();
288   uint32_t result = ~(uint32_t)0;
289   unsigned num_vtx = elem->vertex_count();
290   const size_t* vertices = elem->get_vertex_index_array();
291   indices.clear();
292   for (unsigned i = 0; i < num_vtx; ++i) {
293     if (vertices[i] < pd.num_free_vertices()) {
294       indices.push_back( vertices[i] );
295       result &= ~(uint32_t)(1<<i);
296     }
297   }
298   return result;
299 }
300 
301 
remove_fixed_gradients(EntityTopology elem_type,uint32_t fixed,std::vector<Vector3D> & grads)302 void QualityMetric::remove_fixed_gradients( EntityTopology elem_type,
303                                           uint32_t fixed,
304                                           std::vector<Vector3D>& grads )
305 {
306   const unsigned num_vertex = TopologyInfo::corners( elem_type );
307   unsigned r, w;
308   for (r = 0; r < num_vertex && !(fixed & (1<<r)); ++r);
309   for (w = r++; r < num_vertex; ++r) {
310     if (!(fixed & (1<<r))) {
311       grads[w] = grads[r];
312       ++w;
313     }
314   }
315   grads.resize(w);
316 }
317 
remove_fixed_diagonals(EntityTopology type,uint32_t fixed,std::vector<Vector3D> & grads,std::vector<SymMatrix3D> & diags)318 void QualityMetric::remove_fixed_diagonals( EntityTopology type,
319                                           uint32_t fixed,
320                                           std::vector<Vector3D>& grads,
321                                           std::vector<SymMatrix3D>& diags )
322 {
323   const unsigned num_vertex = TopologyInfo::corners( type );
324   unsigned r, w;
325   for (r = 0; r < num_vertex && !(fixed & (1<<r)); ++r);
326   for (w = r++; r < num_vertex; ++r) {
327     if (!(fixed & (1<<r))) {
328       grads[w] = grads[r];
329       diags[w] = diags[r];
330       ++w;
331     }
332   }
333   grads.resize(w);
334   diags.resize(w);
335 }
336 
remove_fixed_hessians(EntityTopology elem_type,uint32_t fixed,std::vector<Matrix3D> & hessians)337 void QualityMetric::remove_fixed_hessians( EntityTopology elem_type,
338                                          uint32_t fixed,
339                                          std::vector<Matrix3D>& hessians )
340 {
341   const unsigned num_vertex = TopologyInfo::corners( elem_type );
342   unsigned r, c, i = 0, w = 0;
343   for (r = 0; r < num_vertex; ++r) {
344     if (fixed & (1<<r)) {
345       i += num_vertex - r;
346       continue;
347     }
348     for (c = r; c < num_vertex; ++c) {
349       if (!(fixed & (1<<c))) {
350         hessians[w] = hessians[i];
351         ++w;
352       }
353       ++i;
354     }
355   }
356   hessians.resize(w);
357 }
358 
weighted_average_metrics(const double coef[],const double metric_values[],const int & num_values,MsqError &)359 double QualityMetric::weighted_average_metrics(const double coef[],
360                                              const double metric_values[],
361                                              const int& num_values,
362                                              MsqError &/*err*/)
363 {
364   //MSQ_MAX needs to be made global?
365   //double MSQ_MAX=1e10;
366   double total_value=0.0;
367   int i=0;
368   //if no values, return zero
369   if (num_values<=0){
370     return 0.0;
371   }
372 
373   for (i=0;i<num_values;++i){
374     total_value += coef[i]*metric_values[i];
375   }
376   total_value /= (double) num_values;
377 
378   return total_value;
379 }
380 
381 } // namespace MBMesquite
382