diff --git a/Applications/ApplicationsLib/ProjectData.cpp b/Applications/ApplicationsLib/ProjectData.cpp
index 1f9f86beafbc864ef460bafbd686894a6f685453..d214974d3f142273449deaf60706d42d615a0fda 100644
--- a/Applications/ApplicationsLib/ProjectData.cpp
+++ b/Applications/ApplicationsLib/ProjectData.cpp
@@ -377,6 +377,13 @@ void ProjectData::parseProcesses(BaseLib::ConfigTree const& processes_config,
 #ifdef OGS_BUILD_PROCESS_HEATTRANSPORTBHE
             if (type == "HEAT_TRANSPORT_BHE")
         {
+            if (_mesh_vec[0]->getDimension() != 3)
+            {
+                OGS_FATAL(
+                    "HEAT_TRANSPORT_BHE can only work with a 3-dimentional "
+                    "mesh! ");
+            }
+
             process =
                 ProcessLib::HeatTransportBHE::createHeatTransportBHEProcess(
                     *_mesh_vec[0], std::move(jacobian_assembler),
@@ -469,17 +476,17 @@ void ProjectData::parseProcesses(BaseLib::ConfigTree const& processes_config,
             {
                 case 2:
                     process =
-                        ProcessLib::PhaseField::createPhaseFieldProcess<
-                            2>(*_mesh_vec[0], std::move(jacobian_assembler),
-                               _process_variables, _parameters,
-                               integration_order, process_config);
+                        ProcessLib::PhaseField::createPhaseFieldProcess<2>(
+                            *_mesh_vec[0], std::move(jacobian_assembler),
+                            _process_variables, _parameters, integration_order,
+                            process_config);
                     break;
                 case 3:
                     process =
-                        ProcessLib::PhaseField::createPhaseFieldProcess<
-                            3>(*_mesh_vec[0], std::move(jacobian_assembler),
-                               _process_variables, _parameters,
-                               integration_order, process_config);
+                        ProcessLib::PhaseField::createPhaseFieldProcess<3>(
+                            *_mesh_vec[0], std::move(jacobian_assembler),
+                            _process_variables, _parameters, integration_order,
+                            process_config);
                     break;
             }
         }
diff --git a/ProcessLib/BoundaryCondition/BHEInflowDirichletBoundaryCondition.cpp b/ProcessLib/BoundaryCondition/BHEInflowDirichletBoundaryCondition.cpp
index b152909b53e648ed4ba08001d252996e21153086..f26e9b6623cb0bed1dc79e3e3c0a1ba109765d53 100644
--- a/ProcessLib/BoundaryCondition/BHEInflowDirichletBoundaryCondition.cpp
+++ b/ProcessLib/BoundaryCondition/BHEInflowDirichletBoundaryCondition.cpp
@@ -17,8 +17,7 @@
 namespace ProcessLib
 {
 BHEInflowDirichletBoundaryCondition::BHEInflowDirichletBoundaryCondition(
-    GlobalIndexType global_idx_T_in_top,
-    GlobalIndexType global_idx_T_out_top,
+    std::pair<GlobalIndexType, GlobalIndexType>&& in_out_global_indices,
     MeshLib::Mesh const& bc_mesh,
     std::vector<MeshLib::Node*> const& vec_inflow_bc_nodes,
     int const variable_id,
@@ -28,8 +27,7 @@ BHEInflowDirichletBoundaryCondition::BHEInflowDirichletBoundaryCondition(
     : _bc_mesh(bc_mesh), _pt_bhe(pt_bhe)
 {
     DBUG(
-        "Found %d nodes for BHE Inflow Dirichlet BCs for the variable %d "
-        "and "
+        "Found %d nodes for BHE Inflow Dirichlet BCs for the variable %d and "
         "component %d",
         vec_inflow_bc_nodes.size(), variable_id, component_id);
 
@@ -48,8 +46,8 @@ BHEInflowDirichletBoundaryCondition::BHEInflowDirichletBoundaryCondition(
     _bc_values.values.reserve(bc_mesh_subset.getNumberOfNodes());
 
     // that might be slow, but only done once
-    const auto g_idx_T_in = global_idx_T_in_top;
-    const auto g_idx_T_out = global_idx_T_out_top;
+    const auto g_idx_T_in = in_out_global_indices.first;
+    const auto g_idx_T_out = in_out_global_indices.second;
 
     if (g_idx_T_in >= 0 && g_idx_T_out >= 0)
     {
@@ -99,7 +97,7 @@ void BHEInflowDirichletBoundaryCondition::preTimestep(const double /*t*/,
 
 std::unique_ptr<BHEInflowDirichletBoundaryCondition>
 createBHEInflowDirichletBoundaryCondition(
-    GlobalIndexType global_idx_T_in_top, GlobalIndexType global_idx_T_out_top,
+    std::pair<GlobalIndexType, GlobalIndexType>&& in_out_global_indices,
     MeshLib::Mesh const& bc_mesh,
     std::vector<MeshLib::Node*> const& vec_inflow_bc_nodes,
     int const variable_id, int const component_id,
@@ -108,10 +106,8 @@ createBHEInflowDirichletBoundaryCondition(
 {
     DBUG("Constructing BHEInflowDirichletBoundaryCondition.");
 
-    //! \ogs_file_param{prj__process_variables__process_variable__boundary_conditions__boundary_condition__Dirichlet__parameter}
-
     return std::make_unique<BHEInflowDirichletBoundaryCondition>(
-        global_idx_T_in_top, global_idx_T_out_top, bc_mesh, vec_inflow_bc_nodes,
+        std::move(in_out_global_indices), bc_mesh, vec_inflow_bc_nodes,
         variable_id, component_id, pt_bhe);
 }
-}  // namespace ProcessLib
+}  // namespace ProcessLib
\ No newline at end of file
diff --git a/ProcessLib/BoundaryCondition/BHEInflowDirichletBoundaryCondition.h b/ProcessLib/BoundaryCondition/BHEInflowDirichletBoundaryCondition.h
index 53c2afc0f4f784cfcee5c674e98f5128bf83ac29..442473b1492c8be48629c73cb78770ad4d7e7158 100644
--- a/ProcessLib/BoundaryCondition/BHEInflowDirichletBoundaryCondition.h
+++ b/ProcessLib/BoundaryCondition/BHEInflowDirichletBoundaryCondition.h
@@ -21,8 +21,7 @@ class BHEInflowDirichletBoundaryCondition final : public BoundaryCondition
 {
 public:
     BHEInflowDirichletBoundaryCondition(
-        GlobalIndexType global_idx_T_in_top,
-        GlobalIndexType global_idx_T_out_top,
+        std::pair<GlobalIndexType, GlobalIndexType>&& in_out_global_indices,
         MeshLib::Mesh const& bc_mesh,
         std::vector<MeshLib::Node*> const& vec_inflow_bc_nodes,
         int const variable_id,
@@ -37,6 +36,7 @@ public:
     void preTimestep(const double t, const GlobalVector& x) override;
 
 private:
+    // TODO (haibing) re-organize as the bottom BC data structure
     MeshLib::Mesh const& _bc_mesh;
 
     /// Stores the results of the outflow temperatures per boundary node.
@@ -51,7 +51,7 @@ private:
 
 std::unique_ptr<BHEInflowDirichletBoundaryCondition>
 createBHEInflowDirichletBoundaryCondition(
-    GlobalIndexType global_idx_T_in_top, GlobalIndexType global_idx_T_out_top,
+    std::pair<GlobalIndexType, GlobalIndexType>&& in_out_global_indices,
     MeshLib::Mesh const& bc_mesh,
     std::vector<MeshLib::Node*> const& vec_outflow_bc_nodes,
     int const variable_id, int const component_id,
diff --git a/ProcessLib/HeatTransportBHE/BHE/BHEAbstract.h b/ProcessLib/HeatTransportBHE/BHE/BHEAbstract.h
index 9a5631db1a40c2f0cd0b3482e2b16c5f46082266..09126e877d2de5e95bad4c9db5a8e35655619aef 100644
--- a/ProcessLib/HeatTransportBHE/BHE/BHEAbstract.h
+++ b/ProcessLib/HeatTransportBHE/BHE/BHEAbstract.h
@@ -35,6 +35,11 @@
 #include "ProcessLib/Utils/ProcessUtils.h"
 #include "boost/math/constants/constants.hpp"
 
+#include "BoreholeGeometry.h"
+#include "GroutParameters.h"
+#include "PipeParameters.h"
+#include "RefrigerantParameters.h"
+
 namespace ProcessLib
 {
 namespace HeatTransportBHE
@@ -76,127 +81,6 @@ enum class BHE_DISCHARGE_TYPE
 class BHEAbstract
 {
 public:
-    struct BoreholeGeometry
-    {
-        /**
-         * length/depth of the BHE
-         * unit is m
-         */
-        double length;
-
-        /**
-         * diameter of the BHE
-         * unit is m
-         */
-        double diameter;
-    };
-
-    struct PipeParameters
-    {
-        /**
-         * radius of the pipline inner side
-         * unit is m
-         */
-        double r_inner;
-
-        /**
-         * radius of the pipline outer side
-         * unit is m
-         */
-        double r_outer;
-
-        /**
-         * pipe-in wall thickness
-         * unit is m
-         */
-        double b_in;
-
-        /**
-         * pipe-out wall thickness
-         * unit is m
-         */
-        double b_out;
-
-        /**
-         * thermal conductivity of the pipe wall
-         * unit is kg m sec^-3 K^-1
-         */
-
-        double lambda_p;
-
-        /**
-         * thermal conductivity of the inner pipe wall
-         * unit is kg m sec^-3 K^-1
-         */
-        double lambda_p_i;
-
-        /**
-         * thermal conductivity of the outer pipe wall
-         * unit is kg m sec^-3 K^-1
-         */
-        double lambda_p_o;
-    };
-
-    struct RefrigerantParameters
-    {
-        /**
-         * dynamics viscosity of the refrigerant
-         * unit is kg m-1 sec-1
-         */
-        double mu_r;
-
-        /**
-         * density of the refrigerant
-         * unit is kg m-3
-         */
-        double rho_r;
-
-        /**
-         * thermal conductivity of the refrigerant
-         * unit is kg m sec^-3 K^-1
-         */
-        double lambda_r;
-
-        /**
-         * specific heat capacity of the refrigerant
-         * unit is m^2 sec^-2 K^-1
-         */
-        double heat_cap_r;
-
-        /**
-         * longitudinal dispersivity of the
-         * referigerant flow in the pipeline
-         */
-        double alpha_L;
-    };
-
-    struct GroutParameters
-    {
-        /**
-         * density of the grout
-         * unit is kg m-3
-         */
-        double rho_g;
-
-        /**
-         * porosity of the grout
-         * unit is [-]
-         */
-        double porosity_g;
-
-        /**
-         * specific heat capacity of the grout
-         * unit is m^2 sec^-2 K^-1
-         */
-        double heat_cap_g;
-
-        /**
-         * thermal conductivity of the grout
-         * unit is kg m sec^-3 K^-1
-         */
-        double lambda_g;
-    };
-
     struct ExternallyDefinedRaRb
     {
         /**
@@ -300,15 +184,7 @@ public:
      * initialization calcultion,
      * need to be overwritten.
      */
-    virtual void initialize()
-    {
-        calcPipeFlowVelocity();
-        calcRenoldsNum();
-        calcPrandtlNum();
-        calcNusseltNum();
-        calcThermalResistances();
-        calcHeatTransferCoefficients();
-    };
+    virtual void initialize() = 0;
 
     /**
      * update all parameters based on the new flow rate
@@ -317,12 +193,7 @@ public:
     virtual void updateFlowRate(double new_flow_rate)
     {
         Q_r = new_flow_rate;
-        calcPipeFlowVelocity();
-        calcRenoldsNum();
-        calcPrandtlNum();
-        calcNusseltNum();
-        calcThermalResistances();
-        calcHeatTransferCoefficients();
+        initialize();
     };
 
     virtual void updateFlowRateFromCurve(double current_time) = 0;
@@ -334,28 +205,61 @@ public:
     virtual void calcThermalResistances() = 0;
 
     /**
-     * Nusselt number calculation,
-     * need to be overwritten.
-     */
-    virtual void calcNusseltNum() = 0;
-
-    /**
-     * Renolds number calculation,
-     * need to be overwritten.
+     * flow velocity inside the pipeline
      */
-    virtual void calcRenoldsNum() = 0;
+    double calcPipeFlowVelocity(double const& flow_rate,
+                                double const& pipe_diameter)
+    {
+        return 4.0 * flow_rate / (PI * pipe_diameter * pipe_diameter);
+    }
 
-    /**
-     * Prandtl number calculation,
-     * need to be overwritten.
-     */
-    virtual void calcPrandtlNum() = 0;
+    double calcPrandtlNumber(double const& viscosity,
+                             double const& heat_capacity,
+                             double const& heat_conductivity)
+    {
+        return viscosity * heat_capacity / heat_conductivity;
+    }
 
-    /**
-     * flow velocity inside the pipeline
-     * need to be overwritten.
-     */
-    virtual void calcPipeFlowVelocity() = 0;
+    double calcRenoldsNumber(double const velocity_norm,
+                             double const pipe_diameter,
+                             double const viscosity,
+                             double const density)
+    {
+        return velocity_norm * pipe_diameter / (viscosity / density);
+    };
+    double calcNusseltNumber(double const pipe_diameter,
+                             double const pipe_length)
+    {
+        double tmp_Nu(0.0);
+        double gamma(0.0), xi(0.0);
+
+        if (Re < 2300.0)
+        {
+            tmp_Nu = 4.364;
+        }
+        else if (Re >= 2300.0 && Re < 10000.0)
+        {
+            gamma = (Re - 2300) / (10000 - 2300);
+
+            tmp_Nu = (1.0 - gamma) * 4.364;
+            tmp_Nu +=
+                gamma *
+                ((0.0308 / 8.0 * 1.0e4 * Pr) /
+                 (1.0 + 12.7 * std::sqrt(0.0308 / 8.0) *
+                            (std::pow(Pr, 2.0 / 3.0) - 1.0)) *
+                 (1.0 + std::pow(pipe_diameter / pipe_length, 2.0 / 3.0)));
+        }
+        else if (Re > 10000.0)
+        {
+            xi = pow(1.8 * std::log10(Re) - 1.5, -2.0);
+            tmp_Nu = (xi / 8.0 * Re * Re) /
+                     (1.0 + 12.7 * std::sqrt(xi / 8.0) *
+                                (std::pow(Pr, 2.0 / 3.0) - 1.0)) *
+                     (1.0 + std::pow(pipe_diameter / pipe_length, 2.0 / 3.0));
+        }
+
+        return tmp_Nu;
+    };
 
     /**
      * heat transfer coefficient,
@@ -409,6 +313,9 @@ public:
         Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>&
             R_s_matrix) const = 0;
 
+    virtual std::vector<std::pair<int, int>> const&
+    inflowOutflowBcComponentIds() const = 0;
+
 public:
     /**
      * name of the borehole heat exchanger
@@ -530,6 +437,21 @@ protected:
     double Q_r;
 
     const double PI;
+
+    /**
+     * Reynolds number
+     */
+    double Re;
+
+    /**
+     * Prandtl number
+     */
+    double Pr;
+
+    /**
+     * pipe distance
+     */
+    double omega;
 };
 }  // end of namespace BHE
 }  // end of namespace HeatTransportBHE
diff --git a/ProcessLib/HeatTransportBHE/BHE/BHE_1U.cpp b/ProcessLib/HeatTransportBHE/BHE/BHE_1U.cpp
index debfa4ea3602ae8896a8d242375f24ba69444422..85bbd24c3d14fbb90cb1c50f1e6a2b72587716b0 100644
--- a/ProcessLib/HeatTransportBHE/BHE/BHE_1U.cpp
+++ b/ProcessLib/HeatTransportBHE/BHE/BHE_1U.cpp
@@ -11,6 +11,33 @@
 
 using namespace ProcessLib::HeatTransportBHE::BHE;
 
+void BHE_1U::initialize()
+
+{
+    double tmp_u = calcPipeFlowVelocity(Q_r, 2.0 * pipe_param.r_inner);
+    _u(0) = tmp_u;
+    _u(1) = tmp_u;
+
+    // calculate Renolds number
+    Re = calcRenoldsNumber(std::abs(_u(0)),
+                           2.0 * pipe_param.r_inner,
+                           refrigerant_param.mu_r,
+                           refrigerant_param.rho_r);
+    // calculate Prandtl number
+    Pr = calcPrandtlNumber(refrigerant_param.mu_r,
+                           refrigerant_param.heat_cap_r,
+                           refrigerant_param.lambda_r);
+
+    // calculate Nusselt number
+    double tmp_Nu =
+        calcNusseltNumber(2.0 * pipe_param.r_inner, borehole_geometry.length);
+    _Nu(0) = tmp_Nu;
+    _Nu(1) = tmp_Nu;
+
+    calcThermalResistances();
+    calcHeatTransferCoefficients();
+}
+
 /**
  * calculate thermal resistance
  */
@@ -153,74 +180,6 @@ void BHE_1U::calcThermalResistances()
     // -------------------------------------------------------------------------
 }
 
-/**
- * Nusselt number calculation
- */
-void BHE_1U::calcNusseltNum()
-{
-    // see Eq. 32 in Diersch_2011_CG
-
-    double tmp_Nu = 0.0;
-    double gamma, xi;
-    double d;
-    double const& L = borehole_geometry.length;
-
-    d = 2.0 * pipe_param.r_inner;
-
-    if (_Re < 2300.0)
-    {
-        tmp_Nu = 4.364;
-    }
-    else if (_Re >= 2300.0 && _Re < 10000.0)
-    {
-        gamma = (_Re - 2300) / (10000 - 2300);
-
-        tmp_Nu = (1.0 - gamma) * 4.364;
-        tmp_Nu += gamma * ((0.0308 / 8.0 * 1.0e4 * _Pr) /
-                           (1.0 + 12.7 * std::sqrt(0.0308 / 8.0) *
-                                      (std::pow(_Pr, 2.0 / 3.0) - 1.0)) *
-                           (1.0 + std::pow(d / L, 2.0 / 3.0)));
-    }
-    else if (_Re > 10000.0)
-    {
-        xi = pow(1.8 * std::log10(_Re) - 1.5, -2.0);
-        tmp_Nu = (xi / 8.0 * _Re * _Pr) /
-                 (1.0 + 12.7 * std::sqrt(xi / 8.0) *
-                            (std::pow(_Pr, 2.0 / 3.0) - 1.0)) *
-                 (1.0 + std::pow(d / L, 2.0 / 3.0));
-    }
-
-    _Nu(0) = tmp_Nu;
-    _Nu(1) = tmp_Nu;
-}
-
-/**
- * Renolds number calculation
- */
-void BHE_1U::calcRenoldsNum()
-{
-    double u_norm, d;
-    double const& mu_r = refrigerant_param.mu_r;
-    double const& rho_r = refrigerant_param.rho_r;
-
-    u_norm = std::abs(_u(0));
-    d = 2.0 * pipe_param.r_inner;  // inner diameter of the pipeline
-
-    _Re = u_norm * d / (mu_r / rho_r);
-}
-
-/**
- * Prandtl number calculation
- */
-void BHE_1U::calcPrandtlNum()
-{
-    double const& mu_r = refrigerant_param.mu_r;
-    double const& heat_cap_r = refrigerant_param.heat_cap_r;
-    double const& lambda_r = refrigerant_param.lambda_r;
-
-    _Pr = mu_r * heat_cap_r / lambda_r;
-}
-
 /**
  * calculate heat transfer coefficient
  */
@@ -232,19 +191,6 @@ void BHE_1U::calcHeatTransferCoefficients()
     _PHI_gs = 1.0 / _R_gs;
 }
 
-/**
- * flow velocity inside the pipeline
- */
-void BHE_1U::calcPipeFlowVelocity()
-{
-    double tmp_u;
-
-    tmp_u = Q_r / (PI * pipe_param.r_inner * pipe_param.r_inner);
-
-    _u(0) = tmp_u;
-    _u(1) = tmp_u;
-}
-
 double BHE_1U::getMassCoeff(std::size_t idx_unknown) const
 {
     double const& rho_r = refrigerant_param.rho_r;
diff --git a/ProcessLib/HeatTransportBHE/BHE/BHE_1U.h b/ProcessLib/HeatTransportBHE/BHE/BHE_1U.h
index a0d9689a2b22f5d3a9719ff84968508d1a81b079..ec9620a6b981ba17ca2f0d216a2c4ab321e898aa 100644
--- a/ProcessLib/HeatTransportBHE/BHE/BHE_1U.h
+++ b/ProcessLib/HeatTransportBHE/BHE/BHE_1U.h
@@ -76,6 +76,7 @@ public:
     {
         _u = Eigen::Vector2d::Zero();
         _Nu = Eigen::Vector2d::Zero();
+        // 1U type of BHE has 2 pipes
 
         Q_r = my_Qr;
 
@@ -162,6 +163,8 @@ public:
      */
     std::size_t getNumUnknowns() const { return 4; }
 
+    void initialize();
+
     void updateFlowRateFromCurve(double current_time)
     {
         if (use_flowrate_curve)
@@ -177,26 +180,6 @@ public:
      */
     void calcThermalResistances();
 
-    /**
-     * Nusselt number calculation
-     */
-    void calcNusseltNum();
-
-    /**
-     * Renolds number calculation
-     */
-    void calcRenoldsNum();
-
-    /**
-     * Prandtl number calculation
-     */
-    void calcPrandtlNum();
-
-    /**
-     * flow velocity inside the pipeline
-     */
-    void calcPipeFlowVelocity();
-
     /**
      * calculate heat transfer coefficient
      */
@@ -247,6 +230,12 @@ public:
      */
     double getTinByTout(double T_out, double current_time);
 
+    std::vector<std::pair<int, int>> const& inflowOutflowBcComponentIds()
+        const override
+    {
+        return _inflow_outflow_bc_component_ids;
+    }
+
     /**
      * required by eigen library,
      * to make sure the dynamically allocated class has
@@ -255,6 +244,9 @@ public:
     EIGEN_MAKE_ALIGNED_OPERATOR_NEW
 
 private:
+    std::vector<std::pair<int, int>> const _inflow_outflow_bc_component_ids = {
+        {0, 1}};
+
     /**
      * thermal resistances
      */
@@ -296,37 +288,21 @@ private:
     double _PHI_fig, _PHI_fog, _PHI_gg, _PHI_gs;
 
     /**
-     * Reynolds number
+     * specific exchange surfaces S
      */
-    double _Re;
-
+    double S_i, S_o, S_g1, S_gs;
     /**
-     * Prandtl number
+     * cross section area
      */
-    double _Pr;
-
+    double CSA_i, CSA_o, CSA_g1, CSA_g2;
     /**
      * Nusselt number
      */
     Eigen::Vector2d _Nu;
-
     /**
      * flow velocity inside the pipeline
      */
     Eigen::Vector2d _u;
-
-    /**
-     * pipe distance
-     */
-    double omega;
-    /**
-     * specific exchange surfaces S
-     */
-    double S_i, S_o, S_g1, S_gs;
-    /**
-     * cross section area
-     */
-    double CSA_i, CSA_o, CSA_g1, CSA_g2;
 };
 }  // namespace BHE
 }  // namespace HeatTransportBHE
diff --git a/ProcessLib/HeatTransportBHE/BHE/BoreholeGeometry.h b/ProcessLib/HeatTransportBHE/BHE/BoreholeGeometry.h
new file mode 100644
index 0000000000000000000000000000000000000000..658aa9816a3140857fddd45ddfba9b91a65272dd
--- /dev/null
+++ b/ProcessLib/HeatTransportBHE/BHE/BoreholeGeometry.h
@@ -0,0 +1,36 @@
+/**
+ * \file
+ *
+ * \copyright
+ * Copyright (c) 2012-2018, OpenGeoSys Community (http://www.opengeosys.org)
+ *            Distributed under a Modified BSD License.
+ *              See accompanying file LICENSE.txt or
+ *              http://www.opengeosys.org/project/license
+ *
+ */
+
+#pragma once
+
+namespace ProcessLib
+{
+namespace HeatTransportBHE
+{
+namespace BHE
+{
+struct BoreholeGeometry
+{
+    /**
+     * length/depth of the BHE
+     * unit is m
+     */
+    double const length;
+
+    /**
+     * diameter of the BHE
+     * unit is m
+     */
+    double const diameter;
+};
+}  // namespace BHE
+}  // namespace HeatTransportBHE
+}  // namespace ProcessLib
diff --git a/ProcessLib/HeatTransportBHE/BHE/CreateBHE1U.cpp b/ProcessLib/HeatTransportBHE/BHE/CreateBHE1U.cpp
index d01467d69b13d94c1363b5ea9b18a06568211750..1ec84499d054be780769287c38092a19a9889c60 100644
--- a/ProcessLib/HeatTransportBHE/BHE/CreateBHE1U.cpp
+++ b/ProcessLib/HeatTransportBHE/BHE/CreateBHE1U.cpp
@@ -246,4 +246,4 @@ BHE::BHE_1U* CreateBHE1U(
 }
 }  // namespace BHE
 }  // namespace HeatTransportBHE
-}  // namespace ProcessLib
+}  // namespace ProcessLib
\ No newline at end of file
diff --git a/ProcessLib/HeatTransportBHE/BHE/GroutParameters.h b/ProcessLib/HeatTransportBHE/BHE/GroutParameters.h
new file mode 100644
index 0000000000000000000000000000000000000000..c1355838f4065c9acf112bcb3fdba2dffb96641f
--- /dev/null
+++ b/ProcessLib/HeatTransportBHE/BHE/GroutParameters.h
@@ -0,0 +1,48 @@
+/**
+ * \file
+ *
+ * \copyright
+ * Copyright (c) 2012-2018, OpenGeoSys Community (http://www.opengeosys.org)
+ *            Distributed under a Modified BSD License.
+ *              See accompanying file LICENSE.txt or
+ *              http://www.opengeosys.org/project/license
+ *
+ */
+
+#pragma once
+
+namespace ProcessLib
+{
+namespace HeatTransportBHE
+{
+namespace BHE
+{
+struct GroutParameters
+{
+    /**
+     * density of the grout
+     * unit is kg m-3
+     */
+    double const rho_g;
+
+    /**
+     * porosity of the grout
+     * unit is [-]
+     */
+    double const porosity_g;
+
+    /**
+     * specific heat capacity of the grout
+     * unit is m^2 sec^-2 K^-1
+     */
+    double const heat_cap_g;
+
+    /**
+     * thermal conductivity of the grout
+     * unit is kg m sec^-3 K^-1
+     */
+    double const lambda_g;
+};
+}  // namespace BHE
+}  // namespace HeatTransportBHE
+}  // namespace ProcessLib
diff --git a/ProcessLib/HeatTransportBHE/BHE/MeshUtils.cpp b/ProcessLib/HeatTransportBHE/BHE/MeshUtils.cpp
index 1a55cc0c94d3f7f0588cff3c1c901db71b8e3b93..844ce563fa9e267f729e1034c3b57fe799cd8fef 100644
--- a/ProcessLib/HeatTransportBHE/BHE/MeshUtils.cpp
+++ b/ProcessLib/HeatTransportBHE/BHE/MeshUtils.cpp
@@ -18,42 +18,40 @@ namespace ProcessLib
 {
 namespace HeatTransportBHE
 {
-void getBHEDataInMesh(
-    MeshLib::Mesh const& mesh,
-    std::vector<MeshLib::Element*>& vec_soil_elements,
-    std::vector<int>& vec_BHE_mat_IDs,
-    std::vector<std::vector<MeshLib::Element*>>& vec_BHE_elements,
-    std::vector<MeshLib::Node*>& vec_soil_nodes,
-    std::vector<std::vector<MeshLib::Node*>>& vec_BHE_nodes)
+BHEMeshData getBHEDataInMesh(MeshLib::Mesh const& mesh)
 {
-    // get vectors of matrix elements and BHE elements
-    vec_soil_elements.reserve(mesh.getNumberOfElements());
+    BHEMeshData bheMeshData;
+    // partition all the mesh elements, and copy them into
+    // two seperate vectors, one with only matrix elements
+    // and the other only BHE elements
+    bheMeshData.soil_elements.reserve(mesh.getNumberOfElements());
     std::vector<MeshLib::Element*> all_BHE_elements;
-    for (MeshLib::Element* e : mesh.getElements())
-    {
-        // As for the first step, all elements are counted as a soil element
-        // first. Those elements connected with a BHE will picked up and
-        // reorganized into a seperate vector at the end of the function.
-        if (e->getDimension() == mesh.getDimension())
-            vec_soil_elements.push_back(e);
-        else if (e->getDimension() == (unsigned int)1)
-            all_BHE_elements.push_back(e);
-    }
+    auto& all_mesh_elements = mesh.getElements();
+    std::partition_copy(
+        std::begin(all_mesh_elements),
+        std::end(all_mesh_elements),
+        std::back_inserter(all_BHE_elements),
+        std::back_inserter(bheMeshData.soil_elements),
+        [](MeshLib::Element* e) { return e->getDimension() == 1; });
 
     // get BHE material IDs
-    auto opt_material_ids(
-        mesh.getProperties().getPropertyVector<int>("MaterialIDs"));
+    auto opt_material_ids = MeshLib::materialIDs(mesh);
+    if (opt_material_ids == nullptr)
+    {
+        OGS_FATAL("Not able to get material IDs! ");
+    }
     for (MeshLib::Element* e : all_BHE_elements)
-        vec_BHE_mat_IDs.push_back((*opt_material_ids)[e->getID()]);
-    BaseLib::makeVectorUnique(vec_BHE_mat_IDs);
-    DBUG("-> found %d BHE material groups", vec_BHE_mat_IDs.size());
+        bheMeshData.BHE_mat_IDs.push_back((*opt_material_ids)[e->getID()]);
+    BaseLib::makeVectorUnique(bheMeshData.BHE_mat_IDs);
+    DBUG("-> found %d BHE material groups", bheMeshData.BHE_mat_IDs.size());
 
     // create a vector of BHE elements for each group
-    vec_BHE_elements.resize(vec_BHE_mat_IDs.size());
-    for (unsigned bhe_id = 0; bhe_id < vec_BHE_mat_IDs.size(); bhe_id++)
+    bheMeshData.BHE_elements.resize(bheMeshData.BHE_mat_IDs.size());
+    for (unsigned bhe_id = 0; bhe_id < bheMeshData.BHE_mat_IDs.size(); bhe_id++)
     {
-        const auto bhe_mat_id = vec_BHE_mat_IDs[bhe_id];
-        std::vector<MeshLib::Element*>& vec_elements = vec_BHE_elements[bhe_id];
+        const auto bhe_mat_id = bheMeshData.BHE_mat_IDs[bhe_id];
+        std::vector<MeshLib::Element*>& vec_elements =
+            bheMeshData.BHE_elements[bhe_id];
         std::copy_if(all_BHE_elements.begin(), all_BHE_elements.end(),
                      std::back_inserter(vec_elements),
                      [&](MeshLib::Element* e) {
@@ -63,11 +61,11 @@ void getBHEDataInMesh(
     }
 
     // get a vector of BHE nodes
-    vec_BHE_nodes.resize(vec_BHE_mat_IDs.size());
-    for (unsigned bhe_id = 0; bhe_id < vec_BHE_mat_IDs.size(); bhe_id++)
+    bheMeshData.BHE_nodes.resize(bheMeshData.BHE_mat_IDs.size());
+    for (unsigned bhe_id = 0; bhe_id < bheMeshData.BHE_mat_IDs.size(); bhe_id++)
     {
-        std::vector<MeshLib::Node*>& vec_nodes = vec_BHE_nodes[bhe_id];
-        for (MeshLib::Element* e : vec_BHE_elements[bhe_id])
+        std::vector<MeshLib::Node*>& vec_nodes = bheMeshData.BHE_nodes[bhe_id];
+        for (MeshLib::Element* e : bheMeshData.BHE_elements[bhe_id])
         {
             for (unsigned i = 0; i < e->getNumberOfNodes(); i++)
             {
@@ -82,20 +80,20 @@ void getBHEDataInMesh(
     }
 
     // get all the nodes into the pure soil nodes vector
-    vec_soil_nodes.reserve(mesh.getNumberOfNodes());
+    bheMeshData.soil_nodes.reserve(mesh.getNumberOfNodes());
     for (MeshLib::Node* n : mesh.getNodes())
     {
         // All elements are counted as a soil element
-        vec_soil_nodes.push_back(n);
+        bheMeshData.soil_nodes.push_back(n);
     }
 
-    // final count of 3 types of elements
-    // They are
-    // (i)  soil,
-    // (ii) BHE
+    // finalLy counting two types of elements
+    // They are (i) soil, and (ii) BHE type of elements
     DBUG("-> found total %d soil elements and %d BHE elements",
-         vec_soil_elements.size(),
-         all_BHE_elements.size());
+         bheMeshData.soil_elements.size(),
+         bheMeshData.BHE_elements.size());
+
+    return bheMeshData;
 }
 }  // end of namespace HeatTransportBHE
-}  // namespace ProcessLib
+}  // namespace ProcessLib
\ No newline at end of file
diff --git a/ProcessLib/HeatTransportBHE/BHE/MeshUtils.h b/ProcessLib/HeatTransportBHE/BHE/MeshUtils.h
index ac75431d2e0057279b536fc2a13ba6b2f4d32154..d275b896ec2e0c41dbc02948a9544672f3f65def 100644
--- a/ProcessLib/HeatTransportBHE/BHE/MeshUtils.h
+++ b/ProcessLib/HeatTransportBHE/BHE/MeshUtils.h
@@ -21,22 +21,22 @@ namespace ProcessLib
 {
 namespace HeatTransportBHE
 {
+struct BHEMeshData
+{
+    std::vector<MeshLib::Element*> soil_elements;
+    std::vector<int> BHE_mat_IDs;
+    std::vector<std::vector<MeshLib::Element*>> BHE_elements;
+    std::vector<MeshLib::Node*> soil_nodes;
+    std::vector<std::vector<MeshLib::Node*>> BHE_nodes;
+};
+
 /**
  * get data about fracture and matrix elements/nodes from a mesh
  *
  * @param mesh  A mesh which includes BHE elements, i.e. 1-dimensional
  * elements. It is assumed that elements forming a BHE have a distinct
  * material ID.
- * @param vec_soil_elements a vector of soil elements
- * @param vec_BHE_elements  a vector of BHE elements (grouped by BHE IDs)
- * @param vec_BHE_nodes   a vector of BHE nodes (grouped by BHE IDs)
  */
-void getBHEDataInMesh(
-    MeshLib::Mesh const& mesh,
-    std::vector<MeshLib::Element*>& vec_soil_elements,
-    std::vector<int>& vec_BHE_mat_IDs,
-    std::vector<std::vector<MeshLib::Element*>>& vec_BHE_elements,
-    std::vector<MeshLib::Node*>& vec_pure_soil_nodes,
-    std::vector<std::vector<MeshLib::Node*>>& vec_BHE_nodes);
+BHEMeshData getBHEDataInMesh(MeshLib::Mesh const& mesh);
 }  // end of namespace HeatTransportBHE
 }  // namespace ProcessLib
diff --git a/ProcessLib/HeatTransportBHE/BHE/PipeParameters.h b/ProcessLib/HeatTransportBHE/BHE/PipeParameters.h
new file mode 100644
index 0000000000000000000000000000000000000000..1c016d7491186b836f21448f29fd25a1eaeb08cf
--- /dev/null
+++ b/ProcessLib/HeatTransportBHE/BHE/PipeParameters.h
@@ -0,0 +1,67 @@
+/**
+ * \file
+ *
+ * \copyright
+ * Copyright (c) 2012-2018, OpenGeoSys Community (http://www.opengeosys.org)
+ *            Distributed under a Modified BSD License.
+ *              See accompanying file LICENSE.txt or
+ *              http://www.opengeosys.org/project/license
+ *
+ */
+
+#pragma once
+
+namespace ProcessLib
+{
+namespace HeatTransportBHE
+{
+namespace BHE
+{
+struct PipeParameters
+{
+    /**
+     * radius of the pipline inner side
+     * unit is m
+     */
+    double const r_inner;
+
+    /**
+     * radius of the pipline outer side
+     * unit is m
+     */
+    double const r_outer;
+
+    /**
+     * pipe-in wall thickness
+     * unit is m
+     */
+    double const b_in;
+
+    /**
+     * pipe-out wall thickness
+     * unit is m
+     */
+    double const b_out;
+
+    /**
+     * thermal conductivity of the pipe wall
+     * unit is kg m sec^-3 K^-1
+     */
+
+    double const lambda_p;
+
+    /**
+     * thermal conductivity of the inner pipe wall
+     * unit is kg m sec^-3 K^-1
+     */
+    double const lambda_p_i;
+
+    /**
+     * thermal conductivity of the outer pipe wall
+     * unit is kg m sec^-3 K^-1
+     */
+    double const lambda_p_o;
+};
+}  // namespace BHE
+}  // namespace HeatTransportBHE
+}  // namespace ProcessLib
diff --git a/ProcessLib/HeatTransportBHE/BHE/RefrigerantParameters.h b/ProcessLib/HeatTransportBHE/BHE/RefrigerantParameters.h
new file mode 100644
index 0000000000000000000000000000000000000000..8b46b31a3510667dc580dc1215fe298e50c552f9
--- /dev/null
+++ b/ProcessLib/HeatTransportBHE/BHE/RefrigerantParameters.h
@@ -0,0 +1,54 @@
+/**
+ * \file
+ *
+ * \copyright
+ * Copyright (c) 2012-2018, OpenGeoSys Community (http://www.opengeosys.org)
+ *            Distributed under a Modified BSD License.
+ *              See accompanying file LICENSE.txt or
+ *              http://www.opengeosys.org/project/license
+ *
+ */
+
+#pragma once
+
+namespace ProcessLib
+{
+namespace HeatTransportBHE
+{
+namespace BHE
+{
+struct RefrigerantParameters
+{
+    /**
+     * dynamics viscosity of the refrigerant
+     * unit is kg m-1 sec-1
+     */
+    double const mu_r;
+
+    /**
+     * density of the refrigerant
+     * unit is kg m-3
+     */
+    double const rho_r;
+
+    /**
+     * thermal conductivity of the refrigerant
+     * unit is kg m sec^-3 K^-1
+     */
+    double const lambda_r;
+
+    /**
+     * specific heat capacity of the refrigerant
+     * unit is m^2 sec^-2 K^-1
+     */
+    double const heat_cap_r;
+
+    /**
+     * longitudinal dispersivity of the
+     * referigerant flow in the pipeline
+     */
+    double const alpha_L;
+};
+}  // namespace BHE
+}  // namespace HeatTransportBHE
+}  // namespace ProcessLib
diff --git a/ProcessLib/HeatTransportBHE/CMakeLists.txt b/ProcessLib/HeatTransportBHE/CMakeLists.txt
index 2d9def64402e62d9154925303a0ade8532756dab..2838acf7603c13002f509cb6abfc37c9fe4366a9 100644
--- a/ProcessLib/HeatTransportBHE/CMakeLists.txt
+++ b/ProcessLib/HeatTransportBHE/CMakeLists.txt
@@ -5,4 +5,4 @@ APPEND_SOURCE_FILES(SOURCES LocalAssemblers)
 add_library(HeatTransportBHE ${SOURCES})
 target_link_libraries(HeatTransportBHE PUBLIC ProcessLib)
 
-# include(Tests.cmake)
+include(Tests.cmake)
diff --git a/ProcessLib/HeatTransportBHE/HeatTransportBHEProcess.cpp b/ProcessLib/HeatTransportBHE/HeatTransportBHEProcess.cpp
index a8f1a0c8a3477dad8ee81aa169949e6534a57b08..5a9da59c74d1d1e0e95645bcd7817d436fd66513 100644
--- a/ProcessLib/HeatTransportBHE/HeatTransportBHEProcess.cpp
+++ b/ProcessLib/HeatTransportBHE/HeatTransportBHEProcess.cpp
@@ -37,39 +37,36 @@ HeatTransportBHEProcess::HeatTransportBHEProcess(
     : Process(mesh, std::move(jacobian_assembler), parameters,
               integration_order, std::move(process_variables),
               std::move(secondary_variables), std::move(named_function_caller)),
-      _process_data(std::move(process_data))
+      _process_data(std::move(process_data)),
+      _bheMeshData(getBHEDataInMesh(mesh))
 {
-    getBHEDataInMesh(mesh,
-                     _vec_pure_soil_elements,
-                     _vec_BHE_mat_IDs,
-                     _vec_BHE_elements,
-                     _vec_pure_soil_nodes,
-                     _vec_BHE_nodes);
-
-    if (_vec_BHE_mat_IDs.size() != _process_data._vec_BHE_property.size())
+    if (_bheMeshData.BHE_mat_IDs.size() !=
+        _process_data._vec_BHE_property.size())
     {
         OGS_FATAL(
             "The number of the given BHE properties (%d) are not "
             "consistent"
             " with the number of BHE groups in a mesh (%d).",
             _process_data._vec_BHE_property.size(),
-            _vec_BHE_mat_IDs.size());
+            _bheMeshData.BHE_mat_IDs.size());
     }
 
+    auto material_ids = MeshLib::materialIDs(mesh);
+    if (material_ids == nullptr)
+    {
+        OGS_FATAL("Not able to get material IDs! ");
+    }
     // create a map from a material ID to a BHE ID
     auto max_BHE_mat_id =
-        std::max_element(_vec_BHE_mat_IDs.begin(), _vec_BHE_mat_IDs.end());
-    const std::size_t nBHEmatIDs = *max_BHE_mat_id + 1;
-    _process_data._map_materialID_to_BHE_ID.resize(nBHEmatIDs);
-    for (std::size_t i = 0; i < _vec_BHE_mat_IDs.size(); i++)
+        std::max_element(material_ids->begin(), material_ids->end());
+    _process_data._map_materialID_to_BHE_ID.resize(*max_BHE_mat_id + 1);
+    for (std::size_t i = 0; i < _bheMeshData.BHE_mat_IDs.size(); i++)
     {
-        // by default, it is assumed that the soil compartment takes material ID
-        // 0 and the BHE take the successive material group.
-        _process_data._map_materialID_to_BHE_ID[_vec_BHE_mat_IDs[i]] = i;
+        // fill in the map structure
+        _process_data._map_materialID_to_BHE_ID[_bheMeshData.BHE_mat_IDs[i]] =
+            i;
     }
 
-    MeshLib::PropertyVector<int> const* material_ids(
-        mesh.getProperties().getPropertyVector<int>("MaterialIDs"));
     _process_data._mesh_prop_materialIDs = material_ids;
 }
 
@@ -84,16 +81,16 @@ void HeatTransportBHEProcess::constructDofTable()
     //------------------------------------------------------------
     // first all the soil nodes
     _mesh_subset_pure_soil_nodes =
-        std::make_unique<MeshLib::MeshSubset>(_mesh, _vec_pure_soil_nodes);
+        std::make_unique<MeshLib::MeshSubset>(_mesh, _bheMeshData.soil_nodes);
 
     std::vector<int> vec_n_BHE_unknowns;
-    vec_n_BHE_unknowns.reserve(_vec_BHE_nodes.size());
+    vec_n_BHE_unknowns.reserve(_bheMeshData.BHE_nodes.size());
     // the BHE nodes need to be cherry-picked from the vector
-    for (unsigned i = 0; i < _vec_BHE_nodes.size(); i++)
+    for (unsigned i = 0; i < _bheMeshData.BHE_nodes.size(); i++)
     {
         _mesh_subset_BHE_nodes.push_back(
-            std::make_unique<MeshLib::MeshSubset const>(_mesh,
-                                                        _vec_BHE_nodes[i]));
+            std::make_unique<MeshLib::MeshSubset const>(
+                _mesh, _bheMeshData.BHE_nodes[i]));
         int n_BHE_unknowns =
             _process_data._vec_BHE_property[i]->getNumUnknowns();
         vec_n_BHE_unknowns.emplace_back(n_BHE_unknowns);
@@ -122,7 +119,7 @@ void HeatTransportBHEProcess::constructDofTable()
     // temperature.
     vec_n_components.push_back(1);
     // now the BHE subsets
-    for (std::size_t i = 0; i < _vec_BHE_mat_IDs.size(); i++)
+    for (std::size_t i = 0; i < _bheMeshData.BHE_mat_IDs.size(); i++)
     {
         // Here the number of components equals to
         // the number of unknowns on the BHE
@@ -132,9 +129,9 @@ void HeatTransportBHEProcess::constructDofTable()
     std::vector<std::vector<MeshLib::Element*> const*> vec_var_elements;
     // vec_var_elements.push_back(&_vec_pure_soil_elements);
     vec_var_elements.push_back(&(_mesh.getElements()));
-    for (std::size_t i = 0; i < _vec_BHE_elements.size(); i++)
+    for (std::size_t i = 0; i < _bheMeshData.BHE_elements.size(); i++)
     {
-        vec_var_elements.push_back(&_vec_BHE_elements[i]);
+        vec_var_elements.push_back(&_bheMeshData.BHE_elements[i]);
     }
 
     _local_to_global_index_map =
@@ -153,12 +150,10 @@ void HeatTransportBHEProcess::initializeConcreteProcess(
     MeshLib::Mesh const& mesh,
     unsigned const integration_order)
 {
-    const int process_id = 0;
-
-    // this process can only run with 3-dimensional mesh
+    assert(mesh.getDimension() == 3);
     ProcessLib::HeatTransportBHE::createLocalAssemblers<
-        3, /*mesh.getDimension(),*/
-        HeatTransportBHELocalAssemblerSoil, HeatTransportBHELocalAssemblerBHE>(
+        3 /* mesh dimension */, HeatTransportBHELocalAssemblerSoil,
+        HeatTransportBHELocalAssemblerBHE>(
         mesh.getElements(), dof_table, _local_assemblers,
         _process_data._vec_ele_connected_BHE_IDs,
         _process_data._vec_BHE_property, mesh.isAxiallySymmetric(),
@@ -169,10 +164,13 @@ void HeatTransportBHEProcess::initializeConcreteProcess(
     // and one BC at the bottom.
     std::vector<std::unique_ptr<BoundaryCondition>> bc_collections =
         createBHEBoundaryConditionTopBottom();
+
+    const int process_id = 0;
     auto& current_process_BCs = _boundary_conditions[process_id];
-    auto const bc_collections_size = bc_collections.size();
-    for (unsigned i = 0; i < bc_collections_size; i++)
-        current_process_BCs.addCreatedBC(std::move(bc_collections[i]));
+    for (auto& bc_collection : bc_collections)
+    {
+        current_process_BCs.addCreatedBC(std::move(bc_collection));
+    }
 }
 
 void HeatTransportBHEProcess::assembleConcreteProcess(const double t,
@@ -192,19 +190,12 @@ void HeatTransportBHEProcess::assembleConcreteProcess(const double t,
 }
 
 void HeatTransportBHEProcess::assembleWithJacobianConcreteProcess(
-    const double t, GlobalVector const& x, GlobalVector const& xdot,
-    const double dxdot_dx, const double dx_dx, GlobalMatrix& M, GlobalMatrix& K,
-    GlobalVector& b, GlobalMatrix& Jac)
+    const double /*t*/, GlobalVector const& /*x*/, GlobalVector const& /*xdot*/,
+    const double /*dxdot_dx*/, const double /*dx_dx*/, GlobalMatrix& /*M*/,
+    GlobalMatrix& /*K*/, GlobalVector& /*b*/, GlobalMatrix& /*Jac*/)
 {
-    DBUG("AssembleWithJacobian HeatTransportBHE process.");
-
-    std::vector<std::reference_wrapper<NumLib::LocalToGlobalIndexMap>>
-        dof_table = {std::ref(*_local_to_global_index_map)};
-    // Call global assembler for each local assembly item.
-    GlobalExecutor::executeMemberDereferenced(
-        _global_assembler, &VectorMatrixAssembler::assembleWithJacobian,
-        _local_assemblers, dof_table, t, x, xdot, dxdot_dx, dx_dx, M, K, b, Jac,
-        _coupled_solutions);
+    OGS_FATAL(
+        "HeatTransportBHE: analytical Jacobian assembly is not implemented");
 }
 
 void HeatTransportBHEProcess::computeSecondaryVariableConcrete(
@@ -227,189 +218,51 @@ HeatTransportBHEProcess::createBHEBoundaryConditionTopBottom()
 
     int const n_BHEs = static_cast<int>(_process_data._vec_BHE_property.size());
 
-    // bulk dof table
-    NumLib::LocalToGlobalIndexMap dof_table_bulk = *_local_to_global_index_map;
-
-    std::vector<MeshLib::Node*> bc_top_nodes;
-    std::vector<MeshLib::Node*> bc_bottom_nodes;
-
     // for each BHE
     for (int bhe_i = 0; bhe_i < n_BHEs; bhe_i++)
     {
-        // get the BHE type
-        auto const& bhe_typ = _process_data._vec_BHE_property[bhe_i]->bhe_type;
+        auto const& bhe_nodes = _bheMeshData.BHE_nodes[bhe_i];
         // find the variable ID
         // the soil temperature is 0-th variable
         // the BHE temperature is therefore bhe_i + 1
         const int variable_id = bhe_i + 1;
-        // find the node in mesh that are at the top
-        auto const n_bhe_nodes = _vec_BHE_nodes[bhe_i].size();
-        unsigned int idx_top = 0;
-        unsigned int idx_bottom = _vec_BHE_nodes[bhe_i].size() - 1;
-        double top_z_coord = _vec_BHE_nodes[bhe_i][idx_top]->getCoords()[2];
-        double bottom_z_coord =
-            _vec_BHE_nodes[bhe_i][idx_bottom]->getCoords()[2];
-        for (unsigned bhe_node_i = 0; bhe_node_i < n_bhe_nodes; bhe_node_i++)
-        {
-            if (_vec_BHE_nodes[bhe_i][bhe_node_i]->getCoords()[2] >=
-                top_z_coord)
-                idx_top = bhe_node_i;
-
-            if (_vec_BHE_nodes[bhe_i][bhe_node_i]->getCoords()[2] <=
-                bottom_z_coord)
-                idx_bottom = bhe_node_i;
-        }
-        bc_top_nodes.clear();
-        bc_top_nodes.emplace_back(_vec_BHE_nodes[bhe_i][idx_top]);
-        bc_bottom_nodes.clear();
-        bc_bottom_nodes.emplace_back(_vec_BHE_nodes[bhe_i][idx_bottom]);
-
-        MeshLib::MeshSubset bc_mesh_subset_top{_mesh, bc_top_nodes};
-        MeshLib::Mesh const& bc_mesh_top = bc_mesh_subset_top.getMesh();
-
-        MeshLib::MeshSubset bc_mesh_subset_bottom{_mesh, bc_bottom_nodes};
-        MeshLib::Mesh const& bc_mesh_bottom = bc_mesh_subset_bottom.getMesh();
 
-        auto get_global_bhe_bc_index_top = [&](std::size_t const mesh_id,
-                                               int const component_id) {
-            return dof_table_bulk.getGlobalIndex(
-                {mesh_id, MeshLib::MeshItemType::Node,
-                 bc_top_nodes[0]->getID()},
-                variable_id, component_id);
-        };
-        auto get_global_bhe_bc_index_bottom = [&](std::size_t const mesh_id,
-                                                  int const component_id) {
-            return dof_table_bulk.getGlobalIndex(
-                {mesh_id, MeshLib::MeshItemType::Node,
-                 bc_bottom_nodes[0]->getID()},
-                variable_id, component_id);
-        };
-
-        // the create_BC function will be repeatedly used
-        auto create_BC_top_inflow = [&](std::size_t const mesh_id,
-                                        int const component_id_in,
-                                        int const component_id_out) {
-            auto const global_index_in =
-                get_global_bhe_bc_index_top(mesh_id, component_id_in);
-            auto const global_index_out =
-                get_global_bhe_bc_index_top(mesh_id, component_id_out);
-            return ProcessLib::createBHEInflowDirichletBoundaryCondition(
-                global_index_in, global_index_out, _mesh, bc_top_nodes,
-                variable_id, component_id_in,
-                _process_data._vec_BHE_property[bhe_i]);
-        };
-        auto create_BC_bottom_outflow = [&](std::size_t const mesh_id,
-                                            int const component_id_in,
-                                            int const component_id_out) {
-            auto const global_index_in =
-                get_global_bhe_bc_index_bottom(mesh_id, component_id_in);
-            auto const global_index_out =
-                get_global_bhe_bc_index_bottom(mesh_id, component_id_out);
-            return ProcessLib::createBHEBottomDirichletBoundaryCondition(
-                global_index_in, global_index_out, _mesh, bc_top_nodes,
-                variable_id, component_id_in);
-        };
-
-        // depending on the BHE type
-        switch (bhe_typ)
+        // Bottom and top nodes w.r.t. the z coordinate.
+        auto const bottom_top_nodes = std::minmax_element(
+            begin(bhe_nodes), end(bhe_nodes),
+            [&](auto const& a, auto const& b) {
+                return a->getCoords()[2] < b->getCoords()[2];
+            });
+        auto const bc_bottom_node_id = (*bottom_top_nodes.first)->getID();
+        MeshLib::Node* const bc_top_node = *bottom_top_nodes.second;
+
+        auto get_global_bhe_bc_indices =
+            [&](std::size_t const node_id,
+                std::pair<int, int> const& in_out_component_id) {
+                return std::make_pair(
+                    _local_to_global_index_map->getGlobalIndex(
+                        {_mesh.getID(), MeshLib::MeshItemType::Node, node_id},
+                        variable_id, in_out_component_id.first),
+                    _local_to_global_index_map->getGlobalIndex(
+                        {_mesh.getID(), MeshLib::MeshItemType::Node, node_id},
+                        variable_id, in_out_component_id.second));
+            };
+
+        for (auto const& in_out_component_id :
+             _process_data._vec_BHE_property[bhe_i]
+                 ->inflowOutflowBcComponentIds())
         {
-            case ProcessLib::HeatTransportBHE::BHE::BHE_TYPE::TYPE_2U:
-            {
-                unsigned const component_id_T_in_1 = 0;
-                unsigned const component_id_T_out_1 = 2;
-
-                unsigned const component_id_T_out_2 = 3;
-                unsigned const component_id_T_in_2 = 1;
-
-                // there are 2 BCs on the top node
-                std::unique_ptr<BHEInflowDirichletBoundaryCondition> bc_top_1 =
-                    create_BC_top_inflow(bc_mesh_top.getID(),
-                                         component_id_T_in_1,
-                                         component_id_T_out_1);
-                std::unique_ptr<BHEInflowDirichletBoundaryCondition> bc_top_2 =
-                    create_BC_top_inflow(bc_mesh_top.getID(),
-                                         component_id_T_in_2,
-                                         component_id_T_out_2);
-
-                // there are also 2 BCs on the bottom node
-                std::unique_ptr<BHEBottomDirichletBoundaryCondition>
-                    bc_bottom_1 = create_BC_bottom_outflow(
-                        bc_mesh_bottom.getID(), component_id_T_in_1,
-                        component_id_T_out_1);
-                std::unique_ptr<BHEBottomDirichletBoundaryCondition>
-                    bc_bottom_2 = create_BC_bottom_outflow(
-                        bc_mesh_bottom.getID(), component_id_T_in_2,
-                        component_id_T_out_2);
-
-                // add bc_top and bc_bottom to the vector
-                bcs.push_back(std::move(bc_top_1));
-                bcs.push_back(std::move(bc_top_2));
-                bcs.push_back(std::move(bc_bottom_1));
-                bcs.push_back(std::move(bc_bottom_2));
-            }
-            break;
-            case ProcessLib::HeatTransportBHE::BHE::BHE_TYPE::TYPE_1U:
-            {
-                unsigned const component_id_T_in = 0;
-                unsigned const component_id_T_out = 1;
-                // there is one BC on the top node
-                std::unique_ptr<BHEInflowDirichletBoundaryCondition> bc_top =
-                    create_BC_top_inflow(bc_mesh_top.getID(), component_id_T_in,
-                                         component_id_T_out);
-
-                // there is also 1 BC on the bottom node
-                std::unique_ptr<BHEBottomDirichletBoundaryCondition> bc_bottom =
-                    create_BC_bottom_outflow(bc_mesh_bottom.getID(),
-                                             component_id_T_in,
-                                             component_id_T_out);
-
-                // add bc_top and bc_bottom to the vector
-                bcs.push_back(std::move(bc_top));
-                bcs.push_back(std::move(bc_bottom));
-            }
-            break;
-            case ProcessLib::HeatTransportBHE::BHE::BHE_TYPE::TYPE_CXC:
-            {
-                unsigned const component_id_T_in = 1;
-                unsigned const component_id_T_out = 0;
-                // there is one BC on the top node
-                std::unique_ptr<BHEInflowDirichletBoundaryCondition> bc_top =
-                    create_BC_top_inflow(bc_mesh_top.getID(), component_id_T_in,
-                                         component_id_T_out);
-
-                // there is also 1 BC on the bottom node
-                std::unique_ptr<BHEBottomDirichletBoundaryCondition> bc_bottom =
-                    create_BC_bottom_outflow(bc_mesh_bottom.getID(),
-                                             component_id_T_in,
-                                             component_id_T_out);
-
-                // add bc_top and bc_bottom to the vector
-                bcs.push_back(std::move(bc_top));
-                bcs.push_back(std::move(bc_bottom));
-            }
-            break;
-            case ProcessLib::HeatTransportBHE::BHE::BHE_TYPE::TYPE_CXA:
-            {
-                unsigned const component_id_T_in = 0;
-                unsigned const component_id_T_out = 1;
-                // there is one BC on the top node
-                std::unique_ptr<BHEInflowDirichletBoundaryCondition> bc_top =
-                    create_BC_top_inflow(bc_mesh_top.getID(), component_id_T_in,
-                                         component_id_T_out);
-
-                // there is also 1 BC on the bottom node
-                std::unique_ptr<BHEBottomDirichletBoundaryCondition> bc_bottom =
-                    create_BC_bottom_outflow(bc_mesh_bottom.getID(),
-                                             component_id_T_in,
-                                             component_id_T_out);
-
-                // add bc_top and bc_bottom to the vector
-                bcs.push_back(std::move(bc_top));
-                bcs.push_back(std::move(bc_bottom));
-            }
-            break;
-            default:
-                OGS_FATAL("WRONG BHE TYPE");
+            // Top, inflow.
+            bcs.push_back(ProcessLib::createBHEInflowDirichletBoundaryCondition(
+                get_global_bhe_bc_indices(bc_top_node->getID(),
+                                          in_out_component_id),
+                _mesh, {bc_top_node}, variable_id, in_out_component_id.first,
+                _process_data._vec_BHE_property[bhe_i]));
+
+            // Bottom, outflow.
+            bcs.push_back(ProcessLib::createBHEBottomDirichletBoundaryCondition(
+                get_global_bhe_bc_indices(bc_bottom_node_id,
+                                          in_out_component_id)));
         }
     }
 
diff --git a/ProcessLib/HeatTransportBHE/HeatTransportBHEProcess.h b/ProcessLib/HeatTransportBHE/HeatTransportBHEProcess.h
index 217e4da211538bd1e1b2a59f75c329ed2a4d215e..6af02ecd02a1a2afc4e25d43fe8d87b3669d8a54 100644
--- a/ProcessLib/HeatTransportBHE/HeatTransportBHEProcess.h
+++ b/ProcessLib/HeatTransportBHE/HeatTransportBHEProcess.h
@@ -10,6 +10,7 @@
 #pragma once
 
 #include "HeatTransportBHEProcessData.h"
+#include "ProcessLib/HeatTransportBHE/BHE/MeshUtils.h"
 #include "ProcessLib/HeatTransportBHE/LocalAssemblers/HeatTransportBHEProcessAssemblerInterface.h"
 #include "ProcessLib/Process.h"
 
@@ -17,6 +18,8 @@ namespace ProcessLib
 {
 namespace HeatTransportBHE
 {
+struct BHEMeshData;
+
 class HeatTransportBHEProcess final : public Process
 {
 public:
@@ -64,35 +67,18 @@ private:
     std::vector<std::unique_ptr<HeatTransportBHELocalAssemblerInterface>>
         _local_assemblers;
 
-    /**
-     * These are the elements that are representing BHEs
-     */
-    std::vector<std::vector<MeshLib::Element*>> _vec_BHE_elements;
-
-    /**
-     * These are the elements that are not connected with any BHE
-     */
-    std::vector<MeshLib::Element*> _vec_pure_soil_elements;
-
-    /**
-     * These are the soil nodes that are not connected with a BHE
-     */
-    std::vector<MeshLib::Node*> _vec_pure_soil_nodes;
-
-    /**
-     * Mesh nodes that are located on any BHE
-     * ordered according to each BHE
-     */
-    std::vector<std::vector<MeshLib::Node*>> _vec_BHE_nodes;
-
     std::vector<std::unique_ptr<MeshLib::MeshSubset const>>
         _mesh_subset_BHE_nodes;
+
     std::vector<std::unique_ptr<MeshLib::MeshSubset const>>
         _mesh_subset_BHE_soil_nodes;
+
     std::unique_ptr<MeshLib::MeshSubset const> _mesh_subset_pure_soil_nodes;
+
     std::unique_ptr<MeshLib::MeshSubset const>
         _mesh_subset_soil_nodes_connected_with_BHE;
-    std::vector<int> _vec_BHE_mat_IDs;
+
+    const BHEMeshData _bheMeshData;
 };
 }  // namespace HeatTransportBHE
 }  // namespace ProcessLib
diff --git a/ProcessLib/HeatTransportBHE/LocalAssemblers/HeatTransportBHELocalAssemblerBHE.h b/ProcessLib/HeatTransportBHE/LocalAssemblers/HeatTransportBHELocalAssemblerBHE.h
index f7ea07d91062fd54fbce295eec09fb9b6a299b76..0a904d254e297f626b98fa3a82232b36c72f68ba 100644
--- a/ProcessLib/HeatTransportBHE/LocalAssemblers/HeatTransportBHELocalAssemblerBHE.h
+++ b/ProcessLib/HeatTransportBHE/LocalAssemblers/HeatTransportBHELocalAssemblerBHE.h
@@ -42,7 +42,6 @@ public:
 
     HeatTransportBHELocalAssemblerBHE(
         MeshLib::Element const& e,
-        std::size_t const local_matrix_size,
         std::vector<unsigned> const& dofIndex_to_localIndex,
         bool const is_axially_symmetric,
         unsigned const integration_order,
@@ -53,16 +52,6 @@ public:
                   std::vector<double>& /*local_K_data*/,
                   std::vector<double>& /*local_b_data*/) override;
 
-    void assembleWithJacobian(double const /*t*/,
-                              Eigen::VectorXd const& /*local_u*/,
-                              Eigen::VectorXd& /*local_b*/,
-                              Eigen::MatrixXd& /*local_J*/) override
-    {
-        OGS_FATAL(
-            "HeatTransportBHELocalAssembler: assembly with jacobian is not "
-            "implemented.");
-    }
-
     void preTimestepConcrete(std::vector<double> const& /*local_x*/,
                              double const /*t*/,
                              double const /*delta_t*/) override
diff --git a/ProcessLib/HeatTransportBHE/LocalAssemblers/HeatTransportBHELocalAssemblerBHE_impl.h b/ProcessLib/HeatTransportBHE/LocalAssemblers/HeatTransportBHELocalAssemblerBHE_impl.h
index 5285b4b2785a62419eda825da18d9d556ea7fd19..5d5d8c79caa0d95a595570bee259602387f92c40 100644
--- a/ProcessLib/HeatTransportBHE/LocalAssemblers/HeatTransportBHELocalAssemblerBHE_impl.h
+++ b/ProcessLib/HeatTransportBHE/LocalAssemblers/HeatTransportBHELocalAssemblerBHE_impl.h
@@ -25,7 +25,6 @@ template <typename ShapeFunction, typename IntegrationMethod, int BHE_Dim>
 HeatTransportBHELocalAssemblerBHE<ShapeFunction, IntegrationMethod, BHE_Dim>::
     HeatTransportBHELocalAssemblerBHE(
         MeshLib::Element const& e,
-        std::size_t const /*local_matrix_size*/,
         std::vector<unsigned> const& dofIndex_to_localIndex,
         bool const is_axially_symmetric,
         unsigned const integration_order,
@@ -62,9 +61,8 @@ HeatTransportBHELocalAssemblerBHE<ShapeFunction, IntegrationMethod, BHE_Dim>::
     {
         x_position.setIntegrationPoint(ip);
 
-        IntegrationPointDataBHE<ShapeMatricesType> int_Point_Data_BHE(
-            *(_process_data._vec_BHE_property[BHE_id]));
-        _ip_data.emplace_back(int_Point_Data_BHE);
+        // create the class IntegrationPointDataBHE in place
+        _ip_data.emplace_back(*(_process_data._vec_BHE_property[BHE_id]));
         auto const& sm = shape_matrices[ip];
         auto& ip_data = _ip_data[ip];
         ip_data.integration_weight =
@@ -109,7 +107,6 @@ HeatTransportBHELocalAssemblerBHE<ShapeFunction, IntegrationMethod, BHE_Dim>::
         _process_data._vec_BHE_property[BHE_id]->setRMatrices(
             idx_bhe_unknowns, ShapeFunction::NPOINTS, matBHE_loc_R, _R_matrix,
             _R_pi_s_matrix, _R_s_matrix);
-
     }  // end of loop over BHE unknowns
 
     // debugging
diff --git a/ProcessLib/HeatTransportBHE/LocalAssemblers/HeatTransportBHELocalAssemblerSoil.h b/ProcessLib/HeatTransportBHE/LocalAssemblers/HeatTransportBHELocalAssemblerSoil.h
index 3ee83392e2c0674c856465e1fdbf1e399875bd22..f556f807109d5349a5f411ba72a3c22c22d7555b 100644
--- a/ProcessLib/HeatTransportBHE/LocalAssemblers/HeatTransportBHELocalAssemblerSoil.h
+++ b/ProcessLib/HeatTransportBHE/LocalAssemblers/HeatTransportBHELocalAssemblerSoil.h
@@ -26,8 +26,6 @@ namespace ProcessLib
 {
 namespace HeatTransportBHE
 {
-const unsigned NUM_NODAL_DOF_SOIL = 1;
-
 template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
 class HeatTransportBHELocalAssemblerSoil
     : public HeatTransportBHELocalAssemblerInterface
@@ -45,7 +43,6 @@ public:
 
     HeatTransportBHELocalAssemblerSoil(
         MeshLib::Element const& e,
-        std::size_t const local_matrix_size,
         std::vector<unsigned> const& dofIndex_to_localIndex,
         bool is_axially_symmetric,
         unsigned const integration_order,
@@ -56,29 +53,6 @@ public:
                   std::vector<double>& /*local_K_data*/,
                   std::vector<double>& /*local_b_data*/) override;
 
-    void assembleWithJacobian(double const /*t*/,
-                              std::vector<double> const& /*local_x*/,
-                              std::vector<double> const& /*local_xdot*/,
-                              const double /*dxdot_dx*/, const double /*dx_dx*/,
-                              std::vector<double>& /*local_M_data*/,
-                              std::vector<double>& /*local_K_data*/,
-                              std::vector<double>& /*local_b_data*/,
-                              std::vector<double>& /*local_Jac_data*/) override
-    {
-        OGS_FATAL(
-            "HeatTransportBHELocalAssemblerMatrix: assembly with jacobian is "
-            "not "
-            "implemented.");
-    }
-
-    void preTimestepConcrete(std::vector<double> const& /*local_x*/,
-                             double const /*t*/,
-                             double const /*delta_t*/) override
-    {
-    }
-
-    void postTimestepConcrete(std::vector<double> const& /*local_x*/) override;
-
     Eigen::Map<const Eigen::RowVectorXd> getShapeMatrix(
         const unsigned integration_point) const override
     {
@@ -92,8 +66,8 @@ private:
     HeatTransportBHEProcessData& _process_data;
 
     std::vector<
-        IntegrationPointDataSoil<ShapeMatricesType>,
-        Eigen::aligned_allocator<IntegrationPointDataSoil<ShapeMatricesType>>>
+        IntegrationPointDataSoil<NodalMatrixType>,
+        Eigen::aligned_allocator<IntegrationPointDataSoil<NodalMatrixType>>>
         _ip_data;
 
     IntegrationMethod const _integration_method;
diff --git a/ProcessLib/HeatTransportBHE/LocalAssemblers/HeatTransportBHELocalAssemblerSoil_impl.h b/ProcessLib/HeatTransportBHE/LocalAssemblers/HeatTransportBHELocalAssemblerSoil_impl.h
index 3cf4209eefe010d583da12f566ae84e8f0c5f8a5..ffe4d351484de80c34f86c288afd8e2e7bd9fc6b 100644
--- a/ProcessLib/HeatTransportBHE/LocalAssemblers/HeatTransportBHELocalAssemblerSoil_impl.h
+++ b/ProcessLib/HeatTransportBHE/LocalAssemblers/HeatTransportBHELocalAssemblerSoil_impl.h
@@ -37,7 +37,6 @@ HeatTransportBHELocalAssemblerSoil<ShapeFunction,
                                    GlobalDim>::
     HeatTransportBHELocalAssemblerSoil(
         MeshLib::Element const& e,
-        std::size_t const /*local_matrix_size*/,
         std::vector<unsigned> const& dofIndex_to_localIndex,
         bool const is_axially_symmetric,
         unsigned const integration_order,
@@ -60,6 +59,26 @@ HeatTransportBHELocalAssemblerSoil<ShapeFunction,
                                         IntegrationMethod,
                                         GlobalDim>(
         e, is_axially_symmetric, _integration_method);
+
+    SpatialPosition x_position;
+    x_position.setElementID(element_id);
+
+    // ip data initialization
+    for (unsigned ip = 0; ip < n_integration_points; ip++)
+    {
+        x_position.setIntegrationPoint(ip);
+
+        // create the class IntegrationPointDataBHE in place
+        _ip_data.emplace_back();
+        auto const& sm = _shape_matrices[ip];
+        auto& ip_data = _ip_data[ip];
+        double const w = _integration_method.getWeightedPoint(ip).getWeight() *
+                         sm.integralMeasure * sm.detJ;
+        ip_data.NTN_product_times_w = sm.N.transpose() * sm.N * w;
+        ip_data.dNdxTdNdx_product_times_w = sm.dNdx.transpose() * sm.dNdx * w;
+
+        _secondary_data.N[ip] = sm.N;
+    }
 }
 
 template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
@@ -72,14 +91,13 @@ void HeatTransportBHELocalAssemblerSoil<
                          std::vector<double>& local_K_data,
                          std::vector<double>& /*local_b_data*/)
 {
-    auto const local_matrix_size = local_x.size();
-
-    assert(local_matrix_size == ShapeFunction::NPOINTS * NUM_NODAL_DOF_SOIL);
+    assert(local_x.size() == ShapeFunction::NPOINTS);
+    (void)local_x;  // Avoid unused arg warning.
 
     auto local_M = MathLib::createZeroedMatrix<NodalMatrixType>(
-        local_M_data, local_matrix_size, local_matrix_size);
+        local_M_data, ShapeFunction::NPOINTS, ShapeFunction::NPOINTS);
     auto local_K = MathLib::createZeroedMatrix<NodalMatrixType>(
-        local_K_data, local_matrix_size, local_matrix_size);
+        local_K_data, ShapeFunction::NPOINTS, ShapeFunction::NPOINTS);
 
     unsigned const n_integration_points =
         _integration_method.getNumberOfPoints();
@@ -90,8 +108,9 @@ void HeatTransportBHELocalAssemblerSoil<
     for (unsigned ip = 0; ip < n_integration_points; ip++)
     {
         pos.setIntegrationPoint(ip);
-        auto const& sm = _shape_matrices[ip];
-        auto const& wp = _integration_method.getWeightedPoint(ip);
+        auto& ip_data = _ip_data[ip];
+        auto const& M_ip = ip_data.NTN_product_times_w;
+        auto const& K_ip = ip_data.dNdxTdNdx_product_times_w;
 
         // auto const k_f = _process_data.thermal_conductivity_fluid(t, pos)[0];
         // auto const k_g = _process_data.thermal_conductivity_gas(t, pos)[0];
@@ -110,13 +129,10 @@ void HeatTransportBHELocalAssemblerSoil<
         // for now only using the solid phase parameters
 
         // assemble Conductance matrix
-        local_K.noalias() += sm.dNdx.transpose() * k_s * sm.dNdx * sm.detJ *
-                             wp.getWeight() * sm.integralMeasure;
+        local_K.noalias() += K_ip * k_s;
 
         // assemble Mass matrix
-        local_M.noalias() += sm.N.transpose() * density_s * heat_capacity_s *
-                             sm.N * sm.detJ * wp.getWeight() *
-                             sm.integralMeasure;
+        local_M.noalias() += M_ip * density_s * heat_capacity_s;
     }
 
     // debugging
@@ -125,13 +141,5 @@ void HeatTransportBHELocalAssemblerSoil<
     // std::cout << local_K.format(CleanFmt) << sep;
     // std::cout << local_M.format(CleanFmt) << sep;
 }
-
-template <typename ShapeFunction, typename IntegrationMethod, int GlobalDim>
-void HeatTransportBHELocalAssemblerSoil<
-    ShapeFunction,
-    IntegrationMethod,
-    GlobalDim>::postTimestepConcrete(std::vector<double> const& /*local_x*/)
-{
-}
 }  // namespace HeatTransportBHE
 }  // namespace ProcessLib
diff --git a/ProcessLib/HeatTransportBHE/LocalAssemblers/HeatTransportBHEProcessAssemblerInterface.h b/ProcessLib/HeatTransportBHE/LocalAssemblers/HeatTransportBHEProcessAssemblerInterface.h
index 2af5e87a70b990a0cb760e2a32c51aa3055a188b..d0390c17999c0dc34459ea3f520c61e8381c5731 100644
--- a/ProcessLib/HeatTransportBHE/LocalAssemblers/HeatTransportBHEProcessAssemblerInterface.h
+++ b/ProcessLib/HeatTransportBHE/LocalAssemblers/HeatTransportBHEProcessAssemblerInterface.h
@@ -26,38 +26,20 @@ class HeatTransportBHELocalAssemblerInterface
       public NumLib::ExtrapolatableElement
 {
 public:
-    HeatTransportBHELocalAssemblerInterface() : _dofIndex_to_localIndex{} {}
-    HeatTransportBHELocalAssemblerInterface(
-        std::size_t n_local_size, std::vector<unsigned> dofIndex_to_localIndex)
+    // The following function is necessary, because the BHE or Soil assemblers
+    // create their local_x memory based on the passed on dofIndex_to_localIndex
+    // Please keep it unchanged.
+    HeatTransportBHELocalAssemblerInterface(std::size_t /*n_local_size*/,
+                                            std::vector<unsigned>
+                                                dofIndex_to_localIndex)
         : _dofIndex_to_localIndex(std::move(dofIndex_to_localIndex))
     {
     }
 
-    void assembleWithJacobian(double const /*t*/,
-                              std::vector<double> const& /*local_x_*/,
-                              std::vector<double> const& /*local_xdot*/,
-                              const double /*dxdot_dx*/, const double /*dx_dx*/,
-                              std::vector<double>& /*local_M_data*/,
-                              std::vector<double>& /*local_K_data*/,
-                              std::vector<double>& /*local_b_data*/,
-                              std::vector<double>& /*local_Jac_data*/) override
-    {
-        OGS_FATAL(
-            "HeatTransportBHELocalAssemblerInterface::assembleWithJacobian() "
-            "is not implemented");
-    }
-
-    virtual void assembleWithJacobian(double const /*t*/,
-                                      Eigen::VectorXd const& /*local_T*/,
-                                      Eigen::VectorXd& /*local_b*/,
-                                      Eigen::MatrixXd& /*local_A*/)
-    {
-        OGS_FATAL(
-            "HeatTransportBHELocalAssemblerInterface::assembleWithJacobian() "
-            "is not implemented");
-    }
-
 private:
+    // this dofTalbe must be kept here.
+    // When initializing the assembler, this will be needed to
+    // initialize the memory for local assemblers.
     std::vector<unsigned> const _dofIndex_to_localIndex;
 };
 }  // namespace HeatTransportBHE
diff --git a/ProcessLib/HeatTransportBHE/LocalAssemblers/IntegrationPointDataSoil.h b/ProcessLib/HeatTransportBHE/LocalAssemblers/IntegrationPointDataSoil.h
index 97abc53d0dc8d7a9091c545c44147f8aaa9db837..04e5c338af97f8b019a4369dd1ca19e54b2213d5 100644
--- a/ProcessLib/HeatTransportBHE/LocalAssemblers/IntegrationPointDataSoil.h
+++ b/ProcessLib/HeatTransportBHE/LocalAssemblers/IntegrationPointDataSoil.h
@@ -12,22 +12,15 @@
 #include <memory>
 #include <vector>
 
-#include "MaterialLib/SolidModels/MechanicsBase.h"
-
 namespace ProcessLib
 {
 namespace HeatTransportBHE
 {
-template <typename ShapeMatrixType>
+template <typename NodalMatrixType>
 struct IntegrationPointDataSoil final
 {
-    explicit IntegrationPointDataSoil() {}
-
-    typename ShapeMatrixType::NodalRowVectorType N;
-    typename ShapeMatrixType::GlobalDimNodalMatrixType dNdx;
-    double integration_weight;
-
-    void pushBackState() {}
+    NodalMatrixType NTN_product_times_w;
+    NodalMatrixType dNdxTdNdx_product_times_w;
 
     EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
 };
diff --git a/ProcessLib/HeatTransportBHE/LocalAssemblers/LocalDataInitializer.h b/ProcessLib/HeatTransportBHE/LocalAssemblers/LocalDataInitializer.h
index f395d3551890211b73d693412b5a2fb014f50709..ac08d79591b912dbfd474cb4ece29860ecd57276 100644
--- a/ProcessLib/HeatTransportBHE/LocalAssemblers/LocalDataInitializer.h
+++ b/ProcessLib/HeatTransportBHE/LocalAssemblers/LocalDataInitializer.h
@@ -253,17 +253,17 @@ public:
                 type_idx.name());
         }
 
-        auto varIDs = _dof_table.getElementVariableIDs(id);
-        auto n_local_dof = _dof_table.getNumberOfElementDOF(id);
         std::vector<unsigned> dofIndex_to_localIndex;
 
-        if (mesh_item.getDimension() < GlobalDim)
+        // Create customed dof table when it is a BHE element.
+        if (mesh_item.getDimension() == 1)
         {
             // this is a BHE element
+            auto const n_local_dof = _dof_table.getNumberOfElementDOF(id);
             dofIndex_to_localIndex.resize(n_local_dof);
             unsigned dof_id = 0;
             unsigned local_id = 0;
-            for (auto i : varIDs)
+            for (auto const i : _dof_table.getElementVariableIDs(id))
             {
                 auto const n_global_components =
                     _dof_table.getNumberOfElementComponents(i);
@@ -276,7 +276,8 @@ public:
                         MeshLib::Location l(mesh_id,
                                             MeshLib::MeshItemType::Node,
                                             mesh_item.getNodeIndex(k));
-                        auto global_index = _dof_table.getGlobalIndex(l, i, j);
+                        auto const global_index =
+                            _dof_table.getGlobalIndex(l, i, j);
                         if (global_index != NumLib::MeshComponentMap::nop)
                         {
                             dofIndex_to_localIndex[dof_id++] = local_id;
@@ -287,16 +288,13 @@ public:
             }
         }
 
-        data_ptr = it->second(mesh_item, varIDs.size(), n_local_dof,
-                              dofIndex_to_localIndex,
+        data_ptr = it->second(mesh_item, dofIndex_to_localIndex,
                               std::forward<ConstructorArgs>(args)...);
     }
 
 private:
     using LADataBuilder = std::function<LADataIntfPtr(
         MeshLib::Element const& e,
-        std::size_t const n_variables,
-        std::size_t const local_matrix_size,
         std::vector<unsigned> const& dofIndex_to_localIndex,
         ConstructorArgs&&...)>;
 
@@ -340,20 +338,43 @@ private:
     static LADataBuilder makeLocalAssemblerBuilder(std::true_type*)
     {
         return [](MeshLib::Element const& e,
-                  std::size_t const /*n_variables*/,
-                  std::size_t const local_matrix_size,
                   std::vector<unsigned> const& dofIndex_to_localIndex,
-                  ConstructorArgs&&... args) {
+                  ConstructorArgs&&... args) -> LADataIntfPtr {
             if (e.getDimension() == GlobalDim)  // soil elements
             {
                 return LADataIntfPtr{new LADataSoil<ShapeFunction>{
-                    e, local_matrix_size, dofIndex_to_localIndex,
+                    e, dofIndex_to_localIndex,
                     std::forward<ConstructorArgs>(args)...}};
             }
 
-            return LADataIntfPtr{new LADataBHE<ShapeFunction>{
+            return nullptr;
+        };
+    }
+
+    template <>
+    static LADataBuilder makeLocalAssemblerBuilder<NumLib::ShapeLine2>(
+        std::true_type*)
+    {
+        return [](MeshLib::Element const& e,
+                  std::vector<unsigned> const& dofIndex_to_localIndex,
+                  ConstructorArgs&&... args) -> LADataIntfPtr {
+            return LADataIntfPtr{new LADataBHE<NumLib::ShapeLine2>{
+                // BHE elements
+                e, dofIndex_to_localIndex,
+                std::forward<ConstructorArgs>(args)...}};
+        };
+    }
+
+    template <>
+    static LADataBuilder makeLocalAssemblerBuilder<NumLib::ShapeLine3>(
+        std::true_type*)
+    {
+        return [](MeshLib::Element const& e,
+                  std::vector<unsigned> const& dofIndex_to_localIndex,
+                  ConstructorArgs&&... args) -> LADataIntfPtr {
+            return LADataIntfPtr{new LADataBHE<NumLib::ShapeLine3>{
                 // BHE elements
-                e, local_matrix_size, dofIndex_to_localIndex,
+                e, dofIndex_to_localIndex,
                 std::forward<ConstructorArgs>(args)...}};
         };
     }