diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5ef7f8c6ee0c51cadbe6049b1aad59ae641c1288..e51f09d5a1e1f2a958c9a3093fe1275db6337b12 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -225,6 +225,7 @@ set(PUGS_ENABLE_COSTO OFF CACHE STRING
 
 if (PUGS_ENABLE_COSTO MATCHES "^(AUTO|ON)$")
   find_package(Costo 0.0.1 COMPONENTS CPO)
+  find_package(Python3 3.10 EXACT COMPONENTS Interpreter Development NumPy)
 
   if(Costo_FOUND)
   else()
@@ -239,6 +240,8 @@ endif()
 
 if (${Costo_FOUND})
   include_directories(SYSTEM ${Costo_INCLUDE_DIR})
+  include_directories(SYSTEM ${Python3_INCLUDE_DIRS})
+  include_directories(SYSTEM ${Python3_NumPy_INCLUDE_DIRS})
   link_directories(${Costo_LIBRARY_DIR})
 else()
   if (PUGS_ENABLE_COSTO MATCHES "^ON$")
diff --git a/src/language/modules/CouplageModule.cpp b/src/language/modules/CouplageModule.cpp
index 3f53aa49a7bb067488df319e02abc650f9fffe18..f4cb22ab12919df5faabe262266e23b167ed0ccc 100644
--- a/src/language/modules/CouplageModule.cpp
+++ b/src/language/modules/CouplageModule.cpp
@@ -50,8 +50,18 @@ CouplageModule::CouplageModule()
 							  const std::shared_ptr<const MeshVariant> mesh_v,
 							  const int64_t location,
                                                           const int64_t dest, const int64_t tag) -> void {
-                                return Serializer().BC(boundary, mesh_v, location, dest, tag);
-                            }));
+    return Serializer().BC(boundary, mesh_v, location, dest, tag);
+  }));
+
+
+  this->_addBuiltinFunction("cpl_build_graph", std::function([](const std::shared_ptr<const MeshVariant> mesh_v,
+								const std::shared_ptr<const IBoundaryDescriptor>& boundary) -> void {
+    return Serializer().build_graph(mesh_v, boundary);
+  }));
+
+  this->_addBuiltinFunction("cpl_get_dt", std::function([](const double& dt) -> double {
+    return Serializer().cpl_get_dt(dt);
+  }));
 
   this->_addBuiltinFunction("serialize_field",
                             std::function([](const std::shared_ptr<const MeshVariant> mesh,
diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp
index 3526c4d1d81c35fd7e2dd92b6c86867a09c83670..29932e8ab1b5bad310ec7d1ef8551968ee9b28d8 100644
--- a/src/language/modules/SchemeModule.cpp
+++ b/src/language/modules/SchemeModule.cpp
@@ -17,7 +17,6 @@
 #include <mesh/MeshDataManager.hpp>
 #include <mesh/MeshRandomizer.hpp>
 #include <mesh/MeshSmoother.hpp>
-// #include <mesh/NumberedBoundaryDescriptor.hpp>
 #include <mesh/MeshTraits.hpp>
 #include <scheme/AcousticSolver.hpp>
 #include <scheme/AxisBoundaryConditionDescriptor.hpp>
diff --git a/src/scheme/HyperelasticSolver.cpp b/src/scheme/HyperelasticSolver.cpp
index ea1b0f525beb3cea6f29e29c460610cda2290258..b8679dbe2185bde0c3b1244b276831aaf86efb52 100644
--- a/src/scheme/HyperelasticSolver.cpp
+++ b/src/scheme/HyperelasticSolver.cpp
@@ -408,7 +408,7 @@ class HyperelasticSolverHandler::HyperelasticSolver final : public HyperelasticS
 #endif   // PUGS_HAS_COSTO
   void _applySymmetryBC(const BoundaryConditionList& bc_list, NodeValue<Rdxd>& Ar, NodeValue<Rd>& br) const;
   void _applyVelocityBC(const BoundaryConditionList& bc_list, NodeValue<Rdxd>& Ar, NodeValue<Rd>& br) const;
-  void _applyCouplingBC(const BoundaryConditionList& bc_list, NodeValue<Rdxd>& Ar, NodeValue<Rd>& br) const;
+  void _applyCouplingBC(const BoundaryConditionList& bc_list, const MeshType& mesh, NodeValue<Rdxd>& Ar, NodeValue<Rd>& br) const;
 
   void _applyCouplingBC2(const MeshType& mesh,
                          const BoundaryConditionList& bc_list,
@@ -498,7 +498,7 @@ class HyperelasticSolverHandler::HyperelasticSolver final : public HyperelasticS
 
     const BoundaryConditionList bc_list = this->_getBCList(mesh, bc_descriptor_list);
     this->_applyBoundaryConditions(bc_list, mesh, Ar, br);
-    this->_applyCouplingBC(bc_list, Ar, br);
+    this->_applyCouplingBC(bc_list, mesh, Ar, br);
 
     synchronize(Ar);
     synchronize(br);
@@ -864,7 +864,8 @@ HyperelasticSolverHandler::HyperelasticSolver<MeshType>::_applyVelocityBC(const
 template <MeshConcept MeshType>
 void
 HyperelasticSolverHandler::HyperelasticSolver<MeshType>::_applyCouplingBC(const BoundaryConditionList& bc_list,
-                                                                           NodeValue<Rdxd>& Ar,
+									  const MeshType& mesh,
+									  NodeValue<Rdxd>& Ar,
                                                                            NodeValue<Rd>& br) const
 {
   for (const auto& boundary_condition : bc_list) {
@@ -874,68 +875,102 @@ HyperelasticSolverHandler::HyperelasticSolver<MeshType>::_applyCouplingBC(const
         if constexpr (std::is_same_v<CouplingBoundaryCondition, T>) {
           const auto& node_list = bc.nodeList();
 
-        /* std::cout << "\033[01;31m" */
-        /*           << "un applyCoupling" */
-        /*           << "Dimension: " << Dimension << "\033[00;00m" << std::endl; */
-        /* std::cout << "\033[01;31m" */
-        /*           << "node_list.size()" << node_list.size() << "\033[00;00m" << std::endl; */
-
 #ifdef PUGS_HAS_COSTO
-          auto costo     = parallel::Messenger::getInstance().myCoupling;
-          const int dest = costo->myGlobalSize() - 1;
-          int tag        = 200;
+          auto costo     = parallel::Messenger::getInstance().myPyEmbeding;
+	  const auto node_is_owned = mesh.connectivity().nodeIsOwned().arrayView();
+
+
+          // auto costo     = parallel::Messenger::getInstance().myCoupling;
+          // const int dest = costo->myGlobalSize() - 1;
+          // int tag        = 200;
+
+	  // TO DO COMPUTE ONLY ONCE AT THE BEGINING
+	  size_t sz = 0;
+	  for (size_t i_node = 0; i_node < node_list.size(); ++i_node) {
+	    const NodeId r = node_list[i_node];
+	    if (node_is_owned[r]){
+	      sz += 1;
+	    }
+	  }
+          Array<TinyVector<Dimension>> br_extracted(node_list.size());
+          Array<TinyMatrix<Dimension>> Ar_extracted(node_list.size());
+
+
           std::vector<int> shape;
           shape.resize(3);
-          shape[0] = node_list.size();
+          shape[0] = sz;
           shape[1] = Dimension + Dimension * Dimension;
           /* shape[1] = Dimension; */
           shape[2] = 0;
 
           std::vector<double> data;
           data.resize(shape[0] * shape[1] + shape[2]);
-          Array<TinyVector<Dimension>> br_extracted(node_list.size());
-          Array<TinyMatrix<Dimension>> Ar_extracted(node_list.size());
+
           parallel_for(
             node_list.size(), PUGS_LAMBDA(size_t i_node) {
-              NodeId node_id = node_list[i_node];
-              for (size_t i_dim = 0; i_dim < Dimension; i_dim++) {
-                br_extracted[i_node] = br[node_id];
-                Ar_extracted[i_node] = Ar[node_id];
-              }
+              const NodeId node_id = node_list[i_node];
+	      if (node_is_owned[node_id]){
+		for (size_t i_dim = 0; i_dim < Dimension; i_dim++) {
+		  br_extracted[i_node] = br[node_id];
+		  Ar_extracted[i_node] = Ar[node_id];
+		}
+	      }
             });
+
+	  size_t ct_node = 0;
           /*TODO: serializer directement Ar et br pour eviter une copie*/
           for (size_t i_node = 0; i_node < node_list.size(); ++i_node) {
-            for (unsigned short i_dim = 0; i_dim < Dimension; ++i_dim) {
-              data[i_node * shape[1] + i_dim] = br_extracted[i_node][i_dim];
-              for (size_t j_dim = 0; j_dim < Dimension; j_dim++) {
-                data[i_node * shape[1] + Dimension + i_dim * Dimension + j_dim] = Ar_extracted[i_node](i_dim, j_dim);
-              }
-            }
+	    const NodeId r = node_list[i_node];
+	      if (node_is_owned[r]){
+		for (unsigned short i_dim = 0; i_dim < Dimension; ++i_dim) {
+		  data[ct_node * shape[1] + i_dim] = br_extracted[i_node][i_dim];
+		  for (size_t j_dim = 0; j_dim < Dimension; j_dim++) {
+		    data[ct_node * shape[1] + Dimension + i_dim * Dimension + j_dim] = Ar_extracted[i_node](i_dim, j_dim);
+		  }
+		}
+		ct_node += 1;
+	      }
           }
-          costo->sendData(shape, data, dest, tag);
-          std::vector<double> recv;
 
-          tag = 210;
-          costo->recvData(recv, dest, tag);
+	  costo->Contexts->populate_context_old("cpl_bc", "shape", shape);
+	  costo->Contexts->populate_context_old("cpl_bc", "Ar_Br", data);
+	  costo->call_py_function("cpl_bc");
+	  // costo->sendData(shape, data, dest, tag);
+          // std::vector<double> recv;
+
+          // tag = 210;
+          // costo->recvData(recv, dest, tag);
+	  ct_node = 0;
           for (size_t i_node = 0; i_node < node_list.size(); ++i_node) {
-            for (unsigned short i_dim = 0; i_dim < Dimension; ++i_dim) {
-              br_extracted[i_node][i_dim] = recv[i_node * shape[1] + i_dim];
-              for (size_t j_dim = 0; j_dim < Dimension; j_dim++) {
-                Ar_extracted[i_node](i_dim, j_dim) = recv[i_node * shape[1] + Dimension + i_dim * Dimension + j_dim];
-              }
-            }
+	    const NodeId r = node_list[i_node];
+	    if (node_is_owned[r]){
+	      for (unsigned short i_dim = 0; i_dim < Dimension; ++i_dim) {
+		br_extracted[i_node][i_dim] = data[ct_node * shape[1] + i_dim];
+		for (size_t j_dim = 0; j_dim < Dimension; j_dim++) {
+		  Ar_extracted[i_node](i_dim, j_dim) = data[ct_node * shape[1] + Dimension + i_dim * Dimension + j_dim];
+		}
+	      }
+		ct_node += 1;
+	    }
+
+	    // std::cout<<"Br: "<<br_extracted[i_node]<<std::endl;
+	    // std::cout<<"Ar: "<<Ar_extracted[i_node]<<std::endl;
           }
-          costo->m = static_cast<int>(recv[shape[0] * shape[1] + shape[2]]);
+          // costo->m = static_cast<int>(recv[shape[0] * shape[1] + shape[2]]);
+
+          // std::cout << "\033[01;32m"
+          //           << "q recv: " << costo->m << "\033[00;00m" << std::endl;
+
 
-          std::cout << "\033[01;32m"
-                    << "q recv: " << costo->m << "\033[00;00m" << std::endl;
           parallel_for(
             node_list.size(), PUGS_LAMBDA(size_t i_node) {
-              NodeId node_id = node_list[i_node];
-              for (size_t i_dim = 0; i_dim < Dimension; i_dim++) {
-                br[node_id] = br_extracted[i_node];
-                Ar[node_id] = Ar_extracted[i_node];
-              }
+              const NodeId node_id = node_list[i_node];
+	      if (node_is_owned[node_id]){
+		for (size_t i_dim = 0; i_dim < Dimension; i_dim++) {
+		  br[node_id] = br_extracted[i_node];
+		  Ar[node_id] = Ar_extracted[i_node];
+		}
+	      }
             });
 
 #endif   // PUGS_HAS_COSTO
@@ -1308,6 +1343,8 @@ class HyperelasticSolverHandler::HyperelasticSolver<MeshType>::CouplingBoundaryC
   CouplingBoundaryCondition(const MeshNodeBoundary& mesh_node_boundary)
     : m_mesh_node_boundary{mesh_node_boundary}
   {
+
+    // std::cout<<"Build coupling Boundary condition"<<std::endl;
     ;
   }
 
diff --git a/src/utils/Messenger.cpp b/src/utils/Messenger.cpp
index e8262c55457f46b0ec0a387afb580eafaed7b448..2c2466961a26d712d29c87dcedb5498560d93f62 100644
--- a/src/utils/Messenger.cpp
+++ b/src/utils/Messenger.cpp
@@ -35,8 +35,10 @@ Messenger::Messenger([[maybe_unused]] int& argc, [[maybe_unused]] char* argv[])
 
 #ifdef PUGS_HAS_COSTO
   if (CommunicatorManager::hasSplitColor()) {
-    myCoupling = new costo::coupling(CommunicatorManager::splitColor());
+    myPyEmbeding = new costo::embedingPython();
+    myPyEmbeding->call_py_function("cpl_initialize");
 
+    myCoupling = new costo::coupling(CommunicatorManager::splitColor());
     m_pugs_comm_world = myCoupling->getLocalComm();
     /*   auto res = */
     /*   MPI_Comm_split(MPI_COMM_WORLD, CommunicatorManager::splitColor(), key, &m_pugs_comm_world); */
diff --git a/src/utils/Messenger.hpp b/src/utils/Messenger.hpp
index d551fda3efbcd2eb11edc2e0583c223fe2b3ff29..9783efffa974383e47445fa80b11226b2871603b 100644
--- a/src/utils/Messenger.hpp
+++ b/src/utils/Messenger.hpp
@@ -18,6 +18,7 @@
 
 #ifdef PUGS_HAS_COSTO
 #include <CPO/couplingObjects.h>
+#include <CPO/embedingPython.h>
 #endif   // PUGS_HAS_COSTO
 
 #include <utils/CommunicatorManager.hpp>
@@ -413,6 +414,8 @@ class Messenger
  public:
 #ifdef PUGS_HAS_COSTO
   costo::coupling* myCoupling = nullptr;
+  costo::embedingPython* myPyEmbeding;
+
 #endif   // PUGS_HAS_COSTO
   static void create(int& argc, char* argv[]);
   static void destroy();
diff --git a/src/utils/Serializer.cpp b/src/utils/Serializer.cpp
index 9227e2ec7c3e629afce9e4afb9d56c0549fcfcff..a28b5a20c5dd38e7ec4f782d1a712de040913314 100644
--- a/src/utils/Serializer.cpp
+++ b/src/utils/Serializer.cpp
@@ -11,7 +11,6 @@ Serializer::mesh(const std::shared_ptr<const MeshVariant> mesh_v,
   if (not mesh_v) {
     throw NormalError("discrete functions are not defined on the same mesh");
   }
-
   std::visit(
     [&](auto&& p_mesh) {
       using MeshType = mesh_type_t<decltype(p_mesh)>;
@@ -62,6 +61,82 @@ Serializer::mesh(const std::shared_ptr<const MeshVariant> mesh_v,
     mesh_v->variant());
 }
 
+double Serializer::cpl_get_dt(const double& dt)
+{
+  std::vector<double> new_dt;
+  new_dt.resize(1);
+  new_dt[0] = dt*1.0;
+
+  auto costo = parallel::Messenger::getInstance().myPyEmbeding;
+  costo->Contexts->populate_context_old("cpl_get_dt", "dt", new_dt);
+  costo->call_py_function("cpl_get_dt");
+  return new_dt[0];
+}
+
+
+void
+Serializer::build_graph(const std::shared_ptr<const MeshVariant> mesh_v,
+			const IBoundaryDescriptor& boundary)
+{
+
+  if (not mesh_v) {
+    throw NormalError("discrete functions are not defined on the same mesh");
+  }
+  std::visit(
+    [&](auto&& p_mesh) {
+      using MeshType = mesh_type_t<decltype(p_mesh)>;
+      static const auto Dimension = MeshType::Dimension;
+      using Rd           = TinyVector<Dimension>;
+      if constexpr (is_polygonal_mesh_v<MeshType>) {
+	auto costo     = parallel::Messenger::getInstance().myPyEmbeding;
+	std::vector<int> shape;
+	std::vector<double> pts;
+	const std::shared_ptr mesh    = std::dynamic_pointer_cast<const MeshType>(p_mesh);
+
+	const auto xr = mesh->xr();
+
+	const auto& connectivity = mesh->connectivity();
+	const auto node_is_owned = connectivity.nodeIsOwned().arrayView();
+
+	MeshNodeBoundary mesh_node_boundary = getMeshNodeBoundary(*mesh, boundary);
+	const auto node_list = mesh_node_boundary.nodeList();
+
+	Array<TinyVector<Dimension>> positions(node_list.size());
+
+	size_t sz = 0;
+	for (size_t i_node = 0; i_node < node_list.size(); ++i_node) {
+	  const NodeId r = node_list[i_node];
+	  std::cout<<"Serialize node id: "<<r<<std::endl;
+	  if (node_is_owned[r]){
+	    for (unsigned short i = 0; i < Dimension; ++i) {
+	      positions[sz][i] = xr[r][i];
+	    }
+	    sz += 1;
+	  }
+	}
+
+	/*TODO: serializer directement position pour eviter une copie*/
+	pts.resize(sz * Dimension);
+	for (unsigned short r = 0; r < sz; ++r) {
+	  for (unsigned short j = 0; j < Dimension; ++j) {
+	    pts[Dimension * r + j] = positions[r][j];
+	  }
+	}
+	shape.resize(3);
+	shape[0] = sz;
+	shape[1] = Dimension;
+	shape[2] = 0;
+
+	costo->Contexts->populate_context_old("cpl_build_graph", "shape", shape);
+	costo->Contexts->populate_context_old("cpl_build_graph", "mesh", pts);
+	costo->call_py_function("cpl_build_graph");
+      } else {
+        throw NormalError("unexpected mesh type");
+      }
+    },
+    mesh_v->variant());
+}
+
 void Serializer::interfaces(const std::shared_ptr<const MeshVariant> mesh_v,
 			    const std::vector<std::shared_ptr<const IInterfaceDescriptor>>& i_interface_list,
 			    const int64_t dest,
diff --git a/src/utils/Serializer.hpp b/src/utils/Serializer.hpp
index 0060afd7937be5862d2bb4bfe4561b39c3644615..20bd6a9980ff4b829ae2e81c2f6b1147858bfab2 100644
--- a/src/utils/Serializer.hpp
+++ b/src/utils/Serializer.hpp
@@ -59,6 +59,20 @@ public:
 	    const int64_t dest,
 	    const int64_t tag);
 
+
+
+  void build_graph(const std::shared_ptr<const MeshVariant> mesh_v,
+		   const std::shared_ptr<const IBoundaryDescriptor>& boundary)
+  {
+    this->build_graph(mesh_v,*boundary);
+  }
+
+  void build_graph(const std::shared_ptr<const MeshVariant> mesh_v,
+		   const IBoundaryDescriptor& boundary);
+
+  double cpl_get_dt(const double& dt);
+
+
   void
   BC(const std::shared_ptr<const IBoundaryDescriptor>& boundary,
      const std::shared_ptr<const MeshVariant> mesh_v,