diff --git a/CMakeLists.txt b/CMakeLists.txt
index fb52f0b84b67d28f2af26fc316e5a1c904faa28a..d16a35f74b981443162a2267cd42a5ebb04a42d7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -352,217 +352,230 @@ include_directories(${PUGS_SOURCE_DIR}/src)
 include_directories(${PUGS_BINARY_DIR}/src)
 
 # Pugs tests
-set(CATCH_MODULE_PATH "${PUGS_SOURCE_DIR}/packages/Catch2")
-add_subdirectory("${CATCH_MODULE_PATH}")
+# set(CATCH_MODULE_PATH "${PUGS_SOURCE_DIR}/packages/Catch2")
+# add_subdirectory("${CATCH_MODULE_PATH}")
 
-add_subdirectory(tests)
+# add_subdirectory(tests)
 
-if(${PUGS_HAS_MPI})
-  set(MPIEXEC_OPTION_FLAGS --oversubscribe)
-  if (NOT "$ENV{GITLAB_CI}" STREQUAL "")
-    set(MPIEXEC_OPTION_FLAGS ${MPIEXEC_OPTION_FLAGS} --allow-run-as-root)
-  endif()
-  set(MPIEXEC_NUMPROC 3)
-  set(MPIEXEC_PATH_FLAG --path)
-  set(MPI_LAUNCHER ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MPIEXEC_NUMPROC} ${MPIEXEC_PATH_FLAG} ${PUGS_BINARY_DIR} ${MPIEXEC_OPTION_FLAGS})
-endif()
-
-add_custom_target(all_unit_tests
-  DEPENDS unit_tests mpi_unit_tests
-)
-
-add_custom_target(check
-  DEPENDS test
-  )
-
-add_custom_target(test
-  DEPENDS run_all_unit_tests
-  )
-
-add_custom_target(run_all_unit_tests
-  DEPENDS run_mpi_unit_tests
-  )
+# if(${PUGS_HAS_MPI})
+#   set(MPIEXEC_OPTION_FLAGS --oversubscribe)
+#   if (NOT "$ENV{GITLAB_CI}" STREQUAL "")
+#     set(MPIEXEC_OPTION_FLAGS ${MPIEXEC_OPTION_FLAGS} --allow-run-as-root)
+#   endif()
+#   set(MPIEXEC_NUMPROC 3)
+#   set(MPIEXEC_PATH_FLAG --path)
+#   set(MPI_LAUNCHER ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MPIEXEC_NUMPROC} ${MPIEXEC_PATH_FLAG} ${PUGS_BINARY_DIR} ${MPIEXEC_OPTION_FLAGS})
+# endif()
 
-if(PUGS_HAS_MPI)
-  set(RUN_MPI_UNIT_TESTS_COMMENT "Running mpi_unit_tests [using ${MPIEXEC_NUMPROC} procs]")
-else()
-  set(RUN_MPI_UNIT_TESTS_COMMENT "Running mpi_unit_tests [sequentially]")
-endif()
+# add_custom_target(all_unit_tests
+#   DEPENDS unit_tests mpi_unit_tests
+# )
 
-add_custom_target(run_mpi_unit_tests
-  COMMAND ${MPI_LAUNCHER} "${PUGS_BINARY_DIR}/mpi_unit_tests"
-  DEPENDS run_unit_tests
-  COMMENT ${RUN_MPI_UNIT_TESTS_COMMENT}
-  )
+# add_custom_target(check
+#   DEPENDS test
+#   )
 
+# add_custom_target(test
+#   DEPENDS run_all_unit_tests
+#   )
 
-add_custom_target(run_unit_tests
-  COMMAND "${PUGS_BINARY_DIR}/unit_tests"
-  DEPENDS all_unit_tests
-  COMMENT "Running unit_tests"
-  )
+# add_custom_target(run_all_unit_tests
+#   DEPENDS run_mpi_unit_tests
+#   )
 
