diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 0101c12e5c91f607f6b1e934c97ac818bbaf66b9..82881e8d9cc69bdfdaebc618ce8a2932706a29ab 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -49,6 +49,7 @@ add_executable(numerics ${OPENFPM_INIT_FILE} ${CUDA_SOURCES}
 #		level_set/closest_point/closest_point_unit_tests.cpp
 #		level_set/redistancing_Sussman/tests/redistancingSussman_unit_test.cpp
 #		level_set/redistancing_Sussman/tests/convergence_test.cpp
+		level_set/particle_cp/pcp_unit_tests.cpp
 		)
 
 add_dependencies(numerics ofpmmemory)
@@ -92,12 +93,14 @@ target_include_directories (numerics PUBLIC ${Boost_INCLUDE_DIRS})
 target_include_directories (numerics PUBLIC ${Vc_INCLUDE_DIR})
 target_include_directories (numerics PUBLIC ${ALPAKA_ROOT}/include)
 target_include_directories (numerics PUBLIC ${MPI_C_INCLUDE_DIRS})
+target_include_directories (numerics PUBLIC /datapot/lschulze/Applications/minter/src)
 if(EIGEN3_FOUND)
         target_include_directories (numerics PUBLIC /usr/local/include) 
 	target_include_directories (numerics PUBLIC ${EIGEN3_INCLUDE_DIR})
 endif()
 
 link_directories(${PARMETIS_ROOT} ${METIS_ROOT})
+link_directories(/datapot/lschulze/Applications/minter/build)
 target_link_libraries(numerics ${Boost_LIBRARIES})
 target_link_libraries(numerics ${HDF5_LIBRARIES})
 target_link_libraries(numerics ${PARMETIS_LIBRARIES})
