/*===========================================================================

 Copyright (C) 2007-2020 Yves Renard,  Julien Pommier

 This file is a part of GetFEM

 GetFEM  is  free software;  you  can  redistribute  it  and/or modify it
 under  the  terms  of the  GNU  Lesser General Public License as published
 by  the  Free Software Foundation;  either version 3 of the License,  or
 (at your option) any later version along with the GCC Runtime Library
 Exception either version 3.1 or (at your option) any later version.
 This program  is  distributed  in  the  hope  that it will be useful,  but
 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or  FITNESS  FOR  A PARTICULAR PURPOSE.  See the GNU Lesser General Public
 License and GCC Runtime Library Exception for more details.
 You  should  have received a copy of the GNU Lesser General Public License
 along  with  this program;  if not, write to the Free Software Foundation,
 Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.

===========================================================================*/

#include "crack_bilaplacian.h"
#include "getfem/getfem_regular_meshes.h"
#include "getfem/getfem_assembling.h" /* import assembly methods (and norms comp.) */
#include "getfem/getfem_fourth_order.h"
#include "getfem/getfem_model_solvers.h"
#include "getfem/getfem_superlu.h"
#include "getfem/getfem_derivatives.h"

using std::endl; using std::cout; using std::cerr;
using std::ends; using std::cin;

namespace getfem {
  /* build a "ring" of convexes of given center and radius */
  dal::bit_vector 
  build_sif_ring_from_mesh_KL(const mesh &m, 
                           base_node center, scalar_type r) {
    dal::bit_vector bv;
    scalar_type r2 = r - 1e-4;
    unsigned in = 0;
    for (dal::bv_visitor cv(m.convex_index()); !cv.finished(); ++cv) {
      unsigned in1=0, out1=0;
      unsigned in2=0, out2=0;
      for (unsigned i=0; i < m.nb_points_of_convex(cv); ++i) {
        base_node P = m.points_of_convex(cv)[i];
        if (gmm::vect_dist2(P, center) < r) ++in1; else ++out1;
        if (gmm::vect_dist2(P, center) < r2) ++in2; else ++out2;
      }
      if ((in1 && out1) || (in2 && out2)) bv.add(cv);
      in += in1;
    }
    if (in < 8) GMM_WARNING1("looks like the radius is too small...");
    return bv;
  }

  /* return the crack tip in P,
     and the outgoing tangent of the crack in T,
     and the normal in N */
  void get_crack_tip_and_orientation_KL(const level_set &/* ls */,
                                     base_node &P, 
                                     base_small_vector &T, base_small_vector &N) {
    cerr << __PRETTY_FUNCTION__ << " IS TO BE DONE\n";
    /* too lazy to do it now */
    P.resize(2); P[0] = 0.; P[1] = 0;
    T.resize(2); T[0] = 1; T[1] = 0;
    N.resize(2); N[0] = 0; N[1] = 1;
  }




