diff --git a/cutoffs/cut_all.h b/cutoffs/cut_all.h
new file mode 100644
index 0000000000000000000000000000000000000000..476c1f830e459c392ff55db9482cb0402855842b
--- /dev/null
+++ b/cutoffs/cut_all.h
@@ -0,0 +1 @@
+#include "cutoffs.h"
diff --git a/cutoffs/cutoffs.cpp b/cutoffs/cutoffs.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3fbd2613fdf13af5537bb069aac374accf53b155
--- /dev/null
+++ b/cutoffs/cutoffs.cpp
@@ -0,0 +1,166 @@
+#include "cutoffs.h"
+#include <cmath>
+#include <stdexcept>
+
+
+Registry<Cut_Base,double>::Register<Cut_Cos> Cut_Cos_1( "Cut_Cos" );
+Registry<Cut_Base,double>::Register<Cut_Tanh> Cut_Tanh_1( "Cut_Tanh" );
+Registry<Cut_Base,double>::Register<Cut_Poly2> Cut_Poly_1( "Cut_Poly2" );
+Registry<Cut_Base,double>::Register<Cut_Dummy> Cut_Dummy_1( "Cut_Dummy" );
+
+
+Cut_Base::~Cut_Base() {}
+void Cut_Base::test_rcut(const double r)
+{
+    if (r<0.0)
+        throw std::runtime_error("Cutoff distance cannot be negative.");
+}
+
+Cut_Cos::Cut_Cos() {}
+Cut_Cos::Cut_Cos(double rcut)
+{
+    set_rcut(rcut);
+    test_rcut(rcut);
+}
+
+std::string Cut_Cos::label() {
+    return lab;
+}
+
+void Cut_Cos::set_rcut(const double r) {
+    test_rcut(r);
+    rcut=r;
+    rcut_sq=r*r;
+    rcut_inv= r<=0 ? 0.0 : 1.0/r;
+}
+double Cut_Cos::get_rcut() {
+    return rcut;
+}
+
+double Cut_Cos::get_rcut_sq() {
+    return rcut_sq;
+}
+
+double Cut_Cos::calc(double r) {
+    if (r>=rcut) return 0.0;
+    return 0.5 * (std::cos(M_PI*r*rcut_inv) + 1.0);
+}
+double Cut_Cos::calc_prime(double r) {
+    if (r>=rcut) return 0.0;
+    double t = M_PI*rcut_inv;
+    return -0.5 * t*sin(t*r);
+
+}
+
+Cut_Dummy::Cut_Dummy() {}
+Cut_Dummy::Cut_Dummy(double rcut)
+{
+    set_rcut(rcut),
+    test_rcut(rcut);
+}
+
+std::string Cut_Dummy::label() {
+    return lab;
+}
+
+void Cut_Dummy::set_rcut(const double r) {
+    test_rcut(r);
+    rcut=r;
+    rcut_sq=r*r;
+    rcut_inv= r<=0 ? 0.0 : 1.0/r;
+}
+double Cut_Dummy::get_rcut() {
+    return rcut;
+}
+
+double Cut_Dummy::get_rcut_sq() {
+    return rcut_sq;
+}
+
+double Cut_Dummy::calc(double r) {
+    if (r>=rcut) return 0.0;
+    return 1.0;
+}
+double Cut_Dummy::calc_prime(double) {
+    return 0.0;
+}
+
+Cut_Tanh::Cut_Tanh() {}
+Cut_Tanh::Cut_Tanh(double rcut)
+{
+    set_rcut(rcut);
+    test_rcut(rcut);
+}
+
+std::string Cut_Tanh::label() {
+    return lab;
+}
+
+void Cut_Tanh::set_rcut(const double r) {
+    test_rcut(r);
+    rcut=r;
+    rcut_sq=r*r;
+    rcut_inv= r<=0 ? 0.0 : 1.0/r;
+}
+double Cut_Tanh::get_rcut() {
+    return rcut;
+}
+
+double Cut_Tanh::get_rcut_sq() {
+    return rcut_sq;
+}
+
+double Cut_Tanh::calc(double r) {
+    if (r>=rcut) return 0.0;
+    double t = std::tanh(1.0 - r*rcut_inv);
+    return t*t*t;
+}
+double Cut_Tanh::calc_prime(double r) {
+    if (r>=rcut) return 0.0;
+    double a = (1.0-r*rcut_inv);
+    double t = std::tanh(a);
+    double s = 1.0/(std::cosh(a));
+    return -3.0*t*t*rcut_inv*s*s;
+}
+
+Cut_Poly2::Cut_Poly2() {}
+Cut_Poly2::Cut_Poly2(double rcut)
+{
+    if (rcut<=1.0)
+        throw std::runtime_error(
+                "Cut_Poly2 cutoff distance cannot be smaller than 1.0.");
+    set_rcut(rcut);
+    test_rcut(rcut);
+}
+
+std::string Cut_Poly2::label() {
+    return lab;
+}
+
+void Cut_Poly2::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_Poly2::get_rcut() {
+    return rcut;
+}
+
+double Cut_Poly2::get_rcut_sq() {
+    return rcut_sq;
+}
+
+double Cut_Poly2::calc(double r) {
+    if (r>=rcut) return 0.0;
+    else if (r<= rcut_inner) return 1.0;
+    double rs=r-rcut_inner;
+    return rs*rs*rs*(rs*(15.0-6.0*rs)-10.0)+1;
+}
+double Cut_Poly2::calc_prime(double r) {
+    if (r>=rcut) return 0.0;
+    else if (r<= rcut_inner) return 0.0;
+    double rs=r-rcut_inner;
+    return -30.0*(rs-1.0)*(rs-1.0)*rs*rs;
+}
diff --git a/cutoffs/cutoffs.h b/cutoffs/cutoffs.h
new file mode 100644
index 0000000000000000000000000000000000000000..98c5622dd1374e6348c2cecdc29ace547c36a2c0
--- /dev/null
+++ b/cutoffs/cutoffs.h
@@ -0,0 +1,143 @@
+#ifndef CUTOFFS_H
+#define CUTOFFS_H
+
+#include <string>
+#include "../../CORE/config/config.h"
+#include "../../CORE/registry.h"
+
+/** Base class to be inherited by all cutoffs */
+class Cut_Base {
+    public:
+        virtual ~Cut_Base();
+        virtual std::string label()=0;
+        virtual double calc(double r)=0;
+        virtual double get_rcut()=0;
+        virtual void set_rcut(const double r)=0;
+        virtual double get_rcut_sq()=0;
+        virtual double calc_prime(double r)=0;
+        void test_rcut(const double r);
+};
+
+/** \brief Dummy cutoff function
+ * \f[
+ * f_c(r) =
+ * \begin{equation}
+ * \begin{cases}
+ *  1 & \text{if  } r \leq r_c\\
+ *  0 & \text{otherwise}\\
+ * \end{cases}
+ * \end{equation}
+ * \f]
+ */
+class Cut_Dummy : public Cut_Base {
+    private:
+        std::string lab = "Cut_Dummy";
+        double rcut, rcut_sq, rcut_inv;
+    public:
+        Cut_Dummy();
+        Cut_Dummy(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);
+};
+
+/** \brief Cos cutoff function
+ *
+ * \f[
+ * f_c(r) =
+ * \begin{equation}
+ * \begin{cases}
+ *  \frac{1}{2}\big[ \cos\big( \frac{\pi r}{r_c} \big)+1 \big] & \text{if  } r \leq r_c\\
+ *  0 & \text{otherwise}\\
+ * \end{cases}
+ * \end{equation}
+ * \f]
+ *
+ * <div class="csl-entry">Behler, J., Parrinello, M. (2007).
+ * Generalized neural-network representation of high-dimensional
+ * potential-energy surfaces. <i>Physical Review Letters</i>,
+ * <i>98</i>(14), 146401. https://doi.org/10.1103/PhysRevLett.98.146401</div>
+ */
+class Cut_Cos : public Cut_Base {
+    private:
+        std::string lab = "Cut_Cos";
+        double rcut, rcut_sq, rcut_inv;
+    public:
+        Cut_Cos();
+        Cut_Cos(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);
+};
+
+/** \brief Tanh cutoff function.
+ * \f[
+ * f_c(r) =
+ * \begin{equation}
+ *  \begin{cases}
+ *   \tanh^3\big( 1 -\frac{r}{r_c} \big)  & \text{if  } r \leq r_c\\
+ *   0 & \text{otherwise}\\
+ *  \end{cases}
+ * \end{equation}
+ * \f]
+ *
+ * <div class="csl-entry">Behler, J. (2011). Atom-centered symmetry
+ * functions for constructing high-dimensional neural network potentials.
+ * <i>J. Chem. Phys.</i>, <i>134</i>(7), 074106.
+ * https://doi.org/10.1063/1.3553717</div>
+ */
+class Cut_Tanh : public Cut_Base {
+    private:
+        std::string lab = "Cut_Tanh";
+        double rcut, rcut_sq, rcut_inv;
+    public:
+        Cut_Tanh();
+        Cut_Tanh(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);
+};
+
+/** \brief Polynomial-2 cutoff function.
+ *
+ * \f[
+ * f_c(r) =
+ * \begin{equation}
+ *  \begin{cases}
+ *   1 & \text{if  } r \leq (r_c-1)\\
+ *   r^3(r(15-6r)-10)+1  & \text{if  } (r_c-1) < r \leq r_c\\
+ *   0 & \text{otherwise}\\
+ *  \end{cases}
+ * \end{equation}
+ * \f]
+
+ *<div class="csl-entry">Singraber, A., Rg Behler, J., Dellago, C. (2019).
+ Library-Based LAMMPS Implementation of High-Dimensional
+ Neural Network Potentials. https://doi.org/10.1021/acs.jctc.8b00770</div>
+ */
+class Cut_Poly2 : public Cut_Base {
+    private:
+        std::string lab = "Cut_Poly2";
+        double rcut, rcut_sq, rcut_inv;
+        double rcut_inner;
+    public:
+        Cut_Poly2();
+        Cut_Poly2(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/dc_selector.h b/dc_selector.h
new file mode 100644
index 0000000000000000000000000000000000000000..a51cf8e3348059975b00911fcdc01fbdd17b6c3a
--- /dev/null
+++ b/dc_selector.h
@@ -0,0 +1,116 @@
+#ifndef DC_SELECTOR_H
+#define DC_SELECTOR_H
+
+#include "../CORE/config/config.h"
+#include "descriptors/d_all.h"
+#include "cutoffs/cut_all.h"
+#include "../CORE/registry.h"
+
+class DC_Selector {
+    public:
+        Cut_Base *c2b=nullptr;
+        Cut_Base *c3b=nullptr;
+        Cut_Base *cmb=nullptr;
+        D2_Base *d2b=nullptr;
+        D3_Base *d3b=nullptr;
+        DM_Base *dmb=nullptr;
+
+        Config config;
+
+        DC_Selector ()
+        {};
+
+        // Copy constructor
+        DC_Selector(const 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;
+        }
+        // Assignment operator
+        DC_Selector& operator=(DC_Selector& dc)
+        {
+            std::swap(config,dc.config);
+            std::swap(c2b,dc.c2b);
+            std::swap(c3b,dc.c3b);
+            std::swap(cmb,dc.cmb);
+            std::swap(d2b,dc.d2b);
+            std::swap(d3b,dc.d3b);
+            std::swap(dmb,dc.dmb);
+            return *this;
+        }
+
+        DC_Selector (const Config &c):
+            config(c)
+    {
+        init();
+    };
+        void init() {
+            double rcutzero = 0.0; // for dummies
+
+            size_t bias=0;
+            if (config.get<bool>("BIAS"))
+                bias++;
+
+            if (config.get<bool>("INIT2B")) {
+                double rcut2b = config.get<double>("RCUT2B");
+                c2b = factory<Cut_Base,double>( config.get<std::string>("RCTYPE2B"), rcut2b );
+                d2b = factory<D2_Base,Config&>( config.get<std::string>("TYPE2B"), config );
+                d2b->fidx = bias;
+            }
+            else {
+                c2b = factory<Cut_Base,double>( "Cut_Dummy", rcutzero );
+                d2b = factory<D2_Base,Config&>( "D2_Dummy", config );
+                d2b->fidx = bias;
+            }
+
+            if (config.get<bool>("INIT3B")) {
+                double rcut3b = config.get<double>("RCUT3B");
+                c3b = factory<Cut_Base,double>( config.get<std::string>("RCTYPE3B"), rcut3b );
+                d3b = factory<D3_Base,Config&>( config.get<std::string>("TYPE3B"), config );
+                // d3b->fidx = bias;
+            }
+            else {
+                c3b = factory<Cut_Base,double>( "Cut_Dummy", rcutzero );
+                d3b = factory<D3_Base,Config&>( "D3_Dummy", config );
+                // d3b->fidx = bias;
+            }
+
+            if (config.get<bool>("INITMB")) {
+                double rcutmb = config.get<double>("RCUTMB");
+                cmb = factory<Cut_Base,double>( config.get<std::string>("RCTYPEMB"), rcutmb );
+                dmb = factory<DM_Base,Config&>( config.get<std::string>("TYPEMB"), config );
+                dmb->fidx = bias + d2b->size();
+            }
+            else {
+                cmb = factory<Cut_Base,double>( "Cut_Dummy", rcutzero );
+                dmb = factory<DM_Base,Config&>( "DM_Dummy", config );
+                dmb->fidx = bias + d2b->size();
+            }
+        }
+        ~DC_Selector()
+        {
+            if (d2b)
+                delete d2b;
+            if (c2b)
+                delete c2b;
+            if (c3b)
+                delete c3b;
+            if (d3b)
+                delete d3b;
+            if (cmb)
+                delete cmb;
+            if (dmb)
+                delete dmb;
+        }
+};
+#endif
diff --git a/descriptors/d2/d2_all.h b/descriptors/d2/d2_all.h
new file mode 100644
index 0000000000000000000000000000000000000000..684650372ef5ea238567731784c9a4aa317a386a
--- /dev/null
+++ b/descriptors/d2/d2_all.h
@@ -0,0 +1,6 @@
+#include "d2_base.h"
+#include "d2_dummy.h"
+#include "d2_blip.h"
+#include "d2_bp.h"
+#include "d2_eam.h"
+#include "d2_lj.h"
diff --git a/descriptors/d2/d2_base.cpp b/descriptors/d2/d2_base.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..017b0ce5223745e7e82d95a1066e7fdb14fe2650
--- /dev/null
+++ b/descriptors/d2/d2_base.cpp
@@ -0,0 +1,14 @@
+#include "d2_base.h"
+//template struct Registry<D2_Base, Config &>;
+void D2_Base::calc_fd_approx(
+        const double ,
+        const double ,
+        const double ,
+        aed_rtype aedp,
+        aed_rtype aedn,
+        fd_type &fd_ij,
+        const double h) {
+
+    for (size_t i=0; i<(*this).size(); ++i)
+        fd_ij(i,0) = (aedp(i)-aedn(i))/(2*h);
+}
diff --git a/descriptors/d2/d2_base.h b/descriptors/d2/d2_base.h
new file mode 100644
index 0000000000000000000000000000000000000000..6ff85dba928f04fd656c1ba2c5b440eb1d3a6784
--- /dev/null
+++ b/descriptors/d2/d2_base.h
@@ -0,0 +1,68 @@
+#ifndef D2_BASE_H
+#define D2_BASE_H
+
+#include "../d_base.h"
+
+/** \brief Base class for all two-body type descriptors.
+ *
+ * All two-body descriptors **must** inherit this class.
+ */
+class D2_Base: public D_Base {
+    public:
+        size_t fidx=0;    // first index in aed and fd arrays for this descriptor
+        virtual ~D2_Base() {};
+
+        /** \brief Calculate \ref AED
+         *
+         * Calculate Atomic Energy Descriptor for the atom local environment.
+         */
+        virtual void calc_aed(
+                const double rij,
+                const double rij_sq,
+                const double fc_ij,
+                aed_rtype aed)=0;
+
+        /** \brief Calculate \ref FD
+         *
+         * Calculate Force Descriptor for the atom local environment.
+         */
+        virtual void calc_dXijdri(
+                const double rij,
+                const double rij_sq,
+                const double fc_ij,
+                const double fcp_ij,
+                fd_type &fd_ij)=0;
+
+        /** \brief Calculate approximation to \ref FD
+         *
+         * Calculate Force Descriptor for the atom local environment
+         * using central difference approximation.
+         *
+         * h -> something small e.g. 1e-8
+         * fcijp -> cutoff(rij+h,...)
+         * fcijn -> cutoff(rij-h,...)
+         * aedp -> calc_aed(rij+h,...)
+         * aedn -> calc_aed(rij-h,...)
+         * fd_ij - descriptor derivative
+         */
+        virtual void calc_fd_approx(
+        const double rij,
+        const double fcijp,
+        const double fcijn,
+        aed_rtype aedp,
+        aed_rtype aedn,
+        fd_type &fd_ij,
+        const double h);
+
+        /** \brief Calculate \ref AED + \ref FD */
+        virtual void calc_all(
+                const double rij,
+                const double rij_sq,
+                const double fc_ij,
+                const double fcp_ij,
+                aed_rtype aed,
+                fd_type &fd_ij)=0;
+        virtual std::string label()=0;
+};
+template<> inline Registry<D2_Base,Config&>::Map Registry<D2_Base,Config&>::registry{};
+#endif
diff --git a/descriptors/d2/d2_blip.cpp b/descriptors/d2/d2_blip.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..35640daf64e59f191ca18fb904691791147ef75d
--- /dev/null
+++ b/descriptors/d2/d2_blip.cpp
@@ -0,0 +1,82 @@
+#include "d2_blip.h"
+#include "../d_basis_functions.h"
+#include <stdexcept>
+
+Registry<D2_Base,Config&>::Register<D2_Blip> D2_Blip_1( "D2_Blip" );
+
+D2_Blip::D2_Blip(Config &c):
+    verbose(c.get<int>("VERBOSE"))
+{
+
+    if (!c.get<bool>("INIT2B")) return;
+
+    get_grid(c,"CGRID2B",mius);
+    get_grid(c,"SGRID2B",etas);
+
+    if (verbose) {
+        std::cout << std::endl;
+        std::cout << "SGRID2B: " << etas.size() << std::endl;
+        for (auto e:etas) std::cout << e << "  ";
+
+        std::cout << std::endl;
+        std::cout << "CGRID2B: " << mius.size() << std::endl;
+        for (auto m:mius) std::cout << m << "  ";
+        std::cout << std::endl;
+    }
+
+    if (mius.size()!=etas.size()) {
+        throw std::runtime_error("SGRID2B and CGRID2B arrays differ in size.\n");
+    }
+
+    s=mius.size();
+}
+
+void D2_Blip::calc_aed(
+        const double rij,
+        const double ,
+        const double fc_ij,
+        aed_rtype aed)
+{
+
+    size_t i=fidx;
+    for (size_t c=0; c<mius.size(); c++) {
+        double t = B(etas[c]*(rij-mius[c]),fc_ij);
+        aed(i++) += t;
+    }
+
+}
+void D2_Blip::calc_dXijdri(
+        const double rij,
+        const double ,
+        const double fc_ij,
+        const double fcp_ij,
+        fd_type &fd_ij)
+{
+    size_t i=fidx;
+    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);
+    }
+}
+void D2_Blip::calc_all(
+        const double rij,
+        const double ,
+        const double fc_ij,
+        const double fcp_ij,
+        aed_rtype aed,
+        fd_type &fd_ij)
+{
+
+    size_t i=fidx;
+    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);
+        ++i;
+    }
+}
+
+size_t D2_Blip::size() {
+    return s;
+}
+std::string D2_Blip::label() {
+    return lab;
+}
diff --git a/descriptors/d2/d2_blip.h b/descriptors/d2/d2_blip.h
new file mode 100644
index 0000000000000000000000000000000000000000..beb7d762af4d58c3924fb8932cc4443e3eb763be
--- /dev/null
+++ b/descriptors/d2/d2_blip.h
@@ -0,0 +1,77 @@
+#ifndef D2_BLIP_H
+#define D2_BLIP_H
+#include "d2_base.h"
+#include <vector>
+
+/** \brief Blip two-body descriptor.
+ *
+ * \f[
+ * V_i^{\eta,r_s} =\sum_{j \neq i} B(\eta(r_{ij}-r_s))f_c(r_{ij})
+ * \f]
+ *
+ * where \f$ f_c \f$ is a cutoff function and \f$ B \f$ is a blip
+ * basis function centered at \f$r_s\f$ of width \f$4/\eta\f$.
+ *
+ * \ref CGRID2B parameters control position \f$ r_s \f$ of blip centres.
+ *
+ * \ref SGRID2B parameters control width \f$ \eta \f$ of blips.
+ *
+ * Blip basis function is built out of 3rd degree polynomials
+ * in the four intervals [-2,-1], [-1,0], [0,1], [1,2] and is defined as:
+ *
+ * \f[
+	\begin{equation}
+	  B(r) =
+		\begin{cases}
+		  1-\frac{3}{2}r^2+\frac{3}{4}|r|^3 & \text{if} \qquad 0<|r|<1\\
+		  \frac{1}{4}(2-|r|)^3 & \text{if} \qquad 1<|r|<2\\
+		  0 & \text{if} \qquad |r|>2
+		\end{cases}
+	\end{equation}
+ * \f]
+ *
+ * More details about the blip basis functions can be found in the following paper:
+ *
+ * <div class="csl-entry">Hernández, E., Gillan, M., Goringe, C.
+ * (1997). Basis functions for linear-scaling first-principles calculations.
+ * <i>Physical Review B - Condensed Matter and Materials Physics</i>,
+ * <i>55</i>(20), 13485–13493. https://doi.org/10.1103/PhysRevB.55.13485</div>
+ *
+ * Required keys:
+ * \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;
+
+    public:
+        D2_Blip(Config &config);
+        void calc_aed(
+                const double rij,
+                const double rij_sq,
+                const double fc_ij,
+                aed_rtype 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_rtype aed,
+                fd_type &fd_ij);
+        size_t size();
+        std::string label();
+};
+#endif
diff --git a/descriptors/d2/d2_bp.cpp b/descriptors/d2/d2_bp.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..abdce9df95010d09363ad5bfb2b434a8d713745c
--- /dev/null
+++ b/descriptors/d2/d2_bp.cpp
@@ -0,0 +1,79 @@
+#include "d2_bp.h"
+#include "../d_basis_functions.h"
+
+Registry<D2_Base,Config&>::Register<D2_BP> D2_BP_1( "D2_BP" );
+
+D2_BP::D2_BP(Config &c):
+    verbose(c.get<int>("VERBOSE"))
+{
+
+    if (!c.get<bool>("INIT2B")) return;
+
+    get_grid(c,"CGRID2B",mius);
+    get_grid(c,"SGRID2B",etas);
+
+    if (verbose) {
+        std::cout << std::endl;
+        std::cout << "SGRID2B: " << etas.size() << std::endl;
+        for (auto e:etas) std::cout << e << "  ";
+        std::cout << std::endl;
+
+        std::cout << "CGRID2B: " << mius.size() << std::endl;
+        for (auto m:mius) std::cout << m << "  ";
+        std::cout << std::endl;
+    }
+
+    if (mius.size()!=etas.size()) {
+        throw std::runtime_error("SGRID2B and CGRID2B arrays differ in size.\n");
+    }
+
+    s=mius.size();
+}
+
+void D2_BP::calc_aed(
+        const double rij,
+        const double ,
+        const double fc_ij,
+        aed_rtype aed)
+{
+
+    size_t i=fidx;
+    for (size_t c=0; c<mius.size(); c++) {         
+        aed(i++) += G(rij,etas[c],mius[c],fc_ij);
+    }
+
+}
+void D2_BP::calc_dXijdri(
+        const double rij,
+        const double ,
+        const double fc_ij,
+        const double fcp_ij,
+        fd_type &fd_ij)
+{
+    size_t i=fidx;
+    for (size_t c=0; c<mius.size(); c++) {         
+        fd_ij(i++,0) = dG(rij,etas[c],mius[c],fc_ij,fcp_ij);
+    }
+}
+void D2_BP::calc_all(
+        const double rij,
+        const double ,
+        const double fc_ij,
+        const double fcp_ij,
+        aed_rtype aed,
+        fd_type &fd_ij)
+{
+
+    size_t i=fidx;
+    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);
+        ++i;
+    }
+}
+size_t D2_BP::size() {
+    return s;
+}
+std::string D2_BP::label() {
+    return lab;
+}
diff --git a/descriptors/d2/d2_bp.h b/descriptors/d2/d2_bp.h
new file mode 100644
index 0000000000000000000000000000000000000000..d73e87371d518ef8747a97ba056ecbb5757d0a3e
--- /dev/null
+++ b/descriptors/d2/d2_bp.h
@@ -0,0 +1,58 @@
+#ifndef D2_BP_H
+#define D2_BP_H
+#include "d2_base.h"
+#include <vector>
+
+/** \brief Behler-Parrinello two-body descriptor.
+ *
+ * \f[
+ * V_i^{\eta,r_s} = \sum_{j \neq i} \exp{\Big(-\eta(r_{ij}-r_s)^2\Big)}f_c(r_{ij})
+ * \f]
+ *
+ * \ref CGRID2B parameters control position \f$ r_s \f$ of the gaussian basis function.
+ *
+ * \ref SGRID2B parameters control width \f$ \eta \f$ of the gaussian basis function.
+ *
+ * This is essentially a \f$ G^1_i \f$ descriptor from the below paper with
+ * an exception that it can use any cutoff function defined in Ta-dah!:
+ *
+ * <div class="csl-entry">Behler, J., Parrinello, M. (2007).
+ * Generalized neural-network representation of high-dimensional
+ * potential-energy surfaces. <i>Physical Review Letters</i>,
+ * <i>98</i>(14), 146401. https://doi.org/10.1103/PhysRevLett.98.146401</div>
+ *
+ * Required Config keys:
+ * \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;
+
+    public:
+        D2_BP(Config &config);
+        void calc_aed(
+                const double rij,
+                const double ,
+                const double fc_ij,
+                aed_rtype 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_rtype aed,
+                fd_type &fd_ij);
+        size_t size();
+        std::string label();
+};
+#endif
diff --git a/descriptors/d2/d2_dummy.cpp b/descriptors/d2/d2_dummy.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4ea73bac21fe1eaef4706da13aee7eb353aa14b9
--- /dev/null
+++ b/descriptors/d2/d2_dummy.cpp
@@ -0,0 +1,29 @@
+#include "d2_dummy.h"
+
+Registry<D2_Base,Config&>::Register<D2_Dummy> D2_Dummy_1( "D2_Dummy" );
+
+D2_Dummy::D2_Dummy() {}
+D2_Dummy::D2_Dummy(Config &c):
+    verbose(c.get<int>("VERBOSE"))
+{}
+
+void D2_Dummy::calc_aed(
+        const double,
+        const double,
+        const double,
+        aed_rtype ) {}
+void D2_Dummy::calc_dXijdri(
+        const double,
+        const double,
+        const double,
+        const double,
+        fd_type &) {}
+void D2_Dummy::calc_all(
+        const double,
+        const double,
+        const double,
+        const double,
+        aed_rtype ,
+        fd_type &) {}
+size_t D2_Dummy::size() { return s; }
+std::string D2_Dummy::label() { return lab; }
diff --git a/descriptors/d2/d2_dummy.h b/descriptors/d2/d2_dummy.h
new file mode 100644
index 0000000000000000000000000000000000000000..87a73fd640747397e04ce4ea42c1d1194c3a4b0a
--- /dev/null
+++ b/descriptors/d2/d2_dummy.h
@@ -0,0 +1,41 @@
+#ifndef D2_DUMMY_H
+#define D2_DUMMY_H
+#include "d2_base.h"
+
+/**
+ * Dummy two-body descriptor.
+ *
+ * Use it to satisfy DescriptorsCalc requirements in case
+ * when two-body descriptor is not required.
+ *
+ */
+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_rtype 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_rtype aed,
+                fd_type &fd_ij);
+        size_t size();
+        std::string label();
+};
+#endif
diff --git a/descriptors/d2/d2_eam.cpp b/descriptors/d2/d2_eam.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..99bcbf14837cb16641cc83133e0c88dfabe989cb
--- /dev/null
+++ b/descriptors/d2/d2_eam.cpp
@@ -0,0 +1,176 @@
+#include "d2_eam.h"
+
+Registry<D2_Base,Config&>::Register<D2_EAM> D2_EAM_1( "D2_EAM" );
+
+D2_EAM::D2_EAM(Config &c):
+    verbose(c.get<int>("VERBOSE"))
+{
+    if (!c.get<bool>("INIT2B")) {
+        s=0;
+        return;
+    }
+	ef.file_path = c("SETFL")[0];
+	read_setfl();
+
+	if (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));
+
+
+	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);
+
+}
+
+void D2_EAM::calc_aed(
+		const double rij,
+		const double,
+		const double,
+		aed_rtype aed)
+{
+
+	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;
+
+}
+void D2_EAM::calc_dXijdri(
+		const double rij,
+		const double,
+		const double,
+		const double,
+		fd_type &fd_ij)
+{
+
+	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);
+
+}
+void D2_EAM::calc_all(
+		const double rij,
+		const double,
+		const double,
+		const double,
+		aed_rtype aed,
+		fd_type &fd_ij)
+{
+	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;
+}
+std::string D2_EAM::label() {
+	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;;
+    }
+}
+void D2_EAM::gen_splines(int &n, double &delta, std::vector<double> &f,
+		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;
+	}
+}
+
diff --git a/descriptors/d2/d2_eam.h b/descriptors/d2/d2_eam.h
new file mode 100644
index 0000000000000000000000000000000000000000..6a8545d4b33e1404df48af0e6fc8a37a71cae56b
--- /dev/null
+++ b/descriptors/d2/d2_eam.h
@@ -0,0 +1,79 @@
+#ifndef D2_EAM_H
+#define D2_EAM_H
+#include "d2_base.h"
+
+/** \brief Pair-wise part for the Embedded Atom Method descriptor.
+ *
+ * \f[
+ * V_i = \frac{1}{2} \sum_{j \neq i} \psi(r_{ij})
+ * \f]
+ *
+ * This descriptor will load tabulated values for the two-body potential \f$ \phi \f$
+ * from the provided \ref SETFL file.
+ *
+ * This descriptor is usually used together with the many-body descriptor \ref DM_EAM
+ * although this is not required and user can mix it with any other descriptors
+ * or use it on its own.
+ *
+ * This descriptor will enforce cutoff distance as specified in a \ref SETFL file.
+ * Set \ref RCUT2B to the same value to suppress the warning message.
+ *
+ * Required Config keys:
+ * \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;
+
+        std::vector<std::vector<double>> frho_spline;
+        std::vector<std::vector<double>> rhor_spline;
+        std::vector<std::vector<double>> z2r_spline;
+
+        int verbose;
+
+        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_rtype 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_rtype aed,
+                fd_type &fd_ij);
+        size_t size();
+        std::string label();
+};
+#endif
diff --git a/descriptors/d2/d2_lj.cpp b/descriptors/d2/d2_lj.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b0523480ce34325794b55aea3e7983028c2571c5
--- /dev/null
+++ b/descriptors/d2/d2_lj.cpp
@@ -0,0 +1,61 @@
+#include "d2_lj.h"
+
+Registry<D2_Base,Config&>::Register<D2_LJ> D2_LJ_1( "D2_LJ" );
+
+D2_LJ::D2_LJ(Config &c):
+    verbose(c.get<int>("VERBOSE"))
+{
+    if (!c.get<bool>("INIT2B")) {
+        s=0;
+    }
+}
+
+void D2_LJ::calc_aed(
+        const double,
+        const double rij_sq,
+        const double fc_ij,
+        aed_rtype aed)
+{
+    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;
+}
+void D2_LJ::calc_dXijdri(
+        const double rij,
+        const double rij_sq,
+        const double fc_ij,
+        const double fcp_ij,
+        fd_type &fd_ij)
+{
+
+    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;
+}
+void D2_LJ::calc_all(
+        const double rij,
+        const double rij_sq,
+        const double fc_ij,
+        const double fcp_ij,
+        aed_rtype aed,
+        fd_type &fd_ij)
+{
+    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;
+
+    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;
+}
+std::string D2_LJ::label() {
+    return lab;
+}
diff --git a/descriptors/d2/d2_lj.h b/descriptors/d2/d2_lj.h
new file mode 100644
index 0000000000000000000000000000000000000000..a1a3f8dd6d3dfed2d4eb41924e30b7248dd5875c
--- /dev/null
+++ b/descriptors/d2/d2_lj.h
@@ -0,0 +1,59 @@
+#ifndef D2_LJ_H
+#define D2_LJ_H
+#include "d2_base.h"
+/** \anchor D2_LJ \brief Standard Lennard - Jones descriptor
+ *
+ * \f[
+ * V_i = \sum_{j \neq i} 4 \epsilon \Bigg(\Big(\frac{\sigma}{r_{ij}}\Big)^{12} - \Big(\frac{\sigma}{r_{ij}}\Big)^6\Bigg) f_c(r_{ij})
+ * \f]
+ *
+ * or equivalently:
+ *
+ * \f[
+ * V_i = \sum_{j \neq i} \frac{C_{12}}{r_{ij}^{12}} - \frac{C_6}{r_{ij}^6} f_c(r_{ij})
+ * \f]
+ *
+ * Note that machined learned coefficients \f$C_6\f$ and \f$C_{12}\f$
+ * corresponds to \f$\sigma\f$ and \f$\epsilon\f$ through the following relation:
+ *
+ * \f[ \sigma = \Big(\frac{C_{12}}{C_6}\Big)^{1/6} \f]
+ * \f[ \epsilon = \frac{1}{4} \frac{C_6^2}{C_{12}} w(Z) \f]
+ * where \f$w(Z)\f$ is a species depended weight factor (default is an atomic number).
+ *
+ * The machine learned \f$\sigma\f$ and \f$\epsilon\f$ only make sense
+ * (say to compare with the literature ones) when \ref BIAS false
+ * and \ref NORM false and system in monatomic.
+ * It is ok thought to set them to true it's just that
+ * numerical values will be different.
+ *
+ * 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_rtype 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_rtype aed,
+                fd_type &fd_ij);
+        size_t size();
+        std::string label();
+};
+#endif
diff --git a/descriptors/d2/d2_mie.cpp b/descriptors/d2/d2_mie.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d17366417d8a3e677120cd330588822189e8cc2f
--- /dev/null
+++ b/descriptors/d2/d2_mie.cpp
@@ -0,0 +1,72 @@
+#include "d2_mie.h"
+
+Registry<D2_Base,Config&>::Register<D2_MIE> D2_MIE_1( "D2_MIE" );
+
+D2_MIE::D2_MIE(Config &c):
+    verbose(c.get<int>("VERBOSE"))
+{
+    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");
+    }
+}
+
+void D2_MIE::calc_aed(
+        const double rij,
+        const double,
+        const double fc_ij,
+        aed_rtype aed)
+{
+    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;
+}
+void D2_MIE::calc_dXijdri(
+        const double rij,
+        const double,
+        const double fc_ij,
+        const double fcp_ij,
+        fd_type &fd_ij)
+{
+
+    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);
+
+}
+void D2_MIE::calc_all(
+        const double rij,
+        const double,
+        const double fc_ij,
+        const double fcp_ij,
+        aed_rtype aed,
+        fd_type &fd_ij)
+{
+
+    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;
+
+    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;
+
+}
+size_t D2_MIE::size() {
+    return s;
+}
+std::string D2_MIE::label() {
+    return lab;
+}
diff --git a/descriptors/d2/d2_mie.h b/descriptors/d2/d2_mie.h
new file mode 100644
index 0000000000000000000000000000000000000000..054fb221acbefb0774bac9a41e163eae4f27f160
--- /dev/null
+++ b/descriptors/d2/d2_mie.h
@@ -0,0 +1,54 @@
+#ifndef D2_MIE_H
+#define D2_MIE_H
+#include "d2_base.h"
+/** \brief Mie descriptor
+ *
+ * \f[
+ * V_i = \sum_{j \neq i} C \epsilon \Bigg(\Big(\frac{\sigma}{r_{ij}}\Big)^{n} - \Big(\frac{\sigma}{r_{ij}}\Big)^m\Bigg)
+ * \f]
+ *
+ * where
+ * \f[
+ * C=\frac{n}{n-m}\Big( \frac{n}{m} \Big)^{\frac{m}{n-m}}
+ * \f]
+ *
+ *
+ * Any cutoff can be used
+ *
+ * Required Config Key: \ref INIT2B \ref SGRID2B
+ *
+ * e.g. SGID2B 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_rtype 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_rtype aed,
+                fd_type &fd_ij);
+        size_t size();
+        std::string label();
+};
+#endif
diff --git a/descriptors/d3/d3_all.h b/descriptors/d3/d3_all.h
new file mode 100644
index 0000000000000000000000000000000000000000..881cd74cb2651b737fa33d0e0a945b4ca34f84bf
--- /dev/null
+++ b/descriptors/d3/d3_all.h
@@ -0,0 +1,2 @@
+#include "d3_base.h"
+#include "d3_dummy.h"
diff --git a/descriptors/d3/d3_base.cpp b/descriptors/d3/d3_base.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f8c72a35e8a7989879412ea43aed0cafa944c623
--- /dev/null
+++ b/descriptors/d3/d3_base.cpp
@@ -0,0 +1,3 @@
+#include "d3_base.h"
+//template struct Registry<D3_Base, Config &>;
+
diff --git a/descriptors/d3/d3_base.h b/descriptors/d3/d3_base.h
new file mode 100644
index 0000000000000000000000000000000000000000..281932a89db29a8ce063d0c4929ff66f04c6f0cf
--- /dev/null
+++ b/descriptors/d3/d3_base.h
@@ -0,0 +1,40 @@
+#ifndef D3_BASE_H
+#define D3_BASE_H
+
+#include "../d_base.h"
+
+class D3_Base: public D_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_rtype 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_rtype aed,
+                fd_type &fd_ij)=0;
+        virtual size_t size()=0;
+        virtual std::string label()=0;
+};
+template<> inline Registry<D3_Base,Config&>::Map Registry<D3_Base,Config&>::registry{};
+#endif
diff --git a/descriptors/d3/d3_dummy.cpp b/descriptors/d3/d3_dummy.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c145bfd552d087e758dbe6ae6d6fe11d5982b66f
--- /dev/null
+++ b/descriptors/d3/d3_dummy.cpp
@@ -0,0 +1,35 @@
+#include "d3_dummy.h"
+
+Registry<D3_Base,Config&>::Register<D3_Dummy> D3_Dummy_1( "D3_Dummy" );
+
+D3_Dummy::D3_Dummy() {}
+D3_Dummy::D3_Dummy(Config &) {}
+
+void D3_Dummy::calc_aed(
+        const size_t,
+        const double,
+        const double,
+        const double,
+        const double,
+        aed_rtype ) {}
+void D3_Dummy::calc_fd(
+        const size_t,
+        const double,
+        const double,
+        const double,
+        const double,
+        const double,
+        const double,
+        fd_type &) {}
+void D3_Dummy::calc_all(
+        const size_t,
+        const double,
+        const double,
+        const double,
+        const double,
+        const double,
+        const double,
+        aed_rtype ,
+        fd_type &) {}
+size_t D3_Dummy::size() { return s;}
+std::string D3_Dummy::label() {return lab;}
diff --git a/descriptors/d3/d3_dummy.h b/descriptors/d3/d3_dummy.h
new file mode 100644
index 0000000000000000000000000000000000000000..616b08a021e7e6159589baf8c9d1355b1f66cb37
--- /dev/null
+++ b/descriptors/d3/d3_dummy.h
@@ -0,0 +1,40 @@
+#ifndef D3_DUMMY_H
+#define D3_DUMMY_H
+#include "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_rtype 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_rtype aed,
+                fd_type &fd_ij);
+        size_t size();
+        std::string label();
+};
+#endif
diff --git a/descriptors/d_all.h b/descriptors/d_all.h
new file mode 100644
index 0000000000000000000000000000000000000000..f877cc369427025c2cc11561b252217058885296
--- /dev/null
+++ b/descriptors/d_all.h
@@ -0,0 +1,3 @@
+#include "d2/d2_all.h"
+#include "d3/d3_all.h"
+#include "dm/dm_all.h"
diff --git a/descriptors/d_base.h b/descriptors/d_base.h
new file mode 100644
index 0000000000000000000000000000000000000000..4ac2101226451074a6a553ddbfc3edde6c03015e
--- /dev/null
+++ b/descriptors/d_base.h
@@ -0,0 +1,57 @@
+#ifndef D_BASE_H
+#define D_BASE_H
+
+#include "../../CORE/config/config.h"
+#include "../../CORE/typedefs.h"
+#include "../../LIBS/Eigen/Dense"
+#include "../../CORE/registry.h"
+
+/** \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;
+};
+#endif
diff --git a/descriptors/d_basis_functions.h b/descriptors/d_basis_functions.h
new file mode 100644
index 0000000000000000000000000000000000000000..c464210b487869b3f552417839ce79f5857f35fc
--- /dev/null
+++ b/descriptors/d_basis_functions.h
@@ -0,0 +1,101 @@
+#ifndef D_BASIS_F_H
+#define D_BASIS_F_H
+#include <math.h>
+
+// GAUSSIANS
+inline double G(double r, double eta, double miu) {
+    return exp(-eta*(r-miu)*(r-miu));
+}
+inline double G(double r, double eta, double miu, double f) {
+    return exp(-eta*(r-miu)*(r-miu))*f;
+}
+inline double dG(double r, double eta, double miu) {
+    return -2.0*eta*(r-miu)*exp(-eta*(r-miu)*(r-miu));
+}
+inline double dG(double r, double eta, double miu, double f, double fp) {
+    return exp(-eta*(r-miu)*(r-miu) )*(-2.0*f*eta*(r-miu)+fp);
+}
+
+// BLIPS
+inline double B(double x_c)
+    // x_c = eta*(rij-r_s)
+    // return B(x_c)
+{
+    if (-2.0 < x_c && x_c <= -1.0) {
+        double b = 2.0 + x_c;
+        return 0.25 * b*b*b;
+    }
+    else if (-1.0 < x_c && x_c <= 1.0) {
+        double x2 = x_c*x_c;
+        double x3 = x2*x_c;
+        return 1.0 - 1.5*x2 + 0.75*std::fabs(x3);
+    }
+    else if (1.0 < x_c && x_c < 2.0) {
+        double b = 2.0 - x_c;
+        return 0.25 * b*b*b;
+    }
+    else {
+        return 0.0;
+    }
+}
+inline double B(double x_c, double f)
+    // x_c = eta*(rij-r_s)
+    // return B(x_c)*f
+{
+    if (-2.0 < x_c && x_c <= -1.0) {
+        double b = 2.0 + x_c;
+        return 0.25 * b*b*b *f;
+    }
+    else if (-1.0 < x_c && x_c <= 1.0) {
+        double x2 = x_c*x_c;
+        double x3 = x2*x_c;
+        return f*(1.0 - 1.5*x2 + 0.75*std::fabs(x3));
+    }
+    else if (1.0 < x_c && x_c < 2.0) {
+        double b = 2.0 - x_c;
+        return f*(0.25 * b*b*b);
+    }
+    else {
+        return 0.0;
+    }
+}
+inline double dB(double x_c, double eta)
+    // def: x_c = eta*(rij-r_s)
+    // return d/dr_ij B(x_c) = eta*d/dx_c B(x_c)
+{
+    if (-2.0 < x_c && x_c <= -1.0) {
+        double d = 2.0+x_c;
+        return 0.75*eta*d*d;
+    }
+    else if (-1.0 < x_c && x_c <= 1.0) {
+        return -3.0*eta*x_c + 2.25*eta*x_c*std::fabs(x_c);
+    }
+    else if (1.0 < x_c && x_c < 2.0) {
+        double d = 2.0-x_c;
+        return -0.75*eta*d*d;
+    }
+    else {
+        return 0.0;
+    }
+}
+inline double dB(double x_c, double eta, double f, double fp)
+    // def: x_c = eta*(rij-r_s)
+    // return d/dr_ij f(r_ij)*B(x_c)
+{
+    if (-2.0 < x_c && x_c <= -1.0) {
+        double d = 2.0+x_c;
+        return f*(0.75*eta*d*d)+fp*(0.25 * d*d*d);
+    }
+    else if (-1.0 < x_c && x_c <= 1.0) {
+        return f*(-3.0*eta*x_c + 2.25*eta*x_c*std::fabs(x_c))
+            +fp*(1.0 - 1.5*x_c*x_c + 0.75*std::fabs(x_c*x_c*x_c));
+    }
+    else if (1.0 < x_c && x_c < 2.0) {
+        double d = 2.0-x_c;
+        return f*(-0.75*eta*d*d)+fp*(0.25 * d*d*d);
+    }
+    else {
+        return 0.0;
+    }
+}
+#endif
diff --git a/descriptors/dm/dm_all.h b/descriptors/dm/dm_all.h
new file mode 100644
index 0000000000000000000000000000000000000000..5f14169c7a5fedff1ecef0869be62f09a6eacd8a
--- /dev/null
+++ b/descriptors/dm/dm_all.h
@@ -0,0 +1,6 @@
+#include "dm_base.h"
+#include "dm_dummy.h"
+#include "dm_eam.h"
+#include "dm_ead.h"
+#include "dm_blip.h"
+#include "dm_mEAD.h"
diff --git a/descriptors/dm/dm_base.cpp b/descriptors/dm/dm_base.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0099681b1582d07b610298e9f0cd79bfa451029b
--- /dev/null
+++ b/descriptors/dm/dm_base.cpp
@@ -0,0 +1,2 @@
+#include "dm_base.h"
+
diff --git a/descriptors/dm/dm_base.h b/descriptors/dm/dm_base.h
new file mode 100644
index 0000000000000000000000000000000000000000..1912e4be8d4c463cbb018073211dad5c474b9eaf
--- /dev/null
+++ b/descriptors/dm/dm_base.h
@@ -0,0 +1,90 @@
+#ifndef DM_BASE_H
+#define DM_BASE_H
+
+#include <string>
+#include "../d_base.h"
+
+/** \brief Base class for all many-body type descriptors.
+ *
+ * All many-body descriptors **must** inherit this class.
+ */
+class DM_Base: public D_Base {
+    public:
+        size_t fidx=0;    // first index in aed and fd arrays for this descriptor
+        virtual ~DM_Base() {};
+
+        /** \brief Calculate \ref AED
+         *
+         * Calculate Atomic Energy Descriptor for the atom local environment.
+         */
+        virtual void calc_aed(
+                rho_rtype rho,
+                aed_rtype aed)=0;
+
+        /** \brief Calculate \ref FD
+         *
+         * Calculate Force Descriptor between to atoms.
+         *
+         * This method works for half NN lists
+         * and linear models.
+         *
+         * \f[
+         * \mathbf{fd}_{ij} \mathrel{+}= 
+         * \frac{\partial \mathbf{X}_{ij}}{\partial \mathbf{r}_i} +
+         * \frac{\partial \mathbf{X}_{ji}}{\partial \mathbf{r}_i}
+         * \f]
+         */
+        virtual int calc_dXijdri_dXjidri(
+                const double rij,
+                const double rij_sq,
+                const Eigen::Vector3d &vec_ij,
+                const double fc_ij,
+                const double fcp_ij,
+                rho_rtype rhoi,
+                rho_rtype rhoj,
+                fd_type &fd_ij,
+                const double wi,
+                const double wj)=0;
+
+        /** \brief Calculate \ref FD
+         *
+         * Calculate Force Descriptor between to atoms.
+         *
+         * This method works for full NN lists
+         * and all models.
+         *
+         * \f[
+         * \mathbf{fd}_{ij} \mathrel{+}=
+         * \frac{\partial \mathbf{X}_{ij}}{\partial \mathbf{r}_i}
+         * \f]
+         */
+        virtual int calc_dXijdri(
+                const double rij,
+                const double rij_sq,
+                const Eigen::Vector3d &vec_ij,
+                const double fc_ij,
+                const double fcp_ij,
+                rho_rtype rhoi,
+                fd_type &fd_ij)=0;
+
+        /** \brief Resize arrays for a density and F' */
+        virtual void init_rhoi(rho_rtype rhoi)=0;
+
+        /** \brief Calculate density */
+        virtual void calc_rho(
+                const double rij,
+                const double rij_sq,
+                const double fc_ij,
+                const Eigen::Vector3d &vec_ij,
+                rho_rtype rho)=0;
+
+        /** \brief Return size of the density array */
+        virtual size_t rhoi_size()=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;
+};
+template<> inline Registry<DM_Base,Config&>::Map Registry<DM_Base,Config&>::registry{};
+#endif
diff --git a/descriptors/dm/dm_blip.cpp b/descriptors/dm/dm_blip.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0a23ea92e4c3c98e5a313643091f288c56d2bb2a
--- /dev/null
+++ b/descriptors/dm/dm_blip.cpp
@@ -0,0 +1,299 @@
+#include "dm_blip.h"
+#include "../d_basis_functions.h"
+#include <vector>
+#include <iomanip>
+
+Registry<DM_Base,Config&>::Register<DM_Blip> DM_Blip_1( "DM_Blip" );
+
+DM_Blip::DM_Blip(Config &c):
+    verbose(c.get<int>("VERBOSE")),
+    rc(c.get<double>("RCUTMB"))
+{
+
+    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;
+    }
+}
+
+void DM_Blip::calc_aed(
+        rho_rtype rhoi,
+        aed_rtype 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();
+    }
+}
+int DM_Blip::calc_dXijdri_dXjidri(
+        const double rij,
+        const double,
+        const Eigen::Vector3d &vec_ij,
+        const double fc_ij,
+        const double fcp_ij,
+        rho_rtype rhoi,
+        rho_rtype rhoj,
+        fd_type &fd_ij,
+        const double wi,
+        const double wj)
+{
+    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();
+    }
+
+    return 1;
+
+}
+int DM_Blip::calc_dXijdri(
+        const double rij,
+        const double,
+        const Eigen::Vector3d &vec_ij,
+        const double fc_ij,
+        const double fcp_ij,
+        rho_rtype rhoi,
+        fd_type &fd_ij)
+{
+    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();
+    }
+
+    return 1;
+
+}
+size_t DM_Blip::size() {
+    return s;
+}
+std::string DM_Blip::label() {
+    return lab;
+}
+
+void DM_Blip::init_rhoi(rho_rtype rhoi)
+{
+    rhoi.resize(2*rhoisize);
+    rhoi.setZero();
+}
+void DM_Blip::calc_rho(
+        const double rij,
+        const double,
+        const double fc_ij,
+        const Eigen::Vector3d &vec_ij,
+        rho_rtype rhoi)
+{
+    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;
+            }
+        }
+    }
+}
+
+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++;
+                    }
+                }
+            }
+        }
+    }
+    return count;
+}
+double DM_Blip::fact(int n)
+{
+    double f=1.0;
+    while (n) {
+        f *= n;
+        --n;
+    }
+    return f;
+}
+double DM_Blip::my_pow(double x, int n){
+    double r = 1.0;
+
+    while(n > 0){
+        r *= x;
+        --n;
+    }
+
+    return r;
+}
+size_t DM_Blip::rhoi_size() {
+    return rhoisize;
+}
+size_t DM_Blip::rhoip_size() {
+    return rhoisize;
+}
diff --git a/descriptors/dm/dm_blip.h b/descriptors/dm/dm_blip.h
new file mode 100644
index 0000000000000000000000000000000000000000..f11e443cb746f0954bfa37c87de8e00c8e5120cd
--- /dev/null
+++ b/descriptors/dm/dm_blip.h
@@ -0,0 +1,90 @@
+#ifndef DM_BLIP_H
+#define DM_BLIP_H
+#include "dm_base.h"
+
+/**  Blip Many Body Descriptor.
+ *
+ * \f[
+ * V_i^{L,\eta,r_s} = \sum_{l_x,l_y,l_z}^{l_x+l_y+l_z=L} \frac{L!}{l_x!l_y!l_z!}
+ * \Big( \rho_i^{\eta,r_s,l_x,l_y,l_z} \Big)^2
+ * \f]
+ *
+ * where density \f$ \rho \f$ is calculated using modified Gaussian Type Orbitals
+ * (expansion in the Blip basis instead of usual Gaussians):
+ * \f[
+ * \rho_i^{\eta,r_s,l_x,l_y,l_z} = \sum_{j \neq i} x_{ij}^{l_x}y_{ij}^{l_y}z_{ij}^{l_z}
+ * \B{\Big(-\eta(r_{ij}-r_s)^2\Big)}f_c(r_{ij})
+ * \f]
+ *
+ * \ref CGRIDMB parameters control position \f$ r_s \f$ of the gaussian basis function.
+ *
+ * \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:
+ *
+ * <div class="csl-entry">Zhang, Y., Hu, C.,Jiang, B.
+ * (2019). Embedded atom neural network potentials: efficient and accurate
+ * machine learning with a physically inspired representation.
+ * <i>Journal of Physical Chemistry Letters</i>, <i>10</i>(17),
+ * 4962–4967. https://doi.org/10.1021/acs.jpclett.9b02037</div>
+ *
+ * Required Config keys:
+ * \ref INITMB \ref CGRIDMB \ref SGRIDMB \ref AGRIDMB
+ */
+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;
+
+    public:
+        DM_Blip(Config&);
+        void calc_aed(
+                rho_rtype rho,
+                aed_rtype aed);
+        int calc_dXijdri_dXjidri(
+                const double rij,
+                const double rij_sq,
+                const Eigen::Vector3d &vec_ij,
+                const double fc_ij,
+                const double fcp_ij,
+                rho_rtype rhoi,
+                rho_rtype rhoj,
+                fd_type &fd_ij,
+                const double wi,
+                const double wj);
+        int calc_dXijdri(
+                const double rij,
+                const double rij_sq,
+                const Eigen::Vector3d &vec_ij,
+                const double fc_ij,
+                const double fcp_ij,
+                rho_rtype rhoi,
+                fd_type &fd_ij);
+        size_t size();
+        std::string label();
+        void init_rhoi(rho_rtype rhoi);
+        void calc_rho(
+                const double rij,
+                const double rij_sq,
+                const double fc_ij,
+                const Eigen::Vector3d &vec_ij,
+                rho_rtype rho);
+        size_t rhoi_size();
+        size_t rhoip_size();
+};
+#endif
diff --git a/descriptors/dm/dm_dummy.cpp b/descriptors/dm/dm_dummy.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..eff22e7581d0eee12deda7a35a385a67ed3e3f27
--- /dev/null
+++ b/descriptors/dm/dm_dummy.cpp
@@ -0,0 +1,40 @@
+#include "dm_dummy.h"
+
+Registry<DM_Base,Config&>::Register<DM_Dummy> DM_Dummy_1( "DM_Dummy" );
+
+DM_Dummy::DM_Dummy() {}
+DM_Dummy::DM_Dummy(Config&) {}
+void DM_Dummy::calc_aed(
+        rho_rtype,
+        aed_rtype ) {}
+int DM_Dummy::calc_dXijdri_dXjidri(
+        const double,
+        const double,
+        const Eigen::Vector3d &,
+        const double,
+        const double,
+        rho_rtype,
+        rho_rtype,
+        fd_type &,
+        const double,
+        const double) { return 0; }
+int DM_Dummy::calc_dXijdri(
+        const double,
+        const double,
+        const Eigen::Vector3d &,
+        const double,
+        const double,
+        rho_rtype,
+        fd_type &) { return 0; }
+size_t DM_Dummy::size() { return s;}
+std::string DM_Dummy::label() {return lab;}
+void DM_Dummy::init_rhoi(rho_rtype) {}
+void DM_Dummy::calc_rho(
+        const double,
+        const double,
+        const double,
+        const Eigen::Vector3d &,
+        rho_rtype) {}
+size_t DM_Dummy::rhoi_size() { return 0; }
+size_t DM_Dummy::rhoip_size() { return 0; }
+
diff --git a/descriptors/dm/dm_dummy.h b/descriptors/dm/dm_dummy.h
new file mode 100644
index 0000000000000000000000000000000000000000..9996862bef89b185e7bc6cff5ff29e71de5e4429
--- /dev/null
+++ b/descriptors/dm/dm_dummy.h
@@ -0,0 +1,53 @@
+#ifndef DM_DUMMY_H
+#define DM_DUMMY_H
+#include "dm_base.h"
+
+/**
+ * Dummy many-body descriptor.
+ *
+ * Use it to satisfy DescriptorsCalc requirements in case
+ * when many-body descriptor is not required.
+ *
+ */
+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_rtype rho,
+                aed_rtype aed);
+        int calc_dXijdri_dXjidri(
+                const double rij,
+                const double rij_sq,
+                const Eigen::Vector3d &vec_ij,
+                const double fc_ij,
+                const double fcp_ij,
+                rho_rtype rhoi,
+                rho_rtype rhoj,
+                fd_type &fd_ij,
+                const double wi,
+                const double wj);
+        int calc_dXijdri(
+                const double rij,
+                const double rij_sq,
+                const Eigen::Vector3d &vec_ij,
+                const double fc_ij,
+                const double fcp_ij,
+                rho_rtype rhoi,
+                fd_type &fd_ij);
+        size_t size();
+        std::string label();
+        void init_rhoi(rho_rtype rhoi);
+        void calc_rho(
+                const double rij,
+                const double rij_sq,
+                const double fc_ij,
+                const Eigen::Vector3d &vec_ij,
+                rho_rtype rho);
+        size_t rhoi_size();
+        size_t rhoip_size();
+};
+#endif
diff --git a/descriptors/dm/dm_ead.cpp b/descriptors/dm/dm_ead.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..81045d9db7b7dbae8e7d29d68ca833ec92acfeb3
--- /dev/null
+++ b/descriptors/dm/dm_ead.cpp
@@ -0,0 +1,303 @@
+#include "dm_ead.h"
+#include "../d_basis_functions.h"
+#include <vector>
+#include <iomanip>
+
+Registry<DM_Base,Config&>::Register<DM_EAD> DM_EAD_1( "DM_EAD" );
+
+/* 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"))
+{
+
+    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;
+    }
+}
+
+void DM_EAD::calc_aed(
+        rho_rtype rhoi,
+        aed_rtype 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();
+    }
+}
+int DM_EAD::calc_dXijdri_dXjidri(
+        const double rij,
+        const double,
+        const Eigen::Vector3d &vec_ij,
+        const double fc_ij,
+        const double fcp_ij,
+        rho_rtype rhoi,
+        rho_rtype rhoj,
+        fd_type &fd_ij,
+        const double wi,
+        const double wj)
+{
+    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 gauss = G(rij,sgrid[c],cgrid[c],fc_ij);
+                double dgauss = dG(rij,sgrid[c],cgrid[c],fc_ij,fcp_ij);
+
+                const double term2ijx=pqrij*dgauss*x/rij;
+                const double term2ijy=pqrij*dgauss*y/rij;
+                const double term2ijz=pqrij*dgauss*z/rij;
+
+                const double term2jix=-pqrji*dgauss*x/rij;
+                const double term2jiy=-pqrji*dgauss*y/rij;
+                const double term2jiz=-pqrji*dgauss*z/rij;
+
+                const double term1ijx = gauss*txij;
+                const double term1ijy = gauss*tyij;
+                const double term1ijz = gauss*tzij;
+
+                const double term1jix = gauss*txji;
+                const double term1jiy = gauss*tyji;
+                const double term1jiz = gauss*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();
+    }
+
+    return 1;
+
+}
+int DM_EAD::calc_dXijdri(
+        const double rij,
+        const double,
+        const Eigen::Vector3d &vec_ij,
+        const double fc_ij,
+        const double fcp_ij,
+        rho_rtype rhoi,
+        fd_type &fd_ij)
+{
+    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 gauss = G(rij,sgrid[c],cgrid[c],fc_ij);
+                double dgauss = dG(rij,sgrid[c],cgrid[c],fc_ij,fcp_ij);
+
+                const double term2ijx=pqrij*dgauss*x/rij;
+                const double term2ijy=pqrij*dgauss*y/rij;
+                const double term2ijz=pqrij*dgauss*z/rij;
+
+                const double term1ijx = gauss*txij;
+                const double term1ijy = gauss*tyij;
+                const double term1ijz = gauss*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();
+    }
+
+    return 1;
+
+}
+size_t DM_EAD::size() {
+    return s;
+}
+std::string DM_EAD::label() {
+    return lab;
+}
+
+void DM_EAD::init_rhoi(rho_rtype rhoi)
+{
+    rhoi.resize(2*rhoisize);
+    rhoi.setZero();
+}
+void DM_EAD::calc_rho(
+        const double rij,
+        const double,
+        const double fc_ij,
+        const Eigen::Vector3d &vec_ij,
+        rho_rtype rhoi)
+{
+    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;
+            }
+        }
+    }
+}
+
+size_t DM_EAD::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++;
+                    }
+                }
+            }
+        }
+    }
+    return count;
+}
+double DM_EAD::fact(int n)
+{
+    double f=1.0;
+    while (n) {
+        f *= n;
+        --n;
+    }
+    return f;
+}
+double DM_EAD::my_pow(double x, int n){
+    double r = 1.0;
+
+    while(n > 0){
+        r *= x;
+        --n;
+    }
+    return r;
+}
+size_t DM_EAD::rhoi_size() {
+    return rhoisize;
+}
+size_t DM_EAD::rhoip_size() {
+    return rhoisize;
+}
diff --git a/descriptors/dm/dm_ead.h b/descriptors/dm/dm_ead.h
new file mode 100644
index 0000000000000000000000000000000000000000..3c85158f6168401f243715f3f6c0ac7f22855aca
--- /dev/null
+++ b/descriptors/dm/dm_ead.h
@@ -0,0 +1,89 @@
+#ifndef DM_EAD_H
+#define DM_EAD_H
+#include "dm_base.h"
+
+/**  Embedded Atom Descriptor
+ *
+ * \f[
+ * V_i^{L,\eta,r_s} = \sum_{l_x,l_y,l_z}^{l_x+l_y+l_z=L} \frac{L!}{l_x!l_y!l_z!}
+ * \Big( \rho_i^{\eta,r_s,l_x,l_y,l_z} \Big)^2
+ * \f]
+ *
+ * where density \f$ \rho \f$ is calculated using Gaussian Type Orbitals:
+ * \f[
+ * \rho_i^{\eta,r_s,l_x,l_y,l_z} = \sum_{j \neq i} x_{ij}^{l_x}y_{ij}^{l_y}z_{ij}^{l_z}
+ * \exp{\Big(-\eta(r_{ij}-r_s)^2\Big)}f_c(r_{ij})
+ * \f]
+ *
+ * \ref CGRIDMB parameters control position \f$ r_s \f$ of the gaussian basis function.
+ *
+ * \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:
+ *
+ * <div class="csl-entry">Zhang, Y., Hu, C.,Jiang, B.
+ * (2019). Embedded atom neural network potentials: efficient and accurate
+ * machine learning with a physically inspired representation.
+ * <i>Journal of Physical Chemistry Letters</i>, <i>10</i>(17),
+ * 4962–4967. https://doi.org/10.1021/acs.jpclett.9b02037</div>
+ *
+ * Required Config keys:
+ * \ref INITMB \ref CGRIDMB \ref SGRIDMB \ref AGRIDMB
+ */
+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;
+
+    public:
+        DM_EAD(Config&);
+        void calc_aed(
+                rho_rtype rho,
+                aed_rtype aed);
+        int calc_dXijdri_dXjidri(
+                const double rij,
+                const double rij_sq,
+                const Eigen::Vector3d &vec_ij,
+                const double fc_ij,
+                const double fcp_ij,
+                rho_rtype rhoi,
+                rho_rtype rhoj,
+                fd_type &fd_ij,
+                const double wi,
+                const double wj);
+        int calc_dXijdri(
+                const double rij,
+                const double rij_sq,
+                const Eigen::Vector3d &vec_ij,
+                const double fc_ij,
+                const double fcp_ij,
+                rho_rtype rhoi,
+                fd_type &fd_ij);
+        size_t size();
+        std::string label();
+        void init_rhoi(rho_rtype rhoi);
+        void calc_rho(
+                const double rij,
+                const double rij_sq,
+                const double fc_ij,
+                const Eigen::Vector3d &vec_ij,
+                rho_rtype rho);
+        size_t rhoi_size();
+        size_t rhoip_size();
+};
+#endif
diff --git a/descriptors/dm/dm_eam.cpp b/descriptors/dm/dm_eam.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..56c2b8533b059d72230fc9d32dbfb49f3c97a4ce
--- /dev/null
+++ b/descriptors/dm/dm_eam.cpp
@@ -0,0 +1,222 @@
+#include "dm_eam.h"
+
+Registry<DM_Base,Config&>::Register<DM_EAM> DM_EAM_1( "DM_EAM" );
+
+DM_EAM::DM_EAM(Config &c):
+    verbose(c.get<int>("VERBOSE")) 
+{
+    if (!c.get<bool>("INITMB")) {
+        s=0;
+        return;
+    }
+    ef.file_path = c("SETFL")[0];
+    read_setfl();
+
+    if (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();
+    }
+
+
+    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);
+
+}
+
+void DM_EAM::calc_aed(
+		rho_rtype rho,
+		aed_rtype 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];
+
+    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;
+
+    if (rho(0) > ef.rhomax) phi += rho(1) * (rho(0)-ef.rhomax);
+    //phi *= scale[type[i]][type[i]];
+
+    aed(fidx) += phi;
+
+
+}
+int DM_EAM::calc_dXijdri_dXjidri(
+		const double rij,
+		const double,
+		const Eigen::Vector3d &,
+		const double,
+		const double,
+		rho_rtype rhoi,
+		rho_rtype rhoj,
+		fd_type &fd_ij,
+        const double,
+        const double)
+{
+    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;
+
+}
+int DM_EAM::calc_dXijdri(
+		const double rij,
+		const double,
+		const Eigen::Vector3d &,
+		const double,
+		const double,
+		rho_rtype rhoi,
+		fd_type &fd_ij)
+{
+    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;
+}
+std::string DM_EAM::label() {
+	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;
+
+}
+void DM_EAM::gen_splines(int &n, double &delta, std::vector<double> &f,
+		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;
+	}
+}
+void DM_EAM::init_rhoi(rho_rtype rhoi)
+{
+    rhoi.resize(2);
+    rhoi.setZero();
+    //rho.resize(1);
+    //rho[0] = Eigen::Matrix<double,1,2>(1,2);    // [0] - rho, [1] - rho prime aka fp[i]
+}
+void DM_EAM::calc_rho(
+		const double rij,
+        const double,
+        const double,
+        const Eigen::Vector3d &,
+        rho_rtype rho)
+{
+    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[i]
+    rho(0) += ((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;
+}
diff --git a/descriptors/dm/dm_eam.h b/descriptors/dm/dm_eam.h
new file mode 100644
index 0000000000000000000000000000000000000000..e2808544f128baa6d311f84041bddace1438bb9e
--- /dev/null
+++ b/descriptors/dm/dm_eam.h
@@ -0,0 +1,93 @@
+#ifndef DM_EAM_H
+#define DM_EAM_H
+#include "dm_base.h"
+
+/** \brief many-body part for the Embedded Atom Method descriptor.
+ *
+ * \f[
+ * V_i = F\Bigg(\sum_{j \neq i} \rho(r_{ij}) \Bigg)
+ * \f]
+ *
+ * This descriptor will load tabulated values for the density \f$ \rho \f$
+ * and embedded energy \f$ F \f$ from the provided \ref SETFL file.
+ *
+ * This descriptor is usually used together with the two-body descriptor \ref D2_EAM
+ * although this is not required and user can mix it with any other descriptors
+ * or use it on its own.
+ *
+ * This descriptor will enforce cutoff distance as specified in a \ref SETFL file.
+ * Set \ref RCUTMB to the same value to suppress the warning message.
+ *
+ * Required Config keys:
+ * \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;
+
+        };
+        eam_file ef;
+
+        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);
+
+    public:
+        DM_EAM(Config&);
+        void calc_aed(
+                rho_rtype rho,
+                aed_rtype aed);
+        int calc_dXijdri_dXjidri(
+                const double rij,
+                const double rij_sq,
+                const Eigen::Vector3d &vec_ij,
+                const double fc_ij,
+                const double fcp_ij,
+                rho_rtype rhoi,
+                rho_rtype rhoj,
+                fd_type &fd_ij,
+                const double wi,
+                const double wj);
+        int calc_dXijdri(
+                const double rij,
+                const double rij_sq,
+                const Eigen::Vector3d &vec_ij,
+                const double fc_ij,
+                const double fcp_ij,
+                rho_rtype rhoi,
+                fd_type &fd_ij);
+        size_t size();
+        std::string label();
+        void init_rhoi(rho_rtype rhoi);
+        void calc_rho(
+                const double rij,
+                const double rij_sq,
+                const double fc_ij,
+                const Eigen::Vector3d &vec_ij,
+                rho_rtype rho);
+        size_t rhoi_size();
+        size_t rhoip_size();
+};
+#endif
diff --git a/descriptors/dm/dm_mEAD.cpp b/descriptors/dm/dm_mEAD.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..dd2a503ba93d6e63fa774f45c8a1baee4144c5b9
--- /dev/null
+++ b/descriptors/dm/dm_mEAD.cpp
@@ -0,0 +1,318 @@
+#include "dm_mEAD.h"
+#include "../d_basis_functions.h"
+#include <vector>
+#include <iomanip>
+
+
+Registry<DM_Base,Config&>::Register<DM_mEAD<F_RLR>> DM_mEAD_1( "DM_mEAD1" );
+
+/* 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)
+{
+
+    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;
+    }
+}
+
+template <typename F>
+void DM_mEAD<F>::calc_aed(
+        rho_rtype rhoi,
+        aed_rtype 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();
+    }
+}
+template <typename F>
+int DM_mEAD<F>::calc_dXijdri_dXjidri(
+        const double rij,
+        const double,
+        const Eigen::Vector3d &vec_ij,
+        const double fc_ij,
+        const double fcp_ij,
+        rho_rtype rhoi,
+        rho_rtype rhoj,
+        fd_type &fd_ij,
+        const double wi,
+        const double wj)
+{
+    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();
+    }
+
+    return 1;
+
+}
+template <typename F>
+int DM_mEAD<F>::calc_dXijdri(
+        const double rij,
+        const double,
+        const Eigen::Vector3d &vec_ij,
+        const double fc_ij,
+        const double fcp_ij,
+        rho_rtype rhoi,
+        fd_type &fd_ij)
+{
+    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();
+    }
+
+    return 1;
+
+}
+template <typename F>
+size_t DM_mEAD<F>::size() {
+    return s;
+}
+template <typename F>
+std::string DM_mEAD<F>::label() {
+    return lab;
+}
+
+template <typename F>
+void DM_mEAD<F>::init_rhoi(rho_rtype rhoi)
+{
+    rhoi.resize(2*rhoisize);
+    rhoi.setZero();
+}
+template <typename F>
+void DM_mEAD<F>::calc_rho(
+        const double rij,
+        const double,
+        const double fc_ij,
+        const Eigen::Vector3d &vec_ij,
+        rho_rtype rhoi)
+{
+    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;
+            }
+        }
+    }
+}
+
+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++;
+                    }
+                }
+            }
+        }
+    }
+    return count;
+}
+template <typename F>
+double DM_mEAD<F>::fact(int n)
+{
+    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;
+
+    while(n > 0){
+        r *= x;
+        --n;
+    }
+    return r;
+}
+template <typename F>
+size_t DM_mEAD<F>::rhoi_size() {
+    return rhoisize;
+}
+template <typename F>
+size_t DM_mEAD<F>::rhoip_size() {
+    return rhoisize;
+}
diff --git a/descriptors/dm/dm_mEAD.h b/descriptors/dm/dm_mEAD.h
new file mode 100644
index 0000000000000000000000000000000000000000..27f859c600c9fa4c16bc8cf725cd9426a1beb4fc
--- /dev/null
+++ b/descriptors/dm/dm_mEAD.h
@@ -0,0 +1,146 @@
+#ifndef DM_mEAD_H
+#define DM_mEAD_H
+#include "dm_base.h"
+class F_Base {
+    public:
+        virtual double calc_F(double rho, size_t c)=0;
+        virtual double calc_dF(double rho, size_t c)=0;
+};
+
+/** Embedding function of the form:  sx*log(cx)
+ *
+ * REQUIRED KEYS:
+ *
+ * SEMBFUNC, CEMBFUNC
+ * control depth and x-intercept of an embedding function respectively.
+ * The number of keys must match those in the mEAD descriptor.
+ *
+ */
+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):
+            config(&conf)
+    {
+        if (!config->get<bool>("INITMB")) return;
+        D_Base::get_grid(*config,"SEMBFUNC",sgrid);
+        D_Base::get_grid(*config,"CEMBFUNC",cgrid);
+
+    }
+        F_RLR():
+            config(nullptr)
+    {
+        }
+        double calc_F(double rho,size_t c) {
+            if (rho>0)
+                return sgrid[c]*rho*log(cgrid[c]*rho);
+            return 0;
+        };
+        double calc_dF(double rho,size_t c) {
+            if (rho>0)
+                return sgrid[c]*(log(cgrid[c]*rho)+1);
+            return sgrid[c];
+        };
+        //~F_RLR() {
+            // Do not delete config!
+        //}
+};
+
+/** Modified Embedded Atom Descriptor
+ *
+ * REQUIRED KEYS: SCGRIDMB, CGRIDMB
+ * + REQUIRED KEYS BY THE EMBEDDING FUNCTION
+ *
+ *
+ * TODO Below description is for EAD not mEAD
+ * This descriptor has mathematical form very similar to EAD.
+ * It allow: for different embedding functions to be used.
+ *
+ * \f[
+ * V_i^{L,\eta,r_s} = \sum_{l_x,l_y,l_z}^{l_x+l_y+l_z=L} \frac{L!}{l_x!l_y!l_z!}
+ * \Big( \rho_i^{\eta,r_s,l_x,l_y,l_z} \Big)^2
+ * \f]
+ *
+ * where density \f$ \rho \f$ is calculated using Gaussian Type Orbitals:
+ * \f[
+ * \rho_i^{\eta,r_s,l_x,l_y,l_z} = \sum_{j \neq i} x_{ij}^{l_x}y_{ij}^{l_y}z_{ij}^{l_z}
+ * \exp{\Big(-\eta(r_{ij}-r_s)^2\Big)}f_c(r_{ij})
+ * \f]
+ *
+ * \ref CGRIDMB parameters control position \f$ r_s \f$ of the gaussian basis function.
+ *
+ * \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:
+ *
+ * <div class="csl-entry">Zhang, Y., Hu, C.,Jiang, B.
+ * (2019). Embedded atom neural network potentials: efficient and accurate
+ * machine learning with a physically inspired representation.
+ * <i>Journal of Physical Chemistry Letters</i>, <i>10</i>(17),
+ * 4962–4967. https://doi.org/10.1021/acs.jpclett.9b02037</div>
+ *
+ * Required Config keys:
+ * \ref INITMB \ref CGRIDMB \ref SGRIDMB \ref AGRIDMB
+ */
+template <typename F>
+class DM_mEAD: public DM_Base, public F {
+    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;
+
+    public:
+        DM_mEAD(Config&);
+        void calc_aed(
+                rho_rtype rho,
+                aed_rtype aed);
+        int calc_dXijdri_dXjidri(
+                const double rij,
+                const double rij_sq,
+                const Eigen::Vector3d &vec_ij,
+                const double fc_ij,
+                const double fcp_ij,
+                rho_rtype rhoi,
+                rho_rtype rhoj,
+                fd_type &fd_ij,
+                const double wi,
+                const double wj);
+        int calc_dXijdri(
+                const double rij,
+                const double rij_sq,
+                const Eigen::Vector3d &vec_ij,
+                const double fc_ij,
+                const double fcp_ij,
+                rho_rtype rhoi,
+                fd_type &fd_ij);
+        size_t size();
+        std::string label();
+        void init_rhoi(rho_rtype rhoi);
+        void calc_rho(
+                const double rij,
+                const double rij_sq,
+                const double fc_ij,
+                const Eigen::Vector3d &vec_ij,
+                rho_rtype rho);
+        size_t rhoi_size();
+        size_t rhoip_size();
+};
+#endif
diff --git a/ea.h b/ea.h
new file mode 100644
index 0000000000000000000000000000000000000000..528fe973a7a9a862469ed32930cdd26fe2c8f3c6
--- /dev/null
+++ b/ea.h
@@ -0,0 +1,90 @@
+#ifndef M_EA_H
+#define M_EA_H
+
+#include "../LIBS/Eigen/Dense"
+#include "../CORE/config/config.h"
+#include "../CORE/typedefs.h"
+
+class EA {
+    private:
+        Config &config;
+        int verbose;
+    public:
+        EA(Config &c):
+            config(c),
+            verbose(c.get<int>("VERBOSE"))
+    {}
+
+        int run  (const mat &Phi,const vec &T,
+                const mat &S, const mat &U,const mat &V,
+                double &alpha, double &beta) {
+
+            // Phi = USV^T
+            size_t N=Phi.rows();
+            size_t maxsteps=40;
+            const double EPS2 = 5e-12;
+            int convergence_status = 1;
+            double test1 = 2.0*EPS2;
+            double test2 = 2.0*EPS2;
+            double alpha_old = alpha;
+            double beta_old = beta;
+            size_t counter=0;
+            double lambda = alpha/beta;
+
+            // square of the S matrix contains all eigenvalues
+            // of Phi^T*Phi = (VSU^T)(USV^T) = VS^2V^T = S^2
+            const vec eval = S.array().pow(2);
+
+
+            if (verbose) printf("%9s  %9s  %9s  %9s  %9s  %9s\n","del alpha", "del beta", "alpha", "beta", "lambda", "gamma");
+            // D is a filter factors matrix
+            mat D(S.rows(),S.rows());
+            D.setZero();
+            int outprec=config.get<int>("OUTPREC");
+
+            while (test1>EPS2 || test2>EPS2) {
+
+                for (long int i=0; i<S.rows(); ++i) {
+                    double s = S(i,0);
+                    if (s>std::numeric_limits<double>::min())
+                        D(i,i) = (s)/(s*s+lambda);
+                    else
+                        D(i,i) = 0.0;
+                }
+                // regularised least square problem (Tikhonov Regularization):
+                vec m_n = V*D*U.transpose()*T;
+
+                double gamma = 0.0;
+                for (long int j=0; j<S.rows(); j++) {
+                    // exlude eigenvalues which are smaller than ~1e-10
+                    // for numerical stability b/c matrix is not regularized
+                    if (beta*eval(j) > std::numeric_limits<double>::min()) {
+                        gamma += (beta*eval(j))/(beta*eval(j)+alpha);
+                    }
+                }
+                alpha = gamma / m_n.dot(m_n);
+
+                double temp = (T-Phi*m_n).dot(T-Phi*m_n);
+                beta = (static_cast<double>(N)-gamma)/temp;
+
+                test1 = fabs(alpha_old-alpha)/alpha;
+                test2 = fabs(beta_old-beta)/beta;
+                if (verbose) printf("%.3e  %.3e  %.3e  %.3e  %.3e  %.3e\n",test1, test2,alpha, beta, alpha/beta, gamma);
+
+                lambda = alpha/beta;
+                alpha_old = alpha;
+                beta_old = beta;
+
+                if (++counter>maxsteps) {
+                    convergence_status = 0;
+                    break;
+                }
+            }
+            if (convergence_status && verbose) std::cout << "Number of steps for convergence: " + to_string(counter,outprec) << std::endl;
+            if (!convergence_status && verbose) std::cout << "Max number of steps reached: " + to_string(counter,outprec) << std::endl;
+            return convergence_status;
+
+        }
+};
+#endif
+
diff --git a/ekm.h b/ekm.h
new file mode 100644
index 0000000000000000000000000000000000000000..5f60bc487b52622259d5fc2a77e7a592ccdb4c35
--- /dev/null
+++ b/ekm.h
@@ -0,0 +1,50 @@
+#ifndef EKM_H
+#define EKM_H
+
+#include "../LIBS/Eigen/Dense"
+#include "../CORE/typedefs.h"
+
+template <typename K>
+class EKM {
+    private:
+        K kernel;
+    public:
+        Eigen::MatrixXd KK;
+        template <typename T>
+            EKM(T &t):
+                kernel(t)
+    {}
+        void configure(aeds_type &basis) {
+            // KK is temp matrix
+            KK = Eigen::MatrixXd(basis.size(), basis.size());
+
+            for (long int i=0; i<KK.rows(); i++) {
+                for (long int j=0; j<KK.cols(); j++) {
+                    double val = kernel(basis.col(i),basis.col(j));
+                    KK(i,j) = val;
+                }
+            }
+
+            // KK is a kernel matrix
+            // we compute cholesky for KK^-1
+            // then KK = L^-1
+            KK = KK.inverse();
+            Eigen::LLT<Eigen::MatrixXd> llt(KK);
+            KK = llt.matrixL();
+            KK.transposeInPlace();
+        }
+
+        void project(phi_type &Phi) {
+            for (long int i=0; i<Phi.rows(); ++i) {
+                Phi.row(i)= KK*Phi.row(i).transpose();
+            }
+        }
+        aed_type project(aed_rtype aed, aeds_type &basis) {
+            aed_type temp(basis.size());
+            for (long int b=0; b<basis.cols(); b++) {
+                temp(b) = kernel(aed,basis[b]);
+            }
+            return  KK*temp;
+        }
+};
+#endif
diff --git a/functions/basis_functions/bf_all.h b/functions/basis_functions/bf_all.h
new file mode 100644
index 0000000000000000000000000000000000000000..6cef8e3498dc1527d5091e2cc9f4ab3eddcceb45
--- /dev/null
+++ b/functions/basis_functions/bf_all.h
@@ -0,0 +1,2 @@
+#include "bf_linear.h"
+#include "bf_polynomial2.h"
diff --git a/functions/basis_functions/bf_base.cpp b/functions/basis_functions/bf_base.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..82eb1340d45bfd644bfdc9ce1291dffd12c74a1f
--- /dev/null
+++ b/functions/basis_functions/bf_base.cpp
@@ -0,0 +1,2 @@
+#include "bf_base.h"
+BF_Base::~BF_Base() {}
diff --git a/functions/basis_functions/bf_base.h b/functions/basis_functions/bf_base.h
new file mode 100644
index 0000000000000000000000000000000000000000..d76cd7194c918aaccedf85cf9f6ef1e8c3ae9384
--- /dev/null
+++ b/functions/basis_functions/bf_base.h
@@ -0,0 +1,18 @@
+#ifndef BASIS_FUNCTIONS_H
+#define BASIS_FUNCTIONS_H
+
+#include "../../../CORE/typedefs.h"
+#include "../function_base.h"
+
+#include <iostream>
+
+/** \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_rctype& )=0;
+    virtual double fpredict(const t_type &, const fd_type &,
+            const aed_rctype& , const size_t )=0;
+    virtual force_type fpredict(const t_type &, const fd_type &,
+            const aed_rctype& )=0;
+};
+#endif
diff --git a/functions/basis_functions/bf_linear.cpp b/functions/basis_functions/bf_linear.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..714bab185c773131fdc41233e8c7c64fd5430fd3
--- /dev/null
+++ b/functions/basis_functions/bf_linear.cpp
@@ -0,0 +1,25 @@
+#include "bf_linear.h"
+
+Registry<Function_Base>::Register<BF_Linear> BF_Linear_1( "BF_Linear" );
+Registry<Function_Base,Config&>::Register<BF_Linear> BF_Linear_2( "BF_Linear" );
+
+
+BF_Linear::BF_Linear() {}
+BF_Linear::BF_Linear(const Config &c): Function_Base(c) {}
+std::string BF_Linear::get_label() {
+    return label;
+}
+double BF_Linear::epredict(const t_type &weights, const aed_rctype& aed)
+{
+    return weights.transpose()*aed;
+}
+double BF_Linear::fpredict(const t_type &weights, const fd_type &fdij,
+        const aed_rctype& , const size_t k)
+{
+    return -fdij.col(k).transpose() * weights;
+}
+force_type BF_Linear::fpredict(const t_type &weights, const fd_type &fdij,
+        const aed_rctype& )
+{
+    return -fdij.transpose() * weights;
+}
diff --git a/functions/basis_functions/bf_linear.h b/functions/basis_functions/bf_linear.h
new file mode 100644
index 0000000000000000000000000000000000000000..6355fbbcb3a7b832ab0f62663400e6cc1471e764
--- /dev/null
+++ b/functions/basis_functions/bf_linear.h
@@ -0,0 +1,18 @@
+#ifndef BF_LINEAR_H
+#define BF_LINEAR_H
+#include "bf_base.h"
+
+/** Linear basis function */
+struct BF_Linear: public virtual BF_Base
+{
+    BF_Linear();
+    BF_Linear(const Config &c);
+    std::string get_label();
+    std::string label = "BF_Linear";
+    double epredict(const t_type &weights, const aed_rctype& aed);
+    double fpredict(const t_type &weights, const fd_type &fdij,
+            const aed_rctype& aedi, const size_t k);
+    force_type fpredict(const t_type &weights, const fd_type &fdij,
+            const aed_rctype& aedi);
+};
+#endif
diff --git a/functions/basis_functions/bf_polynomial2.cpp b/functions/basis_functions/bf_polynomial2.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..be94802d700a99ea3b0e0771f216e07a2a67964b
--- /dev/null
+++ b/functions/basis_functions/bf_polynomial2.cpp
@@ -0,0 +1,52 @@
+#include "bf_polynomial2.h"
+
+Registry<Function_Base>::Register<BF_Polynomial2> BF_Polynomial2_1( "BF_Polynomial2" );
+Registry<Function_Base,Config&>::Register<BF_Polynomial2> BF_Polynomial2_2( "BF_Polynomial2" );
+
+BF_Polynomial2::BF_Polynomial2() {}
+BF_Polynomial2::BF_Polynomial2(const Config &c): Function_Base(c)
+{}
+std::string BF_Polynomial2::get_label()
+{
+    return label;
+}
+double BF_Polynomial2::epredict(const t_type &weights, const aed_rctype& aed)
+{
+    size_t b=0;
+    double res=0.0;
+    for (long int i=0; i<aed.size(); ++i) {
+        for (long int ii=i; ii<aed.size(); ++ii) {
+            res+= weights(b++)*aed(i)*aed(ii);
+        }
+    }
+    return res;
+}
+double BF_Polynomial2::fpredict(const t_type &weights, const fd_type &fdij,
+        const aed_rctype& aedi, const size_t k)
+{
+    double res=0.0;
+    size_t b=0;
+    for (long int i=0; i<fdij.rows(); ++i) {
+        for (long int ii=i; ii<fdij.rows(); ++ii) {
+            res -= weights(b)*(fdij(i,k)*aedi(ii) + fdij(ii,k)*aedi(i));
+            b++;
+        }
+    }
+    return res;
+}
+force_type BF_Polynomial2::fpredict(const t_type &weights, const fd_type &fdij,
+        const aed_rctype& aedi)
+{
+    force_type v(3);
+    v.setZero();
+    size_t b=0;
+    for (long int i=0; i<fdij.rows(); ++i) {
+        for (long int ii=i; ii<fdij.rows(); ++ii) {
+            for (size_t k=0; k<3; ++k) {
+                v(k) -= weights(b)*(fdij(i,k)*aedi(ii) + fdij(ii,k)*aedi(i));
+            }
+            b++;
+        }
+    }
+    return v;
+}
diff --git a/functions/basis_functions/bf_polynomial2.h b/functions/basis_functions/bf_polynomial2.h
new file mode 100644
index 0000000000000000000000000000000000000000..65f421292f98dd45b561dcefc26ab60091aea188
--- /dev/null
+++ b/functions/basis_functions/bf_polynomial2.h
@@ -0,0 +1,18 @@
+#ifndef BF_POLYNOMIAL2_H
+#define BF_POLYNOMIAL2_H
+#include "bf_base.h"
+
+/** Polynomial order 2 basis function with c=0 */
+struct BF_Polynomial2: public virtual BF_Base
+{
+    BF_Polynomial2();
+    BF_Polynomial2(const Config &c);
+    std::string label = "BF_Polynomial2";
+    std::string get_label();
+    double epredict(const t_type &weights, const aed_rctype& aed);
+    double fpredict(const t_type &weights, const fd_type &fdij,
+            const aed_rctype& aedi, const size_t k);
+    force_type fpredict(const t_type &weights, const fd_type &fdij,
+            const aed_rctype& aedi);
+};
+#endif
diff --git a/functions/function_base.cpp b/functions/function_base.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..20f43072a83025e51248f352e59ef08d56d08874
--- /dev/null
+++ b/functions/function_base.cpp
@@ -0,0 +1,2 @@
+#include "function_base.h"
+
diff --git a/functions/function_base.h b/functions/function_base.h
new file mode 100644
index 0000000000000000000000000000000000000000..66d5823e30b56bd494aa2974d040d8f3879e1fbc
--- /dev/null
+++ b/functions/function_base.h
@@ -0,0 +1,49 @@
+#ifndef FUNCTION_BASE_h
+#define FUNCTION_BASE_h
+
+#include "../../CORE/registry.h"
+#include "../../CORE/typedefs.h"
+#include "../../CORE/config/config.h"
+
+#include <iomanip>
+#include <iostream>
+#include <limits>
+#include <vector>
+
+/** 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_rctype& , const aed_rctype& ) { return 0.0; };
+        virtual aed_type derivative(const aed_rctype& , const aed_rctype& ) { return aed_type(); };
+        virtual double prime(const aed_rctype& , const aed_rctype& ,
+                const aed_rctype& ) { return 0.0; }
+        virtual void set_basis(const aeds_type ) {};
+
+        /** \brief Return label of this object */
+        virtual std::string get_label()=0;
+        virtual double epredict(const t_type &, const aed_rctype& )=0;
+        virtual double fpredict(const t_type &, const fd_type &,
+                const aed_rctype&, const size_t )=0;
+        virtual force_type fpredict(const t_type &, const fd_type &,
+                const aed_rctype& )=0;
+};
+template<> inline Registry<Function_Base>::Map Registry<Function_Base>::registry{};
+template<> inline Registry<Function_Base,Config&>::Map Registry<Function_Base,Config&>::registry{};
+#endif
diff --git a/functions/kernels/kern_all.h b/functions/kernels/kern_all.h
new file mode 100644
index 0000000000000000000000000000000000000000..1a8704ee013a3cac753d30f69513ee792c289148
--- /dev/null
+++ b/functions/kernels/kern_all.h
@@ -0,0 +1,7 @@
+#include "kern_base.h"
+#include "kern_linear.h"
+#include "kern_rbf.h"
+#include "kern_lq.h"
+#include "kern_polynomial.h"
+#include "kern_sigmoid.h"
+#include "kern_quadratic.h"
diff --git a/functions/kernels/kern_base.cpp b/functions/kernels/kern_base.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..71791559c61fba8b3f417cd77a2a1bfaff8cd2e6
--- /dev/null
+++ b/functions/kernels/kern_base.cpp
@@ -0,0 +1,36 @@
+#include "kern_base.h"
+
+Kern_Base::~Kern_Base() {}
+void Kern_Base::set_basis(const aeds_type b)
+{
+    basis = b;
+}
+double Kern_Base::epredict(const t_type &kweights, const aed_rctype &aed)
+{
+    double energy=0;
+    for (long int 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_rctype &aedi, const size_t k)
+{
+    double res=0.0;
+    for (long int b=0; b<basis.cols(); ++b) {
+        res -= kweights[b]*(*this).prime(basis.col(b),aedi,fdij.col(k));
+    }
+    return res;
+}
+force_type Kern_Base::fpredict(const t_type &kweights, const fd_type &fdij,
+        const aed_rctype &aedi)
+{
+    force_type v(3);
+    v.setZero();
+    for (long int 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.col(k));
+        }
+    }
+    return v;
+}
diff --git a/functions/kernels/kern_base.h b/functions/kernels/kern_base.h
new file mode 100644
index 0000000000000000000000000000000000000000..4a620487860edda9f54037f46363b1195f2f2a35
--- /dev/null
+++ b/functions/kernels/kern_base.h
@@ -0,0 +1,58 @@
+#ifndef KERN_BASE_H
+#define KERN_BASE_H
+
+#include "../../../CORE/typedefs.h"
+#include "../function_base.h"
+
+#include <iostream>
+
+/** \brief Abstract class to be used as a base for all kernels.
+ *
+ *  - b = basis vector
+ *  - af = atomic energy descriptor
+ *  - ff = force descriptor
+ *  - all derivatives are defined wrt to the second argument
+ */
+class Kern_Base: public virtual Function_Base {
+    public:
+        aeds_type basis;
+        virtual ~Kern_Base();
+
+        /**
+         * Calculate kernel value given two vectors
+         *
+         * - b = basis vector
+         * - af = atomic energy descriptor
+         */
+        virtual double operator() (const aed_rctype &b, const aed_rctype &af)=0;
+
+        /**
+         * Calculate the kernel derivative wrt to the second argument
+         *
+         * - b = basis vector
+         * - af = atomic energy descriptor
+         */
+        virtual aed_type derivative(const aed_rctype &b, const aed_rctype &af)=0;
+
+        /**
+         * Calculate inner product of the kernel derivative
+         * wrt to the second argument (b wrt af) with the force descriptor
+         *
+         * - b = basis vector
+         * - af = atomic energy descriptor
+         * - ff = force descriptor (TODO i-th dir component of it)
+         *   TODO consider calculating all 3-directions at once
+         */
+        virtual double prime(const aed_rctype &b, const aed_rctype &af,
+                const aed_rctype &ff)=0;
+
+        /** \brief Set basis for calculations */
+        virtual void set_basis(const aeds_type b);
+
+        virtual double epredict(const t_type &kweights, const aed_rctype &aed);
+        virtual double fpredict(const t_type &kweights, const fd_type &fdij,
+                const aed_rctype &aedi, const size_t k);
+        virtual force_type fpredict(const t_type &kweights, const fd_type &fdij,
+                const aed_rctype &aedi);
+};
+#endif
diff --git a/functions/kernels/kern_linear.cpp b/functions/kernels/kern_linear.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3c80fb7579e4a0b88aae38e5e3ab8d7c8b41c5d0
--- /dev/null
+++ b/functions/kernels/kern_linear.cpp
@@ -0,0 +1,40 @@
+#include "kern_linear.h"
+
+Registry<Function_Base>::Register<Kern_Linear> Kern_Linear_1( "Kern_Linear" );
+Registry<Function_Base,Config&>::Register<Kern_Linear> Kern_Linear_2( "Kern_Linear" );
+
+Kern_Linear::Kern_Linear() {}
+Kern_Linear::Kern_Linear (const Config &c):
+    Function_Base(c)
+{}
+double Kern_Linear::operator() (const aed_rctype& b, const aed_rctype& af)
+{
+    return b.transpose()*af;;
+}
+aed_type Kern_Linear::derivative(const aed_rctype& b, const aed_rctype&)
+{
+    return b;
+}
+double Kern_Linear::prime(const aed_rctype& b, const aed_rctype& , const aed_rctype& ff)
+{
+    // return derivative(b,af).transpose() * ff;
+    return b.transpose() * ff;
+}
+std::string Kern_Linear::get_label()
+{
+    return label;
+}
+double Kern_Linear::epredict(const t_type &weights, const aed_rctype& aed)
+{
+    return weights.transpose()*aed;
+}
+double Kern_Linear::fpredict(const t_type &weights, const fd_type &fdij,
+        const aed_rctype& , const size_t k)
+{
+    return -fdij.col(k).transpose() * weights;
+}
+force_type Kern_Linear::fpredict(const t_type &weights, const fd_type &fdij,
+        const aed_rctype& )
+{
+    return -fdij.transpose() * weights;
+}
diff --git a/functions/kernels/kern_linear.h b/functions/kernels/kern_linear.h
new file mode 100644
index 0000000000000000000000000000000000000000..cbad9db39c5f008bd10ff61b5bfe7e966143488d
--- /dev/null
+++ b/functions/kernels/kern_linear.h
@@ -0,0 +1,36 @@
+#ifndef KERN_LINEAR_H
+#define KERN_LINEAR_H
+#include "kern_base.h"
+/**
+ * Linear kernel also knows as dot product kernel
+ *
+ * Defined for two vectors **x** and **y**:
+ *
+ * \f[
+ * K(\mathbf{x}, \mathbf{y}) = \mathbf{x}^T \mathbf{y} = \sum_i x^{(i)} y^{(i)}
+ * \f]
+ *
+ *  @see Kern_Base BF_Linear
+ */
+class Kern_Linear :  public virtual Kern_Base {
+    public:
+        Kern_Linear ();
+        Kern_Linear (const Config &c);
+        std::string get_label();
+
+        /**
+         * Label used for this class
+         */
+        std::string label = "Kern_Linear";
+        double operator() (const aed_rctype& b, const aed_rctype& af);
+        aed_type derivative(const aed_rctype& b, const aed_rctype& );
+        double prime(const aed_rctype& b, const aed_rctype& af, const aed_rctype& ff);
+        void set_basis(const aeds_type ) {}
+
+        double epredict(const t_type &weights, const aed_rctype& aed);
+        double fpredict(const t_type &weights, const fd_type &fdij,
+                const aed_rctype& aedi, const size_t k);
+        force_type fpredict(const t_type &weights, const fd_type &fdij,
+                const aed_rctype& aedi);
+};
+#endif
diff --git a/functions/kernels/kern_lq.cpp b/functions/kernels/kern_lq.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..82b3db722d0667921886de8f3dc1bf88f2e66ac3
--- /dev/null
+++ b/functions/kernels/kern_lq.cpp
@@ -0,0 +1,29 @@
+#include "kern_lq.h"
+
+Registry<Function_Base>::Register<Kern_LQ> Kern_LQ_1( "Kern_LQ" );
+Registry<Function_Base,Config&>::Register<Kern_LQ> Kern_LQ_2( "Kern_LQ" );
+
+Kern_LQ::Kern_LQ () {}
+Kern_LQ::Kern_LQ (const Config &c):
+    Function_Base(c)
+{
+    if (get_verbose()) std::cout << std::endl << label << std::endl;
+}
+double Kern_LQ::operator() (const aed_rctype& b, const aed_rctype& af)
+{
+    double x = b.transpose()*af;
+    return x*x + x;
+}
+aed_type Kern_LQ::derivative(const aed_rctype& b, const aed_rctype& af)
+{
+    double temp = 2.0 * b.transpose()*af;
+    return temp*b + b;
+}
+double Kern_LQ::prime(const aed_rctype& b, const aed_rctype& af, const aed_rctype& ff)
+{
+    return derivative(b, af).transpose()*ff;
+}
+std::string Kern_LQ::get_label()
+{
+    return label;
+}
diff --git a/functions/kernels/kern_lq.h b/functions/kernels/kern_lq.h
new file mode 100644
index 0000000000000000000000000000000000000000..9d5bd19b3ffb3684715b73c9865f3b03f042bffe
--- /dev/null
+++ b/functions/kernels/kern_lq.h
@@ -0,0 +1,32 @@
+#ifndef KERN_LQ_H
+#define KERN_LQ_H
+
+#include "kern_base.h"
+/**
+ * Linear + Quadratic kernel
+ *
+ * Defined for two vectors **x** and **y**:
+ *
+ * \f[
+ * K(\mathbf{x}, \mathbf{y}) = \mathbf{x}^T \mathbf{y} +
+ * \Big(\mathbf{x}^T \mathbf{y} \Big)^2
+ * = \sum_i x^{(i)} y^{(i)} + \Big(\sum_i x^{(i)} y^{(i)} \Big)^2
+ * \f]
+ *
+ *  @see Kern_Base Kern_Linear Kern_Quadratic
+ */
+class Kern_LQ : public virtual Kern_Base {
+    public:
+        Kern_LQ ();
+        Kern_LQ (const Config &c);
+        /**
+         * Label used for this class
+         */
+        std::string label = "Kern_LQ";
+        double operator() (const aed_rctype& b, const aed_rctype& af);
+        aed_type derivative(const aed_rctype& b, const aed_rctype& af);
+        double prime(const aed_rctype& b, const aed_rctype& af, const aed_rctype& ff);
+        std::string get_label();
+
+};
+#endif
diff --git a/functions/kernels/kern_polynomial.cpp b/functions/kernels/kern_polynomial.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..01dcf5e21f8f69e3ae29f414211881b405c1beb7
--- /dev/null
+++ b/functions/kernels/kern_polynomial.cpp
@@ -0,0 +1,31 @@
+#include "kern_polynomial.h"
+
+Registry<Function_Base>::Register<Kern_Polynomial> Kern_Polynomial_1( "Kern_Polynomial" );
+Registry<Function_Base,Config&>::Register<Kern_Polynomial> Kern_Polynomial_2( "Kern_Polynomial" );
+
+Kern_Polynomial::Kern_Polynomial () {}
+Kern_Polynomial::Kern_Polynomial (const Config &c):
+    Function_Base(c),
+    d(c.get<int>("MPARAMS",0)),
+    gamma(c.get<double>("MPARAMS",1)),
+    c(c.get<double>("MPARAMS",2))
+{
+    if (get_verbose()) std::cout << std::endl << label << " | degree: " << d
+        <<" | gamma: " << gamma << " | c: " << c << std::endl;
+}
+double Kern_Polynomial::operator() (const aed_rctype& b, const aed_rctype& af)
+{
+    return std::pow(gamma*(b.dot(af))+c,d);
+}
+aed_type Kern_Polynomial::derivative(const aed_rctype& b, const aed_rctype& af)
+{
+    return  d*gamma*std::pow(gamma*(b.dot(af))+c,d-1)*b;
+}
+double Kern_Polynomial::prime(const aed_rctype& b, const aed_rctype& af, const aed_rctype& ff)
+{
+    return derivative(b, af).transpose()*ff;
+}
+std::string Kern_Polynomial::get_label()
+{
+    return label;
+}
diff --git a/functions/kernels/kern_polynomial.h b/functions/kernels/kern_polynomial.h
new file mode 100644
index 0000000000000000000000000000000000000000..02bf26262d3d044c338a2e1fc7e1e9a8b4d853b9
--- /dev/null
+++ b/functions/kernels/kern_polynomial.h
@@ -0,0 +1,34 @@
+#ifndef KERN_POLYNOMIAL_H
+#define KERN_POLYNOMIAL_H
+#include "kern_base.h"
+/** \brief Polynomial kernel.
+ *
+ * Defined for two vectors **x** and **y**:
+ *
+ * \f[
+ * K(\mathbf{x}, \mathbf{y}) = \big( \gamma*\mathbf{x}^T \mathbf{y} +
+ * \mathrm{c} \big)^{\mathrm{d}}
+ * \f]
+ *
+ *  Required Config key: \ref MPARAMS <int> d  <double> \f$ \gamma \f$  c
+ *
+ *  @see Kern_Base
+ */
+class Kern_Polynomial : public virtual Kern_Base {
+    public:
+        Kern_Polynomial ();
+        Kern_Polynomial (const Config &c);
+        int d; // power
+        double gamma;   // scalling
+        double c;   // shift
+        /**
+         * Label used for this class
+         */
+        std::string label = "Kern_Polynomial";
+        std::string get_label();
+        double operator() (const aed_rctype& b, const aed_rctype& af);
+        aed_type derivative(const aed_rctype& b, const aed_rctype& af);
+        double prime(const aed_rctype& b, const aed_rctype& af, const aed_rctype& ff);
+
+};
+#endif
diff --git a/functions/kernels/kern_quadratic.cpp b/functions/kernels/kern_quadratic.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0d61f734710c1a39e86fa23ab88d2c9a5858a214
--- /dev/null
+++ b/functions/kernels/kern_quadratic.cpp
@@ -0,0 +1,32 @@
+#include "kern_quadratic.h"
+
+Registry<Function_Base>::Register<Kern_Quadratic> Kern_Quadratic_1( "Kern_Quadratic" );
+Registry<Function_Base,Config&>::Register<Kern_Quadratic> Kern_Quadratic_2( "Kern_Quadratic" );
+
+Kern_Quadratic::Kern_Quadratic () {}
+Kern_Quadratic::Kern_Quadratic (const Config &c): 
+    Function_Base(c)
+{
+    if (get_verbose()) std::cout << std::endl << label << std::endl;
+}
+double Kern_Quadratic::operator() (const aed_rctype& b, const aed_rctype& af)
+{
+    double x = b.transpose()*af;
+    return  x*x;
+}
+aed_type Kern_Quadratic::derivative(const aed_rctype& b, const aed_rctype& af)
+{
+    double temp = 2.0 * b.transpose()*af;
+    return b*temp;;
+}
+double Kern_Quadratic::prime(const aed_rctype& b, const aed_rctype& af, const aed_rctype& ff)
+{
+    //return dot(derivative(b, af), ff);
+    // it is faster this way
+    double scale = 2.0*b.transpose()*af;
+    return scale*ff.transpose()*b;
+}
+std::string Kern_Quadratic::get_label()
+{
+    return label;
+}
diff --git a/functions/kernels/kern_quadratic.h b/functions/kernels/kern_quadratic.h
new file mode 100644
index 0000000000000000000000000000000000000000..d9e886bb54253afc4ee918bf98319f117887cc45
--- /dev/null
+++ b/functions/kernels/kern_quadratic.h
@@ -0,0 +1,30 @@
+#ifndef KERN_QUADRATIC_H
+#define KERN_QUADRATIC_H
+#include "kern_base.h"
+/**
+ * Quadratic kernel - special case of 2nd order polynomial kernel
+ *
+ * Defined for two vectors **x** and **y**:
+ *
+ * \f[
+ * K(\mathbf{x}, \mathbf{y}) = \Big( \mathbf{x}^T \mathbf{y} \Big)^2
+ * = \Big( \sum_i x^{(i)} y^{(i)} \Big)^2
+ * \f]
+ *
+ *  @see Kern_Base
+ */
+class Kern_Quadratic : public virtual Kern_Base {
+    public:
+        Kern_Quadratic ();
+        Kern_Quadratic (const Config &c);
+        /**
+         * Label used for this class
+         */
+        std::string label = "Kern_Quadratic";
+        double operator() (const aed_rctype& b, const aed_rctype& af);
+        aed_type derivative(const aed_rctype& b, const aed_rctype& af);
+        double prime(const aed_rctype& b, const aed_rctype& af, const aed_rctype& ff);
+        std::string get_label();
+
+};
+#endif
diff --git a/functions/kernels/kern_rbf.cpp b/functions/kernels/kern_rbf.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a96a9352feb4e8462f1f5ad8fd1eb6c4428ad93d
--- /dev/null
+++ b/functions/kernels/kern_rbf.cpp
@@ -0,0 +1,31 @@
+#include "kern_rbf.h"
+
+Registry<Function_Base>::Register<Kern_RBF> Kern_RBF_1( "Kern_RBF" );
+Registry<Function_Base,Config&>::Register<Kern_RBF> Kern_RBF_2( "Kern_RBF" );
+
+Kern_RBF::Kern_RBF () {}
+Kern_RBF::Kern_RBF (const Config &c):
+    Function_Base(c),
+    sigma(c.get<double>("MPARAMS")),
+    gamma(1.0/(2.0*sigma*sigma))
+{
+    if (get_verbose()) std::cout << std::endl << label << " | sigma: " << sigma
+        << " | gamma: " << gamma << std::endl;
+}
+double Kern_RBF::operator() (const aed_rctype& b, const aed_rctype& af)
+{
+    const aed_type v = af-b;
+    return exp(-gamma*(v.dot(v)));
+}
+aed_type Kern_RBF::derivative(const aed_rctype& b, const aed_rctype& af)
+{
+    return  -2.0*gamma*(af-b)*(*this)(af,b);
+}
+double Kern_RBF::prime(const aed_rctype& b, const aed_rctype& af, const aed_rctype& ff)
+{
+    return derivative(b, af).transpose()*ff;
+}
+std::string Kern_RBF::get_label()
+{
+    return label;
+}
diff --git a/functions/kernels/kern_rbf.h b/functions/kernels/kern_rbf.h
new file mode 100644
index 0000000000000000000000000000000000000000..d91c493ffa0ed3a5f89bc17471968af41dc40419
--- /dev/null
+++ b/functions/kernels/kern_rbf.h
@@ -0,0 +1,35 @@
+#ifndef KERN_RBF_H
+#define KERN_RBF_H
+#include "kern_base.h"
+/** \brief Radial Basis Function kernel
+ *
+ * Defined for two vectors **x** and **y**:
+ *
+ * \f[
+ * K(\mathbf{x}, \mathbf{y}) = \exp\Big( -\frac{||\mathbf{x}-\mathbf{y}||^2}
+ * {2\sigma^2} \Big)
+ * \f]
+ *
+ *  Required Config key: \ref MPARAMS <double> \f$ \sigma \f$
+ *
+ *  @see Kern_Base Kern_Linear Kern_Quadratic
+ */
+class Kern_RBF : public virtual Kern_Base {
+    public:
+        double sigma;
+        /**
+         * Label used for this class
+         */
+        Kern_RBF ();
+        Kern_RBF (const Config &c);
+        std::string label = "Kern_RBF";
+        double operator() (const aed_rctype& b, const aed_rctype& af);
+        aed_type derivative(const aed_rctype& b, const aed_rctype& af);
+        double prime(const aed_rctype& b, const aed_rctype& af, const aed_rctype& ff);
+        std::string get_label();
+
+    private:
+        double gamma;
+
+};
+#endif
diff --git a/functions/kernels/kern_sigmoid.cpp b/functions/kernels/kern_sigmoid.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6f0c9470844d9b359ff2394250836db03d19f1f2
--- /dev/null
+++ b/functions/kernels/kern_sigmoid.cpp
@@ -0,0 +1,30 @@
+#include "kern_sigmoid.h"
+
+Registry<Function_Base>::Register<Kern_Sigmoid> Kern_Sigmoid_1( "Kern_Sigmoid" );
+Registry<Function_Base,Config&>::Register<Kern_Sigmoid> Kern_Sigmoid_2( "Kern_Sigmoid" );
+
+Kern_Sigmoid::Kern_Sigmoid () {}
+Kern_Sigmoid::Kern_Sigmoid (const Config &c):
+    Function_Base(c),
+    gamma(c.get<double>("MPARAMS",0)),
+    c(c.get<double>("MPARAMS",1))
+{
+    if (get_verbose()) std::cout << std::endl << label << " | gamma: " << gamma 
+        << " | c: " << c << std::endl;
+}
+double Kern_Sigmoid::operator() (const aed_rctype& b, const aed_rctype& af)
+{
+    return std::tanh(gamma*(b.dot(af))+c);
+}
+aed_type Kern_Sigmoid::derivative(const aed_rctype& b, const aed_rctype& af)
+{
+    return gamma*(1.0-std::pow((*this)(af,b),2))*b;
+}
+double Kern_Sigmoid::prime(const aed_rctype& b, const aed_rctype& af, const aed_rctype& ff)
+{
+    return derivative(b, af).transpose()*ff;
+}
+std::string Kern_Sigmoid::get_label()
+{
+    return label;
+}
diff --git a/functions/kernels/kern_sigmoid.h b/functions/kernels/kern_sigmoid.h
new file mode 100644
index 0000000000000000000000000000000000000000..600251af6dee361421fc755d841b72ad45033165
--- /dev/null
+++ b/functions/kernels/kern_sigmoid.h
@@ -0,0 +1,33 @@
+#ifndef KERN_SIGMOID_H
+#define KERN_SIGMOID_H
+#include "kern_base.h"
+/** \brief Sigmoid kernel.
+ *
+ * Defined for two vectors **x** and **y**:
+ *
+ * \f[
+ * K(\mathbf{x}, \mathbf{y}) = \tanh\big( \gamma*\mathbf{x}^T \mathbf{y}
+ * + \mathrm{c} \big)
+ * \f]
+ *
+ *  Required Config key: \ref MPARAMS <double> \f$ \gamma \f$  c
+ *
+ *  @see Kern_Base
+ */
+class Kern_Sigmoid : public virtual Kern_Base {
+    public:
+        double gamma;   // scalling
+        double c;   // shift
+        /**
+         * Label used for this class
+         */
+        Kern_Sigmoid ();
+        Kern_Sigmoid (const Config &c);
+        std::string label = "Kern_Sigmoid";
+        double operator() (const aed_rctype& b, const aed_rctype& af);
+        aed_type derivative(const aed_rctype& b, const aed_rctype& af);
+        double prime(const aed_rctype& b, const aed_rctype& af, const aed_rctype& ff);
+        std::string get_label();
+
+};
+#endif
diff --git a/linear_regressor.h b/linear_regressor.h
new file mode 100644
index 0000000000000000000000000000000000000000..feb69fdd5f6d9b984f3cb5b9402c9e68fc9e1b34
--- /dev/null
+++ b/linear_regressor.h
@@ -0,0 +1,111 @@
+#ifndef LINEAR_REGRESSOR_H
+#define LINEAR_REGRESSOR_H
+
+#include "../CORE/config/config.h"
+#include "../CORE/typedefs.h"
+#include "../LIBS/Eigen/Dense"
+#include "ea.h"
+#include <vector>
+class LinearRegressor {
+            // do training...
+            // In general there are 3 methods to solve least squares problem:
+            // 1) Normal equation -direct inverse - fast but least accurate:
+            //Eigen::MatrixXd I = Eigen::MatrixXd::Identity(Phi.cols(),Phi.cols());
+            //Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> PhiPhi = Phi.transpose()*Phi;
+            //weights = (PhiPhi+lambda*I).inverse() * Phi.transpose() * T;
+            // 2) QR - in between 1) and 3) in terms of speed and accuracy
+            //weights = Phi.colPivHouseholderQr().solve(T);
+            // 3) SVD - most accurate but the slowest
+            // w = V*D*S(*)*U^T*T
+            // where S(*) = S.asDiagonal().inverse()
+            // and D is a filter matrix with diagonal entires: S(i)^2/(S(i)^2+lambda)
+    public:
+        static void train(Config &config, const phi_type &Phi, const t_type &T,
+                t_type &weights, mat &Sigma) {
+            int verbose = config.get<int>("VERBOSE");
+
+            double lambda=config.get<double>("LAMBDA");
+
+            if (lambda == 0)
+                weights = Phi.bdcSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(T);
+            else {
+                double alpha = config.get<double>("ALPHA");
+                double beta = config.get<double>("BETA");
+                //Eigen::JacobiSVD<mat> svd(Phi, Eigen::ComputeThinU | Eigen::ComputeThinV);
+                Eigen::BDCSVD<mat> svd(Phi, Eigen::ComputeThinU | Eigen::ComputeThinV);
+                mat S =svd.singularValues();
+                const mat &U =svd.matrixU();
+                const mat &V =svd.matrixV();
+
+                // Filter out v.small singularvalues from S and its inverse
+                for (long int i=0; i<S.rows(); ++i) {
+                    if (S(i,0)<std::numeric_limits<double>::min()) {
+                        S(i,0) = 0.0;
+                    }
+                }
+
+                if (lambda<0) {
+                    EA ea(config);
+                    ea.run(Phi,T,S,U,V,alpha,beta);
+                    lambda=alpha/beta;
+
+                    mat P_inv(S.rows(),S.rows());
+                    P_inv.setZero();
+
+                    for (long int i=0; i<S.rows(); ++i) {
+                        double s = S(i,0);
+                        P_inv(i,i) = 1.0/(beta*s*s+alpha);
+                    }
+
+                    Sigma = V*P_inv*V.transpose();
+                    config.remove("ALPHA");
+                    config.remove("BETA");
+                    config.add("ALPHA",alpha);
+                    config.add("BETA",beta);
+                }
+                mat D(S.rows(),S.rows());
+                D.setZero();
+                for (long int i=0; i<S.rows(); ++i) {
+                    double s = S(i,0);
+                    if (s>std::numeric_limits<double>::min())
+                        D(i,i) = (s)/(s*s+lambda);
+                    else
+                        D(i,i) = 0.0;
+                }
+                if (verbose) std::cout << std::endl << "REG LAMBDA: " << lambda << std::endl;
+                weights = V*D*U.transpose()*T;
+                //weights = beta*Sigma*V*S.asDiagonal()*U.transpose()*T;
+                // add Sigma to the config file
+                dump_sigma(config,Sigma);
+            }
+        }
+        static void dump_sigma(Config &config, mat &Sigma) {
+            vec Sigma_as_vector(Eigen::Map<vec>(Sigma.data(), Sigma.cols()*Sigma.rows()));
+            config.add("SIGMA",Sigma.rows());
+            config.add<vec>("SIGMA",Sigma_as_vector);
+
+        }
+        static void read_sigma(Config &config, mat &Sigma) {
+            using vec=std::vector<double>;
+            // get N = number of rows or cols, Sigma is NxN
+            size_t N;
+            try {
+                N = config.get<size_t>("SIGMA");
+            }
+            catch (const std::runtime_error& error) {
+                throw std::runtime_error("Sigma matrix is not computed. \n\
+                        Hint: It is only computed for LAMBDA -1.\n");
+            }
+            // +1 as first number is N - a number of rows and cols
+            vec Sigma_as_vector(N*N+1);
+            // read sigma including N, it is sotred col by col
+            config.get<vec>("SIGMA",Sigma_as_vector);
+            Sigma_as_vector.erase(Sigma_as_vector.begin());
+            Sigma.resize(N,N);
+            for (size_t c=0; c<N; ++c)
+                for (size_t r=0; r<N; ++r)
+                    Sigma(r,c) = Sigma_as_vector.at(N*c+r);
+
+        }
+};
+#endif
diff --git a/m_base.h b/m_base.h
new file mode 100644
index 0000000000000000000000000000000000000000..4ecb44b1d7a415c2795d466a0f61fec80c5a6248
--- /dev/null
+++ b/m_base.h
@@ -0,0 +1,35 @@
+#ifndef M_BASE_H
+#define M_BASE_H
+
+#include "../LIBS/Eigen/Dense"
+#include "../CORE/typedefs.h"
+#include "functions/function_base.h"
+
+class M_Base {
+
+    public:
+        //Normaliser norm;    // TODO?
+        virtual ~M_Base() {};
+
+        /** \brief Predict local energy of an atom or bond energy.
+         *
+         *  The result depends on how aed is computed.
+         *
+         *  If it is computed between a pair of atoms than the result is a bond energy.
+         *
+         *  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_rctype &aed)=0;
+
+        /** \brief Predict force between a pair of atoms in a k-direction. */
+        virtual double fpredict(const fd_type &fdij, const aed_rctype &aedi, size_t k)=0;
+
+        /** \brief Predict force between a pair of atoms. */
+        virtual force_type fpredict(const fd_type &fdij,
+                const aed_rctype &aedi)=0;
+
+
+};
+//template<> inline Registry<M_Base,Function_Base&,Config&>::Map Registry<M_Base,Function_Base&,Config&>::registry{};
+#endif
diff --git a/m_blr_core.h b/m_blr_core.h
new file mode 100644
index 0000000000000000000000000000000000000000..dea3ea2250f6ba1829a73780aca314998417a73b
--- /dev/null
+++ b/m_blr_core.h
@@ -0,0 +1,73 @@
+#ifndef M_BLR_Core_H
+#define M_BLR_Core_H
+
+#include "linear_regressor.h"
+#include "../CORE/config/config.h"
+#include "functions/function_base.h"
+#include "functions/basis_functions/bf_base.h"
+#include "m_core.h"
+
+#include <iostream>
+
+template
+<class BF=Function_Base&>
+class M_BLR_Core: public virtual M_Core {
+    public:
+        Config &config;
+        mat Sigma;
+        BF bf;
+        M_BLR_Core(Config &c):
+            config(c),
+            bf(c)
+    {
+        static_assert(std::is_base_of<BF_Base, BF>::value,
+                "\nThe provided Basis function is not of a BasisFunction type.\n\
+                For BLR use basis functions defined in basis_functions.h.\n\
+                If you want to use this kernel functions use M_KRR class instead.\n\
+                e.g. Use BF_Linear instead of Kern_Linear\n.");
+
+        init();
+    }
+
+        M_BLR_Core(BF &bf, Config &c):
+            config(c),
+            bf(bf)
+    {
+        if (dynamic_cast<BF_Base*>(&bf) == nullptr)
+            throw std::invalid_argument("Provided object is not of BF_Base type");
+
+        //static_assert(std::is_same<Function_Base&, BF>::value,
+        //        "This constructor requires BF=Function_Base&\n");
+
+        init();
+    }
+        double predict(const rvec &v) const{
+            return bf.epredict(get_weights(),v);
+        };
+
+        void train(phi_type &Phi, const t_type &T) {
+            if(trained) {
+                throw std::runtime_error("This object is already trained!");
+            }
+            LinearRegressor::train(config,Phi, T,weights,Sigma);
+            trained=true;
+        }
+        t_type get_weights_uncertainty() const{
+            double lambda=config.get<double>("LAMBDA");
+            if(lambda >= 0) throw std::runtime_error(
+                    "Sigma matrix is only computed for LAMBDA < 0");
+            return Sigma.diagonal();
+        }
+
+    private:
+        void init() {
+            if (config.exist("WEIGHTS")) {
+                weights.resize(config.size("WEIGHTS"));
+                config.get<t_type>("WEIGHTS",weights);
+                trained=true;
+            }
+
+            M_Core::verbose=(config.get<int>("VERBOSE"));
+        }
+};
+#endif
diff --git a/m_core.h b/m_core.h
new file mode 100644
index 0000000000000000000000000000000000000000..1caa4b00da4379be1ec5c896352c98184202d2a0
--- /dev/null
+++ b/m_core.h
@@ -0,0 +1,28 @@
+#ifndef M_Core_H
+#define M_Core_H
+
+#include "../CORE/typedefs.h"
+
+class M_Core {
+    public:
+        int verbose;
+        bool trained=false;
+        t_type weights;
+
+        virtual ~M_Core() {}
+
+        virtual double predict(const rvec &v)const=0;
+        virtual void train(phi_type &Phi, const t_type &T)=0;
+        virtual t_type get_weights_uncertainty()const=0;
+
+        bool is_trained() const {
+            return trained;
+        }
+        const t_type &get_weights() const {
+            return weights;
+        }
+        void set_weights(const t_type w) {
+            weights=w;
+        }
+};
+#endif
diff --git a/m_krr_core.h b/m_krr_core.h
new file mode 100644
index 0000000000000000000000000000000000000000..3499aad027b3ccd01c04a062c82f24e3d69f5ea6
--- /dev/null
+++ b/m_krr_core.h
@@ -0,0 +1,81 @@
+#ifndef M_KRR_Core_H
+#define M_KRR_Core_H
+
+#include "linear_regressor.h"
+#include "../CORE/config/config.h"
+#include "functions/function_base.h"
+#include "functions/kernels/kern_base.h"
+#include "m_core.h"
+#include "ekm.h"
+
+#include <iostream>
+
+template
+<class K=Function_Base&>
+class M_KRR_Core: public virtual M_Core {
+    public:
+        Config &config;
+        mat Sigma;
+        K kernel;
+        EKM<K> ekm;
+        M_KRR_Core(Config &c):
+            config(c),
+            kernel(c),
+            ekm(c)
+    {
+        static_assert(std::is_base_of<Kern_Base, K>::value,
+                "\nThe provided Kernel K is not of a Kernel type.\n\
+                For KRR use kernels defined in kernel.h.\n\
+                e.g. Use Kern_Linear instead of BF_Linear\n.");
+
+        init();
+    }
+
+        M_KRR_Core(K &kernel, Config &c):
+            config(c),
+            kernel(kernel),
+            ekm(kernel)
+    {
+        if (dynamic_cast<Kern_Base*>(&kernel) == nullptr)
+            throw std::invalid_argument("Provided object is not of Kern_Base type");
+
+        init();
+    }
+        double predict(const rvec &v) const{
+            return kernel.epredict(get_weights(),v);
+        };
+
+        void train(phi_type &Phi, const t_type &T) {
+            if(trained) {
+                throw std::runtime_error("This object is already trained!");
+            }
+            if (kernel.get_label()!="Kern_Linear") {
+                ekm.project(Phi);
+            }
+            LinearRegressor::train(config,Phi, T,weights,Sigma);
+
+            if (kernel.get_label()!="Kern_Linear") {
+                //kernalize weights
+                weights = ekm.KK.transpose()*weights;
+            }
+            trained=true;
+        }
+        t_type get_weights_uncertainty() const{
+            double lambda=config.get<double>("LAMBDA");
+            if(lambda >= 0) throw std::runtime_error(
+                    "Sigma matrix is only computed for LAMBDA < 0");
+            return Sigma.diagonal();
+        }
+
+    private:
+        void init() {
+            if (config.exist("WEIGHTS")) {
+                weights.resize(config.size("WEIGHTS"));
+                config.get<t_type>("WEIGHTS",weights);
+                trained=true;
+            }
+
+            M_Core::verbose=(config.get<int>("VERBOSE"));
+        }
+};
+#endif