diff --git a/include/tadah/mlip/descriptors_calc.h b/include/tadah/mlip/descriptors_calc.h
index 640a86227922792c57250fd16ad40fe1242b0d81..daaf78cbb286483a7e03965f839668480b9deb2f 100644
--- a/include/tadah/mlip/descriptors_calc.h
+++ b/include/tadah/mlip/descriptors_calc.h
@@ -122,6 +122,7 @@ class DescriptorsCalc: public DC_Base {
         void calc(const Structure &st, StDescriptors &std);
         void calc_dimer(const Structure &st, StDescriptors &std);
         void common_constructor();
+        double weights[119];  // ignore zero index; w[1]->H
 
 };
 
diff --git a/include/tadah/mlip/descriptors_calc.hpp b/include/tadah/mlip/descriptors_calc.hpp
index 5afb987346586758255464ae46bfac7c422f7e77..74b4b91b5b922d12ae501d2ff0b9de27d9fdf884 100644
--- a/include/tadah/mlip/descriptors_calc.hpp
+++ b/include/tadah/mlip/descriptors_calc.hpp
@@ -1,441 +1,454 @@
 #ifndef DESCRIPTORS_CALC_HPP
 #define DESCRIPTORS_CALC_HPP
 
-// This file is includeed back to descriptors_calc.h
-// So There is no need to include headers
-// Useful for debugging though...
 #include <tadah/mlip/descriptors_calc.h>
+#include <tadah/core/periodic_table.h>
 
 #include <cstdio>
+
 template <typename D2, typename D3, typename DM, typename C2, typename C3, typename CM>
 template <typename T1, typename T2, typename T3>
 DescriptorsCalc<D2,D3,DM,C2,C3,CM>::DescriptorsCalc(Config &c, T1 &t1, T2 &t2, T3 &t3):
-    config(c),
-    d2(t1),
-    d3(t2),
-    dm(t3)
+  config(c),
+  d2(t1),
+  d3(t2),
+  dm(t3)
 {
-    if (!config.exist("DSIZE"))
-        common_constructor();
-    else {
-        size_t bias=0;
-        if (config.get<bool>("BIAS"))
-            bias++;
-        if (config.get<bool>("INIT2B")) {
-            d2.fidx=bias;
-        }
-        if (config.get<bool>("INITMB")) {
-            dm.fidx=bias+d2.size();
-        }
+  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;
+  }
+  if (!config.exist("DSIZE"))
+    common_constructor();
+  else {
+    size_t bias=0;
+    if (config.get<bool>("BIAS"))
+      bias++;
+    if (config.get<bool>("INIT2B")) {
+      d2.fidx=bias;
+    }
+    if (config.get<bool>("INITMB")) {
+      dm.fidx=bias+d2.size();
     }
+  }
 }
 template <typename D2, typename D3, typename DM, typename C2, typename C3, typename CM>
 DescriptorsCalc<D2,D3,DM,C2,C3,CM>::DescriptorsCalc(Config &c):
-    DescriptorsCalc(c,c,c,c)
+  DescriptorsCalc(c,c,c,c)
 {
-    if (c.get<bool>("INIT2B")) {
-        c2 = C2(c.get<double>("RCUT2B"));
-        if (!config.exist("RCTYPE2B"))
-            config.add("RCTYPE2B",c2.label());
-    }
-    else {
-        c2 = C2(0);
-    }
-    if (c.get<bool>("INIT3B")) {
-        c3 = C3(c.get<double>("RCUT3B"));
-        if (!config.exist("RCTYPE3B"))
-            config.add("RCTYPE3B",c3.label());
-    }
-    else {
-        c3 = C3(0);
-    }
-    if (c.get<bool>("INITMB")) {
-        cm = CM(c.get<double>("RCUTMB"));
-        if (!config.exist("RCTYPEMB"))
-            config.add("RCTYPEMB",cm.label());
-    }
-    else {
-        cm = CM(0);
-    }
+  if (c.get<bool>("INIT2B")) {
+    c2 = C2(c.get<double>("RCUT2B"));
+    if (!config.exist("RCTYPE2B"))
+      config.add("RCTYPE2B",c2.label());
+  }
+  else {
+    c2 = C2(0);
+  }
+  if (c.get<bool>("INIT3B")) {
+    c3 = C3(c.get<double>("RCUT3B"));
+    if (!config.exist("RCTYPE3B"))
+      config.add("RCTYPE3B",c3.label());
+  }
+  else {
+    c3 = C3(0);
+  }
+  if (c.get<bool>("INITMB")) {
+    cm = CM(c.get<double>("RCUTMB"));
+    if (!config.exist("RCTYPEMB"))
+      config.add("RCTYPEMB",cm.label());
+  }
+  else {
+    cm = CM(0);
+  }
 }
 
 template <typename D2, typename D3, typename DM, typename C2, typename C3, typename CM>
-        template <typename T1, typename T2, typename T3,typename T4, typename T5, typename T6>
+template <typename T1, typename T2, typename T3,typename T4, typename T5, typename T6>
 DescriptorsCalc<D2,D3,DM,C2,C3,CM>::DescriptorsCalc(Config &c, T1 &d2, T2 &d3, T3 &dm, T4 &c2, T5 &c3, T6 &cm):