  /* return a (very rough) estimate of the stress intensity factors using 
     the local displacement near the crack tip */
  template <typename VECT> 
  void estimate_crack_stress_intensity_factors(const level_set &ls,
                                               const mesh_fem &mf, const VECT &U, 
                                               scalar_type young_modulus, 
                                               scalar_type &KI, 
                                               scalar_type &KII, double EPS=1e-2) {
    base_node P(2);
    base_small_vector T(2),N(2); 
    get_crack_tip_and_orientation_KL(ls, P, T, N);
    base_node P1 = P - EPS*T + EPS/100.*N;
    base_node P2 = P - EPS*T - EPS/100.*N;
    std::vector<double> V(4);
    getfem::mesh_trans_inv mti(mf.linked_mesh());
    mti.add_point(P1);
    mti.add_point(P2);
    cout << "P1 = " << P1 << ", P2=" << P2 << "\n";
    base_matrix M;
    getfem::interpolation(mf, mti, U, V, false);
    KI = (V[1] - V[3])/sqrt(EPS/(2*M_PI)) * young_modulus / 8.0;
    KII = (V[0] - V[2])/sqrt(EPS/(2*M_PI)) * young_modulus / 8.0;
  }

/* compute with great precision the stress intensity factors using
     integral computed on a ring around the crack tip, for BILAPLACIAN PLATE PROBLEM */
  template <typename VECT>
  void compute_crack_stress_intensity_factors_KL(const level_set &ls, 
                                              const mesh_im &mim,
                                              const mesh_im &mim2,
                                              const mesh_im &mim3,
                                              const mesh_fem &mf_pre_u, 
                                              const mesh_fem &mf, 
                                              const VECT &U,
                                              scalar_type ring_radius, 
                                              scalar_type D, scalar_type nu,
                                              scalar_type young_modulus, scalar_type epsilon,
                                              scalar_type &KI, scalar_type &KII) {
    // build mesh and geometrical features
    const mesh &mring = mim.linked_mesh();
    base_node crack_tip;
    base_small_vector T, N;
    get_crack_tip_and_orientation_KL(ls, crack_tip, T, N);

    dal::bit_vector cvring = build_sif_ring_from_mesh_KL(mring, crack_tip, 
                                                      ring_radius);

    // set mesh_fem
    mesh_fem_global_function mf_mode(mring, 1), mf_mode_3(mring, 1);

    std::vector<pglobal_function> cfun(2), cfun_3(4) ;
    for (unsigned j=4; j < 6; ++j)
        cfun[j-4] = bilaplacian_crack_singular(j, ls, nu, 0.) ;
    mf_mode.set_functions(cfun);

    cfun_3[0] = bilaplacian_crack_singular(311, ls, nu, 0.) ;
    cfun_3[1] = bilaplacian_crack_singular(312, ls, nu, 0.) ;
    cfun_3[2] = bilaplacian_crack_singular(321, ls, nu, 0.) ;
    cfun_3[3] = bilaplacian_crack_singular(322, ls, nu, 0.) ;
    mf_mode_3.set_functions(cfun_3);
    mf_mode_3.set_qdim(2) ;
    mf_mode.set_qdim(1);

    
    getfem::pfem pf ;

    /* fill the "q" ring field with a C^1 field, equal to 
       1 on the inner boundary, and equal to zero on the outer boundary */
    GMM_ASSERT1(!mf_pre_u.is_reduced(), "To be adapted");
    std::vector<scalar_type> q(mf_pre_u.nb_dof());
    for (unsigned d = 0; d < mf_pre_u.nb_dof(); ++d) {
        // getting a getfem::pdof_description about the curent dof
        const getfem::mesh::ind_cv_ct cvs = mf_pre_u.convex_to_basic_dof(d);
        size_type cv = cvs[0], ld = unsigned(-1);
        // getting the local index  (ld)
        for (unsigned dd = 0; dd < mf_pre_u.nb_basic_dof_of_element(cv); dd += 1) {
           if (mf_pre_u.ind_basic_dof_of_element(cv)[dd] == d) {
             ld = dd; 
             break;}
        }
        pf = mf_pre_u.fem_of_element(cv);
        if ( pf->dof_types().at(ld) == getfem::lagrange_dof(2)){ 
                // pf-> dof_types gives a std_vector<pdof_desciption>
//        if (ld == 0 || ld == 3 || ld == 6 || ld == 9){
           base_node P = mf_pre_u.point_of_basic_dof(d);
           q[d] = (gmm::vect_dist2(P, crack_tip) > 0.999 * ring_radius) ? 0. : 1.; 
        }
        else
           q[d] = 0. ;
    }

      std::string datafilename_q = "q_field.vtk" ;
      cout << "export q field to " << datafilename_q << "..\n";
      getfem::vtk_export exp(datafilename_q, 1);
      exp.exporting(mf_pre_u); 
      exp.write_point_data(mf_pre_u, q, "q_field_displacement");
      cout << "export done, you can view the data file with (for example)\n"
	   << "mayavi -d " << datafilename_q
	   << " -m BandedSurfaceMap -m Outline -f WarpScalar\n";
 
    base_vector U_mode(mf_mode.nb_dof()); assert(U_mode.size() == 2);
    base_vector U_mode_3(mf_mode_3.nb_dof()); assert(U_mode_3.size() == 8);
    
  

cout << "OK avant assemblage\n" ;
  

// ESSAI : dérivation du champs solution de U3, pour prendre le laplacien
// dans l'assemblage (probleme : on perd de la régularité en route)

//     mesh_fem mf_du(mim.linked_mesh(), 1) ;
//     mf_du.set_classical_finite_element(3);
//     plain_vector GRAD(mf_du.nb_dof() * 2) ;
//     getfem::compute_gradient(mf, mf_du, U, GRAD) ;
//     // separation des 2 composantes 
//     plain_vector D1(mf_du.nb_dof()), D2(mf_du.nb_dof()) ;
//     for (unsigned i=0 ; i < mf_du.nb_dof() ; ++i){
//        D1[i] = GRAD[2*i] ;
//        D2[i] = GRAD[2*i+1] ;
//     }
//     cout << "1er derivation OK \n " ;
// 
//     mesh_fem mf_d2u(mim.linked_mesh(), 1) ;
//     mf_d2u.set_classical_discontinuous_finite_element(2);
//     plain_vector GRAD_U1(mf_d2u.nb_dof() * 2), GRAD_U2(mf_d2u.nb_dof() * 2) ;
//     getfem::compute_gradient(mf_du, mf_d2u, D1, GRAD_U1) ;
//     getfem::compute_gradient(mf_du, mf_d2u, D2, GRAD_U2) ;
//     cout << "2e derivation : OK \n" ; 
//     // calcul du laplacien
//     plain_vector LAP(mf_d2u.nb_dof()) ;
//     cout << "taille GRAD_U1 = " << GRAD_U1.size() << "\n" ;
//     cout << "taille GRAD_U1 = " << GRAD_U1.size() << "\n" ;
//     
//     for (unsigned i=0 ; i < mf_d2u.nb_dof() ; ++i)
//        LAP[i] = GRAD_U1[2*i] + GRAD_U2[2*i+1] ;
// 
//     //dérivation :
//     mesh_fem mf_d3u(mim.linked_mesh(), 1) ;
//     mf_d3u.set_classical_discontinuous_finite_element(1);
//     plain_vector D3U(mf_d3u.nb_dof() * 2) ;
//     cout << "juste avant la 3e derivation \n";
//     getfem::compute_gradient(mf_d2u, mf_d3u, LAP, D3U) ;
//     mf_d3u.set_qdim(2) ;
//     cout << "derivations : OK ! \n" ;
    

// 
//     mesh_fem mf_du1(mim.linked_mesh(), 1) ;
//     mf_du1.set_classical_finite_element(5);
//     //mf_du.set_qdim(2) ; // peut-etre a enlever ?...
//     plain_vector W(mf_du.nb_dof() * 2), W1(mf_du.nb_dof()), W2(W1) ;
//     getfem::compute_gradient(mf, mf_du, U, W) ;
//     for(unsigned i=0 ; i < mf_du.nb_dof() ; ++i) {
//        W1[i] = W[2*i] ;
//        W2[i] = W[2*i+1] ;}
// 
// 
// 

    /* expression for SIF computation taken from "a finite element
       method for crack growth without remeshing", moes, dolbow &
       belytschko */
//   generic_assembly
//       assem("D_1_nu=data$1(1); D_nu=data$2(1); minus=data$3(1); D=data$4(1); x1=data$5(mdim(#1)); U1=data$6(#1); U2=data$7(#2); q=data$8(#3); U3=data$9(#4); V1=data$10(#5);"
//             "T=U1(i).U2(j).q(k).comp(Hess(#1).Hess(#2).Grad(#3))(i,:,:,j,:,:,k,:);"
//             "t=T;"
//             "V()+=minus(p).D_1_nu(p).t(i,j,i,k,j).x1(k) + minus(p).D_nu(p).t(i,i,j,k,j).x1(k);"
//             "V()+=minus(p).D_1_nu(p).t(i,k,i,j,j).x1(k) + minus(p).D_nu(p).t(j,k,i,i,j).x1(k);"
//             "V()+=D_1_nu(p).t(i,j,i,j,k).x1(k) + D_nu(p).t(i,i,j,j,k).x1(k);");
//     generic_assembly
//       assem("D_1_nu=data$1(1); D_nu=data$2(1); minus=data$3(1); D=data$4(1); x1=data$5(mdim(#1)); U1=data$6(#1); U2=data$7(#2); q=data$8(#3); U3=data$9(#4); D3U=data$10(#5);"
//             "T=U1(i).U2(j).q(k).comp(Hess(#1).Hess(#2).Grad(#3))(i,:,:,j,:,:,k,:);"
//             "t=T;"
//             "V()+=minus(p).D_1_nu(p).t(i,j,i,k,j).x1(k) + minus(p).D_nu(p).t(i,i,j,k,j).x1(k);"
//             "V()+=minus(p).D_1_nu(p).t(i,k,i,j,j).x1(k) + minus(p).D_nu(p).t(j,k,i,i,j).x1(k);"
//             "V()+=D_1_nu(p).t(i,j,i,j,k).x1(k) + D_nu(p).t(i,i,j,j,k).x1(k);"
//             "T2=U3(i).U1(j).q(k).comp(vBase(#4).Grad(#1).Grad(#3))(i,:,j,:,k,:);"
//             "t2=T2;"
//             "V()+=D(p).T2(i,j,i).x1(j);"
//             "T1=D3U(i).U2(j).q(k).comp(vBase(#5).Grad(#2).Grad(#3))(i,:,j,:,k,:);"
//             "t1=T1;"
//             "V()+=D(p).T1(i,j,i).x1(j);");
    generic_assembly
      assem("D_1_nu=data$1(1); D_nu=data$2(1); minus=data$3(1); D=data$4(1); x1=data$5(mdim(#1)); U1=data$6(#1); U2=data$7(#2); q=data$8(#3); U3=data$9(#4);"
            "T=U1(i).U2(j).q(k).comp(Hess(#1).Hess(#2).Grad(#3))(i,:,:,j,:,:,k,:);"
            "t=T;"
            "V()+=minus(p).D_1_nu(p).t(i,j,i,k,j).x1(k) + minus(p).D_nu(p).t(i,i,j,k,j).x1(k);"
            "V()+=minus(p).D_1_nu(p).t(i,k,i,j,j).x1(k) + minus(p).D_nu(p).t(j,k,i,i,j).x1(k);"
            "V()+=D_1_nu(p).t(i,j,i,j,k).x1(k) + D_nu(p).t(i,i,j,j,k).x1(k);"
            "T2=U3(i).U1(j).q(k).comp(vBase(#4).Grad(#1).Grad(#3))(i,:,j,:,k,:);"
            "t2=T2;"
            "V()+=D(p).T2(i,j,i).x1(j);"
            "T11=U1(i).U2(j).q(k).comp(Hess(#1).Grad(#2).Hess(#3))(i,:,:,j,:,k,:,:);"
            "t11=T11;"
            "V()+=minus(p).D(p).t11(i,i,j,k,k).x1(j);"
            "V()+=minus(p).D(p).t(i,i,j,k,k).x1(j);");
    assem.push_mf(mf);
    assem.push_mf(mf_mode);
    assem.push_mf(mf_pre_u);
    assem.push_mf(mf_mode_3);
    assem.push_mi(mim);
    base_vector vD_1_nu(1); vD_1_nu[0] =  D * (1. - nu);
    base_vector vD_nu(1); vD_nu[0] = D * nu ;
    base_vector minus(1); minus[0] = -1. ;
    base_vector vD(1); vD[0] = D ;
    assem.push_data(vD_1_nu); 
    assem.push_data(vD_nu); 
    assem.push_data(minus); 
    assem.push_data(vD);
    assem.push_data(T); // outgoing tangent of the crack
    assem.push_data(U); 
    assem.push_data(U_mode);
    assem.push_data(q);
    assem.push_data(U_mode_3);
    base_vector V(1);
    assem.push_vec(V);

//             "TT2=q(k).comp(%2,Grad(#3))(k,:);"
// 	    "tt2=TT2;" 
// 	    "V()+=D(p).tt2(k).x2(k);"

    generic_assembly assem2("U1=data$1(#1);"
                            "U2=data$2(#2);"
			    "q=data$3(#3);"
                            "D=data$4(1);"
			    "x1=data$5(mdim(#1));"
                            "x2=data$6(mdim(#1));"
			    "minus=data$7(1);"
	    "T=U1(i).U2(j).q(k).comp(Hess(#1).Grad(#2).Grad(#3))(i,:,:,j,:,k,:);"
	    "t=T;"
	    "V()+=minus(p).D(p).t(i,i,j,k).x1(j).x2(k);") ;
    assem2.push_mf(mf);
    assem2.push_mf(mf_mode);
    assem2.push_mf(mf_pre_u);
    assem2.push_mi(mim2);
    assem2.push_data(U); 
    assem2.push_data(U_mode);
    assem2.push_data(q);
    assem2.push_data(vD); 
    assem2.push_data(T); // outgoing tangent of the crack 
    assem2.push_data(N); // Upward normal to the crack
    assem2.push_data(minus);
    base_vector W2(1);
    assem2.push_vec(W2);
    
    generic_assembly assem3("U1=data$1(#1);"
                            "U2=data$2(#2);"
			    "q=data$3(#3);"
                            "D=data$4(1);"
			    "x1=data$5(mdim(#1));"
                            "x2=data$6(mdim(#1));"
			    "minus=data$7(1);"
	    "T=U1(i).U2(j).q(k).comp(Hess(#1).Grad(#2).Grad(#3))(i,:,:,j,:,k,:);"
	    "t=T;"
	    "V()+=D(p).t(i,i,j,k).x1(j).x2(k);") ;
    assem3.push_mf(mf);
    assem3.push_mf(mf_mode);
    assem3.push_mf(mf_pre_u);
    assem3.push_mi(mim3);
    assem3.push_data(U); 
    assem3.push_data(U_mode);
    assem3.push_data(q);
    assem3.push_data(vD); 
    assem3.push_data(T); // outgoing tangent of the crack 
    assem3.push_data(N); // Upward normal to the crack
    assem3.push_data(minus);
    base_vector W3(1);
    assem3.push_vec(W3);
    
    /* fill with the crack opening mode I or mode II */
    for (unsigned mode = 1; mode <= 2; ++mode) {
      base_vector::iterator it = U_mode.begin();
      base_vector::iterator it_3 = U_mode_3.begin();
      scalar_type coeff=sqrt(2.) * (1. - nu * nu) 
                    / (2. * young_modulus * epsilon * (3. + nu));
      switch(mode) {
        case 1: {
          *it++ = 1.;        /* mode I */
          *it++ = 0.;      /* mode II */
          /* "colonne" 1: ux, colonne 2: uy */
          *it_3++ = 1.;     *it_3++ = 0.; /* - cos(3 *theta/2.) */
          *it_3++ = 0.;     *it_3++ = 1.; /* - sin(3 *theta/2.) */
          *it_3++ = 0.;     *it_3++ = 0.; /* - sin(3 *theta/2.) */
          *it_3++ = 0.;     *it_3++ = 0.; /*   cos(3 *theta/2.) */
        } break;
        case 2: {
          *it++ = 0.;
          *it++ = 1.;
          *it_3++ = 0.;     *it_3++ = 0.;
          *it_3++ = 0.;     *it_3++ = 0.;
          *it_3++ = 1.;     *it_3++ = 0.;
          *it_3++ = 0.;     *it_3++ = 1.;
        } break;
      }
      gmm::scale(U_mode, coeff);
      gmm::scale(U_mode_3, coeff);
      //

/*      getfem::compute_gradient(mf, mf_du2, U_mode, DU2) ;
      for(unsigned i=0 ; i < mf_du2.nb_dof() ; ++i) {
       D1U2[i] = DU2[2*i] ;
       D2U2[i] = DU2[2*i+1] ;}*/
      //
      V[0] = 0.;
      cout << "assemblig SIFs ..." << std::flush; 
      double time = gmm::uclock_sec();
      assem.assembly(cvring);
      W2[0] = 0. ;
      W3[0] = 0. ;
      assem2.assembly(cvring);
      assem3.assembly(cvring);
      cout << "done (" << gmm::uclock_sec()-time << " sec)\n";
      scalar_type a = 2. * epsilon * M_PI * (1. + nu) / (3. * young_modulus * (3. + nu) ) ;
      V[0] /= 2. * a ; 
      W2[0] /= 2. * a ;
      W3[0] /= 2. * a ;
      scalar_type W = W2[0] + W3[0] ;   
      if ( mode == 2 ) {
         cout << "mode " << mode << " ;\n  ";
	 cout << "W="  << W     << "\n" ;
         cout << "W2=" << W2[0] << "\n" ;
	 cout << "W3=" << W3[0] << "\n" ;
	 }
      if (mode == 1) KI = V[0]  ; else KII = V[0] ;
    }
  }

} // end of namespace ?

