diff --git a/CMakeLists.txt b/CMakeLists.txt
index 99ad9c12aa0fdc6802e44a7f3ae0a523613b8745..3d1492fb5fec918524ff3a471ecc3e1a77bc09d1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,8 @@
 cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
 project(openfpm_pdata LANGUAGES C CXX)
 
+cmake_policy(SET CMP0074 OLD)
+
 enable_testing()
 
 list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake_modules/)
diff --git a/openfpm_data b/openfpm_data
index 0083a00b0bf82127a828c52ff3c1e922c18e19cf..ad46a9fef9242807d928e64a441d550808b32412 160000
--- a/openfpm_data
+++ b/openfpm_data
@@ -1 +1 @@
-Subproject commit 0083a00b0bf82127a828c52ff3c1e922c18e19cf
+Subproject commit ad46a9fef9242807d928e64a441d550808b32412
diff --git a/src/Amr/grid_dist_amr.hpp b/src/Amr/grid_dist_amr.hpp
index ce1d0eb395088b7ea8f4a1b9aba54399fe736d60..9d6c791fdc6224273e3b60352cf81b49a173bfbc 100644
--- a/src/Amr/grid_dist_amr.hpp
+++ b/src/Amr/grid_dist_amr.hpp
@@ -729,10 +729,11 @@ public:
 				gd_array.get(lvl).construct_link_dw(gd_array.get(lvl+1),mv_off.get(lvl));
 			}
 			else if (lvl == gd_array.size() - 1)
-			{gd_array.get(lvl).construct_link_up(gd_array.get(lvl-1));}
+			{gd_array.get(lvl).construct_link_up(gd_array.get(lvl-1),mv_off.get(lvl));}
 			else
 			{
 				gd_array.get(lvl).construct_link_dw(gd_array.get(lvl+1),mv_off.get(lvl));
+				gd_array.get(lvl).construct_link_up(gd_array.get(lvl-1),mv_off.get(lvl));
 			}
 		}
 	}
@@ -875,6 +876,31 @@ public:
 		key.setLvl(lvl+1);
 	}
 