-    config(c),
-    c2(c2),
-    c3(c3),
-    cm(cm),
-    d2(d2),
-    d3(d3),
-    dm(dm)
+  config(c),
+  c2(c2),
+  c3(c3),
+  cm(cm),
+  d2(d2),
+  d3(d3),
+  dm(dm)
 {
-    if (c.get<bool>("INIT2B")) {
-        if (!config.exist("RCTYPE2B"))
-            config.add("RCTYPE2B",c2.label());
-    }
-    if (c.get<bool>("INIT3B")) {
-        if (!config.exist("RCTYPE3B"))
-            config.add("RCTYPE3B",c3.label());
-    }
-    if (c.get<bool>("INITMB")) {
-        if (!config.exist("RCTYPEMB"))
-            config.add("RCTYPEMB",cm.label());
-    }
-
-    if (!config.exist("DSIZE"))
-        common_constructor();
+  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;
+  }
+  if (c.get<bool>("INIT2B")) {
+    if (!config.exist("RCTYPE2B"))
+      config.add("RCTYPE2B",c2.label());
+  }
+  if (c.get<bool>("INIT3B")) {
+    if (!config.exist("RCTYPE3B"))
+      config.add("RCTYPE3B",c3.label());
+  }
+  if (c.get<bool>("INITMB")) {
+    if (!config.exist("RCTYPEMB"))
+      config.add("RCTYPEMB",cm.label());
+  }
+
+  if (!config.exist("DSIZE"))
+    common_constructor();
 }
 template <typename D2, typename D3, typename DM, typename C2, typename C3, typename CM>
 void DescriptorsCalc<D2,D3,DM,C2,C3,CM>::common_constructor() {
-    size_t dsize=0;
-
-    // Calculate total size of the descriptor.
-    // Add relevant keys to the config.
-    // Set first indices for all types
-    size_t bias=0;
-    if (config.get<bool>("BIAS"))
-        bias++;
-    if (config.get<bool>("INIT2B")) {
-        config.add("SIZE2B",d2.size());
-        if (!config.exist("TYPE2B"))
-            config.add("TYPE2B",d2.label());
-        dsize+=d2.size();
-        d2.fidx=bias;
-    }
-    else {
-        config.add("SIZE2B",0);
-    }
-
-    if (config.get<bool>("INIT3B")) {
-        config.add("SIZE3B",d3.size());
-        if (!config.exist("TYPE3B"))
-            config.add("TYPE3B",d3.label());
-        dsize+=d3.size();
-    }
-    else {
-        config.add("SIZE3B",0);
-    }
-
-    if (config.get<bool>("INITMB")) {
-        config.add("SIZEMB",dm.size());
-        if (!config.exist("TYPEMB"))
-            config.add("TYPEMB",dm.label());
-        dsize+=dm.size();
-        dm.fidx=bias+d2.size();
-    }
-    else {
-        config.add("SIZEMB",0);
-    }
-
-    if (!dsize)
-        throw std::runtime_error("The descriptor size is 0, check your config.");
-
-    if (config.get<bool>("BIAS"))
-        dsize++;
-
-    config.add("DSIZE",dsize);
+  size_t dsize=0;
+
+  // Calculate total size of the descriptor.
+  // Add relevant keys to the config.
+  // Set first indices for all types
+  size_t bias=0;
+  if (config.get<bool>("BIAS"))
+    bias++;
+  if (config.get<bool>("INIT2B")) {
+    config.add("SIZE2B",d2.size());
+    if (!config.exist("TYPE2B"))
+      config.add("TYPE2B",d2.label());
+    dsize+=d2.size();
+    d2.fidx=bias;
+  }
+  else {
+    config.add("SIZE2B",0);
+  }
+
+  if (config.get<bool>("INIT3B")) {
+    config.add("SIZE3B",d3.size());
+    if (!config.exist("TYPE3B"))
+      config.add("TYPE3B",d3.label());
+    dsize+=d3.size();
+  }
+  else {
+    config.add("SIZE3B",0);
+  }
+
+  if (config.get<bool>("INITMB")) {
+    config.add("SIZEMB",dm.size());
+    if (!config.exist("TYPEMB"))
+      config.add("TYPEMB",dm.label());
+    dsize+=dm.size();
+    dm.fidx=bias+d2.size();
+  }
+  else {
+    config.add("SIZEMB",0);
+  }
+
+  if (!dsize)
+    throw std::runtime_error("The descriptor size is 0, check your config.");
+
+  if (config.get<bool>("BIAS"))
+    dsize++;
+
+  config.add("DSIZE",dsize);
 }
 template <typename D2, typename D3, typename DM, typename C2, typename C3, typename CM>
 void DescriptorsCalc<D2,D3,DM,C2,C3,CM>::calc_rho(const Structure &st, StDescriptors &st_d) {
-    double rcut_mb_sq = pow(config.get<double>("RCUTMB"),2);
-    rhos_type &rhos = st_d.rhos;
-    size_t s = dm.rhoi_size()+dm.rhoip_size();
-    rhos.resize(s,st.natoms());
-    rhos.set_zero();
+  double rcut_mb_sq = pow(config.get<double>("RCUTMB"),2);
+  rhos_type &rhos = st_d.rhos;
+  size_t s = dm.rhoi_size()+dm.rhoip_size();
+  rhos.resize(s,st.natoms());
+  rhos.set_zero();
 
   Vec3d delij;
-    for (size_t i=0; i<st.natoms(); ++i) {
-        const Atom &a1 = st(i);
-
-        for (size_t jj=0; jj<st.nn_size(i); ++jj) {
-            const Vec3d &a2pos = st.nn_pos(i,jj);
-            //delij = a1.position - a2pos;
-            delij[0] = a1.position[0] - a2pos[0];
-            delij[1] = a1.position[1] - a2pos[1];
-            delij[2] = a1.position[2] - a2pos[2];
-
-            //double rij_sq = delij * delij;
-            double rij_sq = delij[0]*delij[0] + delij[1]*delij[1] + delij[2]*delij[2];
-            if (rij_sq > rcut_mb_sq) continue;
-            int Zj = st.near_neigh_atoms[i][jj].Z;
-            double rij = sqrt(rij_sq);
-            double fc_ij = Zj*cm.calc(rij);
-            dm.calc_rho(rij,rij_sq,fc_ij,delij,st_d.get_rho(i));
-        }
+  for (size_t i=0; i<st.natoms(); ++i) {
+    const Atom &a1 = st(i);
+
+    for (size_t jj=0; jj<st.nn_size(i); ++jj) {
+      const Vec3d &a2pos = st.nn_pos(i,jj);
+      //delij = a1.position - a2pos;
+      delij[0] = a1.position[0] - a2pos[0];
+      delij[1] = a1.position[1] - a2pos[1];
+      delij[2] = a1.position[2] - a2pos[2];
+
+      //double rij_sq = delij * delij;
+      double rij_sq = delij[0]*delij[0] + delij[1]*delij[1] + delij[2]*delij[2];
+      if (rij_sq > rcut_mb_sq) continue;
+      int Zj = st.near_neigh_atoms[i][jj].Z;
+      double wj = weights[Zj];
+      double rij = sqrt(rij_sq);
+      double fc_ij = wj*cm.calc(rij);
+      dm.calc_rho(rij,rij_sq,fc_ij,delij,st_d.get_rho(i));
     }
+  }
 }
 
 template <typename D2, typename D3, typename DM, typename C2, typename C3, typename CM>
 void DescriptorsCalc<D2,D3,DM,C2,C3,CM>::calc(const Structure &st, StDescriptors &st_d) {
 
-    bool init2b = config.get<bool>("INIT2B");
-    bool initmb = config.get<bool>("INITMB");
+  bool init2b = config.get<bool>("INIT2B");
+  bool initmb = config.get<bool>("INITMB");
 
-    size_t size2b=0;
-    size_t sizemb=0;
-    if (init2b) size2b=config.get<size_t>("SIZE2B");
-    if (initmb) sizemb=config.get<size_t>("SIZEMB");
+  size_t size2b=0;
+  size_t sizemb=0;
+  if (init2b) size2b=config.get<size_t>("SIZE2B");
+  if (initmb) sizemb=config.get<size_t>("SIZEMB");
 
-    // deal with the case initmb is set to true but dummy is used
-    init2b = init2b && size2b;
-    initmb = initmb && sizemb;
+  // deal with the case initmb is set to true but dummy is used
+  init2b = init2b && size2b;
+  initmb = initmb && sizemb;
 
-    size_t bias=0;
-    if (config.get<bool>("BIAS"))
-        bias++;
+  size_t bias=0;
+  if (config.get<bool>("BIAS"))
+    bias++;
 
-    if (initmb)
-        calc_rho(st,st_d);
+  if (initmb)
+    calc_rho(st,st_d);
 
-    double rcut_max_sq = pow(config.get<double>("RCUTMAX"),2);
-    double rcut_2b_sq = 0.0;
-    double rcut_mb_sq = 0.0;
+  double rcut_max_sq = pow(config.get<double>("RCUTMAX"),2);
+  double rcut_2b_sq = 0.0;
+  double rcut_mb_sq = 0.0;
 
-    if (init2b) rcut_2b_sq = pow(config.get<double>("RCUT2B"),2);
-    if (initmb) rcut_mb_sq = pow(config.get<double>("RCUTMB"),2);
+  if (init2b) rcut_2b_sq = pow(config.get<double>("RCUT2B"),2);
+  if (initmb) rcut_mb_sq = pow(config.get<double>("RCUTMB"),2);
 
-    // zero all aeds and set bias
-    for (size_t i=0; i<st.natoms(); ++i) {
-        aed_type2 &aed = st_d.get_aed(i);
-        aed.set_zero();
-        aed(0)=static_cast<double>(bias);   // set bias
-    }
+  // zero all aeds and set bias
+  for (size_t i=0; i<st.natoms(); ++i) {
+    aed_type2 &aed = st_d.get_aed(i);
+    aed.set_zero();
+    aed(0)=static_cast<double>(bias);   // set bias
+  }
 
-    // calculate many-body energy
-    // do it before main loop so rho prime
-    // can be calculated
-    if (initmb) {
-        for (size_t i=0; i<st.natoms(); ++i) {
-            aed_type2 &aed = st_d.get_aed(i);
-            rho_type& rhoi = st_d.get_rho(i);
-            dm.calc_aed(rhoi,aed);
-        }
+  // calculate many-body energy
+  // do it before main loop so rho prime
+  // can be calculated
+  if (initmb) {
+    for (size_t i=0; i<st.natoms(); ++i) {
+      aed_type2 &aed = st_d.get_aed(i);
+      rho_type& rhoi = st_d.get_rho(i);
+      dm.calc_aed(rhoi,aed);
     }
+  }
 
   bool use_force = config.get<bool>("FORCE");
   bool use_stress = config.get<bool>("STRESS");
 
   Vec3d delij;
-    for (size_t i=0; i<st.natoms(); ++i) {
-        const Atom &a1 = st(i);
-        aed_type2 &aed = st_d.get_aed(i);
-
-        for (size_t jj=0; jj<st.nn_size(i); ++jj) {
-            const Vec3d &a2pos = st.nn_pos(i,jj);
-
-            delij[0] = a1.position[0] - a2pos[0];
-            delij[1] = a1.position[1] - a2pos[1];
-            delij[2] = a1.position[2] - a2pos[2];
-
-            double rij_sq = delij[0]*delij[0] + delij[1]*delij[1] + delij[2]*delij[2];
-
-            if (rij_sq > rcut_max_sq) continue;
-            int Zj = st.near_neigh_atoms[i][jj].Z;
-            double rij = sqrt(rij_sq);
-            double rij_inv = 1.0/rij;
-
-            // CALCULATE TWO-BODY TERM
-            if (use_force || use_stress) {
-                fd_type &fd_ij = st_d.fd[i][jj];
-                if (rij_sq <= rcut_2b_sq && init2b) {
-                    double fc_ij = Zj*c2.calc(rij);
-                    double fcp_ij = Zj*c2.calc_prime(rij);
-                    d2.calc_all(rij,rij_sq,fc_ij,fcp_ij,aed,fd_ij);
-                    // Two-body descriptor calculates x-direction only - fd_ij(n,0)
-                    // so we have to copy x-dir to y- and z-dir
-                    // and scale them by the unit directional vector delij/rij.
-                    for (size_t n=bias; n<size2b+bias; ++n) {
-                        fd_ij(n,0) *= 0.5*rij_inv;
-                        fd_ij(n,1) = fd_ij(n,0)*delij[1];
-                        fd_ij(n,2) = fd_ij(n,0)*delij[2];
-                        fd_ij(n,0) *= delij[0];
-                    }
-                }
-                // CALCULATE MANY-BODY TERM
-                if (rij_sq <= rcut_mb_sq && initmb) {
-                    double fc_ij = Zj*cm.calc(rij);
-                    double fcp_ij = Zj*cm.calc_prime(rij);
-                    rho_type& rhoi = st_d.get_rho(i);
-                    int mode = dm.calc_dXijdri(rij,rij_sq,delij,
-                            fc_ij,fcp_ij,rhoi,fd_ij);
-                    if (mode==0) {
-                        // some dm compute x-dir only, similarly to d2 above
-                        for (size_t n=size2b+bias; n<size2b+sizemb+bias; ++n) {
-                            fd_ij(n,0) *= rij_inv;
-                            fd_ij(n,1) = fd_ij(n,0);
-                            fd_ij(n,2) = fd_ij(n,0);
-                            fd_ij(n,0) *= delij[0];
-                            fd_ij(n,1) *= delij[1];
-                            fd_ij(n,2) *= delij[2];
-                        }
-                    }
-                }
-            }
-            else {
-                if (rij_sq <= rcut_2b_sq && init2b) {
-                    double fc_ij = Zj*c2.calc(rij);
-                    d2.calc_aed(rij,rij_sq,fc_ij,aed);
-                }
-            }
-
+  for (size_t i=0; i<st.natoms(); ++i) {
+    const Atom &a1 = st(i);
+    aed_type2 &aed = st_d.get_aed(i);
+
+    for (size_t jj=0; jj<st.nn_size(i); ++jj) {
+      const Vec3d &a2pos = st.nn_pos(i,jj);
+
+      delij[0] = a1.position[0] - a2pos[0];
+      delij[1] = a1.position[1] - a2pos[1];
+      delij[2] = a1.position[2] - a2pos[2];
+
+      double rij_sq = delij[0]*delij[0] + delij[1]*delij[1] + delij[2]*delij[2];
+
+      if (rij_sq > rcut_max_sq) continue;
+      int Zj = st.near_neigh_atoms[i][jj].Z;
+      double wj = weights[Zj];
+      double rij = sqrt(rij_sq);
+      double rij_inv = 1.0/rij;
+
+      // CALCULATE TWO-BODY TERM
+      if (use_force || use_stress) {
+        fd_type &fd_ij = st_d.fd[i][jj];
+        if (rij_sq <= rcut_2b_sq && init2b) {
+          double fc_ij = wj*c2.calc(rij);
+          double fcp_ij = wj*c2.calc_prime(rij);
+          d2.calc_all(rij,rij_sq,fc_ij,fcp_ij,aed,fd_ij);
+          // Two-body descriptor calculates x-direction only - fd_ij(n,0)
+          // so we have to copy x-dir to y- and z-dir
+          // and scale them by the unit directional vector delij/rij.
+          for (size_t n=bias; n<size2b+bias; ++n) {
+            fd_ij(n,0) *= 0.5*rij_inv;
+            fd_ij(n,1) = fd_ij(n,0)*delij[1];
+            fd_ij(n,2) = fd_ij(n,0)*delij[2];
+            fd_ij(n,0) *= delij[0];
+          }
         }
-    }
-    if (init2b) {
-        for (size_t n=0; n<st.natoms(); ++n) {
-            for(size_t s=bias; s<bias+d2.size(); ++s) {
-                st_d.get_aed(n)(s) *= 0.5;
+        // CALCULATE MANY-BODY TERM
+        if (rij_sq <= rcut_mb_sq && initmb) {
+          double fc_ij = wj*cm.calc(rij);
+          double fcp_ij = wj*cm.calc_prime(rij);
+          rho_type& rhoi = st_d.get_rho(i);
+          int mode = dm.calc_dXijdri(rij,rij_sq,delij,
+              fc_ij,fcp_ij,rhoi,fd_ij);
+          if (mode==0) {
+            // some dm compute x-dir only, similarly to d2 above
+            for (size_t n=size2b+bias; n<size2b+sizemb+bias; ++n) {
+              fd_ij(n,0) *= rij_inv;
+              fd_ij(n,1) = fd_ij(n,0);
+              fd_ij(n,2) = fd_ij(n,0);
+              fd_ij(n,0) *= delij[0];
+              fd_ij(n,1) *= delij[1];
+              fd_ij(n,2) *= delij[2];
             }
+          }
+        }
+      }
+      else {
+        if (rij_sq <= rcut_2b_sq && init2b) {
+          double fc_ij = wj*c2.calc(rij);
+          d2.calc_aed(rij,rij_sq,fc_ij,aed);
         }
+      }
+
+    }
+  }
+  if (init2b) {
+    for (size_t n=0; n<st.natoms(); ++n) {
+      for(size_t s=bias; s<bias+d2.size(); ++s) {
+        st_d.get_aed(n)(s) *= 0.5;
+      }
     }
+  }
 }
 template <typename D2, typename D3, typename DM, typename C2, typename C3, typename CM>
 void DescriptorsCalc<D2,D3,DM,C2,C3,CM>::calc_dimer(const Structure &st, StDescriptors &st_d) {
 
-    double r_b = config.get<double>("DIMER",1);
-    bool bond_bool = config.get<bool>("DIMER",2);
-
-    // Here we assume that training data consists of 4 atoms
-    // The cutoff distance specified in the config file works in the usual way
-    // i.e. the maximum distance between two atoms 
-    // I,J are molecules, i1,i2 atom in I, similarly for J
-    //    I- - - - - - - - - - - -J
-    // i1---i2- - - - - - - - -j1---j2
-    //    |- - - -r_com- - - - - -|   
-    //  |- - - - -r_max- - - - - - -|
-
-    bool init2b = config.get<bool>("INIT2B");
-    bool initmb = config.get<bool>("INITMB");
-    size_t size2b=0;
-    size_t sizemb=0;
-    if (init2b) size2b=config.get<size_t>("SIZE2B");
-    if (initmb) sizemb=config.get<size_t>("SIZEMB");
-
-    // deal with the case initmb is set to true and dummy is used
-    init2b = init2b && size2b;
-    initmb = initmb && sizemb;
-
-    //double rcut_max_sq = pow(config.get<double>("RCUTMAX"),2);
-    double rcut_2b_sq = 0.0;
-    double rcut_mb_sq = 0.0;
-
-    if (init2b) rcut_2b_sq = pow(config.get<double>("RCUT2B"),2);
-    if (initmb) rcut_mb_sq = pow(config.get<double>("RCUTMB"),2);
-
-    // Max distance between CoM of two interacting molecules
-    double rcut_com_sq = pow(config.get<double>("RCUTMAX")-r_b,2);
-
-    // Not that this differ to how lammps implements this
-    // TODO make it consistent between those two
-    Mat6R3C delM;  //i1-j1,i2-j1,i1-j2,i2-j2,i1-i2,j1-j2
-    double r_sq[6];
-    double r[6];
-
-    const std::vector<Atom> &atoms = st.atoms;
-
-    size_t bias=0;
-    if (config.get<bool>("BIAS"))
-        bias++;
-
-    // TODO weighting factors
-    // For now assume all are the same type
-    //int Zj = st.near_neigh_atoms[0][0].Z;
-    int Zj = 1;
-
-    // map of atom label and distances
-    // idx[0] - distance label between 0 and 2 atom
-    std::vector<std::pair<int,int>> idx(6);
-    idx[0] = std::make_pair(0,2);
-    idx[1] = std::make_pair(1,2);
-    idx[2] = std::make_pair(0,3);
-    idx[3] = std::make_pair(1,3);
-    idx[4] = std::make_pair(0,1);
-    idx[5] = std::make_pair(2,3);
-
-    // zero all aeds+rho and set bias
-    for (size_t i=0; i<4; ++i) {
-        aed_type2 &aed = st_d.get_aed(i);
-        aed.set_zero();
-        aed(0)=static_cast<double>(bias);   // set bias
-    }
-    if (initmb) {
-        size_t s = dm.rhoi_size()+dm.rhoip_size();
-        st_d.rhos.resize(s,4);
-        st_d.rhos.set_zero();
-    }
-
-    Vec3d xicom = 0.5*(atoms[0].position + atoms[1].position);
-    Vec3d xjcom = 0.5*(atoms[2].position + atoms[3].position);
-
-    Vec3d del_com = xicom-xjcom;;
-    double r_com_sq = del_com * del_com;
-    if (r_com_sq > rcut_com_sq) {
-        return;
-    }
-
-    // compute all 6 distances
-    for (size_t n=0; n<6; ++n) {
-        delM.row(n) = atoms[idx[n].first].position - atoms[idx[n].second].position;
-        r_sq[n] =  delM.row(n) * delM.row(n);
-        r[n] = sqrt(r_sq[n]);
+  double r_b = config.get<double>("DIMER",1);
+  bool bond_bool = config.get<bool>("DIMER",2);
+
+  // Here we assume that training data consists of 4 atoms
+  // The cutoff distance specified in the config file works in the usual way
+  // i.e. the maximum distance between two atoms 
+  // I,J are molecules, i1,i2 atom in I, similarly for J
+  //    I- - - - - - - - - - - -J
+  // i1---i2- - - - - - - - -j1---j2
+  //    |- - - -r_com- - - - - -|   
+  //  |- - - - -r_max- - - - - - -|
+
+  bool init2b = config.get<bool>("INIT2B");
+  bool initmb = config.get<bool>("INITMB");
+  size_t size2b=0;
+  size_t sizemb=0;
+  if (init2b) size2b=config.get<size_t>("SIZE2B");
+  if (initmb) sizemb=config.get<size_t>("SIZEMB");
+
+  // deal with the case initmb is set to true and dummy is used
+  init2b = init2b && size2b;
+  initmb = initmb && sizemb;
+
+  //double rcut_max_sq = pow(config.get<double>("RCUTMAX"),2);
+  double rcut_2b_sq = 0.0;
+  double rcut_mb_sq = 0.0;
+
+  if (init2b) rcut_2b_sq = pow(config.get<double>("RCUT2B"),2);
+  if (initmb) rcut_mb_sq = pow(config.get<double>("RCUTMB"),2);
+
+  // Max distance between CoM of two interacting molecules
+  double rcut_com_sq = pow(config.get<double>("RCUTMAX")-r_b,2);
+
+  // Not that this differ to how lammps implements this
+  // TODO make it consistent between those two
+  Mat6R3C delM;  //i1-j1,i2-j1,i1-j2,i2-j2,i1-i2,j1-j2
+  double r_sq[6];
+  double r[6];
+
+  const std::vector<Atom> &atoms = st.atoms;
+
+  size_t bias=0;
+  if (config.get<bool>("BIAS"))
+    bias++;
+
+  // TODO weighting factors
+  // For now assume all are the same type
+  //int Zj = st.near_neigh_atoms[0][0].Z;
+  int Zj = 1;
+
+  // map of atom label and distances
+  // idx[0] - distance label between 0 and 2 atom
+  std::vector<std::pair<int,int>> idx(6);
+  idx[0] = std::make_pair(0,2);
+  idx[1] = std::make_pair(1,2);
+  idx[2] = std::make_pair(0,3);
+  idx[3] = std::make_pair(1,3);
+  idx[4] = std::make_pair(0,1);
+  idx[5] = std::make_pair(2,3);
+
+  // zero all aeds+rho and set bias
+  for (size_t i=0; i<4; ++i) {
+    aed_type2 &aed = st_d.get_aed(i);
+    aed.set_zero();
+    aed(0)=static_cast<double>(bias);   // set bias
+  }
+  if (initmb) {
+    size_t s = dm.rhoi_size()+dm.rhoip_size();
+    st_d.rhos.resize(s,4);
+    st_d.rhos.set_zero();
+  }
+
+  Vec3d xicom = 0.5*(atoms[0].position + atoms[1].position);
+  Vec3d xjcom = 0.5*(atoms[2].position + atoms[3].position);
+
+  Vec3d del_com = xicom-xjcom;;
+  double r_com_sq = del_com * del_com;
+  if (r_com_sq > rcut_com_sq) {
+    return;
+  }
+
+  // compute all 6 distances
+  for (size_t n=0; n<6; ++n) {
+    delM.row(n) = atoms[idx[n].first].position - atoms[idx[n].second].position;
+    r_sq[n] =  delM.row(n) * delM.row(n);
+    r[n] = sqrt(r_sq[n]);
+  }
+
+  // compute densities and 2b aed for every atom
+  // if bond is not included use 0,1,2,3 distances,
+  // otherwise use all
+  size_t N = bond_bool ? 6 : 4;
+  for (size_t n=0; n<N; ++n) {
+    if (r_sq[n] <= rcut_mb_sq && initmb) {
+      double fcmb = Zj*cm.calc(r[n]);
+      dm.calc_rho(r[n],r_sq[n],fcmb,delM.row(n),st_d.get_rho(idx[n].first));
+      dm.calc_rho(r[n],r_sq[n],fcmb,-delM.row(n),st_d.get_rho(idx[n].second));
     }
-
-    // compute densities and 2b aed for every atom
-    // if bond is not included use 0,1,2,3 distances,
-    // otherwise use all
-    size_t N = bond_bool ? 6 : 4;
-    for (size_t n=0; n<N; ++n) {
-        if (r_sq[n] <= rcut_mb_sq && initmb) {
-            double fcmb = Zj*cm.calc(r[n]);
-            dm.calc_rho(r[n],r_sq[n],fcmb,delM.row(n),st_d.get_rho(idx[n].first));
-            dm.calc_rho(r[n],r_sq[n],fcmb,-delM.row(n),st_d.get_rho(idx[n].second));
-        }
-        // Do not compute 2b term between bonded atoms
-        if (r_sq[n] <= rcut_2b_sq && init2b) {
-            double fc2b = Zj*c2.calc(r[n]);
-            d2.calc_aed(r[n],r_sq[n],fc2b,st_d.get_aed(idx[n].first));
-            d2.calc_aed(r[n],r_sq[n],fc2b,st_d.get_aed(idx[n].second));
-        }
+    // Do not compute 2b term between bonded atoms
+    if (r_sq[n] <= rcut_2b_sq && init2b) {
+      double fc2b = Zj*c2.calc(r[n]);
+      d2.calc_aed(r[n],r_sq[n],fc2b,st_d.get_aed(idx[n].first));
+      d2.calc_aed(r[n],r_sq[n],fc2b,st_d.get_aed(idx[n].second));
     }
+  }
 
-    // calculate many-body aed
-    if (initmb) {
-        for (size_t i=0; i<4; ++i) {
-            aed_type2 &aed = st_d.get_aed(i);
-            rho_type& rhoi = st_d.get_rho(i);
-            dm.calc_aed(rhoi,aed);
-        }
+  // calculate many-body aed
+  if (initmb) {
+    for (size_t i=0; i<4; ++i) {
+      aed_type2 &aed = st_d.get_aed(i);
+      rho_type& rhoi = st_d.get_rho(i);
+      dm.calc_aed(rhoi,aed);
     }
-
-    //if (init2b) {
-    //    for (size_t n=0; n<st.natoms(); ++n) {
-    //        for(size_t s=bias; s<bias+d2.size(); ++s) {
-    //            aeds[n](s) *= 0.5;
-    //        }
-    //    }
-    //}
+  }
+
+  //if (init2b) {
+  //    for (size_t n=0; n<st.natoms(); ++n) {
+  //        for(size_t s=bias; s<bias+d2.size(); ++s) {
+  //            aeds[n](s) *= 0.5;
+  //        }
+  //    }
+  //}
 }
 
 template <typename D2, typename D3, typename DM, typename C2, typename C3, typename CM>
 StDescriptors DescriptorsCalc<D2,D3,DM,C2,C3,CM>::calc(const Structure &st) {
-    StDescriptors st_d(st, config);
-    if (config.get<bool>("DIMER",0))
-        calc_dimer(st,st_d);
-    else
-        calc(st,st_d);
-    return st_d;
+  StDescriptors st_d(st, config);
+  if (config.get<bool>("DIMER",0))
+    calc_dimer(st,st_d);
+  else
+    calc(st,st_d);
+  return st_d;
 }
 
 template <typename D2, typename D3, typename DM, typename C2, typename C3, typename CM>
 StDescriptorsDB DescriptorsCalc<D2,D3,DM,C2,C3,CM>::calc(const StructureDB &stdb) {
-    StDescriptorsDB st_desc_db(stdb, config);
+  StDescriptorsDB st_desc_db(stdb, config);
 
 #pragma omp parallel for
-    for(size_t i=0; i<stdb.size(); ++i)
+  for(size_t i=0; i<stdb.size(); ++i)
     if (config.get<bool>("DIMER",0))
-        calc_dimer(stdb(i),st_desc_db(i));
+      calc_dimer(stdb(i),st_desc_db(i));
     else
-        calc(stdb(i),st_desc_db(i));
-    return st_desc_db;
+      calc(stdb(i),st_desc_db(i));
+  return st_desc_db;
 }
 #endif
diff --git a/include/tadah/mlip/structure_db.h b/include/tadah/mlip/structure_db.h
index 833c3cb3a6087a9021696b8cc471ef4d6ed10521..a8c6b34afbc490a486029305700db7cb2f7eaba8 100644
--- a/include/tadah/mlip/structure_db.h
+++ b/include/tadah/mlip/structure_db.h
@@ -58,7 +58,7 @@ struct StructureDB {
      *      Required Config key: \ref DBFILE
      *
      */
-    void add(const Config &config);
+    void add(Config &config);
 
     /** Add all structures from a file */
     void add(const std::string fn);
@@ -137,6 +137,12 @@ struct StructureDB {
     /** Return unique elements for all Structures. */
     std::set<Element> get_unique_elements() const;
 
+    /** Find unique elements in provided Config file */
+    static std::set<Element> find_unique_elements(const Config &c);
+
+    /** Find unique elements in provided file */
+    static std::set<Element> find_unique_elements(const std::string &fn);
+
     /** Count number of structures and atoms in all datasets from the Config file. */
     static std::pair<int,int> count(const Config &c);
 
@@ -146,9 +152,9 @@ struct StructureDB {
     void clear_nn();
 
     /** Check consistency of the ATOMS key. */
-    void check_atoms_key(Config &config) const;
+    static void check_atoms_key(Config &config, std::set<Element> &unique_elements);
 
     /** Check consistency of the WATOMS key. Add if missing*/
-    void check_watoms_key(Config &config) const;
+    static void check_watoms_key(Config &config, std::set<Element> &unique_elements);
 };
 #endif
diff --git a/src/structure_db.cpp b/src/structure_db.cpp
index cdaf502a3ee9fe4b91e45d745357c889cc618e49..9f31fb60bcee4420295d982b5cfc9ad02124ce8f 100644
--- a/src/structure_db.cpp
+++ b/src/structure_db.cpp
@@ -1,10 +1,10 @@
 #include <tadah/mlip/structure_db.h>
+#include <tadah/core/periodic_table.h>
+#include <cstdio>
 
 StructureDB::StructureDB() {}
 StructureDB::StructureDB(Config &config) {
   add(config);
-  check_atoms_key(config);
-  check_watoms_key(config);
 }
 
 void StructureDB::add(const std::string fn) {
@@ -59,7 +59,7 @@ void StructureDB::remove(size_t i) {
   structures.erase(structures.begin()+i);
 }
 
-void StructureDB::add(const Config &config) {
+void StructureDB::add(Config &config) {
   for (const std::string &s : config("DBFILE")) {
     dbidx.push_back(size());
     add(s);
@@ -106,6 +106,50 @@ std::set<Element> StructureDB::get_unique_elements() const {
       st.unique_elements.begin(),st.unique_elements.end());
   return s;
 }
+std::set<Element> StructureDB::find_unique_elements(const std::string &fn) {
+  std::set<Element> s;
+  std::ifstream datafile(fn);
+  std::string line;
+
+  if (!datafile.is_open()) {
+    std::cerr << "Could not open the file." << std::endl;
+  }
+
+  char symbol[3];
+
+  // move to first non empty line
+  while (std::getline(datafile, line)) {
+    if (!line.empty()) {
+      break;
+    }
+  }
+  // skip 
+  for (int i = 0; i < 7; ++i) {
+    std::getline(datafile, line);
+  }
+  while (std::getline(datafile, line)) {
+    if(line.empty()) {
+      for (int i = 0; i < 8; ++i) {
+        if (!std::getline(datafile, line)) {
+          // Handle the case where there are not enough lines
+          break; // or handle the error
+        }
+      }
+      continue;
+    }
+    sscanf(line.c_str(), "%2s", symbol);
+    s.insert(PeriodicTable::find_by_symbol(symbol));
+  }
+  return s;
+}
+std::set<Element> StructureDB::find_unique_elements(const Config &c) {
+  std::set<Element> s;
+  for (const auto& fn: c("DBFILE")) {
+    std::set<Element> temp = find_unique_elements(fn);
+    s.insert(temp.begin(), temp.end());
+  }
+  return s;
+}
 
 template <typename T,typename U>                                                   
 std::pair<T,U> operator+(const std::pair<T,U>  &l,const std::pair<T,U>  &r) {   
@@ -140,8 +184,7 @@ std::pair<int,int> StructureDB::count(const std::string fn){
 void StructureDB::clear_nn() {
   for (auto &struc: structures) struc.clear_nn();
 }
-void StructureDB::check_atoms_key(Config &config) const {
-  std::set<Element> unique_elements = get_unique_elements();
+void StructureDB::check_atoms_key(Config &config, std::set<Element> &unique_elements) {
   bool error=false;
 
   if (config.exist("ATOMS")) {
@@ -153,7 +196,7 @@ void StructureDB::check_atoms_key(Config &config) const {
     auto set_it = unique_elements.begin();
     auto atoms_it = config("ATOMS").begin();
     while (set_it != unique_elements.end() && atoms_it != config("ATOMS").end()) {
-      if (set_it->symbol != *atoms_it)  {
+      if (set_it->symbol != (*atoms_it))  {
         error=true;
       }
       ++set_it;
@@ -170,8 +213,7 @@ void StructureDB::check_atoms_key(Config &config) const {
     for (const auto &s : unique_elements) config.add("ATOMS", s.symbol);
   }
 }
-void StructureDB::check_watoms_key(Config &config) const {
-  std::set<Element> unique_elements = get_unique_elements();
+void StructureDB::check_watoms_key(Config &config, std::set<Element> &unique_elements) {
   bool error=false;
 
   if (config.exist("WATOMS")) {