// scalar_type young_modulus(scalar_type lambda, scalar_type mu){
//   return 4*mu*(lambda + mu)/(lambda+2*mu);
// }



void bilaplacian_crack_problem::compute_sif(const plain_vector &U, scalar_type ring_radius){

  cout << "Computing stress intensity factors\n";
  base_node tip; 
  base_small_vector T, N;
  get_crack_tip_and_orientation_KL(ls, tip, T, N);
  cout << "crack tip is : " << tip << ", T=" << T << ", N=" << N << "\n";
  scalar_type E = 3. * (1. - nu * nu) * D / (2. * epsilon * epsilon * epsilon) ;
  cout << "young modulus: " << E << "\n";
  //scalar_type ring_radius = 0.2;
  
  scalar_type KI, KII ;
/*  estimate_crack_stress_intensity_factors(ls, mf_u(), U, 
                                          E, 
                                          KI, KII, 1e-2);
  cout << "estimation of crack SIF: " << KI << ", " << KII << "\n";*/
    getfem::level_set ls2(mesh, 1, true) ;
    scalar_type x, y ;
    for (size_type d = 0; d < ls2.get_mesh_fem().nb_dof(); ++d) {
      x = ls2.get_mesh_fem().point_of_basic_dof(d)[0];
      y = ls2.get_mesh_fem().point_of_basic_dof(d)[1];
      ls2.values(0)[d] = y + 1e-6 ;
      ls2.values(1)[d] = x ;
      }
    getfem::mesh_level_set mls2(mesh);
    mls2.add_level_set(ls2);
    mls2.adapt() ;
    
    getfem::level_set ls3(mesh, 1, true) ;
    for (size_type d = 0; d < ls3.get_mesh_fem().nb_dof(); ++d) {
      x = ls3.get_mesh_fem().point_of_basic_dof(d)[0];
      y = ls3.get_mesh_fem().point_of_basic_dof(d)[1];
      ls3.values(0)[d] = y - 1e-6 ;
      ls3.values(1)[d] = x ;
      }  
    getfem::mesh_level_set mls3(mesh);
    mls3.add_level_set(ls3);
    mls3.adapt() ;
    
    
    cout << "initialisations ls2, ls3, mls2, mls3 : OK\n" ;
    
    int where = int(PARAM.int_value("WHERE"));
// 1	getfem::mesh_im_level_set::INTEGRATE_INSIDE (integrate over p(x)<0),
// 2	getfem::mesh_im_level_set::INTEGRATE_OUTSIDE (integrate over p(x)>0),
// 3	getfem::mesh_im_level_set::INTEGRATE_ALL,
// 4	getfem::mesh_im_level_set::INTEGRATE_BOUNDARY (integrate over p(x)=0 and s(x) <= 0)
   switch (where){
   case 3: {
   where = getfem::mesh_im_level_set::INTEGRATE_ALL ;}
   break ;
   case 4:{
   where = getfem::mesh_im_level_set::INTEGRATE_BOUNDARY ;}
   break;
   }
   getfem::mesh_im_level_set mim2(mls2, where) ;
   getfem::mesh_im_level_set mim3(mls3, where) ;
   
   
    if (!PARAM.int_value("MIXED_ELEMENTS")){ 
        // Must put 2D methods.
        std::string INTEGRATION = PARAM.string_value("INTEGRATION_LINE",
   					       "Name of integration method");
        std::string SIMPLEX_INTEGRATION = PARAM.string_value("SIMPLEX_INTEGRATION_LINE",
    					 "Name of simplex integration method");  
        //must put a method defined for quadrangles.
        //std::string SINGULAR_INTEGRATION = PARAM.string_value("SINGULAR_INTEGRATION_LINE");
        getfem::pintegration_method ppi = 
        getfem::int_method_descriptor(INTEGRATION);
        getfem::pintegration_method sppi = 
        getfem::int_method_descriptor(SIMPLEX_INTEGRATION);
        //getfem::pintegration_method sing_ppi = (SINGULAR_INTEGRATION.size() ?
        //  getfem::int_method_descriptor(SINGULAR_INTEGRATION) : 0);
        mim2.set_simplex_im(sppi, 0);   // No need to specify a singular sing integration,
        mim3.set_simplex_im(sppi, 0);   // since the J-integral doesn't meet the crack tip.
        mim2.set_integration_method(mesh.convex_index(), ppi);
        mim3.set_integration_method(mesh.convex_index(), ppi);
    }
    else{
        // Must put 2D methods.
        std::string TRI_INTEGRATION = PARAM.string_value("TRI_INTEGRATION_LINE",
    					       "Name of integration method");
        std::string TRI_SIMPLEX_INTEGRATION = PARAM.string_value("TRI_SIMPLEX_INTEGRATION_LINE",
    					 "Name of simplex integration method");  
        getfem::pintegration_method tri_ppi = 
        getfem::int_method_descriptor(TRI_INTEGRATION);
        getfem::pintegration_method tri_sppi = 
        getfem::int_method_descriptor(TRI_SIMPLEX_INTEGRATION);
        std::string QUAD_INTEGRATION = PARAM.string_value("QUAD_INTEGRATION_LINE",
    					       "Name of integration method");
        std::string QUAD_SIMPLEX_INTEGRATION = PARAM.string_value("QUAD_SIMPLEX_INTEGRATION_LINE",
    					 "Name of simplex integration method");  
        getfem::pintegration_method quad_ppi = 
        getfem::int_method_descriptor(QUAD_INTEGRATION);
        getfem::pintegration_method quad_sppi = 
        getfem::int_method_descriptor(QUAD_SIMPLEX_INTEGRATION);
	mim2.set_simplex_im(tri_sppi, 0);   // No need to specify a singular sing integration,
        mim3.set_simplex_im(tri_sppi, 0);   // since the J-integral doesn't meet the crack tip.
	dal::bit_vector quad_among_cvx, tri_among_cvx ;
        for (dal::bv_visitor i(mesh.convex_index()) ; !i.finished() ; ++i){
          if (mesh.points_of_convex(i).size() == 3)
             tri_among_cvx.add(i) ;
          else if (mesh.points_of_convex(i).size() == 4)
             quad_among_cvx.add(i) ;
          else cout << "WARNING : an element has nor 3 or 4 nodes ! \n" ;
        }
        mim2.set_integration_method(tri_among_cvx, tri_ppi);
        mim2.set_integration_method(quad_among_cvx, quad_ppi);
        mim3.set_integration_method(tri_among_cvx, tri_ppi);
        mim3.set_integration_method(quad_among_cvx, quad_ppi);
    }  
    mim2.adapt() ;
    mim3.adapt() ;
    cout << "mim2.set_integration_method(... -> OK\n";
    
    cout << "done.\nEntering in compute_crack_stress_intensity_factors_KL(... \n" ;
    compute_crack_stress_intensity_factors_KL(ls, mim, mim2, mim3, mf_pre_u, mf_u(), U, ring_radius, D, nu, E, epsilon, KI, KII);
    cout << "computation of crack SIF via J-Integral \nK1_J:" << KI << "\nK2_J:" << KII << "\n";
}