-# unit tests coverage
-
-if("${CMAKE_BUILD_TYPE}" STREQUAL "Coverage")
-  file(GLOB_RECURSE GCNO_FILE_LIST RELATIVE "${PUGS_BINARY_DIR}"  "*.gcno")
-  list(FILTER GCNO_FILE_LIST EXCLUDE REGEX "^\.\./.*" )
-  file(GLOB_RECURSE GCDA_FILE_LIST RELATIVE "${PUGS_BINARY_DIR}"  "*.gcda")
-  list(FILTER GCDA_FILE_LIST EXCLUDE REGEX "^\.\./.*" )
-  foreach(COV_INFO_FILE IN LISTS GCNO_FILE_LIST GCDA_FILE_LIST)
-    string(REGEX REPLACE "/CMakeFiles/.*\.dir/" "/" COV_SRC_FILE "${PUGS_SOURCE_DIR}/${COV_INFO_FILE}")
-    string(REGEX REPLACE "\.gcda$" "" COV_SRC_FILE "${COV_SRC_FILE}")
-    string(REGEX REPLACE "\.gcno$" "" COV_SRC_FILE "${COV_SRC_FILE}")
-    if (NOT EXISTS "${COV_SRC_FILE}")
-      file(REMOVE "${PUGS_BINARY_DIR}/${COV_INFO_FILE}")
-      message(STATUS "removed file ${COV_INFO_FILE}: no longer needed")
-    endif()
-  endforeach()
+# if(PUGS_HAS_MPI)
+#   set(RUN_MPI_UNIT_TESTS_COMMENT "Running mpi_unit_tests [using ${MPIEXEC_NUMPROC} procs]")
+# else()
+#   set(RUN_MPI_UNIT_TESTS_COMMENT "Running mpi_unit_tests [sequentially]")
+# endif()
 
-  find_program(LCOV lcov)
-  if(NOT LCOV)
-    message(FATAL_ERROR "lcov not found, cannot perform coverage.")
-  endif()
+# add_custom_target(run_mpi_unit_tests
+#   COMMAND ${MPI_LAUNCHER} "${PUGS_BINARY_DIR}/mpi_unit_tests"
+#   DEPENDS run_unit_tests
+#   COMMENT ${RUN_MPI_UNIT_TESTS_COMMENT}
+#   )
+
+
+# add_custom_target(run_unit_tests
+#   COMMAND "${PUGS_BINARY_DIR}/unit_tests"
+#   DEPENDS all_unit_tests
+#   COMMENT "Running unit_tests"
+#   )
+
+# # unit tests coverage
+
+# if("${CMAKE_BUILD_TYPE}" STREQUAL "Coverage")
+#   file(GLOB_RECURSE GCNO_FILE_LIST RELATIVE "${PUGS_BINARY_DIR}"  "*.gcno")
+#   list(FILTER GCNO_FILE_LIST EXCLUDE REGEX "^\.\./.*" )
+#   file(GLOB_RECURSE GCDA_FILE_LIST RELATIVE "${PUGS_BINARY_DIR}"  "*.gcda")
+#   list(FILTER GCDA_FILE_LIST EXCLUDE REGEX "^\.\./.*" )
+#   foreach(COV_INFO_FILE IN LISTS GCNO_FILE_LIST GCDA_FILE_LIST)
+#     string(REGEX REPLACE "/CMakeFiles/.*\.dir/" "/" COV_SRC_FILE "${PUGS_SOURCE_DIR}/${COV_INFO_FILE}")
+#     string(REGEX REPLACE "\.gcda$" "" COV_SRC_FILE "${COV_SRC_FILE}")
+#     string(REGEX REPLACE "\.gcno$" "" COV_SRC_FILE "${COV_SRC_FILE}")
+#     if (NOT EXISTS "${COV_SRC_FILE}")
+#       file(REMOVE "${PUGS_BINARY_DIR}/${COV_INFO_FILE}")
+#       message(STATUS "removed file ${COV_INFO_FILE}: no longer needed")
+#     endif()
+#   endforeach()
+
+#   find_program(LCOV lcov)
+#   if(NOT LCOV)
+#     message(FATAL_ERROR "lcov not found, cannot perform coverage.")
+#   endif()
 
