diff --git a/src/language/algorithms/HeatDiamondAlgorithm.cpp b/src/language/algorithms/HeatDiamondAlgorithm.cpp
index c329feb029f63d3a24c7695c2ecd84556a4fe19b..5cf0b0c5d6cdc73719a38b12a1e235933afbbf0f 100644
--- a/src/language/algorithms/HeatDiamondAlgorithm.cpp
+++ b/src/language/algorithms/HeatDiamondAlgorithm.cpp
@@ -15,7 +15,12 @@
 #include <mesh/Mesh.hpp>
 #include <mesh/MeshData.hpp>
 #include <mesh/MeshDataManager.hpp>
+#include <mesh/MeshNodeBoundary.hpp>
 #include <output/VTKWriter.hpp>
+#include <scheme/DirichletBoundaryConditionDescriptor.hpp>
+#include <scheme/FourierBoundaryConditionDescriptor.hpp>
+#include <scheme/NeumannBoundaryConditionDescriptor.hpp>
+#include <scheme/SymmetryBoundaryConditionDescriptor.hpp>
 
 template <size_t Dimension>
 HeatDiamondScheme<Dimension>::HeatDiamondScheme(
@@ -30,9 +35,82 @@ HeatDiamondScheme<Dimension>::HeatDiamondScheme(
   using MeshType         = Mesh<ConnectivityType>;
   using MeshDataType     = MeshData<Dimension>;
 
+  constexpr ItemType FaceType = [] {
+    if constexpr (Dimension > 1) {
+      return ItemType::face;
+    } else {
+      return ItemType::node;
+    }
+  }();
+
+  using BoundaryCondition = std::variant<DirichletBoundaryCondition, FourierBoundaryCondition, NeumannBoundaryCondition,
+                                         SymmetryBoundaryCondition>;
+
+  using BoundaryConditionList = std::vector<BoundaryCondition>;
+
+  BoundaryConditionList boundary_condition_list;
+
   std::cout << "number of bc descr = " << bc_descriptor_list.size() << '\n';
   std::shared_ptr m_mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh);
 
+  for (const auto& bc_descriptor : bc_descriptor_list) {
+    bool is_valid_boundary_condition = true;
+
+    switch (bc_descriptor->type()) {
+    case IBoundaryConditionDescriptor::Type::symmetry: {
+      // const SymmetryBoundaryConditionDescriptor& sym_bc_descriptor =
+      //   dynamic_cast<const SymmetryBoundaryConditionDescriptor&>(*bc_descriptor);
+      throw NotImplementedError("NIY");
+      break;
+    }
+    case IBoundaryConditionDescriptor::Type::dirichlet: {
+      const DirichletBoundaryConditionDescriptor& dirichlet_bc_descriptor =
+        dynamic_cast<const DirichletBoundaryConditionDescriptor&>(*bc_descriptor);
+
+      for (size_t i_ref_face_list = 0;
+           i_ref_face_list < m_mesh->connectivity().template numberOfRefItemList<FaceType>(); ++i_ref_face_list) {
+        const auto& ref_face_list = m_mesh->connectivity().template refItemList<FaceType>(i_ref_face_list);
+        const RefId& ref          = ref_face_list.refId();
+        if (ref == dirichlet_bc_descriptor.boundaryDescriptor()) {
+          MeshNodeBoundary<Dimension> mesh_node_boundary{m_mesh, ref_face_list};
+
+          const FunctionSymbolId g_id = dirichlet_bc_descriptor.rhsSymbolId();
+
+          const auto& node_list = mesh_node_boundary.nodeList();
+
+          Array<const double> value_list =
+            InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::node>(g_id,
+                                                                                                      m_mesh->xr(),
+                                                                                                      node_list);
+
+          boundary_condition_list.push_back(DirichletBoundaryCondition{node_list, value_list});
+        }
+      }
+      break;
+    }
+    case IBoundaryConditionDescriptor::Type::fourier: {
+      // const FourierBoundaryConditionDescriptor& fourier_bc_descriptor =
+      //   dynamic_cast<const FourierBoundaryConditionDescriptor&>(*bc_descriptor);
+      throw NotImplementedError("NIY");
+      break;
+    }
+    case IBoundaryConditionDescriptor::Type::neumann: {
+      // const NeumannBoundaryConditionDescriptor& neumann_bc_descriptor =
+      //   dynamic_cast<const NeumannBoundaryConditionDescriptor&>(*bc_descriptor);
+      throw NotImplementedError("NIY");
+      break;
+    }
+    default: {
+      is_valid_boundary_condition = false;
+    }
+    }
+    if (not is_valid_boundary_condition) {
+      std::ostringstream error_msg;
+      error_msg << *bc_descriptor << " is an invalid boundary condition for heat equation";
+      throw NormalError(error_msg.str());
+    }
+  }
+
   if constexpr (Dimension > 1) {
     MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*m_mesh);
 
@@ -345,8 +423,9 @@ HeatDiamondScheme<Dimension>::HeatDiamondScheme(
 
       Vector<double> error{m_mesh->numberOfCells()};
       parallel_for(
-        m_mesh->numberOfCells(),
-        PUGS_LAMBDA(CellId cell_id) { error[cell_id] = (Temperature[cell_id] - Tj[cell_id]) * primal_Vj[cell_id]; });
+        m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+          error[cell_id] = (Temperature[cell_id] - Tj[cell_id]) * sqrt(primal_Vj[cell_id]);
+        });
 
       std::cout << "||Error||_2 = " << std::sqrt((error, error)) << "\n";
 