void bilaplacian_crack_problem::exact_sif(scalar_type &K1, scalar_type &K2){

   scalar_type E, nu2 ;
   base_small_vector k(2) ;
   nu2 = nu * nu ;
   E = 3. * (1. - nu2) * D / (2. * epsilon * epsilon * epsilon) ;

   switch (sol_ref) {
      case 0: {
      K1 = exact_sol.U[2] * (3.*(nu - 1.)) + exact_sol.U[3] * (3. * nu + 5.) ;
      K1 *= - ( sqrt(2.) * E * epsilon ) / (4. * (1. - nu2) ) ;
      K2 = (exact_sol.U[0] + 3. * exact_sol.U[1]) ;
      K2 *= - E * epsilon * sqrt(2.) * (3. + nu) / (4. * (1. + nu) * (1. + nu) ) ;
      } break;
      case 1: {
      K1 = 3. * sqrt(PARAM.real_value("CRACK_SEMI_LENGTH")) / (2. * epsilon * epsilon) ;
      K2 = 0. ;
      } break;
   }
   cout << "K1_EXACT:"  << K1  << "\n" ;
   cout << "K2_EXACT:" << K2 << "\n" ;      
}

size_type my_is_global_dof_type(getfem::pdof_description dof){
size_type global_dof = 0 ;
   for (dim_type d = 0; d < 4 ; ++d){
       if (dof == getfem::global_dof(d)) {
          global_dof = 1;
	      }
   }
return global_dof ;
}