-  if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
-    string(REGEX MATCH "^[0-9]+" GCC_VERSION
-      "${CMAKE_CXX_COMPILER_VERSION}")
-    find_program(GCOV_BIN NAMES gcov-${GCC_VERSION} gcov
-      HINTS ${COMPILER_PATH})
-  elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
-    string(REGEX MATCH "^[0-9]+" LLVM_VERSION
-      "${CMAKE_CXX_COMPILER_VERSION}")
+#   if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
+#     string(REGEX MATCH "^[0-9]+" GCC_VERSION
+#       "${CMAKE_CXX_COMPILER_VERSION}")
+#     find_program(GCOV_BIN NAMES gcov-${GCC_VERSION} gcov
+#       HINTS ${COMPILER_PATH})
+#   elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+#     string(REGEX MATCH "^[0-9]+" LLVM_VERSION
+#       "${CMAKE_CXX_COMPILER_VERSION}")
+
+#     if(LLVM_VERSION VERSION_GREATER 7)
+#       find_program(LLVM_COV_BIN NAMES "llvm-cov-${LLVM_VERSION}"
+#         "llvm-cov" HINTS ${COMPILER_PATH})
+#       mark_as_advanced(LLVM_COV_BIN)
+
+#       if (LLVM_COV_BIN)
+#         file(MAKE_DIRECTORY "${PUGS_BINARY_DIR}/tools/tmp")
+
+#         file(WRITE "${PUGS_BINARY_DIR}/tools/tmp/llvm-cov.sh" "#! /bin/sh\n")
+#         file(APPEND "${PUGS_BINARY_DIR}/tools/tmp/llvm-cov.sh" "'${LLVM_COV_BIN}' gcov \"\$@\"\n")
+
+#         file(COPY "${PUGS_BINARY_DIR}/tools/tmp/llvm-cov.sh"
+#              DESTINATION "${PUGS_BINARY_DIR}/tools"
+#              FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE)
+
+#         set(GCOV_BIN "${PUGS_BINARY_DIR}/tools/llvm-cov.sh")
+#       endif()
+#     endif()
+#   endif()
 
-    if(LLVM_VERSION VERSION_GREATER 7)
-      find_program(LLVM_COV_BIN NAMES "llvm-cov-${LLVM_VERSION}"
-        "llvm-cov" HINTS ${COMPILER_PATH})
-      mark_as_advanced(LLVM_COV_BIN)
+#   if(NOT GCOV_BIN)
+#      message(FATAL_ERROR "Cannot find a proper gcov tool, cannot perform coverage.")
+#   endif()
 
-      if (LLVM_COV_BIN)
-        file(MAKE_DIRECTORY "${PUGS_BINARY_DIR}/tools/tmp")
+#   find_program(FASTCOV fastcov fastcov.py)
 
-        file(WRITE "${PUGS_BINARY_DIR}/tools/tmp/llvm-cov.sh" "#! /bin/sh\n")
-        file(APPEND "${PUGS_BINARY_DIR}/tools/tmp/llvm-cov.sh" "'${LLVM_COV_BIN}' gcov \"\$@\"\n")
+#   add_custom_target(coverage_unit_tests
+#     ALL # in coverage mode we do coverage!
 
-        file(COPY "${PUGS_BINARY_DIR}/tools/tmp/llvm-cov.sh"
-             DESTINATION "${PUGS_BINARY_DIR}/tools"
-             FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE)
+#     COMMAND "${PUGS_BINARY_DIR}/unit_tests"
 
-        set(GCOV_BIN "${PUGS_BINARY_DIR}/tools/llvm-cov.sh")
-      endif()
-    endif()
-  endif()
+#     COMMENT "Running unit_tests"
+#     DEPENDS coverage_zero_counters
+#     )
 
-  if(NOT GCOV_BIN)
-     message(FATAL_ERROR "Cannot find a proper gcov tool, cannot perform coverage.")
-  endif()
+#   add_custom_target(coverage_mpi_unit_tests
+#     ALL # in coverage mode we do coverage!
 
-  find_program(FASTCOV fastcov fastcov.py)
+#     COMMAND ${MPI_LAUNCHER} "mpi_unit_tests"
 
-  add_custom_target(coverage_unit_tests
-    ALL # in coverage mode we do coverage!
+#     COMMENT "Running mpi_unit_tests"
+#     DEPENDS coverage_unit_tests
+#     )
 
-    COMMAND "${PUGS_BINARY_DIR}/unit_tests"
+#   add_custom_target(coverage_run_all_unit_tests
+#     ALL # in coverage mode we do coverage!
 
-    COMMENT "Running unit_tests"
-    DEPENDS coverage_zero_counters
-    )
+#     DEPENDS coverage_mpi_unit_tests
+#     )
 
-  add_custom_target(coverage_mpi_unit_tests
-    ALL # in coverage mode we do coverage!
+#   if (FASTCOV AND (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "9"))
 
-    COMMAND ${MPI_LAUNCHER} "mpi_unit_tests"
+#     add_custom_target(coverage_zero_counters
+#       ALL # in coverage mode we do coverage!
 
-    COMMENT "Running mpi_unit_tests"
-    DEPENDS coverage_unit_tests
-    )
+#       # zero all counters
+#       COMMAND ${FASTCOV} -q -z --gcov "${GCOV_BIN}"
+#       DEPENDS all_unit_tests
+#       )
 
