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