diff --git a/demo/dirichlet.cpp b/demo/dirichlet.cpp
index ba19399e01808c7d27e7c7ff06f93684b65d3ed0..5b119a8a5c781fbfbf4866b0ad277a8575cc496d 100644
--- a/demo/dirichlet.cpp
+++ b/demo/dirichlet.cpp
@@ -8,8 +8,8 @@
  |   _#_   |   AUTHOR(S)  : Matthieu Aussal                               |
  |  ( # )  |   CREATION   : 01.04.2020                                    |
  |  / 0 \  |   LAST MODIF : 31.10.2020                                    |
- | ( === ) |   SYNOPSIS   : Acoustical scattering using BEM with Dirichlet|
- |  `---'  |                boundary condition                            |
+ | ( === ) |   SYNOPSIS   : Acoustical scattering using H-Matrix BEM      |
+ |  `---'  |                solver for Dirichlet boundary condition       |
  +========================================================================+
  */
 
@@ -18,17 +18,18 @@
 #include <castor/hmatrix.hpp>
 #include <castor/linalg.hpp>
 #include <castor/graphics.hpp>
-#include "bembuilder.hpp"
+#include "fem.hpp"
+#include "bem.hpp"
 
 using namespace castor;
 
-int main (int argc, char* argv[])
+int main(int argc, char* argv[])
 {
     // Load meshes
     matrix<double> Svtx, Pvtx;
     matrix<size_t> Stri, Ptri;
-    std::tie(Stri,Svtx) = triread("/Users/aussal/Git/leprojetcastor/bembuilder/data/","unitSphere_1e3.ply");
-    std::tie(Ptri,Pvtx) = triread("/Users/aussal/Git/leprojetcastor/bembuilder/data/","Oxyz_1e4.ply");
+    std::tie(Stri,Svtx) = triread("/Users/aussal/Git/leprojetcastor/fembem/demo/data/","unitSphere_1e3.ply");
+    std::tie(Ptri,Pvtx) = triread("/Users/aussal/Git/leprojetcastor/fembem/demo/data/","Oxyz_1e4.ply");
     
     // Graphical representation
     figure fig;
@@ -83,7 +84,7 @@ int main (int argc, char* argv[])
     // Solve linear system : S lambda = -PO
     hmatrix<std::complex<double>> Lh,Uh;
     tic();
-    std::tie(Lh,Uh) = lu(Sbnd,tol);
+    std::tie(Lh,Uh) = lu(Sbnd);
     toc();
     disp(Lh);
     disp(Uh);
diff --git a/demo/dirichletBW.cpp b/demo/dirichletBW.cpp
index 23ad4dd6c6b69164e2fdc35bc71d598de8620a4d..c95e45fb29d2c0cc59f21f3a3c63b0cfad24e85f 100644
--- a/demo/dirichletBW.cpp
+++ b/demo/dirichletBW.cpp
@@ -8,8 +8,8 @@
  |   _#_   |   AUTHOR(S)  : Matthieu Aussal                               |
  |  ( # )  |   CREATION   : 01.04.2020                                    |
  |  / 0 \  |   LAST MODIF : 31.10.2020                                    |
- | ( === ) |   SYNOPSIS   : Acoustical scattering using BEM with Dirichlet|
- |  `---'  |                boundary condition                            |
+ | ( === ) |   SYNOPSIS   : Acoustical scattering using H-Matrix BEM      |
+ |  `---'  |                solver for Dirichlet boundary condition       |
  +========================================================================+
  */
 
@@ -18,7 +18,8 @@
 #include <castor/hmatrix.hpp>
 #include <castor/linalg.hpp>
 #include <castor/graphics.hpp>
-#include "bembuilder.hpp"
+#include "fem.hpp"
+#include "bem.hpp"
 
 using namespace castor;
 
@@ -27,8 +28,8 @@ int main (int argc, char* argv[])
     // Load meshes
     matrix<double> Svtx, Pvtx;
     matrix<size_t> Stri, Ptri;
-    std::tie(Stri,Svtx) = triread("/Users/aussal/Git/leprojetcastor/bembuilder/data/","unitSphere_1e3.ply");
-    std::tie(Ptri,Pvtx) = triread("/Users/aussal/Git/leprojetcastor/bembuilder/data/","Oxyz_1e4.ply");
+    std::tie(Stri,Svtx) = triread("/Users/aussal/Git/leprojetcastor/fembem/demo/data/","unitSphere_1e3.ply");
+    std::tie(Ptri,Pvtx) = triread("/Users/aussal/Git/leprojetcastor/fembem/demo/data/","Oxyz_1e4.ply");
     
     // Graphical representation
     figure fig;
diff --git a/demo/head.cpp b/demo/head.cpp
index 7ba825a5a97f7288f11132d56636c2fe18488afc..fe18bfa92c4a9d18669188241fe33d1ea8616bd7 100644
--- a/demo/head.cpp
+++ b/demo/head.cpp
@@ -8,9 +8,9 @@
  |   _#_   |   AUTHOR(S)  : Matthieu Aussal                               |
  |  ( # )  |   CREATION   : 01.04.2020                                    |
  |  / 0 \  |   LAST MODIF : 31.10.2020                                    |
- | ( === ) |   SYNOPSIS   : Acoustical scattering using BEM with Neumann  |
- |  `---'  |                boundary condition                            |
- +========================================================================+
+ | ( === ) |   SYNOPSIS   : Acoustical scattering using H-Matrix BEM      |
+ |  `---'  |                solver for Neumann boundary condition         |
+ +========================================================================+=+
  */
 
 #include <castor/matrix.hpp>
@@ -18,7 +18,8 @@
 #include <castor/hmatrix.hpp>
 #include <castor/linalg.hpp>
 #include <castor/graphics.hpp>
-#include "bembuilder.hpp"
+#include "fem.hpp"
+#include "bem.hpp"
 
 using namespace castor;
 
@@ -27,7 +28,7 @@ int main (int argc, char* argv[])
     // Load meshes
     matrix<double> Svtx;
     matrix<size_t> Stri;
-    std::tie(Stri,Svtx) = triread("/Users/aussal/Git/leprojetcastor/bembuilder/data/","Head03_04kHz.ply");
+    std::tie(Stri,Svtx) = triread("/Users/aussal/Git/leprojetcastor/fembem/demo/data/","Head03_04kHz.ply");
     
     // Graphical representation
     figure fig;
@@ -35,18 +36,14 @@ int main (int argc, char* argv[])
     
     // Parameters
     matrix<double> U = {0,0,-1};
-    double f = 4000;
+    double f = 2000;
     double k = 2*M_PI*f/340;
     float tol = 1e-3;
     
-    // FEM
+    // FEM and identity matrix
     tic();
     femdata<double> v(Stri,Svtx,lagrangeP1,3);
     femdata<double> u(Stri,Svtx,lagrangeP1,3);
-    toc();
-    
-    // Identity matrix
-    tic();
     auto Id = mass<std::complex<double>>(v);
     toc();
 
diff --git a/demo/neumann.cpp b/demo/neumann.cpp
index 4d8433eca19410f71f7b9a322c074472f60f693e..b11ab936e9587b717c583a5e5e3ef4e2c90ed68e 100644
--- a/demo/neumann.cpp
+++ b/demo/neumann.cpp
@@ -8,9 +8,9 @@
  |   _#_   |   AUTHOR(S)  : Matthieu Aussal                               |
  |  ( # )  |   CREATION   : 01.04.2020                                    |
  |  / 0 \  |   LAST MODIF : 31.10.2020                                    |
- | ( === ) |   SYNOPSIS   : Acoustical scattering using BEM with Neumann  |
- |  `---'  |                boundary condition                            |
- +========================================================================+
+ | ( === ) |   SYNOPSIS   : Acoustical scattering using H-Matrix BEM      |
+ |  `---'  |                solver for Neumann boundary condition         |
+ +========================================================================+=+
  */
 
 #include <castor/matrix.hpp>
@@ -18,7 +18,8 @@
 #include <castor/hmatrix.hpp>
 #include <castor/linalg.hpp>
 #include <castor/graphics.hpp>
-#include "bembuilder.hpp"
+#include "fem.hpp"
+#include "bem.hpp"
 
 using namespace castor;
 
@@ -27,8 +28,8 @@ int main (int argc, char* argv[])
     // Load meshes
     matrix<double> Svtx, Pvtx;
     matrix<size_t> Stri, Ptri;
-    std::tie(Stri,Svtx) = triread("/Users/aussal/Git/leprojetcastor/bembuilder/data/","unitSphere_1e3.ply");
-    std::tie(Ptri,Pvtx) = triread("/Users/aussal/Git/leprojetcastor/bembuilder/data/","Oxyz_1e4.ply");
+    std::tie(Stri,Svtx) = triread("/Users/aussal/Git/leprojetcastor/fembem/demo/data/","unitSphere_1e3.ply");
+    std::tie(Ptri,Pvtx) = triread("/Users/aussal/Git/leprojetcastor/fembem/demo/data/","Oxyz_1e4.ply");
     
     // Graphical representation
     figure fig;
diff --git a/demo/neumannBW.cpp b/demo/neumannBW.cpp
index 56b52fccf65c0c11eee091cb3d3b0f81b07142d1..ac94c7774cbb95e28216e22cce4e7eb3b928fad5 100644
--- a/demo/neumannBW.cpp
+++ b/demo/neumannBW.cpp
@@ -8,9 +8,9 @@
  |   _#_   |   AUTHOR(S)  : Matthieu Aussal                               |
  |  ( # )  |   CREATION   : 01.04.2020                                    |
  |  / 0 \  |   LAST MODIF : 31.10.2020                                    |
- | ( === ) |   SYNOPSIS   : Acoustical scattering using BEM with Neumann  |
- |  `---'  |                boundary condition                            |
- +========================================================================+
+ | ( === ) |   SYNOPSIS   : Acoustical scattering using H-Matrix BEM      |
+ |  `---'  |                solver for Neumann boundary condition         |
+ +========================================================================+=+
  */
 
 #include <castor/matrix.hpp>
@@ -18,7 +18,8 @@
 #include <castor/hmatrix.hpp>
 #include <castor/linalg.hpp>
 #include <castor/graphics.hpp>
-#include "bembuilder.hpp"
+#include "fem.hpp"
+#include "bem.hpp"
 
 using namespace castor;
 
@@ -27,8 +28,8 @@ int main (int argc, char* argv[])
     // Load meshes
     matrix<double> Svtx, Pvtx;
     matrix<size_t> Stri, Ptri;
-    std::tie(Stri,Svtx) = triread("/Users/aussal/Git/leprojetcastor/bembuilder/data/","unitSphere_1e3.ply");
-    std::tie(Ptri,Pvtx) = triread("/Users/aussal/Git/leprojetcastor/bembuilder/data/","Oxyz_1e4.ply");
+    std::tie(Stri,Svtx) = triread("/Users/aussal/Git/leprojetcastor/fembem/demo/data/","unitSphere_1e3.ply");
+    std::tie(Ptri,Pvtx) = triread("/Users/aussal/Git/leprojetcastor/fembem/demo/data/","Oxyz_1e4.ply");
     
     // Graphical representation
     figure fig;
diff --git a/demo/overview_bem.cpp b/demo/overview_bem.cpp
index 1e8799249329d4189a265c01c72689cf6e206c18..3f852bc11747f6c42e160ae02004251e7c3e6963 100644
--- a/demo/overview_bem.cpp
+++ b/demo/overview_bem.cpp
@@ -3,13 +3,13 @@
  |         (c) 2020 - PROPERTY OF ECOLE POLYTECHNIQUE - LGPL 3.0          |
  |________________________________________________________________________|
  |   '&`   |                                                              |
- |    #    |   FILE       : overview_bembuilder.cpp                       |
+ |    #    |   FILE       : overview_bem.cpp                              |
  |    #    |   VERSION    : 0.1.0                                         |
  |   _#_   |   AUTHOR(S)  : Matthieu Aussal & Marc Bakry                  |
  |  ( # )  |   CREATION   : 01.04.2020                                    |
  |  / 0 \  |   LAST MODIF : 31.10.2020                                    |
- | ( === ) |   SYNOPSIS   : Boundary Element Methods with Finite elements |
- |  `---'  |                representation                                |
+ | ( === ) |   SYNOPSIS   : Boundary Elements Methods for standard        |
+ |  `---'  |                operators S, D, Dt, H in dense format         |
  +========================================================================+
  */
 
@@ -17,66 +17,13 @@
 #include <castor/smatrix.hpp>
 #include <castor/linalg.hpp>
 #include <castor/graphics.hpp>
-#include "bembuilder.hpp"
+#include "fem.hpp"
+#include "bem.hpp"
 
 using namespace castor;
 
-int main (int argc, char* argv[])
+int main(int argc, char* argv[])
 {
-    //===============================================================
-    std::cout << "+=====================+" << std::endl;
-    std::cout << "|      INITIALIZE     |" << std::endl;
-    std::cout << "+=====================+" << std::endl;
-    
-    // Documentation
-    documentationFiles =
-    {
-        "/Users/aussal/Git/leprojetcastor/matrix/include/matrix/matrix.hpp",
-        "/Users/aussal/Git/leprojetcastor/graphics/graphics.hpp"
-    };
-    help("");
-    
-    //===============================================================
-    std::cout << "+====================+" << std::endl;
-    std::cout << "|    TRIANGLEDATA    |" << std::endl;
-    std::cout << "+====================+" << std::endl;
-    
-    // Unitary triangle mesh
-    matrix<double> Tvtx = {{0,0,0},{2,0,0},{0,2,0}};
-    matrix<std::size_t> Ttri = {0,1,2};
-    matrix<double> ctredg = {{1,1,0},{0,1,0},{1,0,0}};
-    
-    // Triangle data
-    triangledata<double> triangle;
-    triangle.update(Tvtx);
-    disp(triangle.isfar(0,0,3));
-    
-    // Graphical representation
-    figure fig;
-    caxis(fig,{0,1});
-    trimesh(fig,Ttri,Tvtx);
-    quiver(fig,triangle.ctr(),triangle.nrm());
-    quiver(fig,ctredg,triangle.tau());
-    quiver(fig,ctredg,triangle.nu());
-    
-    //===============================================================
-    std::cout << "+====================+" << std::endl;
-    std::cout << "|   DOF/QUADRATURE   |" << std::endl;
-    std::cout << "+====================+" << std::endl;
-
-    // Finite element data
-    femdata<double> fem(Ttri,Tvtx,lagrangeP1,3);
-    disp(sum(fem.wgt()) - 2);
-    disp(fem.base(0,0)+fem.base(0,1)+fem.base(0,2));
-    disp(fem.grad(0,0,0)+fem.grad(0,1,0)+fem.grad(0,2,0));
-
-    // Graphical representation
-    caxis(fig,{0,1});
-    std::size_t nd = size(fem.dof(),1), ng = size(fem.gss(),1);
-    vermesh(fig,range(0,nd),fem.dof(),zeros(nd,1));
-    vermesh(fig,range(0,ng),fem.gss(),ones(ng,1));
-    quiver(fig,fem.gss(),0.2*fem.gssnrm());
-        
     //===============================================================
     std::cout << "+===================+" << std::endl;
     std::cout << "|       MESHES      |" << std::endl;
@@ -97,38 +44,9 @@ int main (int argc, char* argv[])
     std::tie(Ptri,Pvtx) = tridelaunay(X,Y,zeros(size(X)));
 
     // Graphical representation
-    figure fig2;
-    trimesh(fig2,Stri,Svtx);
-    trimesh(fig2,Ptri,Pvtx);
-
-    //===============================================================
-    std::cout << "+======================+" << std::endl;
-    std::cout << "| TRIANGLE INTEGRATION |" << std::endl;
-    std::cout << "+======================+" << std::endl;
-    
-    // Exact integration VS singular
-    X = 3*Svtx;
-    matrix<double> ref(size(X,1),2), sol(size(X,1),2);
-    double rxy, rdotn;
-    for (std::size_t l=0; l<size(X,1); ++l)
-    {
-        // Analytic
-        std::tie(ref(l,0),ref(l,1)) = triangle.intsing(X(l,0),X(l,1),X(l,2));
-        
-        // Numeric (gauss)
-        for (std::size_t g=0; g<size(fem.gss(),1); ++g)
-        {
-            rxy = std::sqrt(std::pow(X(l,0)-fem.gss(g,0),2) +
-                            std::pow(X(l,1)-fem.gss(g,1),2) +
-                            std::pow(X(l,2)-fem.gss(g,2),2) );
-            rdotn = ((X(l,0)-fem.gss(g,0))*fem.gssnrm(g,0) +
-                     (X(l,1)-fem.gss(g,1))*fem.gssnrm(g,1) +
-                     (X(l,2)-fem.gss(g,2))*fem.gssnrm(g,2) );
-            sol(l,0) += 1./rxy * fem.wgt(g);
-            sol(l,1) += rdotn/std::pow(rxy,3) * fem.wgt(g);
-        }
-    }
-    disp(norm(ref-sol,"inf")/norm(ref,"inf"));
+    figure fig;
+    trimesh(fig,Stri,Svtx);
+    trimesh(fig,Ptri,Pvtx);
         
     //===============================================================
     std::cout << "+=======================+" << std::endl;
@@ -175,9 +93,9 @@ int main (int argc, char* argv[])
     toc();
 
     // Graphical representation
-    figure fig5;
-    trimesh(fig5,Ptri,Pvtx,abs(Pdom));
-    trimesh(fig5,Stri,Svtx,abs(Pbnd));
+    figure fig2;
+    trimesh(fig2,Ptri,Pvtx,abs(Pdom));
+    trimesh(fig2,Stri,Svtx,abs(Pbnd));
 
     //===============================================================
     std::cout << "+=======================+" << std::endl;
@@ -208,9 +126,9 @@ int main (int argc, char* argv[])
     }
 
     // Graphical representation
-    figure fig6;
-    trimesh(fig6,Ptri,Pvtx,abs(Pdom));
-    trimesh(fig6,Stri,Svtx,abs(Pbnd));
+    figure fig3;
+    trimesh(fig3,Ptri,Pvtx,abs(Pdom));
+    trimesh(fig3,Stri,Svtx,abs(Pbnd));
 
     //===============================================================
     std::cout << "+========================+" << std::endl;
@@ -242,9 +160,9 @@ int main (int argc, char* argv[])
     }
 
     // Graphical representation
-    figure fig7;
-    trimesh(fig7,Stri,Svtx,abs(Pbnd));
-    trimesh(fig7,Ptri,Pvtx,abs(Pdom));
+    figure fig4;
+    trimesh(fig4,Stri,Svtx,abs(Pbnd));
+    trimesh(fig4,Ptri,Pvtx,abs(Pdom));
 
     //===============================================================
     std::cout << "+===================+" << std::endl;
@@ -276,9 +194,9 @@ int main (int argc, char* argv[])
     }
 
     // Graphical representation
-    figure fig8;
-    trimesh(fig8,Stri,Svtx,abs(Pbnd));
-    trimesh(fig8,Ptri,Pvtx,abs(Pdom));
+    figure fig5;
+    trimesh(fig5,Stri,Svtx,abs(Pbnd));
+    trimesh(fig5,Ptri,Pvtx,abs(Pdom));
     
     //===============================================================
     std::cout << "+====================+" << std::endl;
diff --git a/demo/overview_fem.cpp b/demo/overview_fem.cpp
index c9da1511db93bd4ce6a2f435abcb68b91c9e8555..160fa261057643e732aab47b7ff435653c23cf21 100644
--- a/demo/overview_fem.cpp
+++ b/demo/overview_fem.cpp
@@ -3,13 +3,13 @@
  |         (c) 2020 - PROPERTY OF ECOLE POLYTECHNIQUE - LGPL 3.0          |
  |________________________________________________________________________|
  |   '&`   |                                                              |
- |    #    |   FILE       : overview_bembuilder.cpp                       |
+ |    #    |   FILE       : overview_fem.cpp                              |
  |    #    |   VERSION    : 0.1.0                                         |
  |   _#_   |   AUTHOR(S)  : Matthieu Aussal & Marc Bakry                  |
  |  ( # )  |   CREATION   : 01.04.2020                                    |
  |  / 0 \  |   LAST MODIF : 31.10.2020                                    |
- | ( === ) |   SYNOPSIS   : Boundary Element Methods with Finite elements |
- |  `---'  |                representation                                |
+ | ( === ) |   SYNOPSIS   : Mesh, quadrature and finite elements tools    |
+ |  `---'  |                                                              |
  +========================================================================+
  */
 
@@ -18,25 +18,11 @@
 #include <castor/linalg.hpp>
 #include <castor/graphics.hpp>
 #include "fem.hpp"
-//#include "bem.hpp"
 
 using namespace castor;
 
-int main (int argc, char* argv[])
+int main(int argc, char* argv[])
 {
-    //===============================================================
-    std::cout << "+=====================+" << std::endl;
-    std::cout << "|      INITIALIZE     |" << std::endl;
-    std::cout << "+=====================+" << std::endl;
-    
-    // Documentation
-    documentationFiles =
-    {
-        "/Users/aussal/Git/leprojetcastor/matrix/include/matrix/matrix.hpp",
-        "/Users/aussal/Git/leprojetcastor/graphics/graphics.hpp"
-    };
-    help("");
-    
     //===============================================================
     std::cout << "+====================+" << std::endl;
     std::cout << "|    TRIANGLEDATA    |" << std::endl;
@@ -92,15 +78,10 @@ int main (int argc, char* argv[])
     Z = horzcat(0,reshape(Z,1,numel(Z)));
     std::tie(Stri,Svtx) = tetdelaunay(X,Y,Z);
     std::tie(Stri,Svtx) = tetboundary(Stri,Svtx);
-    
-    // Planar mesh
-    std::tie(X,Y) = meshgrid(linspace(-4,4,50));
-    std::tie(Ptri,Pvtx) = tridelaunay(X,Y,zeros(size(X)));
 
     // Graphical representation
     figure fig2;
     trimesh(fig2,Stri,Svtx);
-    trimesh(fig2,Ptri,Pvtx);
 
     //===============================================================
     std::cout << "+======================+" << std::endl;
@@ -130,181 +111,34 @@ int main (int argc, char* argv[])
         }
     }
     disp(norm(ref-sol,"inf")/norm(ref,"inf"));
-        
-    //===============================================================
-    std::cout << "+=======================+" << std::endl;
-    std::cout << "|      SINGLE LAYER     |" << std::endl;
-    std::cout << "+=======================+" << std::endl;
-    
-    // FEM
-    femdata<double> v(Stri,Svtx,lagrangeP1,3);
-    femdata<double> u(Stri,Svtx,lagrangeP1,3);
     
-    // Incident wave : PW(x,u) = exp(i*k*x.u)
-    matrix<double> U = {-1,0,0};
-    double k = 5;
-    auto Pinc = planeWave(Pvtx,U,k);
-
-    // Identity matrix
-    tic();
-    auto Id = full(mass<std::complex<double>>(v));
-    toc();
-
-    // Left hand side : \int_sigma_x \int_sigma_y v(x) G(x,y) u(y) dx dy
-    tic();
-    auto LHS = singleLayer<std::complex<double>>(v,u,k);
-    toc();
-    disp(sum(LHS)); //   -0.6614 + 2.3185i
-
-    // Right hand side : - \int_sigma_x v(x) PW(x,u) dx
-    tic();
-    auto RHS = - rightHandSide<std::complex<double>>(v,PWsource,U,k);
-    toc();
-
-    // Solve linear system : S lambda = -PO
-    tic();
-    auto lambda = linsolve(LHS,RHS);
-    toc();
-
-    // Radiation
-    tic();
-    auto Pbnd = linsolve(Id,mtimes(LHS,lambda)) + planeWave(v.dof(),U,k);
-    toc();
-    tic();
-    auto Sdom = singleLayer<std::complex<double>>(Pvtx,u,k);
-    auto Pdom = mtimes(Sdom,lambda) + Pinc;
-    toc();
-
-    // Graphical representation
-    figure fig5;
-    trimesh(fig5,Ptri,Pvtx,abs(Pdom));
-    trimesh(fig5,Stri,Svtx,abs(Pbnd));
-
-    //===============================================================
-    std::cout << "+=======================+" << std::endl;
-    std::cout << "|      DOUBLE LAYER     |" << std::endl;
-    std::cout << "+=======================+" << std::endl;
-
-    // Left hand side : \int_sigma_x \int_sigma_y v(x) dnyG(x,y) u(y) dx dy
-    tic();
-    LHS = -(0.5*Id + doubleLayer<std::complex<double>>(v,u,k));
-    toc();
-    disp(sum(LHS)); //    -1.6068 + 5.6271i
-
-    // Right hand side : - \int_sigma_x v(x) PW(x,u) dx
-    RHS = - rightHandSide<std::complex<double>>(v,PWsource,U,k);
-
-    // Solve -(Id/2 - D) = -P0
-    auto mu = linsolve(LHS,RHS);
-
-    // Boundary radiation
-    Pbnd = linsolve(Id,mtimes(LHS,mu)) + planeWave(v.dof(),U,k);
-
-    // Domain radiation
-    auto Ddom = doubleLayer<std::complex<double>>(Pvtx,u,k);
-    Pdom = - mtimes(Ddom,mu) + Pinc;
-    for (std::size_t l=0; l<size(Pvtx,1); ++l)
-    {
-        if (std::pow(Pvtx(l,0),2)+std::pow(Pvtx(l,1),2)+std::pow(Pvtx(l,2),2)<1.1) {Pdom(l)=Pinc(l);}
-    }
-
-    // Graphical representation
-    figure fig6;
-    trimesh(fig6,Ptri,Pvtx,abs(Pdom));
-    trimesh(fig6,Stri,Svtx,abs(Pbnd));
-
     //===============================================================
-    std::cout << "+========================+" << std::endl;
-    std::cout << "| DOUBLE LAYER TRANSPOSE |" << std::endl;
-    std::cout << "+========================+" << std::endl;
-
-    // Left hand side : \int_sigma_x \int_sigma_y v(x) dnyG(x,y) u(y) dx dy
+    std::cout << "+=========================+" << std::endl;
+    std::cout << "| VARIATIONAL FORMULATION |" << std::endl;
+    std::cout << "+=========================+" << std::endl;
+        
+    // Finite element space
+    fem = femdata<double>(Stri,Svtx,lagrangeP1,3);
+    
+    // Mass matrix
     tic();
-    LHS = -0.5*Id + transpose(doubleLayer<std::complex<double>>(v,u,k));
+    auto Ms = mass<double>(fem);
     toc();
-    disp(sum(LHS)); //
-
-    // Right hand side : - \int_sigma_x v(x) PW(x,u) dx
-    RHS = - rightHandSide<std::complex<double>>(v,dnPWsource,U,k);
-
-    // Solve (-Id/2 + Dt) = -Pdn0
-    lambda = linsolve(LHS,RHS);
-
-    // Boundary radiation
-    auto Sbnd = singleLayer<std::complex<double>>(v,u,k);
-    Pbnd = linsolve(Id,mtimes(Sbnd,lambda)) + planeWave(v.dof(),U,k);
-
-    // Domain radiation
-    Sdom = singleLayer<std::complex<double>>(Pvtx,u,k);
-    Pdom = mtimes(Sdom,lambda) + Pinc;
-    for (std::size_t l=0; l<size(Pvtx,1); ++l)
-    {
-        if (std::pow(Pvtx(l,0),2)+std::pow(Pvtx(l,1),2)+std::pow(Pvtx(l,2),2)<1.1) {Pdom(l)=Pinc(l);}
-    }
-
-    // Graphical representation
-    figure fig7;
-    trimesh(fig7,Stri,Svtx,abs(Pbnd));
-    trimesh(fig7,Ptri,Pvtx,abs(Pdom));
-
-    //===============================================================
-    std::cout << "+===================+" << std::endl;
-    std::cout << "|   HYPERSINGULAR   |" << std::endl;
-    std::cout << "+===================+" << std::endl;
-
-    // Left hand side : k^2 * \int_Sx \int_Sy n.psi(x) G(x,y) n.psi(y) dx dy - \int_Sx \int_Sy nxgrad(psi(x)) G(x,y) nxgrad(psi(y)) dx dy
+    disp(4*M_PI - sum(full(Ms)));
+    
+    // Rigidity matrix
     tic();
-    LHS = - hypersingular<std::complex<double>>(v,u,k);
+    auto Ks = rigidity<double>(fem);
     toc();
-    disp(sum(LHS)); //     -22.4022 -13.6573i
-
-    // Right hand side : - \int_sigma_x v(x) PW(x,u) dx
-    RHS = - rightHandSide<std::complex<double>>(v,dnPWsource,U,k);
-
-    // Solve -H = -dnP0
-    mu = linsolve(LHS,RHS);
-
-    // Boundary radiation
-    auto Dbnd = 0.5*Id + doubleLayer<std::complex<double>>(v,u,k);
-    Pbnd = -linsolve(Id,mtimes(Dbnd,mu)) + planeWave(v.dof(),U,k);
-
-    // Domain radiation
-    Ddom = doubleLayer<std::complex<double>>(Pvtx,u,k);
-    Pdom = - mtimes(Ddom,mu) + Pinc;
-    for (std::size_t l=0; l<size(Pvtx,1); ++l)
-    {
-        if (std::pow(Pvtx(l,0),2)+std::pow(Pvtx(l,1),2)+std::pow(Pvtx(l,2),2)<1.1) {Pdom(l)=Pinc(l);}
-    }
-
-    // Graphical representation
-    figure fig8;
-    trimesh(fig8,Stri,Svtx,abs(Pbnd));
-    trimesh(fig8,Ptri,Pvtx,abs(Pdom));
+    disp(max(full(Ks)));
+    disp(sum(full(Ks)));
     
-    //===============================================================
-    std::cout << "+====================+" << std::endl;
-    std::cout << "|     SUB-MATRIX     |" << std::endl;
-    std::cout << "+====================+" << std::endl;
-    
-    // Collocation
-    matrix<std::size_t> I = colon(30,2,900);
-    matrix<std::size_t> J = colon(900,-3,7);
-    matrix<double> S  = singleLayer<double>(Svtx,u,0.);
-    matrix<double> Sb = singleLayer<double>(Svtx,u,0.,I,J);
-    disp(norm(eval(S(I,J))-Sb,"inf"));
-    
-    // Galerkin
-    S  = singleLayer<double>(v,u,0.);
-    Sb = singleLayer<double>(v,u,0.,I,J);
-    disp(norm(eval(S(I,J))-Sb,"inf"));
-    
-    //===============================================================
-    std::cout << "+===================+" << std::endl;
-    std::cout << "|      DRAWNOW      |" << std::endl;
-    std::cout << "+===================+" << std::endl;
+    // Graphical representation
+    figure fig3;
+    spy(fig3,Ms,"b","Mass matrix");
     
-    drawnow(fig);    
     
+    drawnow(fig);
     disp("done !");
     return 0;
 }
diff --git a/include/bem.hpp b/include/bem.hpp
index 8802bde1911995e7c76cb160162bfcd82781da73..1a4a70dc576671f0a0e9e0c6b87028666892ac4c 100644
--- a/include/bem.hpp
+++ b/include/bem.hpp
@@ -3,721 +3,35 @@
  |         (c) 2020 - PROPERTY OF ECOLE POLYTECHNIQUE - LGPL 3.0          |
  |________________________________________________________________________|
  |   '&`   |                                                              |
- |    #    |   FILE       : bembuilder.hpp                                |
+ |    #    |   FILE       : bem.hpp                                       |
  |    #    |   VERSION    : 0.1.0                                         |
  |   _#_   |   AUTHOR(S)  : Matthieu Aussal & Marc Bakry                  |
  |  ( # )  |   CREATION   : 01.04.2020                                    |
  |  / 0 \  |   LAST MODIF : 31.10.2020                                    |
- | ( === ) |   SYNOPSIS   : Boundary Element Methods with Finite elements |
- |  `---'  |                representation                                |
+ | ( === ) |   SYNOPSIS   : Boundary Elements Methods for standard        |
+ |  `---'  |                operators S, D, Dt, H in dense format         |
  +========================================================================+
  */
 
 #pragma once
+
+#define CASTOR_BEM_HPP
+
 #include <castor/matrix.hpp>
 #include <castor/smatrix.hpp>
+#include "fem.hpp"
 
-using namespace castor;
+namespace castor
+{
 
 //==========================================================================//
 //                           ENVIRONMENT DATA                               //
 //==========================================================================//
-enum femoperator{none, gradient};
-enum femtype{vertices, lagrangeP0, lagrangeP1};
 enum bemsource{PWsource, dnPWsource, SWsource, dnSWsource};
 
 
 //==========================================================================//
-//                        TRIANGLEDATA CLASS                                //
-//==========================================================================//
-//==========================================================================
-// [triangledata]
-///
-template<typename T>
-class triangledata
-{
-public:
-    auto intsing(T x0, T y0, T z0) const;
-    bool isfar(T x0, T y0, T z0) const;
-    void projectP1(T x0, T y0, T z0, matrix<T>& P) const;
-    void update(matrix<T>const& S);
-    void update(matrix<T>const& A, matrix<T>const& B, matrix<T>const& C);
-    
-    matrix<T>const& ctr() const {return m_ctr;};
-    matrix<T>const& nrm() const {return m_nrm;};
-    matrix<T>const& nu()  const {return m_nu;};
-    matrix<T>const& tau() const {return m_tau;};
-    T const& A(std::size_t i) const   {return m_A(i);};
-    T const& B(std::size_t i) const   {return m_B(i);};
-    T const& C(std::size_t i) const   {return m_C(i);};
-    T const& ctr(std::size_t i) const {return m_ctr(i);};
-    T const& hgt(std::size_t i) const {return m_hgt(i);};
-    T const& nrm(std::size_t i) const {return m_nrm(i);};
-    T const& nu(std::size_t i, std::size_t j) const {return m_nu(i,j);};
-    T const& srf() const              {return m_srf;}
-    T const& tau(std::size_t i, std::size_t j) const {return m_tau(i,j);};
-    
-private:
-    matrix<T> m_A   = matrix<T>(1,3); // FIRST NODE
-    matrix<T> m_B   = matrix<T>(1,3); // SECOND NODE
-    matrix<T> m_C   = matrix<T>(1,3); // THIRD NODE
-    matrix<T> m_ctr = matrix<T>(1,3); // CENTER
-    matrix<T> m_hgt = matrix<T>(1,3); // HEIGHT
-    matrix<T> m_lgt = matrix<T>(1,3); // EDGE LENGTH
-    matrix<T> m_nrm = matrix<T>(1,3); // TRIANGLE NORMAL
-    matrix<T> m_nu  = matrix<T>(3,3); // EDGE NORMALS
-    T         m_srf = 0;              // SURFACE
-    T         m_stp = 0;              // MAX EDGE LENGTH
-    matrix<T> m_tau = matrix<T>(3,3); // EDGE TANGENT
-};
-
-//==========================================================================
-//
-///
-template<typename T>
-auto triangledata<T>::intsing(T x0, T y0, T z0) const
-{
-    // initialize
-    std::size_t ip, ipp;
-    static matrix<T> xX0S(1,3), yX0S(1,3), zX0S(1,3);
-    static matrix<T> nrmX0S(1,3), ca(1,3), sa(1,3), xnu(1,3);
-    T omega, h, ps, psp1, ps2, ah, ar, intaRm1;
-    T Rm1, dnRm1;
-    T tol = 1e-7;
-    
-    // Point to vertex
-    xX0S(0) = m_A(0) - x0;
-    xX0S(1) = m_B(0) - x0;
-    xX0S(2) = m_C(0) - x0;
-    
-    yX0S(0) = m_A(1) - y0;
-    yX0S(1) = m_B(1) - y0;
-    yX0S(2) = m_C(1) - y0;
-    
-    zX0S(0) = m_A(2) - z0;
-    zX0S(1) = m_B(2) - z0;
-    zX0S(2) = m_C(2) - z0;
-
-    for(std::size_t i=0; i<3; ++i)
-    {
-        nrmX0S(i) = std::sqrt(xX0S(i)*xX0S(i)+yX0S(i)*yX0S(i)+zX0S(i)*zX0S(i));
-    }
-    
-    // Point height to triangle
-    h = xX0S(0)*m_nrm(0) + yX0S(0)*m_nrm(1) + zX0S(0)*m_nrm(2);
-    
-    // solid angle
-    if (std::abs(h)<tol) {omega = 0;}
-    else
-    {
-        for(std::size_t i=0; i<3; ++i)
-        {
-            ip = (i+1)%3;
-            ca(i) = (xX0S(i)*xX0S(ip)+yX0S(i)*yX0S(ip)+zX0S(i)*zX0S(ip))/(nrmX0S(i)*nrmX0S(ip));
-            sa(i) = std::sqrt(1 - ca(i)*ca(i));
-        }
-        omega = -M_PI;
-        for(std::size_t i=0; i<3; ++i)
-        {
-            ip = (i+1)%3; ipp = (ip+1)%3;
-            omega += std::acos((ca(i)-ca(ip)*ca(ipp))/(sa(ip)*sa(ipp)));
-        }
-        if (h < 0) {omega = -omega;}
-    }
-    if (std::isnan(omega))
-    {
-        error(__FILE__, __LINE__, __FUNCTION__,"Point is close to triangle, change h tolerance.");
-    }
-    
-    // initialization of integration
-    Rm1 = -h*omega;
-    dnRm1 = -omega;
-    
-    // edge integration
-    for(std::size_t i=0; i<3; ++i)
-    {
-        ip  = (i+1)%3;
-        ipp = (ip+1)%3;
-        // projections
-        ps   = xX0S(ip)*m_tau(i,0)  + yX0S(ip)*m_tau(i,1)  + zX0S(ip)*m_tau(i,2);
-        psp1 = xX0S(ipp)*m_tau(i,0) + yX0S(ipp)*m_tau(i,1) + zX0S(ipp)*m_tau(i,2);
-        ps2  = ps*(nrmX0S(ipp) - nrmX0S(ip));
-        // Subtil
-        ar = std::abs(psp1 - ps);
-        xnu(0) = xX0S(ip) - ps*m_tau(i,0);
-        xnu(1) = yX0S(ip) - ps*m_tau(i,1);
-        xnu(2) = zX0S(ip) - ps*m_tau(i,2);
-        ah = std::sqrt(xnu(0)*xnu(0) + xnu(1)*xnu(1) + xnu(2)*xnu(2));
-        // integration of 1/R on the edge - singular case
-        if( nrmX0S(ip)-std::abs(ps) < tol)
-        {
-            intaRm1 = 0;
-            // Particle on the left of the edge
-            if (ps > tol && psp1 > tol) {intaRm1 = std::log(nrmX0S(ipp)/nrmX0S(ip));}
-            // Particle on the right of the edge
-            else if (ps < -tol && psp1 < -tol) {intaRm1 = -std::log(nrmX0S(ipp)/nrmX0S(ip));}
-        }
-        // integration of 1/R on the edge - general case h>tol
-        else
-        {
-            intaRm1 = std::asinh(psp1/ah) - std::asinh(ps/ah);
-        }
-        // 1/r
-        Rm1 += intaRm1*(xX0S(ip)*m_nu(i,0) + yX0S(ip)*m_nu(i,1) + zX0S(ip)*m_nu(i,2));
-        // grad[1/r]
-        dnRm1 += intaRm1*(m_nu(i,0)*m_nrm(0) + m_nu(i,1)*m_nrm(1) + m_nu(i,2)*m_nrm(2));
-    }
-    return std::make_tuple(Rm1, dnRm1);
-}
-
-//==========================================================================
-//
-///
-template<typename T>
-bool triangledata<T>::isfar(T x0, T y0, T z0) const
-{
-    return ((x0-m_ctr(0))*(x0-m_ctr(0)) +
-            (y0-m_ctr(1))*(y0-m_ctr(1)) +
-            (z0-m_ctr(2))*(z0-m_ctr(2)) ) > 1.1*m_stp;
-}
-
-//==========================================================================
-//
-///
-template<typename T>
-void triangledata<T>::projectP1(T x0, T y0, T z0, matrix<T>& P) const
-{
-    // ((Sk-X) dot NUj) / Hj
-    P(0) = ((m_B(0)-x0)*m_nu(0,0) +
-            (m_B(1)-y0)*m_nu(0,1) +
-            (m_B(2)-z0)*m_nu(0,2) ) / m_hgt(0);
-    P(1) = ((m_C(0)-x0)*m_nu(1,0) +
-            (m_C(1)-y0)*m_nu(1,1) +
-            (m_C(2)-z0)*m_nu(1,2) ) / m_hgt(0);
-    P(2) = ((m_A(0)-x0)*m_nu(2,0) +
-            (m_A(1)-y0)*m_nu(2,1) +
-            (m_A(2)-z0)*m_nu(2,2) ) / m_hgt(2);
-}
-
-//==========================================================================
-//
-///
-template<typename T>
-inline void triangledata<T>::update(matrix<T>const& S)
-{
-    update({S(0,0),S(0,1),S(0,2)},{S(1,0),S(1,1),S(1,2)},{S(2,0),S(2,1),S(2,2)});
-}
-
-//==========================================================================
-//
-///
-template<typename T>
-inline void triangledata<T>::update(matrix<T>const& A, matrix<T>const& B, matrix<T>const& C)
-{
-    // Nodes and center
-    for(std::size_t j=0; j<3; ++j)
-    {
-        m_A(j)   = A(j);
-        m_B(j)   = B(j);
-        m_C(j)   = C(j);
-        m_ctr(j) = (m_A(j)+m_B(j)+m_C(j))/3.;
-    }
-    
-    // Edges
-    matrix<T> AB = B-A;
-    matrix<T> BC = C-B;
-    matrix<T> CA = A-C;
-    
-    // Normal to ABC and surface
-    m_nrm(0) = AB(1)*BC(2) - AB(2)*BC(1);
-    m_nrm(1) = AB(2)*BC(0) - AB(0)*BC(2);
-    m_nrm(2) = AB(0)*BC(1) - AB(1)*BC(0);
-    m_srf    = 0.5*std::sqrt(m_nrm(0)*m_nrm(0) + m_nrm(1)*m_nrm(1) + m_nrm(2)*m_nrm(2));
-    m_nrm   /= 2*m_srf;
-    
-    // length edges
-    m_lgt(2) = std::sqrt(AB(0)*AB(0)+AB(1)*AB(1)+AB(2)*AB(2));
-    m_lgt(0) = std::sqrt(BC(0)*BC(0)+BC(1)*BC(1)+BC(2)*BC(2));
-    m_lgt(1) = std::sqrt(CA(0)*CA(0)+CA(1)*CA(1)+CA(2)*CA(2));
-    m_stp    = std::max(std::max(m_lgt(0),m_lgt(1)),m_lgt(2));
-    
-    // tangent to edges
-    for(std::size_t j=0; j<3; ++j)
-    {
-        m_tau(2,j) = AB(j)/m_lgt(2);
-        m_tau(0,j) = BC(j)/m_lgt(0);
-        m_tau(1,j) = CA(j)/m_lgt(1);
-    }
-    
-    // normal to edges
-    for(std::size_t i=0; i<3; ++i)
-    {
-        m_nu(i,0) = m_tau(i,1)*m_nrm(2)-m_tau(i,2)*m_nrm(1);
-        m_nu(i,1) = m_tau(i,2)*m_nrm(0)-m_tau(i,0)*m_nrm(2);
-        m_nu(i,2) = m_tau(i,0)*m_nrm(1)-m_tau(i,1)*m_nrm(0);
-    }
-    
-    // Height
-    m_hgt(0) = AB(0)*m_nu(0,0) + AB(1)*m_nu(0,1) + AB(2)*m_nu(0,2);
-    m_hgt(1) = BC(0)*m_nu(1,0) + BC(1)*m_nu(1,1) + BC(2)*m_nu(1,2);
-    m_hgt(2) = CA(0)*m_nu(2,0) + CA(1)*m_nu(2,1) + CA(2)*m_nu(2,2);
-};
-
-
-//==========================================================================//
-//                           FEMDATA CLASS                                  //
-//==========================================================================//
-//==========================================================================
-// [femdata]
-///
-template<typename T>
-class femdata
-{
-public:
-    femdata(matrix<std::size_t>const& elt, matrix<T>const& vtx, femtype fet, std::size_t ngs=0);
-    auto dofsubdata(matrix<std::size_t>const& Idof) const;
-    auto refQuadrature() const;
-    
-    matrix<T>const& dof() const    {return m_dof;};
-    matrix<T>const& gss() const    {return m_gss;};
-    matrix<T>const& gssnrm() const {return m_gssnrm;};
-    matrix<T>const& wgt() const    {return m_wgt;};
-    
-    matrix<std::size_t>const& elt2dof() const {return m_elt2dof;};
-    matrix<std::size_t>const& elt2gss() const {return m_elt2gss;};
-    
-    T const& base(std::size_t i, std::size_t j) const {return m_base(i,j);};
-    T const& dof(std::size_t i, std::size_t j) const {return m_dof(i,j);};
-    T const& grad(std::size_t i, std::size_t j, std::size_t k) const {return m_grad[k](i,j);};
-    T const& gss(std::size_t i, std::size_t j) const {return m_gss(i,j);};
-    T const& gssnrm(std::size_t i, std::size_t j) const {return m_gssnrm(i,j);};
-    T const& nxgrad(std::size_t i, std::size_t j, std::size_t k) const {return m_nxgrad[k](i,j);};
-    T const& wgt(std::size_t i) const {return m_wgt(i);};
-    
-    std::size_t const& elt2dof(std::size_t i, std::size_t j) const {return m_elt2dof(i,j);};
-    std::size_t const& elt2gss(std::size_t i, std::size_t j) const {return m_elt2gss(i,j);};
-    
-    triangledata<T>const& tridata(std::size_t i) const {return m_tridata[i];};
-    
-private:
-    // PRIVATE BUILDER
-    void m_buildBase();
-    void m_buildDof();
-    void m_buildGeometry();
-    void m_buildGrad();
-    void m_buildQuadrature();
-    
-    // ATTRIBUTS
-    femtype                          m_fet;       // ENUM       : TYPE OF FINITE ELEMENT
-    std::size_t                      m_ngs;       // DIMENSION  : SIZE OF ELEMENTARY QUADRATURE
-    matrix<T>                        m_dof;       // COORDINATES: DEGREES OF FREEDOM
-    matrix<T>                        m_gss;       // COORDINATES: GAUSSIAN QUADRATURE
-    matrix<T>                        m_gssnrm;    // COORDINATES: NORMALS AT GAUSS POINTS
-    matrix<T>                        m_vtx;       // COORDINATES: NODES
-    matrix<T>                        m_wgt;       // VALUES     : GAUSSIAN WEIGHTS
-    std::vector<matrix<std::size_t>> m_dof2elt;   // TABLE      : DOF TO ELEMENT
-    matrix<std::size_t>              m_elt2dof;   // TABLE      : ELEMENTS TO DOF
-    matrix<std::size_t>              m_elt2gss;   // TABLE      : ELEMENTS TO GAUSSIAN QUADRATURE
-    matrix<std::size_t>              m_elt2vtx;   // TABLE      : ELEMENTS TO NODES
-    matrix<T>                        m_base;      // OPERATOR   : BASIS
-    std::vector<matrix<T>>           m_grad;      // OPERATOR   : GRADIENT
-    std::vector<matrix<T>>           m_nxgrad;    // OPERATOR   : NORMALxGRADIENT
-    std::vector<triangledata<T>>     m_tridata;   // GEOMETRY   : TRIANGLE DATA
-};
-
-//==========================================================================
-//
-///
-template<typename T>
-femdata<T>::femdata(matrix<std::size_t>const& elt, matrix<T>const& vtx, femtype fet, std::size_t ngs)
-{
-    // Check input
-    if (size(vtx,2)!=3 || size(elt,2)!=3)
-    {
-        error(__FILE__, __LINE__, __FUNCTION__,"Vertices should be a Nvtx-by-3 real matrix and elements a Nelt-by-3 integer matrix.");
-    }
-    
-    // Input data
-    m_vtx     = vtx;
-    m_elt2vtx = elt;
-    m_ngs     = ngs;
-    m_fet     = fet;
-    
-    // Computed data
-    m_buildGeometry();
-    m_buildQuadrature();
-    m_buildDof();
-    m_buildBase();
-    m_buildGrad();
-}
-
-//==========================================================================
-//
-///
-template<typename T>
-auto femdata<T>::dofsubdata(matrix<std::size_t>const& Idof) const
-{
-    // Elements associated to dof indices
-    std::vector<std::size_t> v;
-    for (std::size_t l=0; l<numel(Idof); ++l)
-    {
-        v.insert(v.end(),std::begin(m_dof2elt[Idof(l)].val()),std::end(m_dof2elt[Idof(l)].val()));
-    }
-    matrix<std::size_t> Ielt = unique<std::size_t>(v);
-    
-    // Local renumerotation of dof indices
-    matrix<std::size_t> e2d = eval(m_elt2dof(Ielt,col(m_elt2dof)));
-    matrix<std::size_t> i1=argsort(Idof), i2=argsort(e2d);
-    matrix<long> e2dl(size(e2d,1),size(e2d,2),-1);
-    std::size_t k=0, l=0;
-    while (l<numel(e2d))
-    {
-        if (e2d(i2(l))<Idof(i1(k))) {++l;}
-        else if (e2d(i2(l))==Idof(i1(k)))
-        {
-            e2dl(i2(l)) = i1(k);
-            ++l;
-        }
-        else if (e2d(i2(l))>Idof(i1(k)))
-        {
-            ++k;
-            if (k==numel(Idof)){break;}
-        }
-    }
-    return std::make_tuple(Ielt,e2dl);
-}
-
-//==========================================================================
-//
-///
-template<typename T>
-auto femdata<T>::refQuadrature() const
-{
-    matrix<T> gssloc(m_ngs,2), wgtloc(m_ngs,1);
-    double a, b, w;
-    switch(m_ngs)
-    {
-        case 0:
-            break;
-            
-        case 1:
-            gssloc(0,0) = 1/3.; gssloc(0,1) = 1/3.; wgtloc(0) = 0.5;
-            break;
-            
-        case 3:
-            a = 2/3.; b = 1/6.; w = 1/6.;
-            wgtloc(0) = w; wgtloc(1) = w; wgtloc(2) = w;
-            gssloc(0,0) = a; gssloc(0,1) = b;
-            gssloc(1,0) = b; gssloc(1,1) = a;
-            gssloc(2,0) = b; gssloc(2,1) = b;
-            break;
-            
-        case 6:
-            a = 0.816847572980459; b = 0.091576213509771; w = 0.109951743655322/2.;
-            wgtloc(0) = w; wgtloc(1) = w; wgtloc(2) = w;
-            gssloc(0,0) = a; gssloc(0,1) = b;
-            gssloc(1,0) = b; gssloc(1,1) = a;
-            gssloc(2,0) = b; gssloc(2,1) = b;
-            a = 0.108103018168070; b = 0.445948490915965; w = 0.223381589678011/2.;
-            wgtloc(3) = w; wgtloc(4) = w; wgtloc(5) = w;
-            gssloc(3,0) = a; gssloc(3,1) = b;
-            gssloc(4,0) = b; gssloc(4,1) = a;
-            gssloc(5,0) = b; gssloc(5,1) = b;
-            break;
-            
-        default:
-            error(__FILE__, __LINE__, __FUNCTION__,"Unvalid quadrature size (available are 0, 1, 3 and 6).");
-            break;
-    }
-    return std::make_tuple(gssloc,wgtloc);
-}
-
-//==========================================================================
-//
-///
-template<typename T>
-void femdata<T>::m_buildBase()
-{
-    // Initialize
-    matrix<T> gssloc,wgtloc;
-    std::vector<matrix<T>> fct(size(m_elt2dof,2),matrix<T>(1,m_ngs));
-    m_base = zeros<T>(size(m_gss,1),size(m_elt2dof,2));
-    
-    // Reference quadrature
-    std::tie(gssloc,wgtloc) = refQuadrature();
-    
-    // Basis functions
-    switch(m_fet)
-    {
-        case vertices :
-            break;
-            
-        case lagrangeP0:
-            fct[0] = ones<T>(1,m_ngs);
-            break;
-            
-        case lagrangeP1:
-            for(std::size_t g=0; g<m_ngs; ++g)
-            {
-                fct[0](g) = 1 - gssloc(g,0) - gssloc(g,1);
-                fct[1](g) = gssloc(g,0);
-                fct[2](g) = gssloc(g,1);
-            }
-            break;
-            
-        default:
-            error(__FILE__, __LINE__, __FUNCTION__,"Invalid basis function type");
-            break;
-    }
-    
-    // Assembling
-    for (std::size_t e=0; e<size(m_elt2vtx,1); ++e)
-    {
-        for (std::size_t d=0; d<size(m_elt2dof,2); ++d)
-        {
-            for (std::size_t g=0; g<m_ngs; ++g)
-            {
-                m_base(m_elt2gss(e,g),d) = fct[d](g);
-            }
-        }
-    }
-}
-
-//==========================================================================
-//
-///
-template<typename T>
-void femdata<T>::m_buildDof()
-{
-    // Dof, elt2dof
-    switch(m_fet)
-    {
-        case vertices :
-            break;
-            
-        case lagrangeP0 :
-            m_dof = zeros<T>(size(m_elt2vtx,1),size(m_vtx,2));
-            for (std::size_t i=0; i<size(m_elt2vtx,1); ++i)
-            {
-                for (std::size_t j=0; j<size(m_elt2vtx,2); ++j)
-                {
-                    m_dof(i,0) += m_vtx(m_elt2vtx(i,j),0);
-                    m_dof(i,1) += m_vtx(m_elt2vtx(i,j),1);
-                    m_dof(i,2) += m_vtx(m_elt2vtx(i,j),2);
-                }
-            }
-            m_dof /= size(m_elt2vtx,2);
-            m_elt2dof = transpose(range(0,size(m_elt2vtx,1)));
-            break;
-            
-        case lagrangeP1 :
-            m_dof     = m_vtx;
-            m_elt2dof = m_elt2vtx;
-            break;
-            
-        default:
-            error(__FILE__, __LINE__, __FUNCTION__,"Invalid basis function type");
-            break;
-    }
-    
-    // Number of elements close to dof
-    matrix<std::size_t> ind(1,size(m_dof,1));
-    for (std::size_t l=0; l<numel(m_elt2dof); ++l)
-    {
-        ++ind(m_elt2dof(l));
-    }
-    
-    // Initialize tables
-    m_dof2elt.resize(size(m_dof,1));
-    for (std::size_t d=0; d<size(m_dof,1); ++d)
-    {
-        m_dof2elt[d].resize(1,ind(d));
-        ind(d)=0;
-    }
-    
-    // Fill tables
-    std::size_t d;
-    for (std::size_t e=0; e<size(m_elt2dof,1); ++e)
-    {
-        for (std::size_t j=0; j<size(m_elt2dof,2); ++j)
-        {
-            d = m_elt2dof(e,j);
-            m_dof2elt[d](ind(d)) = e;
-            ++ind(d);
-        }
-    }
-    
-    // Check error
-    if (sum(ind)!=numel(m_elt2dof))
-    {
-        error(__FILE__, __LINE__, __FUNCTION__,"Unavailable case.");
-    }
-}
-
-//==========================================================================
-//
-///
-template<typename T>
-void femdata<T>::m_buildGrad()
-{
-    // Initialize
-    matrix<T> gssloc,wgtloc;
-    std::vector<matrix<T>> fctX(size(m_elt2dof,2),zeros<T>(1,m_ngs));
-    std::vector<matrix<T>> fctY(size(m_elt2dof,2),zeros<T>(1,m_ngs));
-    m_grad   = std::vector<matrix<T>>(3, matrix<T>(size(m_gss,1),size(m_elt2dof,2)));
-    m_nxgrad = std::vector<matrix<T>>(3, matrix<T>(size(m_gss,1),size(m_elt2dof,2)));
-    matrix<T> AB(1,3), AC(1,3), G(2,2), Gm1(2,2), P(2,3);
-    T detG;
-    
-    // Reference quadrature
-    std::tie(gssloc,wgtloc) = refQuadrature();
-    
-    // Basis functions
-    switch (m_fet)
-    {
-        case vertices :
-            break;
-            
-        case lagrangeP0:
-            break;
-            
-        case lagrangeP1:
-            for(std::size_t g=0; g<m_ngs; ++g)
-            {
-                fctX[0](g) = -1; fctY[0](g) = -1;
-                fctX[1](g) = 1;  fctY[1](g) = 0;
-                fctX[2](g) = 0;  fctY[2](g) = 1;
-            }
-            break;
-            
-        default:
-            error(__FILE__, __LINE__, __FUNCTION__,"Invalid basis function type");
-            break;
-    }
-    
-    // Build grad operator
-    for(std::size_t e=0; e<size(m_elt2vtx,1); ++e)
-    {
-        // For each coordinate
-        G = zeros<T>(2,2);
-        for(std::size_t j=0; j<3; ++j)
-        {
-            // Vector basis
-            AB(j) = m_vtx(m_elt2vtx(e,1),j) - m_vtx(m_elt2vtx(e,0),j);
-            AC(j) = m_vtx(m_elt2vtx(e,2),j) - m_vtx(m_elt2vtx(e,0),j);
-            
-            // Gramm matrix (symetric)
-            G(0,0) += AB(j)*AB(j);
-            G(0,1) += AB(j)*AC(j);
-            G(1,1) += AC(j)*AC(j);
-        }
-        G(1,0) = G(0,1);
-        
-        // inverse of Gramm matrix
-        detG     = G(0,0)*G(1,1) - G(1,0)*G(0,1);
-        Gm1(0,0) = G(1,1)/detG;  Gm1(0,1) = -G(0,1)/detG;
-        Gm1(1,0) = -G(1,0)/detG; Gm1(1,1) = G(0,0)/detG;
-        
-        // P = (G^{-1})^t {AB;AC}
-        for(std::size_t j=0; j<3; ++j)
-        {
-            P(0,j) = Gm1(0,0)*AB(j) + Gm1(1,0)*AC(j);
-            P(1,j) = Gm1(0,1)*AB(j) + Gm1(1,1)*AC(j);
-        }
-        
-        // grad(PHI) = P * grad(PHI_ref)
-        for(std::size_t j=0; j<3; ++j)
-        {
-            for(std::size_t d=0; d<size(m_elt2dof,2); ++d)
-            {
-                for(std::size_t g=0; g<m_ngs; ++g)
-                {
-                    m_grad[j](m_elt2gss(e,g),d) = P(0,j)*fctX[d](g) + P(1,j)*fctY[d](g);
-                }
-            }
-        }
-    }
-    
-    // Build nxgrad operator
-    for(std::size_t k=0; k<m_grad.size(); ++k)
-    {
-        for(std::size_t i=0; i<size(m_grad[k],1); ++i)
-        {
-            for(std::size_t j=0; j<size(m_grad[k],2); ++j)
-            {
-                m_nxgrad[k](i,j) = (m_gssnrm(i,(k+1)%3)*m_grad[(k+2)%3](i,j) -
-                                    m_gssnrm(i,(k+2)%3)*m_grad[(k+1)%3](i,j) );
-            }
-        }
-    }
-}
-
-//==========================================================================
-//
-///
-template<typename T>
-void femdata<T>::m_buildGeometry()
-{
-    m_tridata = std::vector<triangledata<T>>(size(m_elt2vtx,1),triangledata<T>());
-    matrix<T> A(1,3), B(1,3), C(1,3);
-    for (std::size_t e=0; e<size(m_elt2vtx,1); ++e)
-    {
-        for (std::size_t j=0; j<3; ++j)
-        {
-            A(j) = m_vtx(m_elt2vtx(e,0),j);
-            B(j) = m_vtx(m_elt2vtx(e,1),j);
-            C(j) = m_vtx(m_elt2vtx(e,2),j);
-        }
-        m_tridata[e].update(A,B,C);
-    }
-}
-
-//==========================================================================
-//
-///
-template<typename T>
-void femdata<T>::m_buildQuadrature()
-{
-    // Initialize data
-    std::size_t ne = size(m_elt2vtx,1);
-    m_gss          = zeros<T>(m_ngs*ne,3);
-    m_wgt          = zeros<T>(m_ngs*ne,1);
-    m_gssnrm       = zeros<T>(m_ngs*ne,3);
-    m_elt2gss      = zeros<std::size_t>(ne,m_ngs);
-    matrix<T> AB(1,3), AC(1,3), gssloc, wgtloc;
-    std::size_t ind;
-    
-    // Quadrature on reference element {(0,0), (1,0), (0,1)}
-    std::tie(gssloc,wgtloc) = refQuadrature();
-    
-    // For each element
-    for(std::size_t e=0; e<ne; ++e)
-    {
-        // Basis vectors (AB and AC)
-        for(std::size_t j=0; j<3; ++j)
-        {
-            AB(j) = m_tridata[e].B(j) - m_tridata[e].A(j);
-            AC(j) = m_tridata[e].C(j) - m_tridata[e].A(j);
-        }
-        
-        // Local gauss nodes: XG = A + [B-A C-A]*XGloc, weights and normals
-        for(std::size_t g=0; g<m_ngs; ++g)
-        {
-            ind = e*m_ngs + g;
-            m_elt2gss(e,g) = ind;
-            m_wgt(ind)     = 2*m_tridata[e].srf()*wgtloc(g);
-            for(std::size_t j=0; j<3; ++j)
-            {
-                m_gss(ind,j)    = m_tridata[e].A(j) + AB(j)*gssloc(g,0) + AC(j)*gssloc(g,1);
-                m_gssnrm(ind,j) = m_tridata[e].nrm(j);
-            }
-        }
-    }
-}
-
-
-//==========================================================================//
-//                           INTEGRAL EQUATION                              //
+//                              EXCITATIONS                                 //
 //==========================================================================//
 //==========================================================================
 //
@@ -793,33 +107,10 @@ matrix<T> rightHandSide(femdata<S>const& v, bemsource source, matrix<S>const& Y,
     return B;
 }
 
-//==========================================================================
-//
-///
-template<typename T, typename S>
-smatrix<T> mass(femdata<S>const& v)
-{
-    std::vector<std::size_t> ind;
-    std::vector<T> val;
-    std::size_t ndof = size(v.dof(),1);
-    for (std::size_t e=0; e<size(v.elt2dof(),1); ++e)
-    {
-        for (std::size_t g=0; g<size(v.elt2gss(),2); ++g)
-        {
-            std::size_t ig = v.elt2gss(e,g);
-            for (std::size_t i=0; i<size(v.elt2dof(),2); ++i)
-            {
-                for (std::size_t j=0; j<size(v.elt2dof(),2); ++j)
-                {
-                    ind.push_back(v.elt2dof(e,i)*ndof+v.elt2dof(e,j));
-                    val.push_back(v.base(ig,i) * v.wgt(ig) * v.base(ig,j));
-                }
-            }
-        }
-    }
-    return smatrix<T>(ndof,ndof,ind,val);
-}
 
+//==========================================================================//
+//                          INTEGRAL OPERATORS                              //
+//==========================================================================//
 //==========================================================================
 //
 ///
@@ -1537,3 +828,5 @@ matrix<T> hypersingular(femdata<S>const& v, femdata<S>const& u, S wavenumber=0,
     M /= (T)(4*M_PI);
     return M;
 }
+
+}
diff --git a/include/fem.hpp b/include/fem.hpp
index 8802bde1911995e7c76cb160162bfcd82781da73..3cf1cbcdc16de910a2582d2c657ea358065fc294 100644
--- a/include/fem.hpp
+++ b/include/fem.hpp
@@ -3,28 +3,30 @@
  |         (c) 2020 - PROPERTY OF ECOLE POLYTECHNIQUE - LGPL 3.0          |
  |________________________________________________________________________|
  |   '&`   |                                                              |
- |    #    |   FILE       : bembuilder.hpp                                |
+ |    #    |   FILE       : fem.hpp                                       |
  |    #    |   VERSION    : 0.1.0                                         |
  |   _#_   |   AUTHOR(S)  : Matthieu Aussal & Marc Bakry                  |
  |  ( # )  |   CREATION   : 01.04.2020                                    |
  |  / 0 \  |   LAST MODIF : 31.10.2020                                    |
- | ( === ) |   SYNOPSIS   : Boundary Element Methods with Finite elements |
- |  `---'  |                representation                                |
+ | ( === ) |   SYNOPSIS   : Quadrature and Finite Element basic tools     |
+ |  `---'  |                                                              |
  +========================================================================+
  */
 
 #pragma once
+
+#define CASTOR_FEM_HPP
+
 #include <castor/matrix.hpp>
 #include <castor/smatrix.hpp>
 
-using namespace castor;
+namespace castor
+{
 
 //==========================================================================//
 //                           ENVIRONMENT DATA                               //
 //==========================================================================//
-enum femoperator{none, gradient};
 enum femtype{vertices, lagrangeP0, lagrangeP1};
-enum bemsource{PWsource, dnPWsource, SWsource, dnSWsource};
 
 
 //==========================================================================//
@@ -717,82 +719,8 @@ void femdata<T>::m_buildQuadrature()
 
 
 //==========================================================================//
-//                           INTEGRAL EQUATION                              //
+//                        VARIATIONAL FORMULATIONS                          //
 //==========================================================================//
-//==========================================================================
-//
-///
-template<typename T>
-matrix<std::complex<T>> planeWave(matrix<T>const& X, matrix<T>const& U, T wavenumber)
-{
-    if (size(X,2)!=size(U,2))
-    {
-        error(__FILE__, __LINE__, __FUNCTION__,"Vertices X and direction U should have same number of columns.");
-    }
-    if (norm(U,"2") != 1.)
-    {
-        warning(__FILE__, __LINE__, __FUNCTION__,"Plane wave direction is not normalized.");
-    }
-    std::complex<T> imk(0,wavenumber);
-    return exp(imk*mtimes(X,transpose(U)));
-}
-
-//==========================================================================
-//
-///
-template<typename T>
-matrix<std::complex<T>> dnplaneWave(matrix<T>const& X, matrix<T>const& N, matrix<T>const& U, T wavenumber)
-{
-    if (size(X,2)!=size(U,2) || size(N,2)!=size(U,2))
-    {
-        error(__FILE__, __LINE__, __FUNCTION__,"Vertices X, normals N and direction U should have same number of columns.");
-    }
-    if (norm(U,"2") != 1.)
-    {
-        warning(__FILE__, __LINE__, __FUNCTION__,"Plane wave direction is not normalized.");
-    }
-    std::complex<T> imk(0,wavenumber);
-    return imk * mtimes(N,transpose(U)) * exp(imk*mtimes(X,transpose(U)));
-}
-
-//==========================================================================
-//
-///
-template<typename T, typename S>
-matrix<T> rightHandSide(femdata<S>const& v, bemsource source, matrix<S>const& Y, S wavenumber=0)
-{
-    // Initialization
-    matrix<T> B(size(v.dof(),1),1);
-    matrix<T> Sxy;
-    switch (source)
-    {
-        case PWsource:
-            Sxy = planeWave<S>(v.gss(),Y,wavenumber);
-            break;
-        case dnPWsource:
-            Sxy = dnplaneWave<S>(v.gss(),v.gssnrm(),Y,wavenumber);
-            break;
-        default:
-            error(__FILE__, __LINE__, __FUNCTION__,"Source term not defined.");
-            break;
-    }
-    
-    // Builder
-    std::size_t ig;
-    for (std::size_t e=0; e<size(v.elt2dof(),1); ++e)
-    {
-        for (std::size_t g=0; g<size(v.elt2gss(),2); ++g)
-        {
-            ig = v.elt2gss(e,g);
-            for (std::size_t d=0; d<size(v.elt2dof(),2); ++d)
-            {
-                B(v.elt2dof(e,d)) += Sxy(ig) * v.wgt(ig) * v.base(ig,d);
-            }
-        }
-    }
-    return B;
-}
-
 //==========================================================================
 //
 ///
@@ -824,716 +752,33 @@ smatrix<T> mass(femdata<S>const& v)
 //
 ///
 template<typename T, typename S>
-matrix<T> singleLayer(matrix<S>const& X, matrix<S>const& Y, S wavenumber=0,
-                      matrix<std::size_t> Ix={}, matrix<std::size_t> Iy={})
-{
-    // Initialize dof data
-    if (numel(Ix)==0) {Ix = range(0,size(X,1));}
-    if (numel(Iy)==0) {Iy = range(0,size(Y,1));}
-    if (max(Ix)>size(X,1) || max(Iy)>size(Y,1))
-    {
-        error(__FILE__, __LINE__, __FUNCTION__,"Indices are not in data bound.");
-    }
-    matrix<T> M(numel(Ix),numel(Iy));
-    
-    // Initialize local data
-    std::size_t ix, iy;
-    S rxy;
-    T imk=std::sqrt(T(-1))*wavenumber;
-    
-    // Build
-    for (std::size_t i=0; i<numel(Ix); ++i)
-    {
-        ix = Ix(i);
-        for (std::size_t j=0; j<numel(Iy); ++j)
-        {
-            iy  = Iy(j);
-            rxy = std::sqrt((X(ix,0)-Y(iy,0))*(X(ix,0)-Y(iy,0)) +
-                            (X(ix,1)-Y(iy,1))*(X(ix,1)-Y(iy,1)) +
-                            (X(ix,2)-Y(iy,2))*(X(ix,2)-Y(iy,2)) );
-            if (wavenumber==0)
-            {
-                M(i,j) = (rxy>1e-12)? (1./rxy) : (0);
-            }
-            else
-            {
-                M(i,j) = (rxy>1e-12)? (std::exp(imk*rxy)/rxy) : (imk);
-            }
-        }
-    }
-    
-    // Return with 4*pi normalization
-    M /= (T)(4*M_PI);
-    return M;
-}
-
-//==========================================================================
-//
-///
-template<typename T, typename S>
-matrix<T> singleLayer(matrix<S>const& X, femdata<S>const& u, S wavenumber=0,
-                      matrix<std::size_t> Ix={}, matrix<std::size_t> Jd={})
-{
-    // Initialize dof data
-    if (numel(Ix)==0) {Ix = range(0,size(X,1));}
-    if (numel(Jd)==0) {Jd = range(0,size(u.dof(),1));}
-    if (max(Ix)>size(X,1) || max(Jd)>size(u.dof(),1))
-    {
-        error(__FILE__, __LINE__, __FUNCTION__,"Indices are not in data bound.");
-    }
-    matrix<T> M(numel(Ix),numel(Jd));
-    
-    // Initialize element data
-    matrix<std::size_t> Je;
-    matrix<long> e2du;
-    std::tie(Je,e2du) = u.dofsubdata(Jd);
-    
-    // Initialize local data
-    std::size_t ix, jg;
-    matrix<S> projectP1(1,3);
-    S rxy, Rm1, dnyRm1;
-    T Gxy, imk=std::sqrt(T(-1))*wavenumber;
-    
-    // Collocation points
-    for (std::size_t i=0; i<numel(Ix); ++i)
-    {
-        ix = Ix(i);
-        // Emmiters elements
-        for (std::size_t j=0; j<length(Je); ++j)
-        {
-            // Analytic integration
-            if (u.tridata(Je(j)).isfar(X(ix,0),X(ix,1),X(ix,2))) {Rm1 = 0;}
-            else
-            {
-                std::tie(Rm1,dnyRm1) = u.tridata(Je(j)).intsing(X(ix,0),X(ix,1),X(ix,2));
-            }
-            
-            // Gaussian quadrature
-            for (std::size_t k=0; k<size(u.elt2gss(),2); ++k)
-            {
-                jg = u.elt2gss(Je(j),k);
-                
-                // Green kernel
-                rxy = std::sqrt((X(ix,0)-u.gss(jg,0))*(X(ix,0)-u.gss(jg,0)) +
-                                (X(ix,1)-u.gss(jg,1))*(X(ix,1)-u.gss(jg,1)) +
-                                (X(ix,2)-u.gss(jg,2))*(X(ix,2)-u.gss(jg,2)));
-                if (wavenumber==0)
-                {
-                    Gxy = (rxy>1e-12)? (1./rxy) : (0);
-                }
-                else
-                {
-                    Gxy = (rxy>1e-12)? (std::exp(imk*rxy)/rxy) : (imk);
-                }
-                
-                // Integration
-                Gxy *= u.wgt(jg);
-                
-                // Dof repartition
-                for (std::size_t l=0; l<size(e2du,2); ++l)
-                {
-                    if (e2du(j,l)!=-1)
-                    {
-                        M(i,e2du(j,l)) += Gxy * u.base(jg,l);
-                    }
-                }
-                
-                // Gaussian integration
-                if (Rm1!=0) {Rm1 -= ((rxy>1e-12)?(1./rxy):(0)) * u.wgt(jg);}
-            }
-            
-            // Regularization
-            if (Rm1!=0)
-            {
-                // Lagrange P0
-                if (size(e2du,2)==1) {M(i,e2du(j)) += Rm1;}
-                // Lagrange P1
-                else if (size(e2du,2)==3)
-                {
-                    u.tridata(Je(j)).projectP1(X(ix,0),X(ix,1),X(ix,2),projectP1);
-                    for (std::size_t l=0; l<size(e2du,2); ++l)
-                    {
-                        if (e2du(j,l)!=-1)
-                        {
-                            M(i,e2du(j,l)) += Rm1 * projectP1(l);
-                        }
-                    }
-                }
-            }
-        }
-    }
-    
-    // Return with 4*pi normalization
-    M /= (T)(4*M_PI);
-    return M;
-}
-
-//==========================================================================
-//
-///
-template<typename T, typename S>
-matrix<T> singleLayer(femdata<S>const& v, femdata<S>const& u, S wavenumber=0,
-                      matrix<std::size_t> Id={}, matrix<std::size_t> Jd={})
-{
-    // Initialize dof data
-    if (numel(Id)==0) {Id = range(0,size(v.dof(),1));}
-    if (numel(Jd)==0) {Jd = range(0,size(u.dof(),1));}
-    matrix<T> M(numel(Id),numel(Jd));
-    
-    // Initialize element data
-    matrix<std::size_t> Ie, Je;
-    matrix<long> e2dv, e2du;
-    std::tie(Ie,e2dv) = v.dofsubdata(Id);
-    std::tie(Je,e2du) = u.dofsubdata(Jd);
-    
-    // Initialize local data
-    std::size_t ig, jg;
-    matrix<S> projectP1(1,3), xg(1,3);
-    S rxy, Rm1, dnyRm1;
-    T tmp, Gxy, imk=std::sqrt(T(-1))*wavenumber;
-    
-    // Receiver elements
-    for (std::size_t i=0; i<length(Ie); ++i)
-    {
-        // Emitters elements
-        for (std::size_t j=0; j<length(Je); ++j)
-        {
-            // Receiver gauss points
-            for (std::size_t k=0; k<size(v.elt2gss(),2); ++k)
-            {
-                // Emmiters analytic integration for receiver gauss point
-                ig = v.elt2gss(Ie(i),k);
-                xg(0)=v.gss(ig,0); xg(1)=v.gss(ig,1); xg(2)=v.gss(ig,2);
-                if (u.tridata(Je(j)).isfar(xg(0),xg(1),xg(2))) {Rm1 = 0;}
-                else
-                {
-                    std::tie(Rm1,dnyRm1) = u.tridata(Je(j)).intsing(xg(0),xg(1),xg(2));
-                }
-                
-                // Emmiter gauss points
-                for (std::size_t l=0; l<size(u.elt2gss(),2); ++l)
-                {
-                    jg = u.elt2gss(Je(j),l);
-                    
-                    // Green kernel
-                    rxy = std::sqrt((xg(0)-u.gss(jg,0))*(xg(0)-u.gss(jg,0))+
-                                    (xg(1)-u.gss(jg,1))*(xg(1)-u.gss(jg,1))+
-                                    (xg(2)-u.gss(jg,2))*(xg(2)-u.gss(jg,2)));
-                    if (wavenumber==0)
-                    {
-                        Gxy = (rxy>1e-12)? (1./rxy) : (0);
-                    }
-                    else
-                    {
-                        Gxy = (rxy>1e-12)? (std::exp(imk*rxy)/rxy) : (imk);
-                    }
-                    
-                    // Integration
-                    Gxy *= v.wgt(ig) * u.wgt(jg);
-                    
-                    // Dof repartition
-                    for (std::size_t m=0; m<size(e2dv,2); ++m)
-                    {
-                        tmp = v.base(ig,m) * Gxy;
-                        for (std::size_t n=0; n<size(e2du,2); ++n)
-                        {
-                            if (e2dv(i,m)!=-1 && e2du(j,n)!=-1)
-                            {
-                                M(e2dv(i,m),e2du(j,n)) += tmp * u.base(jg,n);
-                            }
-                        }
-                    }
-                    
-                    // Gaussian integration
-                    if (Rm1!=0) {Rm1 -= ((rxy>1e-12)?(1./rxy):(0)) * u.wgt(jg);}
-                }
-                
-                // Regularization
-                if (Rm1!=0)
-                {
-                    for (std::size_t m=0; m<size(e2dv,2); ++m)
-                    {
-                        if (e2dv(i,m)!=-1)
-                        {
-                            tmp = v.base(ig,m) * v.wgt(ig) * Rm1;
-                            if (size(e2du,2)==1) {M(e2dv(i,m),e2du(j)) += tmp;}
-                            else if (size(e2du,2)==3)
-                            {
-                                u.tridata(Je(j)).projectP1(xg(0),xg(1),xg(2),projectP1);
-                                for (std::size_t n=0; n<size(e2du,2); ++n)
-                                {
-                                    if (e2du(j,n)!=-1)
-                                    {
-                                        M(e2dv(i,m),e2du(j,n)) += tmp * projectP1(n);
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-    
-    // Return with 4*pi normalization
-    M /= (T)(4*M_PI);
-    return M;
-}
-
-//==========================================================================
-//
-///
-template<typename T, typename S>
-matrix<T> singleLayerInfinite(matrix<S>const& X, femdata<S>const& u, S wavenumber=0,
-                      matrix<std::size_t> Ix={}, matrix<std::size_t> Jd={})
-{
-    // Initialize dof data
-    if (numel(Ix)==0) {Ix = range(0,size(X,1));}
-    if (numel(Jd)==0) {Jd = range(0,size(u.dof(),1));}
-    if (max(Ix)>size(X,1) || max(Jd)>size(u.dof(),1))
-    {
-        error(__FILE__, __LINE__, __FUNCTION__,"Indices are not in data bound.");
-    }
-    matrix<T> M(numel(Ix),numel(Jd));
-    
-    // Initialize element data
-    matrix<std::size_t> Je;
-    matrix<long> e2du;
-    std::tie(Je,e2du) = u.dofsubdata(Jd);
-    
-    // Initialize local data
-    std::size_t ix, jg;
-    S xdoty;
-    T Gxy, imk=std::sqrt(T(-1))*wavenumber;
-    
-    // Collocation points
-    for (std::size_t i=0; i<numel(Ix); ++i)
-    {
-        ix = Ix(i);
-        // Emmiters elements
-        for (std::size_t j=0; j<length(Je); ++j)
-        {
-            // Gaussian quadrature
-            for (std::size_t k=0; k<size(u.elt2gss(),2); ++k)
-            {
-                jg = u.elt2gss(Je(j),k);
-                
-                // Green kernel
-                xdoty = X(ix,0)*u.gss(jg,0) + X(ix,1)*u.gss(jg,1) + X(ix,2)*u.gss(jg,2);
-                Gxy   = std::exp(-imk*xdoty) * u.wgt(jg);
-                
-                // Dof repartition
-                for (std::size_t l=0; l<size(e2du,2); ++l)
-                {
-                    if (e2du(j,l)!=-1)
-                    {
-                        M(i,e2du(j,l)) += Gxy * u.base(jg,l);
-                    }
-                }
-            }
-        }
-    }
-    
-    // Return with 4*pi normalization
-    M /= (T)(4*M_PI);
-    return M;
-}
-
-//==========================================================================
-//
-///
-template<typename T, typename S>
-matrix<T> doubleLayer(matrix<S>const& X, femdata<S>const& u, S wavenumber=0,
-                      matrix<std::size_t> Ix={}, matrix<std::size_t> Jd={})
-{
-    // Initialize dof data
-    if (numel(Ix)==0) {Ix = range(0,size(X,1));}
-    if (numel(Jd)==0) {Jd = range(0,size(u.dof(),1));}
-    if (max(Ix)>size(X,1) || max(Jd)>size(u.dof(),1))
-    {
-        error(__FILE__, __LINE__, __FUNCTION__,"Indices are not in data bound.");
-    }
-    matrix<T> M(numel(Ix),numel(Jd));
-    
-    // Initialize element data
-    matrix<std::size_t> Je;
-    matrix<long> e2du;
-    std::tie(Je,e2du) = u.dofsubdata(Jd);
-    
-    // Initialize local data
-    std::size_t ix, jg;
-    matrix<S> projectP1(1,3);
-    S rxy, rdotny, Rm1, dnyRm1;
-    T Gxy, imk=std::sqrt(T(-1))*wavenumber;
-    
-    // Collocation points
-    for (std::size_t i=0; i<numel(Ix); ++i)
-    {
-        ix = Ix(i);
-        // Emmiters elements
-        for (std::size_t j=0; j<length(Je); ++j)
-        {
-            // Analytic integration
-            if (u.tridata(Je(j)).isfar(X(ix,0),X(ix,1),X(ix,2))) {dnyRm1 = 0;}
-            else
-            {
-                std::tie(Rm1,dnyRm1) = u.tridata(Je(j)).intsing(X(ix,0),X(ix,1),X(ix,2));
-            }
-            
-            // Gaussian quadrature
-            for (std::size_t k=0; k<size(u.elt2gss(),2); ++k)
-            {
-                jg = u.elt2gss(Je(j),k);
-                
-                // Green kernel
-                rxy = std::sqrt((X(ix,0)-u.gss(jg,0))*(X(ix,0)-u.gss(jg,0)) +
-                                (X(ix,1)-u.gss(jg,1))*(X(ix,1)-u.gss(jg,1)) +
-                                (X(ix,2)-u.gss(jg,2))*(X(ix,2)-u.gss(jg,2)) );
-                rdotny = ((X(ix,0)-u.gss(jg,0))*u.gssnrm(jg,0) +
-                          (X(ix,1)-u.gss(jg,1))*u.gssnrm(jg,1) +
-                          (X(ix,2)-u.gss(jg,2))*u.gssnrm(jg,2) );
-                if (wavenumber==0)
-                {
-                    Gxy = (rxy>1e-12)? (rdotny/(rxy*rxy*rxy)) : (0);
-                }
-                else
-                {
-                    Gxy = (rxy>1e-12)? (rdotny*(1./rxy-imk)*std::exp(imk*rxy)/(rxy*rxy)) : (0);
-                }
-                
-                // Integration
-                Gxy *= u.wgt(jg);
-                
-                // Dof repartition
-                for (std::size_t l=0; l<size(e2du,2); ++l)
-                {
-                    if (e2du(j,l)!=-1)
-                    {
-                        M(i,e2du(j,l)) += Gxy * u.base(jg,l);
-                    }
-                }
-                
-                // Gaussian integration
-                if (dnyRm1!=0) {dnyRm1 -= ((rxy>1e-12)?(rdotny/(rxy*rxy*rxy)):(0)) * u.wgt(jg);}
-            }
-            
-            // Regularization
-            if (dnyRm1!=0)
-            {
-                // Lagrange P0
-                if (size(e2du,2)==1) {M(i,e2du(j)) += dnyRm1;}
-                // Lagrange P1
-                else if (size(e2du,2)==3)
-                {
-                    u.tridata(Je(j)).projectP1(X(ix,0),X(ix,1),X(ix,2),projectP1);
-                    for (std::size_t l=0; l<size(e2du,2); ++l)
-                    {
-                        if (e2du(j,l)!=-1)
-                        {
-                            M(i,e2du(j,l)) += dnyRm1 * projectP1(l);
-                        }
-                    }
-                }
-            }
-        }
-    }
-    
-    // Return with 4*pi normalization
-    M /= (T)(4*M_PI);
-    return M;
-}
-
-//==========================================================================
-//
-///
-template<typename T, typename S>
-matrix<T> doubleLayer(femdata<S>const& v, femdata<S>const& u, S wavenumber=0,
-                      matrix<std::size_t> Id={}, matrix<std::size_t> Jd={})
+smatrix<T> rigidity(femdata<S>const& v)
 {
-    // Initialize dof data
-    if (numel(Id)==0) {Id = range(0,size(v.dof(),1));}
-    if (numel(Jd)==0) {Jd = range(0,size(u.dof(),1));}
-    matrix<T> M(numel(Id),numel(Jd));
-    
-    // Initialize element data
-    matrix<std::size_t> Ie, Je;
-    matrix<long> e2dv, e2du;
-    std::tie(Ie,e2dv) = v.dofsubdata(Id);
-    std::tie(Je,e2du) = u.dofsubdata(Jd);
-    
-    // Initialize local data
-    std::size_t ig, jg;
-    matrix<S> projectP1(1,3), xg(1,3);
-    S rxy, rdotny, Rm1, dnyRm1;
-    T tmp, Gxy, imk=std::sqrt(T(-1))*wavenumber;
-    
-    // Receiver elements
-    for (std::size_t i=0; i<length(Ie); ++i)
+    std::vector<std::size_t> ind;
+    std::vector<T> val;
+    std::size_t ndof = size(v.dof(),1);
+    T tmp;
+    for (std::size_t e=0; e<size(v.elt2dof(),1); ++e)
     {
-        // Emitters elements
-        for (std::size_t j=0; j<length(Je); ++j)
+        for (std::size_t g=0; g<size(v.elt2gss(),2); ++g)
         {
-            // Receiver gauss points
-            for (std::size_t k=0; k<size(v.elt2gss(),2); ++k)
+            std::size_t ig = v.elt2gss(e,g);
+            for (std::size_t i=0; i<size(v.elt2dof(),2); ++i)
             {
-                // Emmiters analytic integration for receiver gauss point
-                ig = v.elt2gss(Ie(i),k);
-                xg(0)=v.gss(ig,0); xg(1)=v.gss(ig,1); xg(2)=v.gss(ig,2);
-                if (u.tridata(Je(j)).isfar(xg(0),xg(1),xg(2))) {dnyRm1 = 0;}
-                else
-                {
-                    std::tie(Rm1,dnyRm1) = u.tridata(Je(j)).intsing(xg(0),xg(1),xg(2));
-                }
-                
-                // Emmiter gauss points
-                for (std::size_t l=0; l<size(u.elt2gss(),2); ++l)
-                {
-                    jg = u.elt2gss(Je(j),l);
-                    
-                    // Green kernel
-                    rxy = std::sqrt((xg(0)-u.gss(jg,0))*(xg(0)-u.gss(jg,0)) +
-                                    (xg(1)-u.gss(jg,1))*(xg(1)-u.gss(jg,1)) +
-                                    (xg(2)-u.gss(jg,2))*(xg(2)-u.gss(jg,2)) );
-                    rdotny = ((xg(0)-u.gss(jg,0))*u.gssnrm(jg,0) +
-                              (xg(1)-u.gss(jg,1))*u.gssnrm(jg,1) +
-                              (xg(2)-u.gss(jg,2))*u.gssnrm(jg,2) );
-                    if (wavenumber==0)
-                    {
-                        Gxy = (rxy>1e-12)? (rdotny/(rxy*rxy*rxy)) : (0);
-                    }
-                    else
-                    {
-                        Gxy = (rxy>1e-12)? (rdotny*(1./rxy-imk)*std::exp(imk*rxy)/(rxy*rxy)) : (0);
-                    }
-                    
-                    // Integration
-                    Gxy *= v.wgt(ig) * u.wgt(jg);
-                    
-                    // Dof repartition
-                    for (std::size_t m=0; m<size(e2dv,2); ++m)
-                    {
-                        tmp = v.base(ig,m) * Gxy;
-                        for (std::size_t n=0; n<size(e2du,2); ++n)
-                        {
-                            if (e2dv(i,m)!=-1 && e2du(j,n)!=-1)
-                            {
-                                M(e2dv(i,m),e2du(j,n)) += tmp * u.base(jg,n);
-                            }
-                        }
-                    }
-                    
-                    // Gaussian integration
-                    if (dnyRm1!=0) {dnyRm1 -= ((rxy>1e-12)?(rdotny/(rxy*rxy*rxy)):(0)) * u.wgt(jg);}
-                }
-                
-                // Regularization
-                if (dnyRm1!=0)
+                for (std::size_t j=0; j<size(v.elt2dof(),2); ++j)
                 {
-                    for (std::size_t m=0; m<size(e2dv,2); ++m)
-                    {
-                        if (e2dv(i,m)!=-1)
-                        {
-                            tmp = v.base(ig,m) * v.wgt(ig) * dnyRm1;
-                            if (size(e2du,2)==1) {M(e2dv(i,m),e2du(j)) += tmp;}
-                            else if (size(e2du,2)==3)
-                            {
-                                u.tridata(Je(j)).projectP1(xg(0),xg(1),xg(2),projectP1);
-                                for (std::size_t n=0; n<size(e2du,2); ++n)
-                                {
-                                    if (e2du(j,n)!=-1)
-                                    {
-                                        M(e2dv(i,m),e2du(j,n)) += tmp * projectP1(n);
-                                    }
-                                }
-                            }
-                        }
-                    }
+                    ind.push_back(v.elt2dof(e,i)*ndof+v.elt2dof(e,j));
+                    tmp = v.wgt(ig) *
+                    (  v.grad(ig,i,0) * v.grad(ig,j,0)
+                     + v.grad(ig,i,1) * v.grad(ig,j,1)
+                     + v.grad(ig,i,2) * v.grad(ig,j,2) );
+                    val.push_back(tmp);
                 }
             }
         }
     }
-    
-    // Return with 4*pi normalization
-    M /= (T)(4*M_PI);
-    return M;
+    return smatrix<T>(ndof,ndof,ind,val);
 }
 
-//==========================================================================
-//
-///
-template<typename T, typename S>
-matrix<T> doubleLayerInfinite(matrix<S>const& X, femdata<S>const& u, S wavenumber=0,
-                      matrix<std::size_t> Ix={}, matrix<std::size_t> Jd={})
-{
-    // Initialize dof data
-    if (numel(Ix)==0) {Ix = range(0,size(X,1));}
-    if (numel(Jd)==0) {Jd = range(0,size(u.dof(),1));}
-    if (max(Ix)>size(X,1) || max(Jd)>size(u.dof(),1))
-    {
-        error(__FILE__, __LINE__, __FUNCTION__,"Indices are not in data bound.");
-    }
-    matrix<T> M(numel(Ix),numel(Jd));
-    
-    // Initialize element data
-    matrix<std::size_t> Je;
-    matrix<long> e2du;
-    std::tie(Je,e2du) = u.dofsubdata(Jd);
-    
-    // Initialize local data
-    std::size_t ix, jg;
-    S xdoty, xdotny;
-    T Gxy, imk=std::sqrt(T(-1))*wavenumber;
-    
-    // Collocation points
-    for (std::size_t i=0; i<numel(Ix); ++i)
-    {
-        ix = Ix(i);
-        // Emmiters elements
-        for (std::size_t j=0; j<length(Je); ++j)
-        {
-            // Gaussian quadrature
-            for (std::size_t k=0; k<size(u.elt2gss(),2); ++k)
-            {
-                jg = u.elt2gss(Je(j),k);
-                
-                // Green kernel
-                xdoty  = X(ix,0)*u.gss(jg,0) + X(ix,1)*u.gss(jg,1) + X(ix,2)*u.gss(jg,2);
-                xdotny = X(ix,0)*u.gssnrm(jg,0) + X(ix,1)*u.gssnrm(jg,1) + X(ix,2)*u.gssnrm(jg,2);
-                Gxy    = (-imk*xdotny) * std::exp(-imk*xdoty) * u.wgt(jg);
-                
-                // Dof repartition
-                for (std::size_t l=0; l<size(e2du,2); ++l)
-                {
-                    if (e2du(j,l)!=-1)
-                    {
-                        M(i,e2du(j,l)) += Gxy * u.base(jg,l);
-                    }
-                }
-            }
-        }
-    }
-    
-    // Return with 4*pi normalization
-    M /= (T)(4*M_PI);
-    return M;
 }
 
-//==========================================================================
-//
-///
-template<typename T, typename S>
-matrix<T> hypersingular(femdata<S>const& v, femdata<S>const& u, S wavenumber=0,
-                        matrix<std::size_t> Id={}, matrix<std::size_t> Jd={})
-{
-    // Initialize dof data
-    if (numel(Id)==0) {Id = range(0,size(v.dof(),1));}
-    if (numel(Jd)==0) {Jd = range(0,size(u.dof(),1));}
-    matrix<T> M(numel(Id),numel(Jd));
-    
-    // Initialize element data
-    matrix<std::size_t> Ie, Je;
-    matrix<long> e2dv, e2du;
-    std::tie(Ie,e2dv) = v.dofsubdata(Id);
-    std::tie(Je,e2du) = u.dofsubdata(Jd);
-    
-    // Initialize local data
-    std::size_t ig, jg;
-    matrix<S> projectP1(1,3), xg(1,3);
-    S rxy, Rm1, dnyRm1, k2=std::pow(wavenumber,2);
-    T Gxy, k2ndotnG, imk=std::sqrt(T(-1))*wavenumber;
-    
-    // Receiver elements
-    for (std::size_t i=0; i<length(Ie); ++i)
-    {
-        // Emitters elements
-        for (std::size_t j=0; j<length(Je); ++j)
-        {
-            // Receiver gauss points
-            for (std::size_t k=0; k<size(v.elt2gss(),2); ++k)
-            {
-                // Emmiters analytic integration for receiver gauss point
-                ig = v.elt2gss(Ie(i),k);
-                xg(0)=v.gss(ig,0); xg(1)=v.gss(ig,1); xg(2)=v.gss(ig,2);
-                if (u.tridata(Je(j)).isfar(xg(0),xg(1),xg(2))) {Rm1 = 0;}
-                else
-                {
-                    std::tie(Rm1,dnyRm1) = u.tridata(Je(j)).intsing(xg(0),xg(1),xg(2));
-                }
-                
-                // Emmiter gauss points
-                for (std::size_t l=0; l<size(u.elt2gss(),2); ++l)
-                {
-                    jg = u.elt2gss(Je(j),l);
-                    
-                    // Green kernel
-                    rxy = std::sqrt((xg(0)-u.gss(jg,0))*(xg(0)-u.gss(jg,0))+
-                                    (xg(1)-u.gss(jg,1))*(xg(1)-u.gss(jg,1))+
-                                    (xg(2)-u.gss(jg,2))*(xg(2)-u.gss(jg,2)));
-                    if (wavenumber==0)
-                    {
-                        Gxy = (rxy>1e-12)? (1./rxy) : (0);
-                    }
-                    else
-                    {
-                        Gxy = (rxy>1e-12)? (std::exp(imk*rxy)/rxy) : (imk);
-                    }
-                    
-                    // Integration
-                    Gxy *= v.wgt(ig) * u.wgt(jg);
-                    k2ndotnG = Gxy * k2 * (v.gssnrm(ig,0)*u.gssnrm(jg,0) +
-                                           v.gssnrm(ig,1)*u.gssnrm(jg,1) +
-                                           v.gssnrm(ig,2)*u.gssnrm(jg,2) );
-                    
-                    // Dof repartition
-                    for (std::size_t m=0; m<size(e2dv,2); ++m)
-                    {
-                        for (std::size_t n=0; n<size(e2du,2); ++n)
-                        {
-                            if (e2dv(i,m)!=-1 && e2du(j,n)!=-1)
-                            {
-                                M(e2dv(i,m),e2du(j,n)) += (k2ndotnG * v.base(ig,m) * u.base(jg,n) -
-                                                           Gxy * (
-                                                                  v.nxgrad(ig,m,0) * u.nxgrad(jg,n,0) +
-                                                                  v.nxgrad(ig,m,1) * u.nxgrad(jg,n,1) +
-                                                                  v.nxgrad(ig,m,2) * u.nxgrad(jg,n,2) ));
-                            }
-                        }
-                    }
-                    
-                    // Gaussian integration
-                    if (Rm1!=0) {Rm1 -= ((rxy>1e-12)?(1./rxy):(0)) * u.wgt(jg);}
-                }
-                
-                // Regularization
-                if (Rm1!=0)
-                {
-                    for (std::size_t m=0; m<size(e2dv,2); ++m)
-                    {
-                        if (e2dv(i,m)!=-1)
-                        {
-                            jg = u.elt2gss(Je(j),0);
-                            k2ndotnG = v.base(ig,m) * v.wgt(ig) * Rm1 * k2 * (v.gssnrm(ig,0)*u.gssnrm(jg,0) +
-                                                                              v.gssnrm(ig,1)*u.gssnrm(jg,1) +
-                                                                              v.gssnrm(ig,2)*u.gssnrm(jg,2) );
-                            u.tridata(Je(j)).projectP1(xg(0),xg(1),xg(2),projectP1);
-                            for (std::size_t n=0; n<size(e2du,2); ++n)
-                            {
-                                if (e2du(j,n)!=-1)
-                                {
-                                    M(e2dv(i,m),e2du(j,n)) += k2ndotnG * projectP1(n) - v.wgt(ig) * Rm1 *
-                                    (- v.nxgrad(ig,m,0) * u.tridata(Je(j)).tau(n,0)
-                                     - v.nxgrad(ig,m,1) * u.tridata(Je(j)).tau(n,1)
-                                     - v.nxgrad(ig,m,2) * u.tridata(Je(j)).tau(n,2)) / u.tridata(Je(j)).hgt(n);
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-    
-    // Return with 4*pi normalization
-    M /= (T)(4*M_PI);
-    return M;
-}