diff --git a/include/tadah/models/cutoffs.h b/include/tadah/models/cutoffs.h
index 54036451463548002f1ca52de519ccc5be566d00..882f0c99e37ae34e3350c52e199c0ab975280d34 100644
--- a/include/tadah/models/cutoffs.h
+++ b/include/tadah/models/cutoffs.h
@@ -139,5 +139,20 @@ class Cut_Poly2 : public Cut_Base {
         double calc(double r);
         double calc_prime(double r);
 };
+class Cut_Cos_S : public Cut_Base {
+    private:
+        std::string lab = "Cut_Cos_S";
+        double rcut, rcut_sq, rcut_inv;
+        double rcut_inner;
+    public:
+        Cut_Cos_S();
+        Cut_Cos_S(double rcut);
+        std::string label() ;
+        void set_rcut(const double r);
+        double get_rcut();
+        double get_rcut_sq();
+        double calc(double r);
+        double calc_prime(double r);
+};
 //template<> inline Registry<Cut_Base,double>::Map Registry<Cut_Base,double>::registry{};
 #endif
diff --git a/include/tadah/models/dc_selector.h b/include/tadah/models/dc_selector.h
index 0ffc2ab70f2b69051a3f668bbd08795b0ba36447..07a3539b879f0b8d3d9cdd14787f90acb161135c 100644
--- a/include/tadah/models/dc_selector.h
+++ b/include/tadah/models/dc_selector.h
@@ -15,7 +15,7 @@ class DC_Selector {
         D3_Base *d3b=nullptr;
         DM_Base *dmb=nullptr;
 
-        Config config;
+        //Config config;
 
         DC_Selector ()
         {};
@@ -25,21 +25,21 @@ class DC_Selector {
         {
             std::cout << "Copy constructor called\n";
         }
-        // Const Assignment operator
-        // Instead of deep copying
-        // we use a trick where we just copy
-        // the config file and run init()
-        // as in a costructor
-        DC_Selector& operator=(const DC_Selector& dc)
-        {
-            config=dc.config;
-            init();
-            return *this;
-        }
+         // Const Assignment operator
+         // Instead of deep copying
+         // we use a trick where we just copy
+         // the config file and run init()
+         // as in a costructor
+         // DC_Selector& operator=(const DC_Selector& dc)
+         // {
+         //     //config=dc.config;
+         //     //init();
+         //     return *this;
+         // }
         // Assignment operator
         DC_Selector& operator=(DC_Selector& dc)
         {
-            std::swap(config,dc.config);
+            //std::swap(config,dc.config);
             std::swap(c2b,dc.c2b);
             std::swap(c3b,dc.c3b);
             std::swap(cmb,dc.cmb);
@@ -49,12 +49,12 @@ class DC_Selector {
             return *this;
         }
 
-        DC_Selector (const Config &c):
-            config(c)
+        DC_Selector (Config &c)
+            //config(c)
     {
-        init();
+        init(c);
     };
-        void init() {
+        void init(Config &config) {
             double rcutzero = 0.0; // for dummies
 
             size_t bias=0;
@@ -62,19 +62,19 @@ class DC_Selector {
                 bias++;
 
             if (config.get<bool>("INIT2B")) {
-                double rcut2b = config.get<double>("RCUT2B");
+                double rcut2b = config.get<double>("RCUT2BMAX");
                 c2b = CONFIG::factory<Cut_Base,double>( config.get<std::string>("RCTYPE2B"), rcut2b );
                 d2b = CONFIG::factory<D2_Base,Config&>( config.get<std::string>("TYPE2B"), config );
-                d2b->fidx = bias;
+                d2b->set_fidx(bias);
             }
             else {
                 c2b = CONFIG::factory<Cut_Base,double>( "Cut_Dummy", rcutzero );
                 d2b = CONFIG::factory<D2_Base,Config&>( "D2_Dummy", config );
-                d2b->fidx = bias;
+                d2b->set_fidx(bias);
             }
 
             if (config.get<bool>("INIT3B")) {
-                double rcut3b = config.get<double>("RCUT3B");
+                double rcut3b = config.get<double>("RCUT3BMAX");
                 c3b = CONFIG::factory<Cut_Base,double>( config.get<std::string>("RCTYPE3B"), rcut3b );
                 d3b = CONFIG::factory<D3_Base,Config&>( config.get<std::string>("TYPE3B"), config );
                 // d3b->fidx = bias;
@@ -86,15 +86,15 @@ class DC_Selector {
             }
 
             if (config.get<bool>("INITMB")) {
-                double rcutmb = config.get<double>("RCUTMB");
+                double rcutmb = config.get<double>("RCUTMBMAX");
                 cmb = CONFIG::factory<Cut_Base,double>( config.get<std::string>("RCTYPEMB"), rcutmb );
                 dmb = CONFIG::factory<DM_Base,Config&>( config.get<std::string>("TYPEMB"), config );
-                dmb->fidx = bias + d2b->size();
+                dmb->set_fidx(bias + d2b->size());
             }
             else {
                 cmb = CONFIG::factory<Cut_Base,double>( "Cut_Dummy", rcutzero );
                 dmb = CONFIG::factory<DM_Base,Config&>( "DM_Dummy", config );
-                dmb->fidx = bias + d2b->size();
+                dmb->set_fidx(bias + d2b->size());
             }
         }
         ~DC_Selector()
diff --git a/include/tadah/models/descriptors/d2/d2_all.h b/include/tadah/models/descriptors/d2/d2_all.h
index ac0a29224a720ec196de5e60780c5c3f4fe27262..4b36ad4a4c2b7f1b20942d15763b819515c5b81b 100644
--- a/include/tadah/models/descriptors/d2/d2_all.h
+++ b/include/tadah/models/descriptors/d2/d2_all.h
@@ -5,3 +5,7 @@
 #include <tadah/models/descriptors/d2/d2_eam.h>
 #include <tadah/models/descriptors/d2/d2_lj.h>
 #include <tadah/models/descriptors/d2/d2_mie.h>
+#include <tadah/models/descriptors/d2/d2_zbl.h>
+
+#include <tadah/models/descriptors/d2/d2_join.h>
+#include <tadah/models/descriptors/d2/d2_mjoin.h>
diff --git a/include/tadah/models/descriptors/d2/d2_base.h b/include/tadah/models/descriptors/d2/d2_base.h
index 97d5341061d3a320784c1918819f4ba10d855f7d..8e7917c74c17ba471468ed0b9f2bff27e72ce343 100644
--- a/include/tadah/models/descriptors/d2/d2_base.h
+++ b/include/tadah/models/descriptors/d2/d2_base.h
@@ -1,6 +1,7 @@
 #ifndef D2_BASE_H
 #define D2_BASE_H
 
+#include <tadah/models/cut_all.h>
 #include <tadah/models/descriptors/d_base.h>
 
 /** \brief Base class for all two-body type descriptors.
@@ -9,42 +10,62 @@
  */
 class D2_Base: public D_Base {
 public:
-  size_t fidx=0;    // first index in aed and fd arrays for this descriptor
+  D2_Base() {};
+  D2_Base(Config &c): D_Base(c) {
+    //if (c.get<bool>("INIT2B")) {
+    if (c.exist("RCUT2BMAX") && c.exist("RCTYPE2B")) {
+      double rcut2b = c.get<double>("RCUT2BMAX");
+      set_fcut(CONFIG::factory<Cut_Base,double>( c.get<std::string>("RCTYPE2B"), rcut2b ), true);
+    }
+    else {
+      set_fcut(new Cut_Dummy(0), true);
+    }
+  }
   virtual ~D2_Base() {};
+  virtual std::vector<std::string> get_init_atoms(Config &c) override;
 
   /** \brief Calculate \ref AED
    *
    * Calculate Atomic Energy Descriptor for the atom local environment.
    */
   virtual void calc_aed(
+    const int Zi,
+    const int Zj,
     const double rij,
     const double rij_sq,
-    const double fc_ij,
-    aed_type2& aed)=0;
+    aed_type& aed,
+    const double scale=1)=0;
 
   /** \brief Calculate \ref FD
    *
    * Calculate Force Descriptor for the atom local environment.
    *
+   * For two body this is essentiall df/dr where f is a descriptor function.
+   * Do not include negative sign (as you mifht think E=-df/dr). The negative sign 
+   * is included elsewhere in the code.
+   *
    * Computes x-direction only.
+   *
    * The resulting vector must be scaled by the unit directional vector delij/rij
+   * where delij = r_i - r_j, and rij is |r_i - r_j|
    */
   virtual void calc_dXijdri(
+    const int Zi,
+    const int Zj,
     const double rij,
     const double rij_sq,
-    const double fc_ij,
-    const double fcp_ij,
-    fd_type &fd_ij)=0;
+    fd_type &fd_ij,
+    const double scale=1)=0;
 
   /** \brief Calculate \ref AED + \ref FD */
   virtual void calc_all(
+    const int Zi,
+    const int Zj,
     const double rij,
     const double rij_sq,
-    const double fc_ij,
-    const double fcp_ij,
-    aed_type2& aed,
-    fd_type &fd_ij)=0;
-  virtual std::string label()=0;
+    aed_type& aed,
+    fd_type &fd_ij,
+    const double scale=1)=0;
 
   /** Central difference approximation to \ref FD 
    *
@@ -56,18 +77,15 @@ public:
    * fd: appropriate size container to store computation
    * h: central difference parameter
    */
-  template <typename CUT>
-  void calc_fd_approx(CUT *fc, double r, fd_type &fd, double h=1e-8) {
-    aed_type2 aed_n((*this).size()); // r+h
-    aed_type2 aed_p((*this).size()); // r-h
+  void calc_fd_approx(const int Zi, const int Zj, double r, fd_type &fd, double h=1e-8) {
+    aed_type aed_n((*this).size()); // r+h
+    aed_type aed_p((*this).size()); // r-h
     aed_n.set_zero();
     aed_p.set_zero();
     double rn = r-h;
     double rp = r+h;
-    double fcp = fc->calc(rp);
-    double fcn = fc->calc(rn);
-    (*this).calc_aed(rn,rn*rn,fcn,aed_n);
-    (*this).calc_aed(rp,rp*rp,fcp,aed_p);
+    (*this).calc_aed(Zi,Zj,rn,rn*rn,aed_n);
+    (*this).calc_aed(Zi,Zj,rp,rp*rp,aed_p);
     fd(0) = (0.5/h)*(aed_p - aed_n);
   }
 };
diff --git a/include/tadah/models/descriptors/d2/d2_blip.h b/include/tadah/models/descriptors/d2/d2_blip.h
index bc35c823cccd362acd7beb6a393f0b2b0803cc8d..1ba3b14a89846678768aebe1084d24c95e1feaf7 100644
--- a/include/tadah/models/descriptors/d2/d2_blip.h
+++ b/include/tadah/models/descriptors/d2/d2_blip.h
@@ -40,37 +40,37 @@
  * \ref INIT2B \ref CGRID2B \ref SGRID2B
  */
 class D2_Blip : public D2_Base {
-    private:
-        size_t s=0;
-        std::string lab="D2_Blip";
-        v_type etas;
-        v_type mius;
-        double get_blip(double);
-        double get_dblip(double, double);
-        v_type gen_blip_grid(double, double);
-        int verbose;
+private:
+  std::string lab="D2_Blip";
+  v_type etas;
+  v_type mius;
 
-    public:
-        D2_Blip(Config &config);
-        void calc_aed(
-                const double rij,
-                const double rij_sq,
-                const double fc_ij,
-                aed_type2 &aed);
-        void calc_dXijdri(
-                const double rij,
-                const double rij_sq,
-                const double fc_ij,
-                const double fcp_ij,
-                fd_type &fd_ij);
-        void calc_all(
-                const double rij,
-                const double rij_sq,
-                const double fc_ij,
-                const double fcp_ij,
-                aed_type2 &aed,
-                fd_type &fd_ij);
-        size_t size();
-        std::string label();
+public:
+  D2_Blip();
+  D2_Blip(Config &config);
+  void calc_aed(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double ,
+    aed_type &aed,
+    const double scale=1) override;
+  void calc_dXijdri(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    fd_type &fd_ij,
+    const double scale=1) override;
+  void calc_all(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    aed_type &aed,
+    fd_type &fd_ij,
+    const double scale=1) override;
+  std::string label() override;
+  void init() override;
 };
 #endif
diff --git a/include/tadah/models/descriptors/d2/d2_bp.h b/include/tadah/models/descriptors/d2/d2_bp.h
index bff4ec90dee658eb695c152ccbc2bd423e3bee3c..6dcb726cce12f69da46b3a836e5425f0ecee2275 100644
--- a/include/tadah/models/descriptors/d2/d2_bp.h
+++ b/include/tadah/models/descriptors/d2/d2_bp.h
@@ -25,34 +25,37 @@
  * \ref INIT2B \ref CGRID2B \ref SGRID2B
  */
 class D2_BP : public D2_Base {
-    private:
-        size_t s=0;
-        std::string lab="D2_BP";
-        v_type etas;
-        v_type mius;
-        int verbose;
+private:
+  std::string lab="D2_BP";
+  v_type etas;
+  v_type mius;
 
-    public:
-        D2_BP(Config &config);
-        void calc_aed(
-                const double rij,
-                const double ,
-                const double fc_ij,
-                aed_type2 &aed);
-        void calc_dXijdri(
-                const double rij,
-                const double ,
-                const double fc_ij,
-                const double fcp_ij,
-                fd_type &fd_ij);
-        void calc_all(
-                const double rij,
-                const double ,
-                const double fc_ij,
-                const double fcp_ij,
-                aed_type2 &aed,
-                fd_type &fd_ij);
-        size_t size();
-        std::string label();
+public:
+  D2_BP();
+  D2_BP(Config &config);
+  void calc_aed(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double ,
+    aed_type &aed,
+    const double scale=1) override;
+  void calc_dXijdri(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    fd_type &fd_ij,
+    const double scale=1) override;
+  void calc_all(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    aed_type &aed,
+    fd_type &fd_ij,
+    const double scale=1) override;
+  std::string label() override;
+  void init() override;
 };
 #endif
diff --git a/include/tadah/models/descriptors/d2/d2_dummy.h b/include/tadah/models/descriptors/d2/d2_dummy.h
index f55ae6bd37a79c4e161e1df6b322fe8cb0e69b86..972ec027519d85a298f7f10288b60f2582fc94b0 100644
--- a/include/tadah/models/descriptors/d2/d2_dummy.h
+++ b/include/tadah/models/descriptors/d2/d2_dummy.h
@@ -10,32 +10,34 @@
  *
  */
 class D2_Dummy : public D2_Base {
-    private:
-        size_t s=0;
-        std::string lab="D2_Dummy";
-        int verbose;
-    public:
-        D2_Dummy();
-        D2_Dummy(Config &);
-        void calc_aed(
-                const double,
-                const double,
-                const double,
-                aed_type2 &aed);
-        void calc_dXijdri(
-                const double,
-                const double,
-                const double,
-                const double,
-                fd_type &fd_ij);
-        void calc_all(
-                const double,
-                const double,
-                const double,
-                const double,
-                aed_type2 &aed,
-                fd_type &fd_ij);
-        size_t size();
-        std::string label();
+private:
+  std::string lab="D2_Dummy";
+public:
+  D2_Dummy();
+  D2_Dummy(Config &);
+  void calc_aed(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double ,
+    aed_type &aed,
+    const double scale=1) override;
+  void calc_dXijdri(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    fd_type &fd_ij,
+    const double scale=1) override;
+  void calc_all(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    aed_type &aed,
+    fd_type &fd_ij,
+    const double scale=1) override;
+  std::string label() override;
+  void init() override;
 };
 #endif
diff --git a/include/tadah/models/descriptors/d2/d2_eam.h b/include/tadah/models/descriptors/d2/d2_eam.h
index b34afd4f86149836cf085817e7738bf950182ca0..503366e6170cd63626709343a6dd11cda424f876 100644
--- a/include/tadah/models/descriptors/d2/d2_eam.h
+++ b/include/tadah/models/descriptors/d2/d2_eam.h
@@ -22,58 +22,60 @@
  * \ref INIT2B \ref SETFL
  */
 class D2_EAM : public D2_Base {
-    private:
-        struct eam_file {
-            std::string file_path;
-            std::vector<double> frho;
-            std::vector<double> rhor;
-            std::vector<double> z2r;
-            int nrho=0;
-            double drho=0;
-            int nr;
-            double dr;
-            double rdr;
-            double rdrho;
-            double rcut;
-            int atomic_number;
-            double atomic_mass;
-            double lattice_param;
-            std::string lattice;
-        };
-		eam_file ef;
+private:
+  struct eam_file {
+    std::string file_path;
+    std::vector<double> frho;
+    std::vector<double> rhor;
+    std::vector<double> z2r;
+    int nrho=0;
+    double drho=0;
+    int nr;
+    double dr;
+    double rdr;
+    double rdrho;
+    double rcut;
+    int atomic_number;
+    double atomic_mass;
+    double lattice_param;
+    std::string lattice;
+  };
+  eam_file ef;
 
-        std::vector<std::vector<double>> frho_spline;
-        std::vector<std::vector<double>> rhor_spline;
-        std::vector<std::vector<double>> z2r_spline;
+  std::vector<std::vector<double>> frho_spline;
+  std::vector<std::vector<double>> rhor_spline;
+  std::vector<std::vector<double>> z2r_spline;
 
-        int verbose;
+  std::string lab="D2_EAM";
+  void read_setfl();
+  void gen_splines(int &n, double &delta, std::vector<double> &f, std::vector<std::vector<double>> &spline);
 
-        size_t s=1;
-        std::string lab="D2_EAM";
-		void read_setfl();
-		void gen_splines(int &n, double &delta, std::vector<double> &f, std::vector<std::vector<double>> &spline);
-
-    public:
-        D2_EAM(Config &config);
-        void calc_aed(
-                double rij,
-                const double rij_sq,
-                const double,
-                aed_type2 &aed);
-        void calc_dXijdri(
-                double rij,
-                const double rij_sq,
-                const double,
-                const double,
-                fd_type &fd_ij);
-        void calc_all(
-                double rij,
-                const double rij_sq,
-                const double,
-                const double,
-                aed_type2 &aed,
-                fd_type &fd_ij);
-        size_t size();
-        std::string label();
+public:
+  D2_EAM();
+  D2_EAM(Config &config);
+  void calc_aed(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double ,
+    aed_type &aed,
+    const double scale=1) override;
+  void calc_dXijdri(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    fd_type &fd_ij,
+    const double scale=1) override;
+  void calc_all(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    aed_type &aed,
+    fd_type &fd_ij,
+    const double scale=1) override;
+  std::string label() override;
+  void init() override;
 };
 #endif
diff --git a/include/tadah/models/descriptors/d2/d2_join.h b/include/tadah/models/descriptors/d2/d2_join.h
new file mode 100644
index 0000000000000000000000000000000000000000..d5c9a77a50a1a4efc4974ebf7c4a1a87f6e15760
--- /dev/null
+++ b/include/tadah/models/descriptors/d2/d2_join.h
@@ -0,0 +1,146 @@
+#ifndef D2_JOIN_H
+#define D2_JOIN_H
+#include <cstddef>
+#include <tadah/models/descriptors/d2/d2_base.h>
+
+/**
+ * Meta two-body descriptor for joining two different D2 descriptors.
+ *
+ */
+template<typename D1, typename D2>
+class D2_Join : public D2_Base {
+private:
+  D1 *d1 = nullptr;
+  D2 *d2 = nullptr;
+  //std::string lab="D2_Join";
+  std::string lab;
+public:
+  D2_Join();
+  ~D2_Join() {
+    if (d1) delete d1;
+    if (d2) delete d2;
+  }
+  D2_Join(Config &c): D2_Base(c)
+  {
+    // prepare configs for d1 and d2
+    // first we get joined grid and we have to split it between d1 and d2
+    // we have to deal with -1 -2 algorithms and manual cases
+    // TYPE2B 1st element is type followed be grid sizes (after expansion)
+    //
+    // TODO
+    // We want to be able to use this class in a recurence realtion
+    // where d1 <- any D2 but not D2_Join
+    // and d2 can be any including D2_Join
+    // This means that we trim config for d1 and pass
+    // whatever is left to d2
+    v_type cgrid;
+    cgrid.resize(c.size("CGRID2B"));
+    c.get<v_type>("CGRID2B",cgrid);
+    v_type sgrid;
+    sgrid.resize(c.size("SGRID2B"));
+    c.get<v_type>("SGRID2B",sgrid);
+    Config c1=c;
+    Config c2=c;
+    c1.remove("CGRID2B");
+    c1.remove("SGRID2B");
+    c2.remove("CGRID2B");
+    c2.remove("SGRID2B");
+    try {
+      size_t c1_cgrid_size = c.get<double>("CGRID2B",0) < 0 ? 4 : c.get<size_t>("TYPE2B",1);
+      size_t c1_sgrid_size = c.get<double>("SGRID2B",0) < 0 ? 4 : c.get<size_t>("TYPE2B",1);
+      size_t c2_cgrid_size = c.size("CGRID2B") - c1_cgrid_size;
+      size_t c2_sgrid_size = c.size("SGRID2B") - c1_sgrid_size;
+      for (size_t i=0; i<c1_cgrid_size; ++i) {
+        c1.add("CGRID2B",cgrid[i]);
+      }
+      for (size_t i=0; i<c1_sgrid_size; ++i) {
+        c1.add("SGRID2B",sgrid[i]);
+      }
+      for (size_t i=0; i<c2_cgrid_size; ++i) {
+        c2.add("CGRID2B",cgrid[i+c1_cgrid_size]);
+      }
+      for (size_t i=0; i<c2_sgrid_size; ++i) {
+        c2.add("SGRID2B",sgrid[i+c1_sgrid_size]);
+      }
+    }
+    catch (const std::runtime_error& e) {
+      std::cerr << "Grid Configuration Error: Ensure two grid generators are "
+        << "used for both D2 descriptors or provide manual grids. Avoid "
+        << "relying on a single auto grid generation for both descriptors. "
+        << "Details: " << e.what() << std::endl;
+    }
+    // deal with cutoffs
+    c1.remove("RCTYPE2B"); c2.remove("RCTYPE2B");
+    c1.remove("RCUT2B"); c2.remove("RCUT2B");
+    std::vector<std::string> rctype2b(c.size("RCTYPE2B"));
+    std::vector<std::string> rcut2b(c.size("RCUT2B"));
+    c.get("RCTYPE2B",rctype2b);
+    c.get("RCUT2B",rcut2b);
+    c1.add("RCTYPE2B", rctype2b[0]);
+    c1.add("RCUT2B", rcut2b[0]);
+    rctype2b.erase(rctype2b.begin());
+    rcut2b.erase(rcut2b.begin());
+    c2.add("RCTYPE2B", rctype2b);
+    c2.add("RCUT2B", rcut2b);
+    // std::cout << c1 << std::endl;
+    // std::cout << c2 << std::endl;
+    d1 = new D1(c1);
+    d2 = new D2(c2);
+
+    // update main config file with generated grids
+    cgrid.resize(c1.size("CGRID2B")+c2.size("CGRID2B"));
+    sgrid.resize(c1.size("SGRID2B")+c2.size("SGRID2B"));
+    c.remove("CGRID2B"); c.remove("SGRID2B");
+    for (size_t i=0; i<c1.size("CGRID2B"); ++i) {
+      c.add("CGRID2B",c1.get<double>("CGRID2B",i));
+      c.add("SGRID2B",c1.get<double>("SGRID2B",i));
+    }
+    for (size_t i=0; i<c2.size("CGRID2B"); ++i) {
+      c.add("CGRID2B",c2.get<double>("CGRID2B",i));
+      c.add("SGRID2B",c2.get<double>("SGRID2B",i));
+    }
+
+    s=d1->size()+d2->size();
+    lab="KUTAbezS"+d1->label()+d1->label();
+  }
+  void calc_aed(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    aed_type &aed,
+    const double scale=1) override {
+    d1->calc_aed(Zi,Zj,rij,rij_sq,aed,scale);
+    d2->calc_aed(Zi,Zj,rij,rij_sq,aed,scale);
+  }
+  void calc_dXijdri(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    fd_type &fd_ij,
+    const double scale=1) override {
+    d1->calc_dXijdri(Zi,Zj,rij,rij_sq,fd_ij,scale);
+    d2->calc_dXijdri(Zi,Zj,rij,rij_sq,fd_ij,scale);
+  }
+  void calc_all(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    aed_type &aed,
+    fd_type &fd_ij,
+    const double scale=1) override {
+    d1->calc_all(Zi,Zj,rij,rij_sq,aed,fd_ij,scale);
+    d2->calc_all(Zi,Zj,rij,rij_sq,aed,fd_ij,scale);
+  }
+  std::string label() override {
+    return lab;
+  }
+  void set_fidx(size_t fidx_) override {
+    d1->set_fidx(fidx_);
+    d2->set_fidx(d1->size()+d1->get_fidx());
+  }
+  void init() override {};
+};
+#endif
diff --git a/include/tadah/models/descriptors/d2/d2_join_n.h b/include/tadah/models/descriptors/d2/d2_join_n.h
new file mode 100644
index 0000000000000000000000000000000000000000..61b95684cc934fcc8646b955a903cafe4c750e89
--- /dev/null
+++ b/include/tadah/models/descriptors/d2/d2_join_n.h
@@ -0,0 +1,113 @@
+#ifndef D2_JOIN_N_H
+#define D2_JOIN_N_H
+
+#include <tadah/models/descriptors/d2/d2_base.h>
+#include <tuple>
+#include <string>
+
+/**
+ * Meta multi-body descriptor for joining multiple D2 descriptors.
+ *
+ * <DEVELOPMENT CODE>
+ * Only works with D2 without grids so essentially it is useless
+ */
+// template<typename... Descriptors>
+// class D2_Join_N : public D2_Base {
+// private:
+//   std::tuple<Descriptors...> descriptors;
+//   size_t s = 0;
+//   std::string lab;
+//
+//   template<size_t Index = 0>
+//   typename std::enable_if<Index == sizeof...(Descriptors), void>::type
+//   calculate_sizes() {}
+//
+//   template<size_t Index = 0>
+//   typename std::enable_if<Index < sizeof...(Descriptors), void>::type
+//   calculate_sizes() {
+//     s += std::get<Index>(descriptors).size();
+//     lab += std::get<Index>(descriptors).label();
+//     calculate_sizes<Index + 1>();
+//   }
+//
+//   template<size_t Index = 0>
+//   typename std::enable_if<Index == sizeof...(Descriptors), void>::type
+//   calc_aed_impl(const double, const double, const double, aed_type &) {}
+//
+//   template<size_t Index = 0>
+//   typename std::enable_if<Index < sizeof...(Descriptors), void>::type
+//   calc_aed_impl(const double rij, const double rij_sq, const double fc_ij, aed_type &aed) {
+//     std::get<Index>(descriptors).calc_aed(rij, rij_sq, fc_ij, aed);
+//     calc_aed_impl<Index + 1>(rij, rij_sq, fc_ij, aed);
+//   }
+//
+//   template<size_t Index = 0>
+//   typename std::enable_if<Index == sizeof...(Descriptors), void>::type
+//   calc_dXijdri_impl(const double, const double, const double, const double, fd_type &) {}
+//
+//   template<size_t Index = 0>
+//   typename std::enable_if<Index < sizeof...(Descriptors), void>::type
+//   calc_dXijdri_impl(const double rij, const double rij_sq, const double fc_ij, const double fcp_ij, fd_type &fd_ij) {
+//     std::get<Index>(descriptors).calc_dXijdri(rij, rij_sq, fc_ij, fcp_ij, fd_ij);
+//     calc_dXijdri_impl<Index + 1>(rij, rij_sq, fc_ij, fcp_ij, fd_ij);
+//   }
+//
+//   template<size_t Index = 0>
+//   typename std::enable_if<Index == sizeof...(Descriptors), void>::type
+//   calc_all_impl(const double, const double, const double, const double, aed_type &, fd_type &) {}
+//
+//   template<size_t Index = 0>
+//   typename std::enable_if<Index < sizeof...(Descriptors), void>::type
+//   calc_all_impl(const double rij, const double rij_sq, const double fc_ij, const double fcp_ij, aed_type &aed, fd_type &fd_ij) {
+//     std::get<Index>(descriptors).calc_all(rij, rij_sq, fc_ij, fcp_ij, aed, fd_ij);
+//     calc_all_impl<Index + 1>(rij, rij_sq, fc_ij, fcp_ij, aed, fd_ij);
+//   }
+//
+//   template<size_t Index = 0>
+//   typename std::enable_if<Index == sizeof...(Descriptors), void>::type
+//   set_fidx_impl(size_t) {}
+//
+//   template<size_t Index = 0>
+//   typename std::enable_if<Index < sizeof...(Descriptors), void>::type
+//   set_fidx_impl(size_t fidx) {
+//     std::get<Index>(descriptors).set_fidx(fidx);
+//     set_fidx_impl<Index + 1>(fidx + std::get<Index>(descriptors).size());
+//   }
+//
+//   template<typename T>
+//   T create_descriptor(Config &c) {
+//     return T(c);
+//   }
+//
+// public:
+//   D2_Join_N(Config &c) : descriptors(create_descriptor<Descriptors>(c)...) {
+//     calculate_sizes<0>();
+//   }
+//
+//   void calc_aed(const double rij, const double rij_sq, const double fc_ij, aed_type &aed) override {
+//     calc_aed_impl<0>(rij, rij_sq, fc_ij, aed);
+//   }
+//
+//   void calc_dXijdri(const double rij, const double rij_sq, const double fc_ij, const double fcp_ij, fd_type &fd_ij) override {
+//     calc_dXijdri_impl<0>(rij, rij_sq, fc_ij, fcp_ij, fd_ij);
+//   }
+//
+//   void calc_all(const double rij, const double rij_sq, const double fc_ij, const double fcp_ij, aed_type &aed, fd_type &fd_ij) override {
+//     calc_all_impl<0>(rij, rij_sq, fc_ij, fcp_ij, aed, fd_ij);
+//   }
+//   
+//   size_t size() override {
+//     return s;
+//   }
+//   
+//   std::string label() override {
+//     return lab;
+//   }
+//
+//   void set_fidx(size_t fidx_) override {
+//     set_fidx_impl<0>(fidx_);
+//   }
+// };
+
+#endif
+
diff --git a/include/tadah/models/descriptors/d2/d2_lj.h b/include/tadah/models/descriptors/d2/d2_lj.h
index e281f0f7708e0e3428dcfe108718914b8e9c4f83..4d8976a57c25653310f165872440980a289f00be 100644
--- a/include/tadah/models/descriptors/d2/d2_lj.h
+++ b/include/tadah/models/descriptors/d2/d2_lj.h
@@ -31,31 +31,34 @@
  * Required Config Key: \ref INIT2B
  */
 class D2_LJ : public D2_Base {
-    private:
-        size_t s=2;
-        std::string lab="D2_LJ";
-        int verbose;
-    public:
-        D2_LJ(Config &config);
-        void calc_aed(
-                const double,
-                const double rij_sq,
-                const double fc_ij,
-                aed_type2 &aed);
-        void calc_dXijdri(
-                const double rij,
-                const double rij_sq,
-                const double fc_ij,
-                const double fcp_ij,
-                fd_type &fd_ij);
-        void calc_all(
-                const double rij,
-                const double rij_sq,
-                const double fc_ij,
-                const double fcp_ij,
-                aed_type2 &aed,
-                fd_type &fd_ij);
-        size_t size();
-        std::string label();
+private:
+  std::string lab="D2_LJ";
+public:
+  D2_LJ();
+  D2_LJ(Config &config);
+  void calc_aed(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double ,
+    aed_type &aed,
+    const double scale=1) override;
+  void calc_dXijdri(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    fd_type &fd_ij,
+    const double scale=1) override;
+  void calc_all(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    aed_type &aed,
+    fd_type &fd_ij,
+    const double scale=1) override;
+  std::string label() override;
+  void init() override;
 };
 #endif
diff --git a/include/tadah/models/descriptors/d2/d2_mie.h b/include/tadah/models/descriptors/d2/d2_mie.h
index 6e4c16dd1215fb96c69d89b63420c8273572a269..22163c66cbe9f1905e352f401649e506d75c1add 100644
--- a/include/tadah/models/descriptors/d2/d2_mie.h
+++ b/include/tadah/models/descriptors/d2/d2_mie.h
@@ -17,40 +17,43 @@
  *
  * Any cutoff can be used
  *
- * Required Config Key: \ref INIT2B \ref SGRID2B
+ * Required Config Key: \ref INIT2B \ref TYPE2B
  *
- * e.g. SGID2B 12 6
+ * TYPE2B D2_MIE 12 6
  *
  * will result in Lennard-Jones type descriptor
  */
 class D2_MIE : public D2_Base {
-    private:
-        size_t s=2;
-        std::string lab="D2_MIE";
-        double m,n;
-        v_type etas;
-        int verbose;
-    public:
-        D2_MIE(Config &config);
-        void calc_aed(
-                const double,
-                const double rij_sq,
-                const double fc_ij,
-                aed_type2 &aed);
-        void calc_dXijdri(
-                const double rij,
-                const double rij_sq,
-                const double fc_ij,
-                const double fcp_ij,
-                fd_type &fd_ij);
-        void calc_all(
-                const double rij,
-                const double rij_sq,
-                const double fc_ij,
-                const double fcp_ij,
-                aed_type2 &aed,
-                fd_type &fd_ij);
-        size_t size();
-        std::string label();
+private:
+  std::string lab="D2_MIE";
+  double m,n;
+  v_type etas;
+public:
+  D2_MIE();
+  D2_MIE(Config &config);
+  void calc_aed(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double ,
+    aed_type &aed,
+    const double scale=1) override;
+  void calc_dXijdri(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    fd_type &fd_ij,
+    const double scale=1) override;
+  void calc_all(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    aed_type &aed,
+    fd_type &fd_ij,
+    const double scale=1) override;
+  std::string label() override;
+  void init() override;
 };
 #endif
diff --git a/include/tadah/models/descriptors/d2/d2_mjoin.h b/include/tadah/models/descriptors/d2/d2_mjoin.h
new file mode 100644
index 0000000000000000000000000000000000000000..fc22f65d3554194b22c869d99038a1ffba126233
--- /dev/null
+++ b/include/tadah/models/descriptors/d2/d2_mjoin.h
@@ -0,0 +1,59 @@
+#ifndef D2_MJOIN_H
+#define D2_MJOIN_H
+#include "tadah/core/registry.h"
+#include <cstddef>
+#include <tadah/models/descriptors/d2/d2_base.h>
+#include <tadah/models/descriptors/d_mjoin.h>
+
+/** \brief Meta two-body descriptor for combining multiple D2 descriptors.
+ *
+ * This descriptor provides a convenient interface for concatenating multiple two-body descriptors. 
+ * The resulting descriptor can then be used by Tadah! like any standard two-body descriptor.
+ *
+ * Each descriptor must have a specified type in a configuration file, along with a cutoff function, 
+ * cutoff distance, and optionally SGRID2B and CGRID2B values if applicable.
+ *
+ * When listing descriptors under the TYPE2B key, you must include parameters relevant to this descriptor.
+ *
+ * Here is an example of how to configure these descriptors:
+ * 
+ * ```
+ * TYPE2B    D2_mJoin     # <-- Meta descriptor for concatenating two-body descriptors
+ * TYPE2B    D2_MIE 11 6    # <-- MIE exponents
+ * RCTYPE2B  Cut_Cos
+ * RCUT2B    3.0
+ *
+ * TYPE2B    D2_Blip 6 6    # <-- grid sizes
+ * RCTYPE2B  Cut_Tanh
+ * RCUT2B    7.5
+ * SGRID2B   -2 6 0.1 10   # Grid for D2_Blip, blips widths, auto generated
+ * CGRID2B   0 0 0 0 0 0   # Grid for D2_Blip, blip centers
+ * ```
+ *
+ * Note: Grids can be specified on a single line, and the order of the grids is important. 
+ *
+ * There is no limit to the number of descriptors that can be concatenated.
+ *
+ * \details
+ * - Ensure the types and grids are correctly specified in the configuration file.
+ * - The cutoff functions (RCTYPE2B) and distances (RCUT2B) must be defined for each descriptor.
+ * - Both SGRID2B and CGRID2B should be included if relevant, with their sizes matching the given descriptors.
+ */
+class D2_mJoin : public D2_Base, public D_mJoin {
+private:
+  std::vector<D2_Base*> ds;
+  std::vector<Config> configs;
+  std::string lab = "D2_mJoin";
+public:
+  D2_mJoin();
+  ~D2_mJoin();
+  D2_mJoin(Config &c);
+  
+  void calc_aed(const int Zi, const int Zj, const double rij, const double rij_sq, aed_type &aed, const double scale=1) override;
+  void calc_dXijdri(const int Zi, const int Zj, const double rij, const double rij_sq, fd_type &fd_ij, const double scale=1) override;
+  void calc_all(const int Zi, const int Zj, const double rij, const double rij_sq, aed_type &aed, fd_type &fd_ij, const double scale=1) override;
+  std::string label() override;
+  void set_fidx(size_t fidx_) override;
+  void init() override;
+};
+#endif
diff --git a/include/tadah/models/descriptors/d2/d2_zbl.h b/include/tadah/models/descriptors/d2/d2_zbl.h
new file mode 100644
index 0000000000000000000000000000000000000000..585887ebb2d3dbeb759329597d42693c1e14e3e1
--- /dev/null
+++ b/include/tadah/models/descriptors/d2/d2_zbl.h
@@ -0,0 +1,92 @@
+#ifndef D2_ZBL_H
+#define D2_ZBL_H
+
+#include <tadah/models/descriptors/d2/d2_base.h>
+
+/**
+ * \brief ZBL Descriptor
+ *
+ * The ZBL (Ziegler-Biersack-Littmark) potential is an empirical potential used to model short-range interactions between atoms.
+ *
+ * The constant term \( \frac{4 \pi \varepsilon_0 e^2}{1} \) is set to 1 and will be fitted as needed.
+ *
+ * The simplified expression for the ZBL potential is given by:
+ *
+ * \f[
+ * V(r) = \frac{Z_1 Z_2 }{r} \phi\left(\frac{r}{a}\right)
+ * \f]
+ *
+ * where \( a \) is the screening length, expressed as:
+ * \f[
+ * a = \frac{s_0 \cdot a_0}{Z_1^{p_0} + Z_2^{p_1}}
+ * \f]
+ *
+ * Here, \( a_0 \), \( s_0 \), \( p_0 \), and \( p_1 \) are adjustable hyperparameters. 
+ * Setting any of these to -1 uses the default values:
+ *
+ * - \( a_0 = 0.52917721067 \) Ã…
+ * - \( s_0 = 0.88534 \)
+ * - \( p_0 = 0.23 \)
+ * - \( p_1 = 0.23 \)
+ *
+ *
+ * The screening function \(\phi\) is defined as:
+ * \f[
+ * \phi(x) = 0.1818 e^{-3.2x} + 0.5099 e^{-0.9423x} + 0.2802 e^{-0.4029x} + 0.02817 e^{-0.2016x}
+ * \f]
+ *
+ * Required Config Key: \ref INIT2B \ref TYPE2B
+ *
+ * - TYPE2B D2_ZBL \( a_0 \) \( s_0 \) \( p_0 \) \( p_1 \)
+ *
+ * Examples:
+ *
+ * - TYPE2B D2_ZBL 0.53 0.90 0.23 0.23   # Custom parameters
+ * - TYPE2B D2_ZBL -1 -1 -1 -1   # Default values
+ * - TYPE2B D2_ZBL 0.53 -1 -1 -1   # Mix of default and custom
+ *
+ */
+class D2_ZBL : public D2_Base {
+private:
+  std::string lab="D2_ZBL";
+  double phi(const double x); // x = r/a
+  // dphi/dx
+  double dphi(const double x); // x = r/a
+  double screening_length(int, int); // a = 
+  double a0=0.52917721067; // bohr radius
+  double s0=0.88534; // bohr radius
+  double p0=0.23; // bohr radius
+  double p1=0.23; // bohr radius
+  double **a = nullptr; // precomputed screening length
+  int types[119]; // local index of Zi
+  int ntypes=0; // number of elements for this calc
+public:
+  D2_ZBL();
+  D2_ZBL(Config &config);
+  ~D2_ZBL();
+  void calc_aed(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double ,
+    aed_type &aed,
+    const double scale=1) override;
+  void calc_dXijdri(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    fd_type &fd_ij,
+    const double scale=1) override;
+  void calc_all(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    aed_type &aed,
+    fd_type &fd_ij,
+    const double scale=1) override;
+  std::string label() override;
+  void init() override;
+};
+#endif
diff --git a/include/tadah/models/descriptors/d3/d3_base.h b/include/tadah/models/descriptors/d3/d3_base.h
index 88c216c888064605ed6534f0acf530b22bd2ef8c..c64a76bfd218d1ff385f95fe2935d2358e3e8ef2 100644
--- a/include/tadah/models/descriptors/d3/d3_base.h
+++ b/include/tadah/models/descriptors/d3/d3_base.h
@@ -4,37 +4,37 @@
 #include <tadah/models/descriptors/d_base.h>
 
 class D3_Base: public D_Base {
-    public:
-        virtual ~D3_Base() {};
+public:
+  virtual ~D3_Base() {};
 
-        virtual void calc_aed(
-                const size_t fidx,
-                const double rij,
-                const double rik,
-                const double fc_ij,
-                const double fc_ik,
-                aed_type2& aed)=0;
-        virtual void calc_fd(
-                const size_t fidx,
-                const double rij,
-                const double rik,
-                const double fc_ij,
-                const double fc_ik,
-                const double fcp_ij,
-                const double fcp_ik,
-                fd_type &fd_ij)=0;
-        virtual void calc_all(
-                const size_t fidx,
-                const double rij,
-                const double rik,
-                const double fc_ij,
-                const double fc_ik,
-                const double fcp_ij,
-                const double fcp_ik,
-                aed_type2& aed,
-                fd_type &fd_ij)=0;
-        virtual size_t size()=0;
-        virtual std::string label()=0;
+  virtual void calc_aed(
+    const size_t fidx,
+    const double rij,
+    const double rik,
+    const double fc_ij,
+    const double fc_ik,
+    aed_type& aed)=0;
+  virtual void calc_fd(
+    const size_t fidx,
+    const double rij,
+    const double rik,
+    const double fc_ij,
+    const double fc_ik,
+    const double fcp_ij,
+    const double fcp_ik,
+    fd_type &fd_ij)=0;
+  virtual void calc_all(
+    const size_t fidx,
+    const double rij,
+    const double rik,
+    const double fc_ij,
+    const double fc_ik,
+    const double fcp_ij,
+    const double fcp_ik,
+    aed_type& aed,
+    fd_type &fd_ij)=0;
+  virtual size_t size()override=0;
+  virtual std::string label()override=0;
+  virtual std::vector<std::string> get_init_atoms(Config &c) override;
 };
-//template<> inline Registry<D3_Base,Config&>::Map Registry<D3_Base,Config&>::registry{};
 #endif
diff --git a/include/tadah/models/descriptors/d3/d3_dummy.h b/include/tadah/models/descriptors/d3/d3_dummy.h
index 0b5cdbdab1aad774d9adb95c74c91bf325e7b104..8cac4f5519ca893e8ed5f53aeb39fbef98be4d2c 100644
--- a/include/tadah/models/descriptors/d3/d3_dummy.h
+++ b/include/tadah/models/descriptors/d3/d3_dummy.h
@@ -2,39 +2,40 @@
 #define D3_DUMMY_H
 #include <tadah/models/descriptors/d3/d3_base.h>
 class D3_Dummy: public D3_Base {
-    private:
-        size_t s=0;
-        std::string lab="D3_Dummy";
-    public:
-        D3_Dummy();
-        D3_Dummy(Config&);
-        void calc_aed(
-                const size_t fidx,
-                const double rij,
-                const double rik,
-                const double fc_ij,
-                const double fc_ik,
-                aed_type2& aed);
-        void calc_fd(
-                const size_t fidx,
-                const double rij,
-                const double rik,
-                const double fc_ij,
-                const double fc_ik,
-                const double fcp_ij,
-                const double fcp_ik,
-                fd_type &fd_ij);
-        void calc_all(
-                const size_t fidx,
-                const double rij,
-                const double rik,
-                const double fc_ij,
-                const double fc_ik,
-                const double fcp_ij,
-                const double fcp_ik,
-                aed_type2& aed,
-                fd_type &fd_ij);
-        size_t size();
-        std::string label();
+private:
+  size_t s=0;
+  std::string lab="D3_Dummy";
+public:
+  D3_Dummy();
+  D3_Dummy(Config&);
+  void calc_aed(
+    const size_t fidx,
+    const double rij,
+    const double rik,
+    const double fc_ij,
+    const double fc_ik,
+    aed_type& aed);
+  void calc_fd(
+    const size_t fidx,
+    const double rij,
+    const double rik,
+    const double fc_ij,
+    const double fc_ik,
+    const double fcp_ij,
+    const double fcp_ik,
+    fd_type &fd_ij);
+  void calc_all(
+    const size_t fidx,
+    const double rij,
+    const double rik,
+    const double fc_ij,
+    const double fc_ik,
+    const double fcp_ij,
+    const double fcp_ik,
+    aed_type& aed,
+    fd_type &fd_ij);
+  size_t size();
+  std::string label();
+  void init() override;
 };
 #endif
diff --git a/include/tadah/models/descriptors/d_base.h b/include/tadah/models/descriptors/d_base.h
index 8e9a25564f5fa60445d329d8d6a54fa85ff10911..e02737c67f9f8dbd1c7ac00474bf3c5e3708211b 100644
--- a/include/tadah/models/descriptors/d_base.h
+++ b/include/tadah/models/descriptors/d_base.h
@@ -1,56 +1,98 @@
 #ifndef D_BASE_H
 #define D_BASE_H
 
+#include <string>
 #include <tadah/core/config.h>
 #include <tadah/core/core_types.h>
+#include <tadah/core/periodic_table.h>
 #include <tadah/core/registry.h>
+#include <tadah/models/cut_all.h>
+#include <vector>
+#include <bitset>
+
+class Bitset2D {
+  static constexpr size_t N = 119;
+  std::bitset<N * N> data;
+
+public:
+  void init(size_t i, size_t j) {
+    data[i * N + j] = true;
+    data[j * N + i] = true;
+
+  }
+  void uninit(size_t i, size_t j) {
+    data[i * N + j] = false;
+    data[j * N + i] = false;
+  }
+  bool is_init(size_t i, size_t j) const {
+    return data[i * N + j];
+  }
+};
 
 /** \brief Base class for all descriptor types.
  *
  */
 class D_Base {
-    public:
-        static void get_grid(const Config &c, std::string key, v_type &v) {
-            if (c.get<double>(key) < 0) {
-                // generate grid automatically if first element
-                // is<zero and int. If it is double than assume that
-                // grid is provided manually and min value is just smaller
-                // than zero.
-                double int_part;
-                if (std::modf ( c.get<double>(key), &int_part ) == 0.0) {
-                    // automatic generation
-                    size_t cg = abs(c.get<int>(key,1));
-                    double cgmin = c.get<double>(key,2);
-                    double cgmax = c.get<double>(key,3);
-                    if (c.get<int>(key) == -1) {
-                        v = linspace(cgmin, cgmax, cg);
-                    }
-                    else if (c.get<int>(key) == -2) {
-                        v = logspace(cgmin, cgmax, cg, exp(1));
-                    }
-                    else {
-                        throw std::runtime_error(key+" algorithm not supported\n");
-                    }
-                }
-                else {
-                    v.resize(c.size(key));
-                    c.get<v_type>(key,v);
-                }
-            }
-            else {
-                v.resize(c.size(key));
-                c.get<v_type>(key,v);
-            }
-        }
-        int verbose;
-        virtual ~D_Base() {};
-
-        /** \brief Return dimension of the descriptor.
-         */
-        virtual size_t size()=0;
-
-        /** \brief Return label of this descriptor.
-         */
-        virtual std::string label()=0;
+protected:
+  size_t fidx=0;    // first index in aed and fd arrays for this descriptor
+  size_t s=0;
+  Cut_Base *fcut=nullptr;
+
+public:
+  std::vector<std::string> keys;    // keys required by this descriptor
+  size_t nparams;  // number of params which follow TYPExB
+  std::vector<bool> is_optimizable;
+  static void get_grid(const Config &c, std::string key, v_type &v);
+  static v_type get_grid(std::vector<std::string>&);  // expand joined grids
+  int verbose;
+  virtual ~D_Base();
+
+  // return position of an argument given to TYPExB for a given key
+  // if key is not used, returns -1
+  int get_arg_pos(const std::string &key) const;
+
+
+  /** \brief Return label of this descriptor.
+   */
+  virtual std::string label()=0;
+  double weights[119] = {1};  // ignore zero index; w[1]->H
+  D_Base();
+  D_Base(Config &c);
+
+  virtual void set_fidx(size_t fidx_);
+  virtual size_t get_fidx();
+
+  /**
+ * @brief Initialize the descriptor.
+ *
+ * This method sets up the required keys names and number of parameters for the descriptor.
+ *
+ * Note: The implementation of the descriptor should not rely on these initial values,
+ * allowing for greater flexibility in how the Config file is parsed.
+ *
+ * The keys and parameters are utilized by other classes inheriting from this implementation,
+ * such as meta-descriptors.
+ *
+ */
+  virtual void init() = 0;
+
+  virtual void set_fcut(Cut_Base* cut, bool manage_memory);
+
+  double get_rcut();
+
+  /** \brief Return dimension of the descriptor.
+  */
+  virtual size_t size();
+
+  virtual bool is_init_for_atoms(int Zi, int Zj);
+  virtual void init_for_atoms(int Zi, int Zj);
+  virtual void init_for_atoms(const std::vector<std::string> &Zs);
+  virtual void uninit_for_atoms(int Zi, int Zj);
+  static std::vector<std::string> get_init_atoms(Config &c, std::string type);
+  virtual std::vector<std::string> get_init_atoms(Config &c)=0;
+
+private:
+  bool manage_memory=false; // who owns fcut
+  Bitset2D init_for_atoms_map;  // 0th is unused
 };
 #endif
diff --git a/include/tadah/models/descriptors/d_mjoin.h b/include/tadah/models/descriptors/d_mjoin.h
new file mode 100644
index 0000000000000000000000000000000000000000..182cb6d68eb28090c0c43d4c9ed9da08508c13d2
--- /dev/null
+++ b/include/tadah/models/descriptors/d_mjoin.h
@@ -0,0 +1,29 @@
+#ifndef D_MJOIN_H
+#define D_MJOIN_H
+#include <tadah/core/config.h>
+#include <vector>
+
+/** \brief Base class to be used by meta descriptors for joining
+*/
+class D_mJoin {
+protected:
+  struct D_Type {
+    std::string type;
+    std::string rc_type;
+    double rcut;
+    std::vector<std::string> params;
+    Config c;
+  };
+  std::vector<D_Type> dt;
+  std::string lab = "D_mJoin";
+public:
+  virtual ~D_mJoin();
+  D_mJoin();
+  static std::vector<Config> parse_config(Config &c, std::string type_str);
+
+  static void expand_grids(Config &c, std::vector<Config> &configs, std::string type_str);
+  static void expand_grids(Config &c, std::string type_str);
+  static void expand_grids(Config &c);
+};
+#endif
+
diff --git a/include/tadah/models/descriptors/dm/dm_all.h b/include/tadah/models/descriptors/dm/dm_all.h
index 6d92d07bf484eb9cbcd1250edbe61c35ccf1c305..26e18c7b3b810c8e5f894a0ffe20e8df99d5bcb1 100644
--- a/include/tadah/models/descriptors/dm/dm_all.h
+++ b/include/tadah/models/descriptors/dm/dm_all.h
@@ -4,5 +4,6 @@
 #include <tadah/models/descriptors/dm/dm_ead.h>
 #include <tadah/models/descriptors/dm/dm_blip.h>
 #include <tadah/models/descriptors/dm/dm_mEAD.h>
+#include <tadah/models/descriptors/dm/dm_mjoin.h>
 
 #include <tadah/models/descriptors/dm/dm_func.h>
diff --git a/include/tadah/models/descriptors/dm/dm_base.h b/include/tadah/models/descriptors/dm/dm_base.h
index 3eba787b13569e393aa4702367f6bdd8fc54efad..825a1c52061f7763b196cc4855a41526c8fee7b0 100644
--- a/include/tadah/models/descriptors/dm/dm_base.h
+++ b/include/tadah/models/descriptors/dm/dm_base.h
@@ -9,9 +9,24 @@
  * All many-body descriptors **must** inherit this class.
  */
 class DM_Base: public D_Base {
+
+protected:
+  size_t rfidx=0;
+  size_t rhoisize=0;
 public:
-  size_t fidx=0;    // first index in aed and fd arrays for this descriptor
+  DM_Base() {};
+  DM_Base(Config &c): D_Base(c) {
+    //if (c.get<bool>("INITMB")) {
+    if (c.exist("RCUTMBMAX") && c.exist("RCTYPEMB")) {
+      double rcutmb = c.get<double>("RCUTMBMAX");
+      set_fcut(CONFIG::factory<Cut_Base,double>( c.get<std::string>("RCTYPEMB"), rcutmb ), true);
+    }
+    else {
+      set_fcut(new Cut_Dummy(0), true);
+    }
+  }
   virtual ~DM_Base() {};
+  virtual std::vector<std::string> get_init_atoms(Config &c) override;
 
   /** \brief Calculate \ref AED
    *
@@ -19,7 +34,7 @@ public:
    */
   virtual void calc_aed(
     rho_type& rho,
-    aed_type2 &aed)=0;
+    aed_type &aed)=0;
 
   /** \brief Calculate \ref FD
    *
@@ -34,17 +49,16 @@ public:
    * \frac{\partial \mathbf{X}_{ji}}{\partial \mathbf{r}_i}
    * \f]
    */
-  virtual int calc_dXijdri_dXjidri(
+  virtual void calc_dXijdri_dXjidri(
+    const int Zi,
+    const int Zj,
     const double rij,
     const double rij_sq,
     const Vec3d &vec_ij,
-    const double fc_ij,
-    const double fcp_ij,
     rho_type& rhoi,
     rho_type& rhoj,
     fd_type &fd_ij,
-    const double wi,
-    const double wj)=0;
+    const double scale=1)=0;
 
   /** \brief Calculate \ref FD
    *
@@ -61,62 +75,59 @@ public:
    * \frac{\partial \mathbf{X}_{ij}}{\partial \mathbf{r}_i}
    * \f]
    */
-  virtual int calc_dXijdri(
+  virtual void calc_dXijdri(
+    const int Zi,
+    const int Zj,
     const double rij,
     const double rij_sq,
     const Vec3d &vec_ij,
-    const double fc_ij,
-    const double fcp_ij,
     rho_type& rhoi,
-    fd_type &fd_ij)=0;
+    fd_type &fd_ij,
+    const double scale=1)=0;
 
   /** \brief Resize arrays for a density and F' */
   virtual void init_rhoi(rho_type& rhoi)=0;
 
   /** \brief Calculate density */
   virtual void calc_rho(
+    const int Zi,
+    const int Zj,
     const double rij,
     const double rij_sq,
-    const double fc_ij,
     const Vec3d &vec_ij,
-    rho_type& rho)=0;
-
-  /** \brief Return size of the density array */
-  virtual size_t rhoi_size()=0;
+    rho_type& rho,
+    const double scale=1)=0;
 
-  /** \brief Return size of the derivative of the embedding energy array */
-  virtual size_t rhoip_size()=0;
-  virtual size_t size()=0;
-  virtual std::string label()=0;
 
   /** \brief This is just a convenient wrapper.
-   *
-   * Note that this method should not to be used when 
-   * computational performance matter.
    */
-  void calc_dXijdri(Vec3d &del, rho_type &rhoi, fd_type &fdij, double fc=1, double fcp=0) {
+  void calc_dXijdri(const int Zi, const int Zj, Vec3d &del, rho_type &rhoi, fd_type &fdij, double scale=1) {
     double rsq = del*del;
     double r = sqrt(rsq);
     //(p->*func)(r,rsq,del,1.0,0.0,rhoi,fdij);
-    int mode = (*this).calc_dXijdri(r,rsq,del,fc,fcp,rhoi,fdij);
-    if(mode==0) {
-      fdij(1)=fdij(0);
-      fdij(2)=fdij(0);
-      fdij(0)*=del[0]/r;
-      fdij(1)*=del[1]/r;
-      fdij(2)*=del[2]/r;
-    }
+    (*this).calc_dXijdri(Zi, Zj,r,rsq,del,rhoi,fdij,scale);
   }
 
   /** \brief This is just a convenient wrapper.
-   *
-   * Note that this method should not to be used when 
-   * computational performance matter.
    */
-  void calc_rho_mb(Vec3d &del, rho_type &rhoi, double fc=1) {
+  void calc_rho_mb(const int Zi, const int Zj, Vec3d &del, rho_type &rhoi, double scale=1) {
     double rsq = del*del;
     double r = sqrt(rsq);
-    ((*this).calc_rho)(r,rsq,fc,del,rhoi);
+    ((*this).calc_rho)(Zi, Zj,r,rsq,del,rhoi,scale);
+  }
+  /** \brief Return size of the density array */
+  virtual size_t rhoi_size() {
+    return rhoisize;
   }
+  /** \brief Return size of the derivative of the embedding energy array */
+  virtual size_t rhoip_size() {
+    return rhoisize;
+  }
+  virtual size_t size() {
+    return s;
+  }
+  virtual std::string label()=0;
+  virtual size_t get_rfidx();
+  virtual void set_rfidx(size_t fidx_);
 };
 #endif
diff --git a/include/tadah/models/descriptors/dm/dm_blip.h b/include/tadah/models/descriptors/dm/dm_blip.h
index 6858552196e9ae097641f570ffda16c19d256a5f..9cfe441f5662b316e6d53f767f1f74219ceae5c1 100644
--- a/include/tadah/models/descriptors/dm/dm_blip.h
+++ b/include/tadah/models/descriptors/dm/dm_blip.h
@@ -20,8 +20,6 @@
  *
  * \ref SGRIDMB parameters control width \f$ \eta \f$ of the gaussian basis function.
  *
- * \ref AGRIDMB parameter specify maximum value for the angular momentum \f$L_{max}\f$.
- *
  * e.g. \f$L_{max}=2\f$ will calculate descriptors with \f$ L=0,1,2 \f$ (s,p,d orbitals).
  *
  * More information about this descriptor:
@@ -33,58 +31,56 @@
  * 4962–4967. https://doi.org/10.1021/acs.jpclett.9b02037</div>
  *
  * Required Config keys:
- * \ref INITMB \ref CGRIDMB \ref SGRIDMB \ref AGRIDMB
+ * \ref INITMB \ref CGRIDMB \ref SGRIDMB
  */
 class DM_Blip: public DM_Base {
-    private:
-        int Lmax=0;
-        size_t rhoisize=0;
-        size_t s=0;
-        std::string lab="DM_Blip";
-        v_type sgrid;
-        v_type cgrid;
-        std::vector<std::vector<std::vector<int>>> orbitals;
-        std::vector<std::vector<double>> fac;
-        size_t gen_atomic_orbitals(int L);
-        double fact(int n);
-        double my_pow(double,int);
-        int verbose;
-        double rc;
+private:
+  int Lmax=0;
+  std::string lab="DM_Blip";
+  v_type sgrid;
+  v_type cgrid;
+  std::vector<std::vector<std::vector<int>>> orbitals;
+  std::vector<std::vector<double>> fac;
+  size_t gen_atomic_orbitals(int L);
+  double fact(int n);
+  double my_pow(double,int);
+  double rc;
 
-    public:
-        DM_Blip(Config&);
-        void calc_aed(
-                rho_type& rho,
-                aed_type2 &aed);
-        int calc_dXijdri_dXjidri(
-                const double rij,
-                const double rij_sq,
-                const Vec3d &vec_ij,
-                const double fc_ij,
-                const double fcp_ij,
-                rho_type& rhoi,
-                rho_type& rhoj,
-                fd_type &fd_ij,
-                const double wi,
-                const double wj);
-        int calc_dXijdri(
-                const double rij,
-                const double rij_sq,
-                const Vec3d &vec_ij,
-                const double fc_ij,
-                const double fcp_ij,
-                rho_type& rhoi,
-                fd_type &fd_ij);
-        size_t size();
-        std::string label();
-        void init_rhoi(rho_type& rhoi);
-        void calc_rho(
-                const double rij,
-                const double rij_sq,
-                const double fc_ij,
-                const Vec3d &vec_ij,
-                rho_type& rho);
-        size_t rhoi_size();
-        size_t rhoip_size();
+public:
+  DM_Blip();
+  DM_Blip(Config&);
+  void calc_aed(
+    rho_type& rho,
+    aed_type &aed) override;
+  void calc_dXijdri_dXjidri(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    const Vec3d &vec_ij,
+    rho_type& rhoi,
+    rho_type& rhoj,
+    fd_type &fd_ij,
+    const double scale=1) override;
+  void calc_dXijdri(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    const Vec3d &vec_ij,
+    rho_type& rhoi,
+    fd_type &fd_ij, 
+    const double scale=1) override;
+  std::string label() override;
+  void init_rhoi(rho_type& rhoi) override;
+  void calc_rho(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    const Vec3d &vec_ij,
+    rho_type& rho,
+    const double scale=1) override;
+  void init() override;
 };
 #endif
diff --git a/include/tadah/models/descriptors/dm/dm_dummy.h b/include/tadah/models/descriptors/dm/dm_dummy.h
index 89c2f4b712183d9c4b34a8e19ab0a9851894bd2c..c85c4b56bc23deb2ea45cdffd0b6d4dedd32ecd1 100644
--- a/include/tadah/models/descriptors/dm/dm_dummy.h
+++ b/include/tadah/models/descriptors/dm/dm_dummy.h
@@ -10,44 +10,43 @@
  *
  */
 class DM_Dummy: public DM_Base {
-    private:
-        size_t s=0;
-        std::string lab="DM_Dummy";
-    public:
-        DM_Dummy();
-        DM_Dummy(Config&);
-        void calc_aed(
-                rho_type& rho,
-                aed_type2 &aed);
-        int calc_dXijdri_dXjidri(
-                const double rij,
-                const double rij_sq,
-                const Vec3d &vec_ij,
-                const double fc_ij,
-                const double fcp_ij,
-                rho_type& rhoi,
-                rho_type& rhoj,
-                fd_type &fd_ij,
-                const double wi,
-                const double wj);
-        int calc_dXijdri(
-                const double rij,
-                const double rij_sq,
-                const Vec3d &vec_ij,
-                const double fc_ij,
-                const double fcp_ij,
-                rho_type& rhoi,
-                fd_type &fd_ij);
-        size_t size();
-        std::string label();
-        void init_rhoi(rho_type& rhoi);
-        void calc_rho(
-                const double rij,
-                const double rij_sq,
-                const double fc_ij,
-                const Vec3d &vec_ij,
-                rho_type& rho);
-        size_t rhoi_size();
-        size_t rhoip_size();
+private:
+  std::string lab="DM_Dummy";
+public:
+  DM_Dummy();
+  DM_Dummy(Config&);
+  void calc_aed(
+    rho_type& rho,
+    aed_type &aed) override;
+  void calc_dXijdri_dXjidri(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    const Vec3d &vec_ij,
+    rho_type& rhoi,
+    rho_type& rhoj,
+    fd_type &fd_ij,
+    const double scale=1) override;
+  void calc_dXijdri(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    const Vec3d &vec_ij,
+    rho_type& rhoi,
+    fd_type &fd_ij, 
+    const double scale=1) override;
+  std::string label() override;
+  void init_rhoi(rho_type& rhoi) override;
+  void calc_rho(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    const Vec3d &vec_ij,
+    rho_type& rho,
+    const double scale=1) override;
+  void init() override;
 };
 #endif
diff --git a/include/tadah/models/descriptors/dm/dm_ead.h b/include/tadah/models/descriptors/dm/dm_ead.h
index 5975a1ff1df9590698b17ab23e865ddc728f95db..5d5dfbf0610237868373d5472216daad6bb04087 100644
--- a/include/tadah/models/descriptors/dm/dm_ead.h
+++ b/include/tadah/models/descriptors/dm/dm_ead.h
@@ -19,8 +19,6 @@
  *
  * \ref SGRIDMB parameters control width \f$ \eta \f$ of the gaussian basis function.
  *
- * \ref AGRIDMB parameter specify maximum value for the angular momentum \f$L_{max}\f$.
- *
  * e.g. \f$L_{max}=2\f$ will calculate descriptors with \f$ L=0,1,2 \f$ (s,p,d orbitals).
  *
  * More information about this descriptor:
@@ -32,58 +30,56 @@
  * 4962–4967. https://doi.org/10.1021/acs.jpclett.9b02037</div>
  *
  * Required Config keys:
- * \ref INITMB \ref CGRIDMB \ref SGRIDMB \ref AGRIDMB
+ * \ref INITMB \ref CGRIDMB \ref SGRIDMB
  */
 class DM_EAD: public DM_Base {
-    private:
-        int Lmax=0;
-        size_t rhoisize=0;
-        size_t s=0;
-        std::string lab="DM_EAD";
-        v_type sgrid;
-        v_type cgrid;
-        std::vector<std::vector<std::vector<int>>> orbitals;
-        std::vector<std::vector<double>> fac;
-        size_t gen_atomic_orbitals(int L);
-        double fact(int n);
-        double my_pow(double,int);
-        int verbose;
-        double rc;
+private:
+  int Lmax=0;
+  std::string lab="DM_EAD";
+  v_type sgrid;
+  v_type cgrid;
+  std::vector<std::vector<std::vector<int>>> orbitals;
+  std::vector<std::vector<double>> fac;
+  size_t gen_atomic_orbitals(int L);
+  double fact(int n);
+  double my_pow(double,int);
+  double rc;
 
-    public:
-        DM_EAD(Config&);
-        void calc_aed(
-                rho_type& rho,
-                aed_type2 &aed);
-        int calc_dXijdri_dXjidri(
-                const double rij,
-                const double rij_sq,
-                const Vec3d &vec_ij,
-                const double fc_ij,
-                const double fcp_ij,
-                rho_type& rhoi,
-                rho_type& rhoj,
-                fd_type &fd_ij,
-                const double wi,
-                const double wj);
-        int calc_dXijdri(
-                const double rij,
-                const double rij_sq,
-                const Vec3d &vec_ij,
-                const double fc_ij,
-                const double fcp_ij,
-                rho_type& rhoi,
-                fd_type &fd_ij);
-        size_t size();
-        std::string label();
-        void init_rhoi(rho_type& rhoi);
-        void calc_rho(
-                const double rij,
-                const double rij_sq,
-                const double fc_ij,
-                const Vec3d &vec_ij,
-                rho_type& rho);
-        size_t rhoi_size();
-        size_t rhoip_size();
+public:
+  DM_EAD();
+  DM_EAD(Config&);
+  void calc_aed(
+    rho_type& rho,
+    aed_type &aed) override;
+  void calc_dXijdri_dXjidri(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    const Vec3d &vec_ij,
+    rho_type& rhoi,
+    rho_type& rhoj,
+    fd_type &fd_ij,
+    const double scale=1) override;
+  void calc_dXijdri(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    const Vec3d &vec_ij,
+    rho_type& rhoi,
+    fd_type &fd_ij, 
+    const double scale=1) override;
+  std::string label() override;
+  void init_rhoi(rho_type& rhoi) override;
+  void calc_rho(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    const Vec3d &vec_ij,
+    rho_type& rho,
+    const double scale=1) override;
+  void init() override;
 };
 #endif
diff --git a/include/tadah/models/descriptors/dm/dm_eam.h b/include/tadah/models/descriptors/dm/dm_eam.h
index 5687a32d1c87a1777020603ec7636a676c263a85..b522bdf7dc906ab71c021b7f5a649dd9acf3463b 100644
--- a/include/tadah/models/descriptors/dm/dm_eam.h
+++ b/include/tadah/models/descriptors/dm/dm_eam.h
@@ -22,72 +22,71 @@
  * \ref INITMB \ref SETFL
  */
 class DM_EAM: public DM_Base {
-    private:
-        int verbose;
-        struct eam_file {
-            std::string file_path;
-            std::vector<double> frho;
-            std::vector<double> rhor;
-            std::vector<double> z2r;
-            int nrho=0;
-            double drho=0;
-            int nr;
-            double dr;
-            double rdr;
-            double rdrho;
-            double rcut;
-            int atomic_number;
-            double atomic_mass;
-            double lattice_param;
-            std::string lattice;
-            double rhomax;
+private:
+  struct eam_file {
+    std::string file_path;
+    std::vector<double> frho;
+    std::vector<double> rhor;
+    std::vector<double> z2r;
+    int nrho=0;
+    double drho=0;
+    int nr;
+    double dr;
+    double rdr;
+    double rdrho;
+    double rcut;
+    int atomic_number;
+    double atomic_mass;
+    double lattice_param;
+    std::string lattice;
+    double rhomax;
 
-        };
-        eam_file ef;
+  };
+  eam_file ef;
 
-        std::vector<std::vector<double>> frho_spline;
-        std::vector<std::vector<double>> rhor_spline;
-        std::vector<std::vector<double>> z2r_spline;
+  std::vector<std::vector<double>> frho_spline;
+  std::vector<std::vector<double>> rhor_spline;
+  std::vector<std::vector<double>> z2r_spline;
 
-        size_t s=1;
-        std::string lab="DM_EAM";
-        void read_setfl();
-        void gen_splines(int &n, double &delta, std::vector<double> &f, std::vector<std::vector<double>> &spline);
+  std::string lab="DM_EAM";
+  void read_setfl();
+  void gen_splines(int &n, double &delta, std::vector<double> &f, std::vector<std::vector<double>> &spline);
 
-    public:
-        DM_EAM(Config&);
-        void calc_aed(
-                rho_type& rho,
-                aed_type2 &aed);
-        int calc_dXijdri_dXjidri(
-                const double rij,
-                const double rij_sq,
-                const Vec3d &vec_ij,
-                const double fc_ij,
-                const double fcp_ij,
-                rho_type& rhoi,
-                rho_type& rhoj,
-                fd_type &fd_ij,
-                const double wi,
-                const double wj);
-        int calc_dXijdri(
-                const double rij,
-                const double rij_sq,
-                const Vec3d &vec_ij,
-                const double fc_ij,
-                const double fcp_ij,
-                rho_type& rhoi,
-                fd_type &fd_ij);
-        size_t size();
-        std::string label();
-        void init_rhoi(rho_type& rhoi);
-        void calc_rho(
-                const double rij,
-                const double rij_sq,
-                const double fc_ij,
-                const Vec3d &vec_ij,
-                rho_type& rho);
-        size_t rhoi_size();
-        size_t rhoip_size();
+public:
+  DM_EAM();
+  DM_EAM(Config&);
+  void calc_aed(
+    rho_type& rho,
+    aed_type &aed) override;
+  void calc_dXijdri_dXjidri(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    const Vec3d &vec_ij,
+    rho_type& rhoi,
+    rho_type& rhoj,
+    fd_type &fd_ij,
+    const double scale=1) override;
+  void calc_dXijdri(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    const Vec3d &vec_ij,
+    rho_type& rhoi,
+    fd_type &fd_ij, 
+    const double scale=1) override;
+  std::string label() override;
+  void init_rhoi(rho_type& rhoi) override;
+  void calc_rho(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    const Vec3d &vec_ij,
+    rho_type& rho,
+    const double scale=1) override;
+  void init() override;
 };
 #endif
diff --git a/include/tadah/models/descriptors/dm/dm_func.h b/include/tadah/models/descriptors/dm/dm_func.h
index a35abd7bf2d8f318a6f99c7b569557e863ba80c3..0a94484249aaf21798a0d56be105ac9107ea92e4 100644
--- a/include/tadah/models/descriptors/dm/dm_func.h
+++ b/include/tadah/models/descriptors/dm/dm_func.h
@@ -7,9 +7,9 @@
 #include <cstdlib>
 
 class F_Base {
-    public:
-        virtual double calc_F(double rho, size_t c)=0;
-        virtual double calc_dF(double rho, size_t c)=0;
+public:
+  virtual double calc_F(double rho, size_t c)=0;
+  virtual double calc_dF(double rho, size_t c)=0;
 };
 
 /**
@@ -25,17 +25,17 @@ class F_Base {
  * @note Ensure the configuration keys match the descriptor requirements.
  */
 class F_RLR:  public F_Base {
-    private:
-        Config *config=nullptr;
-        v_type sgrid;   // controls depth
-        v_type cgrid;   // control x-intercep at 1/c
-    public:
-        F_RLR(Config &conf);
-        F_RLR();
-        double calc_F(double rho,size_t c);
-        double calc_dF(double rho,size_t c);
-        //~F_RLR() {
-            // Do not delete config!
-        //}
+private:
+  Config *config=nullptr;
+  v_type sgrid;   // controls depth
+  v_type cgrid;   // control x-intercep at 1/c
+public:
+  F_RLR(Config &conf);
+  F_RLR();
+  double calc_F(double rho,size_t c);
+  double calc_dF(double rho,size_t c);
+  //~F_RLR() {
+  // Do not delete config!
+  //}
 };
 #endif
diff --git a/include/tadah/models/descriptors/dm/dm_mEAD.h b/include/tadah/models/descriptors/dm/dm_mEAD.h
index ec9733417818118b82cefbc7a9b9be7e3008db84..733ebce7d46514c87fcbf2b6e2fb726ecefbdb8d 100644
--- a/include/tadah/models/descriptors/dm/dm_mEAD.h
+++ b/include/tadah/models/descriptors/dm/dm_mEAD.h
@@ -28,7 +28,6 @@
  *
  * \ref SGRIDMB parameters control width \f$ \eta \f$ of the gaussian basis function.
  *
- * \ref AGRIDMB parameter specify maximum value for the angular momentum \f$L_{max}\f$.
  *
  * e.g. \f$L_{max}=2\f$ will calculate descriptors with \f$ L=0,1,2 \f$ (s,p,d orbitals).
  *
@@ -41,61 +40,59 @@
  * 4962–4967. https://doi.org/10.1021/acs.jpclett.9b02037</div>
  *
  * Required Config keys:
- * \ref INITMB \ref CGRIDMB \ref SGRIDMB \ref AGRIDMB
+ * \ref INITMB \ref CGRIDMB \ref SGRIDMB
  */
 template <typename F>
 class DM_mEAD: public DM_Base {
-    private:
-        int Lmax=0;
-        size_t rhoisize=0;
-        size_t s=0;
-        std::string lab="DM_mEAD";
-        v_type sgrid;
-        v_type cgrid;
-        std::vector<std::vector<std::vector<int>>> orbitals;
-        std::vector<std::vector<double>> fac;
-        size_t gen_atomic_orbitals(int L);
-        double fact(int n);
-        double my_pow(double,int);
-        int verbose;
-        double rc;
-        F emb;
+private:
+  int Lmax=0;
+  std::string lab="DM_mEAD";
+  v_type sgrid;
+  v_type cgrid;
+  std::vector<std::vector<std::vector<int>>> orbitals;
+  std::vector<std::vector<double>> fac;
+  size_t gen_atomic_orbitals(int L);
+  double fact(int n);
+  double my_pow(double,int);
+  double rc;
+  F emb;
 
-    public:
-        DM_mEAD(Config&);
-        void calc_aed(
-                rho_type& rho,
-                aed_type2 &aed);
-        int calc_dXijdri_dXjidri(
-                const double rij,
-                const double rij_sq,
-                const Vec3d &vec_ij,
-                const double fc_ij,
-                const double fcp_ij,
-                rho_type& rhoi,
-                rho_type& rhoj,
-                fd_type &fd_ij,
-                const double wi,
-                const double wj);
-        int calc_dXijdri(
-                const double rij,
-                const double rij_sq,
-                const Vec3d &vec_ij,
-                const double fc_ij,
-                const double fcp_ij,
-                rho_type& rhoi,
-                fd_type &fd_ij);
-        size_t size();
-        std::string label();
-        void init_rhoi(rho_type& rhoi);
-        void calc_rho(
-                const double rij,
-                const double rij_sq,
-                const double fc_ij,
-                const Vec3d &vec_ij,
-                rho_type& rho);
-        size_t rhoi_size();
-        size_t rhoip_size();
+public:
+  DM_mEAD();
+  DM_mEAD(Config&);
+  void calc_aed(
+    rho_type& rho,
+    aed_type &aed) override;
+  void calc_dXijdri_dXjidri(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    const Vec3d &vec_ij,
+    rho_type& rhoi,
+    rho_type& rhoj,
+    fd_type &fd_ij,
+    const double scale=1) override;
+  void calc_dXijdri(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    const Vec3d &vec_ij,
+    rho_type& rhoi,
+    fd_type &fd_ij, 
+    const double scale=1) override;
+  std::string label() override;
+  void init_rhoi(rho_type& rhoi) override;
+  void calc_rho(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    const Vec3d &vec_ij,
+    rho_type& rho,
+    const double scale=1) override;
+  void init() override;
 };
 
 #include "dm_mEAD.hpp"
diff --git a/include/tadah/models/descriptors/dm/dm_mEAD.hpp b/include/tadah/models/descriptors/dm/dm_mEAD.hpp
index ba17354ffc757cf3812410b024cd3c5c80e54756..b5493621e7d4de3725b8d4f913b32ac75592af48 100644
--- a/include/tadah/models/descriptors/dm/dm_mEAD.hpp
+++ b/include/tadah/models/descriptors/dm/dm_mEAD.hpp
@@ -1,316 +1,331 @@
-
 #include <tadah/models/descriptors/dm/dm_func.h>
 #include <tadah/models/descriptors/dm/dm_mEAD.h>
 #include <tadah/models/descriptors/d_basis_functions.h>
 
+template <typename F>
+DM_mEAD<F>::DM_mEAD() {
+  init();
+}
+
 /* Index ii iterates rhoi array
  * rhoi array is split 50/50 into density and derivative
  * of the embedding function
  */
 template <typename F>
-DM_mEAD<F>::DM_mEAD(Config &config):
-    verbose(config.get<int>("VERBOSE")),
-    rc(config.get<double>("RCUTMB")),
-    emb(config)
+DM_mEAD<F>::DM_mEAD(Config &config): DM_Base(config),
+  rc(config.get<double>("RCUTMBMAX")),
+  emb(config)
 {
 
-    if (!config.get<bool>("INITMB")) return;
-
-    get_grid(config,"CGRIDMB",cgrid);
-    get_grid(config,"SGRIDMB",sgrid);
-
-    if (cgrid.size()!=sgrid.size()) {
-        throw std::runtime_error("SGRID2B and CGRID2B arrays differ in size.\n");
-    }
-
-    Lmax = config.get<int>("AGRIDMB");
-    s=sgrid.size()*(Lmax+1);
-    rhoisize = gen_atomic_orbitals(Lmax);
-    rhoisize *= sgrid.size();
-
-    if (verbose) {
-        std::cout << std::endl;
-        std::cout << "SGRID (SGRIDMB): " << sgrid.size() << std::endl;
-        for (auto e:sgrid) std::cout << e << "  ";
-        std::cout << std::endl;
-
-        std::cout << "CGRID (CGRIDMB): " << cgrid.size() << std::endl;
-        for (auto L:cgrid) std::cout << L << "  ";
-        std::cout << std::endl;
-        std::cout << "rhoisize: " << rhoisize << std::endl;
-    }
+  if (!config.get<bool>("INITMB")) return;
+  init();
+
+  get_grid(config,"CGRIDMB",cgrid);
+  get_grid(config,"SGRIDMB",sgrid);
+  // update config with generated grid
+  config.remove("CGRIDMB");
+  for (const auto &i: cgrid) config.add("CGRIDMB",i);
+  config.remove("SGRIDMB");
+  for (const auto &i: sgrid) config.add("SGRIDMB",i);
+
+  if (cgrid.size()!=sgrid.size()) {
+    throw std::runtime_error("SGRID2B and CGRID2B arrays differ in size.\n");
+  }
+
+  Lmax = config.get<int>("TYPEMB",1);
+  s=sgrid.size()*(Lmax+1);
+  rhoisize = gen_atomic_orbitals(Lmax);
+  rhoisize *= sgrid.size();
+
+  if (verbose) {
+    std::cout << std::endl;
+    std::cout << "SGRID (SGRIDMB): " << sgrid.size() << std::endl;
+    for (auto e:sgrid) std::cout << e << "  ";
+    std::cout << std::endl;
+
+    std::cout << "CGRID (CGRIDMB): " << cgrid.size() << std::endl;
+    for (auto L:cgrid) std::cout << L << "  ";
+    std::cout << std::endl;
+    std::cout << "rhoisize: " << rhoisize << std::endl;
+  }
+  auto init_atoms = get_init_atoms(config);
+  init_for_atoms(init_atoms);
 }
 
 template <typename F>
 void DM_mEAD<F>::calc_aed(
-        rho_type& rhoi,
-        aed_type2 &aed)
+  rho_type& rhoi,
+  aed_type &aed)
 {
-    size_t Lshift = 0;
-    size_t ii=0;
-    for (int L=0; L<=Lmax; ++L) {
-        for (size_t o=0; o<orbitals[L].size(); ++o) {
-            const double f = fac[L][o];
-            for (size_t c=0; c<cgrid.size(); c++) {
-                aed(c+Lshift+fidx) += f*emb.calc_F(rhoi(ii),c);
-                rhoi(ii+rhoisize) = f*emb.calc_dF(rhoi(ii),c);
-                ii++;
-            }
-        }
-        Lshift += sgrid.size();
+  size_t Lshift = 0;
+  size_t ii=rfidx;
+  for (int L=0; L<=Lmax; ++L) {
+    for (size_t o=0; o<orbitals[L].size(); ++o) {
+      const double f = fac[L][o];
+      for (size_t c=0; c<cgrid.size(); c++) {
+        aed(c+Lshift+fidx) += f*emb.calc_F(rhoi(ii),c);
+        rhoi(ii+rhoisize) = f*emb.calc_dF(rhoi(ii),c);
+        ii++;
+      }
     }
+    Lshift += sgrid.size();
+  }
 }
 template <typename F>
-int DM_mEAD<F>::calc_dXijdri_dXjidri(
-        const double rij,
-        const double,
-        const Vec3d &vec_ij,
-        const double fc_ij,
-        const double fcp_ij,
-        rho_type& rhoi,
-        rho_type& rhoj,
-        fd_type &fd_ij,
-        const double wi,
-        const double wj)
+void DM_mEAD<F>::calc_dXijdri_dXjidri(
+  const int Zi,
+  const int Zj,
+  const double rij,
+  const double,
+  const Vec3d &vec_ij,
+  rho_type& rhoi,
+  rho_type& rhoj,
+  fd_type &fd_ij,
+  const double scale)
 {
-    const double x = vec_ij[0];
-    const double y = vec_ij[1];
-    const double z = vec_ij[2];
-
-    size_t Lshift = 0;
-
-	size_t ii=rhoisize;
-    for (int L=0; L<=Lmax; ++L) {
-        for (size_t o=0; o<orbitals[L].size(); ++o) {
-            const int lx = orbitals[L][o][0];
-            const int ly = orbitals[L][o][1];
-            const int lz = orbitals[L][o][2];
-
-            const double powxlxij = my_pow(x,lx);
-            const double powylyij = my_pow(y,ly);
-            const double powzlzij = my_pow(z,lz);
-
-            const double powxlxji = my_pow(-x,lx);
-            const double powylyji = my_pow(-y,ly);
-            const double powzlzji = my_pow(-z,lz);
-
-            double txij=0.0,tyij=0.0,tzij=0.0;
-            double txji=0.0,tyji=0.0,tzji=0.0;
-            if (lx!=0) {
-                txij = lx*my_pow(x,lx-1)*powylyij*powzlzij;
-                txji = lx*my_pow(-x,lx-1)*powylyji*powzlzji;
-            }
-            if (ly!=0) {
-                tyij = ly*my_pow(y,ly-1)*powxlxij*powzlzij;
-                tyji = ly*my_pow(-y,ly-1)*powxlxji*powzlzji;
-            }
-            if (lz!=0) {
-                tzij = lz*my_pow(z,lz-1)*powylyij*powxlxij;
-                tzji = lz*my_pow(-z,lz-1)*powylyji*powxlxji;
-            }
-            const double pqrij = powxlxij*powylyij*powzlzij;
-            const double pqrji = powxlxji*powylyji*powzlzji;
-
-            for (size_t c=0; c<cgrid.size(); c++) {
-                double bf= G(rij,sgrid[c],cgrid[c],fc_ij);
-                double dbf= dG(rij,sgrid[c],cgrid[c],fc_ij,fcp_ij);
-
-                    const double term2ijx=pqrij*dbf*x/rij;
-                    const double term2ijy=pqrij*dbf*y/rij;
-                    const double term2ijz=pqrij*dbf*z/rij;
-
-                    const double term2jix=-pqrji*dbf*x/rij;
-                    const double term2jiy=-pqrji*dbf*y/rij;
-                    const double term2jiz=-pqrji*dbf*z/rij;
-
-                    const double term1ijx = bf*txij;
-                    const double term1ijy = bf*tyij;
-                    const double term1ijz = bf*tzij;
-
-                    const double term1jix = bf*txji;
-                    const double term1jiy = bf*tyji;
-                    const double term1jiz = bf*tzji;
-
-                    fd_ij(c+Lshift+fidx,0) +=
-                         rhoi(ii)*(term1ijx + term2ijx)*wj
-                        -rhoj(ii)*(term1jix + term2jix)*wi
-                        ;
-
-                    fd_ij(c+Lshift+fidx,1) +=
-                         rhoi(ii)*(term1ijy + term2ijy)*wj
-                        -rhoj(ii)*(term1jiy + term2jiy)*wi
-                        ;
-
-                    fd_ij(c+Lshift+fidx,2) += 
-                         rhoi(ii)*(term1ijz + term2ijz)*wj
-                        -rhoj(ii)*(term1jiz + term2jiz)*wi
-                        ;
-                    ii++;
-            }
-        }
-        Lshift+=sgrid.size();
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
+  const double x = vec_ij[0];
+  const double y = vec_ij[1];
+  const double z = vec_ij[2];
+
+  size_t Lshift = 0;
+
+  double wi = weights[Zi];
+  double wj = weights[Zj];
+  double fc_ij = fcut->calc(rij);
+  double fcp_ij = fcut->calc_prime(rij);
+
+  size_t ii=rhoisize+rfidx;
+  for (int L=0; L<=Lmax; ++L) {
+    for (size_t o=0; o<orbitals[L].size(); ++o) {
+      const int lx = orbitals[L][o][0];
+      const int ly = orbitals[L][o][1];
+      const int lz = orbitals[L][o][2];
+
+      const double powxlxij = my_pow(x,lx);
+      const double powylyij = my_pow(y,ly);
+      const double powzlzij = my_pow(z,lz);
+
+      const double powxlxji = my_pow(-x,lx);
+      const double powylyji = my_pow(-y,ly);
+      const double powzlzji = my_pow(-z,lz);
+
+      double txij=0.0,tyij=0.0,tzij=0.0;
+      double txji=0.0,tyji=0.0,tzji=0.0;
+      if (lx!=0) {
+        txij = lx*my_pow(x,lx-1)*powylyij*powzlzij;
+        txji = lx*my_pow(-x,lx-1)*powylyji*powzlzji;
+      }
+      if (ly!=0) {
+        tyij = ly*my_pow(y,ly-1)*powxlxij*powzlzij;
+        tyji = ly*my_pow(-y,ly-1)*powxlxji*powzlzji;
+      }
+      if (lz!=0) {
+        tzij = lz*my_pow(z,lz-1)*powylyij*powxlxij;
+        tzji = lz*my_pow(-z,lz-1)*powylyji*powxlxji;
+      }
+      const double pqrij = powxlxij*powylyij*powzlzij;
+      const double pqrji = powxlxji*powylyji*powzlzji;
+
+      for (size_t c=0; c<cgrid.size(); c++) {
+        double bf= G(rij,sgrid[c],cgrid[c],fc_ij);
+        double dbf= dG(rij,sgrid[c],cgrid[c],fc_ij,fcp_ij);
+
+        const double term2ijx=pqrij*dbf*x/rij;
+        const double term2ijy=pqrij*dbf*y/rij;
+        const double term2ijz=pqrij*dbf*z/rij;
+
+        const double term2jix=-pqrji*dbf*x/rij;
+        const double term2jiy=-pqrji*dbf*y/rij;
+        const double term2jiz=-pqrji*dbf*z/rij;
+
+        const double term1ijx = bf*txij;
+        const double term1ijy = bf*tyij;
+        const double term1ijz = bf*tzij;
+
+        const double term1jix = bf*txji;
+        const double term1jiy = bf*tyji;
+        const double term1jiz = bf*tzji;
+
+        fd_ij(c+Lshift+fidx,0) += scale*(
+          rhoi(ii)*(term1ijx + term2ijx)*wj
+          -rhoj(ii)*(term1jix + term2jix)*wi)
+        ;
+
+        fd_ij(c+Lshift+fidx,1) += scale*(
+          rhoi(ii)*(term1ijy + term2ijy)*wj
+          -rhoj(ii)*(term1jiy + term2jiy)*wi)
+        ;
+
+        fd_ij(c+Lshift+fidx,2) += scale*(
+          rhoi(ii)*(term1ijz + term2ijz)*wj
+          -rhoj(ii)*(term1jiz + term2jiz)*wi)
+        ;
+        ii++;
+      }
     }
-
-    return 1;
-
+    Lshift+=sgrid.size();
+  }
 }
 template <typename F>
-int DM_mEAD<F>::calc_dXijdri(
-        const double rij,
-        const double,
-        const Vec3d &vec_ij,
-        const double fc_ij,
-        const double fcp_ij,
-        rho_type& rhoi,
-        fd_type &fd_ij)
+void DM_mEAD<F>::calc_dXijdri(
+  const int Zi,
+  const int Zj,
+  const double rij,
+  const double,
+  const Vec3d &vec_ij,
+  rho_type& rhoi,
+  fd_type &fd_ij,
+  const double scale)
 {
-    const double x = vec_ij[0];
-    const double y = vec_ij[1];
-    const double z = vec_ij[2];
-
-    size_t Lshift = 0;
-
-    size_t ii=rhoisize;
-    for (int L=0; L<=Lmax; ++L) {
-        for (size_t o=0; o<orbitals[L].size(); ++o) {
-            const int lx = orbitals[L][o][0];
-            const int ly = orbitals[L][o][1];
-            const int lz = orbitals[L][o][2];
-
-            const double powxlxij = my_pow(x,lx);
-            const double powylyij = my_pow(y,ly);
-            const double powzlzij = my_pow(z,lz);
-
-            double txij=0.0,tyij=0.0,tzij=0.0;
-            if (lx!=0) {
-                txij = lx*my_pow(x,lx-1)*powylyij*powzlzij;
-            }
-            if (ly!=0) {
-                tyij = ly*my_pow(y,ly-1)*powxlxij*powzlzij;
-            }
-            if (lz!=0) {
-                tzij = lz*my_pow(z,lz-1)*powylyij*powxlxij;
-            }
-            const double pqrij = powxlxij*powylyij*powzlzij;
-
-            for (size_t c=0; c<cgrid.size(); c++) {
-                double bf= G(rij,sgrid[c],cgrid[c],fc_ij);
-                double dbf= dG(rij,sgrid[c],cgrid[c],fc_ij,fcp_ij);
-
-                    const double term2ijx=pqrij*dbf*x/rij;
-                    const double term2ijy=pqrij*dbf*y/rij;
-                    const double term2ijz=pqrij*dbf*z/rij;
-
-                    const double term1ijx = bf*txij;
-                    const double term1ijy = bf*tyij;
-                    const double term1ijz = bf*tzij;
-
-                    fd_ij(c+Lshift+fidx,0) +=
-                         rhoi(ii)*(term1ijx + term2ijx)
-                        ;
-
-                    fd_ij(c+Lshift+fidx,1) +=
-                         rhoi(ii)*(term1ijy + term2ijy)
-                        ;
-
-                    fd_ij(c+Lshift+fidx,2) += 
-                         rhoi(ii)*(term1ijz + term2ijz)
-                        ;
-                    ii++;
-            }
-        }
-        Lshift+=sgrid.size();
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
+  const double x = vec_ij[0];
+  const double y = vec_ij[1];
+  const double z = vec_ij[2];
+
+  double wj = weights[Zj];
+  double fc_ij = scale*wj*fcut->calc(rij);
+  double fcp_ij = scale*wj*fcut->calc_prime(rij);
+
+  size_t Lshift = 0;
+
+  size_t ii=rhoisize+rfidx;
+  for (int L=0; L<=Lmax; ++L) {
+    for (size_t o=0; o<orbitals[L].size(); ++o) {
+      const int lx = orbitals[L][o][0];
+      const int ly = orbitals[L][o][1];
+      const int lz = orbitals[L][o][2];
+
+      const double powxlxij = my_pow(x,lx);
+      const double powylyij = my_pow(y,ly);
+      const double powzlzij = my_pow(z,lz);
+
+      double txij=0.0,tyij=0.0,tzij=0.0;
+      if (lx!=0) {
+        txij = lx*my_pow(x,lx-1)*powylyij*powzlzij;
+      }
+      if (ly!=0) {
+        tyij = ly*my_pow(y,ly-1)*powxlxij*powzlzij;
+      }
+      if (lz!=0) {
+        tzij = lz*my_pow(z,lz-1)*powylyij*powxlxij;
+      }
+      const double pqrij = powxlxij*powylyij*powzlzij;
+
+      for (size_t c=0; c<cgrid.size(); c++) {
+        double bf= G(rij,sgrid[c],cgrid[c],fc_ij);
+        double dbf= dG(rij,sgrid[c],cgrid[c],fc_ij,fcp_ij);
+
+        const double term2ijx=pqrij*dbf*x/rij;
+        const double term2ijy=pqrij*dbf*y/rij;
+        const double term2ijz=pqrij*dbf*z/rij;
+
+        const double term1ijx = bf*txij;
+        const double term1ijy = bf*tyij;
+        const double term1ijz = bf*tzij;
+
+        fd_ij(c+Lshift+fidx,0) +=
+          rhoi(ii)*(term1ijx + term2ijx)
+        ;
+
+        fd_ij(c+Lshift+fidx,1) +=
+          rhoi(ii)*(term1ijy + term2ijy)
+        ;
+
+        fd_ij(c+Lshift+fidx,2) += 
+          rhoi(ii)*(term1ijz + term2ijz)
+        ;
+        ii++;
+      }
     }
-
-    return 1;
-
-}
-template <typename F>
-size_t DM_mEAD<F>::size() {
-    return s;
+    Lshift+=sgrid.size();
+  }
 }
 template <typename F>
 std::string DM_mEAD<F>::label() {
-    return lab;
+  return lab;
 }
 
 template <typename F>
 void DM_mEAD<F>::init_rhoi(rho_type& rhoi)
 {
-    rhoi.resize(2*rhoisize);
-    rhoi.set_zero();
+  rhoi.resize(2*rhoisize);
+  rhoi.set_zero();
 }
 template <typename F>
 void DM_mEAD<F>::calc_rho(
-        const double rij,
-        const double,
-        const double fc_ij,
-        const Vec3d &vec_ij,
-        rho_type& rhoi)
+  const int Zi,
+  const int Zj,
+  const double rij,
+  const double,
+  const Vec3d &vec_ij,
+  rho_type& rhoi,
+  const double scale)
 {
-    size_t ii=0;
-    for (int L=0; L<=Lmax; ++L) {
-        for (size_t o=0; o<orbitals[L].size(); ++o) {
-            const size_t lx = orbitals[L][o][0];
-            const size_t ly = orbitals[L][o][1];
-            const size_t lz = orbitals[L][o][2];
-            double t = (my_pow(vec_ij[0],lx)*my_pow(vec_ij[1],ly)*my_pow(vec_ij[2],lz));
-            for (size_t c=0; c<cgrid.size(); c++) {
-                rhoi(ii++) += G(rij,sgrid[c],cgrid[c],fc_ij)*t;
-            }
-        }
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
+  size_t ii=rfidx;
+  double fc_ij = scale*weights[Zj]*fcut->calc(rij);
+  for (int L=0; L<=Lmax; ++L) {
+    for (size_t o=0; o<orbitals[L].size(); ++o) {
+      const size_t lx = orbitals[L][o][0];
+      const size_t ly = orbitals[L][o][1];
+      const size_t lz = orbitals[L][o][2];
+      double t = (my_pow(vec_ij[0],lx)*my_pow(vec_ij[1],ly)*my_pow(vec_ij[2],lz));
+      for (size_t c=0; c<cgrid.size(); c++) {
+        rhoi(ii++) += G(rij,sgrid[c],cgrid[c],fc_ij)*t;
+      }
     }
+  }
 }
 
 template <typename F>
 size_t DM_mEAD<F>::gen_atomic_orbitals(int Lmax) {
-    orbitals.resize(Lmax+1);
-    fac.resize(Lmax+1);
-
-    size_t count = 0;
-    for (int L = 0; L <= Lmax; ++L) {
-        for (int lx = 0; lx <= L; ++lx) {
-            for (int ly = 0; ly <= L; ++ly) {
-                for (int lz = 0; lz <= L; ++lz) {
-                    if (lx+ly+lz == L) {
-                        std::vector<int> o = {lx, ly, lz};
-                        orbitals[L].push_back(o);
-                        const double f = fact(L)/(fact(lx)*fact(ly)*fact(lz))/my_pow(4*rc,L);
-                        fac[L].push_back(f);
-                        count++;
-                    }
-                }
-            }
+  orbitals.resize(Lmax+1);
+  fac.resize(Lmax+1);
+
+  size_t count = 0;
+  for (int L = 0; L <= Lmax; ++L) {
+    for (int lx = 0; lx <= L; ++lx) {
+      for (int ly = 0; ly <= L; ++ly) {
+        for (int lz = 0; lz <= L; ++lz) {
+          if (lx+ly+lz == L) {
+            std::vector<int> o = {lx, ly, lz};
+            orbitals[L].push_back(o);
+            const double f = fact(L)/(fact(lx)*fact(ly)*fact(lz))/my_pow(4*rc,L);
+            fac[L].push_back(f);
+            count++;
+          }
         }
+      }
     }
-    return count;
+  }
+  return count;
 }
 template <typename F>
 double DM_mEAD<F>::fact(int n)
 {
-    double f=1.0;
-    while (n) {
-        f *= n;
-        --n;
-    }
-    return f;
+  double f=1.0;
+  while (n) {
+    f *= n;
+    --n;
+  }
+  return f;
 }
 template <typename F>
 double DM_mEAD<F>::my_pow(double x, int n){
-    double r = 1.0;
+  double r = 1.0;
 
-    while(n > 0){
-        r *= x;
-        --n;
-    }
-    return r;
+  while(n > 0){
+    r *= x;
+    --n;
+  }
+  return r;
 }
 template <typename F>
-size_t DM_mEAD<F>::rhoi_size() {
-    return rhoisize;
+void DM_mEAD<F>::init() {
+  keys.push_back("CGRIDMB");
+  keys.push_back("SGRIDMB");
+  keys.push_back("CEMBFUNC");
+  keys.push_back("SEMBFUNC");
+  nparams=5;
 }
-template <typename F>
-size_t DM_mEAD<F>::rhoip_size() {
-    return rhoisize;
-}
-
diff --git a/include/tadah/models/descriptors/dm/dm_mjoin.h b/include/tadah/models/descriptors/dm/dm_mjoin.h
new file mode 100644
index 0000000000000000000000000000000000000000..164adf93cae87d0a954dd8a73e4fafb0068aec9f
--- /dev/null
+++ b/include/tadah/models/descriptors/dm/dm_mjoin.h
@@ -0,0 +1,92 @@
+#ifndef DM_MJOIN_H
+#define DM_MJOIN_H
+
+#include "tadah/core/registry.h"
+#include <cstddef>
+#include <tadah/models/descriptors/dm/dm_base.h>
+#include <tadah/models/descriptors/d_mjoin.h>
+
+/** \brief Meta many-body descriptor for combining multiple DM descriptors.
+ *
+ * This descriptor provides an interface for concatenating various many-body descriptors.
+ * The resulting descriptor can then be used by Tadah! like any standard many-body descriptor.
+ *
+ * Each descriptor must have a specified type in a configuration file, along with a cutoff function, 
+ * cutoff distance, and other optional keys that are typically expected for this descriptor,
+ * such as SGRIDMB and CGRIDMB.
+ *
+ * When listing descriptors under the TYPEMB key, include parameters relevant to this descriptor.
+ *
+ * Here is an example of configuring these descriptors:
+ * 
+ * ```
+ * TYPEMB    DM_mJoin         # Meta descriptor for concatenating many-body descriptors
+ * TYPEMB    DM_EAD 1 5 5     # L number, cgrid, sgrid
+ * RCTYPEMB  Cut_Cos
+ * RCUTMB    3.0
+ * CGRIDMB   -1 5 0 3.0       # Grid for DM_EAD, blips centers, auto-generated
+ * SGRIDMB   -2 5 1.0 10.0    # Grid for DM_EAD, blips widths, auto-generated
+ * 
+ * TYPEMB    DM_Blip 0 7 7    # L number, cgrid, sgrid
+ * RCTYPEMB  Cut_Tanh
+ * RCUTMB    7.5
+ * SGRIDMB   -2 7 0.1 10      # Grid for DM_Blip, blips widths, auto-generated
+ * CGRIDMB   0 0 0 0 0 0 0    # Grid for DM_Blip, blips centers
+ * ```
+ *
+ * Note: Grids can be specified on a single line, and the order of the grids should match the order of descriptors.
+ *
+ * There is no limit to the number of descriptors that can be concatenated.
+ *
+ * \details
+ * - Ensure the types and grids are correctly specified in the configuration file.
+ * - The cutoff functions (RCTYPEMB) and distances (RCUTMB) must be defined for each descriptor.
+ * - Both SGRIDMB and CGRIDMB should be included if relevant, with their sizes matching the given descriptors.
+ */
+class DM_mJoin : public DM_Base, public D_mJoin {
+private:
+  std::vector<DM_Base*> ds;
+  std::vector<Config> configs;
+  std::string lab = "DM_mJoin";
+public:
+  DM_mJoin();
+  ~DM_mJoin();
+  DM_mJoin(Config &c);
+  void calc_aed(
+    rho_type& rho,
+    aed_type &aed) override;
+  void calc_dXijdri_dXjidri(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    const Vec3d &vec_ij,
+    rho_type& rhoi,
+    rho_type& rhoj,
+    fd_type &fd_ij,
+    const double scale=1) override;
+  void calc_dXijdri(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    const Vec3d &vec_ij,
+    rho_type& rhoi,
+    fd_type &fd_ij, 
+    const double scale=1) override;
+  std::string label() override;
+  void init_rhoi(rho_type& rhoi) override;
+  void calc_rho(
+    const int Zi,
+    const int Zj,
+    const double rij,
+    const double rij_sq,
+    const Vec3d &vec_ij,
+    rho_type& rho,
+    const double scale=1) override;
+  void set_fidx(size_t fidx_) override;
+  void set_rfidx(size_t rfidx_) override;
+  void init() override;
+};
+
+#endif
diff --git a/include/tadah/models/ekm.h b/include/tadah/models/ekm.h
index 9a07ced93a9992ce279bfd7c94e5ecaea5a28010..fa4540fde674435e607deab4732aaaaff093c34c 100644
--- a/include/tadah/models/ekm.h
+++ b/include/tadah/models/ekm.h
@@ -126,8 +126,8 @@ class EKM {
             delete[] c;
 
         }
-        aed_type2 project(aed_type2 &aed, Matrix &basis) {
-            aed_type2 temp(basis.cols());
+        aed_type project(aed_type &aed, Matrix &basis) {
+            aed_type temp(basis.cols());
             for (size_t b=0; b<basis.cols(); b++) {
                 temp(b) = kernel(aed,basis[b]);
             }
diff --git a/include/tadah/models/functions/basis_functions/bf_base.h b/include/tadah/models/functions/basis_functions/bf_base.h
index c68da2165b4f6a67c8ee3b6b91da4f9dc23b602f..c73c6f0fc2b8cd4963a3f14e7abcb17624ccc5b4 100644
--- a/include/tadah/models/functions/basis_functions/bf_base.h
+++ b/include/tadah/models/functions/basis_functions/bf_base.h
@@ -8,11 +8,14 @@
 
 /** \brief Base class to be used by all basis functions */
 struct BF_Base: public virtual Function_Base {
-    virtual ~BF_Base();
-    virtual double epredict(const t_type &, const aed_type2& ) const=0;
-    virtual double fpredict(const t_type &, const fd_type &,
-            const aed_type2& , const size_t ) const=0;
-    virtual force_type fpredict(const t_type &, const fd_type &,
-            const aed_type2& ) const=0;
+  BF_Base();
+  BF_Base(const Config &c);
+  virtual ~BF_Base();
+
+  // virtual double epredict(const t_type &, const aed_type& ) const=0;
+  // virtual double fpredict(const t_type &, const fd_type &,
+  //         const aed_type& , const size_t ) const=0;
+  // virtual force_type fpredict(const t_type &, const fd_type &,
+  //         const aed_type& ) const=0;
 };
 #endif
diff --git a/include/tadah/models/functions/basis_functions/bf_linear.h b/include/tadah/models/functions/basis_functions/bf_linear.h
index ca3609797fb182f0e396132a22e7b44ef56297d4..0fe0b70bdd93f2d22aa07b23d2eb5605265e4951 100644
--- a/include/tadah/models/functions/basis_functions/bf_linear.h
+++ b/include/tadah/models/functions/basis_functions/bf_linear.h
@@ -24,7 +24,7 @@ struct BF_Linear : public virtual BF_Base {
      * @brief Retrieve the label of the basis function.
      * @return String label of the basis function.
      */
-    std::string get_label() const;
+    std::string get_label() const override;
 
     std::string label = "BF_Linear"; ///< Label identifying the basis function.
 
@@ -34,7 +34,7 @@ struct BF_Linear : public virtual BF_Base {
      * @param aed Atomic descriptors.
      * @return Predicted energy.
      */
-    double epredict(const t_type &weights, const aed_type2 &aed) const;
+    double epredict(const t_type &weights, const aed_type &aed) const override;
 
     /**
      * @brief Predicts force for a specific dimension using the linear basis function.
@@ -45,7 +45,7 @@ struct BF_Linear : public virtual BF_Base {
      * @return Predicted force component.
      */
     double fpredict(const t_type &weights, const fd_type &fdij,
-                    const aed_type2 &aedi, const size_t k) const;
+                    const aed_type &aedi, const size_t k) const override;
 
     /**
      * @brief Predicts forces using the linear basis function.
@@ -55,6 +55,6 @@ struct BF_Linear : public virtual BF_Base {
      * @return Predicted forces.
      */
     force_type fpredict(const t_type &weights, const fd_type &fdij,
-                        const aed_type2 &aedi) const;
+                        const aed_type &aedi) const override;
 };
 #endif
diff --git a/include/tadah/models/functions/basis_functions/bf_polynomial2.h b/include/tadah/models/functions/basis_functions/bf_polynomial2.h
index f8dcd9f6d29f69e47bbf20a6a4160c0a71a47489..150fc6e3694a200d924678d814043e3047c22964 100644
--- a/include/tadah/models/functions/basis_functions/bf_polynomial2.h
+++ b/include/tadah/models/functions/basis_functions/bf_polynomial2.h
@@ -26,7 +26,7 @@ struct BF_Polynomial2 : public virtual BF_Base {
      * @brief Retrieve the label of the basis function.
      * @return String label of the basis function.
      */
-    std::string get_label() const;
+    std::string get_label() const override;
 
     /**
      * @brief Predicts energy using the polynomial basis function.
@@ -34,7 +34,7 @@ struct BF_Polynomial2 : public virtual BF_Base {
      * @param aed Atomic descriptors.
      * @return Predicted energy.
      */
-    double epredict(const t_type &weights, const aed_type2 &aed) const;
+    double epredict(const t_type &weights, const aed_type &aed) const override;
 
     /**
      * @brief Predicts force for a specific dimension using the polynomial basis function.
@@ -45,7 +45,7 @@ struct BF_Polynomial2 : public virtual BF_Base {
      * @return Predicted force component.
      */
     double fpredict(const t_type &weights, const fd_type &fdij,
-                    const aed_type2 &aedi, const size_t k) const;
+                    const aed_type &aedi, const size_t k) const override;
 
     /**
      * @brief Predicts forces using the polynomial basis function.
@@ -55,6 +55,6 @@ struct BF_Polynomial2 : public virtual BF_Base {
      * @return Predicted forces.
      */
     force_type fpredict(const t_type &weights, const fd_type &fdij,
-                        const aed_type2 &aedi) const;
+                        const aed_type &aedi) const override;
 };
 #endif
diff --git a/include/tadah/models/functions/function_base.h b/include/tadah/models/functions/function_base.h
index 01d73b7d9789b40d38ab52fed24dd0605a690024..468037fc67eb8e4a5f9cdc061f5cdc7a74f9fc3f 100644
--- a/include/tadah/models/functions/function_base.h
+++ b/include/tadah/models/functions/function_base.h
@@ -8,35 +8,31 @@
 /** Base class for Kernels and Basis Functions */
 struct Function_Base {
 
-    private:
-        int verbose;
-
-    public:
-        Function_Base() {
-            verbose=0;
-        }
-        Function_Base(const Config &c) {
-            verbose = c.get<int>("VERBOSE");
-        }
-        void set_verbose(int v) { verbose=v; }
-        int get_verbose() { return verbose; }
-
-        // Derived classes must implement Derived() and Derived(Config)
-        virtual ~Function_Base() {};
-
-        // For Kernels
-        virtual double operator() (const aed_type2& , const aed_type2& ) { return 0.0; };
-        virtual aed_type2 derivative(const aed_type2& , const aed_type2& ) { return aed_type2(); };
-        virtual double prime(const aed_type2& , const aed_type2& ,
-                const aed_type2& ) { return 0.0; }
-        virtual void set_basis(const Matrix ) {};
-
-        /** \brief Return label of this object */
-        virtual std::string get_label() const=0;
-        virtual double epredict(const t_type &, const aed_type2& ) const=0;
-        virtual double fpredict(const t_type &, const fd_type &,
-                const aed_type2&, const size_t ) const=0;
-        virtual force_type fpredict(const t_type &, const fd_type &,
-                const aed_type2& ) const=0;
+private:
+  int verbose;
+
+public:
+  Function_Base();
+  Function_Base(const Config &c);
+  void set_verbose(int v);
+  int get_verbose();
+
+  // Derived classes must implement Derived() and Derived(Config)
+  virtual ~Function_Base();
+
+  // For Kernels
+  virtual double operator() (const aed_type& , const aed_type& ) const;
+  virtual aed_type derivative(const aed_type& , const aed_type& ) const;
+  virtual double prime(const aed_type& , const aed_type& ,
+                       const aed_type& ) const;
+  virtual void set_basis(const Matrix );
+
+  /** \brief Return label of this object */
+  virtual std::string get_label() const=0;
+  virtual double epredict(const t_type &, const aed_type& ) const=0;
+  virtual double fpredict(const t_type &, const fd_type &,
+                          const aed_type&, const size_t ) const=0;
+  virtual force_type fpredict(const t_type &, const fd_type &,
+                              const aed_type& ) const=0;
 };
 #endif
diff --git a/include/tadah/models/functions/kernels/kern_base.h b/include/tadah/models/functions/kernels/kern_base.h
index 41da02ea2f2218cdf862763fc03aabf32f8fa25a..21a0a81e7c6ccadf1fd32512cff4ba0cb31419f1 100644
--- a/include/tadah/models/functions/kernels/kern_base.h
+++ b/include/tadah/models/functions/kernels/kern_base.h
@@ -16,23 +16,25 @@
 class Kern_Base: public virtual Function_Base {
     public:
         Matrix basis;
+        Kern_Base();
+        Kern_Base(const Config &c);
         virtual ~Kern_Base();
 
-        /**
-         * Calculate kernel value given two vectors
-         *
-         * - b = basis vector
-         * - af = atomic energy descriptor
-         */
-        virtual double operator() (const aed_type2 &b, const aed_type2 &af) const=0;
-
-        /**
-         * Calculate the kernel derivative wrt to the second argument
-         *
-         * - b = basis vector
-         * - af = atomic energy descriptor
-         */
-        virtual aed_type2 derivative(const aed_type2 &b, const aed_type2 &af) const=0;
+        // /**
+        //  * Calculate kernel value given two vectors
+        //  *
+        //  * - b = basis vector
+        //  * - af = atomic energy descriptor
+        //  */
+        virtual double operator() (const aed_type &b, const aed_type &af) const=0;
+        //
+        // /**
+        //  * Calculate the kernel derivative wrt to the second argument
+        //  *
+        //  * - b = basis vector
+        //  * - af = atomic energy descriptor
+        //  */
+        virtual aed_type derivative(const aed_type &b, const aed_type &af) const=0;
 
         /**
          * Calculate inner product of the kernel derivative
@@ -43,8 +45,8 @@ class Kern_Base: public virtual Function_Base {
          * - ff = force descriptor (TODO i-th dir component of it)
          *   TODO consider calculating all 3-directions at once
          */
-        virtual double prime(const aed_type2 &b, const aed_type2 &af,
-                const aed_type2 &ff) const=0;
+        virtual double prime(const aed_type &b, const aed_type &af,
+                const aed_type &ff) const=0;
 
         /** \brief Set basis for calculations */
         virtual void set_basis(const Matrix b);
@@ -54,11 +56,11 @@ class Kern_Base: public virtual Function_Base {
         /** \brief Return basis stored by this object */
         virtual Matrix get_basis();
 
-        virtual double epredict(const t_type &kweights, const aed_type2 &aed) const;
+        virtual double epredict(const t_type &kweights, const aed_type &aed) const;
         virtual double fpredict(const t_type &kweights, const fd_type &fdij,
-                const aed_type2 &aedi, const size_t k) const;
+                const aed_type &aedi, const size_t k) const;
         virtual force_type fpredict(const t_type &kweights, const fd_type &fdij,
-                const aed_type2 &aedi) const;
+                const aed_type &aedi) const;
 
         virtual void read_basis_from_config(const Config &config,
                 Matrix &b);
diff --git a/include/tadah/models/functions/kernels/kern_linear.h b/include/tadah/models/functions/kernels/kern_linear.h
index 697d85f8f3466cc6b1f6cf5bba8068a302513168..64d581eeffa2e2b3f589b879afac4a3b81c11132 100644
--- a/include/tadah/models/functions/kernels/kern_linear.h
+++ b/include/tadah/models/functions/kernels/kern_linear.h
@@ -18,21 +18,21 @@ class Kern_Linear :  public virtual Kern_Base {
     public:
         Kern_Linear ();
         Kern_Linear (const Config &c);
-        std::string get_label() const;
+        std::string get_label() const override;
 
         /**
          * Label used for this class
          */
         std::string label = "Kern_Linear";
-        double operator() (const aed_type2& b, const aed_type2& af) const;
-        aed_type2 derivative(const aed_type2& b, const aed_type2& ) const;
-        double prime(const aed_type2& b, const aed_type2& af, const aed_type2& ff) const;
-        void set_basis(const Matrix ) {}
+        double operator() (const aed_type& b, const aed_type& af) const override;
+        aed_type derivative(const aed_type& b, const aed_type& ) const override;
+        double prime(const aed_type& b, const aed_type& af, const aed_type& ff) const override;
+        void set_basis(const Matrix ) override {}
 
-        double epredict(const t_type &weights, const aed_type2& aed) const;
+        double epredict(const t_type &weights, const aed_type& aed) const override;
         double fpredict(const t_type &weights, const fd_type &fdij,
-                const aed_type2& aedi, const size_t k) const;
+                const aed_type& aedi, const size_t k) const override;
         force_type fpredict(const t_type &weights, const fd_type &fdij,
-                const aed_type2& aedi) const;
+                const aed_type& aedi) const override;
 };
 #endif
diff --git a/include/tadah/models/functions/kernels/kern_lq.h b/include/tadah/models/functions/kernels/kern_lq.h
index 7dbfb7edeadf128662464313a18bbb4a02576b0e..6850a95631b4a2afb340dfabd121267526929040 100644
--- a/include/tadah/models/functions/kernels/kern_lq.h
+++ b/include/tadah/models/functions/kernels/kern_lq.h
@@ -24,10 +24,10 @@ class Kern_LQ : public virtual Kern_Base {
          * Label used for this class
          */
         std::string label = "Kern_LQ";
-        double operator() (const aed_type2& b, const aed_type2& af) const;
-        aed_type2 derivative(const aed_type2& b, const aed_type2& af) const;
-        double prime(const aed_type2& b, const aed_type2& af, const aed_type2& ff) const;
-        std::string get_label() const;
+        double operator() (const aed_type& b, const aed_type& af) const override;
+        aed_type derivative(const aed_type& b, const aed_type& af) const override;
+        double prime(const aed_type& b, const aed_type& af, const aed_type& ff) const override;
+        std::string get_label() const override;
 
 };
 #endif
diff --git a/include/tadah/models/functions/kernels/kern_polynomial.h b/include/tadah/models/functions/kernels/kern_polynomial.h
index 246724921c4705196fa72f9ad360c6ec515d5f64..88fc2b33aec14954ce2c952b5c09205fa91646fb 100644
--- a/include/tadah/models/functions/kernels/kern_polynomial.h
+++ b/include/tadah/models/functions/kernels/kern_polynomial.h
@@ -27,10 +27,10 @@ class Kern_Polynomial : public virtual Kern_Base {
          * Label used for this class
          */
         std::string label = "Kern_Polynomial";
-        std::string get_label() const;
-        double operator() (const aed_type2& b, const aed_type2& af) const;
-        aed_type2 derivative(const aed_type2& b, const aed_type2& af) const;
-        double prime(const aed_type2& b, const aed_type2& af, const aed_type2& ff) const;
+        std::string get_label() const override;
+        double operator() (const aed_type& b, const aed_type& af) const override;
+        aed_type derivative(const aed_type& b, const aed_type& af) const override;
+        double prime(const aed_type& b, const aed_type& af, const aed_type& ff) const override;
 
 };
 #endif
diff --git a/include/tadah/models/functions/kernels/kern_quadratic.h b/include/tadah/models/functions/kernels/kern_quadratic.h
index 187c3e8ccd3564c2ec2edd5a9e202e6acbae96f4..6db5c11c5c898503023ab79aba89adb14bb231a0 100644
--- a/include/tadah/models/functions/kernels/kern_quadratic.h
+++ b/include/tadah/models/functions/kernels/kern_quadratic.h
@@ -23,10 +23,10 @@ class Kern_Quadratic : public virtual Kern_Base {
          * Label used for this class
          */
         std::string label = "Kern_Quadratic";
-        double operator() (const aed_type2& b, const aed_type2& af) const;
-        aed_type2 derivative(const aed_type2& b, const aed_type2& af) const;
-        double prime(const aed_type2& b, const aed_type2& af, const aed_type2& ff) const;
-        std::string get_label() const;
+        double operator() (const aed_type& b, const aed_type& af) const override;
+        aed_type derivative(const aed_type& b, const aed_type& af) const override;
+        double prime(const aed_type& b, const aed_type& af, const aed_type& ff) const override;
+        std::string get_label() const override;
 
 };
 #endif
diff --git a/include/tadah/models/functions/kernels/kern_rbf.h b/include/tadah/models/functions/kernels/kern_rbf.h
index 340d45726dc400c9dd2e9024ed65e70073621e28..322a765122f927c896dd6449e5b9ac2328552e95 100644
--- a/include/tadah/models/functions/kernels/kern_rbf.h
+++ b/include/tadah/models/functions/kernels/kern_rbf.h
@@ -25,10 +25,10 @@ class Kern_RBF : public virtual Kern_Base {
         Kern_RBF ();
         Kern_RBF (const Config &c);
         std::string label = "Kern_RBF";
-        double operator() (const aed_type2& b, const aed_type2& af) const;
-        aed_type2 derivative(const aed_type2& b, const aed_type2& af) const;
-        double prime(const aed_type2& b, const aed_type2& af, const aed_type2& ff) const;
-        std::string get_label() const;
+        double operator() (const aed_type& b, const aed_type& af) const override;
+        aed_type derivative(const aed_type& b, const aed_type& af) const override;
+        double prime(const aed_type& b, const aed_type& af, const aed_type& ff) const override;
+        std::string get_label() const override;
 
     private:
         double gamma;
diff --git a/include/tadah/models/functions/kernels/kern_sigmoid.h b/include/tadah/models/functions/kernels/kern_sigmoid.h
index 7f9c0f6971288943b1a0281ea8eff531197c02a2..c76189cac974fa294c4856641171ca9bae963f39 100644
--- a/include/tadah/models/functions/kernels/kern_sigmoid.h
+++ b/include/tadah/models/functions/kernels/kern_sigmoid.h
@@ -26,10 +26,10 @@ class Kern_Sigmoid : public virtual Kern_Base {
         Kern_Sigmoid ();
         Kern_Sigmoid (const Config &c);
         std::string label = "Kern_Sigmoid";
-        double operator() (const aed_type2& b, const aed_type2& af) const;
-        aed_type2 derivative(const aed_type2& b, const aed_type2& af) const;
-        double prime(const aed_type2& b, const aed_type2& af, const aed_type2& ff) const;
-        std::string get_label() const;
+        double operator() (const aed_type& b, const aed_type& af) const override;
+        aed_type derivative(const aed_type& b, const aed_type& af) const override;
+        double prime(const aed_type& b, const aed_type& af, const aed_type& ff) const override;
+        std::string get_label() const override;
 
 };
 #endif
diff --git a/include/tadah/models/m_blr_core.h b/include/tadah/models/m_blr_core.h
index d4d4b182caff33c3959efc45dd5d1192554e1d9f..144d1c82428d996b4cbcdfda9dcb8558ca826005 100644
--- a/include/tadah/models/m_blr_core.h
+++ b/include/tadah/models/m_blr_core.h
@@ -59,7 +59,7 @@ public:
      * @param v Input vector for prediction.
      * @return Predicted value.
      */
-    double predict(const aed_type2 &v) const {
+    double predict(const aed_type &v) const {
         return bf.epredict(get_weights(), v);
     }
 
diff --git a/include/tadah/models/m_blr_train.h b/include/tadah/models/m_blr_train.h
index d78bd4354854846a4201de64e5adc459bb07c5b2..fdbc457cff90ed6bf89224f2bd81ef5993ae7c97 100644
--- a/include/tadah/models/m_blr_train.h
+++ b/include/tadah/models/m_blr_train.h
@@ -1,6 +1,8 @@
 #ifndef M_BLR_TRAIN_H
 #define M_BLR_TRAIN_H
 
+#include "tadah/core/core_types.h"
+#include "tadah/core/utils.h"
 #include <tadah/models/m_train.h>
 #include <tadah/models/m_blr_core.h>
 #include <tadah/models/linear_regressor.h>
@@ -9,6 +11,7 @@
 #include <tadah/core/config.h>
 
 #include <iostream>
+#include <vector>
 
 /**
  * @class M_BLR_Train
@@ -54,13 +57,63 @@ public:
      * @param T Target vector.
      * @throws std::runtime_error if the object is already trained.
      */
-    void train(phi_type &Phi, t_type &T) {
-        if (trained) {
-            throw std::runtime_error("This object is already trained!");
+  void train(phi_type &Phi, t_type &T) {
+    if (trained) {
+      throw std::runtime_error("This object is already trained!");
+    }
+
+    if (config.exist("FIXWEIGHT") && config.exist("FIXINDEX")) {
+      std::vector<std::string> indices_str(config.size("FIXINDEX"));
+      config.get("FIXINDEX", indices_str);
+
+      // Parse indices and perform checks
+      std::vector<size_t> indices = parse_indices(indices_str);
+
+      // Check that the min index is >= 1 and max index is <= Phi.cols()
+      if (!indices.empty()) {
+        size_t max_index = *std::max_element(indices.begin(), indices.end());
+        if (*std::min_element(indices.begin(), indices.end()) < 1 || max_index > Phi.cols()) {
+          throw std::runtime_error("FIXINDEX: Indices out of bounds: valid range is from 1 to " + std::to_string(Phi.cols()));
         }
-        LinearRegressor::train(config, Phi, T, weights, Sigma);
-        trained = true;
+      }
+
+      // Adjust for 0-based indexing
+      for (auto &i : indices) i--;
+
+      t_type w_f(config.size("FIXWEIGHT"));
+      if (w_f.size() != indices.size()) {
+        throw std::runtime_error("FIXWEIGHT and FIXINDEX differ in size: " + std::to_string(w_f.size()) + " and " + std::to_string(indices.size()));
+      }
+
+      config.get("FIXWEIGHT", w_f);
+
+      t_type T_r;
+      auto move_map = prep_train_with_residuals(Phi, T, indices, w_f, T_r);
+
+      // Resize Phi for training
+      Phi.resize(Phi.rows(), Phi.cols() - indices.size());
+
+      LinearRegressor::train(config, Phi, T_r, weights, Sigma);
+
+      t_type w_temp(weights.size() + indices.size());
+
+      for (size_t i = 0; i < w_f.size(); ++i) {
+        w_temp[w_temp.size() - w_f.size() + i] = w_f[i];
+      }
+
+      for (size_t i = 0; i < weights.size(); ++i) {
+        w_temp[i] = weights[i];
+      }
+
+      weights = w_temp;
+      reverse_vector(weights, move_map);
+      trained = true;
+    }
+    else {
+      LinearRegressor::train(config, Phi, T, weights, Sigma);
+      trained = true;
     }
+  }
 };
 
 #endif // M_BLR_TRAIN_H
diff --git a/include/tadah/models/m_core.h b/include/tadah/models/m_core.h
index a0a2a4ac08cb083859804ab6a1cfb8ec479d2839..96e3e0f2d4f83927883f91489fe3fb35cd444312 100644
--- a/include/tadah/models/m_core.h
+++ b/include/tadah/models/m_core.h
@@ -12,10 +12,12 @@
  */
 class M_Core {
 public:
-    int verbose; ///< Verbose level for logging.
+    int verbose = 0; ///< Verbose level for logging.
     bool trained = false; ///< Indicates if the model has been trained.
     t_type weights; ///< Weights vector for the model.
 
+  int is_verbose() {return verbose;}
+
     /**
      * @brief Virtual destructor for polymorphic deletion.
      */
@@ -56,7 +58,7 @@ public:
      * @param v Input vector for prediction.
      * @return Predicted value.
      */
-    virtual double predict(const aed_type2 &v) const = 0;
+    virtual double predict(const aed_type &v) const = 0;
 
     /**
      * @brief Pure virtual function to get weights' uncertainty.
diff --git a/include/tadah/models/m_krr_core.h b/include/tadah/models/m_krr_core.h
index 2182e6b2feab37845370c80d4cc1800e28c008a4..9eef34c77459ec9fc986ba1cf6043f0b01110c52 100644
--- a/include/tadah/models/m_krr_core.h
+++ b/include/tadah/models/m_krr_core.h
@@ -64,7 +64,7 @@ public:
      * @param v Input vector for prediction.
      * @return Predicted value.
      */
-    double predict(const aed_type2 &v) const {
+    double predict(const aed_type &v) const {
         return kernel.epredict(weights, v);
     }
 
diff --git a/include/tadah/models/m_krr_train.h b/include/tadah/models/m_krr_train.h
index c6160f6f7804c7e6c013ee1eeb76d2f0703a744d..d4e79033377c3051f1cc9c3df054ff2ea439378a 100644
--- a/include/tadah/models/m_krr_train.h
+++ b/include/tadah/models/m_krr_train.h
@@ -60,21 +60,74 @@ public:
      * @param T Target vector.
      * @throws std::runtime_error if the object is already trained.
      */
-    void train(phi_type &Phi, t_type &T) {
-        if (trained) {
-            throw std::runtime_error("This object is already trained!");
-        }
-        if (kernel.get_label() != "Kern_Linear") {
-            ekm.project(Phi);
+  void train(phi_type &Phi, t_type &T) {
+    if (trained) {
+      throw std::runtime_error("This object is already trained!");
+    }
+
+    if (config.exist("FIXWEIGHT") && config.exist("FIXINDEX")) {
+      std::vector<std::string> indices_str(config.size("FIXINDEX"));
+      config.get("FIXINDEX", indices_str);
+
+      // Parse indices and perform checks
+      std::vector<size_t> indices = parse_indices(indices_str);
+
+      // Check that the min index is >= 1 and max index is <= Phi.cols()
+      if (!indices.empty()) {
+        size_t max_index = *std::max_element(indices.begin(), indices.end());
+        if (*std::min_element(indices.begin(), indices.end()) < 1 || max_index > Phi.cols()) {
+          throw std::runtime_error("FIXINDEX: Indices out of bounds: valid range is from 1 to " + std::to_string(Phi.cols()));
         }
-        LinearRegressor::train(config, Phi, T, weights, Sigma);
+      }
+
+      // Adjust for 0-based indexing
+      for (auto &i : indices) i--;
+
+      t_type w_f(config.size("FIXWEIGHT"));
+      if (w_f.size() != indices.size()) {
+        throw std::runtime_error("FIXWEIGHT and FIXINDEX differ in size: " + std::to_string(w_f.size()) + " and " + std::to_string(indices.size()));
+      }
+
+      config.get("FIXWEIGHT", w_f);
 
-        if (kernel.get_label() != "Kern_Linear") {
-            weights = ekm.EKM_mat * weights;
+      t_type T_r;
+      auto move_map = prep_train_with_residuals(Phi, T, indices, w_f, T_r);
+
+      reverse_columns(Phi, move_map);
+
+      for (const auto &i : indices) {
+        for (size_t j=0; j<Phi.rows(); ++j) {
+          Phi(j,i)=0;
         }
-        trained = true;
+      }
+
+      if (kernel.get_label() != "Kern_Linear") {
+        ekm.project(Phi);
+      }
+      LinearRegressor::train(config, Phi, T_r, weights, Sigma);
+      if (kernel.get_label() != "Kern_Linear") {
+        weights = ekm.EKM_mat * weights;
+      }
+
+      // reverse_vector(weights, move_map);
+      for (size_t i=0; i<indices.size(); ++i) {
+        weights[indices[i]]=w_f[i];
+      }
+      trained = true;
+    }
+    else {
+      if (kernel.get_label() != "Kern_Linear") {
+        ekm.project(Phi);
+      }
+      LinearRegressor::train(config, Phi, T, weights, Sigma);
+      if (kernel.get_label() != "Kern_Linear") {
+        weights = ekm.EKM_mat * weights;
+      }
+      trained = true;
     }
 
+  }
+
     /**
      * @brief Standard KRR training using covariance matrix computation.
      *
@@ -90,7 +143,7 @@ public:
         if (lambda < 0)
             throw std::runtime_error("This KRR implementation cannot use LAMBDA -1 (Evidence Approximation)");
         
-        std::cout << "Computing Kernel Matrix" << std::endl;
+        if (M_KRR_Core<Kern>::is_verbose()) std::cout << "Computing Kernel Matrix" << std::endl;
         Matrix K(B.cols(), B.cols());
 
         for (size_t i = 0; i < K.cols(); i++) {
@@ -99,12 +152,12 @@ public:
                 if (i == j) K(i, j) += lambda;
             }
         }
+        if (M_KRR_Core<Kern>::is_verbose()) std::cout << "Matrix condition number: " << condition_number(K) << std::endl;
 
-        std::cout << "Solving..." << std::flush;
+        if (M_KRR_Core<Kern>::is_verbose()) std::cout << "Solving..." << std::flush;
         weights = solve_posv(K, T);
-        std::cout << "Done" << std::endl;
+        if (M_KRR_Core<Kern>::is_verbose()) std::cout << "Done" << std::endl;
 
-        std::cout << "Matrix condition number: " << condition_number(K) << std::endl;
 
         trained = true;
     }
diff --git a/include/tadah/models/m_predict.h b/include/tadah/models/m_predict.h
index 233c75d3ef013c8ccb73a8f77dbffe57c3afd500..04ac366c951e61aa1c046c4c35561817343085fc 100644
--- a/include/tadah/models/m_predict.h
+++ b/include/tadah/models/m_predict.h
@@ -17,13 +17,13 @@ class M_Predict {
          *  If aed contains sum over all nearest neighbours than the result is
          *  a local atomic energy \f$ E_i \f$.
          * */
-        virtual double epredict(const aed_type2 &aed) const=0;
+        virtual double epredict(const aed_type &aed) const=0;
 
         /** \brief Predict force between a pair of atoms in a k-direction. */
-        virtual double fpredict(const fd_type &fdij, const aed_type2 &aedi, size_t k) const=0;
+        virtual double fpredict(const fd_type &fdij, const aed_type &aedi, size_t k) const=0;
 
         /** \brief Predict force between a pair of atoms. */
         virtual force_type fpredict(const fd_type &fdij,
-                const aed_type2 &aedi) const=0;
+                const aed_type &aedi) const=0;
 };
 #endif
diff --git a/include/tadah/models/m_train.h b/include/tadah/models/m_train.h
index abcf824b5854cc95dcd74f40be423e2e87a4686a..09a2113636d4173ead1c8980ca4f1439f1e8e4ac 100644
--- a/include/tadah/models/m_train.h
+++ b/include/tadah/models/m_train.h
@@ -2,6 +2,7 @@
 #define M_TRAIN_H
 
 #include <tadah/core/core_types.h>
+#include <tadah/core/utils.h>
 
 /**
  * @class M_Train
@@ -11,12 +12,12 @@
  */
 class M_Train {
 public:
-    /**
+  /**
      * @brief Virtual destructor for polymorphic deletion.
      */
-    virtual ~M_Train() {}
+  virtual ~M_Train() {}
 
-    /**
+  /**
      * @brief Pure virtual function to train the model.
      *
      * Must be implemented by derived classes.
@@ -24,7 +25,62 @@ public:
      * @param Phi Design matrix containing input features.
      * @param T Target vector for training.
      */
-    virtual void train(phi_type &Phi, t_type &T) = 0;
+  virtual void train(phi_type &Phi, t_type &T) = 0;
+
+  /**
+ * Swaps columns and fits a reduced matrix to obtain residuals.
+ *
+ * This function processes a full matrix Phi by swapping columns specified by indices,
+ * creating a new pointer for the fixed matrix Phi_f, and then use w_f weights
+ * to obtain prediction T_f = Phi_f w_f. The function then copmutes the residual vector
+ * T_r = T - T_f. On exit, the Phi matrix is rearranged such that the indices.size()
+ * rightmost columns of the original matrix are the same as Phi_f.
+ * The function return a mapping between columns of original Phi matrix and rearranged.
+ *
+ * @param Phi         The full matrix to be reduced and fitted to the adjusted target
+ *                    vector.
+ * @param T           The full target vector to which the reduced matrix Phi will
+ *                    be fitted.
+ * @param indices     A vector of indices specifying which columns in the
+ *                    original matrix are fixed and should be considered during
+ *                    the regression.
+ * @param w_f         A vector of fixed weights corresponding to the specified
+ *                    indices. These weights are used to adjust the target
+ *                    vector, resulting in residuals.
+ */
+  template <typename M>
+  std::vector<size_t> prep_train_with_residuals(
+    Matrix_Base<M> &Phi, t_type &T, std::vector<size_t> &indices, const t_type w_f, t_type &T_r) {
+
+    if (w_f.size() != indices.size()) {
+      throw std::runtime_error("Size of w_f must be equal to size of indices.");
+    }
+
+    std::sort(indices.begin(), indices.end());
+
+    if (indices.back() >= Phi.cols()) {
+      throw std::runtime_error("Largest index is greater than the number of columns in Phi.");
+    }
+
+    std::vector<size_t> move_map = move_columns_in_place(Phi,indices);
+
+    //   indices.size() = 1
+    //   indices[0] 1
+    //   1 4 7        1 7    4
+    //   2 5 8   ->   2 8    5
+    //   3 6 9        3 9    6
+    //    Phi        Phi_o   Phi_f
+    //    move_map = {0,2,1}
+
+    // the fixed columns are to the right
+    MatrixView Phi_f(Phi, Phi.cols()-indices.size());
+
+    t_type T_f = Phi_f * w_f;
+
+    T_r = T-T_f;
+
+    return move_map;
+  }
 };
 
 #endif // M_TRAIN_H
diff --git a/src/bf_base.cpp b/src/bf_base.cpp
index 043999f13df608d87e3e3898b943daa3162dd772..67b6d57af50cb9b0971b8fec95e4800b667e70cd 100644
--- a/src/bf_base.cpp
+++ b/src/bf_base.cpp
@@ -1,2 +1,4 @@
 #include <tadah/models/functions/basis_functions/bf_base.h>
+BF_Base::BF_Base(const Config &c): Function_Base(c) {}
+BF_Base::BF_Base() {}
 BF_Base::~BF_Base() {}
diff --git a/src/bf_linear.cpp b/src/bf_linear.cpp
index 50a612bc42f3b25e6e61d4f12d56e288689f6a56..7647f638530ecab013a4695b964f933e75ac78cc 100644
--- a/src/bf_linear.cpp
+++ b/src/bf_linear.cpp
@@ -1,21 +1,23 @@
 #include <tadah/models/functions/basis_functions/bf_linear.h>
 
 BF_Linear::BF_Linear() {}
-BF_Linear::BF_Linear(const Config &c): Function_Base(c) {}
+BF_Linear::BF_Linear(const Config &c):
+  Function_Base(c),
+  BF_Base(c) {}
 std::string BF_Linear::get_label() const {
     return label;
 }
-double BF_Linear::epredict(const t_type &weights, const aed_type2& aed) const
+double BF_Linear::epredict(const t_type &weights, const aed_type& aed) const
 {
     return weights*aed;
 }
 double BF_Linear::fpredict(const t_type &weights, const fd_type &fdij,
-        const aed_type2& , const size_t k) const
+        const aed_type& , const size_t k) const
 {
     return -1*(fdij(k) * weights);
 }
 force_type BF_Linear::fpredict(const t_type &weights, const fd_type &fdij,
-        const aed_type2& ) const
+        const aed_type& ) const
 {
     return -1*(fdij * weights);
 }
diff --git a/src/bf_polynomial2.cpp b/src/bf_polynomial2.cpp
index 8d6e6fc037e11220fa1dd03e0541a2fc090bcfab..d3ee5b3280f1d43c12d9e6ae332fe1378ca8d25c 100644
--- a/src/bf_polynomial2.cpp
+++ b/src/bf_polynomial2.cpp
@@ -1,14 +1,15 @@
 #include <tadah/models/functions/basis_functions/bf_polynomial2.h>
 
-
 BF_Polynomial2::BF_Polynomial2() {}
-BF_Polynomial2::BF_Polynomial2(const Config &c): Function_Base(c)
+BF_Polynomial2::BF_Polynomial2(const Config &c): 
+  Function_Base(c),
+  BF_Base(c)
 {}
 std::string BF_Polynomial2::get_label() const
 {
   return label;
 }
-double BF_Polynomial2::epredict(const t_type &weights, const aed_type2& aed) const
+double BF_Polynomial2::epredict(const t_type &weights, const aed_type& aed) const
 {
   size_t b=0;
   double res=0.0;
@@ -20,7 +21,7 @@ double BF_Polynomial2::epredict(const t_type &weights, const aed_type2& aed) con
   return res;
 }
 double BF_Polynomial2::fpredict(const t_type &weights, const fd_type &fdij,
-                                const aed_type2& aedi, const size_t k) const
+                                const aed_type& aedi, const size_t k) const
 {
   double res=0.0;
   size_t b=0;
@@ -32,7 +33,7 @@ double BF_Polynomial2::fpredict(const t_type &weights, const fd_type &fdij,
   return res;
 }
 force_type BF_Polynomial2::fpredict(const t_type &weights, const fd_type &fdij,
-                                    const aed_type2& aedi) const
+                                    const aed_type& aedi) const
 {
   force_type v;
   for (size_t k=0; k<3; ++k) {
diff --git a/src/cutoffs.cpp b/src/cutoffs.cpp
index 768516ace7cce8618d73b8eb91677554b26d3f0e..b818cdab76a7d70389bed0b258fca95b2de6751c 100644
--- a/src/cutoffs.cpp
+++ b/src/cutoffs.cpp
@@ -5,6 +5,7 @@
 
 template<> CONFIG::Registry<Cut_Base,double>::Map CONFIG::Registry<Cut_Base,double>::registry{};
 CONFIG::Registry<Cut_Base,double>::Register<Cut_Cos> Cut_Cos_1( "Cut_Cos" );
+CONFIG::Registry<Cut_Base,double>::Register<Cut_Cos_S> Cut_Cos_S_1( "Cut_Cos_S" );
 CONFIG::Registry<Cut_Base,double>::Register<Cut_Tanh> Cut_Tanh_1( "Cut_Tanh" );
 CONFIG::Registry<Cut_Base,double>::Register<Cut_Poly2> Cut_Poly_1( "Cut_Poly2" );
 CONFIG::Registry<Cut_Base,double>::Register<Cut_Dummy> Cut_Dummy_1( "Cut_Dummy" );
@@ -165,3 +166,44 @@ double Cut_Poly2::calc_prime(double r) {
     double rs=r-rcut_inner;
     return -30.0*(rs-1.0)*(rs-1.0)*rs*rs;
 }
+
+Cut_Cos_S::Cut_Cos_S() {}
+Cut_Cos_S::Cut_Cos_S(double rcut)
+{
+    set_rcut(rcut);
+    test_rcut(rcut);
+}
+
+std::string Cut_Cos_S::label() {
+    return lab;
+}
+
+void Cut_Cos_S::set_rcut(const double r) {
+    test_rcut(r);
+    rcut=r;
+    rcut_sq=r*r;
+    rcut_inv= r<=0 ? 0.0 : 1.0/r;
+    rcut_inner=rcut-1.0;
+}
+double Cut_Cos_S::get_rcut() {
+    return rcut;
+}
+
+double Cut_Cos_S::get_rcut_sq() {
+    return rcut_sq;
+}
+
+double Cut_Cos_S::calc(double r) {
+  if (r>=rcut) return 0.0;
+  else if (r<= rcut_inner) return 1.0;
+  double rs = (r-rcut_inner)/(rcut-rcut_inner);
+  return 0.5*(1+std::cos(M_PI*rs));
+}
+double Cut_Cos_S::calc_prime(double r) {
+  if (r>=rcut || r<= rcut_inner) return 0.0;
+  else {
+    double rs = (r - rcut_inner) / (rcut - rcut_inner);
+    double drs_dr = 1.0 / (rcut - rcut_inner);
+    return -0.5 * M_PI * std::sin(M_PI * rs) * drs_dr;
+  }
+}
diff --git a/src/d2_all.cpp b/src/d2_all.cpp
index 458293d8f2a171850da5841cd22256be707a9a7f..7bbf5088789054d7685c0cd8435815f3144b9374 100644
--- a/src/d2_all.cpp
+++ b/src/d2_all.cpp
@@ -1,16 +1,52 @@
-#include <tadah/models/descriptors/d2/d2_base.h>
-#include <tadah/models/descriptors/d2/d2_dummy.h>
-#include <tadah/models/descriptors/d2/d2_blip.h>
-#include <tadah/models/descriptors/d2/d2_bp.h>
-#include <tadah/models/descriptors/d2/d2_eam.h>
-#include <tadah/models/descriptors/d2/d2_lj.h>
-#include <tadah/models/descriptors/d2/d2_mie.h>
+#include "tadah/models/descriptors/d2/d2_all.h"
 
+template<> CONFIG::Registry<D_Base>::Map CONFIG::Registry<D_Base>::registry{};
+template<> CONFIG::Registry<D_Base,Config&>::Map CONFIG::Registry<D_Base,Config&>::registry{};
 template<> CONFIG::Registry<D2_Base,Config&>::Map CONFIG::Registry<D2_Base,Config&>::registry{};
 
+CONFIG::Registry<D_Base,Config&>::Register<D2_Blip> D2_Blip_00( "D2_Blip" );
+CONFIG::Registry<D_Base,Config&>::Register<D2_BP> D2_BP_00( "D2_BP" );
+CONFIG::Registry<D_Base,Config&>::Register<D2_Dummy> D2_Dummy_00( "D2_Dummy" );
+CONFIG::Registry<D_Base,Config&>::Register<D2_EAM> D2_EAM_00( "D2_EAM" );
+CONFIG::Registry<D_Base,Config&>::Register<D2_LJ> D2_LJ_00( "D2_LJ" );
+CONFIG::Registry<D_Base,Config&>::Register<D2_MIE> D2_MIE_00( "D2_MIE" );
+CONFIG::Registry<D_Base,Config&>::Register<D2_ZBL> D2_ZBL_00( "D2_ZBL" );
+CONFIG::Registry<D_Base,Config&>::Register<D2_mJoin> D2_mJoin_00( "D2_mJoin" );
+
+CONFIG::Registry<D_Base>::Register<D2_Blip> D2_Blip_0( "D2_Blip" );
+CONFIG::Registry<D_Base>::Register<D2_BP> D2_BP_0( "D2_BP" );
+CONFIG::Registry<D_Base>::Register<D2_Dummy> D2_Dummy_0( "D2_Dummy" );
+CONFIG::Registry<D_Base>::Register<D2_EAM> D2_EAM_0( "D2_EAM" );
+CONFIG::Registry<D_Base>::Register<D2_LJ> D2_LJ_0( "D2_LJ" );
+CONFIG::Registry<D_Base>::Register<D2_MIE> D2_MIE_0( "D2_MIE" );
+CONFIG::Registry<D_Base>::Register<D2_ZBL> D2_ZBL_0( "D2_ZBL" );
+CONFIG::Registry<D_Base>::Register<D2_mJoin> D2_mJoin_0( "D2_mJoin" );
+
 CONFIG::Registry<D2_Base,Config&>::Register<D2_Blip> D2_Blip_1( "D2_Blip" );
 CONFIG::Registry<D2_Base,Config&>::Register<D2_BP> D2_BP_1( "D2_BP" );
 CONFIG::Registry<D2_Base,Config&>::Register<D2_Dummy> D2_Dummy_1( "D2_Dummy" );
 CONFIG::Registry<D2_Base,Config&>::Register<D2_EAM> D2_EAM_1( "D2_EAM" );
 CONFIG::Registry<D2_Base,Config&>::Register<D2_LJ> D2_LJ_1( "D2_LJ" );
-CONFIG::Registry<D2_Base,Config&>::Register<D2_MIE> D2_MIE_1( "D2_MIE" );
\ No newline at end of file
+CONFIG::Registry<D2_Base,Config&>::Register<D2_MIE> D2_MIE_1( "D2_MIE" );
+CONFIG::Registry<D2_Base,Config&>::Register<D2_ZBL> D2_ZBL_1( "D2_ZBL" );
+CONFIG::Registry<D2_Base,Config&>::Register<D2_mJoin> D2_mJoin_1( "D2_mJoin" );
+
+
+
+// This is in development
+CONFIG::Registry<D2_Base,Config&>::Register<D2_Join<D2_LJ,D2_BP>> D2_JOIN_1( "D2_LJ+BP" );
+CONFIG::Registry<D2_Base,Config&>::Register<D2_Join<D2_LJ,D2_Blip>> D2_JOIN_2( "D2_LJ+Blip" );
+
+CONFIG::Registry<D2_Base,Config&>::Register<D2_Join<D2_MIE,D2_BP>> D2_JOIN_3( "D2_MIE+BP" );
+CONFIG::Registry<D2_Base,Config&>::Register<D2_Join<D2_MIE,D2_Blip>> D2_JOIN_4( "D2_MIE+Blip" );
+
+CONFIG::Registry<D2_Base,Config&>::Register<D2_Join<D2_Blip,D2_Blip>> D2_JOIN_5( "D2_Blip_x2" );
+CONFIG::Registry<D2_Base,Config&>::Register<D2_Join<D2_BP,D2_BP>> D2_JOIN_6( "D2_BP_x2" );
+
+CONFIG::Registry<D2_Base,Config&>::Register<D2_Join<D2_EAM,D2_BP>> D2_JOIN_7( "D2_EAM+BP" );
+CONFIG::Registry<D2_Base,Config&>::Register<D2_Join<D2_EAM,D2_Blip>> D2_JOIN_8( "D2_EAM+Blip" );
+
+CONFIG::Registry<D2_Base,Config&>::Register<D2_Join<D2_BP,D2_Blip>> D2_JOIN_9( "D2_BP+Blip" );
+
+// CONFIG::Registry<D2_Base,Config&>::Register<D2_Join_N<D2_LJ,D2_LJ>> D2_JOIN_3( "D2_LJ_D2_LJ" );
+//CONFIG::Registry<D2_Base,Config&>::Register<D2_Join_N<D2_LJ,D2_LJ,D2_LJ>> D2_JOIN_4( "D2_LJ_D2_LJ_D2_LJ" );
diff --git a/src/d2_base.cpp b/src/d2_base.cpp
index 00abcd05957115e8b4026f6b4ad322c8ae77e57b..106639caf445a093db172ccbd18d4e6da02370fa 100644
--- a/src/d2_base.cpp
+++ b/src/d2_base.cpp
@@ -1 +1,13 @@
 #include <tadah/models/descriptors/d2/d2_base.h>
+
+std::vector<std::string> D2_Base::get_init_atoms(Config &c) {
+  if (!c.exist("TYPE2B")) {
+    std::cerr << "Warning: TYPE2B key was not found. "
+      "Atom pairs must be initialized manually." << std::endl;
+    return {};
+  }
+  std::vector<std::string> init_atoms(c.size("TYPE2B"));
+  c.get("TYPE2B", init_atoms);
+  init_atoms.erase(init_atoms.begin(), init_atoms.begin() + nparams+1);
+  return init_atoms;
+}
diff --git a/src/d2_blip.cpp b/src/d2_blip.cpp
index ce95084970e28ee4b6baabae8a5a00de5455a61f..3c16d5b275c25ba4b9c0092c316138f8a181836c 100644
--- a/src/d2_blip.cpp
+++ b/src/d2_blip.cpp
@@ -1,17 +1,24 @@
+#include "tadah/models/descriptors/d_base.h"
 #include <tadah/models/descriptors/d2/d2_blip.h>
 #include <tadah/models/descriptors/d_basis_functions.h>
 #include <cstdlib>
 #include <stdexcept>
 
-
-D2_Blip::D2_Blip(Config &c):
-  verbose(c.get<int>("VERBOSE"))
+D2_Blip::D2_Blip() {
+  init();
+}
+D2_Blip::D2_Blip(Config &c): D2_Base(c)
 {
-
   if (!c.get<bool>("INIT2B")) return;
+  init();
 
   get_grid(c,"CGRID2B",mius);
   get_grid(c,"SGRID2B",etas);
+  // update config with generated grid
+  c.remove("CGRID2B");
+  for (const auto &m: mius) c.add("CGRID2B",m);
+  c.remove("SGRID2B");
+  for (const auto &e: etas) c.add("SGRID2B",e);
 
   if (verbose) {
     std::cout << std::endl;
@@ -34,54 +41,70 @@ D2_Blip::D2_Blip(Config &c):
   }
 
   s=mius.size();
+
+  auto init_atoms = get_init_atoms(c);
+  init_for_atoms(init_atoms);
 }
 
 void D2_Blip::calc_aed(
+  const int Zi,
+  const int Zj,
   const double rij,
-  const double ,
-  const double fc_ij,
-  aed_type2 &aed)
+  const double,
+  aed_type &aed,
+  const double scale)
 {
-
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
   size_t i=fidx;
+  double fc_ij = fcut->calc(rij);
   for (size_t c=0; c<mius.size(); c++) {
     double t = B(etas[c]*(rij-mius[c]),fc_ij);
-    aed(i++) += t;
+    aed(i++) += scale*weights[Zj]*t;
   }
 
 }
 void D2_Blip::calc_dXijdri(
+  const int Zi,
+  const int Zj,
   const double rij,
   const double ,
-  const double fc_ij,
-  const double fcp_ij,
-  fd_type &fd_ij)
+  fd_type &fd_ij,
+  const double scale)
 {
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
   size_t i=fidx;
+  double fc_ij = fcut->calc(rij);
+  double fcp_ij = fcut->calc_prime(rij);
   for (size_t c=0; c<mius.size(); c++) {
-    fd_ij(i++,0) = dB(etas[c]*(rij-mius[c]),etas[c],fc_ij,fcp_ij);
+    fd_ij(i++,0) = scale*weights[Zj]*dB(etas[c]*(rij-mius[c]),etas[c],fc_ij,fcp_ij);
   }
 }
 void D2_Blip::calc_all(
+  const int Zi,
+  const int Zj,
   const double rij,
   const double ,
-  const double fc_ij,
-  const double fcp_ij,
-  aed_type2 &aed,
-  fd_type &fd_ij)
+  aed_type &aed,
+  fd_type &fd_ij,
+  const double scale)
 {
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
 
   size_t i=fidx;
+  double fc_ij = fcut->calc(rij);
+  double fcp_ij = fcut->calc_prime(rij);
   for (size_t c=0; c<mius.size(); c++) {
-    aed(i) += B(etas[c]*(rij-mius[c]),fc_ij);
-    fd_ij(i,0) = dB(etas[c]*(rij-mius[c]),etas[c],fc_ij,fcp_ij);
+    aed(i) += scale*weights[Zj]*B(etas[c]*(rij-mius[c]),fc_ij);
+    fd_ij(i,0) = scale*weights[Zj]*dB(etas[c]*(rij-mius[c]),etas[c],fc_ij,fcp_ij);
     ++i;
   }
 }
 
-size_t D2_Blip::size() {
-  return s;
-}
 std::string D2_Blip::label() {
   return lab;
 }
+void D2_Blip::init() {
+  keys.push_back("CGRID2B");
+  keys.push_back("SGRID2B");
+  nparams=2;
+}
diff --git a/src/d2_bp.cpp b/src/d2_bp.cpp
index 61b28e46bae7f36226c91929ed1e08e7e7d9d73d..2b564f7f404ff8cdb184124bf0057a8a47c5f8e4 100644
--- a/src/d2_bp.cpp
+++ b/src/d2_bp.cpp
@@ -1,14 +1,21 @@
 #include <tadah/models/descriptors/d2/d2_bp.h>
 #include <tadah/models/descriptors/d_basis_functions.h>
 
-D2_BP::D2_BP(Config &c):
-  verbose(c.get<int>("VERBOSE"))
-{
+D2_BP::D2_BP() {
+  init();
+}
+D2_BP::D2_BP(Config &c): D2_Base(c) {
+  init();
 
   if (!c.get<bool>("INIT2B")) return;
 
   get_grid(c,"CGRID2B",mius);
   get_grid(c,"SGRID2B",etas);
+  // update config with generated grid
+  c.remove("CGRID2B");
+  for (const auto &m: mius) c.add("CGRID2B",m);
+  c.remove("SGRID2B");
+  for (const auto &e: etas) c.add("SGRID2B",e);
 
   if (verbose) {
     std::cout << std::endl;
@@ -30,53 +37,68 @@ D2_BP::D2_BP(Config &c):
       throw std::runtime_error("At least one of SGRID2B values is zero.\n");
   }
 
-  s=mius.size();
+  s = mius.size();
+  auto init_atoms = get_init_atoms(c);
+  init_for_atoms(init_atoms);
 }
 
 void D2_BP::calc_aed(
+  const int Zi,
+  const int Zj,
   const double rij,
   const double ,
-  const double fc_ij,
-  aed_type2 &aed)
+  aed_type &aed,
+  const double scale)
 {
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
 
   size_t i=fidx;
+  double fc_ij = fcut->calc(rij);
   for (size_t c=0; c<mius.size(); c++) {         
-    aed(i++) += G(rij,etas[c],mius[c],fc_ij);
+    aed(i++) += scale*weights[Zj]*G(rij,etas[c],mius[c],fc_ij);
   }
 
 }
 void D2_BP::calc_dXijdri(
+  const int Zi,
+  const int Zj,
   const double rij,
   const double ,
-  const double fc_ij,
-  const double fcp_ij,
-  fd_type &fd_ij)
+  fd_type &fd_ij,
+  const double scale)
 {
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
   size_t i=fidx;
+  double fc_ij = fcut->calc(rij);
+  double fcp_ij = fcut->calc_prime(rij);
   for (size_t c=0; c<mius.size(); c++) {         
-    fd_ij(i++,0) = dG(rij,etas[c],mius[c],fc_ij,fcp_ij);
+    fd_ij(i++,0) = scale*weights[Zj]*dG(rij,etas[c],mius[c],fc_ij,fcp_ij);
   }
 }
 void D2_BP::calc_all(
+  const int Zi,
+  const int Zj,
   const double rij,
   const double ,
-  const double fc_ij,
-  const double fcp_ij,
-  aed_type2 &aed,
-  fd_type &fd_ij)
+  aed_type &aed,
+  fd_type &fd_ij,
+  const double scale)
 {
-
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
   size_t i=fidx;
+  double fc_ij = fcut->calc(rij);
+  double fcp_ij = fcut->calc_prime(rij);
   for (size_t c=0; c<mius.size(); c++) {         
-    aed(i) += G(rij,etas[c],mius[c],fc_ij);
-    fd_ij(i,0) = dG(rij,etas[c],mius[c],fc_ij,fcp_ij);
+    aed(i) += scale*weights[Zj]*G(rij,etas[c],mius[c],fc_ij);
+    fd_ij(i,0) = scale*weights[Zj]*dG(rij,etas[c],mius[c],fc_ij,fcp_ij);
     ++i;
   }
 }
-size_t D2_BP::size() {
-  return s;
-}
 std::string D2_BP::label() {
   return lab;
 }
+void D2_BP::init() {
+  keys.push_back("CGRID2B");
+  keys.push_back("SGRID2B");
+  nparams=2;
+}
diff --git a/src/d2_dummy.cpp b/src/d2_dummy.cpp
index 760f7e684f0f80343b88121646e8f29ebd38e289..043e85e690ff393aae0c70df378ba685a61c7d37 100644
--- a/src/d2_dummy.cpp
+++ b/src/d2_dummy.cpp
@@ -1,27 +1,38 @@
+#include "tadah/models/descriptors/d2/d2_base.h"
 #include <tadah/models/descriptors/d2/d2_dummy.h>
 
-D2_Dummy::D2_Dummy() {}
-D2_Dummy::D2_Dummy(Config &c):
-    verbose(c.get<int>("VERBOSE"))
-{}
+D2_Dummy::D2_Dummy() {
+  init();
+}
+D2_Dummy::D2_Dummy(Config &c): D2_Base(c)
+{
+  init();
+}
 
 void D2_Dummy::calc_aed(
-        const double,
-        const double,
-        const double,
-        aed_type2 & ) {}
+  const int,
+  const int,
+  const double,
+  const double,
+  aed_type&,
+  const double) {}
+
 void D2_Dummy::calc_dXijdri(
-        const double,
-        const double,
-        const double,
-        const double,
-        fd_type &) {}
+  const int,
+  const int,
+  const double,
+  const double,
+  fd_type &,
+  const double) {}
 void D2_Dummy::calc_all(
-        const double,
-        const double,
-        const double,
-        const double,
-        aed_type2 & ,
-        fd_type &) {}
-size_t D2_Dummy::size() { return s; }
+  const int,
+  const int,
+  const double,
+  const double,
+  aed_type & ,
+  fd_type &,
+  const double) {}
 std::string D2_Dummy::label() { return lab; }
+void D2_Dummy::init() {
+  nparams=0;
+}
diff --git a/src/d2_eam.cpp b/src/d2_eam.cpp
index b54afb61ce511b7c139b9ce90071d20cf9e27712..90b8b7969ddf2e1b8a7f325982d26a62b621c11c 100644
--- a/src/d2_eam.cpp
+++ b/src/d2_eam.cpp
@@ -1,174 +1,178 @@
+#include "tadah/models/descriptors/d2/d2_base.h"
 #include <tadah/models/descriptors/d2/d2_eam.h>
 
-D2_EAM::D2_EAM(Config &c):
-    verbose(c.get<int>("VERBOSE"))
+D2_EAM::D2_EAM() {
+  init();
+}
+D2_EAM::D2_EAM(Config &c): D2_Base(c)
 {
-    if (!c.get<bool>("INIT2B")) {
-        s=0;
-        return;
+  if (!c.get<bool>("INIT2B")) return;
+  init();
+  s=1;
+  ef.file_path = c("SETFL")[0];
+  read_setfl();
+
+  if (std::abs(ef.rcut - c.get<double>("RCUT2B")) > std::numeric_limits<double>::min()) {
+    if (verbose) {
+      std::cout << std::endl;
+      std::cout << "Config file cutoff and setfl file cutoff differ: "
+        << c.get<double>("RCUT2B") << " " << ef.rcut << std::endl;
+      std::cout << "Enforcing SETFL file cutoff: " << ef.rcut << std::endl;
     }
-	ef.file_path = c("SETFL")[0];
-	read_setfl();
-
-	if (std::abs(ef.rcut - c.get<double>("RCUT2B")) > std::numeric_limits<double>::min()) {
-        if (verbose) {
-            std::cout << std::endl;
-            std::cout << "Config file cutoff and setfl file cutoff differ: "
-                << c.get<double>("RCUT2B") << " " << ef.rcut << std::endl;
-            std::cout << "Enforcing SETFL file cutoff: " << ef.rcut << std::endl;
-        }
-		c.remove("RCUT2B");
-		c.add("RCUT2B", ef.rcut);
-        c.postprocess();
-	}
-
-
-	frho_spline.resize(ef.nrho+1, std::vector<double>(7));
-	rhor_spline.resize(ef.nr+1, std::vector<double>(7));
-	z2r_spline.resize(ef.nr+1, std::vector<double>(7));
+    c.remove("RCUT2B");
+    c.add("RCUT2B", ef.rcut);
+    c.postprocess();
+  }
 
+  frho_spline.resize(ef.nrho+1, std::vector<double>(7));
+  rhor_spline.resize(ef.nr+1, std::vector<double>(7));
+  z2r_spline.resize(ef.nr+1, std::vector<double>(7));
 
-	gen_splines(ef.nrho, ef.drho, ef.frho, frho_spline);
-	gen_splines(ef.nr, ef.dr, ef.rhor, rhor_spline);
-	gen_splines(ef.nr, ef.dr, ef.z2r, z2r_spline);
+  gen_splines(ef.nrho, ef.drho, ef.frho, frho_spline);
+  gen_splines(ef.nr, ef.dr, ef.rhor, rhor_spline);
+  gen_splines(ef.nr, ef.dr, ef.z2r, z2r_spline);
 
+  auto init_atoms = get_init_atoms(c);
+  init_for_atoms(init_atoms);
 }
 
 void D2_EAM::calc_aed(
-		const double rij,
-		const double,
-		const double,
-		aed_type2 &aed)
+  const int Zi,
+  const int Zj,
+  const double rij,
+  const double,
+  aed_type &aed,
+  const double scale)
 {
-
-	double r = rij;
-	const double recip = 1.0/r;
-	double p = r*ef.rdr + 1.0;
-	int m = static_cast<int> (p);
-	m = std::min(m,ef.nr-1);
-	p -= m;
-	p = std::min(p,1.0);
-	double z2 = ((z2r_spline[m][3]*p + z2r_spline[m][4])*p + z2r_spline[m][5])*p + z2r_spline[m][6];
-	aed(fidx) += z2*recip;
-
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
+  double r = rij;
+  const double recip = 1.0/r;
+  double p = r*ef.rdr + 1.0;
+  int m = static_cast<int> (p);
+  m = std::min(m,ef.nr-1);
+  p -= m;
+  p = std::min(p,1.0);
+  double z2 = ((z2r_spline[m][3]*p + z2r_spline[m][4])*p + z2r_spline[m][5])*p + z2r_spline[m][6];
+  aed(fidx) += scale*z2*recip;
 }
 void D2_EAM::calc_dXijdri(
-		const double rij,
-		const double,
-		const double,
-		const double,
-		fd_type &fd_ij)
+  const int Zi,
+  const int Zj,
+  const double rij,
+  const double,
+  fd_type &fd_ij,
+  const double scale)
 {
-
-	const double r = rij;
-	const double recip = 1.0/r;
-	double p = r*ef.rdr + 1.0;
-	int m = static_cast<int> (p);
-	m = std::min(m,ef.nr-1);
-	p -= m;
-	p = std::min(p,1.0);
-	double z2p = (z2r_spline[m][0]*p + z2r_spline[m][1])*p + z2r_spline[m][2];
-	double z2 = ((z2r_spline[m][3]*p + z2r_spline[m][4])*p + z2r_spline[m][5])*p + z2r_spline[m][6];
-	//double phi = z2*recip;
-	//double phip = z2p*recip - z2*recip*recip;
-
-	fd_ij(fidx,0) = (z2p*recip - z2*recip*recip);
-	//force_fp_ij[first_idx] = 0.5*(z2p*recip - z2*recip*recip);
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
+  const double r = rij;
+  const double recip = 1.0/r;
+  double p = r*ef.rdr + 1.0;
+  int m = static_cast<int> (p);
+  m = std::min(m,ef.nr-1);
+  p -= m;
+  p = std::min(p,1.0);
+  double z2p = (z2r_spline[m][0]*p + z2r_spline[m][1])*p + z2r_spline[m][2];
+  double z2 = ((z2r_spline[m][3]*p + z2r_spline[m][4])*p + z2r_spline[m][5])*p + z2r_spline[m][6];
+  //double phi = z2*recip;
+  //double phip = z2p*recip - z2*recip*recip;
+
+  fd_ij(fidx,0) = scale*(z2p*recip - z2*recip*recip);
+  //force_fp_ij[first_idx] = 0.5*(z2p*recip - z2*recip*recip);
 
 }
 void D2_EAM::calc_all(
-		const double rij,
-		const double,
-		const double,
-		const double,
-		aed_type2 &aed,
-		fd_type &fd_ij)
+  const int Zi,
+  const int Zj,
+  const double rij,
+  const double,
+  aed_type &aed,
+  fd_type &fd_ij,
+  const double scale)
 {
-	double r = rij;
-	const double recip = 1.0/r;
-	double p = r*ef.rdr + 1.0;
-	int m = static_cast<int> (p);
-	m = std::min(m,ef.nr-1);
-	p -= m;
-	p = std::min(p,1.0);
-	double z2 = ((z2r_spline[m][3]*p + z2r_spline[m][4])*p + z2r_spline[m][5])*p + z2r_spline[m][6];
-	aed(fidx) += z2*recip;
-
-	double z2p = (z2r_spline[m][0]*p + z2r_spline[m][1])*p + z2r_spline[m][2];
-	// 0.5 b/c full neighbour list is used TODO check
-	fd_ij(fidx,0) = (z2p*recip - z2*recip*recip);
-
-
-}
-size_t D2_EAM::size() {
-	return s;
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
+  double r = rij;
+  const double recip = 1.0/r;
+  double p = r*ef.rdr + 1.0;
+  int m = static_cast<int> (p);
+  m = std::min(m,ef.nr-1);
+  p -= m;
+  p = std::min(p,1.0);
+  double z2 = ((z2r_spline[m][3]*p + z2r_spline[m][4])*p + z2r_spline[m][5])*p + z2r_spline[m][6];
+  aed(fidx) += scale*z2*recip;
+
+  double z2p = (z2r_spline[m][0]*p + z2r_spline[m][1])*p + z2r_spline[m][2];
+  // 0.5 b/c full neighbour list is used TODO check
+  fd_ij(fidx,0) = scale*(z2p*recip - z2*recip*recip);
 }
 std::string D2_EAM::label() {
-	return lab;
+  return lab;
 }
 
 void D2_EAM::read_setfl()
 {
-    std::string line;
-    std::ifstream in_file(ef.file_path);
-    if (!in_file.good())
-        throw std::runtime_error("SETFL file does not exists.\n");
-
-    if (in_file.is_open()) {
-        if (verbose)
-            std::cout << std::endl << "<D2_EAM> Reading setfl: " << ef.file_path << std::endl;
-        // skip ficgridt 3 comment lines
-        getline (in_file,line);
-        getline (in_file,line);
-        getline (in_file,line);
-        // skip number of types and types
-        getline (in_file,line);
-        // read 5th line
-        in_file >> ef.nrho >> ef.drho >> ef.nr >> ef.dr >> ef.rcut;
-        in_file >> ef.atomic_number >> ef.atomic_mass >> ef.lattice_param >> ef.lattice;
-        ef.rdr = 1.0/ef.dr;
-        ef.rdrho = 1.0/ef.drho;
-        // prepare arrays
-        ef.frho.resize(ef.nrho);
-        ef.rhor.resize(ef.nr);
-        ef.z2r.resize(ef.nr);
-        // read all data
-        for (int i=0; i<ef.nrho; ++i) in_file >> ef.frho[i];
-        for (int i=0; i<ef.nr; ++i) in_file >> ef.rhor[i];
-        for (int i=0; i<ef.nr; ++i) in_file >> ef.z2r[i];
-        in_file.close();
-    }
-    else {
-        if (verbose) std::cout << "<D2_EAM> Unable to open file: " << ef.file_path << std::endl;;
-    }
+  std::string line;
+  std::ifstream in_file(ef.file_path);
+  if (!in_file.good())
+    throw std::runtime_error("SETFL file does not exists.\n");
+
+  if (in_file.is_open()) {
+    if (verbose)
+      std::cout << std::endl << "<D2_EAM> Reading setfl: " << ef.file_path << std::endl;
+    // skip ficgridt 3 comment lines
+    getline (in_file,line);
+    getline (in_file,line);
+    getline (in_file,line);
+    // skip number of types and types
+    getline (in_file,line);
+    // read 5th line
+    in_file >> ef.nrho >> ef.drho >> ef.nr >> ef.dr >> ef.rcut;
+    in_file >> ef.atomic_number >> ef.atomic_mass >> ef.lattice_param >> ef.lattice;
+    ef.rdr = 1.0/ef.dr;
+    ef.rdrho = 1.0/ef.drho;
+    // prepare arrays
+    ef.frho.resize(ef.nrho);
+    ef.rhor.resize(ef.nr);
+    ef.z2r.resize(ef.nr);
+    // read all data
+    for (int i=0; i<ef.nrho; ++i) in_file >> ef.frho[i];
+    for (int i=0; i<ef.nr; ++i) in_file >> ef.rhor[i];
+    for (int i=0; i<ef.nr; ++i) in_file >> ef.z2r[i];
+    in_file.close();
+  }
+  else {
+    if (verbose) std::cout << "<D2_EAM> Unable to open file: " << ef.file_path << std::endl;;
+  }
 }
 void D2_EAM::gen_splines(int &n, double &delta, std::vector<double> &f,
-		std::vector<std::vector<double>> &spline)
+                         std::vector<std::vector<double>> &spline)
 {
-	// in lammps f is n+1, here is size n
-	for (int m=1; m<=n; m++) spline[m][6] = f[m-1];
-
-	spline[1][5] = spline[2][6] - spline[1][6];
-	spline[2][5] = 0.5 * (spline[3][6]-spline[1][6]);
-	spline[n-1][5] = 0.5 * (spline[n][6]-spline[n-2][6]);
-	spline[n][5] = spline[n][6] - spline[n-1][6];
-
-	for (int m = 3; m <= n-2; m++)
-		spline[m][5] = ((spline[m-2][6]-spline[m+2][6]) +
-				8.0*(spline[m+1][6]-spline[m-1][6])) / 12.0;
-
-	for (int m = 1; m <= n-1; m++) {
-		spline[m][4] = 3.0*(spline[m+1][6]-spline[m][6]) - 2.0*spline[m][5] - spline[m+1][5];
-		spline[m][3] = spline[m][5] + spline[m+1][5] - 2.0*(spline[m+1][6]-spline[m][6]);
-	}
-
-	spline[n][4] = 0.0;
-	spline[n][3] = 0.0;
-
-	for (int m = 1; m <= n; m++) {
-		spline[m][2] = spline[m][5]/delta;
-		spline[m][1] = 2.0*spline[m][4]/delta;
-		spline[m][0] = 3.0*spline[m][3]/delta;
-	}
+  // in lammps f is n+1, here is size n
+  for (int m=1; m<=n; m++) spline[m][6] = f[m-1];
+
+  spline[1][5] = spline[2][6] - spline[1][6];
+  spline[2][5] = 0.5 * (spline[3][6]-spline[1][6]);
+  spline[n-1][5] = 0.5 * (spline[n][6]-spline[n-2][6]);
+  spline[n][5] = spline[n][6] - spline[n-1][6];
+
+  for (int m = 3; m <= n-2; m++)
+    spline[m][5] = ((spline[m-2][6]-spline[m+2][6]) +
+      8.0*(spline[m+1][6]-spline[m-1][6])) / 12.0;
+
+  for (int m = 1; m <= n-1; m++) {
+    spline[m][4] = 3.0*(spline[m+1][6]-spline[m][6]) - 2.0*spline[m][5] - spline[m+1][5];
+    spline[m][3] = spline[m][5] + spline[m+1][5] - 2.0*(spline[m+1][6]-spline[m][6]);
+  }
+
+  spline[n][4] = 0.0;
+  spline[n][3] = 0.0;
+
+  for (int m = 1; m <= n; m++) {
+    spline[m][2] = spline[m][5]/delta;
+    spline[m][1] = 2.0*spline[m][4]/delta;
+    spline[m][0] = 3.0*spline[m][3]/delta;
+  }
+}
+void D2_EAM::init() {
+  nparams=1;
 }
 
diff --git a/src/d2_lj.cpp b/src/d2_lj.cpp
index b5ba992f05174bd2b10071aaa60381952b8b9539..3d4158322588b40d8d411a81a25ed85d4a1c380e 100644
--- a/src/d2_lj.cpp
+++ b/src/d2_lj.cpp
@@ -1,59 +1,77 @@
+#include "tadah/models/descriptors/d2/d2_base.h"
 #include <tadah/models/descriptors/d2/d2_lj.h>
 
-D2_LJ::D2_LJ(Config &c):
-    verbose(c.get<int>("VERBOSE"))
+D2_LJ::D2_LJ() {
+  init();
+}
+D2_LJ::D2_LJ(Config &c): D2_Base(c)
 {
-    if (!c.get<bool>("INIT2B")) {
-        s=0;
-    }
+  if (!c.get<bool>("INIT2B")) return;
+  init();
+  s=2;
+
+  auto init_atoms = get_init_atoms(c);
+  init_for_atoms(init_atoms);
 }
 
 void D2_LJ::calc_aed(
-        const double,
-        const double rij_sq,
-        const double fc_ij,
-        aed_type2 &aed)
+  const int Zi,
+  const int Zj,
+  const double rij,
+  const double rij_sq,
+  aed_type &aed,
+  const double scale)
 {
-    double r2_inv = 1.0/rij_sq;
-    double r6_inv = r2_inv*r2_inv*r2_inv;
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
+  double r2_inv = 1.0/rij_sq;
+  double r6_inv = r2_inv*r2_inv*r2_inv;
+  double fc_ij = fcut->calc(rij);
 
-    aed(fidx) -= r6_inv*fc_ij;
-    aed(fidx+1) += r6_inv*r6_inv*fc_ij;
+  aed(fidx) -= scale*weights[Zj]*(r6_inv*fc_ij);
+  aed(fidx+1) += scale*weights[Zj]*(r6_inv*r6_inv*fc_ij);
 }
 void D2_LJ::calc_dXijdri(
-        const double rij,
-        const double rij_sq,
-        const double fc_ij,
-        const double fcp_ij,
-        fd_type &fd_ij)
+  const int Zi,
+  const int Zj,
+  const double rij,
+  const double rij_sq,
+  fd_type &fd_ij,
+  const double scale)
 {
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
 
-    double r2_inv = 1.0/rij_sq;
-    double r6_inv = r2_inv*r2_inv*r2_inv;
+  double r2_inv = 1.0/rij_sq;
+  double r6_inv = r2_inv*r2_inv*r2_inv;
 
-    fd_ij(fidx,0) = 6.0*r6_inv*r2_inv*rij*fc_ij - fcp_ij*r6_inv;
-    fd_ij(fidx+1,0) = -12.0*r6_inv*r6_inv*r2_inv*rij*fc_ij + fcp_ij*r6_inv*r6_inv;
+  double fc_ij = fcut->calc(rij);
+  double fcp_ij = fcut->calc_prime(rij);
+  fd_ij(fidx,0) =scale*weights[Zj]*( 6.0*r6_inv*r2_inv*rij*fc_ij - fcp_ij*r6_inv);
+  fd_ij(fidx+1,0) =scale*weights[Zj]*( -12.0*r6_inv*r6_inv*r2_inv*rij*fc_ij + fcp_ij*r6_inv*r6_inv);
 }
 void D2_LJ::calc_all(
-        const double rij,
-        const double rij_sq,
-        const double fc_ij,
-        const double fcp_ij,
-        aed_type2 &aed,
-        fd_type &fd_ij)
+  const int Zi,
+  const int Zj,
+  const double rij,
+  const double rij_sq,
+  aed_type &aed,
+  fd_type &fd_ij,
+  const double scale)
 {
-    double r2_inv = 1.0/rij_sq;
-    double r6_inv = r2_inv*r2_inv*r2_inv;
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
+  double r2_inv = 1.0/rij_sq;
+  double r6_inv = r2_inv*r2_inv*r2_inv;
 
-    aed(fidx) -= r6_inv*fc_ij;
-    aed(fidx+1) += r6_inv*r6_inv*fc_ij;
+  double fc_ij = fcut->calc(rij);
+  double fcp_ij = fcut->calc_prime(rij);
+  aed(fidx) -= scale*weights[Zj]*r6_inv*fc_ij;
+  aed(fidx+1) += scale*weights[Zj]*r6_inv*r6_inv*fc_ij;
 
-    fd_ij(fidx,0) = 6.0*r6_inv*r2_inv*rij*fc_ij - fcp_ij*r6_inv;
-    fd_ij(fidx+1,0) = -12.0*r6_inv*r6_inv*r2_inv*rij*fc_ij + fcp_ij*r6_inv*r6_inv;
-}
-size_t D2_LJ::size() {
-    return s;
+  fd_ij(fidx,0) = scale*weights[Zj]*(6.0*r6_inv*r2_inv*rij*fc_ij - fcp_ij*r6_inv);
+  fd_ij(fidx+1,0) = scale*weights[Zj]*(-12.0*r6_inv*r6_inv*r2_inv*rij*fc_ij + fcp_ij*r6_inv*r6_inv);
 }
 std::string D2_LJ::label() {
-    return lab;
+  return lab;
+}
+void D2_LJ::init() {
+  nparams=0;
 }
diff --git a/src/d2_mie.cpp b/src/d2_mie.cpp
index 1813d477c9a920fda9e8c99bb2a6c5a21dfac6f5..450c2d9f7f5e14818c7498112969cd5ae0a63114 100644
--- a/src/d2_mie.cpp
+++ b/src/d2_mie.cpp
@@ -1,71 +1,81 @@
+#include "tadah/models/descriptors/d2/d2_base.h"
 #include <tadah/models/descriptors/d2/d2_mie.h>
 
-
-D2_MIE::D2_MIE(Config &c):
-    verbose(c.get<int>("VERBOSE"))
+D2_MIE::D2_MIE() {
+  init();
+}
+D2_MIE::D2_MIE(Config &c): D2_Base(c)
 {
-    if (!c.get<bool>("INIT2B")) {
-        s=0;
-        return;
-    }
-    get_grid(c,"SGRID2B",etas);
-    if (etas.size()!=2) {
-        throw std::runtime_error("Number of elements in SGRID2B is != 2\n\
-                Mie descriptor requires SGRID2B with two positive elements.\n");
-    }
-    n=etas[1];
-    m=etas[0];
-    if (n<0 || m<0) {
-        throw std::runtime_error("Both Mie exponents must by positive\n");
-    }
+  if (!c.get<bool>("INIT2B")) return;
+  init();
+  s=2;
+  n = c.get<double>("TYPE2B",1);
+  m = c.get<double>("TYPE2B",2);
+  if (n<0 || m<0) {
+    throw std::runtime_error("Both Mie exponents must by positive\n");
+  }
+  auto init_atoms = get_init_atoms(c);
+  init_for_atoms(init_atoms);
 }
 
 void D2_MIE::calc_aed(
-        const double rij,
-        const double,
-        const double fc_ij,
-        aed_type2 &aed)
+  const int Zi,
+  const int Zj,
+  const double rij,
+  const double,
+  aed_type &aed,
+  const double scale)
 {
-    double rn_inv = 1.0/pow(rij,n);
-    double rm_inv = 1.0/pow(rij,m);
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
+  double rn_inv = 1.0/pow(rij,n);
+  double rm_inv = 1.0/pow(rij,m);
+  double fc_ij = fcut->calc(rij);
 
-    aed(fidx) -= rn_inv*fc_ij;
-    aed(fidx+1) += rm_inv*fc_ij;
+  aed(fidx) -= scale*weights[Zj]*rn_inv*fc_ij;
+  aed(fidx+1) += scale*weights[Zj]*rm_inv*fc_ij;
 }
 void D2_MIE::calc_dXijdri(
-        const double rij,
-        const double,
-        const double fc_ij,
-        const double fcp_ij,
-        fd_type &fd_ij)
+  const int Zi,
+  const int Zj,
+  const double rij,
+  const double,
+  fd_type &fd_ij,
+  const double scale)
 {
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
+  double fc_ij = fcut->calc(rij);
+  double fcp_ij = fcut->calc_prime(rij);
 
-    fd_ij(fidx,0)   = n/pow(rij,n+1)*fc_ij - fcp_ij/pow(rij,n);
-    fd_ij(fidx+1,0) = -m/pow(rij,m+1)*fc_ij + fcp_ij/pow(rij,m);
-
+  fd_ij(fidx,0)   = scale*weights[Zj]*(n/pow(rij,n+1)*fc_ij - fcp_ij/pow(rij,n));
+  fd_ij(fidx+1,0) = scale*weights[Zj]*(-m/pow(rij,m+1)*fc_ij + fcp_ij/pow(rij,m));
 }
 void D2_MIE::calc_all(
-        const double rij,
-        const double,
-        const double fc_ij,
-        const double fcp_ij,
-        aed_type2 &aed,
-        fd_type &fd_ij)
+  const int Zi,
+  const int Zj,
+  const double rij,
+  const double,
+  aed_type &aed,
+  fd_type &fd_ij,
+  const double scale)
 {
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
 
-    double rn_inv = 1.0/pow(rij,n);
-    double rm_inv = 1.0/pow(rij,m);
+  double rn_inv = 1.0/pow(rij,n);
+  double rm_inv = 1.0/pow(rij,m);
 
-    aed(fidx) -= rn_inv*fc_ij;
-    aed(fidx+1) += rm_inv*fc_ij;
+  double fc_ij = fcut->calc(rij);
+  double fcp_ij = fcut->calc_prime(rij);
 
-    fd_ij(fidx,0) = n*rn_inv/rij*fc_ij - fcp_ij*rn_inv;
-    fd_ij(fidx+1,0) = -m*rm_inv/rij*fc_ij + fcp_ij*rm_inv;
+  aed(fidx) -= scale*weights[Zj]*rn_inv*fc_ij;
+  aed(fidx+1) += scale*weights[Zj]*rm_inv*fc_ij;
+
+  fd_ij(fidx,0) = scale*weights[Zj]*(n*rn_inv/rij*fc_ij - fcp_ij*rn_inv);
+  fd_ij(fidx+1,0) = scale*weights[Zj]*(-m*rm_inv/rij*fc_ij + fcp_ij*rm_inv);
 
-}
-size_t D2_MIE::size() {
-    return s;
 }
 std::string D2_MIE::label() {
-    return lab;
+  return lab;
+}
+void D2_MIE::init() {
+  nparams=2;
 }
diff --git a/src/d2_mjoin.cpp b/src/d2_mjoin.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7988ea337f62bd5441dced1771e225016a801e75
--- /dev/null
+++ b/src/d2_mjoin.cpp
@@ -0,0 +1,61 @@
+#include <cstddef>
+#include <tadah/models/descriptors/d2/d2_mjoin.h>
+
+D2_mJoin::D2_mJoin() {
+  init();
+}
+D2_mJoin::~D2_mJoin() {
+  for (auto d : ds) if (d) delete d;
+}
+
+D2_mJoin::D2_mJoin(Config &c) : D2_Base(c) {
+  init();
+  configs = parse_config(c, "TYPE2B");
+  expand_grids(c, configs, "TYPE2B");
+
+  for (auto &c1 : configs) {
+    ds.push_back(CONFIG::factory<D2_Base, Config&>(c1.get<std::string>("TYPE2B"), c1));
+    s += ds.back()->size();
+  }
+}
+
+void D2_mJoin::calc_aed(const int Zi, const int Zj, const double rij,
+                        const double rij_sq, aed_type &aed, const double scale) {
+  for (auto d : ds) {
+    d->calc_aed(Zi, Zj, rij, rij_sq, aed, scale);
+  }
+}
+
+void D2_mJoin::calc_dXijdri(const int Zi, const int Zj, const double rij,
+                            const double rij_sq, fd_type &fd_ij, const double scale) {
+  for (auto d : ds) {
+    d->calc_dXijdri(Zi, Zj, rij, rij_sq, fd_ij, scale);
+  }
+}
+
+void D2_mJoin::calc_all(const int Zi, const int Zj, const double rij,
+                        const double rij_sq, aed_type &aed, fd_type &fd_ij, const double scale) {
+  for (auto d : ds) {
+    d->calc_all(Zi, Zj, rij, rij_sq, aed, fd_ij, scale);
+  }
+}
+
+std::string D2_mJoin::label() {
+  return lab;
+}
+
+void D2_mJoin::set_fidx(size_t fidx_) {
+  ds[0]->set_fidx(fidx_);
+  size_t s = 0;
+  for (size_t i = 1; i < ds.size(); ++i) {
+    s += ds[i - 1]->size();
+    ds[i]->set_fidx(fidx_ + s);
+  }
+}
+
+void D2_mJoin::init() {
+  keys.push_back("TYPE2B");
+  keys.push_back("RCTYPE2B");
+  keys.push_back("RCUT2B");
+  nparams=std::numeric_limits<size_t>::max();
+}
diff --git a/src/d2_zbl.cpp b/src/d2_zbl.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..90542c77445c885ee858139fe391348f1fe3ddd8
--- /dev/null
+++ b/src/d2_zbl.cpp
@@ -0,0 +1,133 @@
+#include <tadah/models/descriptors/d2/d2_zbl.h>
+
+D2_ZBL::D2_ZBL() {
+  init();
+}
+D2_ZBL::D2_ZBL(Config &c): D2_Base(c)
+{
+  if (!c.get<bool>("INIT2B")) return;
+  init();
+  s=1;
+  double temp;
+  temp = c.get<double>("TYPE2B",1);
+  if (temp!=-1) a0=temp;
+  temp = c.get<double>("TYPE2B",2);
+  if (temp!=-1) s0=temp;
+  temp = c.get<double>("TYPE2B",3);
+  if (temp!=-1) p0=temp;
+  temp = c.get<double>("TYPE2B",4);
+  if (temp!=-1) p1=temp;
+  
+  if (verbose) 
+    std::cout << label() << ": " << a0 << " "
+      << s0 << " " << p0 << " " << p1 << std::endl;
+
+  ntypes = c.size("ATOMS");
+  a = new double*[ntypes];
+  for (int i = 0; i < ntypes; ++i) {
+    a[i] = new double[ntypes];
+  }
+  for (int i=0; i<ntypes; ++i) {
+    std::string symboli = c.get<std::string>("ATOMS",i);
+    int Zi = PeriodicTable::find_by_symbol(symboli).Z;
+    types[Zi] = i;
+    for (int j=0; j<ntypes; ++j) {
+      std::string symbolj = c.get<std::string>("ATOMS",j);
+      int Zj = PeriodicTable::find_by_symbol(symbolj).Z;
+      a[i][j] = screening_length(Zi,Zj);
+    }
+  }
+  auto init_atoms = get_init_atoms(c);
+  init_for_atoms(init_atoms);
+}
+D2_ZBL::~D2_ZBL() {
+  if (a) {
+    for (int i = 0; i < ntypes; ++i) {
+      delete[] a[i];  
+    }
+    delete[] a;        
+    a = nullptr;       
+  }
+}
+
+void D2_ZBL::calc_aed(
+  const int Zi,
+  const int Zj,
+  const double rij,
+  const double,
+  aed_type &aed,
+  const double scale)
+{
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
+  double fc_ij = fcut->calc(rij);
+  double a_ = a[types[Zi]][types[Zj]];
+  double x = rij / a_;
+  double p = phi(x);
+  aed(fidx) += scale*weights[Zj]*(Zi * Zj / rij) * p * fc_ij;
+}
+void D2_ZBL::calc_dXijdri(
+  const int Zi,
+  const int Zj,
+  const double rij,
+  const double,
+  fd_type &fd_ij,
+  const double scale)
+{
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
+  double r_inv = 1.0/rij;
+  double a_ = a[types[Zi]][types[Zj]];
+  double x = rij / a_;
+  double p = phi(x);
+  double dp = dphi(x);
+  double fc_ij = fcut->calc(rij);
+  double fcp_ij = fcut->calc_prime(rij);
+
+  double E_ZBL = (Zi * Zj * r_inv) * p;
+  double dE_ZBL_r = -(Zi * Zj * r_inv*r_inv)*p + dp*(Zi * Zj * r_inv) / a_ ;
+  fd_ij(fidx,0) = scale*weights[Zj]*(E_ZBL*fcp_ij + dE_ZBL_r*fc_ij);
+}
+void D2_ZBL::calc_all(
+  const int Zi,
+  const int Zj,
+  const double rij,
+  const double,
+  aed_type &aed,
+  fd_type &fd_ij,
+  const double scale)
+{
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
+  double r_inv = 1.0/rij;
+  double a_ = a[types[Zi]][types[Zj]];
+  double x = rij / a_;
+  double p = phi(x);
+  double dp = dphi(x);
+
+  double fc_ij = fcut->calc(rij);
+  double fcp_ij = fcut->calc_prime(rij);
+
+  aed(fidx) += scale*weights[Zj]*(Zi * Zj * r_inv) * p * fc_ij;
+  double E_ZBL = (Zi * Zj * r_inv) * p;
+  double dE_ZBL_r = -(Zi * Zj * r_inv*r_inv)*p + dp*(Zi * Zj * r_inv) / a_ ;
+  fd_ij(fidx,0) = scale*weights[Zj]*(E_ZBL*fcp_ij + dE_ZBL_r*fc_ij);
+}
+std::string D2_ZBL::label() {
+  return lab;
+}
+void D2_ZBL::init() {
+  nparams=4;
+}
+double D2_ZBL::phi(double x) {
+  return 0.1818 * std::exp(-3.2 * x) +
+  0.5099 * std::exp(-0.9423 * x) +
+  0.2802 * std::exp(-0.4029 * x) +
+  0.02817 * std::exp(-0.2016 * x);
+}
+double D2_ZBL::dphi(double x) {
+  return -3.2 * 0.1818 * std::exp(-3.2 * x) -
+  0.9423 * 0.5099 * std::exp(-0.9423 * x) -
+  0.4029 * 0.2802 * std::exp(-0.4029 * x) -
+  0.2016 * 0.02817 * std::exp(-0.2016 * x);
+}
+double D2_ZBL::screening_length(int Zi, int Zj) {
+  return (s0 * a0) / (std::pow(Zi, p0) + std::pow(Zj, p1));
+}
diff --git a/src/d3_all.cpp b/src/d3_all.cpp
index e87c091bb7445a89188cac9604ab20734565a02a..ef0e046d12eb576d18e5192a9e6b89f1efc3d1d2 100644
--- a/src/d3_all.cpp
+++ b/src/d3_all.cpp
@@ -1,6 +1,8 @@
 #include <tadah/models/descriptors/d3/d3_base.h>
 #include <tadah/models/descriptors/d3/d3_dummy.h>
 
+template<> CONFIG::Registry<D3_Base>::Map CONFIG::Registry<D3_Base>::registry{};
 template<> CONFIG::Registry<D3_Base,Config&>::Map CONFIG::Registry<D3_Base,Config&>::registry{};
 
+CONFIG::Registry<D3_Base>::Register<D3_Dummy> D3_Dummy_0( "D3_Dummy" );
 CONFIG::Registry<D3_Base,Config&>::Register<D3_Dummy> D3_Dummy_1( "D3_Dummy" );
diff --git a/src/d3_base.cpp b/src/d3_base.cpp
index bf59bc896569e2267f1bc1deb8cde5d32d56e769..fe5fa6932ef38b78dac3fd4d1bdf9f5f990629df 100644
--- a/src/d3_base.cpp
+++ b/src/d3_base.cpp
@@ -1,3 +1,6 @@
 #include <tadah/models/descriptors/d3/d3_base.h>
 //template struct Registry<D3_Base, Config &>;
+std::vector<std::string> D3_Base::get_init_atoms(Config &c) {
+  return {};
+}
 
diff --git a/src/d3_dummy.cpp b/src/d3_dummy.cpp
index 049d6a321880ffa736aad9e7d0d9f6002cfd8b9b..e8354d96866879e77c3269a6306c1b7da4e1b728 100644
--- a/src/d3_dummy.cpp
+++ b/src/d3_dummy.cpp
@@ -1,7 +1,11 @@
 #include <tadah/models/descriptors/d3/d3_dummy.h>
 
-D3_Dummy::D3_Dummy() {}
-D3_Dummy::D3_Dummy(Config &) {}
+D3_Dummy::D3_Dummy() {
+  init();
+}
+D3_Dummy::D3_Dummy(Config &) {
+  init();
+}
 
 void D3_Dummy::calc_aed(
         const size_t,
@@ -9,7 +13,7 @@ void D3_Dummy::calc_aed(
         const double,
         const double,
         const double,
-        aed_type2& ) {}
+        aed_type& ) {}
 void D3_Dummy::calc_fd(
         const size_t,
         const double,
@@ -27,7 +31,10 @@ void D3_Dummy::calc_all(
         const double,
         const double,
         const double,
-        aed_type2& ,
+        aed_type& ,
         fd_type &) {}
 size_t D3_Dummy::size() { return s;}
 std::string D3_Dummy::label() {return lab;}
+void D3_Dummy::init() {
+  nparams=0;
+}
diff --git a/src/d_base.cpp b/src/d_base.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bb1e15831abb9bd00a59a8d82dc4dbbc7197c1ce
--- /dev/null
+++ b/src/d_base.cpp
@@ -0,0 +1,154 @@
+#include "tadah/core/core_types.h"
+#include "tadah/core/utils.h"
+#include <string>
+#include <tadah/models/descriptors/d_base.h>
+#include <vector>
+void D_Base::get_grid(const Config &c, std::string key, v_type &v) {
+  if (c.get<double>(key) < 0) {
+    // generate grid automatically if first element
+    // is<zero and int. If it is double than assume that
+    // grid is provided manually and min value is just smaller
+    // than zero.
+    double int_part;
+    if (std::modf ( c.get<double>(key), &int_part ) == 0.0) {
+      // automatic generation
+      size_t cg = abs(c.get<int>(key,1));
+      double cgmin = c.get<double>(key,2);
+      double cgmax = c.get<double>(key,3);
+      if (c.get<int>(key) == -1) {
+        v = linspace(cgmin, cgmax, cg);
+      }
+      else if (c.get<int>(key) == -2) {
+        v = logspace(cgmin, cgmax, cg, exp(1));
+      }
+      else {
+        throw std::runtime_error(key+" algorithm not supported\n");
+      }
+    }
+    else {
+      v.resize(c.size(key));
+      c.get<v_type>(key,v);
+    }
+  }
+  else {
+    v.resize(c.size(key));
+    c.get<v_type>(key,v);
+  }
+}
+v_type D_Base::get_grid(std::vector<std::string>&vold) {
+  v_type vnew;
+  auto it = vold.begin();
+  while (it != vold.end()) {
+    std::string token = *it++;
+    if (token == "-1") {
+      size_t cg = std::stoul(*it++);
+      double cgmin = std::stod(*it++);
+      double cgmax = std::stod(*it++);
+      v_type v = linspace(cgmin, cgmax, cg);
+      vnew.insert(vnew.end(), v.begin(), v.end());
+    }
+    else if (token == "-2") {
+      size_t cg = std::stoul(*it++);
+      double cgmin = std::stod(*it++);
+      double cgmax = std::stod(*it++);
+      v_type v = logspace(cgmin, cgmax, cg, exp(1));
+      vnew.insert(vnew.end(), v.begin(), v.end());
+    }
+    else if (std::stod(token) < -2) {
+      throw std::runtime_error("Either algorithm is not supported or negative value was encountered in a grid: " + token);
+    }
+    else {
+      vnew.push_back(std::stod(token));
+    }
+  }
+  return vnew;
+}
+D_Base::~D_Base() {
+  if (this->manage_memory && fcut) {
+    delete fcut;
+  }
+}
+
+D_Base::D_Base() {}
+D_Base::D_Base(Config &c):
+  verbose(c.get<int>("VERBOSE"))
+{
+  std::fill_n(weights, 119, 1.0);
+  if (c.exist("ATOMS")) {
+    for (size_t i=0; i<c.size("ATOMS"); ++i) {
+      std::string symbol = c.get<std::string>("ATOMS",i);
+      double wi = c.get<double>("WATOMS",i);
+      int Z = PeriodicTable::find_by_symbol(symbol).Z;
+      weights[Z]=wi;
+    }
+  }
+}
+void D_Base::set_fidx(size_t fidx_) { fidx=fidx_; }
+size_t D_Base::get_fidx() { return fidx; }
+void D_Base::set_fcut(Cut_Base* cut, bool manage_memory) {
+  if (this->manage_memory && fcut != cut) {
+    delete fcut;
+  }
+  fcut = cut;
+  this->manage_memory = manage_memory;
+}
+int D_Base::get_arg_pos(const std::string &key) const {
+  auto it = std::find(keys.begin(), keys.end(), key);
+
+  if (it == keys.end()) return -1;
+
+  return std::distance(keys.begin(), it)+(nparams-keys.size());
+}
+size_t D_Base::size() { return s; };
+double D_Base::get_rcut() { 
+  return fcut->get_rcut(); 
+}
+bool D_Base::is_init_for_atoms(int Zi, int Zj) {
+  return init_for_atoms_map.is_init(Zi,Zj) ;
+}
+void D_Base::init_for_atoms(int Zi, int Zj) {
+  init_for_atoms_map.init(Zi,Zj);
+}
+void D_Base::uninit_for_atoms(int Zi, int Zj) {
+  init_for_atoms_map.uninit(Zi,Zj);
+}
+void D_Base::init_for_atoms(const std::vector<std::string> &Zs) {
+  if (Zs.size() % 2 != 0) {
+    throw std::invalid_argument("The vector size must be even.");
+  }
+  
+  for (size_t i = 0; i < Zs.size(); i += 2) {
+    if (Zs[i] == "*" && Zs[i+1] != "*") {
+      int Zj = PeriodicTable::find_by_symbol(Zs[i+1]).Z;
+      for (int Zi = 1; Zi < 119; ++Zi) {
+        init_for_atoms(Zi, Zj);
+      }
+    } 
+    else if (Zs[i] != "*" && Zs[i+1] == "*") {
+      int Zi = PeriodicTable::find_by_symbol(Zs[i]).Z;
+      for (int Zj = 1; Zj < 119; ++Zj) {
+        init_for_atoms(Zi, Zj);
+      }
+    } 
+    else if (Zs[i] == "*" && Zs[i+1] == "*") {
+      for (int Zi = 1; Zi < 119; ++Zi) {
+        for (int Zj = Zi; Zj < 119; ++Zj) {
+          init_for_atoms(Zi, Zj);
+        }
+      }
+    } 
+    else {
+      int Zi = PeriodicTable::find_by_symbol(Zs[i]).Z;
+      int Zj = PeriodicTable::find_by_symbol(Zs[i+1]).Z;
+      init_for_atoms(Zi, Zj);
+    }
+  }
+}
+std::vector<std::string> D_Base::get_init_atoms(Config &c, std::string type) {
+  std::vector<std::string> init_atoms(c.size(type));
+  c.get(type, init_atoms);
+  std::unique_ptr<D_Base> d(CONFIG::factory<D_Base>(init_atoms[0]));
+  size_t nparams = d->nparams;
+  init_atoms.erase(init_atoms.begin(), init_atoms.begin() + nparams);
+  return init_atoms;
+}
diff --git a/src/d_mjoin.cpp b/src/d_mjoin.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9c1e3682847470463bac4b166858ce768017ac11
--- /dev/null
+++ b/src/d_mjoin.cpp
@@ -0,0 +1,188 @@
+#include "tadah/core/registry.h"
+#include <cstddef>
+#include <iostream>
+#include <string>
+#include <tadah/models/descriptors/d_mjoin.h>
+#include <tadah/models/descriptors/d_base.h>
+#include <vector>
+
+D_mJoin::D_mJoin() {}
+D_mJoin::~D_mJoin() {}
+
+std::vector<std::string> convert_to_strings(const v_type& vec, int precision) {
+  std::vector<std::string> result;
+  std::ostringstream oss;
+  oss << std::fixed << std::setprecision(precision);
+
+  for (double num : vec) {
+    oss.str("");   // Clear the stream
+    oss << num;
+    result.push_back(oss.str());
+  }
+
+  return result;
+}
+
+void D_mJoin::expand_grids(Config &c) {
+  if (c.exist("TYPE2B")) {
+    std::vector<Config> configs1 = parse_config(c, "TYPE2B");
+    expand_grids(c,configs1,"TYPE2B");
+  }
+  if (c.exist("TYPEMB")) {
+    std::vector<Config> configs2 = parse_config(c, "TYPEMB");
+    expand_grids(c,configs2,"TYPEMB");
+  }
+}
+void D_mJoin::expand_grids(Config &c, std::string type_str) {
+  std::vector<Config> configs = parse_config(c, type_str);
+  expand_grids(c,configs,type_str);
+}
+void D_mJoin::expand_grids(Config &c, std::vector<Config> &configs,std::string type_str) {
+  if (type_str=="TYPE2B") {
+    c.remove("CGRID2B"); c.remove("SGRID2B");
+    for (auto &c1 : configs) {
+      if (c1.exist("CGRID2B"))
+        for (size_t i = 0; i < c1.size("CGRID2B"); ++i)
+          c.add("CGRID2B", c1.get<double>("CGRID2B", i));
+      if (c1.exist("SGRID2B"))
+        for (size_t i = 0; i < c1.size("SGRID2B"); ++i)
+          c.add("SGRID2B", c1.get<double>("SGRID2B", i));
+    }
+  }
+  else if (type_str=="TYPEMB") {
+    c.remove("CGRIDMB"); c.remove("SGRIDMB");
+    c.remove("CEMBFUNC"); c.remove("SEMBFUNC");
+
+    for (auto &c1 : configs) {
+      if (c1.exist("CGRIDMB"))
+        for (size_t i = 0; i < c1.size("CGRIDMB"); ++i)
+          c.add("CGRIDMB", c1.get<double>("CGRIDMB", i));
+      if (c1.exist("SGRIDMB"))
+        for (size_t i = 0; i < c1.size("SGRIDMB"); ++i)
+          c.add("SGRIDMB", c1.get<double>("SGRIDMB", i));
+      if (c1.exist("CEMBFUNC"))
+        for (size_t i = 0; i < c1.size("CEMBFUNC"); ++i)
+          c.add("CEMBFUNC", c1.get<double>("CEMBFUNC", i));
+      if (c1.exist("SEMBFUNC"))
+        for (size_t i = 0; i < c1.size("SEMBFUNC"); ++i)
+          c.add("SEMBFUNC", c1.get<double>("SEMBFUNC", i));
+    }
+  }
+  else {
+    std::cerr << "Cannot expand config grid: wrong keyword: " << type_str << std::endl;
+  }
+}
+std::vector<Config> D_mJoin::parse_config(Config &c, std::string type_str) {
+  std::string prefix = type_str=="TYPE2B" ? "D2_" : "DM_";
+  std::string rctypekey = "RC" + type_str;
+  std::string rcutkey = "RCUT" + type_str.substr(type_str.size() - 2);
+  std::string cgridkey = "CGRID" + type_str.substr(type_str.size() - 2);
+  std::string sgridkey = "SGRID" + type_str.substr(type_str.size() - 2);
+
+  std::map<std::string, std::vector<std::string>::iterator> data_map;
+
+  std::vector<std::string> types(c.size(type_str)); // including params TYPE2B D2_Blip 7 7
+  c.get(type_str, types);
+  //types.erase(types.begin()); // remove D2_mJoin or DM_mJoin
+  std::string pattern="mJoin";
+  types.erase(
+    std::remove_if(types.begin(), types.end(), [&pattern](const std::string& s) {
+      return s.find(pattern) != std::string::npos;
+    }),
+    types.end()
+  );
+  
+  std::vector<std::string> rctypes(c.size(rctypekey));
+  c.get(rctypekey, rctypes);
+  data_map[rctypekey]=rctypes.begin();
+
+  std::vector<std::string> rcut(c.size(rcutkey));
+  c.get(rcutkey, rcut);
+  data_map[rcutkey]=rcut.begin();
+
+  size_t p = c.get<size_t>("OUTPREC");
+  std::vector<std::string> cgrid;
+  if (c.exist(cgridkey)) {
+    cgrid.resize(c.size(cgridkey));
+    c.get(cgridkey, cgrid);
+    cgrid = convert_to_strings(D_Base::get_grid(cgrid),p); 
+    data_map[cgridkey] = cgrid.begin();
+  }
+  std::vector<std::string> sgrid;
+  if (c.exist(sgridkey)) {
+    sgrid.resize(c.size(sgridkey));
+    c.get(sgridkey, sgrid);
+    sgrid = convert_to_strings(D_Base::get_grid(sgrid),p); 
+    data_map[sgridkey] = sgrid.begin();
+  }
+  std::vector<std::string> cembgrid;
+  if (c.exist("CEMBFUNC")) {
+    cembgrid.resize(c.size("CEMBFUNC"));
+    c.get("CEMBFUNC", cembgrid);
+    cembgrid = convert_to_strings(D_Base::get_grid(cembgrid),p); 
+    data_map["CEMBFUNC"] = cembgrid.begin();
+  }
+  std::vector<std::string> sembgrid;
+  if (c.exist("SEMBFUNC")) {
+    sembgrid.resize(c.size("SEMBFUNC"));
+    c.get("SEMBFUNC", sembgrid);
+    sembgrid = convert_to_strings(D_Base::get_grid(sembgrid),p); 
+    data_map["SEMBFUNC"] = sembgrid.begin();
+  }
+
+  std::vector<Config> configs;
+  for (auto it = types.begin(); it != types.end();) {
+    Config c1 = c;
+    c1.remove(type_str); c1.remove(rctypekey); c1.remove(rcutkey);
+    c1.remove(cgridkey); c1.remove(sgridkey);
+    const auto token = *it++;
+    c1.add(type_str,token);
+    c1.add(rctypekey,*(data_map[rctypekey]++));
+    c1.add(rcutkey,*(data_map[rcutkey]++));
+    D_Base *d = CONFIG::factory<D_Base>(token);
+    size_t fparam = d->nparams - d->keys.size();
+    for (size_t j=0; j<fparam; ++j) {
+      c1.add(type_str,*it++);
+    }
+
+    for (const auto& k : d->keys) {
+      int n = std::stoi(*it++);
+      c1.add(type_str,n);
+      for (int j = 0; j < n; ++j) {
+        c1.add(k,*(data_map[k]++));
+      } 
+    }
+
+    // read init atoms
+    size_t found_init_atoms=0;
+    while (it != types.end()) {
+      if (it->find(prefix) == 0) {
+        break;
+      }
+      c1.add(type_str,*it++);
+      found_init_atoms++;
+    }
+    // if (it != types.end() && it != types.begin()) {
+    //   --it;
+    // }
+    if (!found_init_atoms) {
+      throw std::runtime_error(
+        "Error: No element types specified. "
+        "Please provide element types for this calculator. "
+        "For example: '"+token+" Ti Ti Ti Nb' sets the calculator to compute Ti-Ti and Ti-Nb interactions. "
+        "Token: " + token);
+    }
+
+    if (found_init_atoms % 2 != 0) {
+      throw std::runtime_error(
+        "Error: Element types must be provided in pairs. "
+        "Ensure each element is paired correctly. "
+        "For instance, '" + token + " Ti * Nb Zr' means that interactions of Ti with any atom, as well as Nb-Zr, will be computed. "
+        "Token: " + token);
+    }
+
+    configs.push_back(c1);
+    delete d;
+  }
+  return configs;
+}
diff --git a/src/dm_all.cpp b/src/dm_all.cpp
index 6f0b293fd48a496043b66f826e13829b550c03a8..326872c40105c839cda67802dc309b7f8ef84455 100644
--- a/src/dm_all.cpp
+++ b/src/dm_all.cpp
@@ -1,11 +1,27 @@
 #include <tadah/models/descriptors/dm/dm_all.h>
 
+template<> CONFIG::Registry<DM_Base>::Map CONFIG::Registry<DM_Base>::registry{};
 template<> CONFIG::Registry<DM_Base,Config&>::Map CONFIG::Registry<DM_Base,Config&>::registry{};
 
-CONFIG::Registry<DM_Base,Config&>::Register<DM_Blip> DM_Blip_1( "DM_Blip" );
-CONFIG::Registry<DM_Base,Config&>::Register<DM_Dummy> DM_Dummy_1( "DM_Dummy" );
-CONFIG::Registry<DM_Base,Config&>::Register<DM_EAD> DM_EAD_1( "DM_EAD" );
-CONFIG::Registry<DM_Base,Config&>::Register<DM_EAM> DM_EAM_1( "DM_EAM" );
-CONFIG::Registry<DM_Base,Config&>::Register<DM_mEAD<F_RLR>> DM_mEAM_1("DM_mEAD");
+CONFIG::Registry<D_Base>::Register<DM_Blip> DM_Blip_0( "DM_Blip" );
+CONFIG::Registry<D_Base>::Register<DM_Dummy> DM_Dummy_0( "DM_Dummy" );
+CONFIG::Registry<D_Base>::Register<DM_EAD> DM_EAD_0( "DM_EAD" );
+CONFIG::Registry<D_Base>::Register<DM_EAM> DM_EAM_0( "DM_EAM" );
+CONFIG::Registry<D_Base>::Register<DM_mEAD<F_RLR>> DM_mEAM_0("DM_mEAD");
+CONFIG::Registry<D_Base>::Register<DM_mJoin> DM_mJoin_0("DM_mJoin");
+
+CONFIG::Registry<DM_Base>::Register<DM_Blip> DM_Blip_1( "DM_Blip" );
+CONFIG::Registry<DM_Base>::Register<DM_Dummy> DM_Dummy_1( "DM_Dummy" );
+CONFIG::Registry<DM_Base>::Register<DM_EAD> DM_EAD_1( "DM_EAD" );
+CONFIG::Registry<DM_Base>::Register<DM_EAM> DM_EAM_1( "DM_EAM" );
+CONFIG::Registry<DM_Base>::Register<DM_mEAD<F_RLR>> DM_mEAM_1("DM_mEAD");
+CONFIG::Registry<DM_Base>::Register<DM_mJoin> DM_mJoin_1("DM_mJoin");
+
+CONFIG::Registry<DM_Base,Config&>::Register<DM_Blip> DM_Blip_2( "DM_Blip" );
+CONFIG::Registry<DM_Base,Config&>::Register<DM_Dummy> DM_Dummy_2( "DM_Dummy" );
+CONFIG::Registry<DM_Base,Config&>::Register<DM_EAD> DM_EAD_2( "DM_EAD" );
+CONFIG::Registry<DM_Base,Config&>::Register<DM_EAM> DM_EAM_2( "DM_EAM" );
+CONFIG::Registry<DM_Base,Config&>::Register<DM_mEAD<F_RLR>> DM_mEAM_2("DM_mEAD");
+CONFIG::Registry<DM_Base,Config&>::Register<DM_mJoin> DM_mJoin_2( "DM_mJoin" );
 //template<> Registry<DM_Base,F_Base&,Config&>::Map Registry<DM_Base,F_Base&,Config&>::registry{};
 //Registry<DM_Base,Config&>::Register<DM_mEAD<F_RLR>> DM_mEAD_1( "DM_mEAD" );
diff --git a/src/dm_base.cpp b/src/dm_base.cpp
index a76605da06946d74afac69f10e502df2037d6e3d..7b9f557c26bfb2f37fe564f464cfb29f981f318e 100644
--- a/src/dm_base.cpp
+++ b/src/dm_base.cpp
@@ -1 +1,15 @@
 #include <tadah/models/descriptors/dm/dm_base.h>
+void DM_Base::set_rfidx(size_t rfidx_) { rfidx=rfidx_; }
+size_t DM_Base::get_rfidx() { return rfidx; }
+
+std::vector<std::string> DM_Base::get_init_atoms(Config &c) {
+  if (!c.exist("TYPEMB")) {
+    std::cerr << "Warning: TYPEMB key was not found. "
+      "Atom pairs must be initialized manually." << std::endl;
+    return {};
+  }
+  std::vector<std::string> init_atoms(c.size("TYPEMB"));
+  c.get("TYPEMB", init_atoms);
+  init_atoms.erase(init_atoms.begin(), init_atoms.begin() + nparams + 1);
+  return init_atoms;
+}
diff --git a/src/dm_blip.cpp b/src/dm_blip.cpp
index 85708251949e30ea48e9e05e7f1a2abbc7b48016..48408d27b4d4d1cd18eaaa0d2451773f165d97f0 100644
--- a/src/dm_blip.cpp
+++ b/src/dm_blip.cpp
@@ -1,298 +1,312 @@
+#include "tadah/models/descriptors/dm/dm_base.h"
 #include <tadah/models/descriptors/dm/dm_blip.h>
 #include <tadah/models/descriptors/d_basis_functions.h>
 #include <vector>
-#include <iomanip>
 
-
-DM_Blip::DM_Blip(Config &c):
-    verbose(c.get<int>("VERBOSE")),
-    rc(c.get<double>("RCUTMB"))
+DM_Blip::DM_Blip() {
+  init();
+}
+DM_Blip::DM_Blip(Config &c): DM_Base(c),
+  rc(c.get<double>("RCUTMBMAX"))
 {
-
-    if (!c.get<bool>("INITMB")) return;
-
-    get_grid(c,"CGRIDMB",cgrid);
-    get_grid(c,"SGRIDMB",sgrid);
-    if (cgrid.size()!=sgrid.size()) {
-
-        throw std::runtime_error("SGRID2B and CGRID2B arrays differ in size.\n");
-    }
-
-    Lmax = c.get<int>("AGRIDMB");
-    s=sgrid.size()*(Lmax+1);
-    rhoisize = gen_atomic_orbitals(Lmax);
-    rhoisize *= sgrid.size();
-
-    if (verbose) {
-        std::cout << std::endl;
-        std::cout << "SGRID (SGRIDMB): " << sgrid.size() << std::endl;
-        for (auto e:sgrid) std::cout << e << "  ";
-        std::cout << std::endl;
-
-        std::cout << "CGRID (CGRIDMB): " << cgrid.size() << std::endl;
-        for (auto L:cgrid) std::cout << L << "  ";
-        std::cout << std::endl;
-        std::cout << "rhoisize: " << rhoisize << std::endl;
-    }
+  if (!c.get<bool>("INITMB")) return;
+  init();
+
+  get_grid(c,"CGRIDMB",cgrid);
+  get_grid(c,"SGRIDMB",sgrid);
+  // update config with generated grid
+  c.remove("CGRIDMB");
+  for (const auto &i: cgrid) c.add("CGRIDMB",i);
+  c.remove("SGRIDMB");
+  for (const auto &i: sgrid) c.add("SGRIDMB",i);
+  if (cgrid.size()!=sgrid.size()) {
+
+    throw std::runtime_error("SGRID2B and CGRID2B arrays differ in size.\n");
+  }
+
+  Lmax = c.get<int>("TYPEMB",1);
+  s=sgrid.size()*(Lmax+1);
+  rhoisize = gen_atomic_orbitals(Lmax);
+  rhoisize *= sgrid.size();
+
+  if (verbose) {
+    std::cout << std::endl;
+    std::cout << "SGRID (SGRIDMB): " << sgrid.size() << std::endl;
+    for (auto e:sgrid) std::cout << e << "  ";
+    std::cout << std::endl;
+
+    std::cout << "CGRID (CGRIDMB): " << cgrid.size() << std::endl;
+    for (auto L:cgrid) std::cout << L << "  ";
+    std::cout << std::endl;
+    std::cout << "rhoisize: " << rhoisize << std::endl;
+  }
+  auto init_atoms = get_init_atoms(c);
+  init_for_atoms(init_atoms);
 }
 
 void DM_Blip::calc_aed(
-        rho_type& rhoi,
-        aed_type2 &aed)
+  rho_type& rhoi,
+  aed_type &aed)
 {
-    size_t Lshift = 0;
-    size_t ii=0;
-    for (int L=0; L<=Lmax; ++L) {
-        for (size_t o=0; o<orbitals[L].size(); ++o) {
-            const double f = fac[L][o];
-            for (size_t c=0; c<cgrid.size(); c++) {
-                aed(c+Lshift+fidx) += f*rhoi(ii)*rhoi(ii);
-                rhoi(ii+rhoisize) = 2.0*f*rhoi(ii);
-                ii++;
-            }
-        }
-        Lshift += sgrid.size();
+  size_t Lshift = 0;
+  size_t ii=rfidx;
+  for (int L=0; L<=Lmax; ++L) {
+    for (size_t o=0; o<orbitals[L].size(); ++o) {
+      const double f = fac[L][o];
+      for (size_t c=0; c<cgrid.size(); c++) {
+        aed(c+Lshift+fidx) += f*rhoi(ii)*rhoi(ii);
+        rhoi(ii+rhoisize) = 2.0*f*rhoi(ii);
+        ii++;
+      }
     }
+    Lshift += sgrid.size();
+  }
 }
-int DM_Blip::calc_dXijdri_dXjidri(
-        const double rij,
-        const double,
-        const Vec3d &vec_ij,
-        const double fc_ij,
-        const double fcp_ij,
-        rho_type& rhoi,
-        rho_type& rhoj,
-        fd_type &fd_ij,
-        const double wi,
-        const double wj)
+void DM_Blip::calc_dXijdri_dXjidri(
+  const int Zi,
+  const int Zj,
+  const double rij,
+  const double,
+  const Vec3d &vec_ij,
+  rho_type& rhoi,
+  rho_type& rhoj,
+  fd_type &fd_ij,
+  const double scale)
 {
-    const double x = vec_ij[0];
-    const double y = vec_ij[1];
-    const double z = vec_ij[2];
-
-    size_t Lshift = 0;
-
-    size_t ii=rhoisize;
-    for (int L=0; L<=Lmax; ++L) {
-        for (size_t o=0; o<orbitals[L].size(); ++o) {
-            const int lx = orbitals[L][o][0];
-            const int ly = orbitals[L][o][1];
-            const int lz = orbitals[L][o][2];
-
-            const double powxlxij = my_pow(x,lx);
-            const double powylyij = my_pow(y,ly);
-            const double powzlzij = my_pow(z,lz);
-
-            const double powxlxji = my_pow(-x,lx);
-            const double powylyji = my_pow(-y,ly);
-            const double powzlzji = my_pow(-z,lz);
-
-            double txij=0.0,tyij=0.0,tzij=0.0;
-            double txji=0.0,tyji=0.0,tzji=0.0;
-            if (lx!=0) {
-                txij = lx*my_pow(x,lx-1)*powylyij*powzlzij;
-                txji = lx*my_pow(-x,lx-1)*powylyji*powzlzji;
-            }
-            if (ly!=0) {
-                tyij = ly*my_pow(y,ly-1)*powxlxij*powzlzij;
-                tyji = ly*my_pow(-y,ly-1)*powxlxji*powzlzji;
-            }
-            if (lz!=0) {
-                tzij = lz*my_pow(z,lz-1)*powylyij*powxlxij;
-                tzji = lz*my_pow(-z,lz-1)*powylyji*powxlxji;
-            }
-            const double pqrij = powxlxij*powylyij*powzlzij;
-            const double pqrji = powxlxji*powylyji*powzlzji;
-
-            for (size_t c=0; c<cgrid.size(); c++) {
-                double blip = B(sgrid[c]*(rij-cgrid[c]),fc_ij);
-                double dblip = dB(sgrid[c]*(rij-cgrid[c]),sgrid[c],fc_ij,fcp_ij);
-                const double term2ijx=pqrij*dblip*x/rij;
-                const double term2ijy=pqrij*dblip*y/rij;
-                const double term2ijz=pqrij*dblip*z/rij;
-
-                const double term2jix=-pqrji*dblip*x/rij;
-                const double term2jiy=-pqrji*dblip*y/rij;
-                const double term2jiz=-pqrji*dblip*z/rij;
-
-                const double term1ijx = blip*txij;
-                const double term1ijy = blip*tyij;
-                const double term1ijz = blip*tzij;
-
-                const double term1jix = blip*txji;
-                const double term1jiy = blip*tyji;
-                const double term1jiz = blip*tzji;
-
-                fd_ij(c+Lshift+fidx,0) +=
-                    rhoi(ii)*(term1ijx + term2ijx)*wj
-                    -rhoj(ii)*(term1jix + term2jix)*wi
-                    ;
-
-                fd_ij(c+Lshift+fidx,1) +=
-                    rhoi(ii)*(term1ijy + term2ijy)*wj
-                    -rhoj(ii)*(term1jiy + term2jiy)*wi
-                    ;
-
-                fd_ij(c+Lshift+fidx,2) += 
-                    rhoi(ii)*(term1ijz + term2ijz)*wj
-                    -rhoj(ii)*(term1jiz + term2jiz)*wi
-                    ;
-                ii++;
-            }
-        }
-        Lshift+=sgrid.size();
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
+
+  const double x = vec_ij[0];
+  const double y = vec_ij[1];
+  const double z = vec_ij[2];
+
+  double wi = weights[Zi];
+  double wj = weights[Zj];
+  double fc_ij = fcut->calc(rij);
+  double fcp_ij = fcut->calc_prime(rij);
+
+  size_t Lshift = 0;
+
+  size_t ii=rhoisize+rfidx;
+  for (int L=0; L<=Lmax; ++L) {
+    for (size_t o=0; o<orbitals[L].size(); ++o) {
+      const int lx = orbitals[L][o][0];
+      const int ly = orbitals[L][o][1];
+      const int lz = orbitals[L][o][2];
+
+      const double powxlxij = my_pow(x,lx);
+      const double powylyij = my_pow(y,ly);
+      const double powzlzij = my_pow(z,lz);
+
+      const double powxlxji = my_pow(-x,lx);
+      const double powylyji = my_pow(-y,ly);
+      const double powzlzji = my_pow(-z,lz);
+
+      double txij=0.0,tyij=0.0,tzij=0.0;
+      double txji=0.0,tyji=0.0,tzji=0.0;
+      if (lx!=0) {
+        txij = lx*my_pow(x,lx-1)*powylyij*powzlzij;
+        txji = lx*my_pow(-x,lx-1)*powylyji*powzlzji;
+      }
+      if (ly!=0) {
+        tyij = ly*my_pow(y,ly-1)*powxlxij*powzlzij;
+        tyji = ly*my_pow(-y,ly-1)*powxlxji*powzlzji;
+      }
+      if (lz!=0) {
+        tzij = lz*my_pow(z,lz-1)*powylyij*powxlxij;
+        tzji = lz*my_pow(-z,lz-1)*powylyji*powxlxji;
+      }
+      const double pqrij = powxlxij*powylyij*powzlzij;
+      const double pqrji = powxlxji*powylyji*powzlzji;
+
+      for (size_t c=0; c<cgrid.size(); c++) {
+        double blip = B(sgrid[c]*(rij-cgrid[c]),fc_ij);
+        double dblip = dB(sgrid[c]*(rij-cgrid[c]),sgrid[c],fc_ij,fcp_ij);
+        const double term2ijx=pqrij*dblip*x/rij;
+        const double term2ijy=pqrij*dblip*y/rij;
+        const double term2ijz=pqrij*dblip*z/rij;
+
+        const double term2jix=-pqrji*dblip*x/rij;
+        const double term2jiy=-pqrji*dblip*y/rij;
+        const double term2jiz=-pqrji*dblip*z/rij;
+
+        const double term1ijx = blip*txij;
+        const double term1ijy = blip*tyij;
+        const double term1ijz = blip*tzij;
+
+        const double term1jix = blip*txji;
+        const double term1jiy = blip*tyji;
+        const double term1jiz = blip*tzji;
+
+        fd_ij(c+Lshift+fidx,0) += scale*(
+          rhoi(ii)*(term1ijx + term2ijx)*wj
+          -rhoj(ii)*(term1jix + term2jix)*wi)
+        ;
+
+        fd_ij(c+Lshift+fidx,1) += scale*(
+          rhoi(ii)*(term1ijy + term2ijy)*wj
+          -rhoj(ii)*(term1jiy + term2jiy)*wi)
+        ;
+
+        fd_ij(c+Lshift+fidx,2) += scale*(
+          rhoi(ii)*(term1ijz + term2ijz)*wj
+          -rhoj(ii)*(term1jiz + term2jiz)*wi)
+        ;
+        ii++;
+      }
     }
-
-    return 1;
-
+    Lshift+=sgrid.size();
+  }
 }
-int DM_Blip::calc_dXijdri(
-        const double rij,
-        const double,
-        const Vec3d &vec_ij,
-        const double fc_ij,
-        const double fcp_ij,
-        rho_type& rhoi,
-        fd_type &fd_ij)
+void DM_Blip::calc_dXijdri(
+  const int Zi, 
+  const int Zj, 
+  const double rij,
+  const double,
+  const Vec3d &vec_ij,
+  rho_type& rhoi,
+  fd_type &fd_ij,
+  const double scale)
 {
-    const double x = vec_ij[0];
-    const double y = vec_ij[1];
-    const double z = vec_ij[2];
-
-    size_t Lshift = 0;
-
-	size_t ii=rhoisize;
-    for (int L=0; L<=Lmax; ++L) {
-        for (size_t o=0; o<orbitals[L].size(); ++o) {
-            const int lx = orbitals[L][o][0];
-            const int ly = orbitals[L][o][1];
-            const int lz = orbitals[L][o][2];
-
-            const double powxlxij = my_pow(x,lx);
-            const double powylyij = my_pow(y,ly);
-            const double powzlzij = my_pow(z,lz);
-
-            double txij=0.0,tyij=0.0,tzij=0.0;
-            if (lx!=0) {
-                txij = lx*my_pow(x,lx-1)*powylyij*powzlzij;
-            }
-            if (ly!=0) {
-                tyij = ly*my_pow(y,ly-1)*powxlxij*powzlzij;
-            }
-            if (lz!=0) {
-                tzij = lz*my_pow(z,lz-1)*powylyij*powxlxij;
-            }
-            const double pqrij = powxlxij*powylyij*powzlzij;
-
-            for (size_t c=0; c<cgrid.size(); c++) {
-                double blip = B(sgrid[c]*(rij-cgrid[c]),fc_ij);
-                double dblip = dB(sgrid[c]*(rij-cgrid[c]),sgrid[c],fc_ij,fcp_ij);
-
-                    const double term2ijx=pqrij*dblip*x/rij;
-                    const double term2ijy=pqrij*dblip*y/rij;
-                    const double term2ijz=pqrij*dblip*z/rij;
-
-                    const double term1ijx = blip*txij;
-                    const double term1ijy = blip*tyij;
-                    const double term1ijz = blip*tzij;
-
-                    fd_ij(c+Lshift+fidx,0) +=
-                         rhoi(ii)*(term1ijx + term2ijx)
-                        ;
-
-                    fd_ij(c+Lshift+fidx,1) +=
-                         rhoi(ii)*(term1ijy + term2ijy)
-                        ;
-
-                    fd_ij(c+Lshift+fidx,2) += 
-                         rhoi(ii)*(term1ijz + term2ijz)
-                        ;
-                    ii++;
-            }
-        }
-        Lshift+=sgrid.size();
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
+  const double x = vec_ij[0];
+  const double y = vec_ij[1];
+  const double z = vec_ij[2];
+
+  double wj = weights[Zj];
+  double fc_ij = scale*wj*fcut->calc(rij);
+  double fcp_ij = scale*wj*fcut->calc_prime(rij);
+
+  size_t Lshift = 0;
+
+  size_t ii=rhoisize+rfidx;
+  for (int L=0; L<=Lmax; ++L) {
+    for (size_t o=0; o<orbitals[L].size(); ++o) {
+      const int lx = orbitals[L][o][0];
+      const int ly = orbitals[L][o][1];
+      const int lz = orbitals[L][o][2];
+
+      const double powxlxij = my_pow(x,lx);
+      const double powylyij = my_pow(y,ly);
+      const double powzlzij = my_pow(z,lz);
+
+      double txij=0.0,tyij=0.0,tzij=0.0;
+      if (lx!=0) {
+        txij = lx*my_pow(x,lx-1)*powylyij*powzlzij;
+      }
+      if (ly!=0) {
+        tyij = ly*my_pow(y,ly-1)*powxlxij*powzlzij;
+      }
+      if (lz!=0) {
+        tzij = lz*my_pow(z,lz-1)*powylyij*powxlxij;
+      }
+      const double pqrij = powxlxij*powylyij*powzlzij;
+
+      for (size_t c=0; c<cgrid.size(); c++) {
+        double blip = B(sgrid[c]*(rij-cgrid[c]),fc_ij);
+        double dblip = dB(sgrid[c]*(rij-cgrid[c]),sgrid[c],fc_ij,fcp_ij);
+
+        const double term2ijx=pqrij*dblip*x/rij;
+        const double term2ijy=pqrij*dblip*y/rij;
+        const double term2ijz=pqrij*dblip*z/rij;
+
+        const double term1ijx = blip*txij;
+        const double term1ijy = blip*tyij;
+        const double term1ijz = blip*tzij;
+
+        fd_ij(c+Lshift+fidx,0) +=
+          rhoi(ii)*(term1ijx + term2ijx)
+        ;
+
+        fd_ij(c+Lshift+fidx,1) +=
+          rhoi(ii)*(term1ijy + term2ijy)
+        ;
+
+        fd_ij(c+Lshift+fidx,2) += 
+          rhoi(ii)*(term1ijz + term2ijz)
+        ;
+        ii++;
+      }
     }
-
-    return 1;
-
-}
-size_t DM_Blip::size() {
-    return s;
+    Lshift+=sgrid.size();
+  }
 }
 std::string DM_Blip::label() {
-    return lab;
+  return lab;
 }
 
 void DM_Blip::init_rhoi(rho_type& rhoi)
 {
-    rhoi.resize(2*rhoisize);
-    rhoi.set_zero();
+  rhoi.resize(2*rhoisize);
+  rhoi.set_zero();
 }
 void DM_Blip::calc_rho(
-        const double rij,
-        const double,
-        const double fc_ij,
-        const Vec3d &vec_ij,
-        rho_type& rhoi)
+  const int Zi, 
+  const int Zj,
+  const double rij,
+  const double,
+  const Vec3d &vec_ij,
+  rho_type& rhoi,
+  const double scale)
 {
-    size_t ii=0;
-    for (int L=0; L<=Lmax; ++L) {
-        for (size_t o=0; o<orbitals[L].size(); ++o) {
-            const size_t lx = orbitals[L][o][0];
-            const size_t ly = orbitals[L][o][1];
-            const size_t lz = orbitals[L][o][2];
-            double t = (my_pow(vec_ij[0],lx)*my_pow(vec_ij[1],ly)*my_pow(vec_ij[2],lz));
-            for (size_t c=0; c<cgrid.size(); c++) {
-                rhoi(ii++) += B(sgrid[c]*(rij-cgrid[c]),fc_ij)*t;
-            }
-        }
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
+  size_t ii=rfidx;
+  double fc_ij = scale*weights[Zj]*fcut->calc(rij);
+  for (int L=0; L<=Lmax; ++L) {
+    for (size_t o=0; o<orbitals[L].size(); ++o) {
+      const size_t lx = orbitals[L][o][0];
+      const size_t ly = orbitals[L][o][1];
+      const size_t lz = orbitals[L][o][2];
+      double t = (my_pow(vec_ij[0],lx)*my_pow(vec_ij[1],ly)*my_pow(vec_ij[2],lz));
+      for (size_t c=0; c<cgrid.size(); c++) {
+        rhoi(ii++) += B(sgrid[c]*(rij-cgrid[c]),fc_ij)*t;
+      }
     }
+  }
 }
 
 size_t DM_Blip::gen_atomic_orbitals(int Lmax) {
-    orbitals.resize(Lmax+1);
-    fac.resize(Lmax+1);
-
-    size_t count = 0;
-    for (int L = 0; L <= Lmax; ++L) {
-        for (int lx = 0; lx <= L; ++lx) {
-            for (int ly = 0; ly <= L; ++ly) {
-                for (int lz = 0; lz <= L; ++lz) {
-                    if (lx+ly+lz == L) {
-                        std::vector<int> o = {lx, ly, lz};
-                        orbitals[L].push_back(o);
-                        const double f = fact(L)/(fact(lx)*fact(ly)*fact(lz))/my_pow(4*rc,L);
-                        fac[L].push_back(f);
-                        count++;
-                    }
-                }
-            }
+  orbitals.resize(Lmax+1);
+  fac.resize(Lmax+1);
+
+  size_t count = 0;
+  for (int L = 0; L <= Lmax; ++L) {
+    for (int lx = 0; lx <= L; ++lx) {
+      for (int ly = 0; ly <= L; ++ly) {
+        for (int lz = 0; lz <= L; ++lz) {
+          if (lx+ly+lz == L) {
+            std::vector<int> o = {lx, ly, lz};
+            orbitals[L].push_back(o);
+            const double f = fact(L)/(fact(lx)*fact(ly)*fact(lz))/my_pow(4*rc,L);
+            fac[L].push_back(f);
+            count++;
+          }
         }
+      }
     }
-    return count;
+  }
+  return count;
 }
 double DM_Blip::fact(int n)
 {
-    double f=1.0;
-    while (n) {
-        f *= n;
-        --n;
-    }
-    return f;
+  double f=1.0;
+  while (n) {
+    f *= n;
+    --n;
+  }
+  return f;
 }
 double DM_Blip::my_pow(double x, int n){
-    double r = 1.0;
+  double r = 1.0;
 
-    while(n > 0){
-        r *= x;
-        --n;
-    }
+  while(n > 0){
+    r *= x;
+    --n;
+  }
 
-    return r;
-}
-size_t DM_Blip::rhoi_size() {
-    return rhoisize;
+  return r;
 }
-size_t DM_Blip::rhoip_size() {
-    return rhoisize;
+void DM_Blip::init() {
+  keys.push_back("CGRIDMB");
+  keys.push_back("SGRIDMB");
+  nparams=3;
 }
diff --git a/src/dm_dummy.cpp b/src/dm_dummy.cpp
index bdc648deac72d5d8dabe5ecaa32ba05d9918bea4..cd85a757738bb99bbe6ea0c1bc6018a6c096392c 100644
--- a/src/dm_dummy.cpp
+++ b/src/dm_dummy.cpp
@@ -1,38 +1,45 @@
+#include "tadah/models/descriptors/dm/dm_base.h"
 #include <tadah/models/descriptors/dm/dm_dummy.h>
 
-DM_Dummy::DM_Dummy() {}
-DM_Dummy::DM_Dummy(Config&) {}
+DM_Dummy::DM_Dummy() {
+  init();
+}
+DM_Dummy::DM_Dummy(Config&c): DM_Base(c) {
+  init();
+}
 void DM_Dummy::calc_aed(
-        rho_type&,
-        aed_type2 & ) {}
-int DM_Dummy::calc_dXijdri_dXjidri(
-        const double,
-        const double,
-        const Vec3d &,
-        const double,
-        const double,
-        rho_type&,
-        rho_type&,
-        fd_type &,
-        const double,
-        const double) { return 0; }
-int DM_Dummy::calc_dXijdri(
-        const double,
-        const double,
-        const Vec3d &,
-        const double,
-        const double,
-        rho_type&,
-        fd_type &) { return 0; }
-size_t DM_Dummy::size() { return s;}
+  rho_type&,
+  aed_type & ) {}
+void DM_Dummy::calc_dXijdri_dXjidri(
+  const int,
+  const int,
+  const double,
+  const double,
+  const Vec3d &,
+  rho_type&,
+  rho_type&,
+  fd_type &,
+  const double) {}
+void DM_Dummy::calc_dXijdri(
+  const int,
+  const int,
+  const double,
+  const double,
+  const Vec3d &,
+  rho_type&,
+  fd_type &,
+  const double) {}
 std::string DM_Dummy::label() {return lab;}
 void DM_Dummy::init_rhoi(rho_type&) {}
 void DM_Dummy::calc_rho(
-        const double,
-        const double,
-        const double,
-        const Vec3d &,
-        rho_type&) {}
-size_t DM_Dummy::rhoi_size() { return 0; }
-size_t DM_Dummy::rhoip_size() { return 0; }
+  const int,
+  const int,
+  const double,
+  const double,
+  const Vec3d &,
+  rho_type&,
+  const double) {}
 
+void DM_Dummy::init() {
+  nparams=0;
+}
diff --git a/src/dm_ead.cpp b/src/dm_ead.cpp
index 3cb78a7ff892a0b9e7a1cd78bbc022e23f45aaf0..8cb83aaa524773dd2052e86b6829eed84d8fbb88 100644
--- a/src/dm_ead.cpp
+++ b/src/dm_ead.cpp
@@ -1,31 +1,37 @@
+#include "tadah/models/descriptors/dm/dm_base.h"
 #include <tadah/models/descriptors/dm/dm_ead.h>
 #include <tadah/models/descriptors/d_basis_functions.h>
 #include <stdexcept>
 #include <vector>
-#include <iomanip>
-
 
+DM_EAD::DM_EAD() {
+  init();
+}
 /* Index ii iterates rhoi array
  * rhoi array is split 50/50 into density and derivative
  * of the embedding function
  */
-DM_EAD::DM_EAD(Config &config):
-  verbose(config.get<int>("VERBOSE")),
-  rc(config.get<double>("RCUTMB"))
+DM_EAD::DM_EAD(Config &config): DM_Base(config),
+  rc(config.get<double>("RCUTMBMAX"))
 {
-
   if (!config.get<bool>("INITMB")) {
     throw std::runtime_error("INITMB=false; cannot initialize "+label()+"\n");
   }
+  init();
 
   get_grid(config,"CGRIDMB",cgrid);
   get_grid(config,"SGRIDMB",sgrid);
+  // update config with generated grid
+  config.remove("CGRIDMB");
+  for (const auto &i: cgrid) config.add("CGRIDMB",i);
+  config.remove("SGRIDMB");
+  for (const auto &i: sgrid) config.add("SGRIDMB",i);
 
   if (cgrid.size()!=sgrid.size()) {
     throw std::runtime_error("SGRID2B and CGRID2B arrays differ in size.\n");
   }
 
-  Lmax = config.get<int>("AGRIDMB");
+  Lmax = config.get<int>("TYPEMB",1);
   s=sgrid.size()*(Lmax+1);
   rhoisize = gen_atomic_orbitals(Lmax);
   rhoisize *= sgrid.size();
@@ -41,14 +47,16 @@ DM_EAD::DM_EAD(Config &config):
     std::cout << std::endl;
     std::cout << "rhoisize: " << rhoisize << std::endl;
   }
+  auto init_atoms = get_init_atoms(config);
+  init_for_atoms(init_atoms);
 }
 
 void DM_EAD::calc_aed(
   rho_type& rhoi,
-  aed_type2 &aed)
+  aed_type &aed)
 {
   size_t Lshift = 0;
-  size_t ii=0;
+  size_t ii=rfidx;
   for (int L=0; L<=Lmax; ++L) {
     for (size_t o=0; o<orbitals[L].size(); ++o) {
       const double f = fac[L][o];
@@ -61,25 +69,30 @@ void DM_EAD::calc_aed(
     Lshift += sgrid.size();
   }
 }
-int DM_EAD::calc_dXijdri_dXjidri(
+void DM_EAD::calc_dXijdri_dXjidri(
+  const int Zi,
+  const int Zj,
   const double rij,
   const double,
   const Vec3d &vec_ij,
-  const double fc_ij,
-  const double fcp_ij,
   rho_type& rhoi,
   rho_type& rhoj,
   fd_type &fd_ij,
-  const double wi,
-  const double wj)
+  const double scale)
 {
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
   const double x = vec_ij[0];
   const double y = vec_ij[1];
   const double z = vec_ij[2];
 
+  double wi = weights[Zi];
+  double wj = weights[Zj];
+  double fc_ij = fcut->calc(rij);
+  double fcp_ij = fcut->calc_prime(rij);
+
   size_t Lshift = 0;
 
-  size_t ii=rhoisize;
+  size_t ii=rhoisize+rfidx;
   for (int L=0; L<=Lmax; ++L) {
     for (size_t o=0; o<orbitals[L].size(); ++o) {
       const int lx = orbitals[L][o][0];
@@ -131,45 +144,48 @@ int DM_EAD::calc_dXijdri_dXjidri(
         const double term1jiy = gauss*tyji;
         const double term1jiz = gauss*tzji;
 
-        fd_ij(c+Lshift+fidx,0) +=
+        fd_ij(c+Lshift+fidx,0) += scale*(
           rhoi(ii)*(term1ijx + term2ijx)*wj
-          -rhoj(ii)*(term1jix + term2jix)*wi
+          -rhoj(ii)*(term1jix + term2jix)*wi)
         ;
 
-        fd_ij(c+Lshift+fidx,1) +=
+        fd_ij(c+Lshift+fidx,1) += scale*(
           rhoi(ii)*(term1ijy + term2ijy)*wj
-          -rhoj(ii)*(term1jiy + term2jiy)*wi
+          -rhoj(ii)*(term1jiy + term2jiy)*wi)
         ;
 
-        fd_ij(c+Lshift+fidx,2) += 
+        fd_ij(c+Lshift+fidx,2) += scale*(
           rhoi(ii)*(term1ijz + term2ijz)*wj
-          -rhoj(ii)*(term1jiz + term2jiz)*wi
+          -rhoj(ii)*(term1jiz + term2jiz)*wi)
         ;
         ii++;
       }
     }
     Lshift+=sgrid.size();
   }
-
-  return 1;
-
 }
-int DM_EAD::calc_dXijdri(
+void DM_EAD::calc_dXijdri(
+  const int Zi,
+  const int Zj,
   const double rij,
   const double,
   const Vec3d &vec_ij,
-  const double fc_ij,
-  const double fcp_ij,
   rho_type& rhoi,
-  fd_type &fd_ij)
+  fd_type &fd_ij,
+  const double scale)
 {
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
   const double x = vec_ij[0];
   const double y = vec_ij[1];
   const double z = vec_ij[2];
 
+  double wj = weights[Zj];
+  double fc_ij = scale*wj*fcut->calc(rij);
+  double fcp_ij = scale*wj*fcut->calc_prime(rij);
+
   size_t Lshift = 0;
 
-  size_t ii=rhoisize;
+  size_t ii=rhoisize+rfidx;
   for (int L=0; L<=Lmax; ++L) {
     for (size_t o=0; o<orbitals[L].size(); ++o) {
       const int lx = orbitals[L][o][0];
@@ -220,12 +236,6 @@ int DM_EAD::calc_dXijdri(
     }
     Lshift+=sgrid.size();
   }
-
-  return 1;
-
-}
-size_t DM_EAD::size() {
-  return s;
 }
 std::string DM_EAD::label() {
   return lab;
@@ -237,13 +247,17 @@ void DM_EAD::init_rhoi(rho_type& rhoi)
   rhoi.set_zero();
 }
 void DM_EAD::calc_rho(
+  const int Zi,
+  const int Zj,
   const double rij,
   const double,
-  const double fc_ij,
   const Vec3d &vec_ij,
-  rho_type& rhoi)
+  rho_type& rhoi,
+  const double scale)
 {
-  size_t ii=0;
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
+  size_t ii=rfidx;
+  double fc_ij = scale*weights[Zj]*fcut->calc(rij);
   for (int L=0; L<=Lmax; ++L) {
     for (size_t o=0; o<orbitals[L].size(); ++o) {
       const size_t lx = orbitals[L][o][0];
@@ -297,9 +311,8 @@ double DM_EAD::my_pow(double x, int n){
   }
   return r;
 }
-size_t DM_EAD::rhoi_size() {
-  return rhoisize;
-}
-size_t DM_EAD::rhoip_size() {
-  return rhoisize;
+void DM_EAD::init() {
+  keys.push_back("CGRIDMB");
+  keys.push_back("SGRIDMB");
+  nparams=3;
 }
diff --git a/src/dm_eam.cpp b/src/dm_eam.cpp
index 4aecee7abe9cb6381c793bba81e202f33d27f305..3408b29ec065c682f7d3e17ac63758d7b4621365 100644
--- a/src/dm_eam.cpp
+++ b/src/dm_eam.cpp
@@ -1,217 +1,220 @@
+#include "tadah/models/descriptors/dm/dm_base.h"
 #include <tadah/models/descriptors/dm/dm_eam.h>
 
-DM_EAM::DM_EAM(Config &c):
-    verbose(c.get<int>("VERBOSE")) 
+DM_EAM::DM_EAM() {
+  init();
+}
+DM_EAM::DM_EAM(Config &c): DM_Base(c)
 {
-    if (!c.get<bool>("INITMB")) {
-        s=0;
-        return;
-    }
-    ef.file_path = c("SETFL")[0];
-    read_setfl();
-
-    if (std::abs(ef.rcut - c.get<double>("RCUTMB")) > std::numeric_limits<double>::min()) {
-        if (verbose) {
-            std::cout << "Config file cutoff and setfl file cutoff differ: "
-                << c.get<double>("RCUTMB") << " " << ef.rcut << std::endl;
-            std::cout << "Enforcing SETFL file cutoff: " << ef.rcut << std::endl;
-        }
-        c.remove("RCUTMB");
-        c.add("RCUTMB", ef.rcut);
-        c.postprocess();
+  init();
+  if (!c.get<bool>("INITMB")) return;
+  s=1;
+  ef.file_path = c.get<std::string>("TYPEMB",1);
+  read_setfl();
+
+  if (std::abs(ef.rcut - c.get<double>("RCUTMB")) > std::numeric_limits<double>::min()) {
+    if (verbose) {
+      std::cout << "Config file cutoff and setfl file cutoff differ: "
+        << c.get<double>("RCUTMB") << " " << ef.rcut << std::endl;
+      std::cout << "Enforcing SETFL file cutoff: " << ef.rcut << std::endl;
     }
-
-
-    frho_spline.resize(ef.nrho+1, std::vector<double>(7));
-    rhor_spline.resize(ef.nr+1, std::vector<double>(7));
-    z2r_spline.resize(ef.nr+1, std::vector<double>(7));
-
-
-    gen_splines(ef.nrho, ef.drho, ef.frho, frho_spline);
-    gen_splines(ef.nr, ef.dr, ef.rhor, rhor_spline);
-    gen_splines(ef.nr, ef.dr, ef.z2r, z2r_spline);
-
+    c.remove("RCUTMB");
+    c.add("RCUTMB", ef.rcut);
+    c.postprocess();
+  }
+
+  frho_spline.resize(ef.nrho+1, std::vector<double>(7));
+  rhor_spline.resize(ef.nr+1, std::vector<double>(7));
+  z2r_spline.resize(ef.nr+1, std::vector<double>(7));
+
+  gen_splines(ef.nrho, ef.drho, ef.frho, frho_spline);
+  gen_splines(ef.nr, ef.dr, ef.rhor, rhor_spline);
+  gen_splines(ef.nr, ef.dr, ef.z2r, z2r_spline);
+
+  rhoisize = 1;
+  auto init_atoms = get_init_atoms(c);
+  init_for_atoms(init_atoms);
 }
 
 void DM_EAM::calc_aed(
-		rho_type& rho,
-		aed_type2 &aed)
+  rho_type& rho,
+  aed_type &aed)
 {
-    //std::cout << "rho[i]:   " << rho(0) << std::endl;
-    double p = rho(0)*ef.rdrho + 1.0;
-    int m = static_cast<int> (p);
-    m = std::max(1,std::min(m,ef.nrho-1));
-    p -= m;
-    p = std::min(p,1.0);
-    //std::vector<double> coeff = frho_spline[m];
-    double phi = ((frho_spline[m][3]*p + frho_spline[m][4])*p + frho_spline[m][5])*p + frho_spline[m][6];
+  //std::cout << "rho[i]:   " << rho(0) << std::endl;
+  double p = rho(rfidx)*ef.rdrho + 1.0;
+  int m = static_cast<int> (p);
+  m = std::max(1,std::min(m,ef.nrho-1));
+  p -= m;
+  p = std::min(p,1.0);
+  //std::vector<double> coeff = frho_spline[m];
+  double phi = ((frho_spline[m][3]*p + frho_spline[m][4])*p + frho_spline[m][5])*p + frho_spline[m][6];
 
-    rho(1) = (frho_spline[m][0]*p + frho_spline[m][1])*p + frho_spline[m][2];    // lammps fp[i]
-    //std::cout << "fp[i]:   " << rho(1) << std::endl;
+  rho(1+rfidx) = (frho_spline[m][0]*p + frho_spline[m][1])*p + frho_spline[m][2];    // lammps fp[i]
+  //std::cout << "fp[i]:   " << rho(1) << std::endl;
 
-    if (rho(0) > ef.rhomax) phi += rho(1) * (rho(0)-ef.rhomax);
-    //phi *= scale[type[i]][type[i]];
+  if (rho(rfidx) > ef.rhomax) phi += rho(1+rfidx) * (rho(rfidx)-ef.rhomax);
+  //phi *= scale[type[i]][type[i]];
 
-    aed(fidx) += phi;
+  aed(fidx) += phi;
 
 
 }
-int DM_EAM::calc_dXijdri_dXjidri(
-		const double rij,
-		const double,
-		const Vec3d &,
-		const double,
-		const double,
-		rho_type& rhoi,
-		rho_type& rhoj,
-		fd_type &fd_ij,
-        const double,
-        const double)
+void DM_EAM::calc_dXijdri_dXjidri(
+  const int Zi,
+  const int Zj,
+  const double rij,
+  const double,
+  const Vec3d &vec_ij,
+  rho_type& rhoi,
+  rho_type& rhoj,
+  fd_type &fd_ij,
+  const double scale)
 {
-    double r = rij;
-    //double recip = 1.0/r;
-    double p = r*ef.rdr + 1.0;
-    int m = static_cast<int> (p);
-    m = std::min(m,ef.nr-1);
-    p -= m;
-    p = std::min(p,1.0);
-    //std::vector<double> coeff = rhor_spline[m];
-    double rhoip = (rhor_spline[m][0]*p + rhor_spline[m][1])*p + rhor_spline[m][2];
-    double rhojp = (rhor_spline[m][0]*p + rhor_spline[m][1])*p + rhor_spline[m][2];
-
-    // here rho_i[1] is a derivative of rho_i at local atom, similarly for rho_j
-    //double psip = rho_i[1]*rhojp + rho_j[1]*rhoip;
-    double psip = rhoi(1)*rhojp - rhoj(1)*rhoip;
-
-    fd_ij(fidx,0) = psip; // requires *delij*recip in the main loop
-    return 0;
-
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
+  double r = rij;
+  double rinv = 1/r;
+  double p = r*ef.rdr + 1.0;
+  int m = static_cast<int> (p);
+  m = std::min(m,ef.nr-1);
+  p -= m;
+  p = std::min(p,1.0);
+  double rhoip = (rhor_spline[m][0]*p + rhor_spline[m][1])*p + rhor_spline[m][2];
+  double rhojp = (rhor_spline[m][0]*p + rhor_spline[m][1])*p + rhor_spline[m][2];
+
+  // here rho_i[1] is a derivative of rho_i at local atom, similarly for rho_j
+  //double psip = rho_i[1]*rhojp + rho_j[1]*rhoip;
+  double psip = rhoi(rfidx+1)*rhojp + rhoj(rfidx+1)*rhoip;
+
+  double f = scale*psip*rinv;
+  fd_ij(fidx,0) = f*vec_ij[0]; 
+  fd_ij(fidx,1) = f*vec_ij[1]; 
+  fd_ij(fidx,2) = f*vec_ij[2]; 
 }
-int DM_EAM::calc_dXijdri(
-		const double rij,
-		const double,
-		const Vec3d &,
-		const double,
-		const double,
-		rho_type& rhoi,
-		fd_type &fd_ij)
+void DM_EAM::calc_dXijdri(
+  const int Zi,
+  const int Zj,
+  const double rij,
+  const double,
+  const Vec3d &vec_ij,
+  rho_type& rhoi,
+  fd_type &fd_ij,
+  const double scale)
 {
-    double r = rij;
-    //double recip = 1.0/r;
-    double p = r*ef.rdr + 1.0;
-    int m = static_cast<int> (p);
-    m = std::min(m,ef.nr-1);
-    p -= m;
-    p = std::min(p,1.0);
-    double rhojp = (rhor_spline[m][0]*p + rhor_spline[m][1])*p + rhor_spline[m][2];
-
-    // here rho_i[1] is a derivative of rho_i at local atom
-    double psip = rhoi(1)*rhojp;
-
-    fd_ij(fidx,0) = psip; // requires *delij*recip in the main loop
-    return 0;
-
-}
-size_t DM_EAM::size() {
-	return s;
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
+  double r = rij;
+  double rinv = 1/r;
+  double p = r*ef.rdr + 1.0;
+  int m = static_cast<int> (p);
+  m = std::min(m,ef.nr-1);
+  p -= m;
+  p = std::min(p,1.0);
+  double rhojp = (rhor_spline[m][0]*p + rhor_spline[m][1])*p + rhor_spline[m][2];
+
+  // here rho_i[1] is a derivative of rho_i at local atom
+  double psip = rhoi(rfidx+1)*rhojp;
+
+  double f = scale*psip*rinv;
+  fd_ij(fidx,0) = f*vec_ij[0];
+  fd_ij(fidx,1) = f*vec_ij[1];
+  fd_ij(fidx,2) = f*vec_ij[2];
 }
 std::string DM_EAM::label() {
-	return lab;
+  return lab;
 }
 
 void DM_EAM::read_setfl()
 {
-    std::string line;
-    std::ifstream in_file(ef.file_path);
-    if (!in_file.good())
-        throw std::runtime_error("SETFL file does not exists.\n");
-
-    if (in_file.is_open()) {
-        if (verbose)
-            std::cout << "<DM_EAM> Reading setfl: " << ef.file_path << std::endl;
-        // skip ficgridt 3 comment lines
-        getline (in_file,line);
-        getline (in_file,line);
-        getline (in_file,line);
-        // skip number of types and types
-        getline (in_file,line);
-        // read 5th line
-        in_file >> ef.nrho >> ef.drho >> ef.nr >> ef.dr >> ef.rcut;
-        in_file >> ef.atomic_number >> ef.atomic_mass >> ef.lattice_param >> ef.lattice;
-        ef.rdr = 1.0/ef.dr;
-        ef.rdrho = 1.0/ef.drho;
-        // prepare arrays
-        ef.frho.resize(ef.nrho);
-        ef.rhor.resize(ef.nr);
-        ef.z2r.resize(ef.nr);
-        // read all data
-        for (int i=0; i<ef.nrho; ++i) in_file >> ef.frho[i];
-        for (int i=0; i<ef.nr; ++i) in_file >> ef.rhor[i];
-        for (int i=0; i<ef.nr; ++i) in_file >> ef.z2r[i];
-        in_file.close();
-    }
-    else {
-        std::cout << "<DM_EAM> Unable to open file: " << ef.file_path << std::endl;;
-    }
-    // get max value of rho
-    //ef.rhomax = *std::max_element(ef.rhor.begin(), ef.rhor.end());
-    ef.rhomax = (ef.nrho-1) * ef.drho;
+  std::string line;
+  std::ifstream in_file(ef.file_path);
+  if (!in_file.good())
+    throw std::runtime_error("SETFL file does not exists.\n");
+
+  if (in_file.is_open()) {
+    if (verbose)
+      std::cout << "<DM_EAM> Reading setfl: " << ef.file_path << std::endl;
+    // skip ficgridt 3 comment lines
+    getline (in_file,line);
+    getline (in_file,line);
+    getline (in_file,line);
+    // skip number of types and types
+    getline (in_file,line);
+    // read 5th line
+    in_file >> ef.nrho >> ef.drho >> ef.nr >> ef.dr >> ef.rcut;
+    in_file >> ef.atomic_number >> ef.atomic_mass >> ef.lattice_param >> ef.lattice;
+    ef.rdr = 1.0/ef.dr;
+    ef.rdrho = 1.0/ef.drho;
+    // prepare arrays
+    ef.frho.resize(ef.nrho);
+    ef.rhor.resize(ef.nr);
+    ef.z2r.resize(ef.nr);
+    // read all data
+    for (int i=0; i<ef.nrho; ++i) in_file >> ef.frho[i];
+    for (int i=0; i<ef.nr; ++i) in_file >> ef.rhor[i];
+    for (int i=0; i<ef.nr; ++i) in_file >> ef.z2r[i];
+    in_file.close();
+  }
+  else {
+    std::cout << "<DM_EAM> Unable to open file: " << ef.file_path << std::endl;;
+  }
+  // get max value of rho
+  //ef.rhomax = *std::max_element(ef.rhor.begin(), ef.rhor.end());
+  ef.rhomax = (ef.nrho-1) * ef.drho;
 
 }
 void DM_EAM::gen_splines(int &n, double &delta, std::vector<double> &f,
-		std::vector<std::vector<double>> &spline)
+                         std::vector<std::vector<double>> &spline)
 {
-	// in lammps f is n+1, here is size n
-	for (int m=1; m<=n; m++) spline[m][6] = f[m-1];
-
-	spline[1][5] = spline[2][6] - spline[1][6];
-	spline[2][5] = 0.5 * (spline[3][6]-spline[1][6]);
-	spline[n-1][5] = 0.5 * (spline[n][6]-spline[n-2][6]);
-	spline[n][5] = spline[n][6] - spline[n-1][6];
-
-	for (int m = 3; m <= n-2; m++)
-		spline[m][5] = ((spline[m-2][6]-spline[m+2][6]) +
-				8.0*(spline[m+1][6]-spline[m-1][6])) / 12.0;
-
-	for (int m = 1; m <= n-1; m++) {
-		spline[m][4] = 3.0*(spline[m+1][6]-spline[m][6]) - 2.0*spline[m][5] - spline[m+1][5];
-		spline[m][3] = spline[m][5] + spline[m+1][5] - 2.0*(spline[m+1][6]-spline[m][6]);
-	}
-
-	spline[n][4] = 0.0;
-	spline[n][3] = 0.0;
-
-	for (int m = 1; m <= n; m++) {
-		spline[m][2] = spline[m][5]/delta;
-		spline[m][1] = 2.0*spline[m][4]/delta;
-		spline[m][0] = 3.0*spline[m][3]/delta;
-	}
+  // in lammps f is n+1, here is size n
+  for (int m=1; m<=n; m++) spline[m][6] = f[m-1];
+
+  spline[1][5] = spline[2][6] - spline[1][6];
+  spline[2][5] = 0.5 * (spline[3][6]-spline[1][6]);
+  spline[n-1][5] = 0.5 * (spline[n][6]-spline[n-2][6]);
+  spline[n][5] = spline[n][6] - spline[n-1][6];
+
+  for (int m = 3; m <= n-2; m++)
+    spline[m][5] = ((spline[m-2][6]-spline[m+2][6]) +
+      8.0*(spline[m+1][6]-spline[m-1][6])) / 12.0;
+
+  for (int m = 1; m <= n-1; m++) {
+    spline[m][4] = 3.0*(spline[m+1][6]-spline[m][6]) - 2.0*spline[m][5] - spline[m+1][5];
+    spline[m][3] = spline[m][5] + spline[m+1][5] - 2.0*(spline[m+1][6]-spline[m][6]);
+  }
+
+  spline[n][4] = 0.0;
+  spline[n][3] = 0.0;
+
+  for (int m = 1; m <= n; m++) {
+    spline[m][2] = spline[m][5]/delta;
+    spline[m][1] = 2.0*spline[m][4]/delta;
+    spline[m][0] = 3.0*spline[m][3]/delta;
+  }
 }
 void DM_EAM::init_rhoi(rho_type& rhoi)
 {
-    rhoi.resize(2);
-    rhoi.set_zero();
+  rhoi.resize(2);
+  rhoi.set_zero();
 }
 void DM_EAM::calc_rho(
-		const double rij,
-        const double,
-        const double,
-        const Vec3d &,
-        rho_type& rho)
+  const int Zi,
+  const int Zj,
+  const double rij,
+  const double,
+  const Vec3d &,
+  rho_type& rho,
+  const double scale)
 {
-    double r = rij;
-    double p = r*ef.rdr + 1.0;
-    int m = static_cast<int> (p);
-    m = std::min(m,ef.nr-1);
-    p -= m;
-    p = std::min(p,1.0);
-    std::vector<double> coeff = rhor_spline[m];
-    rho(0) += ((coeff[3]*p + coeff[4])*p + coeff[5])*p + coeff[6];
+  if (!is_init_for_atoms(Zi,Zj) || rij > get_rcut()) return;
+  double r = rij;
+  double p = r*ef.rdr + 1.0;
+  int m = static_cast<int> (p);
+  m = std::min(m,ef.nr-1);
+  p -= m;
+  p = std::min(p,1.0);
+  std::vector<double> coeff = rhor_spline[m];
+  rho(rfidx) += scale*( ((coeff[3]*p + coeff[4])*p + coeff[5])*p + coeff[6]);
 
 }
-size_t DM_EAM::rhoi_size() {
-    return 1;
-}
-size_t DM_EAM::rhoip_size() {
-    return 1;
+void DM_EAM::init() {
+  nparams=1;
 }
diff --git a/src/dm_mjoin.cpp b/src/dm_mjoin.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f1d9cb02765cc17d3d0fd95e9459b585bd455365
--- /dev/null
+++ b/src/dm_mjoin.cpp
@@ -0,0 +1,103 @@
+#include <cstddef>
+#include <tadah/models/descriptors/dm/dm_mjoin.h>
+
+DM_mJoin::DM_mJoin() {
+  init();
+}
+DM_mJoin::~DM_mJoin() {
+  for (auto d : ds) if (d) delete d;
+}
+
+DM_mJoin::DM_mJoin(Config &c) : DM_Base(c) {
+  init();
+  configs = parse_config(c, "TYPEMB");
+  expand_grids(c, configs, "TYPEMB");
+
+  for (auto &c1 : configs) {
+    ds.push_back(CONFIG::factory<DM_Base, Config&>(c1.get<std::string>("TYPEMB"), c1));
+    s += ds.back()->size();
+    rhoisize += ds.back()->rhoi_size();
+  }
+  set_rfidx(0);
+}
+
+void DM_mJoin::calc_aed(rho_type& rho, aed_type &aed) {
+  for (auto d : ds) {
+    d->calc_aed(rho, aed);
+  }
+}
+void DM_mJoin::calc_dXijdri_dXjidri(
+  const int Zi,
+  const int Zj,
+  const double rij,
+  const double rij_sq,
+  const Vec3d &vec_ij,
+  rho_type& rhoi,
+  rho_type& rhoj,
+  fd_type &fd_ij,
+  const double scale) {
+  for (auto d : ds) {
+    d->calc_dXijdri_dXjidri(Zi,Zj,rij,rij_sq,vec_ij,rhoi,rhoj,fd_ij,scale); 
+  }
+}
+void DM_mJoin::calc_dXijdri(
+  const int Zi,
+  const int Zj,
+  const double rij,
+  const double rij_sq,
+  const Vec3d &vec_ij,
+  rho_type& rhoi,
+  fd_type &fd_ij, 
+  const double scale) {
+  for (auto d : ds) { 
+    d->calc_dXijdri(Zi,Zj,rij,rij_sq,vec_ij,rhoi,fd_ij,scale);
+  }
+}
+void DM_mJoin::init_rhoi(rho_type& rhoi) {
+  rhoi.resize(2*rhoisize);
+  rhoi.set_zero();
+}
+void DM_mJoin::calc_rho(
+  const int Zi,
+  const int Zj,
+  const double rij,
+  const double rij_sq,
+  const Vec3d &vec_ij,
+  rho_type& rho,
+  const double scale) {
+  for (auto d : ds) {
+      d->calc_rho(Zi,Zj,rij,rij_sq,vec_ij,rho,scale);
+  }
+}
+std::string DM_mJoin::label() {
+  return lab;
+}
+
+void DM_mJoin::set_fidx(size_t fidx_) {
+  fidx = fidx_;
+  ds[0]->set_fidx(fidx_);
+  size_t s = 0;
+  for (size_t i = 1; i < ds.size(); ++i) {
+    s += ds[i - 1]->size();
+    ds[i]->set_fidx(fidx_ + s);
+  }
+}
+void DM_mJoin::set_rfidx(size_t rfidx_) {
+  rfidx = rfidx_;
+  ds[0]->set_rfidx(rfidx_);
+  size_t s = 0;
+  for (size_t i = 1; i < ds.size(); ++i) {
+    s += ds[i - 1]->rhoi_size();
+    s += ds[i - 1]->rhoip_size();
+    ds[i]->set_rfidx(rfidx_ + s);
+  }
+}
+
+void DM_mJoin::init() {
+  // not really sure about this
+  keys.push_back("TYPEMB");
+  keys.push_back("RCTYPEMB");
+  keys.push_back("RCUTMB");
+  nparams=std::numeric_limits<size_t>::max();
+}
+
diff --git a/src/function_base.cpp b/src/function_base.cpp
index 8e80b849351153537c6986563dac26e5d447d3b7..5b272de2bd825afebee99e58c97ff4036f92c67e 100644
--- a/src/function_base.cpp
+++ b/src/function_base.cpp
@@ -1,2 +1,17 @@
 #include <tadah/models/functions/function_base.h>
 
+Function_Base::Function_Base(): verbose(0) {
+}
+Function_Base::Function_Base(const Config &c) {
+  verbose = (c.exist("VERBOSE")) ? c.get<int>("VERBOSE") : 0;
+}
+void Function_Base::set_verbose(int v) { verbose=v; }
+int Function_Base::get_verbose() { return verbose; }
+
+Function_Base::~Function_Base() {}
+
+double Function_Base::operator() (const aed_type& , const aed_type& ) const { return 0.0; }
+aed_type Function_Base::derivative(const aed_type& , const aed_type& ) const { return aed_type(); }
+double Function_Base::prime(const aed_type& , const aed_type& ,
+                            const aed_type& ) const { return 0.0; }
+void Function_Base::set_basis(const Matrix ) {}
diff --git a/src/kern_base.cpp b/src/kern_base.cpp
index 557cfd1964a3ee8c9c5fa0c8b5bafa1e92a62c6a..1a431efa800ebe9bbf85bfd11f4f5b1c712a4cfe 100644
--- a/src/kern_base.cpp
+++ b/src/kern_base.cpp
@@ -1,57 +1,59 @@
 #include <tadah/models/functions/kernels/kern_base.h>
 
+Kern_Base::Kern_Base() {}
+Kern_Base::Kern_Base(const Config &c): Function_Base(c) {}
 Kern_Base::~Kern_Base() {}
 void Kern_Base::set_basis(const Matrix b)
 {
-    basis = b;
+  basis = b;
 }
-double Kern_Base::epredict(const t_type &kweights, const aed_type2 &aed) const
+double Kern_Base::epredict(const t_type &kweights, const aed_type &aed) const
 {
-    double energy=0;
-    for (size_t b=0; b<basis.cols(); ++b) {
-        energy += kweights[b]*(*this)(basis.col(b),aed);
-    }
-    return energy;
+  double energy=0;
+  for (size_t b=0; b<basis.cols(); ++b) {
+    energy += kweights[b]*(*this)(basis.col(b),aed);
+  }
+  return energy;
 }
 double Kern_Base::fpredict(const t_type &kweights, const fd_type &fdij,
-        const aed_type2 &aedi, const size_t k) const
+                           const aed_type &aedi, const size_t k) const
 {
-    double res=0.0;
-    for (size_t b=0; b<basis.cols(); ++b) {
-        res -= kweights[b]*(*this).prime(basis.col(b),aedi,fdij(k));
-    }
-    return res;
+  double res=0.0;
+  for (size_t b=0; b<basis.cols(); ++b) {
+    res -= kweights[b]*(*this).prime(basis.col(b),aedi,fdij(k));
+  }
+  return res;
 }
 force_type Kern_Base::fpredict(const t_type &kweights, const fd_type &fdij,
-        const aed_type2 &aedi) const
+                               const aed_type &aedi) const
 {
-    force_type v;
-    v.set_zero();
-    for (size_t b=0; b<basis.cols(); ++b) {
-        for (size_t k=0; k<3; ++k) {
-            v(k) -= kweights[b]*(*this).prime(basis.col(b),aedi,fdij(k));
-        }
+  force_type v;
+  v.set_zero();
+  for (size_t b=0; b<basis.cols(); ++b) {
+    for (size_t k=0; k<3; ++k) {
+      v(k) -= kweights[b]*(*this).prime(basis.col(b),aedi,fdij(k));
     }
-    return v;
+  }
+  return v;
 }
 void Kern_Base::read_basis_from_config(const Config &config, Matrix &b) {
-    // storage is in column-major order
-    if (!(config.exist("SBASIS") && config.exist("BASIS"))) return;
-    size_t sbasis = config.get<size_t>("SBASIS");
-    size_t vlen = config("BASIS").size()/sbasis;
-    std::vector<double> temp(sbasis*vlen);
-    config.get<std::vector<double>>("BASIS",temp);
-    b.resize(vlen,sbasis);  // aeds are in column-major order
-    size_t ii=0;
-    for (size_t i=0;i<sbasis; ++i) {
-        for (size_t j=0;j<vlen; ++j) {
-            b(j,i) = temp[ii++];
-        }
+  // storage is in column-major order
+  if (!(config.exist("SBASIS") && config.exist("BASIS"))) return;
+  size_t sbasis = config.get<size_t>("SBASIS");
+  size_t vlen = config("BASIS").size()/sbasis;
+  std::vector<double> temp(sbasis*vlen);
+  config.get<std::vector<double>>("BASIS",temp);
+  b.resize(vlen,sbasis);  // aeds are in column-major order
+  size_t ii=0;
+  for (size_t i=0;i<sbasis; ++i) {
+    for (size_t j=0;j<vlen; ++j) {
+      b(j,i) = temp[ii++];
     }
+  }
 }
 const Matrix &Kern_Base::get_basis() const {
-    return basis;
+  return basis;
 }
 Matrix Kern_Base::get_basis() {
-    return basis;
+  return basis;
 }
diff --git a/src/kern_linear.cpp b/src/kern_linear.cpp
index 52e6ee0563c58b64a160b6a9bad9cc9232700047..0faf649a1862347049fb8948dd060cb444f240e5 100644
--- a/src/kern_linear.cpp
+++ b/src/kern_linear.cpp
@@ -2,17 +2,18 @@
 
 Kern_Linear::Kern_Linear() {}
 Kern_Linear::Kern_Linear (const Config &c):
-    Function_Base(c)
+    Function_Base(c),
+    Kern_Base(c)
 {}
-double Kern_Linear::operator() (const aed_type2& b, const aed_type2& af) const
+double Kern_Linear::operator() (const aed_type& b, const aed_type& af) const
 {
     return b*af;;
 }
-aed_type2 Kern_Linear::derivative(const aed_type2& b, const aed_type2&) const
+aed_type Kern_Linear::derivative(const aed_type& b, const aed_type&) const
 {
     return b;
 }
-double Kern_Linear::prime(const aed_type2& b, const aed_type2& , const aed_type2& ff) const
+double Kern_Linear::prime(const aed_type& b, const aed_type& , const aed_type& ff) const
 {
     // return derivative(b,af).transpose() * ff;
     return b * ff;
@@ -21,17 +22,17 @@ std::string Kern_Linear::get_label() const
 {
     return label;
 }
-double Kern_Linear::epredict(const t_type &weights, const aed_type2& aed) const
+double Kern_Linear::epredict(const t_type &weights, const aed_type& aed) const
 {
     return weights*aed;
 }
 double Kern_Linear::fpredict(const t_type &weights, const fd_type &fdij,
-        const aed_type2& , const size_t k) const
+        const aed_type& , const size_t k) const
 {
     return -(fdij(k) * weights);
 }
 force_type Kern_Linear::fpredict(const t_type &weights, const fd_type &fdij,
-        const aed_type2& ) const
+        const aed_type& ) const
 {
     return -(fdij * weights);
 }
diff --git a/src/kern_lq.cpp b/src/kern_lq.cpp
index 4a4733d011ff47b9435e762fd3401ae418215680..e8b1f5e04c9783a46a8459af08861ea84dc49183 100644
--- a/src/kern_lq.cpp
+++ b/src/kern_lq.cpp
@@ -2,22 +2,23 @@
 
 Kern_LQ::Kern_LQ () {}
 Kern_LQ::Kern_LQ (const Config &c):
-    Function_Base(c)
+    Function_Base(c),
+    Kern_Base(c)
 {
     if (get_verbose()) std::cout << std::endl << label << std::endl;
     read_basis_from_config(c,basis);
 }
-double Kern_LQ::operator() (const aed_type2& b, const aed_type2& af) const
+double Kern_LQ::operator() (const aed_type& b, const aed_type& af) const
 {
     double x = b*af;
     return x*x + x;
 }
-aed_type2 Kern_LQ::derivative(const aed_type2& b, const aed_type2& af) const
+aed_type Kern_LQ::derivative(const aed_type& b, const aed_type& af) const
 {
     double temp = 2.0 * b*af;
     return temp*b + b;
 }
-double Kern_LQ::prime(const aed_type2& b, const aed_type2& af, const aed_type2& ff) const
+double Kern_LQ::prime(const aed_type& b, const aed_type& af, const aed_type& ff) const
 {
     return derivative(b, af)*ff;
 }
diff --git a/src/kern_polynomial.cpp b/src/kern_polynomial.cpp
index cdc3a625f99ec1c14eaaa0c58015e87305256a2a..ca31fad2d44a935b25f7f292ab3f9a503f047eff 100644
--- a/src/kern_polynomial.cpp
+++ b/src/kern_polynomial.cpp
@@ -3,6 +3,7 @@
 Kern_Polynomial::Kern_Polynomial () {}
 Kern_Polynomial::Kern_Polynomial (const Config &config):
     Function_Base(config),
+    Kern_Base(config),
     d(config.get<int>("MPARAMS",0)),
     gamma(config.get<double>("MPARAMS",1)),
     c(config.get<double>("MPARAMS",2))
@@ -11,15 +12,15 @@ Kern_Polynomial::Kern_Polynomial (const Config &config):
         <<" | gamma: " << gamma << " | c: " << c << std::endl;
     read_basis_from_config(config,basis);
 }
-double Kern_Polynomial::operator() (const aed_type2& b, const aed_type2& af) const
+double Kern_Polynomial::operator() (const aed_type& b, const aed_type& af) const
 {
     return std::pow(gamma*(b*af)+c,d);
 }
-aed_type2 Kern_Polynomial::derivative(const aed_type2& b, const aed_type2& af) const
+aed_type Kern_Polynomial::derivative(const aed_type& b, const aed_type& af) const
 {
     return  d*gamma*std::pow(gamma*(b*af)+c,d-1)*b;
 }
-double Kern_Polynomial::prime(const aed_type2& b, const aed_type2& af, const aed_type2& ff) const
+double Kern_Polynomial::prime(const aed_type& b, const aed_type& af, const aed_type& ff) const
 {
     return derivative(b, af)*ff;
 }
diff --git a/src/kern_quadratic.cpp b/src/kern_quadratic.cpp
index 1f1961533c5c63c898296cd8938ab3cc39f224ab..3e772ad1323e173528624c326a2d2ac363b20798 100644
--- a/src/kern_quadratic.cpp
+++ b/src/kern_quadratic.cpp
@@ -2,28 +2,29 @@
 
 Kern_Quadratic::Kern_Quadratic () {}
 Kern_Quadratic::Kern_Quadratic (const Config &c):
-    Function_Base(c)
+    Function_Base(c),
+    Kern_Base(c)
 {
     if (get_verbose()) std::cout << std::endl << label << std::endl;
 
     read_basis_from_config(c,basis);
 }
-double Kern_Quadratic::operator() (const aed_type2& b, const aed_type2& af) const
+double Kern_Quadratic::operator() (const aed_type& b, const aed_type& af) const
 {
     double x = b*af;
     return  x*x;
 }
-aed_type2 Kern_Quadratic::derivative(const aed_type2& b, const aed_type2& af) const
+aed_type Kern_Quadratic::derivative(const aed_type& b, const aed_type& af) const
 {
-    double temp = 2.0 * b*af;
-    return b*temp;;
+    double temp = 2*(b*af);
+    return b*temp;
 }
-double Kern_Quadratic::prime(const aed_type2& b, const aed_type2& af, const aed_type2& ff) const
+double Kern_Quadratic::prime(const aed_type& b, const aed_type& af, const aed_type& ff) const
 {
     //return dot(derivative(b, af), ff);
     // it is faster this way
-    double scale = 2.0*b*af;
-    return scale*ff*b;
+    double scale = 2.0*(b*af);
+    return scale*(ff*b);
 }
 std::string Kern_Quadratic::get_label() const
 {
diff --git a/src/kern_rbf.cpp b/src/kern_rbf.cpp
index f8fb5132de4cfb39388febf24eb8f75d315a514d..e35ed017e6f391cc2aadbf4573286c4c011f7283 100644
--- a/src/kern_rbf.cpp
+++ b/src/kern_rbf.cpp
@@ -3,25 +3,27 @@
 Kern_RBF::Kern_RBF () {}
 Kern_RBF::Kern_RBF (const Config &c):
     Function_Base(c),
+    Kern_Base(c),
     sigma(c.get<double>("MPARAMS")),
-    gamma(1.0/(2.0*sigma*sigma))
+    gamma(1.0/(sigma*sigma))
 {
     if (get_verbose()) std::cout << std::endl << label << " | sigma: " << sigma
         << " | gamma: " << gamma << std::endl;
 
     read_basis_from_config(c,basis);
 }
-double Kern_RBF::operator() (const aed_type2& b, const aed_type2& af) const
+double Kern_RBF::operator() (const aed_type& b, const aed_type& af) const
 {
-    const aed_type2 v = af-b;
-    return exp(-gamma*(v*v));
+    const aed_type v = (af-b);
+    return exp(-0.5*gamma*(v*v));
 }
-aed_type2 Kern_RBF::derivative(const aed_type2& b, const aed_type2& af) const
+aed_type Kern_RBF::derivative(const aed_type& b, const aed_type& af) const
 {
-    return  -2.0*gamma*(af-b)*(*this)(af,b);
+    return  gamma*(b-af)*(*this)(b,af);
 }
-double Kern_RBF::prime(const aed_type2& b, const aed_type2& af, const aed_type2& ff) const
+double Kern_RBF::prime(const aed_type& b, const aed_type& af, const aed_type& ff) const
 {
+  // std::cout << "f:     " << ff << std::endl;
     return derivative(b, af)*ff;
 }
 std::string Kern_RBF::get_label() const
diff --git a/src/kern_sigmoid.cpp b/src/kern_sigmoid.cpp
index b3fbd49b57b8d9b5cfe75cc9e0609910c6608af0..419bb9e3201726ebb93e52315597387398dc617a 100644
--- a/src/kern_sigmoid.cpp
+++ b/src/kern_sigmoid.cpp
@@ -3,6 +3,7 @@
 Kern_Sigmoid::Kern_Sigmoid () {}
 Kern_Sigmoid::Kern_Sigmoid (const Config &config):
     Function_Base(config),
+    Kern_Base(config),
     gamma(config.get<double>("MPARAMS",0)),
     c(config.get<double>("MPARAMS",1))
 {
@@ -11,15 +12,15 @@ Kern_Sigmoid::Kern_Sigmoid (const Config &config):
 
     read_basis_from_config(config,basis);
 }
-double Kern_Sigmoid::operator() (const aed_type2& b, const aed_type2& af) const
+double Kern_Sigmoid::operator() (const aed_type& b, const aed_type& af) const
 {
     return std::tanh(gamma*(b*af)+c);
 }
-aed_type2 Kern_Sigmoid::derivative(const aed_type2& b, const aed_type2& af) const
+aed_type Kern_Sigmoid::derivative(const aed_type& b, const aed_type& af) const
 {
     return gamma*(1.0-std::pow((*this)(af,b),2))*b;
 }
-double Kern_Sigmoid::prime(const aed_type2& b, const aed_type2& af, const aed_type2& ff) const
+double Kern_Sigmoid::prime(const aed_type& b, const aed_type& af, const aed_type& ff) const
 {
     return derivative(b, af)*ff;
 }
diff --git a/src/m_train.cpp b/src/m_train.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3bc76b5d0f4d61120e9393a0672072a6319e8fdb
--- /dev/null
+++ b/src/m_train.cpp
@@ -0,0 +1,4 @@
+#include <tadah/models/m_train.h>
+
+// void M_Train::train_with_residuals(
+// }
diff --git a/tests/test_basis_functions.cpp b/tests/test_basis_functions.cpp
index 6af366dc176446b9afe24b63608d903725c52ba3..15005544be7f580916abfd6ea73f669dd23197b5 100644
--- a/tests/test_basis_functions.cpp
+++ b/tests/test_basis_functions.cpp
@@ -16,9 +16,9 @@
 
 TEST_CASE( "Testing BF_Linear", "[basis_functions]" ) {
   BF_Linear bf;
-  aed_type2 w(2);
+  aed_type w(2);
   w.random();
-  aed_type2 aed(2);
+  aed_type aed(2);
   aed.random();
   fd_type fd(2);
   fd.random();
@@ -32,7 +32,7 @@ TEST_CASE( "Testing BF_Linear", "[basis_functions]" ) {
   double fy = -1*(w*fd(1));
   double fz = -1*(w*fd(2));
   //central_difference<
-  //  aed_type2, aed_type2,BF_Linear, &BF_Linear::epredict>
+  //  aed_type, aed_type,BF_Linear, &BF_Linear::epredict>
   //  ();
   force_type f = bf.fpredict(w,fd,aed);
   REQUIRE_THAT(fx, Catch::Matchers::WithinRel(bf.fpredict(w,fd,aed,0)));
@@ -50,9 +50,9 @@ TEST_CASE( "Testing BF_Linear", "[basis_functions]" ) {
 }
 TEST_CASE( "Testing BF_Polynomial2", "[basis_functions]" ) {
   BF_Polynomial2 bf;
-  aed_type2 w(3);
+  aed_type w(3);
   w.random(12345);
-  aed_type2 aed(2);
+  aed_type aed(2);
   aed.random(12345);
   fd_type fd(2);
   fd.random(12345);
diff --git a/tests/test_cutoffs.cpp b/tests/test_cutoffs.cpp
index 997b03d627c019336081df0585fb294391e17935..aaac9d0561436505138407296c7b83e4475939c8 100644
--- a/tests/test_cutoffs.cpp
+++ b/tests/test_cutoffs.cpp
@@ -162,3 +162,46 @@ TEST_CASE( "Testing Cutoffs: Cut_Poly2", "[Cut_Poly2]" ) {
                Catch::Matchers::WithinAbs(-1.728, 1e-12));
     delete c2b;
 }
+TEST_CASE( "Testing Cutoffs: Cut_Cos_s", "[Cut_Cos_S]" ) {
+
+    REQUIRE_NOTHROW(Cut_Cos_S());
+    double rcut2b = 6.2;
+    double rcut2bsq = rcut2b*rcut2b;
+    using Cut = Cut_Cos_S;
+    std::string cuttype = "Cut_Cos_S";
+    Cut_Base *c2b = new Cut( rcut2b );
+
+    REQUIRE( c2b->label() == cuttype );
+
+    REQUIRE( c2b->calc(rcut2b) < std::numeric_limits<double>::min() );
+    REQUIRE( c2b->calc_prime(rcut2b) < std::numeric_limits<double>::min() );
+    REQUIRE( std::abs(c2b->get_rcut()-rcut2b)<std::numeric_limits<double>::min() );
+    REQUIRE( std::abs(c2b->get_rcut_sq()-rcut2bsq)<std::numeric_limits<double>::min() );
+
+    // cutoff cannot be negative
+    double temp = -0.1;
+    REQUIRE_THROWS(Cut( temp ));
+    REQUIRE_THROWS_AS(c2b->set_rcut(temp), std::runtime_error);
+    REQUIRE_THROWS_AS(c2b->test_rcut(temp), std::runtime_error);
+
+    // recheck after resetting cutoff
+    rcut2b=3.4;
+    rcut2bsq=rcut2b*rcut2b;
+    c2b->set_rcut(100000);
+    c2b->set_rcut(rcut2b);
+    REQUIRE( c2b->calc(rcut2b) < std::numeric_limits<double>::min() );
+    REQUIRE( c2b->calc_prime(rcut2b) < std::numeric_limits<double>::min() );
+    REQUIRE( std::abs(c2b->get_rcut()-rcut2b)<std::numeric_limits<double>::min() );
+    REQUIRE( std::abs(c2b->get_rcut_sq()-rcut2bsq)<std::numeric_limits<double>::min() );
+
+  REQUIRE_THAT(c2b->calc(2.0),
+               Catch::Matchers::WithinAbs(1, 1e-12));
+  REQUIRE_THAT(c2b->calc_prime(2.0),
+               Catch::Matchers::WithinAbs(0, 1e-12));
+
+  REQUIRE_THAT(c2b->calc(3.0),
+               Catch::Matchers::WithinAbs(0.3454915028, 1e-10));
+  REQUIRE_THAT(c2b->calc_prime(3.0),
+               Catch::Matchers::WithinAbs(-1.4939160824, 1e-10));
+    delete c2b;
+}
diff --git a/tests/test_d2.cpp b/tests/test_d2.cpp
index 7991eaa4e5885a57ee3989dab1f3d1c35ec8f494..84faae1792def1f4ed44f0dec4f6747da653d05a 100644
--- a/tests/test_d2.cpp
+++ b/tests/test_d2.cpp
@@ -1,23 +1,29 @@
 #include "catch2/catch.hpp"
 #include <tadah/models/descriptors/d2/d2_all.h>
 #include <tadah/core/maths.h>
+#include <tadah/core/periodic_table.h>
 #include <tadah/models/cutoffs.h>
 
 template<typename D, typename C>
 void test_d2(Config &c, double r) {
-  std::streambuf *orig = std::cout.rdbuf();
+  // std::streambuf *orig = std::cout.rdbuf();
   std::stringstream ss;
-  std::cout.rdbuf (ss.rdbuf()); // redirect cout
+  // std::cout.rdbuf (ss.rdbuf()); // redirect cout
+  c.add("TYPE2B","H");
+  c.add("TYPE2B","H");
+  std::cout << c << std::endl;
   D d(c);
-  std::cout.rdbuf (orig); // restore
+  // std::cout.rdbuf (orig); // restore
   double rcut = c.get<double>("RCUT2B");
+  // d.init_for_atoms(1,1);
   C fc(rcut);
-  aed_type2 aed(d.size());
-  d.calc_aed(r,r*r,fc.calc(r),aed);
+  d.set_fcut(&fc, false);
+  aed_type aed(d.size());
+  d.calc_aed(1,1,r,r*r,aed);
   fd_type fd(d.size());
-  d.calc_dXijdri(r,r*r,fc.calc(r),fc.calc_prime(r),fd);
+  d.calc_dXijdri(1,1,r,r*r,fd);
   fd_type fda(d.size());
-  d.calc_fd_approx(&fc, r, fda, 1e-6);
+  d.calc_fd_approx(1,1, r, fda, 1e-6);
 
   // Convert to std::vector, as catch2 matchers require these
   std::vector<double> fx(fd[0].data(),fd[0].data()+fd[0].size());
@@ -36,9 +42,9 @@ void test_d2(Config &c, double r) {
   REQUIRE_THAT(fya, Catch::Matchers::Approx(fy));
   REQUIRE_THAT(fza, Catch::Matchers::Approx(fz));
 
-  aed_type2 aed2(d.size());
+  aed_type aed2(d.size());
   fd_type fd2(d.size());
-  d.calc_all(r,r*r,fc.calc(r),fc.calc_prime(r),aed2,fd2);
+  d.calc_all(1,1,r,r*r,aed2,fd2);
   std::vector<double> fx2(fd2[0].data(),fd2[0].data()+fd2[0].size());
   std::vector<double> fy2(fd2[1].data(),fd2[1].data()+fd2[1].size());
   std::vector<double> fz2(fd2[2].data(),fd2[2].data()+fd2[2].size());
@@ -50,11 +56,13 @@ void test_d2(Config &c, double r) {
 
 template<typename D>
 void test_D2(Config &c) {
-  std::streambuf *orig = std::cout.rdbuf();
+  // std::streambuf *orig = std::cout.rdbuf();
   std::stringstream ss;
-  std::cout.rdbuf (ss.rdbuf()); // redirect cout
+  // std::cout.rdbuf (ss.rdbuf()); // redirect cout
   c.remove("VERBOSE");
   c.add("VERBOSE", 1);
+  c.add("ATOMS", "H");
+  c.add("WATOMS", 1);
   // We cannot test r=rcut as finite difference will fail
   // but we can test r slightly smaller than rcut
   std::vector<double> rs = {2,2.0001,2.0123,5,11};
@@ -73,143 +81,213 @@ void test_D2(Config &c) {
   Config c2; // INIT2B=false
   D d(c2);
   REQUIRE(d.size()==0);
-  std::cout.rdbuf (orig); // restore
+  // std::cout.rdbuf (orig); // restore
 }
 
-TEST_CASE( "Testing D2_Dummy", "[D2]" ) {
-  Config c;
-  double rcut=5.1;
-  c.remove("INIT2B");
-  c.add("INIT2B", "true");
-  c.add("RCUT2B", rcut);
-  test_d2<D2_Dummy,Cut_Dummy>(c,1);
+// Fixture for initialization
+struct TestFixture {
+    TestFixture() {
+        PeriodicTable::initialize();
+    }
+};
+
+TEST_CASE_METHOD(TestFixture, "Testing D2_Dummy", "[D2]") {
+    Config c;
+    double rcut = 5.1;
+    c.remove("INIT2B");
+    c.add("INIT2B", "true");
+    c.add("RCUT2B", rcut);
+    test_d2<D2_Dummy, Cut_Dummy>(c, 1);
 
-  REQUIRE_NOTHROW(D2_Dummy());
-  REQUIRE(D2_Dummy().label()=="D2_Dummy");
+    REQUIRE_NOTHROW(D2_Dummy());
+    REQUIRE(D2_Dummy().label() == "D2_Dummy");
 }
-TEST_CASE( "Testing D2_LJ", "[D2]" ) {
-  Config c;
-  c.remove("INIT2B");
-  c.add("INIT2B", "true");
-  test_D2<D2_LJ>(c);
-
-  D2_LJ d(c);
-  REQUIRE(d.size()==2);
-  REQUIRE(d.label()=="D2_LJ");
+
+TEST_CASE_METHOD(TestFixture, "Testing D2_LJ", "[D2]") {
+    Config c;
+    c.remove("INIT2B");
+    c.add("INIT2B", "true");
+    c.add("TYPE2B", "D2_LJ");
+    test_D2<D2_LJ>(c);
+
+    D2_LJ d(c);
+    REQUIRE(d.size() == 2);
+    REQUIRE(d.label() == "D2_LJ");
 }
-TEST_CASE( "Testing D2_BLIP", "[D2]" ) {
-  std::streambuf *orig = std::cout.rdbuf();
-  std::stringstream ss;
-  std::cout.rdbuf (ss.rdbuf()); // redirect cout
-  using D = D2_Blip;
-  Config c;
-  c.remove("INIT2B");
-  c.add("INIT2B", "true");
-  c.add("SGRID2B","0.01 0.4 5.6");
-  c.add("CGRID2B","0.0 1.2 3.4");
-  test_D2<D>(c);
-  D d(c);
-  REQUIRE(d.size()==3);
-  REQUIRE(d.label()=="D2_Blip");
-
-  // test throws
-  c.remove("SGRID2B");
-  c.remove("CGRID2B");
-  c.add("SGRID2B","0.4 5.6");
-  c.add("CGRID2B","0.0 1.2 3.4");
-  REQUIRE_THROWS(D(c));
-  c.remove("SGRID2B");
-  c.remove("CGRID2B");
-  c.add("SGRID2B","0.4 5.6");
-  c.add("CGRID2B","0.0");
-  REQUIRE_THROWS(D(c));
-
-  c.remove("SGRID2B");
-  c.remove("CGRID2B");
-  c.add("SGRID2B","0.0 0.4 5.6");
-  c.add("CGRID2B","0.0 1.2 3.4");
-  REQUIRE_THROWS(D(c));
-  std::cout.rdbuf (orig); // restore
+
+TEST_CASE_METHOD(TestFixture, "Testing D2_BLIP", "[D2]") {
+    // std::streambuf *orig = std::cout.rdbuf();
+    std::stringstream ss;
+    // std::cout.rdbuf(ss.rdbuf()); // redirect cout
+    using D = D2_Blip;
+
+    Config c;
+    c.remove("INIT2B");
+    c.add("INIT2B", "true");
+    c.add("SGRID2B", "0.01 0.4 5.6");
+  c.add("CGRID2B", "0.0 1.2 3.4");
+  c.add("TYPE2B", "D2_Blip");
+  c.add("TYPE2B", 3);
+  c.add("TYPE2B", 3);
+    test_D2<D>(c);
+    
+    D d(c);
+    REQUIRE(d.size() == 3);
+    REQUIRE(d.label() == "D2_Blip");
+
+    // Test throws
+    c.remove("SGRID2B");
+    c.remove("CGRID2B");
+    c.add("SGRID2B", "0.4 5.6");
+    c.add("CGRID2B", "0.0 1.2 3.4");
+    REQUIRE_THROWS(D(c));
+    
+    c.remove("SGRID2B");
+    c.remove("CGRID2B");
+    c.add("SGRID2B", "0.4 5.6");
+    c.add("CGRID2B", "0.0");
+    REQUIRE_THROWS(D(c));
+
+    c.remove("SGRID2B");
+    c.remove("CGRID2B");
+    c.add("SGRID2B", "0.0 0.4 5.6");
+    c.add("CGRID2B", "0.0 1.2 3.4");
+    REQUIRE_THROWS(D(c));
+    
+    // std::cout.rdbuf(orig); // restore
 }
-TEST_CASE( "Testing D2_BP", "[D2]" ) {
-  std::streambuf *orig = std::cout.rdbuf();
-  std::stringstream ss;
-  std::cout.rdbuf (ss.rdbuf()); // redirect cout
-  using D = D2_BP;
-  Config c;
-  c.remove("INIT2B");
-  c.add("INIT2B", "true");
-  c.add("SGRID2B","0.01 0.4");
-  c.add("CGRID2B","0.0 1.2");
-  test_D2<D>(c);
-  D d(c);
-  REQUIRE(d.size()==2);
-  REQUIRE(d.label()=="D2_BP");
-  // test throws
-  c.remove("SGRID2B");
-  c.remove("CGRID2B");
-  c.add("SGRID2B","0.4 5.6");
-  c.add("CGRID2B","0.0 1.2 3.4");
-  REQUIRE_THROWS(D(c));
-  c.remove("SGRID2B");
-  c.remove("CGRID2B");
-  c.add("SGRID2B","0.4 5.6");
-  c.add("CGRID2B","0.0");
-  REQUIRE_THROWS(D(c));
-
-  c.remove("SGRID2B");
-  c.remove("CGRID2B");
-  c.add("SGRID2B","0.0 0.4");
-  c.add("CGRID2B","0.0 1.2");
-  REQUIRE_THROWS(D(c));
-  std::cout.rdbuf (orig); // restore
+
+TEST_CASE_METHOD(TestFixture, "Testing D2_BP", "[D2]") {
+    // std::streambuf *orig = std::cout.rdbuf();
+    std::stringstream ss;
+    // std::cout.rdbuf(ss.rdbuf()); // redirect cout
+    using D = D2_BP;
+
+    Config c;
+    c.remove("INIT2B");
+    c.add("INIT2B", "true");
+    c.add("SGRID2B", "0.01 0.4");
+    c.add("CGRID2B", "0.0 1.2");
+  c.add("TYPE2B", "D2_BP");
+  c.add("TYPE2B", 2);
+  c.add("TYPE2B", 2);
+    test_D2<D>(c);
+    
+    D d(c);
+    REQUIRE(d.size() == 2);
+    REQUIRE(d.label() == "D2_BP");
+
+    // Test throws
+    c.remove("SGRID2B");
+    c.remove("CGRID2B");
+    c.add("SGRID2B", "0.4 5.6");
+    c.add("CGRID2B", "0.0 1.2 3.4");
+    REQUIRE_THROWS(D(c));
+    
+    c.remove("SGRID2B");
+    c.remove("CGRID2B");
+    c.add("SGRID2B", "0.4 5.6");
+    c.add("CGRID2B", "0.0");
+    REQUIRE_THROWS(D(c));
+
+    c.remove("SGRID2B");
+    c.remove("CGRID2B");
+    c.add("SGRID2B", "0.0 0.4");
+    c.add("CGRID2B", "0.0 1.2");
+    REQUIRE_THROWS(D(c));
+    
+    // std::cout.rdbuf(orig); // restore
 }
-TEST_CASE( "Testing D2_EAM", "[D2]" ) {
-  std::streambuf *orig = std::cout.rdbuf();
-  std::stringstream ss;
-  std::cout.rdbuf (ss.rdbuf()); // redirect cout
-  Config c;
-  c.remove("INIT2B");
-  c.add("INIT2B", "true");
-  c.add("SETFL","tests_data/eam.alloy");
-  test_D2<D2_EAM>(c);
-  D2_EAM d(c);
-  REQUIRE(d.size()==1);
-  REQUIRE(d.label()=="D2_EAM");
-
-  // thors
-  Config c_err;
-  c_err.remove("INIT2B");
-  c_err.add("INIT2B", "true");
-  c_err.add("SETFL","path/to/nowhere");
-  REQUIRE_THROWS(D2_EAM(c_err));
-  std::cout.rdbuf (orig); // restore
+
+TEST_CASE_METHOD(TestFixture, "Testing D2_EAM", "[D2]") {
+    // std::streambuf *orig = std::cout.rdbuf();
+    std::stringstream ss;
+    // std::cout.rdbuf(ss.rdbuf()); // redirect cout
+    
+    Config c;
+    c.remove("INIT2B");
+    c.add("INIT2B", "true");
+    c.add("SETFL", "tests_data/eam.alloy");
+    test_D2<D2_EAM>(c);
+
+    D2_EAM d(c);
+    REQUIRE(d.size() == 1);
+    REQUIRE(d.label() == "D2_EAM");
+
+    // Test throws
+    Config c_err;
+    c_err.remove("INIT2B");
+    c_err.add("INIT2B", "true");
+    c_err.add("SETFL", "path/to/nowhere");
+    REQUIRE_THROWS(D2_EAM(c_err));
+    
+    // std::cout.rdbuf(orig); // restore
 }
-TEST_CASE( "Testing D2_MIE", "[D2]" ) {
-  Config c;
-  c.remove("INIT2B");
-  c.add("INIT2B", "true");
-  c.add("SGRID2B","7 13");
-  test_D2<D2_MIE>(c);
-  D2_MIE d(c);
-  REQUIRE(d.size()==2);
-  REQUIRE(d.label()=="D2_MIE");
-
-  // test throws
-  c.remove("SGRID2B");
-  c.add("SGRID2B","7");
-  REQUIRE_THROWS(D2_MIE(c));
-
-  c.remove("SGRID2B");
-  c.add("SGRID2B","7 8 9");
-  REQUIRE_THROWS(D2_MIE(c));
-
-  c.remove("SGRID2B");
-  c.add("SGRID2B","-8 9");
-  REQUIRE_THROWS(D2_MIE(c));
-
-  c.remove("SGRID2B");
-  c.add("SGRID2B","8 -9");
-  REQUIRE_THROWS(D2_MIE(c));
+
+TEST_CASE_METHOD(TestFixture, "Testing D2_MIE", "[D2]") {
+    Config c;
+    c.remove("INIT2B");
+    c.add("INIT2B", "true");
+    c.add("TYPE2B", "D2_MIE");
+    c.add("TYPE2B", 7);
+    c.add("TYPE2B", 13);
+    test_D2<D2_MIE>(c);
+
+    D2_MIE d(c);
+    REQUIRE(d.size() == 2);
+    REQUIRE(d.label() == "D2_MIE");
+
+    // Test throws
+    c.remove("TYPE2B");
+    c.add("TYPE2B", "D2_MIE");
+    c.add("TYPE2B", 7);
+    REQUIRE_THROWS(D2_MIE(c));
+
+    c.remove("TYPE2B");
+    c.add("TYPE2B", "D2_MIE");
+    c.add("TYPE2B", 7);
+    c.add("TYPE2B", -8);
+    REQUIRE_THROWS(D2_MIE(c));
+
+    c.remove("TYPE2B");
+    c.add("TYPE2B", "D2_MIE");
+    c.add("TYPE2B", -7);
+    c.add("TYPE2B", 8);
+    REQUIRE_THROWS(D2_MIE(c));
+
+}
+TEST_CASE_METHOD(TestFixture, "Testing D2_ZBL", "[D2]") {
+    Config c;
+    c.remove("INIT2B");
+    c.add("INIT2B", "true");
+    c.add("TYPE2B", "D2_ZBL");
+    c.add("TYPE2B", -1);
+    c.add("TYPE2B", -1);
+    c.add("TYPE2B", -1);
+    c.add("TYPE2B", -1);
+    test_D2<D2_ZBL>(c);
+
+    D2_ZBL d(c);
+    REQUIRE(d.size() == 1);
+    REQUIRE(d.label() == "D2_ZBL");
+
+    // Test throws
+    c.remove("TYPE2B");
+    c.add("TYPE2B", "D2_ZBL");
+    c.add("TYPE2B", 7);
+    REQUIRE_THROWS(D2_ZBL(c));
+
+    c.remove("TYPE2B");
+    c.add("TYPE2B", "D2_ZBL");
+    c.add("TYPE2B", -7);
+    c.add("TYPE2B", -8);
+    REQUIRE_THROWS(D2_ZBL(c));
+
+    c.remove("TYPE2B");
+    c.add("TYPE2B", "D2_ZBL");
+    c.add("TYPE2B", 7);
+    c.add("TYPE2B", 8);
+    c.add("TYPE2B", 9);
+    REQUIRE_THROWS(D2_ZBL(c));
 
 }
diff --git a/tests/test_d3.cpp b/tests/test_d3.cpp
index a0927942b17955b69a4432479177efd668f53ad8..c8327eb67166aaf0641f5bc73c554ca269fa1dee 100644
--- a/tests/test_d3.cpp
+++ b/tests/test_d3.cpp
@@ -13,7 +13,7 @@ TEST_CASE( "Testing D3_Dummy", "[D3]" ) {
   D3_Dummy d(c);
   REQUIRE(d.size()==0);
   REQUIRE(d.label()=="D3_Dummy");
-  aed_type2 aed;
+  aed_type aed;
   fd_type fd;
   REQUIRE_NOTHROW(d.calc_aed(0,0,0,0,0,aed));
   REQUIRE_NOTHROW(d.calc_all(0,0,0,0,0,0.0,0.0,aed,fd));
diff --git a/tests/test_dm.cpp b/tests/test_dm.cpp
index 268f72e8b38763b0baebe7dc75df49758f6ab273..1800b4c1e71cb3c58f56ddea67cc212bb0111df0 100644
--- a/tests/test_dm.cpp
+++ b/tests/test_dm.cpp
@@ -6,8 +6,8 @@
 #include <cmath>
 #include <vector>
 
-template <typename DM, typename CUT>
-void central_difference_mb(DM *dm, CUT *fc, std::vector<Vec3d> &positions, 
+template <typename DM>
+void central_difference_mb(DM *dm, std::vector<Vec3d> &positions, 
                            size_t j, fd_type &fdij, double h=1e-10) {
 
   //size_t i=0;
@@ -30,22 +30,22 @@ void central_difference_mb(DM *dm, CUT *fc, std::vector<Vec3d> &positions,
 
     Vec3d del12n = x1-xjn;
     Vec3d del12p = x1-xjp;
-    aed_type2 rhoip;
-    aed_type2 rhoin;
+    aed_type rhoip;
+    aed_type rhoin;
 
     dm->init_rhoi(rhoip);
     dm->init_rhoi(rhoin);
 
-    dm->calc_rho_mb(del12p, rhoip,fc->calc(std::sqrt(del12p*del12p)));
-    dm->calc_rho_mb(del13,  rhoip,fc->calc(std::sqrt(del13*del13)));
-    dm->calc_rho_mb(del14,  rhoip,fc->calc(std::sqrt(del14*del14)));
+    dm->calc_rho_mb(1,1,del12p, rhoip);
+    dm->calc_rho_mb(1,1,del13,  rhoip);
+    dm->calc_rho_mb(1,1,del14,  rhoip);
 
-    dm->calc_rho_mb(del12n, rhoin,fc->calc(std::sqrt(del12n*del12n)));
-    dm->calc_rho_mb(del13, rhoin ,fc->calc(std::sqrt(del13*del13))); 
-    dm->calc_rho_mb(del14, rhoin ,fc->calc(std::sqrt(del14*del14)));
+    dm->calc_rho_mb(1,1,del12n, rhoin);
+    dm->calc_rho_mb(1,1,del13, rhoin ); 
+    dm->calc_rho_mb(1,1,del14, rhoin );
 
-    aed_type2 aedip(fdij.rows());
-    aed_type2 aedin(fdij.rows());
+    aed_type aedip(fdij.rows());
+    aed_type aedin(fdij.rows());
     aedip.set_zero();
     aedin.set_zero();
     dm->calc_aed(rhoip, aedip);
@@ -59,6 +59,10 @@ template<typename DM, typename CUT>
 void test_case(Config &config_mb, 
                std::string label,
                size_t size) {
+  config_mb.add("ATOMS", "H");
+  config_mb.add("WATOMS", 1);
+  config_mb.add("TYPEMB", "H");
+  config_mb.add("TYPEMB", "H");
   // Define a simple system of 4 atoms
   // Will calculate forces acting on the first atom
   Vec3d x1(0.0,0.0,0.0);
@@ -67,56 +71,35 @@ void test_case(Config &config_mb,
   Vec3d x4(-4.11,5.33,-3.44);
   std::vector<Vec3d> positions = {x1,x2,x3,x4};
 
-  Vec3d del12 = x1-x2;
-  Vec3d del13 = x1-x3;
-  Vec3d del14 = x1-x4;
-
-  Vec3d del21 = x2-x1;
-  Vec3d del23 = x2-x3;
-  Vec3d del24 = x2-x4;
-
-  Vec3d del31 = x3-x1;
-  Vec3d del32 = x3-x2;
-  Vec3d del34 = x3-x4;
-
-  Vec3d del41 = x4-x1;
-  Vec3d del42 = x4-x2;
-  Vec3d del43 = x4-x3;
-
-  double rsq12 = del12*del12;
-  double rsq13 = del13*del13;
-  double rsq14 = del14*del14;
-  double r12 = sqrt(rsq12);
-  double r13 = sqrt(rsq13);
-  double r14 = sqrt(rsq14);
-
-  double rsq21 = del21*del21;
-  double rsq23 = del23*del23;
-  double rsq24 = del24*del24;
-  double r21 = sqrt(rsq21);
-  double r23 = sqrt(rsq23);
-  double r24 = sqrt(rsq24);
-
-  double rsq31 = del31*del31;
-  double rsq32 = del32*del32;
-  double rsq34 = del34*del34;
-  double r31 = sqrt(rsq31);
-  double r32 = sqrt(rsq32);
-  double r34 = sqrt(rsq34);
-
-  double rsq41 = del41*del41;
-  double rsq42 = del42*del42;
-  double rsq43 = del43*del43;
-  double r41 = sqrt(rsq41);
-  double r42 = sqrt(rsq42);
-  double r43 = sqrt(rsq43);
+  Vec3d del12 = x1-x2; Vec3d del21 = x2-x1;
+  Vec3d del13 = x1-x3; Vec3d del23 = x2-x3;
+  Vec3d del14 = x1-x4; Vec3d del24 = x2-x4;
+
+  Vec3d del31 = x3-x1; Vec3d del41 = x4-x1;
+  Vec3d del32 = x3-x2; Vec3d del42 = x4-x2;
+  Vec3d del34 = x3-x4; Vec3d del43 = x4-x3;
+
+  double rsq12 = del12*del12;  double rsq21 = del21*del21;
+  double rsq13 = del13*del13;  double rsq23 = del23*del23;
+  double rsq14 = del14*del14;  double rsq24 = del24*del24;
+  double r12 = sqrt(rsq12);    double r21 = sqrt(rsq21);
+  double r13 = sqrt(rsq13);    double r23 = sqrt(rsq23);
+  double r14 = sqrt(rsq14);    double r24 = sqrt(rsq24);
+
+  double rsq31 = del31*del31;  double rsq41 = del41*del41;
+  double rsq32 = del32*del32;  double rsq42 = del42*del42;
+  double rsq34 = del34*del34;  double rsq43 = del43*del43;
+  double r31 = sqrt(rsq31);    double r41 = sqrt(rsq41);
+  double r32 = sqrt(rsq32);    double r42 = sqrt(rsq42);
+  double r34 = sqrt(rsq34);    double r43 = sqrt(rsq43);
 
   CUT fc(config_mb.get<double>("RCUTMB"));
   DM dm(config_mb);
-  aed_type2 aed1(dm.size());
-  aed_type2 aed2(dm.size());
-  aed_type2 aed3(dm.size());
-  aed_type2 aed4(dm.size());
+  dm.set_fcut(&fc,false);
+  aed_type aed1(dm.size());
+  aed_type aed2(dm.size());
+  aed_type aed3(dm.size());
+  aed_type aed4(dm.size());
   aed1.set_zero();
   aed2.set_zero();
   aed3.set_zero();
@@ -166,43 +149,42 @@ void test_case(Config &config_mb,
   dm.init_rhoi(rho3);
   dm.init_rhoi(rho4);
 
-  dm.calc_rho_mb(del12, rho1,fc.calc(r12));
-  dm.calc_rho_mb(del13, rho1,fc.calc(r13));
-  dm.calc_rho_mb(del14, rho1,fc.calc(r14));
+  dm.calc_rho_mb(1,1,del12, rho1);
+  dm.calc_rho_mb(1,1,del13, rho1);
+  dm.calc_rho_mb(1,1,del14, rho1);
 
-  dm.calc_rho_mb(del21, rho2,fc.calc(r21));
-  dm.calc_rho_mb(del23, rho2,fc.calc(r23));
-  dm.calc_rho_mb(del24, rho2,fc.calc(r24));
+  dm.calc_rho_mb(1,1,del21, rho2);
+  dm.calc_rho_mb(1,1,del23, rho2);
+  dm.calc_rho_mb(1,1,del24, rho2);
 
-  dm.calc_rho_mb(del31, rho3,fc.calc(r31));
-  dm.calc_rho_mb(del32, rho3,fc.calc(r32));
-  dm.calc_rho_mb(del34, rho3,fc.calc(r34));
+  dm.calc_rho_mb(1,1,del31, rho3);
+  dm.calc_rho_mb(1,1,del32, rho3);
+  dm.calc_rho_mb(1,1,del34, rho3);
 
-  dm.calc_rho_mb(del41, rho4,fc.calc(r41));
-  dm.calc_rho_mb(del42, rho4,fc.calc(r42));
-  dm.calc_rho_mb(del43, rho4,fc.calc(r43));
+  dm.calc_rho_mb(1,1,del41, rho4);
+  dm.calc_rho_mb(1,1,del42, rho4);
+  dm.calc_rho_mb(1,1,del43, rho4);
 
   dm.calc_aed(rho1,aed1);
   dm.calc_aed(rho2,aed2);
   dm.calc_aed(rho3,aed3);
   dm.calc_aed(rho4,aed4);
 
-  int mode = 
-  dm.calc_dXijdri(r12,rsq12,del12,fc.calc(r12),fc.calc_prime(r12),rho1,fd12);
-  dm.calc_dXijdri(r13,rsq13,del13,fc.calc(r13),fc.calc_prime(r13),rho1,fd13);
-  dm.calc_dXijdri(r14,rsq14,del14,fc.calc(r14),fc.calc_prime(r14),rho1,fd14);
+  dm.calc_dXijdri(1,1,r12,rsq12,del12,rho1,fd12);
+  dm.calc_dXijdri(1,1,r13,rsq13,del13,rho1,fd13);
+  dm.calc_dXijdri(1,1,r14,rsq14,del14,rho1,fd14);
 
-  dm.calc_dXijdri(r21,rsq21,del21,fc.calc(r21),fc.calc_prime(r21),rho2,fd21);
-  dm.calc_dXijdri(r23,rsq23,del23,fc.calc(r23),fc.calc_prime(r23),rho2,fd23);
-  dm.calc_dXijdri(r24,rsq24,del24,fc.calc(r24),fc.calc_prime(r24),rho2,fd24);
+  dm.calc_dXijdri(1,1,r21,rsq21,del21,rho2,fd21);
+  dm.calc_dXijdri(1,1,r23,rsq23,del23,rho2,fd23);
+  dm.calc_dXijdri(1,1,r24,rsq24,del24,rho2,fd24);
 
-  dm.calc_dXijdri(r31,rsq31,del31,fc.calc(r31),fc.calc_prime(r31),rho3,fd31);
-  dm.calc_dXijdri(r32,rsq32,del32,fc.calc(r32),fc.calc_prime(r32),rho3,fd32);
-  dm.calc_dXijdri(r34,rsq34,del34,fc.calc(r34),fc.calc_prime(r34),rho3,fd34);
+  dm.calc_dXijdri(1,1,r31,rsq31,del31,rho3,fd31);
+  dm.calc_dXijdri(1,1,r32,rsq32,del32,rho3,fd32);
+  dm.calc_dXijdri(1,1,r34,rsq34,del34,rho3,fd34);
 
-  dm.calc_dXijdri(r41,rsq41,del41,fc.calc(r41),fc.calc_prime(r41),rho4,fd41);
-  dm.calc_dXijdri(r42,rsq42,del42,fc.calc(r42),fc.calc_prime(r42),rho4,fd42);
-  dm.calc_dXijdri(r43,rsq43,del43,fc.calc(r43),fc.calc_prime(r43),rho4,fd43);
+  dm.calc_dXijdri(1,1,r41,rsq41,del41,rho4,fd41);
+  dm.calc_dXijdri(1,1,r42,rsq42,del42,rho4,fd42);
+  dm.calc_dXijdri(1,1,r43,rsq43,del43,rho4,fd43);
 
   fd_type FD12(dm.size());
   fd_type FD13(dm.size());
@@ -235,33 +217,21 @@ void test_case(Config &config_mb,
   FD41.set_zero();
   FD42.set_zero();
   FD43.set_zero();
-  dm.calc_dXijdri_dXjidri(r12,rsq12,del12,fc.calc(r12),fc.calc_prime(r12),
-                          rho1,rho2,FD12,1,1);
-  dm.calc_dXijdri_dXjidri(r13,rsq13,del13,fc.calc(r13),fc.calc_prime(r13),
-                          rho1,rho3,FD13,1,1);
-  dm.calc_dXijdri_dXjidri(r14,rsq14,del14,fc.calc(r14),fc.calc_prime(r14),
-                          rho1,rho4,FD14,1,1);
-
-  dm.calc_dXijdri_dXjidri(r21,rsq21,del21,fc.calc(r21),fc.calc_prime(r21),
-                          rho2,rho1,FD21,1,1);
-  dm.calc_dXijdri_dXjidri(r23,rsq23,del23,fc.calc(r23),fc.calc_prime(r23),
-                          rho2,rho3,FD23,1,1);
-  dm.calc_dXijdri_dXjidri(r24,rsq24,del24,fc.calc(r24),fc.calc_prime(r24),
-                          rho2,rho4,FD24,1,1);
-
-  dm.calc_dXijdri_dXjidri(r31,rsq31,del31,fc.calc(r31),fc.calc_prime(r31),
-                          rho3,rho1,FD31,1,1);
-  dm.calc_dXijdri_dXjidri(r32,rsq32,del32,fc.calc(r32),fc.calc_prime(r32),
-                          rho3,rho2,FD32,1,1);
-  dm.calc_dXijdri_dXjidri(r34,rsq34,del34,fc.calc(r34),fc.calc_prime(r34),
-                          rho3,rho4,FD34,1,1);
-
-  dm.calc_dXijdri_dXjidri(r41,rsq41,del41,fc.calc(r41),fc.calc_prime(r41),
-                          rho4,rho1,FD41,1,1);
-  dm.calc_dXijdri_dXjidri(r42,rsq42,del42,fc.calc(r42),fc.calc_prime(r42),
-                          rho4,rho2,FD42,1,1);
-  dm.calc_dXijdri_dXjidri(r43,rsq43,del43,fc.calc(r43),fc.calc_prime(r43),
-                          rho4,rho3,FD43,1,1);
+  dm.calc_dXijdri_dXjidri(1,1,r12,rsq12,del12,rho1,rho2,FD12,1);
+  dm.calc_dXijdri_dXjidri(1,1,r13,rsq13,del13,rho1,rho3,FD13,1);
+  dm.calc_dXijdri_dXjidri(1,1,r14,rsq14,del14,rho1,rho4,FD14,1);
+
+  dm.calc_dXijdri_dXjidri(1,1,r21,rsq21,del21,rho2,rho1,FD21,1);
+  dm.calc_dXijdri_dXjidri(1,1,r23,rsq23,del23,rho2,rho3,FD23,1);
+  dm.calc_dXijdri_dXjidri(1,1,r24,rsq24,del24,rho2,rho4,FD24,1);
+
+  dm.calc_dXijdri_dXjidri(1,1,r31,rsq31,del31,rho3,rho1,FD31,1);
+  dm.calc_dXijdri_dXjidri(1,1,r32,rsq32,del32,rho3,rho2,FD32,1);
+  dm.calc_dXijdri_dXjidri(1,1,r34,rsq34,del34,rho3,rho4,FD34,1);
+
+  dm.calc_dXijdri_dXjidri(1,1,r41,rsq41,del41,rho4,rho1,FD41,1);
+  dm.calc_dXijdri_dXjidri(1,1,r42,rsq42,del42,rho4,rho2,FD42,1);
+  dm.calc_dXijdri_dXjidri(1,1,r43,rsq43,del43,rho4,rho3,FD43,1);
 
   REQUIRE(FD12.isApprox(fd12-fd21,1e-6));
   REQUIRE(FD13.isApprox(fd13-fd31,1e-6));
@@ -279,25 +249,6 @@ void test_case(Config &config_mb,
   REQUIRE(FD42.isApprox(fd42-fd24,1e-6));
   REQUIRE(FD43.isApprox(fd43-fd34,1e-6));
 
-  // Do scalling just before approximation
-  if (mode==0) {
-    fd12[0] *= del12[0]/r12;
-    fd13[0] *= del13[0]/r13;
-    fd14[0] *= del14[0]/r14;
-
-    fd21[0] *= del21[0]/r21;
-    fd23[0] *= del23[0]/r23;
-    fd24[0] *= del24[0]/r24;
-
-    fd31[0] *= del31[0]/r31;
-    fd32[0] *= del32[0]/r32;
-    fd34[0] *= del34[0]/r34;
-
-    fd41[0] *= del41[0]/r41;
-    fd42[0] *= del42[0]/r42;
-    fd43[0] *= del43[0]/r43;
-  }
-
   // Calculate approximation
   fd_type fd12a(dm.size());
   fd_type fd13a(dm.size());
@@ -306,9 +257,9 @@ void test_case(Config &config_mb,
   fd13a.set_zero();
   fd14a.set_zero();
   double margin=1e-5;
-  central_difference_mb(&dm,&fc,positions, 1, fd12a, 1e-8);
-  central_difference_mb(&dm,&fc,positions, 2, fd13a, 1e-8);
-  central_difference_mb(&dm,&fc,positions, 3, fd14a, 1e-8);
+  central_difference_mb(&dm,positions, 1, fd12a, 1e-8);
+  central_difference_mb(&dm,positions, 2, fd13a, 1e-8);
+  central_difference_mb(&dm,positions, 3, fd14a, 1e-8);
 
   std::vector<double> fx12(fd12[0].data(),fd12[0].data()+fd12[0].size());
   std::vector<double> fy12(fd12[1].data(),fd12[1].data()+fd12[1].size());
@@ -343,117 +294,149 @@ void test_case(Config &config_mb,
   REQUIRE(dm.label() == label);
   REQUIRE(dm.size() == size);
 }
-TEST_CASE( "Testing DM_EAD 1", "[DM]" ) {
-  Config config_mb;
-  config_mb.add("RCUTMB",10);
-  config_mb.remove("INITMB");
-  config_mb.add("INITMB","true");
-  config_mb.add("AGRIDMB","2");
-  config_mb.add("CGRIDMB","1 2");
-  config_mb.add("SGRIDMB","0.1 0.2");
-  test_case<DM_EAD,Cut_Dummy>(config_mb, "DM_EAD", 3*2);
-  test_case<DM_EAD,Cut_Cos>(config_mb, "DM_EAD", 3*2);
-  test_case<DM_EAD,Cut_Tanh>(config_mb, "DM_EAD", 3*2);
-  test_case<DM_EAD,Cut_Poly2>(config_mb, "DM_EAD", 3*2);
+
+// Fixture to initialize the PeriodicTable
+struct TestFixture {
+    TestFixture() {
+        PeriodicTable::initialize();
+    }
+};
+TEST_CASE_METHOD(TestFixture, "Testing DM_EAD 1", "[DM]") {
+    Config config_mb;
+    config_mb.add("RCUTMB", 10);
+    config_mb.remove("INITMB");
+    config_mb.add("INITMB", "true");
+    config_mb.add("TYPEMB", "DM_EAD");
+    config_mb.add("TYPEMB", 2);
+    config_mb.add("TYPEMB", 2);
+    config_mb.add("TYPEMB", 2);
+    config_mb.add("CGRIDMB", "1 2");
+    config_mb.add("SGRIDMB", "0.1 0.2");
+    test_case<DM_EAD, Cut_Dummy>(config_mb, "DM_EAD", 3 * 2);
+    test_case<DM_EAD, Cut_Cos>(config_mb, "DM_EAD", 3 * 2);
+    test_case<DM_EAD, Cut_Tanh>(config_mb, "DM_EAD", 3 * 2);
+    test_case<DM_EAD, Cut_Poly2>(config_mb, "DM_EAD", 3 * 2);
 }
-TEST_CASE( "Testing DM_EAD 2", "[DM]" ) {
-  Config config_mb;
-  config_mb.add("RCUTMB",6);
-  config_mb.remove("INITMB");
-  config_mb.add("INITMB","true");
-  config_mb.add("AGRIDMB","3");
-  config_mb.add("CGRIDMB","1 2 3");
-  config_mb.add("SGRIDMB","0.1 0.2 0.9");
-  test_case<DM_EAD,Cut_Dummy>(config_mb, "DM_EAD", 4*3);
-  test_case<DM_EAD,Cut_Cos>(config_mb, "DM_EAD", 4*3);
-  test_case<DM_EAD,Cut_Tanh>(config_mb, "DM_EAD", 4*3);
-  test_case<DM_EAD,Cut_Poly2>(config_mb, "DM_EAD", 4*3);
+
+TEST_CASE_METHOD(TestFixture, "Testing DM_EAD 2", "[DM]") {
+    Config config_mb;
+    config_mb.add("RCUTMB", 6);
+    config_mb.remove("INITMB");
+    config_mb.add("INITMB", "true");
+    config_mb.add("TYPEMB", "DM_EAD");
+    config_mb.add("TYPEMB", 3);
+    config_mb.add("TYPEMB", 3);
+    config_mb.add("TYPEMB", 3);
+    config_mb.add("CGRIDMB", "1 2 3");
+    config_mb.add("SGRIDMB", "0.1 0.2 0.9");
+    test_case<DM_EAD, Cut_Dummy>(config_mb, "DM_EAD", 4 * 3);
+    test_case<DM_EAD, Cut_Cos>(config_mb, "DM_EAD", 4 * 3);
+    test_case<DM_EAD, Cut_Tanh>(config_mb, "DM_EAD", 4 * 3);
+    test_case<DM_EAD, Cut_Poly2>(config_mb, "DM_EAD", 4 * 3);
 }
-TEST_CASE( "Testing DM_Blip 1", "[DM]" ) {
-  Config config_mb;
-  config_mb.add("RCUTMB",10);
-  config_mb.remove("INITMB");
-  config_mb.add("INITMB","true");
-  config_mb.add("AGRIDMB","2");
-  config_mb.add("CGRIDMB","1 2");
-  config_mb.add("SGRIDMB","0.1 0.2");
-  test_case<DM_Blip,Cut_Dummy>(config_mb, "DM_Blip", 3*2);
-  test_case<DM_Blip,Cut_Cos>(config_mb, "DM_Blip", 3*2);
-  test_case<DM_Blip,Cut_Tanh>(config_mb, "DM_Blip", 3*2);
-  test_case<DM_Blip,Cut_Poly2>(config_mb, "DM_Blip", 3*2);
+
+TEST_CASE_METHOD(TestFixture, "Testing DM_Blip 1", "[DM]") {
+    Config config_mb;
+    config_mb.add("RCUTMB", 10);
+    config_mb.remove("INITMB");
+    config_mb.add("INITMB", "true");
+    config_mb.add("TYPEMB", "DM_Blip");
+    config_mb.add("TYPEMB", 2);
+    config_mb.add("TYPEMB", 2);
+    config_mb.add("TYPEMB", 2);
+    config_mb.add("CGRIDMB", "1 2");
+    config_mb.add("SGRIDMB", "0.1 0.2");
+    test_case<DM_Blip, Cut_Dummy>(config_mb, "DM_Blip", 3 * 2);
+    test_case<DM_Blip, Cut_Cos>(config_mb, "DM_Blip", 3 * 2);
+    test_case<DM_Blip, Cut_Tanh>(config_mb, "DM_Blip", 3 * 2);
+    test_case<DM_Blip, Cut_Poly2>(config_mb, "DM_Blip", 3 * 2);
 }
-TEST_CASE( "Testing DM_Blip 2", "[DM]" ) {
-  Config config_mb;
-  config_mb.add("RCUTMB",6);
-  config_mb.remove("INITMB");
-  config_mb.add("INITMB","true");
-  config_mb.add("AGRIDMB","3");
-  config_mb.add("CGRIDMB","1 2 3");
-  config_mb.add("SGRIDMB","0.1 0.2 0.9");
-  test_case<DM_Blip,Cut_Dummy>(config_mb, "DM_Blip", 4*3);
-  test_case<DM_Blip,Cut_Cos>(config_mb, "DM_Blip", 4*3);
-  test_case<DM_Blip,Cut_Tanh>(config_mb, "DM_Blip", 4*3);
-  test_case<DM_Blip,Cut_Poly2>(config_mb, "DM_Blip", 4*3);
+
+TEST_CASE_METHOD(TestFixture, "Testing DM_Blip 2", "[DM]") {
+    Config config_mb;
+    config_mb.add("RCUTMB", 6);
+    config_mb.remove("INITMB");
+    config_mb.add("INITMB", "true");
+    config_mb.add("TYPEMB", "DM_Blip");
+    config_mb.add("TYPEMB", 3);
+    config_mb.add("TYPEMB", 3);
+    config_mb.add("TYPEMB", 3);
+    config_mb.add("CGRIDMB", "1 2 3");
+    config_mb.add("SGRIDMB", "0.1 0.2 0.9");
+    test_case<DM_Blip, Cut_Dummy>(config_mb, "DM_Blip", 4 * 3);
+    test_case<DM_Blip, Cut_Cos>(config_mb, "DM_Blip", 4 * 3);
+    test_case<DM_Blip, Cut_Tanh>(config_mb, "DM_Blip", 4 * 3);
+    test_case<DM_Blip, Cut_Poly2>(config_mb, "DM_Blip", 4 * 3);
 }
-TEST_CASE( "Testing DM_EAM 1", "[DM]" ) {
-  Config config_mb;
-  config_mb.add("RCUTMB",5.58190742024);
-  config_mb.remove("INITMB");
-  config_mb.add("INITMB","true");
-  config_mb.add("SETFL","tests_data/eam.alloy");
-  test_case<DM_EAM,Cut_Dummy>(config_mb, "DM_EAM", 1);
-
-  // throws
-  Config c_err;
-  c_err.remove("INITMB");
-  c_err.add("INITMB", "true");
-  c_err.add("SETFL","path/to/nowhere");
-  REQUIRE_THROWS(DM_EAM(c_err));
+
+TEST_CASE_METHOD(TestFixture, "Testing DM_EAM 1", "[DM]") {
+    Config config_mb;
+    config_mb.add("RCUTMB", 5.58190742024);
+    config_mb.remove("INITMB");
+    config_mb.add("INITMB", "true");
+    config_mb.add("TYPEMB", "DM_EAM");
+    config_mb.add("TYPEMB", "tests_data/eam.alloy");
+    test_case<DM_EAM, Cut_Dummy>(config_mb, "DM_EAM", 1);
+
+    // Throws
+    Config c_err;
+    c_err.remove("INITMB");
+    c_err.add("INITMB", "true");
+    c_err.add("TYPEMB", "DM_EAM");
+    c_err.add("TYPEMB", "path/to/nowhere");
+    REQUIRE_THROWS(DM_EAM(c_err));
 }
-TEST_CASE( "Testing DM_EAM 2", "[DM]" ) {
-  Config config_mb;
-  config_mb.add("RCUTMB",5.58190742024);
-  config_mb.remove("INITMB");
-  config_mb.add("INITMB","true");
-  config_mb.add("SETFL","tests_data/eam.alloy");
-  test_case<DM_EAM,Cut_Dummy>(config_mb, "DM_EAM", 1);
+
+TEST_CASE_METHOD(TestFixture, "Testing DM_EAM 2", "[DM]") {
+    Config config_mb;
+    config_mb.add("RCUTMB", 5.58190742024);
+    config_mb.remove("INITMB");
+    config_mb.add("INITMB", "true");
+    config_mb.add("TYPEMB", "DM_EAM");
+    config_mb.add("TYPEMB", "tests_data/eam.alloy");
+    test_case<DM_EAM, Cut_Dummy>(config_mb, "DM_EAM", 1);
 }
-TEST_CASE( "Testing DM_Dummy 1", "[DM]" ) {
-  Config config_mb;
-  config_mb.add("RCUTMB",5.58190742024);
-  config_mb.remove("INITMB");
-  config_mb.add("INITMB","true");
-  config_mb.add("SETFL","tests_data/eam.alloy");
-  test_case<DM_EAM,Cut_Dummy>(config_mb, "DM_EAM", 1);
-
-  // throws
-  Config c_err;
-  c_err.remove("INITMB");
-  c_err.add("INITMB", "true");
-  c_err.add("SETFL","path/to/nowhere");
-  REQUIRE_THROWS(DM_EAM(c_err));
+
+TEST_CASE_METHOD(TestFixture, "Testing DM_EAM 3", "[DM]") {
+    Config config_mb;
+    config_mb.add("RCUTMB", 5.58190742024);
+    config_mb.remove("INITMB");
+    config_mb.add("INITMB", "true");
+    config_mb.add("TYPEMB", "DM_EAM");
+    config_mb.add("TYPEMB", "tests_data/eam.alloy");
+    test_case<DM_EAM, Cut_Dummy>(config_mb, "DM_EAM", 1);
+
+    // Throws
+    Config c_err;
+    c_err.remove("INITMB");
+    c_err.add("INITMB", "true");
+    c_err.add("SETFL", "path/to/nowhere");
+    REQUIRE_THROWS(DM_EAM(c_err));
 }
-TEST_CASE( "Testing DM_Dummy", "[DM]" ) {
-  Config c;
-  c.add("RCUTMB",5.58190742024);
-  c.remove("INITMB");
-  c.add("INITMB","true");
-
-  REQUIRE_NOTHROW(DM_Dummy());
-  REQUIRE_NOTHROW(DM_Dummy(c));
-
-  DM_Dummy d(c);
-  REQUIRE(d.size()==0);
-  REQUIRE(d.label()=="DM_Dummy");
-  aed_type2 aed;
-  rho_type rho1;
-  fd_type fd;
-  Vec3d del;
-  REQUIRE_NOTHROW(d.calc_aed(rho1,aed));
-  REQUIRE_NOTHROW(d.init_rhoi(rho1));
-  REQUIRE_NOTHROW(d.calc_rho(0,0,0,del,rho1));
-  REQUIRE_NOTHROW(d.calc_dXijdri(0,0,del,0,0,rho1,fd));
-  REQUIRE_NOTHROW(d.calc_dXijdri_dXjidri(0,0,del,0,0,rho1,rho1,fd,1,1));
-  REQUIRE(d.rhoi_size()==0);
-  REQUIRE(d.rhoip_size()==0);
+
+TEST_CASE_METHOD(TestFixture, "Testing DM_Dummy", "[DM]") {
+    Config c;
+    c.add("RCUTMB", 5.58190742024);
+    c.add("RCTYPEMB", "Cut_Cos");
+    c.remove("INITMB");
+    c.add("INITMB", "true");
+    c.add("TYPEMB", "DM_Dummy");
+
+    REQUIRE_NOTHROW(DM_Dummy());
+    REQUIRE_NOTHROW(DM_Dummy(c));
+
+    DM_Dummy d(c);
+    REQUIRE(d.size() == 0);
+    REQUIRE(d.label() == "DM_Dummy");
+    aed_type aed;
+    rho_type rho1;
+    fd_type fd;
+    Vec3d del;
+    REQUIRE_NOTHROW(d.calc_aed(rho1, aed));
+    REQUIRE_NOTHROW(d.init_rhoi(rho1));
+    REQUIRE_NOTHROW(d.calc_rho(0,0, 0, 0, del, rho1));
+    REQUIRE_NOTHROW(d.calc_dXijdri(1,1, 0, 0, del, rho1, fd));
+    REQUIRE_NOTHROW(d.calc_dXijdri_dXjidri(1, 1, 0, 0, del, rho1, rho1, fd, 1));
+    REQUIRE(d.rhoi_size() == 0);
+    REQUIRE(d.rhoip_size() == 0);
 }
diff --git a/tests/test_factory_cutoffs.cpp b/tests/test_factory_cutoffs.cpp
index 22e6a45a02e21e94fee71d27054eb8294ac5b5f1..2b19927b0722a5797634de870585a235bfedd7cd 100644
--- a/tests/test_factory_cutoffs.cpp
+++ b/tests/test_factory_cutoffs.cpp
@@ -36,5 +36,6 @@ TEST_CASE( "Testing Factory: Cutoffs", "[factory_cutoffs]" ) {
         REQUIRE( c2b->calc_prime(rcut2b) < std::numeric_limits<double>::min() );
         REQUIRE( std::abs(c2b->get_rcut()-rcut2b)<std::numeric_limits<double>::min() );
         REQUIRE( std::abs(c2b->get_rcut_sq()-rcut2bsq)<std::numeric_limits<double>::min() );
+        if (c2b) delete c2b;
     };
 }
diff --git a/tests/test_factory_functions.cpp b/tests/test_factory_functions.cpp
index 41704b1521be78f1f32ae636bb149a7cbdb8f6cd..191e2ed3bcb4afce94633c6b5a9d53b053785683 100644
--- a/tests/test_factory_functions.cpp
+++ b/tests/test_factory_functions.cpp
@@ -51,5 +51,6 @@ TEST_CASE( "Testing Factory: Functions", "[factory_functions]" ) {
     //REQUIRE( c2b->calc_prime(rcut2b) < std::numeric_limits<double>::min() );
     //REQUIRE( std::abs(c2b->get_rcut()-rcut2b)<std::numeric_limits<double>::min() );
     //REQUIRE( std::abs(c2b->get_rcut_sq()-rcut2bsq)<std::numeric_limits<double>::min() );
+    if (fb) delete fb;
   }
 }
diff --git a/tests/test_kernels.cpp b/tests/test_kernels.cpp
index 5066d6ce0b41d30d1a55c789751327b6c3f44551..66271a75de4ba48f0505c53882caf25fbc4b371f 100644
--- a/tests/test_kernels.cpp
+++ b/tests/test_kernels.cpp
@@ -29,9 +29,9 @@ TEST_CASE( "Testing Kern_Base", "[kernels]" ) {
   REQUIRE(Kern_RBF().get_label()=="Kern_RBF");
   REQUIRE(Kern_Sigmoid().get_label()=="Kern_Sigmoid");
 
-  aed_type2 w(2);
+  aed_type w(2);
   w.random(123,-1e-1,1e-1);
-  aed_type2 aed(3);
+  aed_type aed(3);
   aed.random(993,-1e-1,1e-1);
   fd_type fd(3);
   fd.random(753,-1e-1,1e-1);
@@ -65,8 +65,8 @@ TEST_CASE( "Testing Kern_Base", "[kernels]" ) {
 
   for(size_t i=0;i<2;++i)
     for(size_t j=0;j<3;++j) {
-      REQUIRE_THAT(matbas(i,j), 
-                   Catch::Matchers::WithinAbs(test_basis(i,j),1e-12));
+      REQUIRE_THAT(matbas(j,i), 
+                   Catch::Matchers::WithinAbs(test_basis(j,i),1e-12));
     }
 }
 TEST_CASE( "Testing Kern_Linear 1", "[kernels]" ) {
@@ -74,9 +74,9 @@ TEST_CASE( "Testing Kern_Linear 1", "[kernels]" ) {
   Config c;
   REQUIRE_NOTHROW(K(c));
   K kern;
-  aed_type2 w(4);
+  aed_type w(4);
   w.random();
-  aed_type2 aed(4);
+  aed_type aed(4);
   aed.random();
   fd_type fd(4);
   fd.random();
@@ -100,9 +100,9 @@ TEST_CASE( "Testing Kern_Linear 2", "[kernels]" ) {
   // Kern_Linear and BF_Linear should give the same anwer
   Kern_Linear kern;
   BF_Linear bf;
-  aed_type2 w(2);
+  aed_type w(2);
   w.random();
-  aed_type2 aed(2);
+  aed_type aed(2);
   aed.random();
   fd_type fd(2);
   fd.random();
@@ -125,9 +125,9 @@ TEST_CASE( "Testing Kern_Linear 3", "[kernels]" ) {
   using K=Kern_Linear;
   K kern;
 
-  aed_type2 w(2);
+  aed_type w(2);
   w.random(12345);
-  aed_type2 aed(2);
+  aed_type aed(2);
   aed.random(12345);
   fd_type fd(2);
   fd.random(12345);
@@ -151,15 +151,15 @@ TEST_CASE( "Testing Kern_Quadratic", "[kernels]" ) {
   basis.random();
   kern.set_basis(basis);
 
-  aed_type2 w(2);
+  aed_type w(2);
   w.random(12345);
-  aed_type2 aed(2);
+  aed_type aed(2);
   aed.random(12345);
   fd_type fd(2);
   fd.random(12345);
 
   for (size_t i=0; i<basis.cols(); ++i) {
-    const aed_type2 &b = basis.col(i);
+    const aed_type &b = basis.col(i);
     // test operator()
     REQUIRE_THAT((b*aed)*(b*aed), 
                  Catch::Matchers::WithinRel(kern(b,aed),1e-10));
@@ -168,7 +168,7 @@ TEST_CASE( "Testing Kern_Quadratic", "[kernels]" ) {
             isApprox(kern.derivative(b,aed),1e-10));
     // test prime
     for (size_t k=0; k<3; ++k) {
-      REQUIRE(( (2*b*aed)*fd(k)*b )==(kern.prime(b,aed,fd(k))));
+      REQUIRE( (2 * b * aed * fd(k) * b) == Approx(kern.prime(b, aed, fd(k))).epsilon(1e-10) );
       REQUIRE_THAT(kern.derivative(b, aed)*fd(k),
                    Catch::Matchers::WithinRel(kern.prime(b,aed,fd(k)),1e-10));
     }
@@ -184,15 +184,15 @@ TEST_CASE( "Testing Kern_LQ", "[kernels]" ) {
   basis.random();
   kern.set_basis(basis);
 
-  aed_type2 w(2);
+  aed_type w(2);
   w.random(12345);
-  aed_type2 aed(2);
+  aed_type aed(2);
   aed.random(12345);
   fd_type fd(2);
   fd.random(12345);
 
   for (size_t i=0; i<basis.cols(); ++i) {
-    const aed_type2 &b = basis.col(i);
+    const aed_type &b = basis.col(i);
     // test operator()
     REQUIRE_THAT(((b*aed)*(b*aed))+(b*aed), 
                  Catch::Matchers::WithinRel(kern(b,aed),1e-10));
@@ -224,15 +224,15 @@ TEST_CASE( "Testing Kern_RBF", "[kernels]" ) {
   basis.random(123,-1e-1,1e-1);
   kern.set_basis(basis);
 
-  aed_type2 w(2);
+  aed_type w(2);
   w.random(23,-1e-1,1e-1);
-  aed_type2 aed(2);
+  aed_type aed(2);
   aed.random(143,-1e-1,1e-1);
   fd_type fd(2);
   fd.random(133,-1e-1,1e-1);
 
   for (size_t i=0; i<basis.cols(); ++i) {
-    const aed_type2 &b = basis.col(i);
+    const aed_type &b = basis.col(i);
     // test operator()
     REQUIRE_THAT(exp(-g*((aed-b)*(aed-b))), 
                  Catch::Matchers::WithinRel(kern(b,aed)));
@@ -266,15 +266,15 @@ TEST_CASE( "Testing Kern_Sigmoid", "[kernels]" ) {
   basis.random(123,-1e-1,1e-1);
   kern.set_basis(basis);
 
-  aed_type2 w(2);
+  aed_type w(2);
   w.random(23,-1e-1,1e-1);
-  aed_type2 aed(2);
+  aed_type aed(2);
   aed.random(143,-1e-1,1e-1);
   fd_type fd(2);
   fd.random(133,-1e-1,1e-1);
 
   for (size_t i=0; i<basis.cols(); ++i) {
-    const aed_type2 &b = basis.col(i);
+    const aed_type &b = basis.col(i);
     // test operator()
     REQUIRE_THAT(std::tanh(g*(b*aed)+s), Catch::Matchers::WithinRel(kern(b,aed)));
     // test derivative
@@ -309,15 +309,15 @@ TEST_CASE( "Testing Kern_Polynomial", "[kernels]" ) {
   basis.random(123,-1e-1,1e-1);
   kern.set_basis(basis);
 
-  aed_type2 w(2);
+  aed_type w(2);
   w.random(23,-1e-1,1e-1);
-  aed_type2 aed(2);
+  aed_type aed(2);
   aed.random(143,-1e-1,1e-1);
   fd_type fd(2);
   fd.random(133,-1e-1,1e-1);
 
   for (size_t i=0; i<basis.cols(); ++i) {
-    const aed_type2 &b = basis.col(i);
+    const aed_type &b = basis.col(i);
     // test operator()
     REQUIRE_THAT(std::pow(g*(b*aed)+s,d), Catch::Matchers::WithinRel(kern(b,aed)));
     // test derivative
diff --git a/tests/test_ols.cpp b/tests/test_ols.cpp
index 81c776452f59e941b865b4b99b4dfdb72b4935b9..8e6e63ef051166372c659f795660ea477a1b22d4 100644
--- a/tests/test_ols.cpp
+++ b/tests/test_ols.cpp
@@ -7,15 +7,15 @@
 TEST_CASE("Testing OLS") {
   Matrix Phi1(3, 3, {1, 4, 7, 2, 5, 8, 3, 6, 9});
   Matrix Phi2(3, 3, {1, 4, 7, 2, 5, 8, 3, 6, 9});
-  aed_type2 b1(3);
+  aed_type b1(3);
   b1[0]=1;
   b1[1]=1;
   b1[2]=1;
-  aed_type2 w(3);
-  aed_type2 b2=b1;
+  aed_type w(3);
+  aed_type b2=b1;
 
   OLS::solve(Phi1,b1,w);
-  aed_type2 p= Phi2*w;
+  aed_type p= Phi2*w;
   REQUIRE_THAT(p[0], Catch::Matchers::WithinRel(b2[0]));
   REQUIRE_THAT(p[1], Catch::Matchers::WithinRel(b2[1]));
   REQUIRE_THAT(p[2], Catch::Matchers::WithinRel(b2[2]));
diff --git a/tests/test_ridge_regression.cpp b/tests/test_ridge_regression.cpp
index 317d6af615ed7e19d6f1ef4c322f35421f368b46..5c56e51d2d9f950b528ebcca92a2af82d3230dd4 100644
--- a/tests/test_ridge_regression.cpp
+++ b/tests/test_ridge_regression.cpp
@@ -6,17 +6,17 @@
 TEST_CASE("Testing RR") {
   Matrix Phi1(3, 3, {1, 4, 7, 2, 5, 8, 3, 6, 9});
   Matrix Phi2(3, 3, {1, 4, 7, 2, 5, 8, 3, 6, 9});
-  aed_type2 b1(3);
+  aed_type b1(3);
   b1[0]=1;
   b1[1]=1;
   b1[2]=1;
-  aed_type2 w(3);
-  aed_type2 b2=b1;
+  aed_type w(3);
+  aed_type b2=b1;
 
   SECTION("lambda zero") {
     double lambda = 0;
     RidgeRegression::solve(Phi1,b1,w, lambda);
-    aed_type2 p= Phi2*w;
+    aed_type p= Phi2*w;
     REQUIRE_THAT(p[0], Catch::Matchers::WithinRel(b2[0]));
     REQUIRE_THAT(p[1], Catch::Matchers::WithinRel(b2[1]));
     REQUIRE_THAT(p[2], Catch::Matchers::WithinRel(b2[2]));
@@ -24,7 +24,7 @@ TEST_CASE("Testing RR") {
   SECTION("small lambda ") {
     double lambda = 1e-10;
     RidgeRegression::solve(Phi1,b1,w, lambda);
-    aed_type2 p= Phi2*w;
+    aed_type p= Phi2*w;
     REQUIRE(p.isApprox(b2,lambda));
   }
 }
diff --git a/tests/test_svd.cpp b/tests/test_svd.cpp
index e86a18524a947601f5884977c8a7fee5d1632e7d..d434096dbfc846836abec24fd1fa514d81f726db 100644
--- a/tests/test_svd.cpp
+++ b/tests/test_svd.cpp
@@ -1,7 +1,6 @@
 #include "catch2/catch.hpp"
 #include <tadah/core/maths.h>
 #include <tadah/models/svd.h>
-#include <iomanip>
 
 void multiplyMatrices(const double* A, const double* B, double* C, int m, int n, int p) {
   // C = A * B
@@ -76,7 +75,7 @@ TEST_CASE("SVD Numeric Accuracy - Column-Major Order") {
   Matrix mU (U, m,n);
   Matrix mVT (VT,n,n);
 
-  aed_type2 vec(S, n);
+  aed_type vec(S, n);
   REQUIRE_THAT(16.848103352614209, Catch::Matchers::WithinRel(S[0]));
   REQUIRE_THAT(1.068369514554709, Catch::Matchers::WithinRel(S[1]));
   REQUIRE_THAT(0, Catch::Matchers::WithinAbs(S[2],1e-15));