diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp
index 31dd68cfdd15a88cdb75b091a15a717c2d9beecd..bf6da680dee9204324a8c5d2b4b10c2dde115b2e 100644
--- a/src/language/modules/SchemeModule.cpp
+++ b/src/language/modules/SchemeModule.cpp
@@ -268,6 +268,34 @@ SchemeModule::SchemeModule()
 
                               ));
 
+  this->_addBuiltinFunction("implicitSmoothMesh",
+                            std::function(
+
+                              [](std::shared_ptr<const IMesh> p_mesh,
+                                 const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
+                                   bc_descriptor_list,
+                                 const FunctionSymbolId& function_symbol_id) -> std::shared_ptr<const IMesh> {
+                                ImplicitMeshSmootherHandler handler;
+                                return handler.getSmoothedMesh(p_mesh, bc_descriptor_list, function_symbol_id);
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("implicitSmoothMesh",
+                            std::function(
+
+                              [](std::shared_ptr<const IMesh> p_mesh,
+                                 const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
+                                   bc_descriptor_list,
+                                 const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>&
+                                   discrete_function_variant_list) -> std::shared_ptr<const IMesh> {
+                                ImplicitMeshSmootherHandler handler;
+                                return handler.getSmoothedMesh(p_mesh, bc_descriptor_list,
+                                                               discrete_function_variant_list);
+                              }
+
+                              ));
+
   this->_addBuiltinFunction("smoothMesh", std::function(
 
                                             [](std::shared_ptr<const IMesh> p_mesh,
diff --git a/src/mesh/ImplicitMeshSmoother.cpp b/src/mesh/ImplicitMeshSmoother.cpp
index 64c5b67101dbee80d89513d7f97864166948e76e..43538914a2069268254c8d76d765bdf0ce39d7bc 100644
--- a/src/mesh/ImplicitMeshSmoother.cpp
+++ b/src/mesh/ImplicitMeshSmoother.cpp
@@ -32,10 +32,10 @@ class ImplicitMeshSmootherHandler::ImplicitMeshSmoother
 
   //  class AxisBoundaryCondition;
   class FixedBoundaryCondition;
-  //  class SymmetryBoundaryCondition;
+  class SymmetryBoundaryCondition;
 
-  // using BoundaryCondition = std::variant<AxisBoundaryCondition, FixedBoundaryCondition, SymmetryBoundaryCondition>;
-  using BoundaryCondition = std::variant<FixedBoundaryCondition>;
+  //  using BoundaryCondition = std::variant<AxisBoundaryCondition, FixedBoundaryCondition, SymmetryBoundaryCondition>;
+  using BoundaryCondition = std::variant<FixedBoundaryCondition, SymmetryBoundaryCondition>;
 
   using BoundaryConditionList = std::vector<BoundaryCondition>;
   BoundaryConditionList m_boundary_condition_list;
@@ -48,21 +48,21 @@ class ImplicitMeshSmootherHandler::ImplicitMeshSmoother
 
     for (const auto& bc_descriptor : bc_descriptor_list) {
       switch (bc_descriptor->type()) {
-      // case IBoundaryConditionDescriptor::Type::axis: {
-      //   if constexpr (Dimension == 1) {
-      //     bc_list.emplace_back(FixedBoundaryCondition{getMeshNodeBoundary(mesh,
-      //     bc_descriptor->boundaryDescriptor())});
-      //   } else {
-      //     bc_list.emplace_back(
-      //       AxisBoundaryCondition{getMeshLineNodeBoundary(mesh, bc_descriptor->boundaryDescriptor())});
-      //   }
-      //   break;
-      // }
-      // case IBoundaryConditionDescriptor::Type::symmetry: {
-      //   bc_list.emplace_back(
-      //     SymmetryBoundaryCondition{getMeshFlatNodeBoundary(mesh, bc_descriptor->boundaryDescriptor())});
-      //   break;
-      // }
+        // case IBoundaryConditionDescriptor::Type::axis: {
+        //   if constexpr (Dimension == 1) {
+        //     bc_list.emplace_back(FixedBoundaryCondition{getMeshNodeBoundary(mesh,
+        //     bc_descriptor->boundaryDescriptor())});
+        //   } else {
+        //     bc_list.emplace_back(
+        //       AxisBoundaryCondition{getMeshLineNodeBoundary(mesh, bc_descriptor->boundaryDescriptor())});
+        //   }
+        //   break;
+        // }
+      case IBoundaryConditionDescriptor::Type::symmetry: {
+        bc_list.emplace_back(
+          SymmetryBoundaryCondition{getMeshFlatNodeBoundary(mesh, bc_descriptor->boundaryDescriptor())});
+        break;
+      }
       case IBoundaryConditionDescriptor::Type::fixed: {
         bc_list.emplace_back(FixedBoundaryCondition{getMeshNodeBoundary(mesh, bc_descriptor->boundaryDescriptor())});
         break;
@@ -240,7 +240,7 @@ class ImplicitMeshSmootherHandler::ImplicitMeshSmoother
   }
 
   NodeValue<Rd>
-  _getPosition() const
+  _getPosition(NodeValue<const bool> is_displaced) const
   {
     const ConnectivityType& connectivity = m_given_mesh.connectivity();
     NodeValue<const Rd> given_xr         = m_given_mesh.xr();
@@ -249,7 +249,12 @@ class ImplicitMeshSmootherHandler::ImplicitMeshSmoother
     NodeValue<bool> is_fixed{connectivity};
     is_fixed.fill(false);
     parallel_for(
-      m_given_mesh.numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { pos_r[node_id] = given_xr[node_id]; });
+      m_given_mesh.numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
+        pos_r[node_id] = given_xr[node_id];
+        if (not is_displaced[node_id]) {
+          is_fixed[node_id] = true;
+        }
+      });
     _browseBC(is_fixed);
 
     NodeValue<int> node_dof_id{connectivity};
@@ -257,51 +262,17 @@ class ImplicitMeshSmootherHandler::ImplicitMeshSmoother
     NodeValue<int> id_free{connectivity};
     NodeValue<int> id_fixed{connectivity};
     _computeMatrixSize(is_fixed, id_fixed, id_free, nb_free, nb_fixed);
-    // std::cout << " is_fixed: "
-    //           << "\n";
-    // std::cout << is_fixed << "\n";
-    // std::cout << " id_fixed: "
-    //           << "\n";
-    // std::cout << id_fixed << "\n";
-    // std::cout << " is_free: "
-    //           << "\n";
-    // std::cout << id_free << "\n";
-    //    std::cout << "nb_free " << nb_free << " nb_fixed " << nb_fixed << "\n";
     Array<int> non_zeros_free{nb_free};
     Array<int> non_zeros_fixed{nb_free};
     Array<NodeId> gid_node_free{nb_free};
     Array<NodeId> gid_node_fixed{nb_fixed};
-    // size_t local_free_id  = 0;
-    // size_t local_fixed_id = 0;
     _findLocalIds(is_fixed, gid_node_free, gid_node_fixed, non_zeros_free, non_zeros_fixed);
-    // for (NodeId n_id = 0; n_id < m_given_mesh.numberOfNodes(); n_id++) {
-    //   if (is_fixed[n_id]) {
-    //     gid_node_fixed[local_fixed_id] = n_id;
-    //     local_fixed_id++;
-    //   } else {
-    //     gid_node_free[local_free_id] = n_id;
-    //     local_free_id++;
-    //   }
-    // }
-    // std::cout << " gid_node_fixed: "
-    //           << "\n";
-    // std::cout << gid_node_fixed << "\n";
-    // std::cout << " gid_node_free: "
-    //           << "\n";
-    // std::cout << gid_node_free << "\n";
     std::cout << " nb_free " << nb_free << " nb_fixed " << nb_fixed << "\n";
 
     CRSMatrixDescriptor<double> Afree(nb_free, nb_free, non_zeros_free);
     CRSMatrixDescriptor<double> Afixed(nb_free, nb_fixed, non_zeros_fixed);
     LinearSolver solver;
     _fillMatrix(Afree, Afixed, is_fixed, id_free, id_fixed);
-    // std::cout << " Afree :"
-    //           << "\n";
-    // std::cout << Afree << "\n";
-    // std::cout << " Afixed :"
-    //           << "\n";
-    // std::cout << Afixed << "\n";
-
     Vector<double> F{nb_fixed};
     CRSMatrix Mfree{Afree.getCRSMatrix()};
     CRSMatrix Mfixed{Afixed.getCRSMatrix()};
@@ -312,8 +283,6 @@ class ImplicitMeshSmootherHandler::ImplicitMeshSmoother
       Vector<double> X{nb_free};
       Vector<double> b{nb_free};
       b = Mfixed * F;
-      // std::cout << " F " << F << "\n";
-      // std::cout << " b " << b << "\n";
       solver.solveLocalSystem(Mfree, X, b);
       parallel_for(
         m_given_mesh.numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
@@ -322,6 +291,7 @@ class ImplicitMeshSmootherHandler::ImplicitMeshSmoother
           }
         });
     }
+    //    synchronize(pos_r);
     return pos_r;
   }
 
@@ -329,9 +299,54 @@ class ImplicitMeshSmootherHandler::ImplicitMeshSmoother
   std::shared_ptr<const IMesh>
   getSmoothedMesh() const
   {
+    NodeValue<bool> is_displaced{m_given_mesh.connectivity()};
+    is_displaced.fill(true);
     NodeValue<const Rd> given_xr = m_given_mesh.xr();
 
-    NodeValue<Rd> xr = this->_getPosition();
+    NodeValue<Rd> xr = this->_getPosition(is_displaced);
+
+    return std::make_shared<MeshType>(m_given_mesh.shared_connectivity(), xr);
+  }
+
+  std::shared_ptr<const IMesh>
+  getSmoothedMesh(const FunctionSymbolId& function_symbol_id) const
+  {
+    NodeValue<const Rd> given_xr = m_given_mesh.xr();
+    NodeValue<const bool> is_displaced =
+      InterpolateItemValue<bool(const Rd)>::interpolate(function_symbol_id, given_xr);
+
+    NodeValue<Rd> xr = this->_getPosition(is_displaced);
+
+    return std::make_shared<MeshType>(m_given_mesh.shared_connectivity(), xr);
+  }
+
+  std::shared_ptr<const IMesh>
+  getSmoothedMesh(
+    const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const
+  {
+    NodeValue<const Rd> given_xr = m_given_mesh.xr();
+    auto node_to_cell_matrix     = m_given_mesh.connectivity().nodeToCellMatrix();
+
+    NodeValue<bool> is_displaced{m_given_mesh.connectivity()};
+    is_displaced.fill(false);
+    for (size_t i_zone = 0; i_zone < discrete_function_variant_list.size(); ++i_zone) {
+      auto is_zone_cell = discrete_function_variant_list[i_zone]->get<DiscreteFunctionP0<Dimension, const double>>();
+
+      parallel_for(
+        m_given_mesh.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) {
+          auto node_cell_list = node_to_cell_matrix[node_id];
+          bool displace       = true;
+          for (size_t i_node_cell = 0; i_node_cell < node_cell_list.size(); ++i_node_cell) {
+            const CellId cell_id = node_cell_list[i_node_cell];
+            displace &= (is_zone_cell[cell_id] != 0);
+          }
+          if (displace) {
+            is_displaced[node_id] = true;
+          }
+        });
+    }
+
+    NodeValue<Rd> xr = this->_getPosition(is_displaced);
 
     return std::make_shared<MeshType>(m_given_mesh.shared_connectivity(), xr);
   }
@@ -347,45 +362,6 @@ class ImplicitMeshSmootherHandler::ImplicitMeshSmoother
   ~ImplicitMeshSmoother() = default;
 };
 
-// template <size_t Dimension>
-// class ImplicitMeshSmootherHandler::ImplicitMeshSmoother<Dimension>::AxisBoundaryCondition
-// {
-//  public:
-//   using Rd = TinyVector<Dimension, double>;
-
-//  private:
-//   const MeshLineNodeBoundary<Dimension> m_mesh_line_node_boundary;
-
-//  public:
-//   const Rd&
-//   direction() const
-//   {
-//     return m_mesh_line_node_boundary.direction();
-//   }
-
-//   const Array<const NodeId>&
-//   nodeList() const
-//   {
-//     return m_mesh_line_node_boundary.nodeList();
-//   }
-
-//   AxisBoundaryCondition(MeshLineNodeBoundary<Dimension>&& mesh_line_node_boundary)
-//     : m_mesh_line_node_boundary(mesh_line_node_boundary)
-//   {
-//     ;
-//   }
-
-//   ~AxisBoundaryCondition() = default;
-// };
-
-// template <>
-// class ImplicitMeshSmootherHandler::ImplicitMeshSmoother<1>::AxisBoundaryCondition
-// {
-//  public:
-//   AxisBoundaryCondition()  = default;
-//   ~AxisBoundaryCondition() = default;
-// };
-
 template <size_t Dimension>
 class ImplicitMeshSmootherHandler::ImplicitMeshSmoother<Dimension>::FixedBoundaryCondition
 {
@@ -404,36 +380,36 @@ class ImplicitMeshSmootherHandler::ImplicitMeshSmoother<Dimension>::FixedBoundar
   ~FixedBoundaryCondition() = default;
 };
 
-// template <size_t Dimension>
-// class ImplicitMeshSmootherHandler::ImplicitMeshSmoother<Dimension>::SymmetryBoundaryCondition
-// {
-//  public:
-//   using Rd = TinyVector<Dimension, double>;
-
-//  private:
-//   const MeshFlatNodeBoundary<Dimension> m_mesh_flat_node_boundary;
-
-//  public:
-//   const Rd&
-//   outgoingNormal() const
-//   {
-//     return m_mesh_flat_node_boundary.outgoingNormal();
-//   }
-
-//   const Array<const NodeId>&
-//   nodeList() const
-//   {
-//     return m_mesh_flat_node_boundary.nodeList();
-//   }
-
-//   SymmetryBoundaryCondition(MeshFlatNodeBoundary<Dimension>&& mesh_flat_node_boundary)
-//     : m_mesh_flat_node_boundary(mesh_flat_node_boundary)
-//   {
-//     ;
-//   }
-
-//   ~SymmetryBoundaryCondition() = default;
-// };
+template <size_t Dimension>
+class ImplicitMeshSmootherHandler::ImplicitMeshSmoother<Dimension>::SymmetryBoundaryCondition
+{
+ public:
+  using Rd = TinyVector<Dimension, double>;
+
+ private:
+  const MeshFlatNodeBoundary<Dimension> m_mesh_flat_node_boundary;
+
+ public:
+  const Rd&
+  outgoingNormal() const
+  {
+    return m_mesh_flat_node_boundary.outgoingNormal();
+  }
+
+  const Array<const NodeId>&
+  nodeList() const
+  {
+    return m_mesh_flat_node_boundary.nodeList();
+  }
+
+  SymmetryBoundaryCondition(MeshFlatNodeBoundary<Dimension>&& mesh_flat_node_boundary)
+    : m_mesh_flat_node_boundary(mesh_flat_node_boundary)
+  {
+    ;
+  }
+
+  ~SymmetryBoundaryCondition() = default;
+};
 
 std::shared_ptr<const IMesh>
 ImplicitMeshSmootherHandler::getSmoothedMesh(
@@ -462,3 +438,70 @@ ImplicitMeshSmootherHandler::getSmoothedMesh(
   }
   }
 }
+std::shared_ptr<const IMesh>
+ImplicitMeshSmootherHandler::getSmoothedMesh(
+  const std::shared_ptr<const IMesh>& mesh,
+  const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list,
+  const FunctionSymbolId& function_symbol_id) const
+{
+  switch (mesh->dimension()) {
+  case 1: {
+    throw NotImplementedError("ImplicitMeshSmoother not implemented in 1D");
+    break;
+  }
+  case 2: {
+    constexpr size_t Dimension = 2;
+    using MeshType             = Mesh<Connectivity<Dimension>>;
+    ImplicitMeshSmoother smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list);
+    return smoother.getSmoothedMesh(function_symbol_id);
+  }
+  case 3: {
+    constexpr size_t Dimension = 3;
+    using MeshType             = Mesh<Connectivity<Dimension>>;
+    ImplicitMeshSmoother smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list);
+    return smoother.getSmoothedMesh(function_symbol_id);
+  }
+  default: {
+    throw UnexpectedError("invalid mesh dimension");
+  }
+  }
+}
+
+std::shared_ptr<const IMesh>
+ImplicitMeshSmootherHandler::getSmoothedMesh(
+  const std::shared_ptr<const IMesh>& mesh,
+  const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list,
+  const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const
+{
+  if (not hasSameMesh(discrete_function_variant_list)) {
+    throw NormalError("discrete functions are not defined on the same mesh");
+  }
+
+  std::shared_ptr<const IMesh> common_mesh = getCommonMesh(discrete_function_variant_list);
+
+  if (common_mesh != mesh) {
+    throw NormalError("discrete functions are not defined on the smoothed mesh");
+  }
+
+  switch (mesh->dimension()) {
+  case 1: {
+    throw NotImplementedError("ImplicitMeshSmoother not implemented in 1D");
+    break;
+  }
+  case 2: {
+    constexpr size_t Dimension = 2;
+    using MeshType             = Mesh<Connectivity<Dimension>>;
+    ImplicitMeshSmoother smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list);
+    return smoother.getSmoothedMesh(discrete_function_variant_list);
+  }
+  case 3: {
+    constexpr size_t Dimension = 3;
+    using MeshType             = Mesh<Connectivity<Dimension>>;
+    ImplicitMeshSmoother smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list);
+    return smoother.getSmoothedMesh(discrete_function_variant_list);
+  }
+  default: {
+    throw UnexpectedError("invalid mesh dimension");
+  }
+  }
+}
diff --git a/src/mesh/ImplicitMeshSmoother.hpp b/src/mesh/ImplicitMeshSmoother.hpp
index b5ca016bc1c5c09cc9dcafb5a237788e3587edcd..7678417a5e6274f298b54783ff9a1354bcfdabdd 100644
--- a/src/mesh/ImplicitMeshSmoother.hpp
+++ b/src/mesh/ImplicitMeshSmoother.hpp
@@ -41,6 +41,14 @@ class ImplicitMeshSmootherHandler
   std::shared_ptr<const IMesh> getSmoothedMesh(
     const std::shared_ptr<const IMesh>& mesh,
     const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const;
+  std::shared_ptr<const IMesh> getSmoothedMesh(
+    const std::shared_ptr<const IMesh>& mesh,
+    const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list,
+    const FunctionSymbolId& function_symbol_id) const;
+  std::shared_ptr<const IMesh> getSmoothedMesh(
+    const std::shared_ptr<const IMesh>& mesh,
+    const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list,
+    const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const;
 
   // std::shared_ptr<const IMesh> getSmoothedMesh(
   //   const std::shared_ptr<const IMesh>& mesh,