30   bool boundary_has_fem_nodes(
bool slave_flag, 
int nodes_mode) {
 
   31     return (slave_flag && nodes_mode) ||
 
   32            (!slave_flag && nodes_mode == 2);
 
   37                       const model_real_plain_vector &coeff,
 
   38                       base_node &n0, base_node &n,
 
   41       if (in_reference_conf) {
 
   44         ctx.pf()->interpolation_grad(ctx, coeff, grad, dim_type(ctx.N()));
 
   45         gmm::add(gmm::identity_matrix(), grad);
 
   46         scalar_type J = bgeot::lu_inverse(&(*(grad.begin())), ctx.N());
 
   47         if (J <= scalar_type(0)) {GMM_WARNING1(
"Inverted element !" << J);
 
   49             gmm::scale(n, gmm::sgn(J));
 
   55         gmm::scale(n, gmm::sgn(J));} 
 
   67   (
const model_real_plain_vector *U, 
const std::string &name,
 
   68    const model_real_plain_vector *w, 
const std::string &wname) {
 
   71     for (; i < Us.size(); ++i) 
if (Us[i] == U) 
return i;
 
   74     Unames.push_back(name);
 
   75     Wnames.push_back(wname);
 
   76     ext_Us.resize(Us.size());
 
   77     ext_Ws.resize(Us.size());
 
   82   (
const model_real_plain_vector *lambda, 
const std::string &name) {
 
   85     for (; i < lambdas.size(); ++i) 
if (lambdas[i] == lambda) 
return i;
 
   86     lambdas.push_back(lambda);
 
   87     lambdanames.push_back(name);
 
   88     ext_lambdas.resize(lambdas.size());
 
   92   void multi_contact_frame::extend_vectors() {
 
   93     dal::bit_vector iU, ilambda;
 
   94     for (
size_type i = 0; i < contact_boundaries.size(); ++i) {
 
   95       size_type ind_U = contact_boundaries[i].ind_U;
 
   97         const mesh_fem &mf = *(contact_boundaries[i].mfu);
 
   99         mf.extend_vector(*(Us[ind_U]), ext_Us[ind_U]);
 
  102           mf.extend_vector(*(Ws[ind_U]), ext_Ws[ind_U]);
 
  106       size_type ind_lambda = contact_boundaries[i].ind_lambda;
 
  107       if (ind_lambda != 
size_type(-1) && !(ilambda[ind_lambda])) {
 
  108         const mesh_fem &mf = *(contact_boundaries[i].mflambda);
 
  109         gmm::resize(ext_lambdas[ind_lambda], mf.nb_basic_dof());
 
  110         mf.extend_vector(*(lambdas[ind_lambda]), ext_lambdas[ind_lambda]);
 
  111         ilambda.add(ind_lambda);
 
  116   void multi_contact_frame::normal_cone_simplification() {
 
  118       scalar_type threshold = ::cos(cut_angle);
 
  119       for (
size_type i = 0; i < boundary_points_info.size(); ++i) {
 
  120         normal_cone &nc = boundary_points_info[i].normals;
 
  122           base_small_vector n_mean = nc[0];
 
  123           for (
size_type j = 1; j < nc.size(); ++j) n_mean += nc[j];
 
  125           GMM_ASSERT1(nn_mean != scalar_type(0), 
"oupssss");
 
  126           if (nn_mean != scalar_type(0)) {
 
  127             gmm::scale(n_mean, scalar_type(1)/nn_mean);
 
  129             for (
size_type j = 0; j < nc.size(); ++j)
 
  130               if (gmm::vect_sp(n_mean, nc[j]) < threshold)
 
  131                 { reduce = 
false; 
break; }
 
  133               boundary_points_info[i].normals = normal_cone(n_mean);
 
  141   bool multi_contact_frame::test_normal_cones_compatibility
 
  142   (
const normal_cone &nc1, 
const normal_cone &nc2) {
 
  143     for (
size_type i = 0; i < nc1.size(); ++i)
 
  144       for (
size_type j = 0; j < nc2.size(); ++j)
 
  145         if (gmm::vect_sp(nc1[i], nc2[j]) < scalar_type(0))
 
  150   bool multi_contact_frame::test_normal_cones_compatibility
 
  151   (
const base_small_vector &n, 
const normal_cone &nc2) {
 
  152     for (
size_type j = 0; j < nc2.size(); ++j)
 
  153       if (gmm::vect_sp(n, nc2[j]) < scalar_type(0))
 
  160     const mesh_fem &mf1 = mfdisp_of_boundary(ib1);
 
  161     const mesh_fem &mf2 = mfdisp_of_boundary(ib2);
 
  162     if ( &(mf1.linked_mesh()) != &(mf2.linked_mesh())) 
return false;
 
  163     GMM_ASSERT1(!(mf1.is_reduced()) && !(mf2.is_reduced()),
 
  164                 "Nodal strategy can only be applied for non reduced fems");
 
  165     const mesh::ind_cv_ct &ic1 = mf1.convex_to_basic_dof(idof1);
 
  166     const mesh::ind_cv_ct &ic2 = mf2.convex_to_basic_dof(idof2);
 
  168     for (
size_type i = 0; i < ic1.size(); ++i) aux_dof_cv.add(ic1[i]);
 
  169     for (
size_type i = 0; i < ic2.size(); ++i)
 
  170       if (aux_dof_cv.is_in(ic2[i])) { lk = 
true; 
break; }
 
  171     for (
size_type i = 0; i < ic1.size(); ++i) aux_dof_cv.sup(ic1[i]);
 
  177     const mesh_fem &mf1 = mfdisp_of_boundary(ib1);
 
  178     const mesh_fem &mf2 = mfdisp_of_boundary(ib2);
 
  179     if ( &(mf1.linked_mesh()) != &(mf2.linked_mesh())) 
return false;
 
  180     GMM_ASSERT1(!(mf1.is_reduced()) && !(mf2.is_reduced()),
 
  181                 "Nodal strategy can only be applied for non reduced fems");
 
  182     const mesh::ind_cv_ct &ic1 = mf1.convex_to_basic_dof(idof1);
 
  183     for (
size_type i = 0; i < ic1.size(); ++i)
 
  184       if (cv == ic1[i]) 
return true;
 
  188   void multi_contact_frame::add_potential_contact_face
 
  191     std::vector<face_info> &sfi = potential_pairs[ip];
 
  192     for (
size_type k = 0; k < sfi.size(); ++k)
 
  193       if (sfi[k].ind_boundary == ib &&
 
  194           sfi[k].ind_element == ie &&
 
  195           sfi[k].ind_face == iff) found = 
true;
 
  197     if (!found) sfi.push_back(face_info(ib, ie, iff));
 
  200   void multi_contact_frame::clear_aux_info() {
 
  201     boundary_points = std::vector<base_node>();
 
  202     boundary_points_info = std::vector<boundary_point>();
 
  203     element_boxes.clear();
 
  204     element_boxes_info = std::vector<influence_box>();
 
  205     potential_pairs = std::vector<std::vector<face_info> >();
 
  208   multi_contact_frame::multi_contact_frame(
size_type NN, scalar_type r_dist,
 
  209                                            bool dela, 
bool selfc,
 
  211                                            bool rayt, 
int nmode, 
bool refc)
 
  212     : N(NN), self_contact(selfc), ref_conf(refc), use_delaunay(dela),
 
  213       nodes_mode(nmode), raytrace(rayt), release_distance(r_dist),
 
  214       cut_angle(cut_a), EPS(1E-8), md(0), coordinates(N), pt(N) {
 
  215     if (N > 0) coordinates[0] = 
"x";
 
  216     if (N > 1) coordinates[1] = 
"y";
 
  217     if (N > 2) coordinates[2] = 
"z";
 
  218     if (N > 3) coordinates[3] = 
"w";
 
  219     GMM_ASSERT1(N <= 4, 
"Complete the definition for contact in " 
  220                   "dimension greater than 4");
 
  223   multi_contact_frame::multi_contact_frame(
const model &mdd, 
size_type NN,
 
  225                                            bool dela, 
bool selfc,
 
  227                                            bool rayt, 
int nmode, 
bool refc)
 
  228     : N(NN), self_contact(selfc), ref_conf(refc),
 
  229       use_delaunay(dela), nodes_mode(nmode), raytrace(rayt),
 
  230       release_distance(r_dist), cut_angle(cut_a), EPS(1E-8), md(&mdd),
 
  231       coordinates(N), pt(N) {
 
  232     if (N > 0) coordinates[0] = 
"x";
 
  233     if (N > 1) coordinates[1] = 
"y";
 
  234     if (N > 2) coordinates[2] = 
"z";
 
  235     if (N > 3) coordinates[3] = 
"w";
 
  236     GMM_ASSERT1(N <= 4, 
"Complete the definition for contact in " 
  237                   "dimension greater than 4");
 
  240   size_type multi_contact_frame::add_obstacle(
const std::string &obs) {
 
  242     obstacles.push_back(obs);
 
  243     obstacles_velocities.push_back(
"");
 
  244     obstacles_gw.push_back(ga_workspace());
 
  245     pt.resize(N); ptx.resize(1); pty.resize(1); ptz.resize(1); ptw.resize(1);
 
  246     obstacles_gw.back().add_fixed_size_constant(
"X", pt);
 
  247     if (N >= 4) obstacles_gw.back().add_fixed_size_constant(
"w", ptw);
 
  248     if (N >= 3) obstacles_gw.back().add_fixed_size_constant(
"z", ptz);
 
  249     if (N >= 2) obstacles_gw.back().add_fixed_size_constant(
"y", pty);
 
  250     if (N >= 1) obstacles_gw.back().add_fixed_size_constant(
"x", ptx);
 
  251     obstacles_f.push_back(ga_function(obstacles_gw.back(), obs));
 
  252     obstacles_f.back().compile();
 
  258   size_type multi_contact_frame::add_master_boundary
 
  259   (
const mesh_im &mim, 
const mesh_fem *mfu,
 
  260    const model_real_plain_vector *U, 
size_type reg,
 
  261    const mesh_fem *mflambda, 
const model_real_plain_vector *lambda,
 
  262    const model_real_plain_vector *w,
 
  263    const std::string &vvarname,
 
  264    const std::string &mmultname, 
const std::string &wname) {
 
  265     GMM_ASSERT1(mfu->linked_mesh().dim() == N,
 
  266                 "Mesh dimension is " << mfu->linked_mesh().dim()
 
  267                 << 
"should be " << N << 
".");
 
  268     GMM_ASSERT1(&(mfu->linked_mesh()) == &(mim.linked_mesh()),
 
  269                 "Integration and finite element are not on the same mesh !");
 
  271       GMM_ASSERT1(&(mflambda->linked_mesh()) == &(mim.linked_mesh()),
 
  272                   "Integration and finite element are not on the same mesh !");
 
  273     contact_boundary cb(reg, mfu, mim, add_U(U, vvarname, w, wname),
 
  274                         mflambda, add_lambda(lambda, mmultname));
 
  275     contact_boundaries.push_back(cb);
 
  276     return size_type(contact_boundaries.size() - 1);
 
  279   size_type multi_contact_frame::add_slave_boundary
 
  280   (
const mesh_im &mim, 
const mesh_fem *mfu,
 
  281    const model_real_plain_vector *U, 
size_type reg,
 
  282    const mesh_fem *mflambda, 
const model_real_plain_vector *lambda,
 
  283    const model_real_plain_vector *w,
 
  284    const std::string &vvarname,
 
  285    const std::string &mmultname, 
const std::string &wname) {
 
  287       = add_master_boundary(mim, mfu, U, reg, mflambda, lambda, w,
 
  288                             vvarname, mmultname, wname);
 
  289     contact_boundaries[ind].slave = 
true;
 
  294   size_type multi_contact_frame::add_master_boundary
 
  295   (
const mesh_im &mim, 
size_type reg, 
const std::string &vvarname,
 
  296    const std::string &mmultname, 
const std::string &wname) {
 
  297     GMM_ASSERT1(md, 
"This multi contact frame object is not linked " 
  299     const mesh_fem *mfl(0);
 
  300     const model_real_plain_vector *l(0);
 
  301     if (mmultname.size()) {
 
  302       mfl = &(md->mesh_fem_of_variable(mmultname));
 
  303       l = &(md->real_variable(mmultname));
 
  305     const model_real_plain_vector *w(0);
 
  306     if (wname.compare(vvarname) == 0) {
 
  307       GMM_ASSERT1(md->n_iter_of_variable(vvarname) > 1, 
"More than one " 
  308                  "versions of the displacement variable were expected here");
 
  309       w = &(md->real_variable(vvarname,1));
 
  311     else if (wname.size()) {
 
  312       GMM_ASSERT1(&(md->mesh_fem_of_variable(wname))
 
  313                  == &(md->mesh_fem_of_variable(vvarname)), 
"The previous " 
  314                  "displacement should be defined on the same mesh_fem as the " 
  316       w = &(md->real_variable(wname));
 
  318     return add_master_boundary(mim, &(md->mesh_fem_of_variable(vvarname)),
 
  319                                &(md->real_variable(vvarname)), reg, mfl, l, w,
 
  320                                vvarname, mmultname, wname);
 
  323   size_type multi_contact_frame::add_slave_boundary
 
  324   (
const mesh_im &mim, 
size_type reg, 
const std::string &vvarname,
 
  325    const std::string &mmultname, 
const std::string &wname) {
 
  326     GMM_ASSERT1(md, 
"This multi contact frame object is not linked " 
  328     const mesh_fem *mfl(0);
 
  329     const model_real_plain_vector *l(0);
 
  330     if (mmultname.size()) {
 
  331       mfl = &(md->mesh_fem_of_variable(mmultname));
 
  332       l = &(md->real_variable(mmultname));
 
  334     const model_real_plain_vector *w(0);
 
  335     if (wname.compare(vvarname) == 0) {
 
  336       GMM_ASSERT1(md->n_iter_of_variable(vvarname) > 1, 
"More than one " 
  337                  "versions of the displacement variable were expected here");
 
  338       w = &(md->real_variable(vvarname,1));
 
  340     else if (wname.size()) {
 
  341       GMM_ASSERT1(&(md->mesh_fem_of_variable(wname))
 
  342                  == &(md->mesh_fem_of_variable(vvarname)), 
"The previous " 
  343                  "displacement should be defined on the same mesh_fem as the " 
  345       w = &(md->real_variable(wname));
 
  347     return add_slave_boundary(mim, &(md->mesh_fem_of_variable(vvarname)),
 
  348                               &(md->real_variable(vvarname)), reg, mfl, l, w,
 
  349                               vvarname, mmultname, wname);
 
  353   void multi_contact_frame::compute_boundary_points(
bool slave_only) {
 
  354     fem_precomp_pool fppool;
 
  356     model_real_plain_vector coeff;
 
  358     for (
size_type i = 0; i < contact_boundaries.size(); ++i)
 
  359       if (!slave_only || is_slave_boundary(i)) {
 
  361         const mesh_fem &mfu = mfdisp_of_boundary(i);
 
  362         const mesh_im &mim = mim_of_boundary(i);
 
  363         const model_real_plain_vector &U = disp_of_boundary(i);
 
  364         const mesh &m = mfu.linked_mesh();
 
  366           boundary_has_fem_nodes(is_slave_boundary(i), nodes_mode);
 
  368         base_node val(N), bmin(N), bmax(N);
 
  369         base_small_vector n0(N), n(N), n_mean(N);
 
  370         base_matrix grad(N,N);
 
  371         mesh_region region = m.region(bnum);
 
  372         GMM_ASSERT1(mfu.get_qdim() == N, 
"Wrong mesh_fem qdim");
 
  375         dal::bit_vector dof_already_interpolated;
 
  376         std::vector<size_type> dof_ind(mfu.nb_basic_dof());
 
  380           pfem pf_s = mfu.fem_of_element(cv);
 
  384           mfu.linked_mesh().points_of_convex(cv, G);
 
  388           std::vector<size_type> indpt, indpfp;
 
  390             dim_type qqdim = mfu.get_qdim() / pf_s->target_dim();
 
  391             pfp = fppool(pf_s, pf_s->node_tab(cv));
 
  392             nbptf = pf_s->node_convex(cv).structure()->nb_points_of_face(v.f());
 
  393             indpt.resize(nbptf); indpfp.resize(nbptf);
 
  396                 mfu.ind_basic_dof_of_face_of_element(cv,v.f())[ip*qqdim];
 
  398                 pf_s->node_convex(cv).structure()->ind_points_of_face(v.f())[ip];
 
  402             pintegration_method pim = mim.int_method_of_element(cv);
 
  403             GMM_ASSERT1(pim, 
"Integration method should be defined");
 
  404             pfp = fppool(pf_s, pim->approx_method()->pintegration_points());
 
  405             nbptf = pim->approx_method()->nb_points_on_face(v.f());
 
  406             indpt.resize(nbptf); indpfp.resize(nbptf);
 
  408               indpt[ip] = indpfp[ip] =
 
  409                 pim->approx_method()->ind_first_point_on_face(v.f())+ip;
 
  411           fem_interpolation_context ctx(pgt,pfp,
size_type(-1),G,cv,v.f());
 
  414             ctx.set_ii(indpfp[ip]);
 
  417             if (!(on_fem_nodes && dof_already_interpolated[ind])) {
 
  419                 pf_s->interpolation(ctx, coeff, val, dim_type(N));
 
  424               if (on_fem_nodes) dof_ind[ind] = boundary_points.size();
 
  432             if (on_fem_nodes && dof_already_interpolated[ind]) {
 
  433               boundary_points_info[dof_ind[ind]].normals.add_normal(n);
 
  435               boundary_points.push_back(val);
 
  436               boundary_points_info.push_back(boundary_point(ctx.xreal(), i, cv,
 
  440             if (on_fem_nodes) dof_already_interpolated.add(ind);
 
  446   void multi_contact_frame::compute_potential_contact_pairs_delaunay() {
 
  448     compute_boundary_points();
 
  449     normal_cone_simplification();
 
  450     potential_pairs = std::vector<std::vector<face_info> >();
 
  451     potential_pairs.resize(boundary_points.size());
 
  453     gmm::dense_matrix<size_type> simplexes;
 
  454     base_small_vector rr(N);
 
  460     bgeot::qhull_delaunay(boundary_points, simplexes);
 
  463     for (
size_type is = 0; is < gmm::mat_ncols(simplexes); ++is) {
 
  467           size_type ipt1 = simplexes(i, is), ipt2 = simplexes(j, is);
 
  468           boundary_point *pt_info1 = &(boundary_points_info[ipt1]);
 
  469           boundary_point *pt_info2 = &(boundary_points_info[ipt2]);
 
  472           bool sl1 = is_slave_boundary(ib1);
 
  473           bool sl2 = is_slave_boundary(ib2);
 
  475             std::swap(ipt1, ipt2);
 
  476             std::swap(pt_info1, pt_info2);
 
  482           const mesh_fem &mf1 = mfdisp_of_boundary(ib1);
 
  483           const mesh_fem &mf2 = mfdisp_of_boundary(ib2);
 
  491                || (self_contact && !sl1 && !sl2))
 
  493               && test_normal_cones_compatibility(pt_info1->normals,
 
  499                       && (( &(mf1.linked_mesh()) != &(mf2.linked_mesh()))
 
  500                           || (pt_info1->ind_element != pt_info2->ind_element)))
 
  501                   || ((nodes_mode == 2)
 
  502                       && !(are_dof_linked(ib1, pt_info1->ind_pt,
 
  503                                           ib2, pt_info2->ind_pt)))
 
  509             if (boundary_has_fem_nodes(sl2, nodes_mode)) {
 
  510               const mesh::ind_cv_ct &ic2
 
  511                 = mf2.convex_to_basic_dof(pt_info2->ind_pt);
 
  512               for (
size_type k = 0; k < ic2.size(); ++k) {
 
  513                 mesh_region::face_bitset fbs
 
  514                   = mf2.linked_mesh().region(ir2).faces_of_convex(ic2[k]);
 
  515                 short_type nbf = mf2.linked_mesh().nb_faces_of_convex(ic2[k]);
 
  518                     add_potential_contact_face(ipt1,
 
  519                                                pt_info2->ind_boundary,
 
  523               add_potential_contact_face(ipt1, pt_info2->ind_boundary,
 
  524                                          pt_info2->ind_element,
 
  527             if (self_contact && !sl1 && !sl2) {
 
  528               if (boundary_has_fem_nodes(sl2, nodes_mode)) {
 
  529                 const mesh::ind_cv_ct &ic1
 
  530                   = mf1.convex_to_basic_dof(pt_info1->ind_pt);
 
  531                 for (
size_type k = 0; k < ic1.size(); ++k) {
 
  532                   mesh_region::face_bitset fbs
 
  533                     = mf1.linked_mesh().region(ir1).faces_of_convex(ic1[k]);
 
  534                   short_type nbf = mf1.linked_mesh().nb_faces_of_convex(ic1[k]);
 
  537                       add_potential_contact_face(ipt2,
 
  538                                                  pt_info1->ind_boundary,
 
  542                 add_potential_contact_face(ipt2, pt_info1->ind_boundary,
 
  543                                            pt_info1->ind_element,
 
  554   void multi_contact_frame::compute_influence_boxes() {
 
  555     fem_precomp_pool fppool;
 
  558     model_real_plain_vector coeff;
 
  560     for (
size_type i = 0; i < contact_boundaries.size(); ++i)
 
  561       if (!is_slave_boundary(i)) {
 
  563         const mesh_fem &mfu = mfdisp_of_boundary(i);
 
  564         const model_real_plain_vector &U = disp_of_boundary(i);
 
  565         const mesh &m = mfu.linked_mesh();
 
  567         base_node val(N), bmin(N), bmax(N);
 
  568         base_small_vector n0(N), n(N), n_mean(N);
 
  569         base_matrix grad(N,N);
 
  570         mesh_region region = m.region(bnum);
 
  571         GMM_ASSERT1(mfu.get_qdim() == N, 
"Wrong mesh_fem qdim");
 
  573         dal::bit_vector points_already_interpolated;
 
  574         std::vector<base_node> transformed_points(m.nb_max_points());
 
  578           pfem pf_s = mfu.fem_of_element(cv);
 
  579           pfem_precomp pfp = fppool(pf_s, pgt->pgeometric_nodes());
 
  582           mfu.linked_mesh().points_of_convex(cv, G);
 
  583           fem_interpolation_context ctx(pgt,pfp,
size_type(-1), G, cv);
 
  586           dal::bit_vector points_on_face;
 
  588           for (
size_type k = 0; k < cvs->nb_points_of_face(v.f()); ++k)
 
  589             points_on_face.add(cvs->ind_points_of_face(v.f())[k]);
 
  594             size_type ind = m.ind_points_of_convex(cv)[ip];
 
  598             if (!(points_already_interpolated.is_in(ind))) {
 
  600                 pf_s->interpolation(ctx, coeff, val, dim_type(N));
 
  602                 transformed_points[ind] = val;
 
  604                 transformed_points[ind] = ctx.xreal();
 
  606               points_already_interpolated.add(ind);
 
  608               val = transformed_points[ind];
 
  611             if (points_on_face[ip]) {
 
  622                 bmin[k] = std::min(bmin[k], val[k]);
 
  623                 bmax[k] = std::max(bmax[k], val[k]);
 
  630           GMM_ASSERT1(nb_pt_on_face,
 
  631                       "This element has no vertex on considered face !");
 
  635           scalar_type h = bmax[0] - bmin[0];
 
  636           for (
size_type k = 1; k < N; ++k) h = std::max(h, bmax[k]-bmin[k]);
 
  637           if (h < release_distance/scalar_type(40) && !avert) {
 
  638             GMM_WARNING1(
"Found an element whose size is smaller than 1/40 " 
  639                          "of the release distance. You should probably " 
  640                          "adapt the release distance.");
 
  644             { bmin[k] -= release_distance; bmax[k] += release_distance; }
 
  647           element_boxes.add_box(bmin, bmax, element_boxes_info.size());
 
  649           element_boxes_info.push_back(influence_box(i, cv, v.f(), n_mean));
 
  652     element_boxes.build_tree();
 
  655   void multi_contact_frame::compute_potential_contact_pairs_influence_boxes() {
 
  656     compute_influence_boxes();
 
  657     compute_boundary_points(!self_contact); 
 
  658     normal_cone_simplification();
 
  659     potential_pairs = std::vector<std::vector<face_info> >();
 
  660     potential_pairs.resize(boundary_points.size());
 
  662     for (
size_type ip = 0; ip < boundary_points.size(); ++ip) {
 
  664       bgeot::rtree::pbox_set bset;
 
  665       element_boxes.find_boxes_at_point(boundary_points[ip], bset);
 
  666       boundary_point *pt_info = &(boundary_points_info[ip]);
 
  667       const mesh_fem &mf1 = mfdisp_of_boundary(pt_info->ind_boundary);
 
  670       bgeot::rtree::pbox_set::iterator it = bset.begin();
 
  671       for (; it != bset.end(); ++it) {
 
  672         influence_box &ibx = element_boxes_info[(*it)->id];
 
  674         const mesh_fem &mf2 = mfdisp_of_boundary(ib2);
 
  679             test_normal_cones_compatibility(ibx.mean_normal,
 
  683             && (((nodes_mode < 2)
 
  684                  && (( &(mf1.linked_mesh()) != &(mf2.linked_mesh()))
 
  685                      || (pt_info->ind_element != ibx.ind_element)))
 
  686                 || ((nodes_mode == 2)
 
  687                     && !(is_dof_linked(ib1, pt_info->ind_pt,
 
  688                                        ibx.ind_boundary, ibx.ind_element)))
 
  692           add_potential_contact_face(ip, ibx.ind_boundary, ibx.ind_element,
 
  700   struct proj_pt_surf_cost_function_object {
 
  703     const base_node &x0, &x;
 
  704     fem_interpolation_context &ctx;
 
  705     const model_real_plain_vector &coeff;
 
  706     const std::vector<base_small_vector> &ti;
 
  708     mutable base_node dxy;
 
  709     mutable base_matrix grad, gradtot;
 
  711     scalar_type operator()(
const base_small_vector& a)
 const {
 
  713       for (
size_type i= 0; i < N-1; ++i) xx += a[i] * ti[i];
 
  716         ctx.pf()->interpolation(ctx, coeff, dxy, dim_type(N));
 
  717         dxy += ctx.xreal() - x;
 
  719         dxy = ctx.xreal() - x;
 
  723     scalar_type operator()(
const base_small_vector& a,
 
  724                            base_small_vector &grada)
 const {
 
  726       for (
size_type i = 0; i < N-1; ++i) xx += a[i] * ti[i];
 
  729         ctx.pf()->interpolation(ctx, coeff, dxy, dim_type(N));
 
  730         dxy += ctx.xreal() - x;
 
  731         ctx.pf()->interpolation_grad(ctx, coeff, grad, dim_type(N));
 
  732         gmm::add(gmm::identity_matrix(), grad);
 
  735         dxy = ctx.xreal() - x;
 
  739         grada[i] = gmm::vect_sp(gradtot, ti[i], dxy);
 
  742     void operator()(
const base_small_vector& a,
 
  743                     base_matrix &hessa)
 const {
 
  744       base_small_vector b = a;
 
  745       base_small_vector grada(N-1), gradb(N-1);
 
  751           hessa(j, i) = (gradb[j] - grada[j])/EPS;
 
  756     proj_pt_surf_cost_function_object
 
  757     (
const base_node &x00, 
const base_node &xx,
 
  758      fem_interpolation_context &ctxx,
 
  759      const model_real_plain_vector &coefff,
 
  760      const std::vector<base_small_vector> &tii,
 
  761      scalar_type EPSS, 
bool rc)
 
  762       : N(gmm::vect_size(x00)), EPS(EPSS), x0(x00), x(xx),
 
  763         ctx(ctxx), coeff(coefff), ti(tii), ref_conf(rc),
 
  764         dxy(N), grad(N,N), gradtot(N,N) {}
 
  768   struct raytrace_pt_surf_cost_function_object {
 
  770     const base_node &x0, &x;
 
  771     fem_interpolation_context &ctx;
 
  772     const model_real_plain_vector &coeff;
 
  773     const std::vector<base_small_vector> &ti;
 
  774     const std::vector<base_small_vector> &Ti;
 
  776     mutable base_node dxy;
 
  777     mutable base_matrix grad, gradtot;
 
  779     void operator()(
const base_small_vector& a,
 
  780                     base_small_vector &res)
 const {
 
  782       for (
size_type i = 0; i < N-1; ++i) xx += a[i] * ti[i];
 
  785         ctx.pf()->interpolation(ctx, coeff, dxy, dim_type(N));
 
  786         dxy += ctx.xreal() - x;
 
  788         dxy = ctx.xreal() - x;
 
  790         res[i] = gmm::vect_sp(dxy, Ti[i]);
 
  793     void operator()(
const base_small_vector& a,
 
  794                     base_matrix &hessa)
 const {
 
  796       for (
size_type i = 0; i < N-1; ++i) xx += a[i] * ti[i];
 
  799         ctx.pf()->interpolation_grad(ctx, coeff, grad, dim_type(N));
 
  800         gmm::add(gmm::identity_matrix(), grad);
 
  811     raytrace_pt_surf_cost_function_object
 
  812     (
const base_node &x00, 
const base_node &xx,
 
  813      fem_interpolation_context &ctxx,
 
  814      const model_real_plain_vector &coefff,
 
  815      const std::vector<base_small_vector> &tii,
 
  816      const std::vector<base_small_vector> &Tii,
 
  818       : N(gmm::vect_size(x00)), x0(x00), x(xx),
 
  819         ctx(ctxx), coeff(coefff), ti(tii), Ti(Tii), ref_conf(rc),
 
  820         dxy(N), grad(N,N), gradtot(N,N) {}
 
  833   void multi_contact_frame::compute_contact_pairs() {
 
  834     base_matrix G, grad(N,N);
 
  835     model_real_plain_vector coeff;
 
  836     base_small_vector a(N-1), ny(N);
 
  838     std::vector<base_small_vector> ti(N-1), Ti(N-1);
 
  844     contact_pairs = std::vector<contact_pair>();
 
  846     if (!ref_conf) extend_vectors();
 
  848     bool only_slave(
true), only_master(
true);
 
  849     for (
size_type i = 0; i < contact_boundaries.size(); ++i)
 
  850       if (is_slave_boundary(i)) only_master = 
false;
 
  851       else only_slave = 
false;
 
  853     if (only_master && !self_contact) {
 
  854       GMM_WARNING1(
"There is only master boundary and no self-contact to detect. Exiting");
 
  859       compute_boundary_points();
 
  860       potential_pairs.resize(boundary_points.size());
 
  862     else if (use_delaunay)
 
  863       compute_potential_contact_pairs_delaunay();
 
  865       compute_potential_contact_pairs_influence_boxes();
 
  871     for (
size_type ip = 0; ip < potential_pairs.size(); ++ip) {
 
  872       bool first_pair_found = 
false;
 
  873       const base_node &x = boundary_points[ip];
 
  874       boundary_point &bpinfo = boundary_points_info[ip];
 
  876       bool slx = is_slave_boundary(ibx);
 
  877       scalar_type d0 = 1E300, d1, d2;
 
  879       base_small_vector nx = bpinfo.normals[0];
 
  881         if (bpinfo.normals.size() > 1) { 
 
  882           for (
size_type i = 1; i < bpinfo.normals.size(); ++i)
 
  883             gmm::add(bpinfo.normals[i], nx);
 
  885           GMM_ASSERT1(nnx != scalar_type(0), 
"Invalid normal cone");
 
  886           gmm::scale(nx, scalar_type(1)/nnx);
 
  890       if (self_contact || slx) {
 
  895         if (N >= 4) ptw[0] = pt[3];
 
  896         if (N >= 3) ptz[0] = pt[2];
 
  897         if (N >= 2) pty[0] = pt[1];
 
  898         if (N >= 1) ptx[0] = pt[0];
 
  899         for (
size_type i = 0; i < obstacles.size(); ++i) {
 
  900           d1 = (obstacles_f[i].eval())[0];
 
  901           if (gmm::abs(d1) < release_distance && d1 < d0) {
 
  903             for (
size_type j=0; j < bpinfo.normals.size(); ++j) {
 
  904               gmm::add(gmm::scaled(bpinfo.normals[j], EPS), pt);
 
  905               if (N >= 4) ptw[0] = pt[3];
 
  906               if (N >= 3) ptz[0] = pt[2];
 
  907               if (N >= 2) pty[0] = pt[1];
 
  908               if (N >= 1) ptx[0] = pt[0];
 
  909               d2 =  (obstacles_f[i].eval())[0];
 
  910               if (d2 < d1) { d0 = d1; irigid_obstacle = i; 
break; }
 
  912               if (N >= 4) ptw[0] = pt[3];
 
  913               if (N >= 3) ptz[0] = pt[2];
 
  914               if (N >= 2) pty[0] = pt[1];
 
  915               if (N >= 1) ptx[0] = pt[0];
 
  923           if (N >= 4) ptw[0] = pt[3];
 
  924           if (N >= 3) ptz[0] = pt[2];
 
  925           if (N >= 2) pty[0] = pt[1];
 
  926           if (N >= 1) ptx[0] = pt[0];
 
  929           scalar_type 
alpha(0), beta(0);
 
  932           while (++nit < 50 && nb_fail < 3) {
 
  936               case 4: ptw[0] += EPS; 
break;
 
  937               case 3: ptz[0] += EPS; 
break;
 
  938               case 2: pty[0] += EPS; 
break;
 
  939               case 1: ptx[0] += EPS; 
break;
 
  941               d2 = (obstacles_f[irigid_obstacle].eval())[0];
 
  942               ny[k] = (d2 - d1) / EPS;
 
  945               case 4: ptw[0] -= EPS; 
break;
 
  946               case 3: ptz[0] -= EPS; 
break;
 
  947               case 2: pty[0] -= EPS; 
break;
 
  948               case 1: ptx[0] -= EPS; 
break;
 
  952             if (gmm::abs(d1) < 1E-13)
 
  956             for (scalar_type lambda(1); lambda >= 1E-3; lambda /= scalar_type(2)) {
 
  959                 gmm::add(x, gmm::scaled(nx, alpha), pt);
 
  961                 gmm::add(gmm::scaled(ny, -d1/gmm::vect_norm2_sqr(ny)), y, pt);
 
  963               if (N >= 4) ptw[0] = pt[3];
 
  964               if (N >= 3) ptz[0] = pt[2];
 
  965               if (N >= 2) pty[0] = pt[1];
 
  966               if (N >= 1) ptx[0] = pt[0];
 
  967               d2 = (obstacles_f[irigid_obstacle].eval())[0];
 
  972               if (gmm::abs(d2) < gmm::abs(d1)) 
break;
 
  975                 gmm::abs(beta - d1 / gmm::vect_sp(ny, nx)) > scalar_type(500))
 
  980           if (gmm::abs(d1) > 1E-8) {
 
  981             GMM_WARNING1(
"Projection/raytrace on rigid obstacle failed");
 
  986           if (gmm::vect_dist2(y, x) > release_distance)
 
  993           contact_pair ct(x, nx, bpinfo, y, ny, irigid_obstacle, d0);
 
  995           contact_pairs.push_back(ct);
 
  996           first_pair_found = 
true;
 
 1002       for (
size_type ipf = 0; ipf < potential_pairs[ip].size(); ++ipf) {
 
 1021         const face_info &fi = potential_pairs[ip][ipf];
 
 1026         const mesh_fem &mfu = mfdisp_of_boundary(ib);
 
 1027         const mesh &m = mfu.linked_mesh();
 
 1028         pfem pf_s = mfu.fem_of_element(cv);
 
 1034         m.points_of_convex(cv, G);
 
 1036         const auto face_pts = pf_s->ref_convex(cv)->points_of_face(iff);
 
 1037         const base_node &x0 = face_pts[0];
 
 1038         fem_interpolation_context ctx(pgt, pf_s, x0, G, cv, iff);
 
 1040         const base_small_vector &n0 = pf_s->ref_convex(cv)->normals()[iff];
 
 1043           scalar_type norm(0);
 
 1044           while(norm < 1E-5) {
 
 1048               ti[k] -= gmm::vect_sp(ti[k], ti[l]) * ti[l];
 
 1054         bool converged = 
false;
 
 1055         scalar_type residual(0);
 
 1060           base_small_vector res(N-1), res2(N-1), dir(N-1), b(N-1);
 
 1062           base_matrix hessa(N-1, N-1);
 
 1067             scalar_type norm(0);
 
 1068             while (norm < 1E-5) {
 
 1072                 Ti[k] -= gmm::vect_sp(Ti[k], Ti[l]) * Ti[l];
 
 1078           raytrace_pt_surf_cost_function_object pps(x0, x, ctx, coeff, ti, Ti,
 
 1083           scalar_type residual2(0), det(0);
 
 1084           bool exited = 
false;
 
 1086           for (;residual > 2E-12 && niter <= 30; ++niter) {
 
 1090               det = gmm::abs(bgeot::lu_inverse(&(*(hessa.begin())),N-1, 
false));
 
 1091               if (det > 1E-15) 
break;
 
 1093                 a[i] += gmm::random() * 1E-7;
 
 1094               if (++subiter > 4) 
break;
 
 1096             if (det <= 1E-15) 
break;
 
 1098             gmm::mult(hessa, gmm::scaled(res, scalar_type(-1)), dir);
 
 1100             if (gmm::vect_norm2(dir) > scalar_type(10)) nbfail++;
 
 1101             if (nbfail >= 4) 
break;
 
 1104             scalar_type lambda(1);
 
 1106               gmm::add(a, gmm::scaled(dir, lambda), b);
 
 1109               if (residual2 < residual) 
break;
 
 1110               lambda /= ((j < 3) ? scalar_type(2) : scalar_type(5));
 
 1113             residual = residual2;
 
 1120             if (niter > 1 && dist_ref > 15) 
break;
 
 1121             if (niter > 5 && dist_ref > 8) 
break;
 
 1122             if ((niter > 1 && dist_ref > 7) || nbfail == 3) exited = 
true;
 
 1125           GMM_ASSERT1(!((exited && converged &&
 
 1126                          pf_s->ref_convex(cv)->is_in(ctx.xref()) < 1E-6)),
 
 1127                       "A non conformal case !! " << gmm::vect_norm2(res)
 
 1128                       << 
" : " << nbfail << 
" : " << niter);
 
 1132           proj_pt_surf_cost_function_object pps(x0, x, ctx, coeff, ti,
 
 1139           base_small_vector grada(N-1), dir(N-1), b(N-1);
 
 1141           base_matrix hessa(N-1, N-1);
 
 1144           scalar_type dist = pps(a, grada);
 
 1150               det = gmm::abs(bgeot::lu_inverse(&(*(hessa.begin())),N-1, 
false));
 
 1151               if (det > 1E-15) 
break;
 
 1153                 a[i] += gmm::random() * 1E-7;
 
 1154               if (++subiter > 4) 
break;
 
 1156             if (det <= 1E-15) 
break;
 
 1158             gmm::mult(hessa, gmm::scaled(grada, scalar_type(-1)), dir);
 
 1161             for (scalar_type lambda(1);
 
 1162                  lambda >= 1E-3; lambda /= scalar_type(2)) {
 
 1163               gmm::add(a, gmm::scaled(dir, lambda), b);
 
 1164               if (pps(b) < dist) 
break;
 
 1165               gmm::add(a, gmm::scaled(dir, -lambda), b);
 
 1166               if (pps(b) < dist) 
break;
 
 1169             dist = pps(a, grada);
 
 1177             gmm::bfgs(pps, pps, a, 10, iter, 0, 0.5);
 
 1178             residual = gmm::abs(iter.get_res());
 
 1179             converged = (residual < 2E-5);
 
 1183         bool is_in = (pf_s->ref_convex(cv)->is_in(ctx.xref()) < 1E-6);
 
 1185         if (is_in || (!converged && !raytrace)) {
 
 1187             ctx.pf()->interpolation(ctx, coeff, y, dim_type(N));
 
 1197           if (!raytrace && nbwarn < 4) {
 
 1198             GMM_WARNING3(
"Projection or raytrace algorithm did not converge " 
 1199                          "for point " << x << 
" residual " << residual
 
 1200                          << 
" projection computed " << y);
 
 1216         if (!is_in) 
continue;
 
 1220         if (signed_dist > release_distance) 
continue;
 
 1223         base_small_vector ny0(N);
 
 1226         signed_dist *= gmm::sgn(gmm::vect_sp(x - y, ny));
 
 1230         if (first_pair_found && contact_pairs.back().signed_dist < signed_dist)
 
 1234         if (!(test_normal_cones_compatibility(ny, bpinfo.normals)))
 
 1239         if (&m == &(mfdisp_of_boundary(ibx).linked_mesh())) {
 
 1241           base_small_vector diff = bpinfo.ref_point - ctx.xreal();
 
 1244           if ( (ref_dist < scalar_type(4) * release_distance)
 
 1245                && (gmm::vect_sp(diff, ny0) < - 0.01 * ref_dist) )
 
 1249         contact_pair ct(x, nx, bpinfo, ctx.xref(), y, ny, fi, signed_dist);
 
 1250         if (first_pair_found) {
 
 1251           contact_pairs.back() = ct;
 
 1253           contact_pairs.push_back(ct);
 
 1254           first_pair_found = 
true;
 
 1271   class  raytracing_interpolate_transformation
 
 1272     : 
public virtual_interpolate_transformation {
 
 1275     struct contact_boundary {
 
 1278       std::string dispname;        
 
 1279       mutable const model_real_plain_vector *U;      
 
 1280       mutable model_real_plain_vector U_unred; 
 
 1284         : region(-1), mfu(0), dispname(
""), U(0), U_unred(0), slave(false) {}
 
 1285       contact_boundary(
size_type r, 
const mesh_fem *mf, 
const std::string &dn,
 
 1287         : region(r), mfu(mf), dispname(dn), slave(sl) {}
 
 1290     struct face_box_info {     
 
 1294       base_small_vector mean_normal;   
 
 1296         : ind_boundary(-1), ind_element(-1), ind_face(-1), mean_normal(0) {}
 
 1299         : ind_boundary(ib), ind_element(ie), ind_face(iff), mean_normal(n) {}
 
 1302     scalar_type release_distance;  
 
 1305     std::vector<contact_boundary> contact_boundaries;
 
 1306     typedef std::map<const mesh *, std::vector<size_type> > mesh_boundary_cor;
 
 1307     mesh_boundary_cor boundary_for_mesh;
 
 1311       const ga_workspace *parent_workspace;
 
 1314       mutable base_vector X;
 
 1315       mutable ga_function f, der_f;
 
 1316       mutable bool compiled;
 
 1318       void compile()
 const {
 
 1320           f = ga_function(*md, expr);
 
 1321         else if (parent_workspace)
 
 1322           f = ga_function(*parent_workspace, expr);
 
 1324           f = ga_function(expr);
 
 1326         f.workspace().add_fixed_size_variable(
"X", gmm::sub_interval(0, N), X);
 
 1327         if (N >= 1) f.workspace().add_macro(
"x", 
"X(1)");
 
 1328         if (N >= 2) f.workspace().add_macro(
"y", 
"X(2)");
 
 1329         if (N >= 3) f.workspace().add_macro(
"z", 
"X(3)");
 
 1330         if (N >= 4) f.workspace().add_macro(
"w", 
"X(4)");
 
 1333         der_f.derivative(
"X");
 
 1339       base_vector &point()
 const { 
return X; }
 
 1341       const base_tensor &eval()
 const {
 
 1342         if (!compiled) compile();
 
 1345       const base_tensor &eval_derivative()
 const {
 
 1346         if (!compiled) compile();
 
 1347         return der_f.eval();
 
 1351         : md(0), parent_workspace(0), expr(
""), X(0), f(), der_f() {}
 
 1352       obstacle(
const model &md_, 
const std::string &expr_, 
size_type N)
 
 1353         : md(&md_), parent_workspace(0), expr(expr_), X(N),
 
 1354           f(), der_f(), compiled(false) {}
 
 1355       obstacle(
const ga_workspace &parent_workspace_,
 
 1357         : md(0), parent_workspace(&parent_workspace_), expr(expr_), X(N),
 
 1358           f(), der_f(), compiled(false) {}
 
 1359       obstacle(
const obstacle &obs)
 
 1360         : md(obs.md), parent_workspace(obs.parent_workspace), expr(obs.expr),
 
 1361           X(obs.X), f(), der_f(), compiled(false) {}
 
 1362       obstacle &operator =(
const obstacle& obs) {
 
 1364         parent_workspace = obs.parent_workspace;
 
 1368         der_f = ga_function();
 
 1375     std::vector<obstacle> obstacles;
 
 1378     mutable std::vector<face_box_info> face_boxes_info;
 
 1381     void compute_face_boxes()
 const { 
 
 1382       fem_precomp_pool fppool;
 
 1384       model_real_plain_vector coeff;
 
 1386       face_boxes_info.resize(0);
 
 1388       for (
size_type i = 0; i < contact_boundaries.size(); ++i) {
 
 1389         const contact_boundary &cb = contact_boundaries[i];
 
 1392           const mesh_fem &mfu = *(cb.mfu);
 
 1393           const model_real_plain_vector &U = *(cb.U);
 
 1394           const mesh &m = mfu.linked_mesh();
 
 1397           base_node val(N), bmin(N), bmax(N);
 
 1398           base_small_vector n0_x(N), n_x(N), n0_y(N), n_y(N), n_mean(N);
 
 1399           base_matrix grad(N,N);
 
 1400           mesh_region region = m.region(bnum);
 
 1401           GMM_ASSERT1(mfu.get_qdim() == N, 
"Wrong mesh_fem qdim");
 
 1403           dal::bit_vector points_already_interpolated;
 
 1404           std::vector<base_node> transformed_points(m.nb_max_points());
 
 1408             pfem pf_s = mfu.fem_of_element(cv);
 
 1409             pfem_precomp pfp = fppool(pf_s, pgt->pgeometric_nodes());
 
 1411             mfu.linked_mesh().points_of_convex(cv, G);
 
 1412             fem_interpolation_context ctx(pgt, pfp, 
size_type(-1), G, cv);
 
 1415             size_type nb_pt_on_face = cvs->nb_points_of_face(v.f());
 
 1416             GMM_ASSERT1(nb_pt_on_face >= 2, 
"This element has less than two " 
 1417                         "vertices on considered face !");
 
 1420             for (
size_type k = 0; k < nb_pt_on_face; ++k) {
 
 1421               size_type ip = cvs->ind_points_of_face(v.f())[k];
 
 1422               size_type ind = m.ind_points_of_convex(cv)[ip];
 
 1426               if (!(points_already_interpolated.is_in(ind))) {
 
 1427                 pf_s->interpolation(ctx, coeff, val, dim_type(N));
 
 1429                 transformed_points[ind] = val;
 
 1430                 points_already_interpolated.add(ind);
 
 1432                 val = transformed_points[ind];
 
 1443                   bmin[l] = std::min(bmin[l], val[l]);
 
 1444                   bmax[l] = std::max(bmax[l], val[l]);
 
 1450             scalar_type h = bmax[0] - bmin[0];
 
 1451             for (
size_type k = 1; k < N; ++k) h = std::max(h, bmax[k]-bmin[k]);
 
 1453               { bmin[k] -= h * 0.15; bmax[k] += h * 0.15; }
 
 1456             face_boxes.add_box(bmin, bmax, face_boxes_info.size());
 
 1458             face_boxes_info.push_back(face_box_info(i, cv, v.f(), n_mean));
 
 1462       face_boxes.build_tree();
 
 1467     void add_rigid_obstacle(
const model &md, 
const std::string &expr,
 
 1469       obstacles.push_back(obstacle(md, expr, N));
 
 1472     void add_rigid_obstacle(
const ga_workspace &parent_workspace,
 
 1474      obstacles.push_back(obstacle(parent_workspace, expr, N));
 
 1477     void add_contact_boundary(
const model &md, 
const mesh &m,
 
 1478                               const std::string dispname,
 
 1480       const mesh_fem *mf = 0;
 
 1481       if (md.variable_group_exists(dispname)) {
 
 1482         for (
const std::string &t : md.variable_group(dispname)) {
 
 1483           const mesh_fem *mf2 = md.pmesh_fem_of_variable(t);
 
 1484           if (mf2 && &(mf2->linked_mesh()) == &m)
 
 1485             { mf = mf2; 
break; }
 
 1488         mf = md.pmesh_fem_of_variable(dispname);
 
 1490       GMM_ASSERT1(mf, 
"Displacement should be a fem variable");
 
 1491       contact_boundary cb(region, mf, dispname, slave);
 
 1492       boundary_for_mesh[&(mf->linked_mesh())]
 
 1493         .push_back(contact_boundaries.size());
 
 1494       contact_boundaries.push_back(cb);
 
 1497     void add_contact_boundary(
const ga_workspace &workspace, 
const mesh &m,
 
 1498                               const std::string dispname,
 
 1500       const mesh_fem *mf = 0;
 
 1501       if (workspace.variable_group_exists(dispname)) {
 
 1502         for (
const std::string &t : workspace.variable_group(dispname)) {
 
 1503           const mesh_fem *mf2 = workspace.associated_mf(t);
 
 1504           if (mf2 && &(mf2->linked_mesh()) == &m)
 
 1505             { mf = mf2; 
break; }
 
 1508         mf = workspace.associated_mf(dispname);
 
 1510       GMM_ASSERT1(mf, 
"Displacement should be a fem variable");
 
 1511       contact_boundary cb(region, mf, dispname, slave);
 
 1512       boundary_for_mesh[&(mf->linked_mesh())]
 
 1513         .push_back(contact_boundaries.size());
 
 1514       contact_boundaries.push_back(cb);
 
 1517     void extract_variables(
const ga_workspace &workspace,
 
 1518                            std::set<var_trans_pair> &vars,
 
 1519                            bool ignore_data, 
const mesh &m_x,
 
 1520                            const std::string &interpolate_name)
 const {
 
 1522       bool expand_groups = !ignore_data;
 
 1527       mesh_boundary_cor::const_iterator it = boundary_for_mesh.find(&m_x);
 
 1528       GMM_ASSERT1(it != boundary_for_mesh.end(), 
"Raytracing interpolate " 
 1529                   "transformation: Mesh with no declared contact boundary");
 
 1530       for (
const size_type &boundary_ind : it->second) {
 
 1531         const contact_boundary &cb = contact_boundaries[boundary_ind];
 
 1532         const std::string &dispname_x
 
 1533           = workspace.variable_in_group(cb.dispname, m_x);
 
 1534         if (!ignore_data || !(workspace.is_constant(dispname_x)))
 
 1535           vars.insert(var_trans_pair(dispname_x, 
""));
 
 1538       for (
const contact_boundary &cb : contact_boundaries) {
 
 1540           if (expand_groups && workspace.variable_group_exists(cb.dispname)
 
 1541               && (!ignore_data || !(workspace.is_constant(cb.dispname)))) {
 
 1542             for (
const std::string &t : workspace.variable_group(cb.dispname))
 
 1543               vars.insert(var_trans_pair(t, interpolate_name));
 
 1545             if (!ignore_data || !(workspace.is_constant(cb.dispname)))
 
 1546               vars.insert(var_trans_pair(cb.dispname, interpolate_name));
 
 1552     void init(
const ga_workspace &workspace)
 const {
 
 1553       for (
const contact_boundary &cb : contact_boundaries) {
 
 1554         const mesh_fem &mfu = *(cb.mfu);
 
 1555         const std::string dispname_x
 
 1556           =  workspace.variable_in_group(cb.dispname, mfu.linked_mesh());
 
 1558         if (mfu.is_reduced()) {
 
 1560           mfu.extend_vector(workspace.value(dispname_x), cb.U_unred);
 
 1561           cb.U = &(cb.U_unred);
 
 1563           cb.U = &(workspace.value(dispname_x));
 
 1566       compute_face_boxes();
 
 1569     void finalize()
 const {
 
 1571       face_boxes_info = std::vector<face_box_info>();
 
 1572       for (
const contact_boundary &cb : contact_boundaries)
 
 1573         cb.U_unred = model_real_plain_vector();
 
 1576     int transform(
const ga_workspace &workspace, 
const mesh &m_x,
 
 1577                   fem_interpolation_context &ctx_x,
 
 1578                   const base_small_vector &,
 
 1581                   base_small_vector &N_y,
 
 1582                   std::map<var_trans_pair, base_tensor> &derivatives,
 
 1583                   bool compute_derivatives)
 const {
 
 1586       GMM_ASSERT1(face_x != 
short_type(-1), 
"The contact transformation can " 
 1587                   "only be applied to a boundary");
 
 1592       mesh_boundary_cor::const_iterator it =  boundary_for_mesh.find(&m_x);
 
 1593       GMM_ASSERT1(it != boundary_for_mesh.end(),
 
 1594                   "Mesh with no declared contact boundary");
 
 1596       for (
const size_type &boundary_ind : it->second) {
 
 1597         const contact_boundary &cb = contact_boundaries[boundary_ind];
 
 1598         if (m_x.region(cb.region).is_in(cv_x, face_x))
 
 1599           { ib_x = boundary_ind; 
break; }
 
 1602                   "No contact region found for this point");
 
 1603       const contact_boundary &cb_x = contact_boundaries[ib_x];
 
 1604       const mesh_fem &mfu_x = *(cb_x.mfu); 
 
 1605       pfem pfu_x = mfu_x.fem_of_element(cv_x);
 
 1606       size_type N = mfu_x.linked_mesh().dim();
 
 1607       GMM_ASSERT1(mfu_x.get_qdim() == N,
 
 1608                   "Displacment field with wrong dimension");
 
 1610       model_real_plain_vector coeff_x, coeff_y, stored_coeff_y;
 
 1611       base_small_vector a(N-1), b(N-1), pt_x(N), pt_y(N), n_x(N);
 
 1612       base_small_vector stored_pt_y(N), stored_n_y(N), stored_pt_y_ref(N);
 
 1613       base_small_vector n0_x, n_y(N), n0_y, res(N-1), res2(N-1), dir(N-1);
 
 1614       base_matrix G_x, G_y, grad(N,N), hessa(N-1, N-1);
 
 1615       std::vector<base_small_vector> ti(N-1), Ti(N-1);
 
 1616       scalar_type stored_signed_distance(0);
 
 1617       std::string stored_dispname;
 
 1618       scalar_type d0 = 1E300, d1, d2;
 
 1619       const mesh *stored_m_y(0);
 
 1622       fem_interpolation_context stored_ctx_y;
 
 1628       m_x.points_of_convex(cv_x, G_x);
 
 1629       ctx_x.set_pf(pfu_x);
 
 1630       pfu_x->interpolation(ctx_x, coeff_x, pt_x, dim_type(N));
 
 1631       pt_x += ctx_x.xreal();
 
 1640       bool first_pair_found = 
false;
 
 1642       for (
size_type i = 0; i < obstacles.size(); ++i) {
 
 1643         const obstacle &obs = obstacles[i];
 
 1645         const base_tensor &t = obs.eval();
 
 1647         GMM_ASSERT1(t.size() == 1, 
"Obstacle level set function as to be " 
 1648                     "a scalar valued one");
 
 1651         if (gmm::abs(d1) < release_distance && d1 < d0) {
 
 1652           const base_tensor &t_der = obs.eval_derivative();
 
 1653           GMM_ASSERT1(t_der.size() == n_x.size(), 
"Bad derivative size");
 
 1654           if (gmm::vect_sp(t_der.as_vector(), n_x) < scalar_type(0)) {
 
 1656             irigid_obstacle = i;
 
 1664         const obstacle &obs = obstacles[irigid_obstacle];
 
 1668         scalar_type 
alpha(0), beta(0);
 
 1671         while (gmm::abs(d1) > 1E-13 && ++nit < 50 && nb_fail < 3) {
 
 1672           if (nit != 1) 
gmm::copy(obs.eval_derivative().as_vector(), n_y);
 
 1674           for (scalar_type lambda(1); lambda >= 1E-3; lambda/=scalar_type(2)) {
 
 1676             gmm::add(pt_x, gmm::scaled(n_x, alpha), obs.point());
 
 1678             if (gmm::abs(d2) < gmm::abs(d1)) 
break;
 
 1680           if (gmm::abs(beta - d1 / gmm::vect_sp(n_y, n_x)) > scalar_type(500))
 
 1682           beta = 
alpha; d1 = d2;
 
 1686         if (gmm::abs(d1) > 1E-8) {
 
 1687            GMM_WARNING1(
"Raytrace on rigid obstacle failed");
 
 1689         else if (gmm::vect_dist2(pt_y, pt_x) <= release_distance) {
 
 1692           stored_pt_y = stored_pt_y_ref = pt_y;
 
 1694           stored_signed_distance = d0;
 
 1695           first_pair_found = 
true;
 
 1703       bgeot::rtree::pbox_set bset;
 
 1704       base_node bmin(pt_x), bmax(pt_x);
 
 1706         { bmin[i] -= release_distance; bmax[i] += release_distance; }
 
 1708       face_boxes.find_line_intersecting_boxes(pt_x, n_x, bmin, bmax, bset);
 
 1714       for (
const auto &pbox : bset) {
 
 1715         face_box_info &fbox_y = face_boxes_info[pbox->id];
 
 1717         const contact_boundary &cb_y = contact_boundaries[ib_y];
 
 1718         const mesh_fem &mfu_y = *(cb_y.mfu);
 
 1719         const mesh &m_y = mfu_y.linked_mesh();
 
 1721         pfem pfu_y = mfu_y.fem_of_element(cv_y);
 
 1727         if (gmm::vect_sp(fbox_y.mean_normal, n_x) >= scalar_type(0) ||
 
 1728             (cv_x == cv_y && &m_x == &m_y))
 
 1735         m_y.points_of_convex(cv_y, G_y);
 
 1737         const auto face_pts = pfu_y->ref_convex(cv_y)->points_of_face(face_y);
 
 1738         const base_node &Y0 = face_pts[0];
 
 1739         fem_interpolation_context ctx_y(pgt_y, pfu_y, Y0, G_y, cv_y, face_y);
 
 1741         const base_small_vector &NY0
 
 1742           = pfu_y->ref_convex(cv_y)->normals()[face_y];
 
 1745           scalar_type norm(0);
 
 1746           while(norm < 1E-5) {
 
 1750               ti[k] -= gmm::vect_sp(ti[k], ti[l]) * ti[l];
 
 1760           scalar_type norm(0);
 
 1761           while (norm < 1E-5) {
 
 1765               Ti[k] -= gmm::vect_sp(Ti[k], Ti[l]) * Ti[l];
 
 1773         raytrace_pt_surf_cost_function_object pps(Y0, pt_x, ctx_y, coeff_y,
 
 1777         scalar_type residual2(0), det(0);
 
 1778         bool exited = 
false;
 
 1780         for (;residual > 2E-12 && niter <= 30; ++niter) {
 
 1782           for (
size_type subiter(0); subiter <= 4; ++subiter) {
 
 1784             det = gmm::abs(bgeot::lu_inverse(&(*(hessa.begin())), N-1, 
false));
 
 1785             if (det > 1E-15) 
break;
 
 1787               a[i] += gmm::random() * 1E-7;
 
 1789           if (det <= 1E-15) 
break;
 
 1791           gmm::mult(hessa, gmm::scaled(res, scalar_type(-1)), dir);
 
 1793           if (gmm::vect_norm2(dir) > scalar_type(10)) nbfail++;
 
 1794           if (nbfail >= 4) 
break;
 
 1797           scalar_type lambda(1);
 
 1799             gmm::add(a, gmm::scaled(dir, lambda), b);
 
 1802             if (residual2 < residual) 
break;
 
 1803             lambda /= ((j < 3) ? scalar_type(2) : scalar_type(5));
 
 1806           residual = residual2;
 
 1811           if (niter > 1 && dist_ref > 15) 
break;
 
 1812           if (niter > 5 && dist_ref > 8) 
break;
 
 1813           if ( nbfail == 3) exited = 
true;
 
 1816         bool is_in = (pfu_y->ref_convex(cv_y)->is_in(ctx_y.xref()) < 1E-6);
 
 1821           ctx_y.pf()->interpolation(ctx_y, coeff_y, pt_y, dim_type(N));
 
 1822           pt_y += ctx_y.xreal();
 
 1827         if (!converged) 
continue;
 
 1830         if (!is_in) 
continue;
 
 1834         if (signed_dist > release_distance) 
continue;
 
 1839         signed_dist *= gmm::sgn(gmm::vect_sp(pt_x - pt_y, n_y));
 
 1843         if (first_pair_found && stored_signed_distance < signed_dist)
 
 1847         if (gmm::vect_sp(n_y, n_x) >= scalar_type(0)) 
continue;
 
 1852           base_small_vector diff = ctx_x.xreal() - ctx_y.xreal();
 
 1854           if ( (ref_dist < scalar_type(4) * release_distance)
 
 1855                && (gmm::vect_sp(diff, n0_y) < - 0.01 * ref_dist) )
 
 1859         stored_pt_y = pt_y; stored_pt_y_ref = ctx_y.xref();
 
 1860         stored_m_y = &m_y; stored_cv_y = cv_y; stored_face_y = face_y;
 
 1862         stored_ctx_y = ctx_y;
 
 1863         stored_coeff_y = coeff_y;
 
 1864         stored_signed_distance = signed_dist;
 
 1865         stored_dispname = cb_y.dispname;
 
 1866         first_pair_found = 
true;
 
 1875         P_ref = stored_pt_y; N_y = stored_n_y;
 
 1877       } 
else if (first_pair_found) {
 
 1878         *m_t = stored_m_y; cv = stored_cv_y; face_num = stored_face_y;
 
 1879         P_ref = stored_pt_y_ref; N_y = stored_n_y;
 
 1888       if (compute_derivatives) {
 
 1889         if (ret_type >= 1) {
 
 1890           fem_interpolation_context &ctx_y = stored_ctx_y;
 
 1892           if (ret_type == 1) cv_y = ctx_y.convex_num();
 
 1894           base_matrix I_nxny(N,N); 
 
 1895           gmm::copy(gmm::identity_matrix(), I_nxny);
 
 1896           gmm::rank_one_update(I_nxny, n_x,
 
 1897                                gmm::scaled(stored_n_y,scalar_type(-1)
 
 1898                                            / gmm::vect_sp(n_x, stored_n_y)));
 
 1901           base_matrix F_y(N,N), F_y_inv(N,N), M1(N, N), M2(N, N);
 
 1903           if (ret_type == 1) {
 
 1905             pfu_y->interpolation_grad(ctx_y, stored_coeff_y, F_y, dim_type(N));
 
 1906             gmm::add(gmm::identity_matrix(), F_y);
 
 1908             bgeot::lu_inverse(&(*(F_y_inv.begin())), N);
 
 1911             gmm::copy(gmm::identity_matrix(), F_y_inv);
 
 1915           base_matrix F_x(N,N), F_x_inv(N,N);
 
 1916           pfu_x->interpolation_grad(ctx_x, coeff_x, F_x, dim_type(N));
 
 1917           gmm::add(gmm::identity_matrix(), F_x);
 
 1919           bgeot::lu_inverse(&(*(F_x_inv.begin())), N);
 
 1922           base_tensor base_ux;
 
 1923           base_matrix vbase_ux;
 
 1924           ctx_x.base_value(base_ux);
 
 1925           size_type qdim_ux = pfu_x->target_dim();
 
 1926           size_type ndof_ux = pfu_x->nb_dof(cv_x) * N / qdim_ux;
 
 1927           vectorize_base_tensor(base_ux, vbase_ux, ndof_ux, qdim_ux, N);
 
 1929           base_tensor base_uy;
 
 1930           base_matrix vbase_uy;
 
 1932           if (ret_type == 1) {
 
 1933             ctx_y.base_value(base_uy);
 
 1934             size_type qdim_uy = pfu_y->target_dim();
 
 1935             ndof_uy = pfu_y->nb_dof(cv_y) * N / qdim_uy;
 
 1936             vectorize_base_tensor(base_uy, vbase_uy, ndof_uy, qdim_uy, N);
 
 1939           base_tensor grad_base_ux, vgrad_base_ux;
 
 1940           ctx_x.grad_base_value(grad_base_ux);
 
 1941           vectorize_grad_base_tensor(grad_base_ux, vgrad_base_ux, ndof_ux,
 
 1950           base_matrix der_x(ndof_ux, N);
 
 1951           gmm::mult(vbase_ux, gmm::transposed(M1), der_x);
 
 1954           base_matrix der_y(ndof_uy, N);
 
 1955           if (ret_type == 1) {
 
 1956             gmm::mult(vbase_uy, gmm::transposed(M1), der_y);
 
 1957             gmm::scale(der_y, scalar_type(-1));
 
 1961           gmm::mult(M1, gmm::transposed(F_x_inv), M2);
 
 1966                   der_x(i, j) -= M2(j, k) * vgrad_base_ux(i, l, k)
 
 1967                     * n_x[l] * stored_signed_distance;
 
 1969           const std::string &dispname_x
 
 1970             = workspace.variable_in_group(cb_x.dispname, m_x);
 
 1972           for (
auto&& d : derivatives) {
 
 1973             if (dispname_x.compare(d.first.varname) == 0 &&
 
 1974                 d.first.transname.size() == 0) {
 
 1975               d.second.adjust_sizes(ndof_ux, N);
 
 1976               gmm::copy(der_x.as_vector(), d.second.as_vector());
 
 1977             } 
else if (ret_type == 1 &&
 
 1978                        stored_dispname.compare(d.first.varname) == 0 &&
 
 1979                        d.first.transname.size() != 0) {
 
 1980               d.second.adjust_sizes(ndof_uy, N);
 
 1981               gmm::copy(der_y.as_vector(), d.second.as_vector());
 
 1983               d.second.adjust_sizes(0, 0);
 
 1986           for (
auto&& d : derivatives)
 
 1987             d.second.adjust_sizes(0, 0);
 
 1993     raytracing_interpolate_transformation(scalar_type d)
 
 1994       : release_distance(d) {}
 
 2004   class  projection_interpolate_transformation
 
 2005     : 
public raytracing_interpolate_transformation {
 
 2007     scalar_type release_distance;  
 
 2010     int transform(
const ga_workspace &workspace, 
const mesh &m_x,
 
 2011                   fem_interpolation_context &ctx_x,
 
 2012                   const base_small_vector &,
 
 2015                   base_small_vector &N_y,
 
 2016                   std::map<var_trans_pair, base_tensor> &derivatives,
 
 2017                   bool compute_derivatives)
 const {
 
 2020       GMM_ASSERT1(face_x != 
short_type(-1), 
"The contact transformation can " 
 2021                   "only be applied to a boundary");
 
 2026       mesh_boundary_cor::const_iterator it =  boundary_for_mesh.find(&m_x);
 
 2027       GMM_ASSERT1(it != boundary_for_mesh.end(),
 
 2028                   "Mesh with no declared contact boundary");
 
 2030       for (
const auto &boundary_ind : it->second) {
 
 2031         const contact_boundary &cb = contact_boundaries[boundary_ind];
 
 2032         if (m_x.region(cb.region).is_in(cv_x, face_x))
 
 2033           { ib_x = boundary_ind; 
break; }
 
 2036                   "No contact region found for this point");
 
 2037       const contact_boundary &cb_x = contact_boundaries[ib_x];
 
 2038       const mesh_fem &mfu_x = *(cb_x.mfu); 
 
 2039       pfem pfu_x = mfu_x.fem_of_element(cv_x);
 
 2040       size_type N = mfu_x.linked_mesh().dim();
 
 2041       GMM_ASSERT1(mfu_x.get_qdim() == N,
 
 2042                   "Displacment field with wrong dimension");
 
 2044       model_real_plain_vector coeff_x, coeff_y, stored_coeff_y;
 
 2045       base_small_vector a(N-1), b(N-1), pt_x(N), pt_y(N), n_x(N);
 
 2046       base_small_vector stored_pt_y(N), stored_n_y(N), stored_pt_y_ref(N);
 
 2047       base_small_vector n0_x, n_y(N), n0_y, res(N-1), res2(N-1), dir(N-1);
 
 2048       base_matrix G_x, G_y, grad(N,N), hessa(N-1, N-1);
 
 2049       std::vector<base_small_vector> ti(N-1);
 
 2050       scalar_type stored_signed_distance(0);
 
 2051       std::string stored_dispname;
 
 2052       scalar_type d0 = 1E300, d1, d2(0);
 
 2053       const mesh *stored_m_y(0);
 
 2056       fem_interpolation_context stored_ctx_y;
 
 2064       bgeot::vectors_to_base_matrix(G_x, m_x.points_of_convex(cv_x));
 
 2065       ctx_x.set_pf(pfu_x);
 
 2066       pfu_x->interpolation(ctx_x, coeff_x, pt_x, dim_type(N));
 
 2067       pt_x += ctx_x.xreal();
 
 2076       bool first_pair_found = 
false;
 
 2078       for (
size_type i = 0; i < obstacles.size(); ++i) {
 
 2079         const obstacle &obs = obstacles[i];
 
 2081         const base_tensor &t = obs.eval();
 
 2083         GMM_ASSERT1(t.size() == 1, 
"Obstacle level set function as to be " 
 2084                     "a scalar valued one");
 
 2087         if (gmm::abs(d1) < release_distance && d1 < d0) {
 
 2088           const base_tensor &t_der = obs.eval_derivative();
 
 2089           GMM_ASSERT1(t_der.size() == n_x.size(), 
"Bad derivative size");
 
 2090           if (gmm::vect_sp(t_der.as_vector(), n_x) < scalar_type(0)) 
 
 2091             { d0 = d1; irigid_obstacle = i; 
gmm::copy(t_der.as_vector(),n_y); }
 
 2097         const obstacle &obs = obstacles[irigid_obstacle];
 
 2101         scalar_type 
alpha(0), beta(0);
 
 2104         while (gmm::abs(d1) > 1E-13 && ++nit < 50 && nb_fail < 3) {
 
 2105           if (nit != 1) 
gmm::copy(obs.eval_derivative().as_vector(), n_y);
 
 2107           for (scalar_type lambda(1); lambda >= 1E-3; lambda/=scalar_type(2)) {
 
 2108           gmm::add(gmm::scaled(n_y, -d1/gmm::vect_norm2_sqr(n_y)), pt_y, pt_x);
 
 2110             if (gmm::abs(d2) < gmm::abs(d1)) 
break;
 
 2112           if (gmm::abs(beta - d1 / gmm::vect_sp(n_y, n_x)) > scalar_type(500))
 
 2114           beta = 
alpha; d1 = d2;
 
 2118         if (gmm::abs(d1) > 1E-8) {
 
 2119            GMM_WARNING1(
"Raytrace on rigid obstacle failed");
 
 2121         else if (gmm::vect_dist2(pt_y, pt_x) <= release_distance) {
 
 2124           stored_pt_y = stored_pt_y_ref = pt_y; stored_n_y = n_y, 
 
 2125           stored_signed_distance = d0;
 
 2126           first_pair_found = 
true;
 
 2134       bgeot::rtree::pbox_set bset;
 
 2135       base_node bmin(pt_x), bmax(pt_x);
 
 2137         { bmin[i] -= release_distance; bmax[i] += release_distance; }
 
 2139       face_boxes.find_line_intersecting_boxes(pt_x, n_x, bmin, bmax, bset);
 
 2144       for (
const auto &pbox : bset) {
 
 2145         face_box_info &fbox_y = face_boxes_info[pbox->id];
 
 2147         const contact_boundary &cb_y =  contact_boundaries[ib_y];
 
 2148         const mesh_fem &mfu_y = *(cb_y.mfu);
 
 2149         const mesh &m_y = mfu_y.linked_mesh();
 
 2150         bool ref_conf=
false;
 
 2152         pfem pfu_y = mfu_y.fem_of_element(cv_y);
 
 2158         if (gmm::vect_sp(fbox_y.mean_normal, n_x) >= scalar_type(0) ||
 
 2159             (cv_x == cv_y && &m_x == &m_y))
 
 2165         bgeot::vectors_to_base_matrix(G_y, m_y.points_of_convex(cv_y));
 
 2167         const auto face_pts = pfu_y->ref_convex(cv_y)->points_of_face(face_y);
 
 2168         const base_node &Y0 = face_pts[0];
 
 2169         fem_interpolation_context ctx_y(pgt_y, pfu_y, Y0, G_y, cv_y, face_y);
 
 2171         const base_small_vector &NY0
 
 2172           = pfu_y->ref_convex(cv_y)->normals()[face_y];
 
 2178           scalar_type norm(0);
 
 2179           while(norm < 1E-5) {
 
 2183               ti[k] -= gmm::vect_sp(ti[k], ti[l]) * ti[l];
 
 2189         proj_pt_surf_cost_function_object pps(Y0, pt_x, ctx_y, coeff_y, ti,
 
 2191          base_small_vector grada(N-1);
 
 2194           scalar_type dist = pps(a, grada);
 
 2201               det = gmm::abs(gmm::lu_inverse(hessa, 
false));
 
 2202               if (det > 1E-15) 
break;
 
 2204                 a[i] += gmm::random() * 1E-7;
 
 2205               if (++subiter > 4) 
break;
 
 2207             if (det <= 1E-15) 
break;
 
 2209             gmm::mult(hessa, gmm::scaled(grada, scalar_type(-1)), dir);
 
 2212             for (scalar_type lambda(1);
 
 2213                  lambda >= 1E-3; lambda /= scalar_type(2)) {
 
 2214               gmm::add(a, gmm::scaled(dir, lambda), b);
 
 2215               if (pps(b) < dist) 
break;
 
 2216               gmm::add(a, gmm::scaled(dir, -lambda), b);
 
 2217               if (pps(b) < dist) 
break;
 
 2221             dist = pps(a, grada);
 
 2228             gmm::bfgs(pps, pps, a, 10, iter, 0, 0.5);
 
 2229             residual = gmm::abs(iter.get_res());
 
 2230             converged = (residual < 2E-5);
 
 2233         bool is_in = (pfu_y->ref_convex(cv_y)->is_in(ctx_y.xref()) < 1E-6);
 
 2241         if (is_in || (!converged )) {
 
 2243             ctx_y.pf()->interpolation(ctx_y, coeff_y, pt_y, dim_type(N));
 
 2244             pt_y += ctx_y.xreal();
 
 2246             pt_y = ctx_y.xreal();
 
 2253             GMM_WARNING3(
"Projection algorithm did not converge " 
 2254                          "for point " << pt_x << 
" residual " << residual
 
 2255                          << 
" projection computed " << pt_y);
 
 2271         if (!is_in) 
continue;
 
 2277         if (signed_dist > release_distance) 
continue;
 
 2280         base_small_vector ny0(N);
 
 2281         compute_normal(ctx_y, face_y, ref_conf, coeff_y, ny0, n_y, grad);
 
 2283         signed_dist *= gmm::sgn(gmm::vect_sp(pt_x - pt_y, n_y));
 
 2287         if (first_pair_found && stored_signed_distance < signed_dist)
 
 2291         if (gmm::vect_sp(n_y, n_x) >= scalar_type(0)) 
continue;
 
 2297           base_small_vector diff = ctx_x.xreal() - ctx_y.xreal();
 
 2300           if ( (ref_dist < scalar_type(4) * release_distance)
 
 2301                && (gmm::vect_sp(diff, ny0) < - 0.01 * ref_dist) )
 
 2305         stored_pt_y = pt_y; stored_pt_y_ref = ctx_y.xref();
 
 2306         stored_m_y = &m_y; stored_cv_y = cv_y; stored_face_y = face_y;
 
 2308         stored_ctx_y = ctx_y;
 
 2309         stored_coeff_y = coeff_y;
 
 2310         stored_signed_distance = signed_dist;
 
 2311         stored_dispname = cb_y.dispname;
 
 2312         first_pair_found = 
true;
 
 2324         P_ref = stored_pt_y; N_y = stored_n_y;
 
 2326       } 
else if (first_pair_found) {
 
 2327         *m_t = stored_m_y; cv = stored_cv_y; face_num = stored_face_y;
 
 2328         P_ref = stored_pt_y_ref;
 
 2337       if (compute_derivatives) {
 
 2338         if (ret_type >= 1) {
 
 2339           fem_interpolation_context &ctx_y = stored_ctx_y;
 
 2341           if (ret_type == 1) cv_y = ctx_y.convex_num();
 
 2344           base_matrix I_nyny(N,N); 
 
 2345           gmm::copy(gmm::identity_matrix(), I_nyny);
 
 2346           gmm::rank_one_update(I_nyny, stored_n_y,
 
 2347                                gmm::scaled(stored_n_y,scalar_type(-1)));
 
 2350           base_matrix F_y(N,N), F_y_inv(N,N), M1(N, N), M2(N, N);
 
 2352           if (ret_type == 1) {
 
 2354             pfu_y->interpolation_grad(ctx_y, stored_coeff_y, F_y, dim_type(N));
 
 2355             gmm::add(gmm::identity_matrix(), F_y);
 
 2360             gmm::copy(gmm::identity_matrix(), F_y_inv);
 
 2364           base_tensor base_ux;
 
 2365           base_matrix vbase_ux;
 
 2366           ctx_x.base_value(base_ux);
 
 2367           size_type qdim_ux = pfu_x->target_dim();
 
 2368           size_type ndof_ux = pfu_x->nb_dof(cv_x) * N / qdim_ux;
 
 2369           vectorize_base_tensor(base_ux, vbase_ux, ndof_ux, qdim_ux, N);
 
 2371           base_tensor base_uy;
 
 2372           base_matrix vbase_uy;
 
 2374           if (ret_type == 1) {
 
 2375             ctx_y.base_value(base_uy);
 
 2376             size_type qdim_uy = pfu_y->target_dim();
 
 2377             ndof_uy = pfu_y->nb_dof(cv_y) * N / qdim_uy;
 
 2378             vectorize_base_tensor(base_uy, vbase_uy, ndof_uy, qdim_uy, N);
 
 2381           base_tensor grad_base_ux, vgrad_base_ux;
 
 2382           ctx_x.grad_base_value(grad_base_ux);
 
 2383           vectorize_grad_base_tensor(grad_base_ux, vgrad_base_ux, ndof_ux,
 
 2391           base_matrix der_x(ndof_ux, N);
 
 2392           gmm::mult(vbase_ux, gmm::transposed(M1), der_x);
 
 2395           base_matrix der_y(ndof_uy, N);
 
 2396           if (ret_type == 1) {
 
 2397             gmm::mult(vbase_uy, gmm::transposed(M1), der_y);
 
 2398             gmm::scale(der_y, scalar_type(-1));
 
 2401           const std::string &dispname_x
 
 2402             = workspace.variable_in_group(cb_x.dispname, m_x);
 
 2404           for (
auto&& d : derivatives) {
 
 2405             if (dispname_x.compare(d.first.varname) == 0 &&
 
 2406                 d.first.transname.size() == 0) {
 
 2407               d.second.adjust_sizes(ndof_ux, N);
 
 2408               gmm::copy(der_x.as_vector(), d.second.as_vector());
 
 2409             } 
else if (ret_type == 1 &&
 
 2410                        stored_dispname.compare(d.first.varname) == 0 &&
 
 2411                        d.first.transname.size() != 0) {
 
 2412               d.second.adjust_sizes(ndof_uy, N);
 
 2413               gmm::copy(der_y.as_vector(), d.second.as_vector());
 
 2415               d.second.adjust_sizes(0, 0);
 
 2418           for (
auto&& d : derivatives)
 
 2419             d.second.adjust_sizes(0, 0);
 
 2424     projection_interpolate_transformation(
const scalar_type &d)
 
 2425       :raytracing_interpolate_transformation(d), release_distance(d) {}
 
 2428   (
model &md, 
const std::string &transname, scalar_type d) {
 
 2429     pinterpolate_transformation
 
 2430       p = std::make_shared<raytracing_interpolate_transformation>(d);
 
 2435   (ga_workspace &workspace, 
const std::string &transname, scalar_type d) {
 
 2436     pinterpolate_transformation
 
 2437       p = std::make_shared<raytracing_interpolate_transformation>(d);
 
 2438     workspace.add_interpolate_transformation(transname, p);
 
 2442   (
model &md, 
const std::string &transname, 
const mesh &m,
 
 2443    const std::string &dispname, 
size_type region) {
 
 2444     raytracing_interpolate_transformation *p
 
 2445       = 
dynamic_cast<raytracing_interpolate_transformation *
> 
 2446       (
const_cast<virtual_interpolate_transformation *
> 
 2448     p->add_contact_boundary(md, m, dispname, region, 
false);
 
 2452   (
model &md, 
const std::string &transname, 
const mesh &m,
 
 2453    const std::string &dispname, 
size_type region) {
 
 2454     raytracing_interpolate_transformation *p
 
 2455       = 
dynamic_cast<raytracing_interpolate_transformation *
> 
 2456       (
const_cast<virtual_interpolate_transformation *
> 
 2458     p->add_contact_boundary(md, m, dispname, region, 
true);
 
 2462   (ga_workspace &workspace, 
const std::string &transname, 
const mesh &m,
 
 2463    const std::string &dispname, 
size_type region) {
 
 2464     raytracing_interpolate_transformation *p
 
 2465       = 
dynamic_cast<raytracing_interpolate_transformation *
> 
 2466       (
const_cast<virtual_interpolate_transformation *
> 
 2467        (&(*(workspace.interpolate_transformation(transname)))));
 
 2468     p->add_contact_boundary(workspace, m, dispname, region, 
false);
 
 2472   (ga_workspace &workspace, 
const std::string &transname, 
const mesh &m,
 
 2473    const std::string &dispname, 
size_type region) {
 
 2474     raytracing_interpolate_transformation *p
 
 2475       = 
dynamic_cast<raytracing_interpolate_transformation *
> 
 2476       (
const_cast<virtual_interpolate_transformation *
> 
 2477        (&(*(workspace.interpolate_transformation(transname)))));
 
 2478     p->add_contact_boundary(workspace, m, dispname, region, 
true);
 
 2482   (
model &md, 
const std::string &transname,
 
 2484     raytracing_interpolate_transformation *p
 
 2485       = 
dynamic_cast<raytracing_interpolate_transformation *
> 
 2486       (
const_cast<virtual_interpolate_transformation *
> 
 2488     p->add_rigid_obstacle(md, expr, N);
 
 2492   (ga_workspace &workspace, 
const std::string &transname,
 
 2494     raytracing_interpolate_transformation *p
 
 2495       = 
dynamic_cast<raytracing_interpolate_transformation *
> 
 2496       (
const_cast<virtual_interpolate_transformation *
> 
 2497        (&(*(workspace.interpolate_transformation(transname)))));
 
 2498     p->add_rigid_obstacle(workspace, expr, N);
 
 2502   (
model &md, 
const std::string &transname, scalar_type d) {
 
 2503     pinterpolate_transformation
 
 2504       p = std::make_shared<projection_interpolate_transformation>(d);
 
 2509   (ga_workspace &workspace, 
const std::string &transname, scalar_type d) {
 
 2510     pinterpolate_transformation
 
 2511       p = std::make_shared<projection_interpolate_transformation>(d);
 
 2512     workspace.add_interpolate_transformation(transname, p);
 
 2516   (
model &md, 
const std::string &transname, 
const mesh &m,
 
 2517    const std::string &dispname, 
size_type region) {
 
 2518     projection_interpolate_transformation *p
 
 2519       = 
dynamic_cast<projection_interpolate_transformation *
> 
 2520       (
const_cast<virtual_interpolate_transformation *
> 
 2522     p->add_contact_boundary(md, m, dispname, region, 
false);
 
 2526   (
model &md, 
const std::string &transname, 
const mesh &m,
 
 2527    const std::string &dispname, 
size_type region) {
 
 2528     projection_interpolate_transformation *p
 
 2529       = 
dynamic_cast<projection_interpolate_transformation *
> 
 2530       (
const_cast<virtual_interpolate_transformation *
> 
 2532     p->add_contact_boundary(md, m, dispname, region, 
true);
 
 2536   (ga_workspace &workspace, 
const std::string &transname, 
const mesh &m,
 
 2537    const std::string &dispname, 
size_type region) {
 
 2538     projection_interpolate_transformation *p
 
 2539       = 
dynamic_cast<projection_interpolate_transformation *
> 
 2540       (
const_cast<virtual_interpolate_transformation *
> 
 2541        (&(*(workspace.interpolate_transformation(transname)))));
 
 2542     p->add_contact_boundary(workspace, m, dispname, region, 
false);
 
 2546   (ga_workspace &workspace, 
const std::string &transname, 
const mesh &m,
 
 2547    const std::string &dispname, 
size_type region) {
 
 2548     projection_interpolate_transformation *p
 
 2549       = 
dynamic_cast<projection_interpolate_transformation *
> 
 2550       (
const_cast<virtual_interpolate_transformation *
> 
 2551        (&(*(workspace.interpolate_transformation(transname)))));
 
 2552     p->add_contact_boundary(workspace, m, dispname, region, 
true);
 
 2556   (
model &md, 
const std::string &transname,
 
 2558     projection_interpolate_transformation *p
 
 2559       = 
dynamic_cast<projection_interpolate_transformation *
> 
 2560       (
const_cast<virtual_interpolate_transformation *
> 
 2562     p->add_rigid_obstacle(md, expr, N);
 
 2566   (ga_workspace &workspace, 
const std::string &transname,
 
 2568     projection_interpolate_transformation *p
 
 2569       = 
dynamic_cast<projection_interpolate_transformation *
> 
 2570       (
const_cast<virtual_interpolate_transformation *
> 
 2571        (&(*(workspace.interpolate_transformation(transname)))));
 
 2572     p->add_rigid_obstacle(workspace, expr, N);
 
 2585   static void ga_init_vector(bgeot::multi_index &mi, 
size_type N)
 
 2586   { mi.resize(1); mi[0] = N; }
 
 2592   struct Transformed_unit_vector : 
public ga_nonlinear_operator {
 
 2593     bool result_size(
const arg_list &args, bgeot::multi_index &sizes)
 const {
 
 2594       if (args.size() != 2 || args[0]->sizes().size() != 2
 
 2595           || args[1]->size() != args[0]->sizes()[0]
 
 2596           || args[0]->sizes()[0] != args[0]->sizes()[1]) 
return false;
 
 2597       ga_init_vector(sizes, args[0]->sizes()[0]);
 
 2602     void value(
const arg_list &args, base_tensor &result)
 const {
 
 2604       base_matrix F(N, N);
 
 2605       gmm::copy(args[0]->as_vector(), F.as_vector());
 
 2606       gmm::add(gmm::identity_matrix(), F);
 
 2607       bgeot::lu_inverse(&(*(F.begin())), N);
 
 2608       gmm::mult(gmm::transposed(F), args[1]->as_vector(), result.as_vector());
 
 2609       gmm::scale(result.as_vector(),
 
 2610                  scalar_type(1)/gmm::vect_norm2(result.as_vector()));
 
 2621     void derivative(
const arg_list &args, 
size_type nder,
 
 2622                     base_tensor &result)
 const {
 
 2624       base_matrix F(N, N), G(N, N);
 
 2625       base_small_vector ndef(N), aux(N);
 
 2626       gmm::copy(args[0]->as_vector(), F.as_vector());
 
 2627       gmm::add(gmm::identity_matrix(), F);
 
 2628       bgeot::lu_inverse(&(*(F.begin())), N);
 
 2629       gmm::mult(gmm::transposed(F), args[1]->as_vector(), ndef);
 
 2631       gmm::scale(ndef, scalar_type(1)/norm_ndef);
 
 2634       gmm::rank_one_update(G, gmm::scaled(ndef, scalar_type(-1)), aux);
 
 2635       base_tensor::iterator it = result.begin();
 
 2641               *it = -G(i, k) * ndef[j];
 
 2646             *it = (F(j,i) - ndef[i]*ndef[j])/norm_ndef;
 
 2648       default: GMM_ASSERT1(
false, 
"Internal error");
 
 2650       GMM_ASSERT1(it == result.end(), 
"Internal error");
 
 2655                            base_tensor &)
 const {
 
 2656       GMM_ASSERT1(
false, 
"Sorry, second derivative not implemented");
 
 2662   struct Coulomb_friction_coupled_projection : 
public ga_nonlinear_operator {
 
 2663     bool result_size(
const arg_list &args, bgeot::multi_index &sizes)
 const {
 
 2664       if (args.size() != 6) 
return false;
 
 2666       if (N == 0 || args[1]->size() != N || args[2]->size() != N
 
 2667           || args[3]->size() != 1 || args[4]->size() > 3
 
 2668           || args[4]->size() == 0 || args[5]->size() != 1 ) 
return false;
 
 2669       ga_init_vector(sizes, N);
 
 2674     void value(
const arg_list &args, base_tensor &result)
 const {
 
 2675       const base_vector &lambda = *(args[0]);
 
 2676       const base_vector &n = *(args[1]);
 
 2677       const base_vector &Vs = *(args[2]);
 
 2678       base_vector &F = result;
 
 2679       scalar_type g = (*(args[3]))[0];
 
 2680       const base_vector &f = *(args[4]);
 
 2681       scalar_type r = (*(args[5]))[0];
 
 2686       scalar_type lambdan_aug = gmm::neg(lambdan + r * g);
 
 2688       scalar_type tau = ((s_f >= 3) ? f[2] : scalar_type(0)) + f[0]*lambdan_aug;
 
 2689       if (s_f >= 2) tau = std::min(tau, f[1]);
 
 2691       if (tau > scalar_type(0)) {
 
 2692         gmm::add(lambda, gmm::scaled(Vs, -r), F);
 
 2694         gmm::add(gmm::scaled(n, -mu/nn), F);
 
 2696         if (norm > tau) gmm::scale(F, tau / norm);
 
 2699       gmm::add(gmm::scaled(n, -lambdan_aug/nn), F);
 
 2710     void derivative(
const arg_list &args, 
size_type nder,
 
 2711                     base_tensor &result)
 const { 
 
 2713       const base_vector &lambda = *(args[0]);
 
 2714       const base_vector &n = *(args[1]);
 
 2715       const base_vector &Vs = *(args[2]);
 
 2716       base_vector F(N), dg(N);
 
 2717       base_matrix dVs(N,N), dn(N,N);
 
 2718       scalar_type g = (*(args[3]))[0];
 
 2719       const base_vector &f = *(args[4]);
 
 2720       scalar_type r = (*(args[5]))[0];
 
 2724       scalar_type lambdan_aug = gmm::neg(lambdan + r * g);
 
 2726       scalar_type tau = ((s_f >= 3) ? f[2] : scalar_type(0))+f[0]*lambdan_aug;
 
 2727       if (s_f >= 2) tau = std::min(tau, f[1]);
 
 2728       scalar_type norm(0);
 
 2730       if (tau > scalar_type(0)) {
 
 2731         gmm::add(lambda, gmm::scaled(Vs, -r), F); 
 
 2733         gmm::add(gmm::scaled(n, -mu/nn), F);      
 
 2736         gmm::scale(dn, -mu/nn);                                         
 
 2737         gmm::rank_one_update(dn, gmm::scaled(n, mu/(nn*nn*nn)), n);     
 
 2738         gmm::rank_one_update(dn, gmm::scaled(n, scalar_type(-1)/(nn*nn)), F);  
 
 2740         gmm::rank_one_update(dVs, n, gmm::scaled(n, scalar_type(-1)/(nn*nn))); 
 
 2743           gmm::rank_one_update(dVs, F,
 
 2744                                gmm::scaled(F, scalar_type(-1)/(norm*norm)));
 
 2745           gmm::scale(dVs, tau / norm);
 
 2746           gmm::copy(gmm::scaled(F, scalar_type(1)/norm), dg);                  
 
 2747           gmm::rank_one_update(dn, gmm::scaled(F, mu/(norm*norm*nn)), F);
 
 2748           gmm::scale(dn, tau / norm);
 
 2749           gmm::scale(F, tau / norm);
 
 2757       base_tensor::iterator it = result.begin();
 
 2760         if (norm > tau && ((s_f <= 1) || tau < f[1]) &&
 
 2761             ((s_f <= 2) || tau > f[2]))
 
 2762           gmm::rank_one_update(dVs, dg, gmm::scaled(n, -f[0]/nn));
 
 2763         if (lambdan_aug > scalar_type(0))
 
 2764           gmm::rank_one_update(dVs, n, gmm::scaled(n, scalar_type(1)/(nn*nn)));
 
 2770         if (norm > tau && ((s_f <= 1) || tau < f[1])
 
 2771             && ((s_f <= 2) || tau > f[2])) {
 
 2772           gmm::rank_one_update(dn, dg, gmm::scaled(lambda, -f[0]/nn));
 
 2773           gmm::rank_one_update(dn, dg, gmm::scaled(n, f[0]*lambdan/(nn*nn)));
 
 2775         if (lambdan_aug > scalar_type(0)) {
 
 2776           gmm::rank_one_update(dn, gmm::scaled(n, scalar_type(1)/(nn*nn)),
 
 2778           gmm::rank_one_update(dn,
 
 2779                           gmm::scaled(n,(lambdan_aug-lambdan)/(nn*nn*nn)), n);
 
 2780           for (
size_type j = 0; j < N; ++j) dn(j,j) -= lambdan_aug/nn;
 
 2787         gmm::scale(dVs, -r);
 
 2793          if (norm > tau && ((s_f <= 1) || tau < f[1])
 
 2794              && ((s_f <= 2) || tau > f[2]))
 
 2795            gmm::scale(dg, -f[0]*r);
 
 2798          if (lambdan_aug > scalar_type(0))
 
 2799            gmm::add(gmm::scaled(n, r/nn), dg);
 
 2804         if (norm > tau && ((s_f <= 1) || tau < f[1])
 
 2805             && ((s_f <= 2) || tau > f[2]))
 
 2806           gmm::scale(dg, -f[0]*g);
 
 2810         if (lambdan_aug > scalar_type(0))
 
 2811           gmm::add(gmm::scaled(n, g/nn), dg);
 
 2817         base_small_vector dtau_df(s_f);
 
 2818         if ((s_f <= 1) || tau < f[1]) dtau_df[0] = lambdan_aug;
 
 2819         if (s_f >= 2 && tau == f[1]) dtau_df[1] = 1;
 
 2820         if (s_f >= 3 && tau < f[1]) dtau_df[2] = 1;
 
 2823             *it = dg[i] * dtau_df[j];
 
 2826       GMM_ASSERT1(it == result.end(), 
"Internal error");
 
 2831                            base_tensor &)
 const {
 
 2832       GMM_ASSERT1(
false, 
"Sorry, second derivative not implemented");
 
 2836   static bool init_predef_operators() {
 
 2838     ga_predef_operator_tab &PREDEF_OPERATORS
 
 2841     PREDEF_OPERATORS.add_method(
"Transformed_unit_vector",
 
 2842                                 std::make_shared<Transformed_unit_vector>());
 
 2843     PREDEF_OPERATORS.add_method(
"Coulomb_friction_coupled_projection",
 
 2844                     std::make_shared<Coulomb_friction_coupled_projection>());
 
 2850   bool predef_operators_contact_initialized
 
 2851     = init_predef_operators();
 
Balanced tree of n-dimensional rectangles.
 
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).
 
`‘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,...
 
A language for generic assembly of pde boundary value problems.
 
void mult_add(const L1 &l1, const L2 &l2, L3 &l3)
*/
 
void copy(const L1 &l1, L2 &l2)
*/
 
number_traits< typename linalg_traits< V >::value_type >::magnitude_type vect_norm2(const V &v)
Euclidean norm of a vector.
 
void fill_random(L &l)
fill a vector or matrix with random value (uniform [-1,1]).
 
void clear(L &l)
clear (fill with zeros) a vector or matrix.
 
number_traits< typename linalg_traits< V1 >::value_type >::magnitude_type vect_dist2(const V1 &v1, const V2 &v2)
Euclidean distance between two vectors.
 
void resize(V &v, size_type n)
*/
 
void mult(const L1 &l1, const L2 &l2, L3 &l3)
*/
 
strongest_value_type< V1, V2 >::value_type vect_sp(const V1 &v1, const V2 &v2)
*/
 
void add(const L1 &l1, L2 &l2)
*/
 
void lu_inverse(const DenseMatrixLU &LU, const Pvector &pvector, const DenseMatrix &AInv_)
Given an LU factored matrix, build the inverse of the matrix.
 
std::shared_ptr< const getfem::virtual_fem > pfem
type of pointer on a fem description
 
gmm::uint16_type short_type
used as the common short type integer in the library
 
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
 
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 .
 
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...