-  add_custom_target(coverage_run_all_unit_tests
-    ALL # in coverage mode we do coverage!
+#     add_custom_target(coverage
+#       ALL # in coverage mode we do coverage!
 
-    DEPENDS coverage_mpi_unit_tests
-    )
+#       COMMAND ${FASTCOV} --gcov "${GCOV_BIN}"
+#       --include "${PUGS_SOURCE_DIR}/src"
+#       --exclude "${PUGS_SOURCE_DIR}/src/main.cpp" "${PUGS_SOURCE_DIR}/src/utils/BacktraceManager.*" "${PUGS_SOURCE_DIR}/src/utils/FPEManager.*" "${PUGS_SOURCE_DIR}/src/utils/SignalManager.*"
+#       --lcov -o coverage.info -n
 
-  if (FASTCOV AND (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "9"))
+#       COMMAND ${LCOV} --gcov "${GCOV_BIN}" --list coverage.info
 
-    add_custom_target(coverage_zero_counters
-      ALL # in coverage mode we do coverage!
+#       DEPENDS coverage_run_all_unit_tests
+#       COMMENT "Running test coverage."
+#       WORKING_DIRECTORY "${PUGS_BINARY_DIR}"
+#       )
 
-      # zero all counters
-      COMMAND ${FASTCOV} -q -z --gcov "${GCOV_BIN}"
-      DEPENDS all_unit_tests
-      )
+#   else()
 
-    add_custom_target(coverage
-      ALL # in coverage mode we do coverage!
+#     add_custom_target(coverage_zero_counters
+#       ALL # in coverage mode we do coverage!
+#       # Cleanup previously generated profiling data
+#       COMMAND ${LCOV} -q --gcov "${GCOV_BIN}" --base-directory "${PUGS_BINARY_DIR}/src" --directory "${PUGS_BINARY_DIR}" --zerocounters
+#       # Initialize profiling data with zero coverage for every instrumented line of the project
+#       # This way the percentage of total lines covered will always be correct, even when not all source code files were loaded during the test(s)
+#       COMMAND ${LCOV} -q --gcov "${GCOV_BIN}" --base-directory "${PUGS_BINARY_DIR}/src" --directory "${PUGS_BINARY_DIR}" --capture --initial --output-file coverage_base.info
+#       DEPENDS all_unit_tests
+#       )
 
-      COMMAND ${FASTCOV} --gcov "${GCOV_BIN}"
-      --include "${PUGS_SOURCE_DIR}/src"
-      --exclude "${PUGS_SOURCE_DIR}/src/main.cpp" "${PUGS_SOURCE_DIR}/src/utils/BacktraceManager.*" "${PUGS_SOURCE_DIR}/src/utils/FPEManager.*" "${PUGS_SOURCE_DIR}/src/utils/SignalManager.*"
-      --lcov -o coverage.info -n
 
-      COMMAND ${LCOV} --gcov "${GCOV_BIN}" --list coverage.info
+#     add_custom_target(coverage
+#       # Collect data from executions
+#       COMMAND ${LCOV} --gcov "${GCOV_BIN}" --base-directory "${PUGS_BINARY_DIR}/src" --directory "${PUGS_BINARY_DIR}" --capture --output-file coverage_ctest.info
+#       # Combine base and ctest results
+#       COMMAND ${LCOV} --gcov "${GCOV_BIN}" -q --add-tracefile coverage_base.info --add-tracefile coverage_ctest.info --output-file coverage_full.info
+#       # Extract only project data (--no-capture or --remove options may be used to select collected data)
+#       COMMAND ${LCOV} --gcov "${GCOV_BIN}" -q --extract coverage_full.info "'${PUGS_SOURCE_DIR}/src/*'" --output-file coverage_extract.info
 
-      DEPENDS coverage_run_all_unit_tests
-      COMMENT "Running test coverage."
-      WORKING_DIRECTORY "${PUGS_BINARY_DIR}"
-      )
+#       # Remove unwanted stuff
+#       COMMAND ${LCOV} --gcov "${GCOV_BIN}" --remove coverage_extract.info  --output-file coverage.info
+#       '${PUGS_SOURCE_DIR}/src/main.cpp'
+#       '${PUGS_SOURCE_DIR}/src/utils/BacktraceManager.*'
 
-  else()
+#       COMMAND ${LCOV} --gcov "${GCOV_BIN}" --list coverage.info
 