@@ -105,6 +108,8 @@ target_link_libraries(numerics -L${LIBHILBERT_LIBRARY_DIRS} ${LIBHILBERT_LIBRARI
 target_link_libraries(numerics ${Vc_LIBRARIES})
 target_link_libraries(numerics ${MPI_C_LIBRARIES})
 target_link_libraries(numerics ${MPI_CXX_LIBRARIES})
+target_link_libraries(numerics minter)
+
 if(PETSC_FOUND)
 	target_include_directories (numerics PUBLIC ${PETSC_INCLUDES})
 	target_link_libraries(numerics ${PETSC_LIBRARIES})
@@ -129,8 +134,8 @@ if (TEST_COVERAGE)
 endif()
 
 ## Regression
-target_include_directories(numerics PUBLIC /Users/lschulze/minter/src/)
-target_link_libraries(numerics -L/Users/lschulze/minter/build minter)
+target_include_directories(numerics PUBLIC /datapot/lschulze/Applications/minter/src/)
+target_link_libraries(numerics -L/datapot/lschulze/Applications/minter/build minter)
 ##
 
 # Request that particles be built with -std=c++11
diff --git a/src/DCPSE/SupportBuilder.hpp b/src/DCPSE/SupportBuilder.hpp
index 7702d4345c21e0363a70dec8919634c89176a761..9cd3a8e8f4ac9190bb79034cba69615cc677df48 100644
--- a/src/DCPSE/SupportBuilder.hpp
+++ b/src/DCPSE/SupportBuilder.hpp
@@ -17,7 +17,8 @@
 enum support_options
 {
 	N_PARTICLES,
-	RADIUS
+	RADIUS,
+	AT_LEAST_N_PARTICLES
 };
 
 template<typename vector_type>
diff --git a/src/level_set/particle_cp/pcp_unit_test_helpfunctions.h b/src/level_set/particle_cp/pcp_unit_test_helpfunctions.h
new file mode 100644
index 0000000000000000000000000000000000000000..6f13bc7b694640a739393ccaeccb4eebd133c50f
--- /dev/null
+++ b/src/level_set/particle_cp/pcp_unit_test_helpfunctions.h
@@ -0,0 +1,196 @@
+#include <math.h>
+// This is a collection of helpfunctions that were used to run convergence tests and benchmarks for the ellipse/ellipsoid
+// in the particle closest point draft. Computing the theoretical closest points and distances from a given query point
+// is done using the first four functions which were adopted from David Eberly "Distance from a Point to an Ellipse, an 
+// Ellipsoid, or a Hyperellipsoid", 2013.
+//
+// Created by lschulze
+double GetRoot (double r0, double z0, double z1, double g)
+    {
+	const int maxIter = 100;
+        double n0 = r0*z0;
+        double s0 = z1 - 1;
+        double s1 = ( g < 0 ? 0 : sqrt(n0*n0+z1*z1) - 1 ) ;
+        double s = 0;
+        for ( int i = 0; i < maxIter; ++i ){
+	    if (i == (maxIter - 1)) std::cout<<"distance point ellipse algorithm did not converge."<<std::endl;
+            s = ( s0 + s1 ) / 2 ;
+            if ( s == s0 || s == s1 ) {break; }
+            double ratio0 = n0 /( s + r0 );
+            double ratio1 = z1 /( s + 1 );
+            g = ratio0*ratio0 + ratio1*ratio1 - 1 ;
+            if (g > 0) {s0 = s;} else if (g < 0) {s1 = s ;} else {break ;}
+        }
+        return s;
+    }
+
+double GetRoot(double r0, double r1, double z0, double z1, double z2, double g)
+{	
+	const int maxIter = 100;
+	double n0 = r0*z0;
+	double n1 = r1*z1;
+	double s0 = z2 - 1;
+	double s1 = (g < 0 ? 0 : sqrt(n0*n0 + n1*n1 + z2*z2) - 1) ;
+	double s = s;
+	for(int i = 0 ; i < maxIter ; ++i )
+	{
+		if (i == (maxIter - 1)) std::cout<<"distance point ellipse algorithm did not converge."<<std::endl;
+		s = ( s0 + s1 ) / 2 ;
+		if ( s == s0 || s == s1 ) {break; }
+		double ratio0 = n0 / ( s + r0 ); 
+		double ratio1 = n1 / ( s + r1 );
+		double ratio2 = z2 / ( s + 1 );
+		g = ratio0*ratio0 + ratio1*ratio1 +ratio2*ratio2 - 1;
+		if ( g > 0 ) { s0 = s ;} 
+		else if ( g < 0 ) { s1 = s ; }
+		else {break;}
+	}
+	return (s);
+}
+
+double DistancePointEllipse(double e0, double e1, double y0, double y1, double& x0, double& x1)
+    {
+        double distance;
+        if ( y1 > 0){
+            if ( y0 > 0){
+                double z0 = y0 / e0;
+                double z1 = y1 / e1;
+                double g = z0*z0+z1*z1 - 1;
+                if ( g != 0){
+                    double r0 = (e0/e1)*(e0/e1);
+                    double sbar = GetRoot(r0 , z0 , z1 , g);
+                    x0 = r0 * y0 /( sbar + r0 );
+                    x1 = y1 /( sbar + 1 );
+                    distance = sqrt( (x0-y0)*(x0-y0) + (x1-y1)*(x1-y1) );
+                    }else{
+                        x0 = y0;
+                        x1 = y1;
+                        distance = 0;
+                    }
+                }
+                else // y0 == 0
+                    {x0 = 0 ; x1 = e1 ; distance = abs( y1 - e1 );}
+        }else{ // y1 == 0
+            double numer0 = e0*y0 , denom0 = e0*e0 - e1*e1;
+            if ( numer0 < denom0 ){
+                    double xde0 = numer0/denom0;
+                    x0 = e0*xde0 ; x1 = e1*sqrt(1 - xde0*xde0 );
+                    distance = sqrt( (x0-y0)*(x0-y0) + x1*x1 );
+                }else{
+                    x0 = e0;
+                    x1 = 0;
+                    distance = abs( y0 - e0 );
+            }
+        }
+        return distance;
+    }
+
+double DistancePointEllipsoid(double e0, double e1, double e2, double y0, double y1, double y2, double& x0, double& x1, double& x2)
+{
+	double distance;
+	if( y2 > 0 )
+	{
+		if( y1 > 0 )
+		{
+			if( y0 > 0 )
+			{
+				double z0 = y0 / e0;
+				double z1 = y1 / e1;
+				double z2 = y2 / e2;
+				double g = z0*z0 + z1*z1 + z2*z2 - 1 ;
+				if( g != 0 )
+				{
+					double r0 = (e0/e2)*(e0/e2);
+					double r1 = (e1/e2)*(e1/e2);
+					double sbar = GetRoot ( r0 , r1 , z0 , z1 , z2 , g );
+					x0 = r0 *y0 / ( sbar + r0 );
+					x1 = r1 *y1 / ( sbar + r1 );
+					x2 = y2 / ( sbar + 1 );
+					distance = sqrt( (x0 - y0)*(x0 - y0) + (x1 - y1)*(x1 - y1) + (x2 - y2)*(x2 - y2));
+				}
+				else
+				{
+					x0 = y0;
+					x1 = y1;
+					x2 = y2;
+					distance = 0;
+				}
+			}
+			else // y0 == 0
+			{
+				x0 = 0;
+				distance = DistancePointEllipse( e1 , e2 , y1 , y2, x1, x2);
+			}
+		}
+		else // y1 == 0
+		{
+			if( y0 > 0 )
+			{
+				x1 = 0;
+				distance = DistancePointEllipse( e0 , e2 , y0 , y2, x0, x2);
+			}
+			else // y0 == 0
+			{
+				x0 = 0;
+				x1 = 0;
+				x2 = e2;
+				distance = abs(y2 - e2);
+			}
+		}
+	}
+	else // y2 == 0
+	{
+		double denom0 = e0*e0 - e2*e2;
+		double denom1 = e1*e1 - e2*e2;
+		double numer0 = e0*y0;
+		double numer1 = e1*y1;
+		bool computed = false;
+		if((numer0 < denom0) && (numer1 < denom1))
+		{
+			double xde0 = numer0/denom0;
+			double xde1 = numer1/denom1 ;
+			double xde0sqr = xde0 *xde0;
+			double xde1sqr = xde1 * xde1 ;
+			double discr = 1 - xde0sqr - xde1sqr;
+			if( discr > 0 )
+			{
+				x0 = e0*xde0;
+				x1 = e1*xde1;
+				x2 = e2*sqrt(discr);
+				distance = sqrt((x0 - y0)*(x0 - y0) + (x1 - y1)*(x1 - y1) + x2*x2);
+				computed = true;
+			}
+		}
+		if( !computed )
+		{
+			x2 = 0;
+			distance = DistancePointEllipse(e0 , e1 , y0 , y1, x0, x1);
+		}
+	}
+	return distance;
+}
+
+constexpr unsigned int factorial(unsigned int x)
+{
+    unsigned int fact = 1;
+    for(int i = 1; i < (x + 1); i++) fact = fact*i;
+    return(fact);
+}
+constexpr unsigned int minter_lp_degree_one_num_coeffs(unsigned int dims, unsigned int poly_degree)
+{
+    return(factorial(dims + poly_degree)/(factorial(dims)*factorial(poly_degree)));
+}
+
+int return_sign(double phi)
+{
+	if (phi > 0) return 1;
+	if (phi < 0) return -1;
+	return 0;
+}
+
+double randMinusOneToOne()
+{
+    double temp = rand() / (RAND_MAX + 1.0);
+    //std::cout<<(2.0*temp - 1.0)<<std::endl;
+    return(2.0*temp - 1.0);
+}
diff --git a/src/level_set/particle_cp/pcp_unit_tests.cpp b/src/level_set/particle_cp/pcp_unit_tests.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..30aa41b2b819428586d71f4b291beeb26d7d70f1
--- /dev/null
+++ b/src/level_set/particle_cp/pcp_unit_tests.cpp
@@ -0,0 +1,196 @@
+/* Unit tests for the particle closest point method
+ * Date : 29 June 2023
+ * Author : lschulze
+ */
+
+#include<iostream>
+#include <boost/test/unit_test_log.hpp>
+#define BOOST_TEST_DYN_LINK
+#include <boost/test/unit_test.hpp>
+#include <math.h>
+//#include <sys/_types/_size_t.h>
+#include "Vector/vector_dist.hpp"
+#include "DCPSE/Dcpse.hpp"
+#include "DCPSE/MonomialBasis.hpp"
+#include "Draw/DrawParticles.hpp"
+#include "particle_cp.hpp"
+#include "Operators/Vector/vector_dist_operators.hpp"
+#include <chrono>
+#include "pcp_unit_test_helpfunctions.h"
+
+typedef struct EllipseParameters{
+    double origin[3];
+    double radiusA;
+    double radiusB;
+    double radiusC;
+    double eccentricity;
+} EllipseParams;
+
+// Generate an ellipsoid initial levelset signed distance function
+template<typename particles_type, typename iterator_type, size_t sdf, size_t ref_cp>
+void initializeLSEllipsoid(particles_type &vd, iterator_type particle_it, const EllipseParams &params, double bandwidth, double perturb_factor, double H)
+{
+    while(particle_it.isNext())
+    {
+        double posx = particle_it.get().get(0); 
+        double posy = particle_it.get().get(1); 
+        double posz = particle_it.get().get(2);
+
+	double ref_cpx = 0.0;
+	double ref_cpy = 0.0;
+	double ref_cpz = 0.0;
+        
+	double dist = DistancePointEllipsoid(params.radiusA, params.radiusB, params.radiusC, abs(posx), abs(posy), abs(posz), ref_cpx, ref_cpy, ref_cpz);
+        if (abs(dist) < bandwidth/2.0)
+	{
+		posx = posx + perturb_factor*randMinusOneToOne()*H;
+		posy = posy + perturb_factor*randMinusOneToOne()*H;
+		posz = posz + perturb_factor*randMinusOneToOne()*H;
+		
+		dist = DistancePointEllipsoid(params.radiusA, params.radiusB, params.radiusC, abs(posx), abs(posy), abs(posz), ref_cpx, ref_cpy, ref_cpz);
+		vd.add();
+		vd.getLastPos()[0] = posx;
+		vd.getLastPos()[1] = posy;
+		vd.getLastPos()[2] = posz;
+
+		double phi_val = sqrt(((posx - params.origin[0])/params.radiusA)*((posx - params.origin[0])/params.radiusA) + ((posy - params.origin[1])/params.radiusB)*((posy - params.origin[1])/params.radiusB) + ((posz - params.origin[2])/params.radiusC)*((posz - params.origin[2])/params.radiusC)) - 1.0;
+        	vd.template getLastProp<sdf>() = phi_val;
+		vd.template getLastProp<ref_cp>()[0] = return_sign(posx)*ref_cpx;
+		vd.template getLastProp<ref_cp>()[1] = return_sign(posy)*ref_cpy;
+		vd.template getLastProp<ref_cp>()[2] = return_sign(posz)*ref_cpz;
+	}
+	
+	++particle_it;
+    }
+}
+
+BOOST_AUTO_TEST_SUITE( particle_closest_point_test )
+
+BOOST_AUTO_TEST_CASE( ellipsoid )
+{
+	// simulation params
+	constexpr int poly_order = 4;
+	const double H = 1.0/64.0;
+	const double perturb_factor = 0.3;
+
+	// pcp params
+	const double bandwidth = 12.0*H;
+	const double regression_rcut_factor = 2.4;
+	const double threshold = 1e-13;
+
+	// initialize domain and particles
+	const double l = 2.0;
+	Box<3, double> domain({-l/2.0, -l/3.0, -l/3.0}, {l/2.0, l/3.0, l/3.0});
+	size_t sz[3] = {(size_t)(l/H + 0.5), (size_t)((2.0/3.0)*l/H + 0.5), (size_t)((2.0/3.0)*l/H + 0.5)};
+	size_t bc[3] = {NON_PERIODIC, NON_PERIODIC, NON_PERIODIC};
+	Ghost<3, double> g(bandwidth);
+
+	constexpr int sdf = 0;
+	constexpr int cp = 1;
+	constexpr int normal = 2;
+	constexpr int curvature = 3;
+	constexpr int surf_flag = 4;
+	constexpr int ref_cp = 5;
+	typedef vector_dist<3, double, aggregate<double, Point<3, double>, Point<3, double>, double, int, Point<3, double>>> particles;
+	//					 |		|		|		|	|		|
+	//					SDF	closest point		|  	   curvature  surf flag 	|
+	//								surface normal	   		reference closest point
+
+	particles vd(0, domain, bc, g, DEC_GRAN(512));
+	openfpm::vector<std::string> names({"sdf","cp","normal","curvature","C flag", "ref_cp"});
+	vd.setPropNames(names);
+	EllipseParameters params;
+	for (int k = 0; k < 3; k++) params.origin[k] = 0.0;
+	params.radiusA = 0.75;
+	params.radiusB = 0.5;
+	params.radiusC = 0.5;
+
+	auto particle_it = DrawParticles::DrawBox(vd, sz, domain, domain);
+
+	// initialize spurious sdf values and reference solution
+	initializeLSEllipsoid<particles, decltype(particle_it), sdf, ref_cp>(vd, particle_it, params, bandwidth, perturb_factor, H);
+
+	//vd.write("pcpunittest_init");
+	// initialize and run pcp redistancing
+	Redist_options rdistoptions;
+	rdistoptions.minter_poly_degree = poly_order;
+	rdistoptions.H = H;
+	rdistoptions.r_cutoff_factor = regression_rcut_factor;
+	rdistoptions.sampling_radius = 0.75*bandwidth;
+	rdistoptions.tolerance = threshold;
+	rdistoptions.write_cp = 1;
+	rdistoptions.compute_normals = 1;
+	rdistoptions.compute_curvatures = 1;
+	rdistoptions.only_narrowband = 0;
+
+	static constexpr unsigned int num_coeffs = minter_lp_degree_one_num_coeffs(3, poly_order);
+
+	particle_cp_redistancing<particles, taylor4, sdf, cp, normal, curvature, num_coeffs> pcprdist(vd, rdistoptions);
+	pcprdist.run_redistancing();
+	
+	//vd.write("pcpunittest");
+	// iterate through particles and compute error
+	auto part = vd.getDomainIterator();
+	double sdferr = 0.0;
+	double sdfmaxerr = 0.0;
+	double sdfmeanerr = 0.0;
+	double cperr = 0.0;
+	double cpmaxerr = 0.0;
+	double normalerr = 0.0;
+	double normalmaxerr = 0.0;
+	double curvatureerr = 0.0;
+	double curvaturemaxerr = 0.0;
+	int particlecount = 0;
+
+	while(part.isNext())
+	{
+		auto a = part.get();
+		Point<3, double> pos = vd.getPos(a);
+		double reference_sdf = return_sign(sqrt(((pos[0] - params.origin[0])/params.radiusA)*((pos[0] - params.origin[0])/params.radiusA) + ((pos[1] - params.origin[1])/params.radiusB)*((pos[1] - params.origin[1])/params.radiusB) + ((pos[2] - params.origin[2])/params.radiusC)*((pos[2] - params.origin[2])/params.radiusC)) - 1.0)*norm(vd.getProp<ref_cp>(a) - pos);
+		Point<3, double> reference_normal;
+		double reference_curvature;
+	
+		// compute reference SDF value w/ reference closest point and check computed SDF value
+		sdferr = abs(vd.getProp<sdf>(a) - reference_sdf);
+		if (sdferr > sdfmaxerr) sdfmaxerr = sdferr;
+		sdfmeanerr += sdferr;
+		++particlecount;
+
+		// check computed closest point against reference closest point
+		cperr = norm(vd.getProp<ref_cp>(a) - vd.getProp<cp>(a));
+		if (cperr > cpmaxerr) cpmaxerr = cperr;
+
+		// compute reference normal and check computed normal
+		reference_normal[0] = 2*vd.getProp<ref_cp>(a)[0]/(params.radiusA*params.radiusA);
+		reference_normal[1] = 2*vd.getProp<ref_cp>(a)[1]/(params.radiusB*params.radiusB);
+		reference_normal[2] = 2*vd.getProp<ref_cp>(a)[2]/(params.radiusC*params.radiusC);
+		reference_normal = return_sign(reference_sdf)*reference_normal/norm(reference_normal);
+		normalerr = norm(reference_normal - vd.getProp<normal>(a));
+		if (normalerr > normalmaxerr) normalmaxerr = normalerr;
+		
+		// compute reference curvature and check computed curvature
+		reference_curvature = (std::abs(vd.getProp<ref_cp>(a)[0]*vd.getProp<ref_cp>(a)[0] + vd.getProp<ref_cp>(a)[1]*vd.getProp<ref_cp>(a)[1] + vd.getProp<ref_cp>(a)[2]*vd.getProp<ref_cp>(a)[2] - params.radiusA*params.radiusA  - params.radiusB*params.radiusB - params.radiusC*params.radiusC))/(2*params.radiusA*params.radiusA*params.radiusB*params.radiusB*params.radiusC*params.radiusC*std::pow(vd.getProp<ref_cp>(a)[0]*vd.getProp<ref_cp>(a)[0]/std::pow(params.radiusA, 4) + vd.getProp<ref_cp>(a)[1]*vd.getProp<ref_cp>(a)[1]/std::pow(params.radiusB, 4) + vd.getProp<ref_cp>(a)[2]*vd.getProp<ref_cp>(a)[2]/std::pow(params.radiusC, 4), 1.5));
+		curvatureerr = abs(reference_curvature - vd.getProp<curvature>(a));
+		if (curvatureerr > curvaturemaxerr) curvaturemaxerr = curvatureerr;
+
+		++part;
+	}
+	sdfmeanerr = sdfmeanerr/particlecount;
+	std::cout<<"Mean error for sdf is: "<<sdfmeanerr<<std::endl;
+	std::cout<<"Maximum error for sdf is: "<<sdfmaxerr<<std::endl;
+        std::cout<<"Maximum error for the closest point is: "<<cpmaxerr<<std::endl;
+        std::cout<<"Maximum error for surface normal is: "<<normalmaxerr<<std::endl;
+        std::cout<<"Maximum error for curvature is: "<<curvaturemaxerr<<std::endl;
+	
+    	double tolerance = 1e-7;
+   	bool check;
+    	if (std::abs(sdfmaxerr) < tolerance)
+        	check = true;
+    	else
+        	check = false;
+
+    	BOOST_TEST( check );
+
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/regression/regression.hpp b/src/regression/regression.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..30aefa14642c33e582f9634a647ac8111ee11ecf
--- /dev/null
+++ b/src/regression/regression.hpp
@@ -0,0 +1,461 @@
+/*
+ * Regression module
+ * Obtains polynomial models for data from vector_dist
+ * author : sachin (sthekke@mpi-cbg.de)
+ * date : 28.04.2022
+ *
+ */
+#ifndef OPENFPM_NUMERICS_REGRESSION_HPP
+#define OPENFPM_NUMERICS_REGRESSION_HPP
+
+#include "Vector/map_vector.hpp"
+#include "Space/Shape/Point.hpp"
+#include "DMatrix/EMatrix.hpp"
+#include "DCPSE/SupportBuilder.hpp"
+#include "/datapot/lschulze/Applications/minter/include/minter.h"
+
+
+template<typename vector_type_support, typename NN_type>
+class RegressionSupport
+{
+	//std::vector<size_t> keys;
+	openfpm::vector<size_t> keys;
+	
+public:
+
+	template<typename iterator_type>
+	RegressionSupport(vector_type_support &vd, iterator_type itPoint, unsigned int requiredSize, support_options opt, NN_type &cellList_in) : domain(vd),
+	cellList(cellList_in)
+	{
+		rCut = cellList_in.getCellBox().getHigh(0);
+		// Get spatial position from point iterator
+		vect_dist_key_dx p = itPoint.get();
+		vect_dist_key_dx pOrig = itPoint.getOrig();
+		Point<vector_type_support::dims, typename vector_type_support::stype> pos = domain.getPos(p.getKey());
+
+		// Get cell containing current point and add it to the set of cell keys
+		grid_key_dx<vector_type_support::dims> curCellKey = cellList.getCellGrid(pos); // Here get the key of the cell where the current point is
+		std::set<grid_key_dx<vector_type_support::dims>> supportCells;
+        	supportCells.insert(curCellKey);
+
+		// Make sure to consider a set of cells providing enough points for the support
+		enlargeSetOfCellsUntilSize(supportCells, requiredSize + 1,
+                                   opt); // NOTE: this +1 is because we then remove the point itself
+
+        	// Now return all the points from the support into a vector
+        	keys = getPointsInSetOfCells(supportCells, p, pOrig, requiredSize, opt);
+	}
+	
+	auto getKeys()
+	{
+		return keys;
+	}
+
+	auto getNumParticles()
+	{
+		return keys.size();
+	}
+
+private:
+
+	vector_type_support &domain;
+	NN_type &cellList;
+	typename vector_type_support::stype rCut;
+
+
+    	void enlargeSetOfCellsUntilSize(std::set<grid_key_dx<vector_type_support::dims>> &set, unsigned int requiredSize,
+                                    support_options opt) {
+        	if (opt == support_options::RADIUS) {
+            	auto cell = *set.begin();
+            	grid_key_dx<vector_type_support::dims> middle;
+            	int n = std::ceil(rCut / cellList.getCellBox().getHigh(0));
+            	size_t sz[vector_type_support::dims];
+            	for (int i = 0; i < vector_type_support::dims; i++) {
+                	sz[i] = 2 * n + 1;
+                	middle.set_d(i, n);
+            	}
+            	grid_sm<vector_type_support::dims, void> g(sz);
+            	grid_key_dx_iterator<vector_type_support::dims> g_k(g);
+            	while (g_k.isNext()) {
+                	auto key = g_k.get();
+                	key = cell + key - middle;
+                	if (isCellKeyInBounds(key)) {
+                    		set.insert(key);
+                	}
+                	++g_k;
+            	}
+        	}
+	        else if (opt == support_options::AT_LEAST_N_PARTICLES) {
+
+		int done = 0;
+		int n = 1;
+            	auto cell = *set.begin();
+            	grid_key_dx<vector_type_support::dims> middle;
+		size_t sz[vector_type_support::dims];
+		
+		while(true) // loop for the number of cells enlarged per dimension
+		{
+			std::set<grid_key_dx<vector_type_support::dims>> temp_set;
+            		for (int i = 0; i < vector_type_support::dims; i++) {
+                		sz[i] = 2 * n + 1;
+                		middle.set_d(i, n);
+            		}
+
+            		grid_sm<vector_type_support::dims, void> g(sz);
+            		grid_key_dx_iterator<vector_type_support::dims> g_k(g);
+            		while (g_k.isNext()) {
+                		auto key = g_k.get();
+                		key = cell + key - middle;
+                		if (isCellKeyInBounds(key)) {
+                    			temp_set.insert(key);
+                		}
+                		++g_k;
+            		}
+			if (getNumElementsInSetOfCells(temp_set) < requiredSize) n++;
+			else 
+			{
+				set = temp_set;
+				break;
+			}
+		}
+		}
+		else {
+            	while (getNumElementsInSetOfCells(set) <
+                   5.0 * requiredSize) //Why 5*requiredSize? Becasue it can help with adaptive resolutions.
+            	{
+                	auto tmpSet = set;
+                	for (const auto el: tmpSet) {
+                    	for (unsigned int i = 0; i < vector_type_support::dims; ++i) {
+                        	const auto pOneEl = el.move(i, +1);
+                        	const auto mOneEl = el.move(i, -1);
+                        	if (isCellKeyInBounds(pOneEl)) {
+                            	set.insert(pOneEl);
+                        	}
+                        	if (isCellKeyInBounds(mOneEl)) {
+                            	set.insert(mOneEl);
+                        	}
+                    	}
+                	}
+
+            	}	
+		}   	
+    }
+
+    openfpm::vector<size_t> getPointsInSetOfCells(std::set<grid_key_dx<vector_type_support::dims>> set,
+                                              vect_dist_key_dx &p,
+                                              vect_dist_key_dx &pOrig,
+                                              size_t requiredSupportSize,
+                                              support_options opt) {
+        struct reord {
+            typename vector_type_support::stype dist;
+            size_t offset;
+
+            bool operator<(const reord &p) const { return this->dist < p.dist; }
+        };
+
+        openfpm::vector<reord> rp;
+        openfpm::vector<size_t> points;
+        Point<vector_type_support::dims, typename vector_type_support::stype> xp = domain.getPos(p);
+        for (const auto cellKey: set) {
+            const size_t cellLinId = getCellLinId(cellKey);
+            const size_t elemsInCell = getNumElementsInCell(cellKey);
+            for (size_t k = 0; k < elemsInCell; ++k) {
+                size_t el = cellList.get(cellLinId, k);
+
+                Point<vector_type_support::dims, typename vector_type_support::stype> xq = domain.getPosOrig(el);
+                //points.push_back(el);
+
+                reord pr;
+
+                pr.dist = xp.distance(xq);
+                pr.offset = el;
+                rp.add(pr);
+            }
+        }
+
+        if (opt == support_options::RADIUS) {
+            for (int i = 0; i < rp.size(); i++) {
+                if (rp.get(i).dist < rCut) {
+                    points.add(rp.get(i).offset);
+                }
+            }
+            /*      #ifdef SE_CLASS1
+                    if (points.size()<requiredSupportSize)
+                    {
+                        std::cerr<<__FILE__<<":"<<__LINE__<<"Note that the DCPSE neighbourhood doesn't have asked no. particles (Increase the rCut or reduce the over_sampling factor)";
+                        std::cout<<"Particels asked (minimum*oversampling_factor): "<<requiredSupportSize<<". Particles Possible with given options:"<<points.size()<<"."<<std::endl;
+                    }
+                    #endif*/
+        }
+	else if (opt == support_options::AT_LEAST_N_PARTICLES) {
+	    for (int i = 0; i  < rp.size(); i++) points.add(rp.get(i).offset);
+	    }
+        else {
+            rp.sort();
+            for (int i = 0; i < requiredSupportSize; i++) {
+                    points.add(rp.get(i).offset);
+                }
+            }
+        //MinSpacing=MinSpacing/requiredSupportSize
+        return points;
+    }
+    
+    size_t getCellLinId(const grid_key_dx<vector_type_support::dims> &cellKey) {
+        mem_id id = cellList.getGrid().LinId(cellKey);
+        return static_cast<size_t>(id);
+    }
+
+    size_t getNumElementsInCell(const grid_key_dx<vector_type_support::dims> &cellKey) {
+        const size_t curCellId = getCellLinId(cellKey);
+        size_t numElements = cellList.getNelements(curCellId);
+        return numElements;
+    }
+    size_t getNumElementsInSetOfCells(const std::set<grid_key_dx<vector_type_support::dims>> &set)
+    {
+	    size_t tot = 0;
+	    for (const auto cell : set)
+	    {
+		    tot += getNumElementsInCell(cell);
+	    }
+	    return tot;
+}
+
+    bool isCellKeyInBounds(grid_key_dx<vector_type_support::dims> key)
+    {
+        const size_t *cellGridSize = cellList.getGrid().getSize();
+        for (size_t i = 0; i < vector_type_support::dims; ++i)
+        {
+            if (key.value(i) < 0 || key.value(i) >= cellGridSize[i])
+            {
+                return false;
+            }
+        }
+        return true;
+    }
+};
+
+
+template<int spatial_dim, unsigned int prp_id, typename MatType = EMatrixXd, typename VecType = EVectorXd>
+class RegressionModel
+{
+
+public:
+	minter::PolyModel<spatial_dim, MatType, VecType> *model = nullptr;
+	minter::PolyModel<spatial_dim, MatType, VecType> *deriv_model[spatial_dim];
+	
+	RegressionModel(unsigned int poly_degree, float lp_degree) {
+		// construct polynomial model
+    		model = new minter::PolyModel<spatial_dim, MatType, VecType>();
+		model->setDegree(poly_degree, lp_degree);
+
+    		// placeholder for derivatives
+		for(int i = 0;i < spatial_dim;++i)
+			deriv_model[i] = nullptr;
+	}
+
+	template<typename vector_type, typename reg_support_type>
+	RegressionModel(vector_type &vd, reg_support_type &support, unsigned int poly_degree, float lp_degree = 2.0)
+	{
+		int num_particles = support->getNumParticles();
+		int dim = vector_type::dims;
+
+		MatType points(num_particles, dim);
+		VecType values(num_particles);
+		
+		auto keys = support->getKeys();
+		for(int i = 0;i < num_particles;++i)
+		{
+			for(int j = 0;j < dim;++j)
+				points(i,j) = vd.getPos(keys.get(i))[j];
+			values(i) = vd.template getProp<prp_id>(keys.get(i));
+		}
+
+		// construct polynomial model
+    		model = new minter::PolyModel<spatial_dim, MatType, VecType>(points, values, poly_degree, lp_degree);
+
+    		// placeholder for derivatives
+		for(int i = 0;i < dim;++i)
+			deriv_model[i] = nullptr;
+	}
+
+	
+	// Constructor for all points in a proc (domain + ghost) and a specified poly_degree
+	template<typename vector_type>
+	RegressionModel(vector_type &vd, unsigned int poly_degree, float lp_degree = 2.0)
+	{
+		int num_particles = vd.size_local_with_ghost();
+		int dim = vector_type::dims;
+
+		MatType points(num_particles, dim);
+		VecType values(num_particles);
+		
+		auto it = vd.getDomainAndGhostIterator();
+		int i = 0;
+		while (it.isNext())
+		{
+			auto key = it.get();
+			for(int j = 0;j < dim;++j)
+				points(i,j) = vd.getPos(key)[j];
+
+			values(i) = vd.template getProp<prp_id>(key);
+
+			++it;
+			++i;
+		}
+
+		// construct polynomial model 
+		model = new minter::PolyModel<spatial_dim, MatType, VecType>(points, values, poly_degree, lp_degree);
+
+		for(i = 0;i < dim;++i)
+			deriv_model[i] = nullptr;
+	}
+
+	// Constructor for all points in a proc (domain + ghost) within a tolerance
+	template<typename vector_type>
+	RegressionModel(vector_type &vd, double tolerance)
+	{
+		int num_particles = vd.size_local_with_ghost();
+		int dim = vector_type::dims;
+
+		MatType points(num_particles, dim);
+		VecType values(num_particles);
+		
+		auto it = vd.getDomainAndGhostIterator();
+		int i = 0;
+		while (it.isNext())
+		{
+			auto key = it.get();
+			for(int j = 0;j < dim;++j)
+				points(i,j) = vd.getPos(key)[j];
+
+			values(i) = vd.template getProp<prp_id>(key);
+
+			++it;
+			++i;
+		}
+
+		int poly_degree = 1;
+		double error = -1.0;
+		minter::PolyModel<spatial_dim, MatType, VecType> *mdl = nullptr;
+		
+		do
+		{
+			++poly_degree;
+			if(mdl)
+				delete mdl;
+
+			// construct polynomial model
+	    	mdl = new minter::PolyModel<spatial_dim, MatType, VecType>(points, values, poly_degree, 2.0);
+
+	    	// check if linf_error is within the tolerance
+	    	error = -1.0;
+	    	for(i = 0;i < num_particles;++i)
+	    	{
+	    		double pred = mdl->eval(points.block(i,0,1,dim))(0); // evaluated for one point
+	    		double err = std::abs(pred - values(i));
+	    		if (err > error)
+	    			error = err;
+	    	}
+	    	// std::cout<<"Fit of degree "<<poly_degree<<" with error = "<<error<<std::endl;
+
+	    }while(error > tolerance);
+
+	    model = mdl;
+
+	    for(i = 0;i < dim;++i)
+			deriv_model[i] = nullptr;
+	    
+	}
+
+	template<typename vector_type, typename reg_support_type>
+	void computeCoeffs(vector_type& vd, reg_support_type& support)
+    	{
+
+        	unsigned int dim = vector_type::dims;
+		auto num_particles = support.getNumParticles();
+
+        	Eigen::MatrixXd points(num_particles, dim);
+        	Eigen::VectorXd values(num_particles);
+	
+		auto keys = support.getKeys();
+		for(int i = 0;i < num_particles;++i)
+		{
+			for(int j = 0;j < dim;++j)
+				points(i,j) = vd.getPos(keys.get(i))[j];
+			values(i) = vd.template getProp<prp_id>(keys.get(i));
+		}
+
+	        model->computeCoeffs(points, values);
+    	}
+
+	~RegressionModel()
+	{
+
+		if(model)
+			delete model;
+
+		for(int i = 0;i < spatial_dim;++i)
+		{
+			if(deriv_model[i])
+				delete deriv_model[i];
+		}
+	}
+
+	template<typename T> // Typical: Point<vector_type::dims, typename vector_type::stype> 
+	double eval(T pos)
+	{
+		int dim = pos.dims;
+		MatType point(1,dim);
+		for(int j = 0;j < dim;++j)
+			point(0,j) = pos.get(j);
+
+		return model->eval(point)(0);
+	}
+
+	// T1 : Point<vector_type::dims, typename vector_type::stype>
+	// T2 : Point<vector_type::dims, int>
+	template<typename T1, typename T2> 
+	double deriv(T1 pos, T2 deriv_order)
+	{
+
+		int dim = pos.dims;
+		MatType point(1,dim);
+		for(int j = 0;j < dim;++j)
+			point(0,j) = pos.get(j);
+
+		std::vector<int> order;
+		for(int j = 0;j < dim;++j)
+			order.push_back(deriv_order.get(j));
+
+		return model->deriv_eval(point, order)(0);
+	}
+
+	void compute_grad()
+	{
+		for(int i = 0;i < spatial_dim;++i)
+		{
+			std::vector<int> ord(spatial_dim, 0);
+			ord[i] = 1;
+			deriv_model[i] = model->derivative(ord);
+		}
+	}
+
+	// T: Point<vector_type::dims, typename vector_type::stype>
+	template<typename T>
+	T eval_grad(T pos)
+	{
+		T res;
+
+		if(!deriv_model[0])
+			compute_grad();
+
+		for(int i = 0;i < spatial_dim;++i)
+			res.get(i) = deriv_model[i]->eval(pos);
+
+		return res;
+	}
+
+};
+
+
+#endif /* REGRESSION_HPP_ */