GetFEM++  5.3
getfem_contact_and_friction_common.cc
1 /*===========================================================================
2 
3  Copyright (C) 2013-2017 Yves Renard, Konstantinos Poulios.
4 
5  This file is a part of GetFEM++
6 
7  GetFEM++ is free software; you can redistribute it and/or modify it
8  under the terms of the GNU Lesser General Public License as published
9  by the Free Software Foundation; either version 3 of the License, or
10  (at your option) any later version along with the GCC Runtime Library
11  Exception either version 3.1 or (at your option) any later version.
12  This program is distributed in the hope that it will be useful, but
13  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15  License and GCC Runtime Library Exception for more details.
16  You should have received a copy of the GNU Lesser General Public License
17  along with this program; if not, write to the Free Software Foundation,
18  Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
19 
20 ===========================================================================*/
21 
24 #ifndef _WIN32
25 #include <unistd.h>
26 #endif
27 
28 namespace getfem {
29 
30  bool boundary_has_fem_nodes(bool slave_flag, int nodes_mode) {
31  return (slave_flag && nodes_mode) ||
32  (!slave_flag && nodes_mode == 2);
33  }
34 
35  void compute_normal(const fem_interpolation_context &ctx,
36  size_type face, bool in_reference_conf,
37  const model_real_plain_vector &coeff,
38  base_node &n0, base_node &n,
39  base_matrix &grad) {
40  n0 = bgeot::compute_normal(ctx, face);
41  if (in_reference_conf) {
42  n = n0;
43  } else {
44  ctx.pf()->interpolation_grad(ctx, coeff, grad, dim_type(ctx.N()));
45  gmm::add(gmm::identity_matrix(), grad);
46  scalar_type J = bgeot::lu_inverse(&(*(grad.begin())), ctx.N());
47  if (J <= scalar_type(0)) {GMM_WARNING1("Inverted element !" << J);
48  gmm::mult(gmm::transposed(grad), n0, n);
49  gmm::scale(n, gmm::sgn(J));
50  //cout<< "n0 =" << n0 << "n =" <<n<< endl;
51  //cout<< "x0=" << ctx.xref() << "x =" <<ctx.xreal()<< endl;
52  }
53  else {
54  gmm::mult(gmm::transposed(grad), n0, n);
55  gmm::scale(n, gmm::sgn(J));} // Test
56  }
57  }
58 
59  //=========================================================================
60  //
61  // Structure which store the contact boundaries, rigid obstacles and
62  // computes the contact pairs in large sliding/large deformation.
63  //
64  //=========================================================================
65 
66  size_type multi_contact_frame::add_U
67  (const model_real_plain_vector *U, const std::string &name,
68  const model_real_plain_vector *w, const std::string &wname) {
69  if (!U) return size_type(-1);
70  size_type i = 0;
71  for (; i < Us.size(); ++i) if (Us[i] == U) return i;
72  Us.push_back(U);
73  Ws.push_back(w);
74  Unames.push_back(name);
75  Wnames.push_back(wname);
76  ext_Us.resize(Us.size());
77  ext_Ws.resize(Us.size());
78  return i;
79  }
80 
81  size_type multi_contact_frame::add_lambda
82  (const model_real_plain_vector *lambda, const std::string &name) {
83  if (!lambda) return size_type(-1);
84  size_type i = 0;
85  for (; i < lambdas.size(); ++i) if (lambdas[i] == lambda) return i;
86  lambdas.push_back(lambda);
87  lambdanames.push_back(name);
88  ext_lambdas.resize(lambdas.size());
89  return i;
90  }
91 
92  void multi_contact_frame::extend_vectors() {
93  dal::bit_vector iU, ilambda;
94  for (size_type i = 0; i < contact_boundaries.size(); ++i) {
95  size_type ind_U = contact_boundaries[i].ind_U;
96  if (!(iU[ind_U])) {
97  const mesh_fem &mf = *(contact_boundaries[i].mfu);
98  gmm::resize(ext_Us[ind_U], mf.nb_basic_dof());
99  mf.extend_vector(*(Us[ind_U]), ext_Us[ind_U]);
100  if (Ws[ind_U]) {
101  gmm::resize(ext_Ws[ind_U], mf.nb_basic_dof());
102  mf.extend_vector(*(Ws[ind_U]), ext_Ws[ind_U]);
103  } else gmm::resize(ext_Ws[ind_U], 0);
104  iU.add(ind_U);
105  }
106  size_type ind_lambda = contact_boundaries[i].ind_lambda;
107  if (ind_lambda != size_type(-1) && !(ilambda[ind_lambda])) {
108  const mesh_fem &mf = *(contact_boundaries[i].mflambda);
109  gmm::resize(ext_lambdas[ind_lambda], mf.nb_basic_dof());
110  mf.extend_vector(*(lambdas[ind_lambda]), ext_lambdas[ind_lambda]);
111  ilambda.add(ind_lambda);
112  }
113  }
114  }
115 
116  void multi_contact_frame::normal_cone_simplification() {
117  if (nodes_mode) {
118  scalar_type threshold = ::cos(cut_angle);
119  for (size_type i = 0; i < boundary_points_info.size(); ++i) {
120  normal_cone &nc = boundary_points_info[i].normals;
121  if (nc.size() > 1) {
122  base_small_vector n_mean = nc[0];
123  for (size_type j = 1; j < nc.size(); ++j) n_mean += nc[j];
124  scalar_type nn_mean = gmm::vect_norm2(n_mean);
125  GMM_ASSERT1(nn_mean != scalar_type(0), "oupssss");
126  if (nn_mean != scalar_type(0)) {
127  gmm::scale(n_mean, scalar_type(1)/nn_mean);
128  bool reduce = true;
129  for (size_type j = 0; j < nc.size(); ++j)
130  if (gmm::vect_sp(n_mean, nc[j]) < threshold)
131  { reduce = false; break; }
132  if (reduce) {
133  boundary_points_info[i].normals = normal_cone(n_mean);
134  }
135  }
136  }
137  }
138  }
139  }
140 
141  bool multi_contact_frame::test_normal_cones_compatibility
142  (const normal_cone &nc1, const normal_cone &nc2) {
143  for (size_type i = 0; i < nc1.size(); ++i)
144  for (size_type j = 0; j < nc2.size(); ++j)
145  if (gmm::vect_sp(nc1[i], nc2[j]) < scalar_type(0))
146  return true;
147  return false;
148  }
149 
150  bool multi_contact_frame::test_normal_cones_compatibility
151  (const base_small_vector &n, const normal_cone &nc2) {
152  for (size_type j = 0; j < nc2.size(); ++j)
153  if (gmm::vect_sp(n, nc2[j]) < scalar_type(0))
154  return true;
155  return false;
156  }
157 
158  bool multi_contact_frame::are_dof_linked(size_type ib1, size_type idof1,
159  size_type ib2, size_type idof2) {
160  const mesh_fem &mf1 = mfdisp_of_boundary(ib1);
161  const mesh_fem &mf2 = mfdisp_of_boundary(ib2);
162  if ( &(mf1.linked_mesh()) != &(mf2.linked_mesh())) return false;
163  GMM_ASSERT1(!(mf1.is_reduced()) && !(mf2.is_reduced()),
164  "Nodal strategy can only be applied for non reduced fems");
165  const mesh::ind_cv_ct &ic1 = mf1.convex_to_basic_dof(idof1);
166  const mesh::ind_cv_ct &ic2 = mf2.convex_to_basic_dof(idof2);
167  bool lk = false;
168  for (size_type i = 0; i < ic1.size(); ++i) aux_dof_cv.add(ic1[i]);
169  for (size_type i = 0; i < ic2.size(); ++i)
170  if (aux_dof_cv.is_in(ic2[i])) { lk = true; break; }
171  for (size_type i = 0; i < ic1.size(); ++i) aux_dof_cv.sup(ic1[i]);
172  return lk;
173  }
174 
175  bool multi_contact_frame::is_dof_linked(size_type ib1, size_type idof1,
176  size_type ib2, size_type cv) {
177  const mesh_fem &mf1 = mfdisp_of_boundary(ib1);
178  const mesh_fem &mf2 = mfdisp_of_boundary(ib2);
179  if ( &(mf1.linked_mesh()) != &(mf2.linked_mesh())) return false;
180  GMM_ASSERT1(!(mf1.is_reduced()) && !(mf2.is_reduced()),
181  "Nodal strategy can only be applied for non reduced fems");
182  const mesh::ind_cv_ct &ic1 = mf1.convex_to_basic_dof(idof1);
183  for (size_type i = 0; i < ic1.size(); ++i)
184  if (cv == ic1[i]) return true;
185  return false;
186  }
187 
188  void multi_contact_frame::add_potential_contact_face
189  (size_type ip, size_type ib, size_type ie, short_type iff) {
190  bool found = false;
191  std::vector<face_info> &sfi = potential_pairs[ip];
192  for (size_type k = 0; k < sfi.size(); ++k)
193  if (sfi[k].ind_boundary == ib &&
194  sfi[k].ind_element == ie &&
195  sfi[k].ind_face == iff) found = true;
196 
197  if (!found) sfi.push_back(face_info(ib, ie, iff));
198  }
199 
200  void multi_contact_frame::clear_aux_info() {
201  boundary_points = std::vector<base_node>();
202  boundary_points_info = std::vector<boundary_point>();
203  element_boxes.clear();
204  element_boxes_info = std::vector<influence_box>();
205  potential_pairs = std::vector<std::vector<face_info> >();
206  }
207 
208  multi_contact_frame::multi_contact_frame(size_type NN, scalar_type r_dist,
209  bool dela, bool selfc,
210  scalar_type cut_a,
211  bool rayt, int nmode, bool refc)
212  : N(NN), self_contact(selfc), ref_conf(refc), use_delaunay(dela),
213  nodes_mode(nmode), raytrace(rayt), release_distance(r_dist),
214  cut_angle(cut_a), EPS(1E-8), md(0), coordinates(N), pt(N) {
215  if (N > 0) coordinates[0] = "x";
216  if (N > 1) coordinates[1] = "y";
217  if (N > 2) coordinates[2] = "z";
218  if (N > 3) coordinates[3] = "w";
219  GMM_ASSERT1(N <= 4, "Complete the definition for contact in "
220  "dimension greater than 4");
221  }
222 
223  multi_contact_frame::multi_contact_frame(const model &mdd, size_type NN,
224  scalar_type r_dist,
225  bool dela, bool selfc,
226  scalar_type cut_a,
227  bool rayt, int nmode, bool refc)
228  : N(NN), self_contact(selfc), ref_conf(refc),
229  use_delaunay(dela), nodes_mode(nmode), raytrace(rayt),
230  release_distance(r_dist), cut_angle(cut_a), EPS(1E-8), md(&mdd),
231  coordinates(N), pt(N) {
232  if (N > 0) coordinates[0] = "x";
233  if (N > 1) coordinates[1] = "y";
234  if (N > 2) coordinates[2] = "z";
235  if (N > 3) coordinates[3] = "w";
236  GMM_ASSERT1(N <= 4, "Complete the definition for contact in "
237  "dimension greater than 4");
238  }
239 
240  size_type multi_contact_frame::add_obstacle(const std::string &obs) {
241  size_type ind = obstacles.size();
242  obstacles.push_back(obs);
243  obstacles_velocities.push_back("");
244  obstacles_gw.push_back(ga_workspace());
245  pt.resize(N); ptx.resize(1); pty.resize(1); ptz.resize(1); ptw.resize(1);
246  obstacles_gw.back().add_fixed_size_constant("X", pt);
247  switch(N) {
248  default:
249  case 4: obstacles_gw.back().add_fixed_size_constant("w", ptw);
250  case 3: obstacles_gw.back().add_fixed_size_constant("z", ptz);
251  case 2: obstacles_gw.back().add_fixed_size_constant("y", pty);
252  case 1: obstacles_gw.back().add_fixed_size_constant("x", ptx);
253  }
254  obstacles_f.push_back(ga_function(obstacles_gw.back(), obs));
255  obstacles_f.back().compile();
256  return ind;
257  }
258 
259 
260 
261  size_type multi_contact_frame::add_master_boundary
262  (const mesh_im &mim, const mesh_fem *mfu,
263  const model_real_plain_vector *U, size_type reg,
264  const mesh_fem *mflambda, const model_real_plain_vector *lambda,
265  const model_real_plain_vector *w,
266  const std::string &vvarname,
267  const std::string &mmultname, const std::string &wname) {
268  GMM_ASSERT1(mfu->linked_mesh().dim() == N,
269  "Mesh dimension is " << mfu->linked_mesh().dim()
270  << "should be " << N << ".");
271  GMM_ASSERT1(&(mfu->linked_mesh()) == &(mim.linked_mesh()),
272  "Integration and finite element are not on the same mesh !");
273  if (mflambda)
274  GMM_ASSERT1(&(mflambda->linked_mesh()) == &(mim.linked_mesh()),
275  "Integration and finite element are not on the same mesh !");
276  contact_boundary cb(reg, mfu, mim, add_U(U, vvarname, w, wname),
277  mflambda, add_lambda(lambda, mmultname));
278  contact_boundaries.push_back(cb);
279  return size_type(contact_boundaries.size() - 1);
280  }
281 
282  size_type multi_contact_frame::add_slave_boundary
283  (const mesh_im &mim, const mesh_fem *mfu,
284  const model_real_plain_vector *U, size_type reg,
285  const mesh_fem *mflambda, const model_real_plain_vector *lambda,
286  const model_real_plain_vector *w,
287  const std::string &vvarname,
288  const std::string &mmultname, const std::string &wname) {
289  size_type ind
290  = add_master_boundary(mim, mfu, U, reg, mflambda, lambda, w,
291  vvarname, mmultname, wname);
292  contact_boundaries[ind].slave = true;
293  return ind;
294  }
295 
296 
297  size_type multi_contact_frame::add_master_boundary
298  (const mesh_im &mim, size_type reg, const std::string &vvarname,
299  const std::string &mmultname, const std::string &wname) {
300  GMM_ASSERT1(md, "This multi contact frame object is not linked "
301  "to a model");
302  const mesh_fem *mfl(0);
303  const model_real_plain_vector *l(0);
304  if (mmultname.size()) {
305  mfl = &(md->mesh_fem_of_variable(mmultname));
306  l = &(md->real_variable(mmultname));
307  }
308  const model_real_plain_vector *w(0);
309  if (wname.compare(vvarname) == 0) {
310  GMM_ASSERT1(md->n_iter_of_variable(vvarname) > 1, "More than one "
311  "versions of the displacement variable were expected here");
312  w = &(md->real_variable(vvarname,1));
313  }
314  else if (wname.size()) {
315  GMM_ASSERT1(&(md->mesh_fem_of_variable(wname))
316  == &(md->mesh_fem_of_variable(vvarname)), "The previous "
317  "displacement should be defined on the same mesh_fem as the "
318  "current one");
319  w = &(md->real_variable(wname));
320  }
321  return add_master_boundary(mim, &(md->mesh_fem_of_variable(vvarname)),
322  &(md->real_variable(vvarname)), reg, mfl, l, w,
323  vvarname, mmultname, wname);
324  }
325 
326  size_type multi_contact_frame::add_slave_boundary
327  (const mesh_im &mim, size_type reg, const std::string &vvarname,
328  const std::string &mmultname, const std::string &wname) {
329  GMM_ASSERT1(md, "This multi contact frame object is not linked "
330  "to a model");
331  const mesh_fem *mfl(0);
332  const model_real_plain_vector *l(0);
333  if (mmultname.size()) {
334  mfl = &(md->mesh_fem_of_variable(mmultname));
335  l = &(md->real_variable(mmultname));
336  }
337  const model_real_plain_vector *w(0);
338  if (wname.compare(vvarname) == 0) {
339  GMM_ASSERT1(md->n_iter_of_variable(vvarname) > 1, "More than one "
340  "versions of the displacement variable were expected here");
341  w = &(md->real_variable(vvarname,1));
342  }
343  else if (wname.size()) {
344  GMM_ASSERT1(&(md->mesh_fem_of_variable(wname))
345  == &(md->mesh_fem_of_variable(vvarname)), "The previous "
346  "displacement should be defined on the same mesh_fem as the "
347  "current one");
348  w = &(md->real_variable(wname));
349  }
350  return add_slave_boundary(mim, &(md->mesh_fem_of_variable(vvarname)),
351  &(md->real_variable(vvarname)), reg, mfl, l, w,
352  vvarname, mmultname, wname);
353  }
354 
355 
356  void multi_contact_frame::compute_boundary_points(bool slave_only) {
357  fem_precomp_pool fppool;
358  base_matrix G;
359  model_real_plain_vector coeff;
360 
361  for (size_type i = 0; i < contact_boundaries.size(); ++i)
362  if (!slave_only || is_slave_boundary(i)) {
363  size_type bnum = region_of_boundary(i);
364  const mesh_fem &mfu = mfdisp_of_boundary(i);
365  const mesh_im &mim = mim_of_boundary(i);
366  const model_real_plain_vector &U = disp_of_boundary(i);
367  const mesh &m = mfu.linked_mesh();
368  bool on_fem_nodes =
369  boundary_has_fem_nodes(is_slave_boundary(i), nodes_mode);
370 
371  base_node val(N), bmin(N), bmax(N);
372  base_small_vector n0(N), n(N), n_mean(N);
373  base_matrix grad(N,N);
374  mesh_region region = m.region(bnum);
375  GMM_ASSERT1(mfu.get_qdim() == N, "Wrong mesh_fem qdim");
376 
377 
378  dal::bit_vector dof_already_interpolated;
379  std::vector<size_type> dof_ind(mfu.nb_basic_dof());
380  for (getfem::mr_visitor v(region,m); !v.finished(); ++v) {
381  size_type cv = v.cv();
382  bgeot::pgeometric_trans pgt = m.trans_of_convex(cv);
383  pfem pf_s = mfu.fem_of_element(cv);
384 
385  if (!ref_conf)
386  slice_vector_on_basic_dof_of_element(mfu, U, cv, coeff);
387  mfu.linked_mesh().points_of_convex(cv, G);
388 
389  pfem_precomp pfp(0);
390  size_type nbptf(0);
391  std::vector<size_type> indpt, indpfp;
392  if (on_fem_nodes) {
393  dim_type qqdim = mfu.get_qdim() / pf_s->target_dim();
394  pfp = fppool(pf_s, pf_s->node_tab(cv));
395  nbptf = pf_s->node_convex(cv).structure()->nb_points_of_face(v.f());
396  indpt.resize(nbptf); indpfp.resize(nbptf);
397  for (short_type ip = 0; ip < nbptf; ++ip) {
398  indpt[ip] =
399  mfu.ind_basic_dof_of_face_of_element(cv,v.f())[ip*qqdim];
400  indpfp[ip] =
401  pf_s->node_convex(cv).structure()->ind_points_of_face(v.f())[ip];
402  }
403  }
404  else {
405  pintegration_method pim = mim.int_method_of_element(cv);
406  GMM_ASSERT1(pim, "Integration method should be defined");
407  pfp = fppool(pf_s, pim->approx_method()->pintegration_points());
408  nbptf = pim->approx_method()->nb_points_on_face(v.f());
409  indpt.resize(nbptf); indpfp.resize(nbptf);
410  for (short_type ip = 0; ip < nbptf; ++ip)
411  indpt[ip] = indpfp[ip] =
412  pim->approx_method()->ind_first_point_on_face(v.f())+ip;
413  }
414  fem_interpolation_context ctx(pgt,pfp,size_type(-1),G,cv,v.f());
415 
416  for (short_type ip = 0; ip < nbptf; ++ip) {
417  ctx.set_ii(indpfp[ip]);
418 
419  size_type ind = indpt[ip];
420  if (!(on_fem_nodes && dof_already_interpolated[ind])) {
421  if (!ref_conf) {
422  pf_s->interpolation(ctx, coeff, val, dim_type(N));
423  val += ctx.xreal();
424  } else {
425  val = ctx.xreal();
426  }
427  if (on_fem_nodes) dof_ind[ind] = boundary_points.size();
428 
429  }
430 
431  // unit normal vector computation
432  compute_normal(ctx, v.f(), ref_conf, coeff, n0, n, grad);
433  n /= gmm::vect_norm2(n);
434 
435  if (on_fem_nodes && dof_already_interpolated[ind]) {
436  boundary_points_info[dof_ind[ind]].normals.add_normal(n);
437  } else {
438  boundary_points.push_back(val);
439  boundary_points_info.push_back(boundary_point(ctx.xreal(), i, cv,
440  v.f(), ind, n));
441  }
442 
443  if (on_fem_nodes) dof_already_interpolated.add(ind);
444  }
445  }
446  }
447  }
448 
449  void multi_contact_frame::compute_potential_contact_pairs_delaunay() {
450 
451  compute_boundary_points();
452  normal_cone_simplification();
453  potential_pairs = std::vector<std::vector<face_info> >();
454  potential_pairs.resize(boundary_points.size());
455 
456  gmm::dense_matrix<size_type> simplexes;
457  base_small_vector rr(N);
458  // Necessary ?
459  // for (size_type i = 0; i < boundary_points.size(); ++i) {
460  // gmm::fill_random(rr);
461  // boundary_points[i] += 1E-9*rr;
462  // }
463  bgeot::qhull_delaunay(boundary_points, simplexes);
464 
465  // connectivity analysis
466  for (size_type is = 0; is < gmm::mat_ncols(simplexes); ++is) {
467 
468  for (size_type i = 1; i <= N; ++i)
469  for (size_type j = 0; j < i; ++j) {
470  size_type ipt1 = simplexes(i, is), ipt2 = simplexes(j, is);
471  boundary_point *pt_info1 = &(boundary_points_info[ipt1]);
472  boundary_point *pt_info2 = &(boundary_points_info[ipt2]);
473  size_type ib1 = pt_info1->ind_boundary;
474  size_type ib2 = pt_info2->ind_boundary;
475  bool sl1 = is_slave_boundary(ib1);
476  bool sl2 = is_slave_boundary(ib2);
477  if (!sl1 && sl2) { // The slave in first if any
478  std::swap(ipt1, ipt2);
479  std::swap(pt_info1, pt_info2);
480  std::swap(ib1, ib2);
481  std::swap(sl1, sl2);
482  }
483  size_type ir1 = region_of_boundary(ib1);
484  size_type ir2 = region_of_boundary(ib2);
485  const mesh_fem &mf1 = mfdisp_of_boundary(ib1);
486  const mesh_fem &mf2 = mfdisp_of_boundary(ib2);
487 
488  // CRITERION 1 : The unit normal cone / vector are compatible
489  // and the two points are not in the same element.
490  if (
491  // slave-master case
492  ((sl1 && !sl2)
493  // master-master self-contact case
494  || (self_contact && !sl1 && !sl2))
495  // test of unit normal vectors or cones
496  && test_normal_cones_compatibility(pt_info1->normals,
497  pt_info2->normals)
498  // In case of self-contact, test if the two points share the
499  // same element.
500  && (sl1
501  || ((nodes_mode < 2)
502  && (( &(mf1.linked_mesh()) != &(mf2.linked_mesh()))
503  || (pt_info1->ind_element != pt_info2->ind_element)))
504  || ((nodes_mode == 2)
505  && !(are_dof_linked(ib1, pt_info1->ind_pt,
506  ib2, pt_info2->ind_pt)))
507  )
508  ) {
509 
510  // Store the potential contact pairs
511 
512  if (boundary_has_fem_nodes(sl2, nodes_mode)) {
513  const mesh::ind_cv_ct &ic2
514  = mf2.convex_to_basic_dof(pt_info2->ind_pt);
515  for (size_type k = 0; k < ic2.size(); ++k) {
516  mesh_region::face_bitset fbs
517  = mf2.linked_mesh().region(ir2).faces_of_convex(ic2[k]);
518  short_type nbf = mf2.linked_mesh().nb_faces_of_convex(ic2[k]);
519  for (short_type f = 0; f < nbf; ++f)
520  if (fbs.test(f))
521  add_potential_contact_face(ipt1,
522  pt_info2->ind_boundary,
523  ic2[k], f);
524  }
525  } else
526  add_potential_contact_face(ipt1, pt_info2->ind_boundary,
527  pt_info2->ind_element,
528  pt_info2->ind_face);
529 
530  if (self_contact && !sl1 && !sl2) {
531  if (boundary_has_fem_nodes(sl2, nodes_mode)) {
532  const mesh::ind_cv_ct &ic1
533  = mf1.convex_to_basic_dof(pt_info1->ind_pt);
534  for (size_type k = 0; k < ic1.size(); ++k) {
535  mesh_region::face_bitset fbs
536  = mf1.linked_mesh().region(ir1).faces_of_convex(ic1[k]);
537  short_type nbf = mf1.linked_mesh().nb_faces_of_convex(ic1[k]);
538  for (short_type f = 0; f < nbf; ++f)
539  if (fbs.test(f))
540  add_potential_contact_face(ipt2,
541  pt_info1->ind_boundary,
542  ic1[k], f);
543  }
544  } else
545  add_potential_contact_face(ipt2, pt_info1->ind_boundary,
546  pt_info1->ind_element,
547  pt_info1->ind_face);
548  }
549 
550  }
551 
552  }
553  }
554  }
555 
556 
557  void multi_contact_frame::compute_influence_boxes() {
558  fem_precomp_pool fppool;
559  bool avert = false;
560  base_matrix G;
561  model_real_plain_vector coeff;
562 
563  for (size_type i = 0; i < contact_boundaries.size(); ++i)
564  if (!is_slave_boundary(i)) {
565  size_type bnum = region_of_boundary(i);
566  const mesh_fem &mfu = mfdisp_of_boundary(i);
567  const model_real_plain_vector &U = disp_of_boundary(i);
568  const mesh &m = mfu.linked_mesh();
569 
570  base_node val(N), bmin(N), bmax(N);
571  base_small_vector n0(N), n(N), n_mean(N);
572  base_matrix grad(N,N);
573  mesh_region region = m.region(bnum);
574  GMM_ASSERT1(mfu.get_qdim() == N, "Wrong mesh_fem qdim");
575 
576  dal::bit_vector points_already_interpolated;
577  std::vector<base_node> transformed_points(m.nb_max_points());
578  for (getfem::mr_visitor v(region,m); !v.finished(); ++v) {
579  size_type cv = v.cv();
580  bgeot::pgeometric_trans pgt = m.trans_of_convex(cv);
581  pfem pf_s = mfu.fem_of_element(cv);
582  pfem_precomp pfp = fppool(pf_s, pgt->pgeometric_nodes());
583  if (!ref_conf)
584  slice_vector_on_basic_dof_of_element(mfu, U, cv, coeff);
585  mfu.linked_mesh().points_of_convex(cv, G);
586  fem_interpolation_context ctx(pgt,pfp,size_type(-1), G, cv);
587 
588  size_type nb_pt_on_face = 0;
589  dal::bit_vector points_on_face;
590  bgeot::pconvex_structure cvs = pgt->structure();
591  for (size_type k = 0; k < cvs->nb_points_of_face(v.f()); ++k)
592  points_on_face.add(cvs->ind_points_of_face(v.f())[k]);
593 
594  gmm::clear(n_mean);
595  size_type nbd_t = pgt->nb_points();
596  for (short_type ip = 0; ip < nbd_t; ++ip) {
597  size_type ind = m.ind_points_of_convex(cv)[ip];
598 
599  // computation of transformed vertex
600  ctx.set_ii(ip);
601  if (!(points_already_interpolated.is_in(ind))) {
602  if (!ref_conf) {
603  pf_s->interpolation(ctx, coeff, val, dim_type(N));
604  val += ctx.xreal();
605  transformed_points[ind] = val;
606  } else {
607  transformed_points[ind] = ctx.xreal();
608  }
609  points_already_interpolated.add(ind);
610  } else {
611  val = transformed_points[ind];
612  }
613  // computation of unit normal vector if the vertex is on the face
614  if (points_on_face[ip]) {
615  compute_normal(ctx, v.f(), ref_conf, coeff, n0, n, grad);
616  n /= gmm::vect_norm2(n);
617  n_mean += n;
618  ++nb_pt_on_face;
619  }
620 
621  if (ip == 0) // computation of bounding box
622  bmin = bmax = val;
623  else {
624  for (size_type k = 0; k < N; ++k) {
625  bmin[k] = std::min(bmin[k], val[k]);
626  bmax[k] = std::max(bmax[k], val[k]);
627  }
628  }
629 
630  }
631 
632  // is nb_pt_on_face really necessary, is this possible to occur?
633  GMM_ASSERT1(nb_pt_on_face,
634  "This element has no vertex on considered face !");
635 
636  // Computation of influence box :
637  // offset of the bounding box relatively to the release distance
638  scalar_type h = bmax[0] - bmin[0];
639  for (size_type k = 1; k < N; ++k) h = std::max(h, bmax[k]-bmin[k]);
640  if (h < release_distance/scalar_type(40) && !avert) {
641  GMM_WARNING1("Found an element whose size is smaller than 1/40 "
642  "of the release distance. You should probably "
643  "adapt the release distance.");
644  avert = true;
645  }
646  for (size_type k = 0; k < N; ++k)
647  { bmin[k] -= release_distance; bmax[k] += release_distance; }
648 
649  // Store the influence box and additional information.
650  element_boxes.add_box(bmin, bmax, element_boxes_info.size());
651  n_mean /= gmm::vect_norm2(n_mean);
652  element_boxes_info.push_back(influence_box(i, cv, v.f(), n_mean));
653  }
654  }
655  }
656 
657  void multi_contact_frame::compute_potential_contact_pairs_influence_boxes() {
658  compute_influence_boxes();
659  compute_boundary_points(!self_contact); // vraiment necessaire ?
660  normal_cone_simplification();
661  potential_pairs = std::vector<std::vector<face_info> >();
662  potential_pairs.resize(boundary_points.size());
663 
664  for (size_type ip = 0; ip < boundary_points.size(); ++ip) {
665 
666  bgeot::rtree::pbox_set bset;
667  element_boxes.find_boxes_at_point(boundary_points[ip], bset);
668  boundary_point *pt_info = &(boundary_points_info[ip]);
669  const mesh_fem &mf1 = mfdisp_of_boundary(pt_info->ind_boundary);
670  size_type ib1 = pt_info->ind_boundary;
671 
672  bgeot::rtree::pbox_set::iterator it = bset.begin();
673  for (; it != bset.end(); ++it) {
674  influence_box &ibx = element_boxes_info[(*it)->id];
675  size_type ib2 = ibx.ind_boundary;
676  const mesh_fem &mf2 = mfdisp_of_boundary(ib2);
677 
678  // CRITERION 1 : The unit normal cone / vector are compatible
679  // and the two points are not in the same element.
680  if (
681  test_normal_cones_compatibility(ibx.mean_normal,
682  pt_info->normals)
683  // In case of self-contact, test if the points and the face
684  // share the same element.
685  && (((nodes_mode < 2)
686  && (( &(mf1.linked_mesh()) != &(mf2.linked_mesh()))
687  || (pt_info->ind_element != ibx.ind_element)))
688  || ((nodes_mode == 2)
689  && !(is_dof_linked(ib1, pt_info->ind_pt,
690  ibx.ind_boundary, ibx.ind_element)))
691  )
692  ) {
693 
694  add_potential_contact_face(ip, ibx.ind_boundary, ibx.ind_element,
695  ibx.ind_face);
696  }
697  }
698 
699  }
700  }
701 
702  struct proj_pt_surf_cost_function_object {
703  size_type N;
704  scalar_type EPS;
705  const base_node &x0, &x;
706  fem_interpolation_context &ctx;
707  const model_real_plain_vector &coeff;
708  const std::vector<base_small_vector> &ti;
709  bool ref_conf;
710  mutable base_node dxy;
711  mutable base_matrix grad, gradtot;
712 
713  scalar_type operator()(const base_small_vector& a) const {
714  base_node xx = x0;
715  for (size_type i= 0; i < N-1; ++i) xx += a[i] * ti[i];
716  ctx.set_xref(xx);
717  if (!ref_conf) {
718  ctx.pf()->interpolation(ctx, coeff, dxy, dim_type(N));
719  dxy += ctx.xreal() - x;
720  } else
721  dxy = ctx.xreal() - x;
722  return gmm::vect_norm2(dxy)/scalar_type(2);
723  }
724 
725  scalar_type operator()(const base_small_vector& a,
726  base_small_vector &grada) const {
727  base_node xx = x0;
728  for (size_type i = 0; i < N-1; ++i) xx += a[i] * ti[i];
729  ctx.set_xref(xx);
730  if (!ref_conf) {
731  ctx.pf()->interpolation(ctx, coeff, dxy, dim_type(N));
732  dxy += ctx.xreal() - x;
733  ctx.pf()->interpolation_grad(ctx, coeff, grad, dim_type(N));
734  gmm::add(gmm::identity_matrix(), grad);
735  gmm::mult(grad, ctx.K(), gradtot);
736  } else {
737  dxy = ctx.xreal() - x;
738  gmm::copy(ctx.K(), gradtot);
739  }
740  for (size_type i = 0; i < N-1; ++i)
741  grada[i] = gmm::vect_sp(gradtot, ti[i], dxy);
742  return gmm::vect_norm2(dxy)/scalar_type(2);
743  }
744  void operator()(const base_small_vector& a,
745  base_matrix &hessa) const {
746  base_small_vector b = a;
747  base_small_vector grada(N-1), gradb(N-1);
748  (*this)(b, grada);
749  for (size_type i = 0; i < N-1; ++i) {
750  b[i] += EPS;
751  (*this)(b, gradb);
752  for (size_type j = 0; j < N-1; ++j)
753  hessa(j, i) = (gradb[j] - grada[j])/EPS;
754  b[i] -= EPS;
755  }
756  }
757 
758  proj_pt_surf_cost_function_object
759  (const base_node &x00, const base_node &xx,
760  fem_interpolation_context &ctxx,
761  const model_real_plain_vector &coefff,
762  const std::vector<base_small_vector> &tii,
763  scalar_type EPSS, bool rc)
764  : N(gmm::vect_size(x00)), EPS(EPSS), x0(x00), x(xx),
765  ctx(ctxx), coeff(coefff), ti(tii), ref_conf(rc),
766  dxy(N), grad(N,N), gradtot(N,N) {}
767 
768  };
769 
770  struct raytrace_pt_surf_cost_function_object {
771  size_type N;
772  const base_node &x0, &x;
773  fem_interpolation_context &ctx;
774  const model_real_plain_vector &coeff;
775  const std::vector<base_small_vector> &ti;
776  const std::vector<base_small_vector> &Ti;
777  bool ref_conf;
778  mutable base_node dxy;
779  mutable base_matrix grad, gradtot;
780 
781  void operator()(const base_small_vector& a,
782  base_small_vector &res) const {
783  base_node xx = x0;
784  for (size_type i = 0; i < N-1; ++i) xx += a[i] * ti[i];
785  ctx.set_xref(xx);
786  if (!ref_conf) {
787  ctx.pf()->interpolation(ctx, coeff, dxy, dim_type(N));
788  dxy += ctx.xreal() - x;
789  } else
790  dxy = ctx.xreal() - x;
791  for (size_type i = 0; i < N-1; ++i)
792  res[i] = gmm::vect_sp(dxy, Ti[i]);
793  }
794 
795  void operator()(const base_small_vector& a,
796  base_matrix &hessa) const {
797  base_node xx = x0;
798  for (size_type i = 0; i < N-1; ++i) xx += a[i] * ti[i];
799  ctx.set_xref(xx);
800  if (!ref_conf) {
801  ctx.pf()->interpolation_grad(ctx, coeff, grad, dim_type(N));
802  gmm::add(gmm::identity_matrix(), grad);
803  gmm::mult(grad, ctx.K(), gradtot);
804  } else {
805  gmm::copy(ctx.K(), gradtot);
806  }
807  for (size_type i = 0; i < N-1; ++i)
808  for (size_type j = 0; j < N-1; ++j)
809  hessa(j, i) = gmm::vect_sp(gradtot, ti[i], Ti[j]);
810  }
811 
812 
813  raytrace_pt_surf_cost_function_object
814  (const base_node &x00, const base_node &xx,
815  fem_interpolation_context &ctxx,
816  const model_real_plain_vector &coefff,
817  const std::vector<base_small_vector> &tii,
818  const std::vector<base_small_vector> &Tii,
819  bool rc)
820  : N(gmm::vect_size(x00)), x0(x00), x(xx),
821  ctx(ctxx), coeff(coefff), ti(tii), Ti(Tii), ref_conf(rc),
822  dxy(N), grad(N,N), gradtot(N,N) {}
823 
824  };
825 
826  // Ideas to improve efficiency :
827  // - From an iteration to another, is it possible to simplify the
828  // computation ? For instance in testing the old contact pairs ...
829  // But how to detect new contact situations ?
830  // - A pre-test before projection (for Delaunay) : if the distance to a
831  // node is greater than the release distance + h then give up.
832  // - Case J3 of valid/invalid contact situations is not really taken into
833  // account. How to take it into account in a cheap way ?
834 
835  void multi_contact_frame::compute_contact_pairs() {
836  base_matrix G, grad(N,N);
837  model_real_plain_vector coeff;
838  base_small_vector a(N-1), ny(N);
839  base_node y(N);
840  std::vector<base_small_vector> ti(N-1), Ti(N-1);
841  size_type nbwarn(0);
842 
843  // double time = dal::uclock_sec();
844 
845  clear_aux_info();
846  contact_pairs = std::vector<contact_pair>();
847 
848  if (!ref_conf) extend_vectors();
849 
850  bool only_slave(true), only_master(true);
851  for (size_type i = 0; i < contact_boundaries.size(); ++i)
852  if (is_slave_boundary(i)) only_master = false;
853  else only_slave = false;
854 
855  if (only_master && !self_contact) {
856  GMM_WARNING1("There is only master boundary and no self-contact to detect. Exiting");
857  return;
858  }
859 
860  if (only_slave) {
861  compute_boundary_points();
862  potential_pairs.resize(boundary_points.size());
863  }
864  else if (use_delaunay)
865  compute_potential_contact_pairs_delaunay();
866  else
867  compute_potential_contact_pairs_influence_boxes();
868 
869  // cout << "Time for computing potential pairs: " << dal::uclock_sec() - time << endl; time = dal::uclock_sec();
870 
871 
872  // Scan of potential pairs
873  for (size_type ip = 0; ip < potential_pairs.size(); ++ip) {
874  bool first_pair_found = false;
875  const base_node &x = boundary_points[ip];
876  boundary_point &bpinfo = boundary_points_info[ip];
877  size_type ibx = bpinfo.ind_boundary;
878  bool slx = is_slave_boundary(ibx);
879  scalar_type d0 = 1E300, d1, d2;
880 
881  base_small_vector nx = bpinfo.normals[0];
882  if (raytrace) {
883  if (bpinfo.normals.size() > 1) { // take the mean normal vector
884  for (size_type i = 1; i < bpinfo.normals.size(); ++i)
885  gmm::add(bpinfo.normals[i], nx);
886  scalar_type nnx = gmm::vect_norm2(nx);
887  GMM_ASSERT1(nnx != scalar_type(0), "Invalid normal cone");
888  gmm::scale(nx, scalar_type(1)/nnx);
889  }
890  }
891 
892  if (self_contact || slx) {
893  // Detect here the nearest rigid obstacle (taking into account
894  // the release distance)
895  size_type irigid_obstacle(-1);
896  gmm::copy(x, pt);
897  switch(N) {
898  default:
899  case 4: ptw[0] = pt[3];
900  case 3: ptz[0] = pt[2];
901  case 2: pty[0] = pt[1];
902  case 1: ptx[0] = pt[0];
903  }
904  for (size_type i = 0; i < obstacles.size(); ++i) {
905  d1 = (obstacles_f[i].eval())[0];
906  if (gmm::abs(d1) < release_distance && d1 < d0) {
907 
908  for (size_type j=0; j < bpinfo.normals.size(); ++j) {
909  gmm::add(gmm::scaled(bpinfo.normals[j], EPS), pt);
910  switch(N) {
911  default:
912  case 4: ptw[0] = pt[3];
913  case 3: ptz[0] = pt[2];
914  case 2: pty[0] = pt[1];
915  case 1: ptx[0] = pt[0];
916  }
917  d2 = (obstacles_f[i].eval())[0];
918  if (d2 < d1) { d0 = d1; irigid_obstacle = i; break; }
919  gmm::copy(x, pt);
920  switch(N) {
921  default:
922  case 4: ptw[0] = pt[3];
923  case 3: ptz[0] = pt[2];
924  case 2: pty[0] = pt[1];
925  case 1: ptx[0] = pt[0];
926  }
927  }
928  }
929  }
930 
931  if (irigid_obstacle != size_type(-1)) {
932 
933  gmm::copy(x, pt);
934  switch(N) {
935  default:
936  case 4: ptw[0] = pt[3];
937  case 3: ptz[0] = pt[2];
938  case 2: pty[0] = pt[1];
939  case 1: ptx[0] = pt[0];
940  }
941  gmm::copy(x, y);
942  size_type nit = 0, nb_fail = 0;
943  scalar_type alpha(0), beta(0);
944  d1 = d0;
945 
946  while (++nit < 50 && nb_fail < 3) {
947  for (size_type k = 0; k < N; ++k) {
948  pt[k] += EPS;
949  switch(N) {
950  case 4: ptw[0] += EPS; break;
951  case 3: ptz[0] += EPS; break;
952  case 2: pty[0] += EPS; break;
953  case 1: ptx[0] += EPS; break;
954  }
955  d2 = (obstacles_f[irigid_obstacle].eval())[0];
956  ny[k] = (d2 - d1) / EPS;
957  pt[k] -= EPS;
958  switch(N) {
959  case 4: ptw[0] -= EPS; break;
960  case 3: ptz[0] -= EPS; break;
961  case 2: pty[0] -= EPS; break;
962  case 1: ptx[0] -= EPS; break;
963  }
964  }
965 
966  if (gmm::abs(d1) < 1E-13)
967  break; // point already lies on the rigid obstacle surface
968 
969  // ajouter un test de divergence ...
970  for (scalar_type lambda(1); lambda >= 1E-3; lambda /= scalar_type(2)) {
971  if (raytrace) {
972  alpha = beta - lambda * d1 / gmm::vect_sp(ny, nx);
973  gmm::add(x, gmm::scaled(nx, alpha), pt);
974  } else {
975  gmm::add(gmm::scaled(ny, -d1/gmm::vect_norm2_sqr(ny)), y, pt);
976  }
977  switch(N) {
978  default:
979  case 4: ptw[0] = pt[3];
980  case 3: ptz[0] = pt[2];
981  case 2: pty[0] = pt[1];
982  case 1: ptx[0] = pt[0];
983  }
984  d2 = (obstacles_f[irigid_obstacle].eval())[0];
985 // if (nit > 10)
986 // cout << "nit = " << nit << " lambda = " << lambda
987 // << " alpha = " << alpha << " d2 = " << d2
988 // << " d1 = " << d1 << endl;
989  if (gmm::abs(d2) < gmm::abs(d1)) break;
990  }
991  if (raytrace &&
992  gmm::abs(beta - d1 / gmm::vect_sp(ny, nx)) > scalar_type(500))
993  nb_fail++;
994  gmm::copy(pt, y); beta = alpha; d1 = d2;
995  }
996 
997  if (gmm::abs(d1) > 1E-8) {
998  GMM_WARNING1("Projection/raytrace on rigid obstacle failed");
999  continue;
1000  }
1001 
1002  // CRITERION 4 for rigid bodies : Apply the release distance
1003  if (gmm::vect_dist2(y, x) > release_distance)
1004  continue;
1005 
1006  gmm::copy(pt, y);
1007  ny /= gmm::vect_norm2(ny);
1008 
1009  d0 = gmm::vect_dist2(y, x) * gmm::sgn(d0);
1010  contact_pair ct(x, nx, bpinfo, y, ny, irigid_obstacle, d0);
1011 
1012  contact_pairs.push_back(ct);
1013  first_pair_found = true;
1014  }
1015  }
1016 
1017  // if (potential_pairs[ip].size())
1018  // cout << "number of potential pairs for point " << ip << " : " << potential_pairs[ip].size() << endl;
1019  for (size_type ipf = 0; ipf < potential_pairs[ip].size(); ++ipf) {
1020  // Point to surface projection. Principle :
1021  // - One parametrizes first the face on the reference element by
1022  // obtaining a point x_0 on that face and t_i, i=1..d-1 some
1023  // orthonormals tangent vectors to the face.
1024  // - Let y_0 be the point to be projected and y the searched
1025  // projected point. Then one searches for the minimum of
1026  // J = (1/2)|| y - x ||
1027  // with
1028  // y = \phi(x0 + a_i t_i)
1029  // (with a summation on i), where \phi = I+u(\tau(x)), and \tau
1030  // the geometric transformation between reference and real
1031  // elements.
1032  // - The gradient of J with respect to a_i is
1033  // \partial_{a_j} J = (\phi(x0 + a_i t_i) - x)
1034  // . (\nabla \phi(x0 + a_i t_i) t_j
1035  // - A Newton algorithm is applied.
1036  // - If it fails, a BFGS is called.
1037 
1038  const face_info &fi = potential_pairs[ip][ipf];
1039  size_type ib = fi.ind_boundary;
1040  size_type cv = fi.ind_element;
1041  short_type iff = fi.ind_face;
1042 
1043  const mesh_fem &mfu = mfdisp_of_boundary(ib);
1044  const mesh &m = mfu.linked_mesh();
1045  pfem pf_s = mfu.fem_of_element(cv);
1046  bgeot::pgeometric_trans pgt = m.trans_of_convex(cv);
1047 
1048  if (!ref_conf)
1049  slice_vector_on_basic_dof_of_element(mfu, disp_of_boundary(ib),
1050  cv, coeff);
1051 
1052  m.points_of_convex(cv, G);
1053 
1054  const base_node &x0 = pf_s->ref_convex(cv)->points_of_face(iff)[0];
1055  fem_interpolation_context ctx(pgt, pf_s, x0, G, cv, iff);
1056 
1057  const base_small_vector &n0 = pf_s->ref_convex(cv)->normals()[iff];
1058  for (size_type k = 0; k < N-1; ++k) { // A basis for the face
1059  gmm::resize(ti[k], N);
1060  scalar_type norm(0);
1061  while(norm < 1E-5) {
1062  gmm::fill_random(ti[k]);
1063  ti[k] -= gmm::vect_sp(ti[k], n0) * n0;
1064  for (size_type l = 0; l < k; ++l)
1065  ti[k] -= gmm::vect_sp(ti[k], ti[l]) * ti[l];
1066  norm = gmm::vect_norm2(ti[k]);
1067  }
1068  ti[k] /= norm;
1069  }
1070 
1071  bool converged = false;
1072  scalar_type residual(0);
1073 
1074 
1075  if (raytrace) { // Raytrace search for y by a Newton algorithm
1076 
1077  base_small_vector res(N-1), res2(N-1), dir(N-1), b(N-1);
1078 
1079  base_matrix hessa(N-1, N-1);
1080  gmm::clear(a);
1081 
1082  for (size_type k = 0; k < N-1; ++k) {
1083  gmm::resize(Ti[k], N);
1084  scalar_type norm(0);
1085  while (norm < 1E-5) {
1086  gmm::fill_random(Ti[k]);
1087  Ti[k] -= gmm::vect_sp(Ti[k], nx) * nx;
1088  for (size_type l = 0; l < k; ++l)
1089  Ti[k] -= gmm::vect_sp(Ti[k], Ti[l]) * Ti[l];
1090  norm = gmm::vect_norm2(Ti[k]);
1091  }
1092  Ti[k] /= norm;
1093  }
1094 
1095  raytrace_pt_surf_cost_function_object pps(x0, x, ctx, coeff, ti, Ti,
1096  ref_conf);
1097 
1098  pps(a, res);
1099  residual = gmm::vect_norm2(res);
1100  scalar_type residual2(0), det(0);
1101  bool exited = false;
1102  size_type nbfail = 0, niter = 0;
1103  for (;residual > 2E-12 && niter <= 30; ++niter) {
1104 
1105  for (size_type subiter(0);;) {
1106  pps(a, hessa);
1107  det = gmm::abs(bgeot::lu_inverse(&(*(hessa.begin())),N-1, false));
1108  if (det > 1E-15) break;
1109  for (size_type i = 0; i < N-1; ++i)
1110  a[i] += gmm::random() * 1E-7;
1111  if (++subiter > 4) break;
1112  }
1113  if (det <= 1E-15) break;
1114  // Computation of the descent direction
1115  gmm::mult(hessa, gmm::scaled(res, scalar_type(-1)), dir);
1116 
1117  if (gmm::vect_norm2(dir) > scalar_type(10)) nbfail++;
1118  if (nbfail >= 4) break;
1119 
1120  // Line search
1121  scalar_type lambda(1);
1122  for (size_type j = 0; j < 5; ++j) {
1123  gmm::add(a, gmm::scaled(dir, lambda), b);
1124  pps(b, res2);
1125  residual2 = gmm::vect_norm2(res2);
1126  if (residual2 < residual) break;
1127  lambda /= ((j < 3) ? scalar_type(2) : scalar_type(5));
1128  }
1129 
1130  residual = residual2;
1131  gmm::copy(res2, res);
1132  gmm::copy(b, a);
1133  scalar_type dist_ref = gmm::vect_norm2(a);
1134 // if (niter == 15)
1135 // cout << "more than 15 iterations " << a
1136 // << " dir " << dir << " nbfail : " << nbfail << endl;
1137  if (niter > 1 && dist_ref > 15) break;
1138  if (niter > 5 && dist_ref > 8) break;
1139  if ((niter > 1 && dist_ref > 7) || nbfail == 3) exited = true;
1140  }
1141  converged = (gmm::vect_norm2(res) < 2E-6);
1142  GMM_ASSERT1(!((exited && converged &&
1143  pf_s->ref_convex(cv)->is_in(ctx.xref()) < 1E-6)),
1144  "A non conformal case !! " << gmm::vect_norm2(res)
1145  << " : " << nbfail << " : " << niter);
1146 
1147  } else { // Classical projection for y
1148 
1149  proj_pt_surf_cost_function_object pps(x0, x, ctx, coeff, ti,
1150  EPS, ref_conf);
1151 
1152  // Projection could be ameliorated by finding a starting point near
1153  // x (with respect to the integration method, for instance).
1154 
1155  // A specific (Quasi) Newton algorithm for computing the projection
1156  base_small_vector grada(N-1), dir(N-1), b(N-1);
1157  gmm::clear(a);
1158  base_matrix hessa(N-1, N-1);
1159  scalar_type det(0);
1160 
1161  scalar_type dist = pps(a, grada);
1162  for (size_type niter = 0;
1163  gmm::vect_norm2(grada) > 1E-12 && niter <= 50; ++niter) {
1164 
1165  for (size_type subiter(0);;) {
1166  pps(a, hessa);
1167  det = gmm::abs(bgeot::lu_inverse(&(*(hessa.begin())),N-1, false));
1168  if (det > 1E-15) break;
1169  for (size_type i = 0; i < N-1; ++i)
1170  a[i] += gmm::random() * 1E-7;
1171  if (++subiter > 4) break;
1172  }
1173  if (det <= 1E-15) break;
1174  // Computation of the descent direction
1175  gmm::mult(hessa, gmm::scaled(grada, scalar_type(-1)), dir);
1176 
1177  // Line search
1178  for (scalar_type lambda(1);
1179  lambda >= 1E-3; lambda /= scalar_type(2)) {
1180  gmm::add(a, gmm::scaled(dir, lambda), b);
1181  if (pps(b) < dist) break;
1182  gmm::add(a, gmm::scaled(dir, -lambda), b);
1183  if (pps(b) < dist) break;
1184  }
1185  gmm::copy(b, a);
1186  dist = pps(a, grada);
1187  }
1188 
1189  converged = (gmm::vect_norm2(grada) < 2E-6);
1190 
1191  if (!converged) { // Try with BFGS
1192  gmm::iteration iter(1E-12, 0 /* noisy*/, 100 /*maxiter*/);
1193  gmm::clear(a);
1194  gmm::bfgs(pps, pps, a, 10, iter, 0, 0.5);
1195  residual = gmm::abs(iter.get_res());
1196  converged = (residual < 2E-5);
1197  }
1198  }
1199 
1200  bool is_in = (pf_s->ref_convex(cv)->is_in(ctx.xref()) < 1E-6);
1201 
1202  if (is_in || (!converged && !raytrace)) {
1203  if (!ref_conf) {
1204  ctx.pf()->interpolation(ctx, coeff, y, dim_type(N));
1205  y += ctx.xreal();
1206  } else {
1207  y = ctx.xreal();
1208  }
1209  }
1210 
1211  // CRITERION 2 : The contact pair is eliminated when
1212  // projection/raytrace do not converge.
1213  if (!converged) {
1214  if (!raytrace && nbwarn < 4) {
1215  GMM_WARNING3("Projection or raytrace algorithm did not converge "
1216  "for point " << x << " residual " << residual
1217  << " projection computed " << y);
1218  ++nbwarn;
1219  }
1220  continue;
1221  }
1222 
1223  // CRITERION 3 : The projected point is inside the element
1224  // The test should be completed: If the point is outside
1225  // the element, a rapid reprojection on the face
1226  // (on the reference element, with a linear algorithm)
1227  // can be applied and a test with a neigbhour element
1228  // to decide if the point is in fact ok ...
1229  // (to be done only if there is no projection on other
1230  // element which coincides and with a test on the
1231  // distance ... ?) To be specified (in this case,
1232  // change xref).
1233  if (!is_in) continue;
1234 
1235  // CRITERION 4 : Apply the release distance
1236  scalar_type signed_dist = gmm::vect_dist2(y, x);
1237  if (signed_dist > release_distance) continue;
1238 
1239  // compute the unit normal vector at y and the signed distance.
1240  base_small_vector ny0(N);
1241  compute_normal(ctx, iff, ref_conf, coeff, ny0, ny, grad);
1242  // ny /= gmm::vect_norm2(ny); // Useful only if the unit normal is kept
1243  signed_dist *= gmm::sgn(gmm::vect_sp(x - y, ny));
1244 
1245  // CRITERION 5 : comparison with rigid obstacles
1246  // CRITERION 7 : smallest signed distance on contact pairs
1247  if (first_pair_found && contact_pairs.back().signed_dist < signed_dist)
1248  continue;
1249 
1250  // CRITERION 1 : again on found unit normal vector
1251  if (!(test_normal_cones_compatibility(ny, bpinfo.normals)))
1252  continue;
1253 
1254  // CRITERION 6 : for self-contact only : apply a test on
1255  // unit normals in reference configuration.
1256  if (&m == &(mfdisp_of_boundary(ibx).linked_mesh())) {
1257 
1258  base_small_vector diff = bpinfo.ref_point - ctx.xreal();
1259  scalar_type ref_dist = gmm::vect_norm2(diff);
1260 
1261  if ( (ref_dist < scalar_type(4) * release_distance)
1262  && (gmm::vect_sp(diff, ny0) < - 0.01 * ref_dist) )
1263  continue;
1264  }
1265 
1266  contact_pair ct(x, nx, bpinfo, ctx.xref(), y, ny, fi, signed_dist);
1267  if (first_pair_found) {
1268  contact_pairs.back() = ct;
1269  } else {
1270  contact_pairs.push_back(ct);
1271  first_pair_found = true;
1272  }
1273 
1274  }
1275  }
1276 
1277  // cout << "Time for computing pairs: " << dal::uclock_sec() - time << endl; time = dal::uclock_sec();
1278 
1279  clear_aux_info();
1280  }
1281 
1282  //=========================================================================
1283  //
1284  // Raytracing interpolate transformation for generic assembly
1285  //
1286  //=========================================================================
1287 
1288  class raytracing_interpolate_transformation
1289  : public virtual_interpolate_transformation {
1290  protected:
1291  // Structure describing a contact boundary
1292  struct contact_boundary {
1293  size_type region; // Boundary number
1294  const getfem::mesh_fem *mfu; // F.e.m. for the displacement.
1295  std::string dispname; // Variable name for the displacement
1296  mutable const model_real_plain_vector *U; // Displacement
1297  mutable model_real_plain_vector U_unred; // Unreduced displacement
1298  bool slave;
1299 
1300  contact_boundary()
1301  : region(-1), mfu(0), dispname(""), U(0), U_unred(0), slave(false) {}
1302  contact_boundary(size_type r, const mesh_fem *mf, const std::string &dn,
1303  bool sl)
1304  : region(r), mfu(mf), dispname(dn), slave(sl) {}
1305  };
1306 
1307  struct face_box_info { // Additional information for a face box
1308  size_type ind_boundary; // Boundary number
1309  size_type ind_element; // Element number
1310  short_type ind_face; // Face number in element
1311  base_small_vector mean_normal; // Mean outward normal unit vector
1312  face_box_info()
1313  : ind_boundary(-1), ind_element(-1), ind_face(-1), mean_normal(0) {}
1314  face_box_info(size_type ib, size_type ie,
1315  short_type iff, const base_small_vector &n)
1316  : ind_boundary(ib), ind_element(ie), ind_face(iff), mean_normal(n) {}
1317  };
1318 
1319  scalar_type release_distance; // Limit distance beyond which the contact
1320  // will not be considered.
1321 
1322  std::vector<contact_boundary> contact_boundaries;
1323  typedef std::map<const mesh *, std::vector<size_type> > mesh_boundary_cor;
1324  mesh_boundary_cor boundary_for_mesh;
1325 
1326  class obstacle {
1327  const model *md;
1328  const ga_workspace *parent_workspace;
1329  std::string expr;
1330 
1331  mutable base_vector X;
1332  mutable ga_function f, der_f;
1333  mutable bool compiled;
1334 
1335  void compile() const {
1336  if (md)
1337  f = ga_function(*md, expr);
1338  else if (parent_workspace)
1339  f = ga_function(*parent_workspace, expr);
1340  else
1341  f = ga_function(expr);
1342  size_type N = gmm::vect_size(X);
1343  f.workspace().add_fixed_size_variable("X", gmm::sub_interval(0, N), X);
1344  if (N >= 1) f.workspace().add_macro("x", "X(1)");
1345  if (N >= 2) f.workspace().add_macro("y", "X(2)");
1346  if (N >= 3) f.workspace().add_macro("z", "X(3)");
1347  if (N >= 4) f.workspace().add_macro("w", "X(4)");
1348  f.compile();
1349  der_f = f;
1350  der_f.derivative("X");
1351  compiled = true;
1352  }
1353 
1354  public:
1355 
1356  base_vector &point() const { return X; }
1357 
1358  const base_tensor &eval() const {
1359  if (!compiled) compile();
1360  return f.eval();
1361  }
1362  const base_tensor &eval_derivative() const {
1363  if (!compiled) compile();
1364  return der_f.eval();
1365  }
1366 
1367  obstacle()
1368  : md(0), parent_workspace(0), expr(""), X(0), f(), der_f() {}
1369  obstacle(const model &md_, const std::string &expr_, size_type N)
1370  : md(&md_), parent_workspace(0), expr(expr_), X(N),
1371  f(), der_f(), compiled(false) {}
1372  obstacle(const ga_workspace &parent_workspace_,
1373  const std::string &expr_, size_type N)
1374  : md(0), parent_workspace(&parent_workspace_), expr(expr_), X(N),
1375  f(), der_f(), compiled(false) {}
1376  obstacle(const obstacle &obs)
1377  : md(obs.md), parent_workspace(obs.parent_workspace), expr(obs.expr),
1378  X(obs.X), f(), der_f(), compiled(false) {}
1379  obstacle &operator =(const obstacle& obs) {
1380  md = obs.md;
1381  parent_workspace = obs.parent_workspace;
1382  expr = obs.expr;
1383  X = obs.X;
1384  f = ga_function();
1385  der_f = ga_function();
1386  compiled = false;
1387  return *this;
1388  }
1389  ~obstacle() {}
1390  };
1391 
1392  std::vector<obstacle> obstacles;
1393 
1394  mutable bgeot::rtree face_boxes;
1395  mutable std::vector<face_box_info> face_boxes_info;
1396 
1397 
1398  void compute_face_boxes() const { // called by init
1399  fem_precomp_pool fppool;
1400  base_matrix G;
1401  model_real_plain_vector coeff;
1402  face_boxes.clear();
1403  face_boxes_info.resize(0);
1404 
1405  for (size_type i = 0; i < contact_boundaries.size(); ++i) {
1406  const contact_boundary &cb = contact_boundaries[i];
1407  if (! cb.slave) {
1408  size_type bnum = cb.region;
1409  const mesh_fem &mfu = *(cb.mfu);
1410  const model_real_plain_vector &U = *(cb.U);
1411  const mesh &m = mfu.linked_mesh();
1412  size_type N = m.dim();
1413 
1414  base_node val(N), bmin(N), bmax(N);
1415  base_small_vector n0_x(N), n_x(N), n0_y(N), n_y(N), n_mean(N);
1416  base_matrix grad(N,N);
1417  mesh_region region = m.region(bnum);
1418  GMM_ASSERT1(mfu.get_qdim() == N, "Wrong mesh_fem qdim");
1419 
1420  dal::bit_vector points_already_interpolated;
1421  std::vector<base_node> transformed_points(m.nb_max_points());
1422  for (getfem::mr_visitor v(region,m); !v.finished(); ++v) {
1423  size_type cv = v.cv();
1424  bgeot::pgeometric_trans pgt = m.trans_of_convex(cv);
1425  pfem pf_s = mfu.fem_of_element(cv);
1426  pfem_precomp pfp = fppool(pf_s, pgt->pgeometric_nodes());
1427  slice_vector_on_basic_dof_of_element(mfu, U, cv, coeff);
1428  mfu.linked_mesh().points_of_convex(cv, G);
1429  fem_interpolation_context ctx(pgt, pfp, size_type(-1), G, cv);
1430 
1431  bgeot::pconvex_structure cvs = pgt->structure();
1432  size_type nb_pt_on_face = cvs->nb_points_of_face(v.f());
1433  GMM_ASSERT1(nb_pt_on_face >= 2, "This element has less than two "
1434  "vertices on considered face !");
1435  gmm::clear(n_mean);
1436 
1437  for (size_type k = 0; k < nb_pt_on_face; ++k) {
1438  size_type ip = cvs->ind_points_of_face(v.f())[k];
1439  size_type ind = m.ind_points_of_convex(cv)[ip];
1440 
1441  // computation of transformed vertex
1442  ctx.set_ii(ip);
1443  if (!(points_already_interpolated.is_in(ind))) {
1444  pf_s->interpolation(ctx, coeff, val, dim_type(N));
1445  val += ctx.xreal();
1446  transformed_points[ind] = val;
1447  points_already_interpolated.add(ind);
1448  } else {
1449  val = transformed_points[ind];
1450  }
1451  // computation of unit normal vector if the vertex is on the face
1452  compute_normal(ctx, v.f(), false, coeff, n0_x, n_x, grad);
1453  n_x /= gmm::vect_norm2(n_x);
1454  n_mean += n_x;
1455 
1456  if (k == 0) // computation of bounding box
1457  bmin = bmax = val;
1458  else {
1459  for (size_type l = 0; l < N; ++l) {
1460  bmin[l] = std::min(bmin[l], val[l]);
1461  bmax[l] = std::max(bmax[l], val[l]);
1462  }
1463  }
1464  }
1465 
1466  // Security coefficient of 1.3 (for nonlinear transformations)
1467  scalar_type h = bmax[0] - bmin[0];
1468  for (size_type k = 1; k < N; ++k) h = std::max(h, bmax[k]-bmin[k]);
1469  for (size_type k = 0; k < N; ++k)
1470  { bmin[k] -= h * 0.15; bmax[k] += h * 0.15; }
1471 
1472  // Store the bounding box and additional information.
1473  face_boxes.add_box(bmin, bmax, face_boxes_info.size());
1474  n_mean /= gmm::vect_norm2(n_mean);
1475  face_boxes_info.push_back(face_box_info(i, cv, v.f(), n_mean));
1476  }
1477  }
1478  }
1479 
1480  }
1481 
1482  public:
1483 
1484  void add_rigid_obstacle(const model &md, const std::string &expr,
1485  size_type N) {
1486  obstacles.push_back(obstacle(md, expr, N));
1487  }
1488 
1489  void add_rigid_obstacle(const ga_workspace &parent_workspace,
1490  const std::string &expr, size_type N) {
1491  obstacles.push_back(obstacle(parent_workspace, expr, N));
1492  }
1493 
1494  void add_contact_boundary(const model &md, const mesh &m,
1495  const std::string dispname,
1496  size_type region, bool slave) {
1497  const mesh_fem *mf = 0;
1498  if (md.variable_group_exists(dispname)) {
1499  for (const std::string &t : md.variable_group(dispname)) {
1500  const mesh_fem *mf2 = md.pmesh_fem_of_variable(t);
1501  if (mf2 && &(mf2->linked_mesh()) == &m)
1502  { mf = mf2; break; }
1503  }
1504  } else
1505  mf = md.pmesh_fem_of_variable(dispname);
1506 
1507  GMM_ASSERT1(mf, "Displacement should be a fem variable");
1508  contact_boundary cb(region, mf, dispname, slave);
1509  boundary_for_mesh[&(mf->linked_mesh())]
1510  .push_back(contact_boundaries.size());
1511  contact_boundaries.push_back(cb);
1512  }
1513 
1514  void add_contact_boundary(const ga_workspace &workspace, const mesh &m,
1515  const std::string dispname,
1516  size_type region, bool slave) {
1517  const mesh_fem *mf = 0;
1518  if (workspace.variable_group_exists(dispname)) {
1519  for (const std::string &t : workspace.variable_group(dispname)) {
1520  const mesh_fem *mf2 = workspace.associated_mf(t);
1521  if (mf2 && &(mf2->linked_mesh()) == &m)
1522  { mf = mf2; break; }
1523  }
1524  } else
1525  mf = workspace.associated_mf(dispname);
1526 
1527  GMM_ASSERT1(mf, "Displacement should be a fem variable");
1528  contact_boundary cb(region, mf, dispname, slave);
1529  boundary_for_mesh[&(mf->linked_mesh())]
1530  .push_back(contact_boundaries.size());
1531  contact_boundaries.push_back(cb);
1532  }
1533 
1534  void extract_variables(const ga_workspace &workspace,
1535  std::set<var_trans_pair> &vars,
1536  bool ignore_data, const mesh &m_x,
1537  const std::string &interpolate_name) const {
1538 
1539  bool expand_groups = !ignore_data;
1540  // const mesh_fem *mf = workspace.associated_mf(name);
1541  // GMM_ASSERT1(mf, "Internal error");
1542  // const mesh &m_x = mf->linked_mesh();
1543 
1544  mesh_boundary_cor::const_iterator it = boundary_for_mesh.find(&m_x);
1545  GMM_ASSERT1(it != boundary_for_mesh.end(), "Raytracing interpolate "
1546  "transformation: Mesh with no declared contact boundary");
1547  for (const size_type &boundary_ind : it->second) {
1548  const contact_boundary &cb = contact_boundaries[boundary_ind];
1549  const std::string &dispname_x
1550  = workspace.variable_in_group(cb.dispname, m_x);
1551  if (!ignore_data || !(workspace.is_constant(dispname_x)))
1552  vars.insert(var_trans_pair(dispname_x, ""));
1553  }
1554 
1555  for (const contact_boundary &cb : contact_boundaries) {
1556  if (!(cb.slave)) {
1557  if (expand_groups && workspace.variable_group_exists(cb.dispname)
1558  && (!ignore_data || !(workspace.is_constant(cb.dispname)))) {
1559  for (const std::string &t : workspace.variable_group(cb.dispname))
1560  vars.insert(var_trans_pair(t, interpolate_name));
1561  } else {
1562  if (!ignore_data || !(workspace.is_constant(cb.dispname)))
1563  vars.insert(var_trans_pair(cb.dispname, interpolate_name));
1564  }
1565  }
1566  }
1567  }
1568 
1569  void init(const ga_workspace &workspace) const {
1570  for (const contact_boundary &cb : contact_boundaries) {
1571  const mesh_fem &mfu = *(cb.mfu);
1572  const std::string dispname_x
1573  = workspace.variable_in_group(cb.dispname, mfu.linked_mesh());
1574 
1575  if (mfu.is_reduced()) {
1576  gmm::resize(cb.U_unred, mfu.nb_basic_dof());
1577  mfu.extend_vector(workspace.value(dispname_x), cb.U_unred);
1578  cb.U = &(cb.U_unred);
1579  } else {
1580  cb.U = &(workspace.value(dispname_x));
1581  }
1582  }
1583  compute_face_boxes();
1584  };
1585 
1586  void finalize() const {
1587  face_boxes.clear();
1588  face_boxes_info = std::vector<face_box_info>();
1589  for (const contact_boundary &cb : contact_boundaries)
1590  cb.U_unred = model_real_plain_vector();
1591  }
1592 
1593  int transform(const ga_workspace &workspace, const mesh &m_x,
1594  fem_interpolation_context &ctx_x,
1595  const base_small_vector &/*Normal*/,
1596  const mesh **m_t,
1597  size_type &cv, short_type &face_num, base_node &P_ref,
1598  base_small_vector &N_y,
1599  std::map<var_trans_pair, base_tensor> &derivatives,
1600  bool compute_derivatives) const {
1601  size_type cv_x = ctx_x.convex_num();
1602  short_type face_x = ctx_x.face_num();
1603  GMM_ASSERT1(face_x != short_type(-1), "The contact transformation can "
1604  "only be applied to a boundary");
1605 
1606  //
1607  // Find the right (slave) contact boundary
1608  //
1609  mesh_boundary_cor::const_iterator it = boundary_for_mesh.find(&m_x);
1610  GMM_ASSERT1(it != boundary_for_mesh.end(),
1611  "Mesh with no declared contact boundary");
1612  size_type ib_x = size_type(-1);
1613  for (const size_type &boundary_ind : it->second) {
1614  const contact_boundary &cb = contact_boundaries[boundary_ind];
1615  if (m_x.region(cb.region).is_in(cv_x, face_x))
1616  { ib_x = boundary_ind; break; }
1617  }
1618  GMM_ASSERT1(ib_x != size_type(-1),
1619  "No contact region found for this point");
1620  const contact_boundary &cb_x = contact_boundaries[ib_x];
1621  const mesh_fem &mfu_x = *(cb_x.mfu);
1622  pfem pfu_x = mfu_x.fem_of_element(cv_x);
1623  size_type N = mfu_x.linked_mesh().dim();
1624  GMM_ASSERT1(mfu_x.get_qdim() == N,
1625  "Displacment field with wrong dimension");
1626 
1627  model_real_plain_vector coeff_x, coeff_y, stored_coeff_y;
1628  base_small_vector a(N-1), b(N-1), pt_x(N), pt_y(N), n_x(N);
1629  base_small_vector stored_pt_y(N), stored_n_y(N), stored_pt_y_ref(N);
1630  base_small_vector n0_x, n_y(N), n0_y, res(N-1), res2(N-1), dir(N-1);
1631  base_matrix G_x, G_y, grad(N,N), hessa(N-1, N-1);
1632  std::vector<base_small_vector> ti(N-1), Ti(N-1);
1633  scalar_type stored_signed_distance(0);
1634  std::string stored_dispname;
1635  scalar_type d0 = 1E300, d1, d2;
1636  const mesh *stored_m_y(0);
1637  size_type stored_cv_y(-1);
1638  short_type stored_face_y(-1);
1639  fem_interpolation_context stored_ctx_y;
1640 
1641  //
1642  // Computation of the deformed point and unit normal vectors
1643  //
1644  slice_vector_on_basic_dof_of_element(mfu_x, *(cb_x.U), cv_x, coeff_x);
1645  m_x.points_of_convex(cv_x, G_x);
1646  ctx_x.set_pf(pfu_x);
1647  pfu_x->interpolation(ctx_x, coeff_x, pt_x, dim_type(N));
1648  pt_x += ctx_x.xreal();
1649  compute_normal(ctx_x, face_x, false, coeff_x, n0_x, n_x, grad);
1650  n_x /= gmm::vect_norm2(n_x);
1651 
1652  //
1653  // Determine the nearest rigid obstacle, taking into account
1654  // the release distance.
1655  //
1656 
1657  bool first_pair_found = false;
1658  size_type irigid_obstacle(-1);
1659  for (size_type i = 0; i < obstacles.size(); ++i) {
1660  const obstacle &obs = obstacles[i];
1661  gmm::copy(pt_x, obs.point());
1662  const base_tensor &t = obs.eval();
1663 
1664  GMM_ASSERT1(t.size() == 1, "Obstacle level set function as to be "
1665  "a scalar valued one");
1666  d1 = t[0];
1667  // cout << "d1 = " << d1 << endl;
1668  if (gmm::abs(d1) < release_distance && d1 < d0) {
1669  const base_tensor &t_der = obs.eval_derivative();
1670  GMM_ASSERT1(t_der.size() == n_x.size(), "Bad derivative size");
1671  if (gmm::vect_sp(t_der.as_vector(), n_x) < scalar_type(0)) {
1672  d0 = d1;
1673  irigid_obstacle = i;
1674  gmm::copy(t_der.as_vector(), n_y);
1675  }
1676  }
1677  }
1678 
1679  if (irigid_obstacle != size_type(-1)) {
1680  // cout << "Testing obstacle " << irigid_obstacle << endl;
1681  const obstacle &obs = obstacles[irigid_obstacle];
1682  gmm::copy(pt_x, obs.point());
1683  gmm::copy(pt_x, pt_y);
1684  size_type nit = 0, nb_fail = 0;
1685  scalar_type alpha(0), beta(0);
1686  d1 = d0;
1687 
1688  while (gmm::abs(d1) > 1E-13 && ++nit < 50 && nb_fail < 3) {
1689  if (nit != 1) gmm::copy(obs.eval_derivative().as_vector(), n_y);
1690 
1691  for (scalar_type lambda(1); lambda >= 1E-3; lambda/=scalar_type(2)) {
1692  alpha = beta - lambda * d1 / gmm::vect_sp(n_y, n_x);
1693  gmm::add(pt_x, gmm::scaled(n_x, alpha), obs.point());
1694  d2 = obs.eval()[0];
1695  if (gmm::abs(d2) < gmm::abs(d1)) break;
1696  }
1697  if (gmm::abs(beta - d1 / gmm::vect_sp(n_y, n_x)) > scalar_type(500))
1698  nb_fail++;
1699  beta = alpha; d1 = d2;
1700  }
1701  gmm::copy(obs.point(), pt_y);
1702 
1703  if (gmm::abs(d1) > 1E-8) {
1704  GMM_WARNING1("Raytrace on rigid obstacle failed");
1705  } // CRITERION 4 for rigid bodies : Apply the release distance
1706  else if (gmm::vect_dist2(pt_y, pt_x) <= release_distance) {
1707  n_y /= gmm::vect_norm2(n_y);
1708  d0 = gmm::vect_dist2(pt_y, pt_x) * gmm::sgn(d0);
1709  stored_pt_y = stored_pt_y_ref = pt_y;
1710  stored_n_y = n_y;
1711  stored_signed_distance = d0;
1712  first_pair_found = true;
1713  } else
1714  irigid_obstacle = size_type(-1);
1715  }
1716 
1717  //
1718  // Determine the potential contact pairs with deformable bodies
1719  //
1720  bgeot::rtree::pbox_set bset;
1721  base_node bmin(pt_x), bmax(pt_x);
1722  for (size_type i = 0; i < N; ++i)
1723  { bmin[i] -= release_distance; bmax[i] += release_distance; }
1724 
1725  face_boxes.find_line_intersecting_boxes(pt_x, n_x, bmin, bmax, bset);
1726 
1727  //
1728  // Iteration on potential contact pairs and application
1729  // of selection criteria
1730  //
1731  for (const auto &pbox : bset) {
1732  face_box_info &fbox_y = face_boxes_info[pbox->id];
1733  size_type ib_y = fbox_y.ind_boundary;
1734  const contact_boundary &cb_y = contact_boundaries[ib_y];
1735  const mesh_fem &mfu_y = *(cb_y.mfu);
1736  const mesh &m_y = mfu_y.linked_mesh();
1737  size_type cv_y = fbox_y.ind_element;
1738  pfem pfu_y = mfu_y.fem_of_element(cv_y);
1739  short_type face_y = fbox_y.ind_face;
1740  bgeot::pgeometric_trans pgt_y= m_y.trans_of_convex(cv_y);
1741 
1742  // CRITERION 1 : The unit normal vector are compatible
1743  // and the two points are not in the same element.
1744  if (gmm::vect_sp(fbox_y.mean_normal, n_x) >= scalar_type(0) ||
1745  (cv_x == cv_y && &m_x == &m_y))
1746  continue;
1747 
1748  //
1749  // Raytrace search for y by Newton's algorithm
1750  //
1751 
1752  m_y.points_of_convex(cv_y, G_y);
1753  const base_node &Y0
1754  = pfu_y->ref_convex(cv_y)->points_of_face(face_y)[0];
1755  fem_interpolation_context ctx_y(pgt_y, pfu_y, Y0, G_y, cv_y, face_y);
1756 
1757  const base_small_vector &NY0
1758  = pfu_y->ref_convex(cv_y)->normals()[face_y];
1759  for (size_type k = 0; k < N-1; ++k) { // A basis for the face
1760  gmm::resize(ti[k], N);
1761  scalar_type norm(0);
1762  while(norm < 1E-5) {
1763  gmm::fill_random(ti[k]);
1764  ti[k] -= gmm::vect_sp(ti[k], NY0) * NY0;
1765  for (size_type l = 0; l < k; ++l)
1766  ti[k] -= gmm::vect_sp(ti[k], ti[l]) * ti[l];
1767  norm = gmm::vect_norm2(ti[k]);
1768  }
1769  ti[k] /= norm;
1770  }
1771 
1772  gmm::clear(a);
1773 
1774  for (size_type k = 0; k < N-1; ++k) {
1775  gmm::resize(Ti[k], N);
1776  scalar_type norm(0);
1777  while (norm < 1E-5) {
1778  gmm::fill_random(Ti[k]);
1779  Ti[k] -= gmm::vect_sp(Ti[k], n_x) * n_x;
1780  for (size_type l = 0; l < k; ++l)
1781  Ti[k] -= gmm::vect_sp(Ti[k], Ti[l]) * Ti[l];
1782  norm = gmm::vect_norm2(Ti[k]);
1783  }
1784  Ti[k] /= norm;
1785  }
1786 
1787  slice_vector_on_basic_dof_of_element(mfu_y, *(cb_y.U), cv_y, coeff_y);
1788 
1789  raytrace_pt_surf_cost_function_object pps(Y0, pt_x, ctx_y, coeff_y,
1790  ti, Ti, false);
1791  pps(a, res);
1792  scalar_type residual = gmm::vect_norm2(res);
1793  scalar_type residual2(0), det(0);
1794  bool exited = false;
1795  size_type nbfail = 0, niter = 0;
1796  for (;residual > 2E-12 && niter <= 30; ++niter) {
1797 
1798  for (size_type subiter(0); subiter <= 4; ++subiter) {
1799  pps(a, hessa);
1800  det = gmm::abs(bgeot::lu_inverse(&(*(hessa.begin())), N-1, false));
1801  if (det > 1E-15) break;
1802  for (size_type i = 0; i < N-1; ++i)
1803  a[i] += gmm::random() * 1E-7;
1804  }
1805  if (det <= 1E-15) break;
1806  // Computation of the descent direction
1807  gmm::mult(hessa, gmm::scaled(res, scalar_type(-1)), dir);
1808 
1809  if (gmm::vect_norm2(dir) > scalar_type(10)) nbfail++;
1810  if (nbfail >= 4) break;
1811 
1812  // Line search
1813  scalar_type lambda(1);
1814  for (size_type j = 0; j < 5; ++j) {
1815  gmm::add(a, gmm::scaled(dir, lambda), b);
1816  pps(b, res2);
1817  residual2 = gmm::vect_norm2(res2);
1818  if (residual2 < residual) break;
1819  lambda /= ((j < 3) ? scalar_type(2) : scalar_type(5));
1820  }
1821 
1822  residual = residual2;
1823  gmm::copy(res2, res);
1824  gmm::copy(b, a);
1825  scalar_type dist_ref = gmm::vect_norm2(a);
1826 
1827  if (niter > 1 && dist_ref > 15) break;
1828  if (niter > 5 && dist_ref > 8) break;
1829  if (/*(niter > 1 && dist_ref > 7) ||*/ nbfail == 3) exited = true;
1830  }
1831  bool converged = (gmm::vect_norm2(res) < 2E-6);
1832  bool is_in = (pfu_y->ref_convex(cv_y)->is_in(ctx_y.xref()) < 1E-6);
1833  // GMM_ASSERT1(!(exited && converged && is_in),
1834  // "A non conformal case !! " << gmm::vect_norm2(res)
1835  // << " : " << nbfail << " : " << niter);
1836  if (is_in) {
1837  ctx_y.pf()->interpolation(ctx_y, coeff_y, pt_y, dim_type(N));
1838  pt_y += ctx_y.xreal();
1839  }
1840 
1841  // CRITERION 2 : The contact pair is eliminated when
1842  // raytrace do not converge.
1843  if (!converged) continue;
1844 
1845  // CRITERION 3 : The raytraced point is inside the element
1846  if (!is_in) continue;
1847 
1848  // CRITERION 4 : Apply the release distance
1849  scalar_type signed_dist = gmm::vect_dist2(pt_y, pt_x);
1850  if (signed_dist > release_distance) continue;
1851 
1852  // compute the unit normal vector at y and the signed distance.
1853  compute_normal(ctx_y, face_y, false, coeff_y, n0_y, n_y, grad);
1854  n_y /= gmm::vect_norm2(n_y);
1855  signed_dist *= gmm::sgn(gmm::vect_sp(pt_x - pt_y, n_y));
1856 
1857  // CRITERION 5 : comparison with rigid obstacles
1858  // CRITERION 7 : smallest signed distance on contact pairs
1859  if (first_pair_found && stored_signed_distance < signed_dist)
1860  continue;
1861 
1862  // CRITERION 1 : again on found unit normal vector
1863  if (gmm::vect_sp(n_y, n_x) >= scalar_type(0)) continue;
1864 
1865  // CRITERION 6 : for self-contact only : apply a test on
1866  // unit normals in reference configuration.
1867  if (&m_x == &m_y) {
1868  base_small_vector diff = ctx_x.xreal() - ctx_y.xreal();
1869  scalar_type ref_dist = gmm::vect_norm2(diff);
1870  if ( (ref_dist < scalar_type(4) * release_distance)
1871  && (gmm::vect_sp(diff, n0_y) < - 0.01 * ref_dist) )
1872  continue;
1873  }
1874 
1875  stored_pt_y = pt_y; stored_pt_y_ref = ctx_y.xref();
1876  stored_m_y = &m_y; stored_cv_y = cv_y; stored_face_y = face_y;
1877  stored_n_y = n_y;
1878  stored_ctx_y = ctx_y;
1879  stored_coeff_y = coeff_y;
1880  stored_signed_distance = signed_dist;
1881  stored_dispname = cb_y.dispname;
1882  first_pair_found = true;
1883  irigid_obstacle = size_type(-1);
1884  }
1885 
1886  int ret_type = 0;
1887  *m_t = 0;
1888  cv = size_type(-1);
1889  face_num = short_type(-1);
1890  if (irigid_obstacle != size_type(-1)) {
1891  P_ref = stored_pt_y; N_y = stored_n_y;
1892  ret_type = 2;
1893  } else if (first_pair_found) {
1894  *m_t = stored_m_y; cv = stored_cv_y; face_num = stored_face_y;
1895  P_ref = stored_pt_y_ref; N_y = stored_n_y;
1896  ret_type = 1;
1897  }
1898 
1899  // Note on derivatives of the transformation : for efficiency and
1900  // simplicity reasons, the derivative should be computed with
1901  // the value of corresponding test functions. This means that
1902  // for a transformation F(u) the conputed derivative is F'(u).Test_u
1903  // including the Test_u.
1904  if (compute_derivatives) {
1905  if (ret_type >= 1) {
1906  fem_interpolation_context &ctx_y = stored_ctx_y;
1907  size_type cv_y = 0;
1908  if (ret_type == 1) cv_y = ctx_y.convex_num();
1909 
1910  base_matrix I_nxny(N,N); // I - nx@ny/nx.ny
1911  gmm::copy(gmm::identity_matrix(), I_nxny);
1912  gmm::rank_one_update(I_nxny, n_x,
1913  gmm::scaled(stored_n_y,scalar_type(-1)
1914  / gmm::vect_sp(n_x, stored_n_y)));
1915 
1916  // Computation of F_y
1917  base_matrix F_y(N,N), F_y_inv(N,N), M1(N, N), M2(N, N);
1918  pfem pfu_y = 0;
1919  if (ret_type == 1) {
1920  pfu_y = ctx_y.pf();
1921  pfu_y->interpolation_grad(ctx_y, stored_coeff_y, F_y, dim_type(N));
1922  gmm::add(gmm::identity_matrix(), F_y);
1923  gmm::copy(F_y, F_y_inv);
1924  bgeot::lu_inverse(&(*(F_y_inv.begin())), N);
1925  } else {
1926  gmm::copy(gmm::identity_matrix(), F_y);
1927  gmm::copy(gmm::identity_matrix(), F_y_inv);
1928  }
1929 
1930  // Computation of F_x
1931  base_matrix F_x(N,N), F_x_inv(N,N);
1932  pfu_x->interpolation_grad(ctx_x, coeff_x, F_x, dim_type(N));
1933  gmm::add(gmm::identity_matrix(), F_x);
1934  gmm::copy(F_x, F_x_inv);
1935  bgeot::lu_inverse(&(*(F_x_inv.begin())), N);
1936 
1937 
1938  base_tensor base_ux;
1939  base_matrix vbase_ux;
1940  ctx_x.base_value(base_ux);
1941  size_type qdim_ux = pfu_x->target_dim();
1942  size_type ndof_ux = pfu_x->nb_dof(cv_x) * N / qdim_ux;
1943  vectorize_base_tensor(base_ux, vbase_ux, ndof_ux, qdim_ux, N);
1944 
1945  base_tensor base_uy;
1946  base_matrix vbase_uy;
1947  size_type ndof_uy = 0;
1948  if (ret_type == 1) {
1949  ctx_y.base_value(base_uy);
1950  size_type qdim_uy = pfu_y->target_dim();
1951  ndof_uy = pfu_y->nb_dof(cv_y) * N / qdim_uy;
1952  vectorize_base_tensor(base_uy, vbase_uy, ndof_uy, qdim_uy, N);
1953  }
1954 
1955  base_tensor grad_base_ux, vgrad_base_ux;
1956  ctx_x.grad_base_value(grad_base_ux);
1957  vectorize_grad_base_tensor(grad_base_ux, vgrad_base_ux, ndof_ux,
1958  qdim_ux, N);
1959 
1960  // Derivative : F_y^{-1}*I_nxny*(Test_u(X)-Test_u(Y)+gDn_x[Test_u])
1961  // with Dn_x[Test_u] =-(I-nx@nx)*F_x^{-T}*Grad_Test_u^{T}*n_x
1962  // and I_nxny*(I - nx@nx) = I_nxny
1963 
1964  // F_y^{-1}*I_nxny*Test_u(X)
1965  gmm::mult(F_y_inv, I_nxny, M1);
1966  base_matrix der_x(ndof_ux, N);
1967  gmm::mult(vbase_ux, gmm::transposed(M1), der_x);
1968 
1969  // -F_y^{-1}*I_nxny*Test_u(Y)
1970  base_matrix der_y(ndof_uy, N);
1971  if (ret_type == 1) {
1972  gmm::mult(vbase_uy, gmm::transposed(M1), der_y);
1973  gmm::scale(der_y, scalar_type(-1));
1974  }
1975 
1976  // F_y^{-1}*I_nxny*gDn_x[Test_u]
1977  gmm::mult(M1, gmm::transposed(F_x_inv), M2);
1978  for (size_type i = 0; i < ndof_ux; ++i)
1979  for (size_type j = 0; j < N; ++j)
1980  for (size_type k = 0; k < N; ++k)
1981  for (size_type l = 0; l < N; ++l)
1982  der_x(i, j) -= M2(j, k) * vgrad_base_ux(i, l, k)
1983  * n_x[l] * stored_signed_distance;
1984 
1985  const std::string &dispname_x
1986  = workspace.variable_in_group(cb_x.dispname, m_x);
1987 
1988  for (auto&& d : derivatives) {
1989  if (dispname_x.compare(d.first.varname) == 0 &&
1990  d.first.transname.size() == 0) {
1991  d.second.adjust_sizes(ndof_ux, N);
1992  gmm::copy(der_x.as_vector(), d.second.as_vector());
1993  } else if (ret_type == 1 &&
1994  stored_dispname.compare(d.first.varname) == 0 &&
1995  d.first.transname.size() != 0) {
1996  d.second.adjust_sizes(ndof_uy, N);
1997  gmm::copy(der_y.as_vector(), d.second.as_vector());
1998  } else
1999  d.second.adjust_sizes(0, 0);
2000  }
2001  } else {
2002  for (auto&& d : derivatives)
2003  d.second.adjust_sizes(0, 0);
2004  }
2005  }
2006  return ret_type;
2007  }
2008 
2009  raytracing_interpolate_transformation(scalar_type d)
2010  : release_distance(d) {}
2011  };
2012 //=========================================================================
2013  //
2014  // Projection interpolate transformation for generic assembly
2015  //
2016  //=========================================================================
2017 
2018  class projection_interpolate_transformation
2019  : public raytracing_interpolate_transformation {
2020 
2021  scalar_type release_distance; // Limit distance beyond which the contact
2022  // will not be considered.
2023  public:
2024  int transform(const ga_workspace &workspace, const mesh &m_x,
2025  fem_interpolation_context &ctx_x,
2026  const base_small_vector &/*Normal*/,
2027  const mesh **m_t,
2028  size_type &cv, short_type &face_num, base_node &P_ref,
2029  base_small_vector &N_y,
2030  std::map<var_trans_pair, base_tensor> &derivatives,
2031  bool compute_derivatives) const {
2032  size_type cv_x = ctx_x.convex_num();
2033  short_type face_x = ctx_x.face_num();
2034  GMM_ASSERT1(face_x != short_type(-1), "The contact transformation can "
2035  "only be applied to a boundary");
2036 
2037  //
2038  // Find the right (slave) contact boundary
2039  //
2040  mesh_boundary_cor::const_iterator it = boundary_for_mesh.find(&m_x);
2041  GMM_ASSERT1(it != boundary_for_mesh.end(),
2042  "Mesh with no declared contact boundary");
2043  size_type ib_x = size_type(-1);
2044  for (const auto &boundary_ind : it->second) {
2045  const contact_boundary &cb = contact_boundaries[boundary_ind];
2046  if (m_x.region(cb.region).is_in(cv_x, face_x))
2047  { ib_x = boundary_ind; break; }
2048  }
2049  GMM_ASSERT1(ib_x != size_type(-1),
2050  "No contact region found for this point");
2051  const contact_boundary &cb_x = contact_boundaries[ib_x];
2052  const mesh_fem &mfu_x = *(cb_x.mfu);
2053  pfem pfu_x = mfu_x.fem_of_element(cv_x);
2054  size_type N = mfu_x.linked_mesh().dim();
2055  GMM_ASSERT1(mfu_x.get_qdim() == N,
2056  "Displacment field with wrong dimension");
2057 
2058  model_real_plain_vector coeff_x, coeff_y, stored_coeff_y;
2059  base_small_vector a(N-1), b(N-1), pt_x(N), pt_y(N), n_x(N);
2060  base_small_vector stored_pt_y(N), stored_n_y(N), stored_pt_y_ref(N);
2061  base_small_vector n0_x, n_y(N), n0_y, res(N-1), res2(N-1), dir(N-1);
2062  base_matrix G_x, G_y, grad(N,N), hessa(N-1, N-1);
2063  std::vector<base_small_vector> ti(N-1);
2064  scalar_type stored_signed_distance(0);
2065  std::string stored_dispname;
2066  scalar_type d0 = 1E300, d1, d2(0);
2067  const mesh *stored_m_y(0);
2068  size_type stored_cv_y(-1);
2069  short_type stored_face_y(-1);
2070  fem_interpolation_context stored_ctx_y;
2071  size_type nbwarn(0);
2072 
2073 
2074  //
2075  // Computation of the deformed point and unit normal vectors
2076  //
2077  slice_vector_on_basic_dof_of_element(mfu_x, *(cb_x.U), cv_x, coeff_x);
2078  bgeot::vectors_to_base_matrix(G_x, m_x.points_of_convex(cv_x));
2079  ctx_x.set_pf(pfu_x);
2080  pfu_x->interpolation(ctx_x, coeff_x, pt_x, dim_type(N));
2081  pt_x += ctx_x.xreal();
2082  compute_normal(ctx_x, face_x, false, coeff_x, n0_x, n_x, grad);
2083  n_x /= gmm::vect_norm2(n_x);
2084 
2085  //
2086  // Determine the nearest rigid obstacle, taking into account
2087  // the release distance.
2088  //
2089 
2090  bool first_pair_found = false;
2091  size_type irigid_obstacle(-1);
2092  for (size_type i = 0; i < obstacles.size(); ++i) {
2093  const obstacle &obs = obstacles[i];
2094  gmm::copy(pt_x, obs.point());
2095  const base_tensor &t = obs.eval();
2096 
2097  GMM_ASSERT1(t.size() == 1, "Obstacle level set function as to be "
2098  "a scalar valued one");
2099  d1 = t[0];
2100  // cout << "d1 = " << d1 << endl;
2101  if (gmm::abs(d1) < release_distance && d1 < d0) {
2102  const base_tensor &t_der = obs.eval_derivative();
2103  GMM_ASSERT1(t_der.size() == n_x.size(), "Bad derivative size");
2104  if (gmm::vect_sp(t_der.as_vector(), n_x) < scalar_type(0))
2105  { d0 = d1; irigid_obstacle = i; gmm::copy(t_der.as_vector(),n_y); }
2106  }
2107  }
2108 
2109  if (irigid_obstacle != size_type(-1)) {
2110  // cout << "Testing obstacle " << irigid_obstacle << endl;
2111  const obstacle &obs = obstacles[irigid_obstacle];
2112  gmm::copy(pt_x, obs.point());
2113  gmm::copy(pt_x, pt_y);
2114  size_type nit = 0, nb_fail = 0;
2115  scalar_type alpha(0), beta(0);
2116  d1 = d0;
2117 
2118  while (gmm::abs(d1) > 1E-13 && ++nit < 50 && nb_fail < 3) {
2119  if (nit != 1) gmm::copy(obs.eval_derivative().as_vector(), n_y);
2120 
2121  for (scalar_type lambda(1); lambda >= 1E-3; lambda/=scalar_type(2)) {
2122  gmm::add(gmm::scaled(n_y, -d1/gmm::vect_norm2_sqr(n_y)), pt_y, pt_x);
2123 
2124  if (gmm::abs(d2) < gmm::abs(d1)) break;
2125  }
2126  if (gmm::abs(beta - d1 / gmm::vect_sp(n_y, n_x)) > scalar_type(500))
2127  nb_fail++;
2128  beta = alpha; d1 = d2;
2129  }
2130  gmm::copy(obs.point(), pt_y);
2131 
2132  if (gmm::abs(d1) > 1E-8) {
2133  GMM_WARNING1("Raytrace on rigid obstacle failed");
2134  } // CRITERION 4 for rigid bodies : Apply the release distance
2135  else if (gmm::vect_dist2(pt_y, pt_x) <= release_distance) {
2136  n_y /= gmm::vect_norm2(n_y);
2137  d0 = gmm::vect_dist2(pt_y, pt_x) * gmm::sgn(d0);
2138  stored_pt_y = stored_pt_y_ref = pt_y; stored_n_y = n_y,
2139  stored_signed_distance = d0;
2140  first_pair_found = true;
2141  } else
2142  irigid_obstacle = size_type(-1);
2143  }
2144 
2145  //
2146  // Determine the potential contact pairs with deformable bodies
2147  //
2148  bgeot::rtree::pbox_set bset;
2149  base_node bmin(pt_x), bmax(pt_x);
2150  for (size_type i = 0; i < N; ++i)
2151  { bmin[i] -= release_distance; bmax[i] += release_distance; }
2152 
2153  face_boxes.find_line_intersecting_boxes(pt_x, n_x, bmin, bmax, bset);
2154  //
2155  // Iteration on potential contact pairs and application
2156  // of selection criteria
2157  //
2158  for (const auto &pbox : bset) {
2159  face_box_info &fbox_y = face_boxes_info[pbox->id];
2160  size_type ib_y = fbox_y.ind_boundary;
2161  const contact_boundary &cb_y = contact_boundaries[ib_y];
2162  const mesh_fem &mfu_y = *(cb_y.mfu);
2163  const mesh &m_y = mfu_y.linked_mesh();
2164  bool ref_conf=false;
2165  size_type cv_y = fbox_y.ind_element;
2166  pfem pfu_y = mfu_y.fem_of_element(cv_y);
2167  short_type face_y = fbox_y.ind_face;
2168  bgeot::pgeometric_trans pgt_y= m_y.trans_of_convex(cv_y);
2169 
2170  // CRITERION 1 : The unit normal vectors are compatible
2171  // and the two points are not in the same element.
2172  if (gmm::vect_sp(fbox_y.mean_normal, n_x) >= scalar_type(0) ||
2173  (cv_x == cv_y && &m_x == &m_y))
2174  continue;
2175 
2176  //
2177  // Classical projection for y by quasi Newton algorithm
2178  //
2179  bgeot::vectors_to_base_matrix(G_y, m_y.points_of_convex(cv_y));
2180  const base_node &Y0
2181  = pfu_y->ref_convex(cv_y)->points_of_face(face_y)[0];
2182  fem_interpolation_context ctx_y(pgt_y, pfu_y, Y0, G_y, cv_y, face_y);
2183 
2184  const base_small_vector &NY0
2185  = pfu_y->ref_convex(cv_y)->normals()[face_y];
2186 
2187  gmm::clear(a);
2188 
2189  for (size_type k = 0; k < N-1; ++k) { // A basis for the face
2190  gmm::resize(ti[k], N);
2191  scalar_type norm(0);
2192  while(norm < 1E-5) {
2193  gmm::fill_random(ti[k]);
2194  ti[k] -= gmm::vect_sp(ti[k], NY0) * NY0;
2195  for (size_type l = 0; l < k; ++l)
2196  ti[k] -= gmm::vect_sp(ti[k], ti[l]) * ti[l];
2197  norm = gmm::vect_norm2(ti[k]);
2198  }
2199  ti[k] /= norm;
2200  }
2201  slice_vector_on_basic_dof_of_element(mfu_y, *(cb_y.U), cv_y, coeff_y);
2202  proj_pt_surf_cost_function_object pps(Y0, pt_x, ctx_y, coeff_y, ti,
2203  1E-10, ref_conf);
2204  base_small_vector grada(N-1);
2205  scalar_type det(0);
2206  scalar_type residual = gmm::vect_norm2(res);
2207  scalar_type dist = pps(a, grada);
2208  pps(a, res);
2209  for (size_type niter = 0;
2210  gmm::vect_norm2(grada) > 1E-7 && niter <= 50; ++niter) {
2211 
2212  for (size_type subiter(0);;) {
2213  pps(a, hessa);
2214  det = gmm::abs(gmm::lu_inverse(hessa, false));
2215  if (det > 1E-15) break;
2216  for (size_type i = 0; i < N-1; ++i)
2217  a[i] += gmm::random() * 1E-7;
2218  if (++subiter > 4) break;
2219  }
2220  if (det <= 1E-15) break;
2221  // Computation of the descent direction
2222  gmm::mult(hessa, gmm::scaled(grada, scalar_type(-1)), dir);
2223 
2224  // Line search
2225  for (scalar_type lambda(1);
2226  lambda >= 1E-3; lambda /= scalar_type(2)) {
2227  gmm::add(a, gmm::scaled(dir, lambda), b);
2228  if (pps(b) < dist) break;
2229  gmm::add(a, gmm::scaled(dir, -lambda), b);
2230  if (pps(b) < dist) break;
2231  }
2232  //cout<< "b =" << b <<endl;
2233  gmm::copy(b, a);
2234  dist = pps(a, grada);
2235  }
2236 
2237  bool converged = (gmm::vect_norm2(grada) < 2E-6);
2238  if (!converged) { // Try with BFGS
2239  gmm::iteration iter(1E-10, 0 /* noisy*/, 100 /*maxiter*/);
2240  gmm::clear(a);
2241  gmm::bfgs(pps, pps, a, 10, iter, 0, 0.5);
2242  residual = gmm::abs(iter.get_res());
2243  converged = (residual < 2E-5);
2244  }
2245 
2246  bool is_in = (pfu_y->ref_convex(cv_y)->is_in(ctx_y.xref()) < 1E-6);
2247 
2248  //cout<< "y_ref =" << ctx_y.xref() <<endl;
2249  //cout<< "x_ref =" << ctx_x.xref() <<endl;
2250  // cout<< "y =" << ctx_y.xreal() <<endl;
2251  // cout<< "x =" << ctx_x.xreal() <<endl;
2252  // cout<< "is_in =" << is_in <<endl;
2253 
2254  if (is_in || (!converged )) {
2255  if (!ref_conf) {
2256  ctx_y.pf()->interpolation(ctx_y, coeff_y, pt_y, dim_type(N));
2257  pt_y += ctx_y.xreal();
2258  } else {
2259  pt_y = ctx_y.xreal();
2260  }
2261  }
2262  // CRITERION 2 : The contact pair is eliminated when
2263  // projection/raytrace do not converge.
2264  if (!converged) {
2265  if ( nbwarn < 4) {
2266  GMM_WARNING3("Projection algorithm did not converge "
2267  "for point " << pt_x << " residual " << residual
2268  << " projection computed " << pt_y);
2269  ++nbwarn;
2270  }
2271  continue;
2272  }
2273 
2274  // CRITERION 3 : The projected point is inside the element
2275  // The test should be completed: If the point is outside
2276  // the element, a rapid reprojection on the face
2277  // (on the reference element, with a linear algorithm)
2278  // can be applied and a test with a neigbhour element
2279  // to decide if the point is in fact ok ...
2280  // (to be done only if there is no projection on other
2281  // element which coincides and with a test on the
2282  // distance ... ?) To be specified (in this case,
2283  // change xref).
2284  if (!is_in) continue;
2285 
2286  // CRITERION 4 : Apply the release distance
2287 
2288  scalar_type signed_dist = gmm::vect_dist2(pt_y, pt_x);
2289  //cout<< "signd_dist="<< signed_dist << "relaese_dist="<< release_distance <<endl;
2290  if (signed_dist > release_distance) continue;
2291 
2292  // compute the unit normal vector at y and the signed distance.
2293  base_small_vector ny0(N);
2294  compute_normal(ctx_y, face_y, ref_conf, coeff_y, ny0, n_y, grad);
2295  // ny /= gmm::vect_norm2(ny); // Useful only if the unit normal is kept
2296  signed_dist *= gmm::sgn(gmm::vect_sp(pt_x - pt_y, n_y));
2297 
2298  // CRITERION 5 : comparison with rigid obstacles
2299  // CRITERION 7 : smallest signed distance on contact pairs
2300  if (first_pair_found && stored_signed_distance < signed_dist)
2301  continue;
2302 
2303  // CRITERION 1 : again on found unit normal vector
2304  if (gmm::vect_sp(n_y, n_x) >= scalar_type(0)) continue;
2305 
2306  // CRITERION 6 : for self-contact only : apply a test on
2307  // unit normals in reference configuration.
2308  if (&m_x == &m_y) {
2309 
2310  base_small_vector diff = ctx_x.xreal() - ctx_y.xreal();
2311  scalar_type ref_dist = gmm::vect_norm2(diff);
2312 
2313  if ( (ref_dist < scalar_type(4) * release_distance)
2314  && (gmm::vect_sp(diff, ny0) < - 0.01 * ref_dist) )
2315  continue;
2316  }
2317 
2318  stored_pt_y = pt_y; stored_pt_y_ref = ctx_y.xref();
2319  stored_m_y = &m_y; stored_cv_y = cv_y; stored_face_y = face_y;
2320  stored_n_y = n_y;
2321  stored_ctx_y = ctx_y;
2322  stored_coeff_y = coeff_y;
2323  stored_signed_distance = signed_dist;
2324  stored_dispname = cb_y.dispname;
2325  first_pair_found = true;
2326  irigid_obstacle = size_type(-1);
2327 
2328  // Projection could be ameliorated by finding a starting point near
2329  // x (with respect to the integration method, for instance).
2330 
2331  // A specific (Quasi) Newton algorithm for computing the projection
2332  }
2333 
2334  int ret_type = 0;
2335  *m_t = 0; cv = size_type(-1); face_num = short_type(-1);
2336  if (irigid_obstacle != size_type(-1)) {
2337  P_ref = stored_pt_y; N_y = stored_n_y;
2338  ret_type = 2;
2339  } else if (first_pair_found) {
2340  *m_t = stored_m_y; cv = stored_cv_y; face_num = stored_face_y;
2341  P_ref = stored_pt_y_ref;
2342  ret_type = 1;
2343  }
2344  // Note on derivatives of the transformation : for efficiency and
2345  // simplicity reasons, the derivative should be computed with
2346  // the value of corresponding test functions. This means that
2347  // for a transformation F(u) the conputed derivative is F'(u).Test_u
2348  // including the Test_u.
2349 
2350  if (compute_derivatives) {
2351  if (ret_type >= 1) {
2352  fem_interpolation_context &ctx_y = stored_ctx_y;
2353  size_type cv_y = 0;
2354  if (ret_type == 1) cv_y = ctx_y.convex_num();
2355 
2356 
2357  base_matrix I_nyny(N,N); // I - ny@ny
2358  gmm::copy(gmm::identity_matrix(), I_nyny);
2359  gmm::rank_one_update(I_nyny, stored_n_y,
2360  gmm::scaled(stored_n_y,scalar_type(-1)));
2361 
2362  // Computation of F_y
2363  base_matrix F_y(N,N), F_y_inv(N,N), M1(N, N), M2(N, N);
2364  pfem pfu_y = 0;
2365  if (ret_type == 1) {
2366  pfu_y = ctx_y.pf();
2367  pfu_y->interpolation_grad(ctx_y, stored_coeff_y, F_y, dim_type(N));
2368  gmm::add(gmm::identity_matrix(), F_y);
2369  gmm::copy(F_y, F_y_inv);
2370  gmm::lu_inverse(F_y_inv);
2371  } else {
2372  gmm::copy(gmm::identity_matrix(), F_y);
2373  gmm::copy(gmm::identity_matrix(), F_y_inv);
2374  }
2375 
2376 
2377  base_tensor base_ux;
2378  base_matrix vbase_ux;
2379  ctx_x.base_value(base_ux);
2380  size_type qdim_ux = pfu_x->target_dim();
2381  size_type ndof_ux = pfu_x->nb_dof(cv_x) * N / qdim_ux;
2382  vectorize_base_tensor(base_ux, vbase_ux, ndof_ux, qdim_ux, N);
2383 
2384  base_tensor base_uy;
2385  base_matrix vbase_uy;
2386  size_type ndof_uy = 0;
2387  if (ret_type == 1) {
2388  ctx_y.base_value(base_uy);
2389  size_type qdim_uy = pfu_y->target_dim();
2390  ndof_uy = pfu_y->nb_dof(cv_y) * N / qdim_uy;
2391  vectorize_base_tensor(base_uy, vbase_uy, ndof_uy, qdim_uy, N);
2392  }
2393 
2394  base_tensor grad_base_ux, vgrad_base_ux;
2395  ctx_x.grad_base_value(grad_base_ux);
2396  vectorize_grad_base_tensor(grad_base_ux, vgrad_base_ux, ndof_ux,
2397  qdim_ux, N);
2398 
2399  // Derivative : F_y^{-1}*I_nyny*(Test_u(X)-Test_u(Y))
2400  // and I_nyny*(I - ny@ny) = I_nyny
2401 
2402  // F_y^{-1}*I_nyny*Test_u(X)
2403  gmm::mult(F_y_inv, I_nyny, M1);
2404  base_matrix der_x(ndof_ux, N);
2405  gmm::mult(vbase_ux, gmm::transposed(M1), der_x);
2406 
2407  // -F_y^{-1}*I_nyny*Test_u(Y)
2408  base_matrix der_y(ndof_uy, N);
2409  if (ret_type == 1) {
2410  gmm::mult(vbase_uy, gmm::transposed(M1), der_y);
2411  gmm::scale(der_y, scalar_type(-1));
2412  }
2413 
2414  const std::string &dispname_x
2415  = workspace.variable_in_group(cb_x.dispname, m_x);
2416 
2417  for (auto&& d : derivatives) {
2418  if (dispname_x.compare(d.first.varname) == 0 &&
2419  d.first.transname.size() == 0) {
2420  d.second.adjust_sizes(ndof_ux, N);
2421  gmm::copy(der_x.as_vector(), d.second.as_vector());
2422  } else if (ret_type == 1 &&
2423  stored_dispname.compare(d.first.varname) == 0 &&
2424  d.first.transname.size() != 0) {
2425  d.second.adjust_sizes(ndof_uy, N);
2426  gmm::copy(der_y.as_vector(), d.second.as_vector());
2427  } else
2428  d.second.adjust_sizes(0, 0);
2429  }
2430  } else {
2431  for (auto&& d : derivatives)
2432  d.second.adjust_sizes(0, 0);
2433  }
2434  }
2435  return ret_type;
2436  }
2437  projection_interpolate_transformation(const scalar_type &d)
2438  :raytracing_interpolate_transformation(d), release_distance(d) {}
2439  };
2441  (model &md, const std::string &transname, scalar_type d) {
2442  pinterpolate_transformation
2443  p = std::make_shared<raytracing_interpolate_transformation>(d);
2444  md.add_interpolate_transformation(transname, p);
2445  }
2446 
2448  (ga_workspace &workspace, const std::string &transname, scalar_type d) {
2449  pinterpolate_transformation
2450  p = std::make_shared<raytracing_interpolate_transformation>(d);
2451  workspace.add_interpolate_transformation(transname, p);
2452  }
2453 
2455  (model &md, const std::string &transname, const mesh &m,
2456  const std::string &dispname, size_type region) {
2457  raytracing_interpolate_transformation *p
2458  = dynamic_cast<raytracing_interpolate_transformation *>
2459  (const_cast<virtual_interpolate_transformation *>
2460  (&(*(md.interpolate_transformation(transname)))));
2461  p->add_contact_boundary(md, m, dispname, region, false);
2462  }
2463 
2465  (model &md, const std::string &transname, const mesh &m,
2466  const std::string &dispname, size_type region) {
2467  raytracing_interpolate_transformation *p
2468  = dynamic_cast<raytracing_interpolate_transformation *>
2469  (const_cast<virtual_interpolate_transformation *>
2470  (&(*(md.interpolate_transformation(transname)))));
2471  p->add_contact_boundary(md, m, dispname, region, true);
2472  }
2473 
2475  (ga_workspace &workspace, const std::string &transname, const mesh &m,
2476  const std::string &dispname, size_type region) {
2477  raytracing_interpolate_transformation *p
2478  = dynamic_cast<raytracing_interpolate_transformation *>
2479  (const_cast<virtual_interpolate_transformation *>
2480  (&(*(workspace.interpolate_transformation(transname)))));
2481  p->add_contact_boundary(workspace, m, dispname, region, false);
2482  }
2483 
2485  (ga_workspace &workspace, const std::string &transname, const mesh &m,
2486  const std::string &dispname, size_type region) {
2487  raytracing_interpolate_transformation *p
2488  = dynamic_cast<raytracing_interpolate_transformation *>
2489  (const_cast<virtual_interpolate_transformation *>
2490  (&(*(workspace.interpolate_transformation(transname)))));
2491  p->add_contact_boundary(workspace, m, dispname, region, true);
2492  }
2493 
2495  (model &md, const std::string &transname,
2496  const std::string &expr, size_type N) {
2497  raytracing_interpolate_transformation *p
2498  = dynamic_cast<raytracing_interpolate_transformation *>
2499  (const_cast<virtual_interpolate_transformation *>
2500  (&(*(md.interpolate_transformation(transname)))));
2501  p->add_rigid_obstacle(md, expr, N);
2502  }
2503 
2505  (ga_workspace &workspace, const std::string &transname,
2506  const std::string &expr, size_type N) {
2507  raytracing_interpolate_transformation *p
2508  = dynamic_cast<raytracing_interpolate_transformation *>
2509  (const_cast<virtual_interpolate_transformation *>
2510  (&(*(workspace.interpolate_transformation(transname)))));
2511  p->add_rigid_obstacle(workspace, expr, N);
2512  }
2513 
2515  (model &md, const std::string &transname, scalar_type d) {
2516  pinterpolate_transformation
2517  p = std::make_shared<projection_interpolate_transformation>(d);
2518  md.add_interpolate_transformation(transname, p);
2519  }
2520 
2522  (ga_workspace &workspace, const std::string &transname, scalar_type d) {
2523  pinterpolate_transformation
2524  p = std::make_shared<projection_interpolate_transformation>(d);
2525  workspace.add_interpolate_transformation(transname, p);
2526  }
2527 
2529  (model &md, const std::string &transname, const mesh &m,
2530  const std::string &dispname, size_type region) {
2531  projection_interpolate_transformation *p
2532  = dynamic_cast<projection_interpolate_transformation *>
2533  (const_cast<virtual_interpolate_transformation *>
2534  (&(*(md.interpolate_transformation(transname)))));
2535  p->add_contact_boundary(md, m, dispname, region, false);
2536  }
2537 
2539  (model &md, const std::string &transname, const mesh &m,
2540  const std::string &dispname, size_type region) {
2541  projection_interpolate_transformation *p
2542  = dynamic_cast<projection_interpolate_transformation *>
2543  (const_cast<virtual_interpolate_transformation *>
2544  (&(*(md.interpolate_transformation(transname)))));
2545  p->add_contact_boundary(md, m, dispname, region, true);
2546  }
2547 
2549  (ga_workspace &workspace, const std::string &transname, const mesh &m,
2550  const std::string &dispname, size_type region) {
2551  projection_interpolate_transformation *p
2552  = dynamic_cast<projection_interpolate_transformation *>
2553  (const_cast<virtual_interpolate_transformation *>
2554  (&(*(workspace.interpolate_transformation(transname)))));
2555  p->add_contact_boundary(workspace, m, dispname, region, false);
2556  }
2557 
2559  (ga_workspace &workspace, const std::string &transname, const mesh &m,
2560  const std::string &dispname, size_type region) {
2561  projection_interpolate_transformation *p
2562  = dynamic_cast<projection_interpolate_transformation *>
2563  (const_cast<virtual_interpolate_transformation *>
2564  (&(*(workspace.interpolate_transformation(transname)))));
2565  p->add_contact_boundary(workspace, m, dispname, region, true);
2566  }
2567 
2569  (model &md, const std::string &transname,
2570  const std::string &expr, size_type N) {
2571  projection_interpolate_transformation *p
2572  = dynamic_cast<projection_interpolate_transformation *>
2573  (const_cast<virtual_interpolate_transformation *>
2574  (&(*(md.interpolate_transformation(transname)))));
2575  p->add_rigid_obstacle(md, expr, N);
2576  }
2577 
2579  (ga_workspace &workspace, const std::string &transname,
2580  const std::string &expr, size_type N) {
2581  projection_interpolate_transformation *p
2582  = dynamic_cast<projection_interpolate_transformation *>
2583  (const_cast<virtual_interpolate_transformation *>
2584  (&(*(workspace.interpolate_transformation(transname)))));
2585  p->add_rigid_obstacle(workspace, expr, N);
2586  }
2587 
2588 
2589 
2590  //=========================================================================
2591  //
2592  // Specific nonlinear operator of the high-level generic assembly language
2593  // dedicated to contact/friction
2594  //
2595  //=========================================================================
2596 
2597  // static void ga_init_scalar(bgeot::multi_index &mi) { mi.resize(0); }
2598  static void ga_init_vector(bgeot::multi_index &mi, size_type N)
2599  { mi.resize(1); mi[0] = N; }
2600  // static void ga_init_square_matrix(bgeot::multi_index &mi, size_type N)
2601  // { mi.resize(2); mi[0] = mi[1] = N; }
2602 
2603 
2604  // Transformed_unit_vector(Grad_u, n) = (I+Grad_u)^{-T}n / ||(I+Grad_u)^{-T}n||
2605  struct Transformed_unit_vector : public ga_nonlinear_operator {
2606  bool result_size(const arg_list &args, bgeot::multi_index &sizes) const {
2607  if (args.size() != 2 || args[0]->sizes().size() != 2
2608  || args[1]->size() != args[0]->sizes()[0]
2609  || args[0]->sizes()[0] != args[0]->sizes()[1]) return false;
2610  ga_init_vector(sizes, args[0]->sizes()[0]);
2611  return true;
2612  }
2613 
2614  // Value : (I+Grad_u)^{-T}n / ||(I+Grad_u)^{-T}n||
2615  void value(const arg_list &args, base_tensor &result) const {
2616  size_type N = args[0]->sizes()[0];
2617  base_matrix F(N, N);
2618  gmm::copy(args[0]->as_vector(), F.as_vector());
2619  gmm::add(gmm::identity_matrix(), F);
2620  bgeot::lu_inverse(&(*(F.begin())), N);
2621  gmm::mult(gmm::transposed(F), args[1]->as_vector(), result.as_vector());
2622  gmm::scale(result.as_vector(),
2623  scalar_type(1)/gmm::vect_norm2(result.as_vector()));
2624  }
2625 
2626  // Derivative / Grad_u: -(I - n@n)(I+Grad_u)^{-T}Test_Grad_u n
2627  // Implementation: A{ijk} = -G{ik}ndef{j}
2628  // with G = (I - n@n)(I+Grad_u)^{-T}
2629  // and ndef the transformed normal
2630  // Derivative / n: ((I+Grad_u)^{-T}Test_n
2631  // - ndef(ndef.Test_n))/||(I+Grad_u)^{-T}n||
2632  // Implementation: A{ij} = (F{ij} - ndef{i}ndef{j})/norm_ndef
2633  // with F = (I+Grad_u)^{-1}
2634  void derivative(const arg_list &args, size_type nder,
2635  base_tensor &result) const {
2636  size_type N = args[0]->sizes()[0];
2637  base_matrix F(N, N), G(N, N);
2638  base_small_vector ndef(N), aux(N);
2639  gmm::copy(args[0]->as_vector(), F.as_vector());
2640  gmm::add(gmm::identity_matrix(), F);
2641  bgeot::lu_inverse(&(*(F.begin())), N);
2642  gmm::mult(gmm::transposed(F), args[1]->as_vector(), ndef);
2643  scalar_type norm_ndef = gmm::vect_norm2(ndef);
2644  gmm::scale(ndef, scalar_type(1)/norm_ndef);
2645  gmm::copy(gmm::transposed(F), G);
2646  gmm::mult(F, ndef, aux);
2647  gmm::rank_one_update(G, gmm::scaled(ndef, scalar_type(-1)), aux);
2648  base_tensor::iterator it = result.begin();
2649  switch (nder) {
2650  case 1:
2651  for (size_type k = 0; k < N; ++k)
2652  for (size_type j = 0; j < N; ++j)
2653  for (size_type i = 0; i < N; ++i, ++it)
2654  *it = -G(i, k) * ndef[j];
2655  break;
2656  case 2:
2657  for (size_type j = 0; j < N; ++j)
2658  for (size_type i = 0; i < N; ++i, ++it)
2659  *it = (F(j,i) - ndef[i]*ndef[j])/norm_ndef;
2660  break;
2661  default: GMM_ASSERT1(false, "Internal error");
2662  }
2663  GMM_ASSERT1(it == result.end(), "Internal error");
2664  }
2665 
2666  // Second derivative : not implemented
2667  void second_derivative(const arg_list &, size_type, size_type,
2668  base_tensor &) const {
2669  GMM_ASSERT1(false, "Sorry, second derivative not implemented");
2670  }
2671  };
2672 
2673 
2674  // Coulomb_friction_coupled_projection(lambda, n, Vs, g, f, r)
2675  struct Coulomb_friction_coupled_projection : public ga_nonlinear_operator {
2676  bool result_size(const arg_list &args, bgeot::multi_index &sizes) const {
2677  if (args.size() != 6) return false;
2678  size_type N = args[0]->size();
2679  if (N == 0 || args[1]->size() != N || args[2]->size() != N
2680  || args[3]->size() != 1 || args[4]->size() > 3
2681  || args[4]->size() == 0 || args[5]->size() != 1 ) return false;
2682  ga_init_vector(sizes, N);
2683  return true;
2684  }
2685 
2686  // Value : (lambda.n+rg)_- n - P_B(n, f(lambda.n+rg)_-)(lambda-r Vs)
2687  void value(const arg_list &args, base_tensor &result) const {
2688  const base_vector &lambda = *(args[0]);
2689  const base_vector &n = *(args[1]);
2690  const base_vector &Vs = *(args[2]);
2691  base_vector &F = result;
2692  scalar_type g = (*(args[3]))[0];
2693  const base_vector &f = *(args[4]);
2694  scalar_type r = (*(args[5]))[0];
2695 
2696 
2697  scalar_type nn = gmm::vect_norm2(n);
2698  scalar_type lambdan = gmm::vect_sp(lambda, n)/nn;
2699  scalar_type lambdan_aug = gmm::neg(lambdan + r * g);
2700  size_type s_f = gmm::vect_size(f);
2701  scalar_type tau = ((s_f >= 3) ? f[2] : scalar_type(0)) + f[0]*lambdan_aug;
2702  if (s_f >= 2) tau = std::min(tau, f[1]);
2703 
2704  if (tau > scalar_type(0)) {
2705  gmm::add(lambda, gmm::scaled(Vs, -r), F);
2706  scalar_type mu = gmm::vect_sp(F, n)/nn;
2707  gmm::add(gmm::scaled(n, -mu/nn), F);
2708  scalar_type norm = gmm::vect_norm2(F);
2709  if (norm > tau) gmm::scale(F, tau / norm);
2710  } else { gmm::clear(F); }
2711 
2712  gmm::add(gmm::scaled(n, -lambdan_aug/nn), F);
2713  }
2714 
2715  // Derivative / Grad_u: -(I - n@n)(I+Grad_u)^{-T}Test_Grad_u n
2716  // Implementation: A{ijk} = -G{kj}ndef{i}
2717  // with G = (I - n@n)(I+Grad_u)^{-T}
2718  // and ndef the transformed normal
2719  // Derivative / n: ((I+Grad_u)^{-T}Test_n
2720  // - ndef(ndef.Test_n))/||(I+Grad_u)^{-T}n||
2721  // Implementation: A{ij} = (F{ij} - ndef{i}ndef{j})/norm_ndef
2722  // with F = (I+Grad_u)^{-1}
2723  void derivative(const arg_list &args, size_type nder,
2724  base_tensor &result) const { // Can be optimized ?
2725  size_type N = args[0]->size();
2726  const base_vector &lambda = *(args[0]);
2727  const base_vector &n = *(args[1]);
2728  const base_vector &Vs = *(args[2]);
2729  base_vector F(N), dg(N);
2730  base_matrix dVs(N,N), dn(N,N);
2731  scalar_type g = (*(args[3]))[0];
2732  const base_vector &f = *(args[4]);
2733  scalar_type r = (*(args[5]))[0];
2734 
2735  scalar_type nn = gmm::vect_norm2(n);
2736  scalar_type lambdan = gmm::vect_sp(lambda, n)/nn;
2737  scalar_type lambdan_aug = gmm::neg(lambdan + r * g);
2738  size_type s_f = gmm::vect_size(f);
2739  scalar_type tau = ((s_f >= 3) ? f[2] : scalar_type(0))+f[0]*lambdan_aug;
2740  if (s_f >= 2) tau = std::min(tau, f[1]);
2741  scalar_type norm(0);
2742 
2743  if (tau > scalar_type(0)) {
2744  gmm::add(lambda, gmm::scaled(Vs, -r), F);
2745  scalar_type mu = gmm::vect_sp(F, n)/nn;
2746  gmm::add(gmm::scaled(n, -mu/nn), F);
2747  norm = gmm::vect_norm2(F);
2748  gmm::copy(gmm::identity_matrix(), dn);
2749  gmm::scale(dn, -mu/nn);
2750  gmm::rank_one_update(dn, gmm::scaled(n, mu/(nn*nn*nn)), n);
2751  gmm::rank_one_update(dn, gmm::scaled(n, scalar_type(-1)/(nn*nn)), F);
2752  gmm::copy(gmm::identity_matrix(), dVs);
2753  gmm::rank_one_update(dVs, n, gmm::scaled(n, scalar_type(-1)/(nn*nn)));
2754 
2755  if (norm > tau) {
2756  gmm::rank_one_update(dVs, F,
2757  gmm::scaled(F, scalar_type(-1)/(norm*norm)));
2758  gmm::scale(dVs, tau / norm);
2759  gmm::copy(gmm::scaled(F, scalar_type(1)/norm), dg);
2760  gmm::rank_one_update(dn, gmm::scaled(F, mu/(norm*norm*nn)), F);
2761  gmm::scale(dn, tau / norm);
2762  gmm::scale(F, tau / norm);
2763  } // else gmm::clear(dg);
2764 
2765  } // else { gmm::clear(dg); gmm::clear(dVs); gmm::clear(F); gmm::clear(dn); }
2766  // At this stage, F = P_{B_T}, dVs = d_q P_{B_T}, dn = d_n P_{B_T}
2767  // and dg = d_tau P_{B_T}.
2768 
2769 
2770  base_tensor::iterator it = result.begin();
2771  switch (nder) {
2772  case 1: // Derivative with respect to lambda
2773  if (norm > tau && ((s_f <= 1) || tau < f[1]) &&
2774  ((s_f <= 2) || tau > f[2]))
2775  gmm::rank_one_update(dVs, dg, gmm::scaled(n, -f[0]/nn));
2776  if (lambdan_aug > scalar_type(0))
2777  gmm::rank_one_update(dVs, n, gmm::scaled(n, scalar_type(1)/(nn*nn)));
2778  for (size_type j = 0; j < N; ++j)
2779  for (size_type i = 0; i < N; ++i, ++it)
2780  *it = dVs(i, j);
2781  break;
2782  case 2: // Derivative with respect to n
2783  if (norm > tau && ((s_f <= 1) || tau < f[1])
2784  && ((s_f <= 2) || tau > f[2])) {
2785  gmm::rank_one_update(dn, dg, gmm::scaled(lambda, -f[0]/nn));
2786  gmm::rank_one_update(dn, dg, gmm::scaled(n, f[0]*lambdan/(nn*nn)));
2787  }
2788  if (lambdan_aug > scalar_type(0)) {
2789  gmm::rank_one_update(dn, gmm::scaled(n, scalar_type(1)/(nn*nn)),
2790  lambda);
2791  gmm::rank_one_update(dn,
2792  gmm::scaled(n,(lambdan_aug-lambdan)/(nn*nn*nn)), n);
2793  for (size_type j = 0; j < N; ++j) dn(j,j) -= lambdan_aug/nn;
2794  }
2795  for (size_type j = 0; j < N; ++j)
2796  for (size_type i = 0; i < N; ++i, ++it)
2797  *it = dn(i, j);
2798  break;
2799  case 3:
2800  gmm::scale(dVs, -r);
2801  for (size_type j = 0; j < N; ++j)
2802  for (size_type i = 0; i < N; ++i, ++it)
2803  *it = dVs(i, j);
2804  break;
2805  case 4:
2806  if (norm > tau && ((s_f <= 1) || tau < f[1])
2807  && ((s_f <= 2) || tau > f[2]))
2808  gmm::scale(dg, -f[0]*r);
2809  else
2810  gmm::clear(dg);
2811  if (lambdan_aug > scalar_type(0))
2812  gmm::add(gmm::scaled(n, r/nn), dg);
2813  for (size_type i = 0; i < N; ++i, ++it)
2814  *it = dg[i];
2815  break;
2816  case 5:
2817  if (norm > tau && ((s_f <= 1) || tau < f[1])
2818  && ((s_f <= 2) || tau > f[2]))
2819  gmm::scale(dg, -f[0]*g);
2820  else
2821  gmm::clear(dg);
2822  gmm::mult_add(dVs, gmm::scaled(Vs, scalar_type(-1)), dg);
2823  if (lambdan_aug > scalar_type(0))
2824  gmm::add(gmm::scaled(n, g/nn), dg);
2825  for (size_type i = 0; i < N; ++i, ++it)
2826  *it = dg[i];
2827  it = result.end();
2828  break;
2829  case 6:
2830  base_small_vector dtau_df(s_f);
2831  if ((s_f <= 1) || tau < f[1]) dtau_df[0] = lambdan_aug;
2832  if (s_f >= 2 && tau == f[1]) dtau_df[1] = 1;
2833  if (s_f >= 3 && tau < f[1]) dtau_df[2] = 1;
2834  for (size_type j = 0; j < s_f; ++j)
2835  for (size_type i = 0; i < N; ++i, ++it)
2836  *it = dg[i] * dtau_df[j];
2837  break;
2838  }
2839  GMM_ASSERT1(it == result.end(), "Internal error");
2840  }
2841 
2842  // Second derivative : not implemented
2843  void second_derivative(const arg_list &, size_type, size_type,
2844  base_tensor &) const {
2845  GMM_ASSERT1(false, "Sorry, second derivative not implemented");
2846  }
2847  };
2848 
2849  static bool init_predef_operators() {
2850 
2851  ga_predef_operator_tab &PREDEF_OPERATORS
2853 
2854  PREDEF_OPERATORS.add_method("Transformed_unit_vector",
2855  std::make_shared<Transformed_unit_vector>());
2856  PREDEF_OPERATORS.add_method("Coulomb_friction_coupled_projection",
2857  std::make_shared<Coulomb_friction_coupled_projection>());
2858 
2859  return true;
2860  }
2861 
2862  // declared in getfem_generic_assembly.cc
2863  bool predef_operators_contact_initialized
2864  = init_predef_operators();
2865 
2866 } /* end of namespace getfem. */
void add_slave_contact_boundary_to_raytracing_transformation(model &md, const std::string &transname, const mesh &m, const std::string &dispname, size_type region)
Add a slave boundary with corresponding displacement variable &#39;dispname&#39; on a specific boundary &#39;regi...
void slice_vector_on_basic_dof_of_element(const mesh_fem &mf, const VEC1 &vec, size_type cv, VEC2 &coeff, size_type qmult1=size_type(-1), size_type qmult2=size_type(-1))
Given a mesh_fem.
void add_projection_transformation(model &md, const std::string &transname, scalar_type release_distance)
Add a projection interpolate transformation called &#39;transname&#39; to a model to be used by the generic a...
void add_slave_contact_boundary_to_projection_transformation(model &md, const std::string &transname, const mesh &m, const std::string &dispname, size_type region)
Add a slave boundary with corresponding displacement variable &#39;dispname&#39; on a specific boundary &#39;regi...
The Iteration object calculates whether the solution has reached the desired accuracy, or whether the maximum number of iterations has been reached.
Definition: gmm_iter.h:53
Describe a mesh (collection of convexes (elements) and points).
Definition: getfem_mesh.h:95
void fill_random(L &l, double cfill)
*/
Definition: gmm_blas.h:154
number_traits< typename linalg_traits< V1 >::value_type >::magnitude_type vect_dist2(const V1 &v1, const V2 &v2)
Euclidean distance between two vectors.
Definition: gmm_blas.h:597
pinterpolate_transformation interpolate_transformation(const std::string &name) const
Get a pointer to the interpolate transformation name.
static T & instance()
Instance from the current thread.
number_traits< typename linalg_traits< V >::value_type >::magnitude_type vect_norm2(const V &v)
Euclidean norm of a vector.
Definition: gmm_blas.h:557
``Model&#39;&#39; variables store the variables, the data and the description of a model. ...
std::shared_ptr< const convex_structure > pconvex_structure
Pointer on a convex structure description.
size_t size_type
used as the common size type in the library
Definition: bgeot_poly.h:49
void add_master_contact_boundary_to_raytracing_transformation(model &md, const std::string &transname, const mesh &m, const std::string &dispname, size_type region)
Add a master boundary with corresponding displacement variable &#39;dispname&#39; on a specific boundary &#39;reg...
"iterator" class for regions.
void add_interpolate_transformation(const std::string &name, pinterpolate_transformation ptrans)
Add a interpolate transformation to the model to be used with the generic assembly.
A langage for generic assembly of pde boundary value problems.
GEneric Tool for Finite Element Methods.
std::shared_ptr< const getfem::virtual_fem > pfem
type of pointer on a fem description
Definition: getfem_fem.h:239
Describe a finite element method linked to a mesh.
void clear(L &l)
clear (fill with zeros) a vector or matrix.
Definition: gmm_blas.h:59
gmm::uint16_type short_type
used as the common short type integer in the library
Definition: bgeot_config.h:79
Comomon tools for unilateral contact and Coulomb friction bricks.
base_small_vector compute_normal(const geotrans_interpolation_context &c, size_type face)
norm of returned vector is the ratio between the face surface on the real element and the face surfac...
void add_rigid_obstacle_to_raytracing_transformation(model &md, const std::string &transname, const std::string &expr, size_type N)
Add a rigid obstacle whose geometry corresponds to the zero level-set of the high-level generic assem...
void add_raytracing_transformation(model &md, const std::string &transname, scalar_type release_distance)
Add a raytracing interpolate transformation called &#39;transname&#39; to a model to be used by the generic a...
void add_rigid_obstacle_to_projection_transformation(model &md, const std::string &transname, const std::string &expr, size_type N)
Add a rigid obstacle whose geometry corresponds to the zero level-set of the high-level generic assem...
size_type alpha(short_type n, short_type d)
Return the value of which is the number of monomials of a polynomial of variables and degree ...
Definition: bgeot_poly.cc:46
Balanced tree of n-dimensional rectangles.
Definition: bgeot_rtree.h:66
void resize(M &v, size_type m, size_type n)
*/
Definition: gmm_blas.h:231
void add_master_contact_boundary_to_projection_transformation(model &md, const std::string &transname, const mesh &m, const std::string &dispname, size_type region)
Add a master boundary with corresponding displacement variable &#39;dispname&#39; on a specific boundary &#39;reg...
std::shared_ptr< const bgeot::geometric_trans > pgeometric_trans
pointer type for a geometric transformation