-    add_custom_target(coverage_zero_counters
-      ALL # in coverage mode we do coverage!
-      # Cleanup previously generated profiling data
-      COMMAND ${LCOV} -q --gcov "${GCOV_BIN}" --base-directory "${PUGS_BINARY_DIR}/src" --directory "${PUGS_BINARY_DIR}" --zerocounters
-      # Initialize profiling data with zero coverage for every instrumented line of the project
-      # This way the percentage of total lines covered will always be correct, even when not all source code files were loaded during the test(s)
-      COMMAND ${LCOV} -q --gcov "${GCOV_BIN}" --base-directory "${PUGS_BINARY_DIR}/src" --directory "${PUGS_BINARY_DIR}" --capture --initial --output-file coverage_base.info
-      DEPENDS all_unit_tests
-      )
-
-
-    add_custom_target(coverage
-      # Collect data from executions
-      COMMAND ${LCOV} --gcov "${GCOV_BIN}" --base-directory "${PUGS_BINARY_DIR}/src" --directory "${PUGS_BINARY_DIR}" --capture --output-file coverage_ctest.info
-      # Combine base and ctest results
-      COMMAND ${LCOV} --gcov "${GCOV_BIN}" -q --add-tracefile coverage_base.info --add-tracefile coverage_ctest.info --output-file coverage_full.info
-      # Extract only project data (--no-capture or --remove options may be used to select collected data)
-      COMMAND ${LCOV} --gcov "${GCOV_BIN}" -q --extract coverage_full.info "'${PUGS_SOURCE_DIR}/src/*'" --output-file coverage_extract.info
-
-      # Remove unwanted stuff
-      COMMAND ${LCOV} --gcov "${GCOV_BIN}" --remove coverage_extract.info  --output-file coverage.info
-      '${PUGS_SOURCE_DIR}/src/main.cpp'
-      '${PUGS_SOURCE_DIR}/src/utils/BacktraceManager.*'
-
-      COMMAND ${LCOV} --gcov "${GCOV_BIN}" --list coverage.info
-
-      DEPENDS coverage_run_all_unit_tests
-      COMMENT "Running test coverage."
-      WORKING_DIRECTORY "${PUGS_BINARY_DIR}"
-      )
-  endif()
+#       DEPENDS coverage_run_all_unit_tests
+#       COMMENT "Running test coverage."
+#       WORKING_DIRECTORY "${PUGS_BINARY_DIR}"
+#       )
+#   endif()
 
-  find_program(GENHTML genhtml)
-  if(NOT GENHTML)
-    message(WARNING "genhtml not found, cannot perform report-coverage.")
-  else()
-    add_custom_target(coverage-report
-      ALL
-      COMMAND ${CMAKE_COMMAND} -E remove_directory "${PUGS_BINARY_DIR}/coverage"
-      COMMAND ${CMAKE_COMMAND} -E make_directory "${PUGS_BINARY_DIR}/coverage"
-      COMMAND ${GENHTML} --demangle-cpp -q -o coverage -t "${CMAKE_PROJECT_NAME} test coverage" --ignore-errors source --legend --num-spaces 2 coverage.info
-      DEPENDS coverage
-      COMMENT "Building coverage html report."
-      WORKING_DIRECTORY "${PUGS_BINARY_DIR}"
-      )
-  endif()
-endif()
+#   find_program(GENHTML genhtml)
+#   if(NOT GENHTML)
+#     message(WARNING "genhtml not found, cannot perform report-coverage.")
+#   else()
+#     add_custom_target(coverage-report
+#       ALL
+#       COMMAND ${CMAKE_COMMAND} -E remove_directory "${PUGS_BINARY_DIR}/coverage"
+#       COMMAND ${CMAKE_COMMAND} -E make_directory "${PUGS_BINARY_DIR}/coverage"
+#       COMMAND ${GENHTML} --demangle-cpp -q -o coverage -t "${CMAKE_PROJECT_NAME} test coverage" --ignore-errors source --legend --num-spaces 2 coverage.info
+#       DEPENDS coverage
+#       COMMENT "Building coverage html report."
+#       WORKING_DIRECTORY "${PUGS_BINARY_DIR}"
+#       )
+#   endif()
+# endif()
 
 # -----------------------------------------------------
 
 link_libraries("-rdynamic")
 
 # ------------------- Source files --------------------