+	/*! \brief Move down (to finer level) the key
+	 *
+	 * \param i level
+	 * \param key of grid at level i
+	 *
+	 */
+	template<typename bg_key>
+	void moveLvlDw(int i, grid_dist_key_dx<dim,bg_key> & key)
+	{
+#ifdef SE_CLASS1
+
+		if (i >= this->getNLvl() - 1)
+		{std::cerr << __FILE__ << ":" << __LINE__ << " error: we are already at the last level, we cannot go one level down" << std::endl;}
+
+#endif
+
+		auto & key_ref = key.getKeyRef();
+		size_t lvl = i;
+
+		for (size_t j = 0 ; j < dim ; j++)
+		{
+			key_ref.set_d(j,(key_ref.get(j) << 1) + mv_off.get(i).get(key.getSub()).dw.get(j) );
+		}
+	}
+
 	/*! \brief From a distributed key it return a AMR key that contain also the grid level
 	 *
 	 * \param lvl level
diff --git a/src/Amr/tests/amr_base_gpu_unit_tests.cu b/src/Amr/tests/amr_base_gpu_unit_tests.cu
index b224547198bfbe691c9c387866002d0530503cec..b58d5457b2d895356eaeae65762825631ded4373 100644
--- a/src/Amr/tests/amr_base_gpu_unit_tests.cu
+++ b/src/Amr/tests/amr_base_gpu_unit_tests.cu
@@ -340,7 +340,6 @@ BOOST_AUTO_TEST_CASE( grid_dist_id_amr_gpu_link_test_more_dense )
 	auto it = amr_g.getGridIterator(0,start,stop);
 	auto it2 = amr_g.getGridIterator(1,start_lvl_dw,stop_lvl_dw);
 	auto it3 = amr_g.getGridIterator(2,start_lvl_dw2,stop_lvl_dw2);
-//	it.setGPUInsertBuffer(4);
 
 	auto & lvl_0 = amr_g.getDistGrid(0);
 	auto & lvl_1 = amr_g.getDistGrid(1);
@@ -391,6 +390,18 @@ BOOST_AUTO_TEST_CASE( grid_dist_id_amr_gpu_link_test_more_dense )
 
 	// For each local grid
 
+	size_t tot_dw_offs_12 = 0;
+	size_t tot_dw_lk_12 = 0;
+
+	size_t tot_dw_offs_23 = 0;
+	size_t tot_dw_lk_23 = 0;
+
+	size_t tot_up_offs_12 = 0;
+	size_t tot_up_lk_12 = 0;
+
+	size_t tot_up_offs_23 = 0;
+	size_t tot_up_lk_23 = 0;
+
 	for (int i = 0 ; i < lvl_zero_d.getN_loc_grid() ; i++)
 	{
 
@@ -402,8 +413,14 @@ BOOST_AUTO_TEST_CASE( grid_dist_id_amr_gpu_link_test_more_dense )
 		auto & offs_dw_link = lvl_zero.getDownLinksOffsets();
 		auto & dw_links = lvl_zero.getDownLinks();
 
-		BOOST_REQUIRE_EQUAL(offs_dw_link.size(),(i+1)*56);
-		BOOST_REQUIRE_EQUAL(dw_links.size(),(i+1)*56*4);
+		auto & offs_up_link = lvl_one.getUpLinksOffsets();
+		auto & up_links = lvl_one.getUpLinks();
+
+		tot_dw_offs_12 += offs_dw_link.size();
+		tot_dw_lk_12 += dw_links.size();
+
+		tot_up_offs_12 += offs_up_link.size();
+		tot_up_lk_12 += up_links.size();
 
 		auto & indexL0 = lvl_zero.private_get_blockMap().getIndexBuffer();
 		auto & indexL1 = lvl_one.private_get_blockMap().getIndexBuffer();
@@ -414,26 +431,70 @@ BOOST_AUTO_TEST_CASE( grid_dist_id_amr_gpu_link_test_more_dense )
 		auto & dataL2 = lvl_two.private_get_blockMap().getDataBuffer();
 
 		dw_links.template deviceToHost<0,1>();
+		up_links.template deviceToHost<0,1>();
 
 		for (int i = 0 ; i < dw_links.size(); i++)
 		{
-			BOOST_REQUIRE_EQUAL(dataL1.template get<0>(dw_links.template get<0>(0))[dw_links.template get<1>(0)],2);
+			BOOST_REQUIRE_EQUAL(dataL1.template get<0>(dw_links.template get<0>(i))[dw_links.template get<1>(i)],2);
+		}
+
+		for (int i = 0 ; i < up_links.size(); i++)
+		{
+			BOOST_REQUIRE_EQUAL(dataL0.template get<0>(up_links.template get<0>(i))[up_links.template get<1>(i)],1);
 		}
 
 		auto & offs_dw_link_1 = lvl_one.getDownLinksOffsets();
 		auto & dw_links_1 = lvl_one.getDownLinks();
 
-		BOOST_REQUIRE_EQUAL(offs_dw_link_1.size(),116);
-		BOOST_REQUIRE_EQUAL(dw_links_1.size(),116*4);
+		auto & offs_up_link_1 = lvl_two.getUpLinksOffsets();
+		auto & up_links_1 = lvl_two.getUpLinks();
+
+		tot_dw_offs_23 += offs_dw_link_1.size();
+		tot_dw_lk_23 += dw_links_1.size();
+
+		tot_up_offs_23 += offs_up_link_1.size();
+		tot_up_lk_23 += up_links_1.size();
 
 		dw_links_1.template deviceToHost<0,1>();
+		up_links_1.template deviceToHost<0,1>();
 
 		for (int i = 0 ; i < dw_links_1.size(); i++)
 		{
-			BOOST_REQUIRE_EQUAL(dataL2.template get<0>(dw_links_1.template get<0>(0))[dw_links_1.template get<1>(0)],3);
+			BOOST_REQUIRE_EQUAL(dataL2.template get<0>(dw_links_1.template get<0>(i))[dw_links_1.template get<1>(i)],3);
+		}
+
+		for (int i = 0 ; i < up_links_1.size(); i++)
+		{
+			BOOST_REQUIRE_EQUAL(dataL1.template get<0>(up_links_1.template get<0>(i))[up_links_1.template get<1>(i)],2);
 		}
 	}
 
+	v_cl.sum(tot_dw_offs_12);
+	v_cl.sum(tot_dw_lk_12);
+
+	v_cl.sum(tot_dw_offs_23);
+	v_cl.sum(tot_dw_lk_23);
+
+	v_cl.sum(tot_up_offs_12);
+	v_cl.sum(tot_up_lk_12);
+
+	v_cl.sum(tot_up_offs_23);
+	v_cl.sum(tot_up_lk_23);
+
+	v_cl.execute();
+
+	BOOST_REQUIRE_EQUAL(tot_dw_offs_12,56);
+	BOOST_REQUIRE_EQUAL(tot_dw_lk_12,56*4);
+
+	BOOST_REQUIRE_EQUAL(tot_dw_offs_23,116);
+	BOOST_REQUIRE_EQUAL(tot_dw_lk_23,116*4);
+
+	BOOST_REQUIRE_EQUAL(tot_up_offs_12,116);
+	BOOST_REQUIRE_EQUAL(tot_up_lk_12,116);
+
+	BOOST_REQUIRE_EQUAL(tot_up_offs_23,236);
+	BOOST_REQUIRE_EQUAL(tot_up_lk_23,236);
+
 	/////////////////////////////////////////////////////////////
 }
 
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index f7a2c3857982a40cd95f91ae5fc763f0280c01fc..995632ec8060be9c289a4547fdce95958ff3654a 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -165,6 +165,11 @@ install(FILES Grid/grid_dist_id.hpp
 	      Grid/staggered_dist_grid_copy.hpp
 	      DESTINATION openfpm_pdata/include/Grid/ )
 
+install(FILES Amr/grid_dist_amr_key_iterator.hpp 
+	      Amr/grid_dist_amr_key.hpp
+	      Amr/grid_dist_amr.hpp
+	      DESTINATION openfpm_pdata/include/Amr/ )
+
 install(FILES Grid/Iterators/grid_dist_id_iterator_util.hpp
               Grid/Iterators/grid_dist_id_iterator_dec.hpp
               Grid/Iterators/grid_dist_id_iterator_dec_skin.hpp
diff --git a/src/Grid/Iterators/grid_dist_id_iterator_dec.hpp b/src/Grid/Iterators/grid_dist_id_iterator_dec.hpp
index 3f5d37fcb44f7d85bde103b7dfdf659c648b4d52..d902588092a6750eb3ac4f48489656bcbfd1b3d0 100644
--- a/src/Grid/Iterators/grid_dist_id_iterator_dec.hpp
+++ b/src/Grid/Iterators/grid_dist_id_iterator_dec.hpp
@@ -40,6 +40,8 @@ class grid_dist_id_iterator_dec
 	//! Spacing
 	typename Decomposition::stype spacing[Decomposition::dims];
 
+	//! Domain
+	Box<Decomposition::dims,typename Decomposition::stype> domain;
 
 	/*! \brief from g_c increment g_c until you find a valid grid
 	 *
@@ -96,6 +98,8 @@ class grid_dist_id_iterator_dec
 		start = tmp.start;
 		stop = tmp.stop;
 
+		domain = tmp.domain;
+
 		return *this;
 	}
 
@@ -118,6 +122,8 @@ class grid_dist_id_iterator_dec
 	grid_dist_id_iterator_dec(Decomposition & dec, const size_t (& sz)[Decomposition::dims])
 	:g_c(0)
 	{
+		domain = dec.getDomain();
+
 		// Initialize start and stop
 		start.zero();
 		for (size_t i = 0 ; i < Decomposition::dims ; i++)
@@ -142,6 +148,8 @@ class grid_dist_id_iterator_dec
 	grid_dist_id_iterator_dec(Decomposition & dec, const size_t (& sz)[Decomposition::dims], grid_key_dx<Decomposition::dims> start, grid_key_dx<Decomposition::dims> stop)
 	:g_c(0),start(start),stop(stop)
 	{
+		domain = dec.getDomain();
+
 		// From the decomposition construct gdb_ext
 		create_gdb_ext<Decomposition::dims,Decomposition>(gdb_ext,dec,sz,dec.getDomain(),spacing);
 
@@ -276,6 +284,24 @@ class grid_dist_id_iterator_dec
 		return k_glob;
 	}
 
+	/*! \brief Return the point coordinates
+	 *
+	 * \return the point
+	 *
+	 */
+	inline Point<Decomposition::dims,typename Decomposition::stype> getPoint()
+	{
+		Point<Decomposition::dims,typename Decomposition::stype> p;
+		auto key = this->get();
+
+		for (int i = 0 ; i < Decomposition::dims ; i++)
+		{
+			p.get(i) = spacing[i] * key.get(i) + domain.getLow(i);
+		}
+
+		return p;
+	}
+
 	/*! \brief Get the actual grid key for a distributed grid
 	 *
 	 * \note if you are using this iterator and you need the position for grid_dist_id use this
diff --git a/src/Grid/grid_dist_id.hpp b/src/Grid/grid_dist_id.hpp
index 76c9d7f08721dd2ec574ef51b71338bb2a330b41..9bf3de0687cade0b129a9f126b74244d9acd60d9 100644
--- a/src/Grid/grid_dist_id.hpp
+++ b/src/Grid/grid_dist_id.hpp
@@ -2201,6 +2201,43 @@ public:
 		return loc_grid.get(v1.getSub()).template get<p>(v1.getKey());
 	}
 
+	/*! \brief Get the reference of the selected element
+	 *
+	 * \tparam p property to get (is an integer)
+	 * \param v1 grid_key that identify the element in the grid
+	 *
+	 * \return the selected element
+	 *
+	 */
+	template <typename bg_key>
+	inline Point<dim,St> getPos(const grid_dist_key_dx<dim,bg_key> & v1)
+	{
+#ifdef SE_CLASS2
+		check_valid(this,8);
+#endif
+		Point<dim,St> p;
+
+		for (int i = 0 ; i < dim ; i++)
+		{
+			p.get(i) = (gdb_ext.get(v1.getSub()).origin.get(i) + v1.getKeyRef().get(i)) * this->spacing(i);
+		}
+
+		return p;
+	}
+
+	/*! \brief Check if the point exist
+	 *
+	 * \param v1 grid_key that identify the element in the grid
+	 *
+	 * \return the true if the point exist
+	 *
+	 */
+	template<typename bg_key>
+	inline bool existPoint(const grid_dist_key_dx<dim,bg_key> & v1) const
+	{
+		return loc_grid.get(v1.getSub()).existPoint(v1.getKey());
+	}
+
 	/*! \brief Get the reference of the selected element
 	 *
 	 * \tparam p property to get (is an integer)
@@ -2347,7 +2384,7 @@ public:
 					// center point
 					auto Cp = it.template getStencil<0>();
 
-					dst.get_o(Cp) = src.get_o(Cp);
+					dst.insert_o(Cp) = src.get_o(Cp);
 
 					++it;
 				}
@@ -2357,6 +2394,35 @@ public:
 		return *this;
 	}
 
+	/*! \brief Copy the give grid into this grid
+	 *
+	 * It copy the first grid into the given grid (No ghost)
+	 *
+	 * \warning the Decomposition must be ensured to be the same, otherwise crashes can happen, if you want to copy the grid independently from the decomposition please use the operator equal
+	 *
+	 * \param g Grid to copy
+	 * \param use_memcpy use memcpy function if possible
+	 *
+	 * \return itself
+	 *
+	 */
+	grid_dist_id<dim,St,T,Decomposition,Memory,device_grid> & copy_sparse(grid_dist_id<dim,St,T,Decomposition,Memory,device_grid> & g, bool use_memcpy = true)
+	{
+		grid_key_dx<dim> cnt[1];
+		cnt[0].zero();
+
+		size_t ele_id;
+
+		for (size_t i = 0 ; i < this->getN_loc_grid() ; i++)
+		{
+			auto & dst = this->get_loc_grid(i);
+			auto & src = g.get_loc_grid(i);
+
+			dst = src;
+		}
+		return *this;
+	}
+
 	/*! \brief Get the spacing on each dimension
 	 *
 	 * \return the spacing of the grid on each dimension as a point
@@ -2659,7 +2725,7 @@ public:
 			for(int j = 0 ; j < dim ; j++)
 			{p_dw.get(j) = mvof.get(i).dw.get(j);}
 
-			loc_grid.get(i).construct_link_dw(grid_dw.get_loc_grid(i),p_dw,v_cl.getmgpuContext());
+			loc_grid.get(i).construct_link_dw(grid_dw.get_loc_grid(i),gdb_ext.get(i).Dbox,p_dw,v_cl.getmgpuContext());
 		}
 	}
 
@@ -2669,11 +2735,15 @@ public:
 	 * \param grid_dw grid level down
 	 *
 	 */