@@ -365,6 +444,54 @@ HeatDiamondScheme<Dimension>::HeatDiamondScheme(
   }
 }
 
+template <size_t Dimension>
+class HeatDiamondScheme<Dimension>::DirichletBoundaryCondition
+{
+ private:
+  const Array<const double> m_value_list;
+  const Array<const NodeId> m_node_list;
+
+ public:
+  const Array<const NodeId>&
+  nodeList() const
+  {
+    return m_node_list;
+  }
+
+  const Array<const double>&
+  valueList() const
+  {
+    return m_value_list;
+  }
+
+  DirichletBoundaryCondition(const Array<const NodeId>& node_list, const Array<const double>& value_list)
+    : m_value_list{value_list}, m_node_list{node_list}
+  {}
+
+  ~DirichletBoundaryCondition() = default;
+};
+
+template <size_t Dimension>
+class HeatDiamondScheme<Dimension>::FourierBoundaryCondition
+{
+ public:
+  ~FourierBoundaryCondition() = default;
+};
+
+template <size_t Dimension>
+class HeatDiamondScheme<Dimension>::NeumannBoundaryCondition
+{
+ public:
+  ~NeumannBoundaryCondition() = default;
+};
+
+template <size_t Dimension>
+class HeatDiamondScheme<Dimension>::SymmetryBoundaryCondition
+{
+ public:
+  ~SymmetryBoundaryCondition() = default;
+};
+
 template HeatDiamondScheme<1>::HeatDiamondScheme(
   std::shared_ptr<const IMesh>,
   const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&,
diff --git a/src/language/algorithms/HeatDiamondAlgorithm.hpp b/src/language/algorithms/HeatDiamondAlgorithm.hpp
index fc8885d618f4aa5071ab74e640cad689e9314d71..c3b3d2e84ff6e13c9fefb32303e7eb2d4b564c01 100644
--- a/src/language/algorithms/HeatDiamondAlgorithm.hpp
+++ b/src/language/algorithms/HeatDiamondAlgorithm.hpp
@@ -6,11 +6,19 @@
 #include <scheme/IBoundaryConditionDescriptor.hpp>
 
 #include <memory>
+#include <variant>
 #include <vector>
 
 template <size_t Dimension>
-struct HeatDiamondScheme
+class HeatDiamondScheme
 {
+ private:
+  class DirichletBoundaryCondition;
+  class FourierBoundaryCondition;
+  class NeumannBoundaryCondition;
+  class SymmetryBoundaryCondition;
+
+ public:
   HeatDiamondScheme(std::shared_ptr<const IMesh> i_mesh,
                     const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list,
                     const FunctionSymbolId& T_id,
diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp
index a30d2a252807c9620e25a93a1a7f0e01dbb8edf5..d2d25382cbb376c6fab9ae67c8044bf719e47b42 100644
--- a/src/language/modules/SchemeModule.cpp
+++ b/src/language/modules/SchemeModule.cpp
@@ -83,6 +83,44 @@ SchemeModule::SchemeModule()
 
                               ));
 
+  this->_addBuiltinFunction("dirichlet",
+                            std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr<
+                              const IBoundaryConditionDescriptor>(std::shared_ptr<const IBoundaryDescriptor>,
+                                                                  const FunctionSymbolId&)>>(
+
+                              [](std::shared_ptr<const IBoundaryDescriptor> boundary,
+                                 const FunctionSymbolId& g_id) -> std::shared_ptr<const IBoundaryConditionDescriptor> {
+                                return std::make_shared<DirichletBoundaryConditionDescriptor>("dirichlet", boundary,
+                                                                                              g_id);
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("fourier",
+                            std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr<
+                              const IBoundaryConditionDescriptor>(std::shared_ptr<const IBoundaryDescriptor>,
+                                                                  const FunctionSymbolId&, const FunctionSymbolId&)>>(
+
+                              [](std::shared_ptr<const IBoundaryDescriptor> boundary, const FunctionSymbolId& alpha_id,
+                                 const FunctionSymbolId& g_id) -> std::shared_ptr<const IBoundaryConditionDescriptor> {
+                                return std::make_shared<FourierBoundaryConditionDescriptor>("fourier", boundary,
+                                                                                            alpha_id, g_id);
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("neumann",
+                            std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr<
+                              const IBoundaryConditionDescriptor>(std::shared_ptr<const IBoundaryDescriptor>,
+                                                                  const FunctionSymbolId&)>>(
+
+                              [](std::shared_ptr<const IBoundaryDescriptor> boundary,
+                                 const FunctionSymbolId& g_id) -> std::shared_ptr<const IBoundaryConditionDescriptor> {
+                                return std::make_shared<NeumannBoundaryConditionDescriptor>("neumann", boundary, g_id);
+                              }
+
+                              ));
+
   this->_addBuiltinFunction("glace",
                             std::make_shared<BuiltinFunctionEmbedder<
                               void(std::shared_ptr<const IMesh>,