+set(COSTO_INSTALL_DIR "/home/chantraitt/softs/04-costo/install/depot_git")
+
+# add_library(costo SHARED IMPORTED) # or STATIC instead of SHARED
+# set_target_properties(costo PROPERTIES
+#   IMPORTED_LOCATION "${COSTO_INSTALL_DIR}/lib/libcosto.so"
+#   INTERFACE_INCLUDE_DIRECTORIES "${COSTO_INSTALL_DIR}/includes"
+#   )
+add_library(CPO SHARED IMPORTED) # or STATIC instead of SHARED
+set_target_properties(CPO PROPERTIES
+  IMPORTED_LOCATION "${COSTO_INSTALL_DIR}/lib/libCPO.so"
+  INTERFACE_INCLUDE_DIRECTORIES "${COSTO_INSTALL_DIR}/includes"
+  )
+
 # Pugs binary
 add_executable(
   pugs
@@ -594,6 +607,7 @@ target_link_libraries(
   ${OPENMP_LINK_FLAGS}
   ${PUGS_STD_LINK_FLAGS}
   stdc++fs
+  CPO
   )
 
 # -------------------- Documentation --------------------
diff --git a/src/language/modules/CouplageModule.cpp b/src/language/modules/CouplageModule.cpp
index 1a9c1b16c2a2c2be5415afb4e33bfbdfee6d5769..e8750597c493d67f992a76088f3d605b844ecefc 100644
--- a/src/language/modules/CouplageModule.cpp
+++ b/src/language/modules/CouplageModule.cpp
@@ -7,8 +7,14 @@
 #include <memory>
 #include <mesh/Connectivity.hpp>
 #include <mesh/Mesh.hpp>
+#include <mesh/MeshNodeBoundary.hpp>
+#include <scheme/DirichletBoundaryConditionDescriptor.hpp>
+#include <scheme/FixedBoundaryConditionDescriptor.hpp>
+#include <scheme/IBoundaryConditionDescriptor.hpp>
+#include <scheme/IDiscreteFunction.hpp>
 #include <utils/CommunicatorManager.hpp>
 #include <utils/Messenger.hpp>
+#include <utils/Serializer.hpp>
 #ifdef PUGS_HAS_MPI
 #include <mpi.h>
 #endif   // PUGS_HAS_MPI
@@ -27,6 +33,58 @@ CouplageModule::CouplageModule()
                                       }
 
                                       ));
+  this->_addBuiltinFunction("test2", std::function(
+
+                                       [](const std::shared_ptr<const IMesh>& i_mesh,
+                                          const std::shared_ptr<const IBoundaryDescriptor>& boundary) -> void {
+                                         const auto dim = i_mesh->dimension();
+                                         std::cout << "\033[01;31m"
+                                                   << "DIM:" << dim << "\033[00;00m" << std::endl;
+
+                                         /* for (const auto& bc_descriptor : bc_descriptor_list) { */
+                                         /*   switch (bc_descriptor->type()) { */
+                                         /*   case IBoundaryConditionDescriptor::Type::dirichlet: { */
+                                         /*     std::cout << "\033[01;31m" */
+                                         /*               << "LA DIRICHLET" */
+                                         /*               << "\033[00;00m" << std::endl; */
+                                         /*     break; */
+                                         /*   } */
+                                         /*   default: { */
+                                         /*     std::cout << "\033[01;31m" */
+                                         /*               << "NO TYPE" */
+                                         /*               << "\033[00;00m" << std::endl; */
+                                         /*   } */
+                                         /*   } */
+                                         /* } */
+                                         /* auto& bc_descriptor = bc_descriptor_list[0]; */
+                                         /* std::cout << "\033[01;31m" */
+                                         /*           << "BC_type" << bc_descriptor->type() << "\033[00;00m" <<
+                                          * std::endl; */
+
+                                         /* const DirichletBoundaryConditionDescriptor&
+                                     extern_bc_descriptor = */
+                                         /*   dynamic_cast<const
+                                          * DirichletBoundaryConditionDescriptor&>(*bc_descriptor);
+                                          */
+                                         /* const MeshNodeBoundary<2> mesh_node_boundary = */
+                                         /*   getMeshNodeBoundary(i_mesh, extern_bc_descriptor->boundaryDescriptor());
+                                          */
+                                         /* auto size = mesh_node_boundary.nodeList().size(); */
+
+                                         std::cout << "\033[01;31m"
+                                                   << "Coucou"
+                                                   << "\033[00;00m" << std::endl;
+                                       }
+
+                                       ));
+  this->_addBuiltinFunction("serialize", std::function(
+
+                                           [](const std::shared_ptr<const IBoundaryDescriptor>& boundary,
+                                              const std::shared_ptr<const IDiscreteFunction>& field) -> void {
+                                             return Serializer(boundary, field).run();
+                                           }
+
+                                           ));
   this->_addBuiltinFunction("test", std::function(
 
                                       [](const std::shared_ptr<const IMesh>& i_mesh) -> void {
@@ -46,16 +104,9 @@ CouplageModule::CouplageModule()
                                         auto nFaces = p_mesh->numberOfFaces();
                                         std::cout << "n faces: " << nFaces << std::endl;
                                         const auto nodes = p_mesh->xr();
-#ifdef PUGS_HAS_MPI
 
-                                        if (CommunicatorManager::hasSplitColor()) {
-                                          std::cout << "\033[01;31m"
-                                                    << "COUPLED"
-                                                    << "\033[00;00m" << std::endl;
-                                          auto COMM_WORLD = parallel::Messenger::getInstance().globalComm();
-                                          auto COMM_LOCAL = parallel::Messenger::getInstance().comm();
-                                        }
-#endif   // PUGS_HAS_MPI
+                                        /* if (CommunicatorManager::hasSplitColor()) { */
+                                        /* } */
                                       }
 
                                       ));
diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt
index 1cce13c8daf68e2c61ceeeecb44dab4e3f2bf4e7..ccde036a316f8337c5170f422366d611885239b8 100644
--- a/src/utils/CMakeLists.txt
+++ b/src/utils/CMakeLists.txt
@@ -17,12 +17,23 @@ add_library(
   RevisionInfo.cpp
   SignalManager.cpp
   SLEPcWrapper.cpp
-  Socket.cpp)
+  Serializer.cpp
+  Socket.cpp
+  )
+
+set(COSTO_INSTALL_DIR "/home/chantraitt/softs/04-costo/install/depot_git")
+
+add_library(costo SHARED IMPORTED) # or STATIC instead of SHARED
+set_target_properties(costo PROPERTIES
+  IMPORTED_LOCATION "${COSTO_INSTALL_DIR}/lib/libcosto.so"
+  INTERFACE_INCLUDE_DIRECTORIES "${COSTO_INSTALL_DIR}/includes"
+  )
 
 target_link_libraries(
   PugsUtils
   ${PETSC_LIBRARIES}
   ${SLEPC_LIBRARIES}
+  costo
 )
 
 # --------------- get git revision info ---------------
diff --git a/src/utils/Messenger.cpp b/src/utils/Messenger.cpp
index 05dfa43e5a22f5223fd6c1e8291302c5366f5015..6a17cdae7f66c10d07c8392691ef329f7ae498b1 100644
--- a/src/utils/Messenger.cpp
+++ b/src/utils/Messenger.cpp
@@ -1,5 +1,6 @@
 #include <utils/Messenger.hpp>
 
+#include "couplingObjects.hpp"
 #include <utils/CommunicatorManager.hpp>
 
 #include <iostream>