void bilaplacian_crack_problem::sif_direct_estimation(const plain_vector &U){
   
   unsigned q = mf_u().get_qdim();
   base_small_vector tab_fic(4) ;
   unsigned cpt = 0;
   assert(enrichment_option > 2) ; // global singularities not available in XFEM 
                                   // standard or without any enrichment.

   // Looking for coefficients corresponding to global singularites
   GMM_ASSERT1(!mf_u().is_reduced(), "To be adapted");
   for (size_type d=0; d < mf_u().nb_dof(); d += q) {
      size_type cv = mf_u().first_convex_of_basic_dof(d) ;
      getfem::pfem pf = mf_u().fem_of_element(cv);
      unsigned ld = unsigned(-1);
      for (unsigned dd = 0; dd < mf_u().nb_basic_dof_of_element(cv); dd += q) {
         if (mf_u().ind_basic_dof_of_element(cv)[dd] == d){ 
            ld = dd/q; 
            break;
         }
      }   
      if (ld == unsigned(-1)) {
         cout << "DOF " << d << "NOT FOUND in " << cv << " BUG BUG\n";
      } 
      else {
         if ( my_is_global_dof_type(pf->dof_types().at(ld)) ){
         //cout << "coeff:" << U[d] << "\n" ;
         tab_fic[cpt] = U[d] ;
         cpt += 1 ;
         }
      }
   }

   scalar_type E, nu2 ;
   base_small_vector k(2) ;
   nu2 = nu * nu ;
   E = 3. * (1. - nu2) * D / (2. * epsilon * epsilon * epsilon) ;

   // Calculation of SIF KI and KII

   if (PARAM.int_value("SING_BASE_TYPE") == 0){
      k[0] = tab_fic[2] * (3.*(nu - 1.)) + tab_fic[3] * (3. * nu + 5.) ;
      k[0] *= - ( sqrt(2.) * E * epsilon ) / (4. * (1. - nu2) ) ;
      k[1] = (tab_fic[0] + 3. * tab_fic[1]) ;
      k[1] *= - E * epsilon * sqrt(2.) * (3. + nu) / (4. * (1. + nu) * (1. + nu) ) ;
   }
   if (PARAM.int_value("SING_BASE_TYPE") == 1){
      scalar_type A ;
      A =  sqrt(2) * epsilon * E * (nu + 3.) / (1. - nu2) ;
      k[0] = - tab_fic[0] * A ;
      k[1] =   tab_fic[1] * A ; 
   }
   cout << "K1_DIRECT:" << k[0] << "\n" ;
   cout << "K2_DIRECT:" << k[1] << "\n" ;
    
}