-	void construct_link_up(self & grid_up)
+	void construct_link_up(self & grid_up, openfpm::vector<offset_mv<dim>> & mvof)
 	{
 		for (int i = 0 ; i < loc_grid.size() ; i++)
 		{
-			loc_grid.get(i).construct_link_up(grid_up.get_loc_grid(i),v_cl.getmgpuContext());
+			Point<dim,int> p_up;
+			for(int j = 0 ; j < dim ; j++)
+			{p_up.get(j) = mvof.get(i).up.get(j);}
+
+			loc_grid.get(i).construct_link_up(grid_up.get_loc_grid(i),gdb_ext.get(i).Dbox,p_up,v_cl.getmgpuContext());
 		}
 	}
 
diff --git a/src/Grid/grid_dist_key.hpp b/src/Grid/grid_dist_key.hpp
index 666e2bd321ceee3b827856ecbb6b92096b5c178e..26edc906aad72e859e07d90019da494b95908b92 100644
--- a/src/Grid/grid_dist_key.hpp
+++ b/src/Grid/grid_dist_key.hpp
@@ -93,6 +93,16 @@ public:
 		return key;
 	}
 
+	/*! \brief Get the reference key
+	 *
+	 * \return the local key
+	 *
+	 */
+	inline const base_key & getKeyRef() const
+	{
+		return key;
+	}
+
 	/* \brief Check if two key are the same
 	 *
 	 * \param key_t key to check
diff --git a/src/Vector/vector_dist.hpp b/src/Vector/vector_dist.hpp
index d69adf2a8ec477a1e6abb0c6cf90fbc2c3d8e670..949825477772cc1d5bd7566caea7868e38fc6a26 100644
--- a/src/Vector/vector_dist.hpp
+++ b/src/Vector/vector_dist.hpp
@@ -2029,7 +2029,7 @@ public:
 		se3.getIterator();
 #endif
 
-		return v_pos.getGPUIteratorTo(size_local(),n_thr);
+		return v_pos.getGPUIteratorTo(v_pos.size(),n_thr);
 	}
 
 	/*! \brief Merge the properties calculated on the sorted vector on the original vector