@@ -34,12 +35,13 @@ Messenger::Messenger([[maybe_unused]] int& argc, [[maybe_unused]] char* argv[])
   MPI_Init(&argc, &argv);
 
   if (CommunicatorManager::hasSplitColor()) {
-    int key = 0;
-
-    auto res = MPI_Comm_split(MPI_COMM_WORLD, CommunicatorManager::splitColor(), key, &m_pugs_comm_world);
-    if (res) {
-      MPI_Abort(MPI_COMM_WORLD, res);
-    }
+    costo::coupling myCoupling = 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); */
+    /* if (res) { */
+    /*   MPI_Abort(MPI_COMM_WORLD, res); */
+    /* } */
   }
 
   m_rank = [&]() {
diff --git a/src/utils/Serializer.cpp b/src/utils/Serializer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..77bb699960872e1f267d6f531518c2b915b5bc40
--- /dev/null
+++ b/src/utils/Serializer.cpp
@@ -0,0 +1,74 @@
+#include <mesh/MeshNodeBoundary.hpp>
+#include <utils/Serializer.hpp>
+
+void
+Serializer::run()
+{
+  std::cout << "\033[01;31m"
+            << "here i am "
+            << "\033[00;00m" << std::endl;
+}
+
+Serializer::Serializer(const std::shared_ptr<const IBoundaryDescriptor>& boundary,
+                       const std::shared_ptr<const IDiscreteFunction>& field)
+{
+  const std::shared_ptr i_mesh = getCommonMesh({field});
+  if (not i_mesh) {
+    throw NormalError("primal discrete functions are not defined on the same mesh");
+  }
+  std::cout << "\033[01;31m"
+            << "LA"
+            << "\033[00;00m" << std::endl;
+  switch (i_mesh->dimension()) {
+  case 1: {
+    break;
+  }
+  case 2: {
+    using MeshType = Mesh<Connectivity<2>>;
+    /* assert(MeshType(i_mesh) == field->mesh()); */
+    std::cout << "\033[01;31m"
+              << "DIM 2D"
+              << "\033[00;00m" << std::endl;
+    const std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh);
+    /* const IBoundaryDescriptor& bd = *boundary; */
+    /* const Mesh<Connectivity<2>> mt = <const MeshType>(i_mesh); */
+
+    /* const auto& n_list = MeshNodeBoundary<2>(mt, bd); */
+
+    using Rd                      = TinyVector<MeshType::Dimension>;
+    const NodeValue<const Rd>& xr = mesh->xr();
+    Array<TinyVector<2>> positions(mesh->numberOfNodes());
+    parallel_for(
+      mesh->numberOfNodes(), PUGS_LAMBDA(NodeId r) {
+        for (unsigned short i = 0; i < MeshType::Dimension; ++i) {
+          positions[r][i] = xr[r][i];
+        }
+        /* for (unsigned short i = MeshType::Dimension; i < 3; ++i) { */
+        /*   positions[r][i] = 0; */
+        /* } */
+      });
+
+    std::vector<double> pts;
+    pts.resize(mesh->numberOfNodes() * 2);
+    for (unsigned short r = 0; r < mesh->numberOfNodes(); ++r) {
+      for (unsigned short j = 0; j < MeshType::Dimension; ++j) {
+        pts[2 * r + j] = positions[r][j];
+      }
+      std::cout << "\033[01;31m" << pts[2 * r] << "; " << pts[2 * r + 1] << "\033[00;00m" << std::endl;
+    }
+
+    /* for (size_t i_node = 0; i_node < mesh->numberOfNodes(); i_node++) { */
+    /*   std::cout << "\033[01;31m" << coords [[i_node]] << "\033[00;00m" << std::endl; */
+    /* } */
+    /* const auto& node_to_cell_matrix = mesh->connectivity().nodeToCellMatrix(); */
+    /* const auto& cell_to_node_matrix = mesh->connectivity().cellToNodeMatrix(); */
+    /* const auto& coords              = mesh->xr(); */
+    /* for (size_t i_node = 0; i_node < mesh->numberOfCells(); i_node++) { */
+    /*   std::cout << "\033[01;31m" << coords[cell_to_node_matrix[i_node]] << "\033[00;00m" << std::endl; */
+    /* } */
+    break;
+  }
+  }
+}
+
+Serializer::~Serializer() = default;
diff --git a/src/utils/Serializer.hpp b/src/utils/Serializer.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9d5395af9bc83b34e684d431691d79c9eccea816
--- /dev/null
+++ b/src/utils/Serializer.hpp
@@ -0,0 +1,36 @@
+#ifndef SERIALIZER_HPP
+#define SERIALIZER_HPP
+
+#include <language/utils/InterpolateItemValue.hpp>
+#include <mesh/Connectivity.hpp>
+#include <mesh/Mesh.hpp>
+#include <mesh/MeshData.hpp>
+#include <mesh/MeshDataManager.hpp>
+#include <mesh/MeshFaceBoundary.hpp>
+#include <mesh/MeshFlatFaceBoundary.hpp>
+#include <scheme/DirichletBoundaryConditionDescriptor.hpp>
+#include <scheme/DiscreteFunctionUtils.hpp>
+#include <scheme/NeumannBoundaryConditionDescriptor.hpp>
+#include <scheme/SymmetryBoundaryConditionDescriptor.hpp>
+#include <utils/PugsAssert.hpp>
+#include <utils/PugsMacros.hpp>
+#include <utils/pugs_config.hpp>
+
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#ifdef PUGS_HAS_MPI
+#include <mpi.h>
+#endif   // PUGS_HAS_MPI
+
+class Serializer
+{
+ public:
+  void run();
+  Serializer(const std::shared_ptr<const IBoundaryDescriptor>& boundary,
+             const std::shared_ptr<const IDiscreteFunction>& field);
+  ~Serializer();
+};
+
+#endif   // SERIALIZER