diff --git a/Applications/CLI/ogs_embedded_python.cpp b/Applications/CLI/ogs_embedded_python.cpp
index 03289d566a56bcc220412ff5fbd7aa4018f347c8..2bb020fa9083af2259c61dba817247d3bafad8a4 100644
--- a/Applications/CLI/ogs_embedded_python.cpp
+++ b/Applications/CLI/ogs_embedded_python.cpp
@@ -7,11 +7,13 @@
  *
  */
 
+#include "ogs_embedded_python.h"
+
 #include <pybind11/embed.h>
-#include <logog/include/logog.hpp>
 
-#include "ogs_embedded_python.h"
+#include <logog/include/logog.hpp>
 
+#include "ProcessLib/BoundaryCondition/Python/BHEInflowPythonBoundaryConditionModule.h"
 #include "ProcessLib/BoundaryCondition/Python/PythonBoundaryConditionModule.h"
 #include "ProcessLib/SourceTerms/Python/PythonSourceTermModule.h"
 
@@ -20,6 +22,7 @@ PYBIND11_EMBEDDED_MODULE(OpenGeoSys, m)
     DBUG("Binding Python module OpenGeoSys.");
 
     ProcessLib::pythonBindBoundaryCondition(m);
+    ProcessLib::bheInflowpythonBindBoundaryCondition(m);
     ProcessLib::SourceTerms::Python::pythonBindSourceTerm(m);
 }
 
diff --git a/ProcessLib/BoundaryCondition/Python/BHEInflowPythonBoundaryCondition.h b/ProcessLib/BoundaryCondition/Python/BHEInflowPythonBoundaryCondition.h
new file mode 100644
index 0000000000000000000000000000000000000000..074e49907f1f90368e606999bd3e3a5e39235315
--- /dev/null
+++ b/ProcessLib/BoundaryCondition/Python/BHEInflowPythonBoundaryCondition.h
@@ -0,0 +1,124 @@
+/**
+ * \copyright
+ * Copyright (c) 2012-2019, 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
+#ifdef OGS_USE_PYTHON
+#include <pybind11/pybind11.h>
+#endif  // OGS_USE_PYTHON
+#include <algorithm>
+#include <iostream>
+#include <logog/include/logog.hpp>
+#include <vector>
+
+#include "BHEInflowPythonBoundaryConditionPythonSideInterface.h"
+#include "BaseLib/Error.h"
+#include "NumLib/IndexValueVector.h"
+#include "ProcessLib/BoundaryCondition/BoundaryCondition.h"
+#include "ProcessLib/BoundaryCondition/GenericNaturalBoundaryConditionLocalAssembler.h"
+#include "ProcessLib/HeatTransportBHE/BHE/BHETypes.h"
+#include "ProcessLib/Utils/CreateLocalAssemblers.h"
+#include "ProcessLib/Utils/ProcessUtils.h"
+#include "PythonBoundaryConditionLocalAssembler.h"
+
+namespace ProcessLib
+{
+//! A boundary condition whose values are computed by a Python script.
+template <typename BHEType>
+class BHEInflowPythonBoundaryCondition final : public BoundaryCondition
+{
+public:
+    BHEInflowPythonBoundaryCondition(
+        std::pair<GlobalIndexType, GlobalIndexType>&& in_out_global_indices,
+        BHEType& bhe,
+        BHEInflowPythonBoundaryConditionPythonSideInterface& py_bc_object)
+        : _in_out_global_indices(std::move(in_out_global_indices)),
+          _bhe(bhe),
+          _py_bc_object(py_bc_object)
+    {
+        const auto g_idx_T_out = in_out_global_indices.second;
+
+        // store the bc node ids to BHE network dataframe
+        std::get<4>(_py_bc_object.dataframe_network).emplace_back(g_idx_T_out);
+    }
+
+    void getEssentialBCValues(
+        const double t, const GlobalVector& x,
+        NumLib::IndexValueVector<GlobalIndexType>& bc_values) const override
+    {
+        bc_values.ids.resize(1);
+        bc_values.values.resize(1);
+        auto const& data_exchange = _py_bc_object.dataframe_network;
+        // get the number of all boundary nodes
+        const std::size_t n_bc_nodes = std::get<4>(data_exchange).size();
+
+        // get T_in bc_id
+        bc_values.ids[0] = _in_out_global_indices.first;
+
+        // get T_out bc_id
+        auto const boundary_node_id = _in_out_global_indices.second;
+
+        // return T_in from currently BHE dataframe column 2,
+        // update flowrate and HeatTransferCoefficients for each BHE
+        for (std::size_t i = 0; i < n_bc_nodes; i++)
+        {
+            // auto pair_flag_value =
+            // _bc_data.bc_object->getDirichletBCValue(boundary_node_id);
+            auto const dataframe_node_id = std::get<4>(data_exchange);
+            auto const dataframe_Tin_val = std::get<2>(data_exchange);
+            auto const dataframe_BHE_flowrate = std::get<5>(data_exchange);
+            if (dataframe_node_id[i] == boundary_node_id)
+            {
+                bc_values.values[0] = dataframe_Tin_val[i];
+                _bhe.updateHeatTransferCoefficients(dataframe_BHE_flowrate[i]);
+                break;
+            }
+        }
+
+        // store the current time to network dataframe
+        std::get<1>(_py_bc_object.dataframe_network) = t;
+    }
+
+private:
+    std::pair<GlobalIndexType, GlobalIndexType> const _in_out_global_indices;
+    BHEType& _bhe;
+    BHEInflowPythonBoundaryConditionPythonSideInterface& _py_bc_object;
+};
+
+template <typename BHEType>
+std::unique_ptr<BHEInflowPythonBoundaryCondition<BHEType>>
+createBHEInflowPythonBoundaryCondition(
+    std::pair<GlobalIndexType, GlobalIndexType>&& in_out_global_indices,
+    BHEType& bhe,
+    BHEInflowPythonBoundaryConditionPythonSideInterface& py_bc_object)
+
+{
+    DBUG("Constructing BHEInflowPythonBoundaryCondition.");
+
+    // In case of partitioned mesh the boundary could be empty, i.e. there is no
+    // boundary condition.
+#ifdef USE_PETSC
+    // For this special boundary condition the boundary condition is not empty
+    // if the global indices are non-negative.
+    if (in_out_global_indices.first < 0 && in_out_global_indices.second < 0)
+    {
+        return nullptr;
+    }
+    // If only one of the global indices (in or out) is negative the
+    // implementation is not valid.
+    if (in_out_global_indices.first < 0 || in_out_global_indices.second < 0)
+    {
+        OGS_FATAL(
+            "The partition cuts the BHE into two independent parts. This "
+            "behaviour is not implemented.");
+    }
+#endif  // USE_PETSC
+    return std::make_unique<BHEInflowPythonBoundaryCondition<BHEType>>(
+        std::move(in_out_global_indices), bhe, py_bc_object);
+}
+}  // namespace ProcessLib
diff --git a/ProcessLib/BoundaryCondition/Python/BHEInflowPythonBoundaryConditionModule.cpp b/ProcessLib/BoundaryCondition/Python/BHEInflowPythonBoundaryConditionModule.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..08de918d1b2db431d740b93e5ac257472f5825fb
--- /dev/null
+++ b/ProcessLib/BoundaryCondition/Python/BHEInflowPythonBoundaryConditionModule.cpp
@@ -0,0 +1,82 @@
+/**
+ * \copyright
+ * Copyright (c) 2012-2019, OpenGeoSys Community (http://www.opengeosys.org)
+ *            Distributed under a Modified BSD License.
+ *              See accompanying file LICENSE.txt or
+ *              http://www.opengeosys.org/project/license
+ *
+ */
+
+#include "BHEInflowPythonBoundaryConditionModule.h"
+
+#include <pybind11/stl.h>
+
+#include "BHEInflowPythonBoundaryConditionPythonSideInterface.h"
+
+namespace ProcessLib
+{
+//! Trampoline class allowing methods of class
+//! PythonBoundaryConditionPythonSideInterface to be overridden on the Python
+//! side. Cf. https://pybind11.readthedocs.io/en/stable/advanced/classes.html
+class BHEInflowPythonBoundaryConditionPythonSideInterfaceTrampoline
+    : public BHEInflowPythonBoundaryConditionPythonSideInterface
+{
+public:
+    using BHEInflowPythonBoundaryConditionPythonSideInterface::
+        BHEInflowPythonBoundaryConditionPythonSideInterface;
+
+    std::tuple<bool, double, std::vector<double>, std::vector<double>,
+               std::vector<int>, std::vector<double>>
+    initializeDataContainer() const override
+    {
+        using Ret =
+            std::tuple<bool, double, std::vector<double>, std::vector<double>,
+                       std::vector<int>, std::vector<double>>;
+        PYBIND11_OVERLOAD(Ret,
+                          BHEInflowPythonBoundaryConditionPythonSideInterface,
+                          initializeDataContainer);
+    }
+
+    std::tuple<bool, bool, std::vector<double>> tespyThermalSolver(
+        double t,
+        std::vector<double> const& Tin_val,
+        std::vector<double> const& Tout_val) const override
+    {
+        using Ret = std::tuple<bool, bool, std::vector<double>>;
+        PYBIND11_OVERLOAD(Ret,
+                          BHEInflowPythonBoundaryConditionPythonSideInterface,
+                          tespyThermalSolver, t, Tin_val, Tout_val);
+    }
+
+    std::tuple<bool, std::vector<double>> tespyHydroSolver(
+        double t) const override
+    {
+        using Ret = std::tuple<bool, std::vector<double>>;
+        PYBIND11_OVERLOAD(Ret,
+                          BHEInflowPythonBoundaryConditionPythonSideInterface,
+                          tespyHydroSolver, t);
+    }
+};
+
+void bheInflowpythonBindBoundaryCondition(pybind11::module& m)
+{
+    namespace py = pybind11;
+
+    py::class_<BHEInflowPythonBoundaryConditionPythonSideInterface,
+               BHEInflowPythonBoundaryConditionPythonSideInterfaceTrampoline>
+        pybc(m, "BHENetwork");
+
+    pybc.def(py::init());
+
+    pybc.def("initializeDataContainer",
+             &BHEInflowPythonBoundaryConditionPythonSideInterface::
+                 initializeDataContainer);
+    pybc.def("tespyThermalSolver",
+             &BHEInflowPythonBoundaryConditionPythonSideInterface::
+                 tespyThermalSolver);
+    pybc.def(
+        "tespyHydroSolver",
+        &BHEInflowPythonBoundaryConditionPythonSideInterface::tespyHydroSolver);
+}
+
+}  // namespace ProcessLib
diff --git a/ProcessLib/BoundaryCondition/Python/BHEInflowPythonBoundaryConditionModule.h b/ProcessLib/BoundaryCondition/Python/BHEInflowPythonBoundaryConditionModule.h
new file mode 100644
index 0000000000000000000000000000000000000000..d91fa1304272541e2a84957e7fb3b953f94eb5e1
--- /dev/null
+++ b/ProcessLib/BoundaryCondition/Python/BHEInflowPythonBoundaryConditionModule.h
@@ -0,0 +1,21 @@
+/**
+ * \copyright
+ * Copyright (c) 2012-2019, 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
+
+#include <pybind11/pybind11.h>
+
+#include "BaseLib/ExportSymbol.h"
+
+namespace ProcessLib
+{
+//! Creates BHE Inflow Python bindings for the Python BC class.
+OGS_EXPORT_SYMBOL void bheInflowpythonBindBoundaryCondition(
+    pybind11::module& m);
+}  // namespace ProcessLib
diff --git a/ProcessLib/BoundaryCondition/Python/BHEInflowPythonBoundaryConditionPythonSideInterface.h b/ProcessLib/BoundaryCondition/Python/BHEInflowPythonBoundaryConditionPythonSideInterface.h
new file mode 100644
index 0000000000000000000000000000000000000000..a45a7532af166acea1b1b38c332a4ec3506e3e57
--- /dev/null
+++ b/ProcessLib/BoundaryCondition/Python/BHEInflowPythonBoundaryConditionPythonSideInterface.h
@@ -0,0 +1,106 @@
+/**
+ * \copyright
+ * Copyright (c) 2012-2019, 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
+{
+//! Base class for BHENetwork.
+//! This class will get Python bindings and is intended to be to be derived in
+//! Python.
+class BHEInflowPythonBoundaryConditionPythonSideInterface
+{
+public:
+    /*!
+     * Initialize network dataframe
+     * return a tuple (time, BHE inflow temperature, BHE outflow
+     * temperature, BHE outflow bc node id, BHE flowrate)
+     * set at that position and the parameters of the BHE network.
+     */
+    virtual std::tuple<bool,
+                       double /*time*/,
+                       std::vector<double> /*Tin_val*/,
+                       std::vector<double> /*Tout_val*/,
+                       std::vector<int> /*bc_out_ids*/,
+                       std::vector<double> /*BHE_flowrate*/>
+    initializeDataContainer() const
+    {
+        _overridden_essential = false;
+        return std::tuple<bool,
+                          double,
+                          std::vector<double>,
+                          std::vector<double>,
+                          std::vector<int>,
+                          std::vector<double>>{
+            false, std::numeric_limits<double>::quiet_NaN(), {}, {}, {}, {}};
+    }
+
+    /*!
+     * transfer BHE network dataframe to TESPy and get Tin from TESPy
+     *
+     * \return a tuple (time, BHE Tin and Tout value from TESPy)
+     * indicating if a natural BC shall be set at that position and the new
+     * inflow temperature of all BHEs
+     */
+    virtual std::tuple<bool, bool, std::vector<double>> tespyThermalSolver(
+        double /*t*/,
+        std::vector<double> const& /*Tin_val*/,
+        std::vector<double> const& /*Tout_val*/) const
+    {
+        _overridden_natural = false;
+        return std::tuple<bool, bool, std::vector<double>>{false, false, {}};
+    }
+
+    /*!
+     * call Tespy hydraulic solver to get flow velocity in each pipe
+     *
+     * \return a tuple (is_natural,  f_velocity ) indicating if a
+     * natural BC shall be set at that position and the flow velocity in each
+     * pipe of all BHEs
+     */
+    virtual std::tuple<bool, std::vector<double>> tespyHydroSolver(
+        double /*t*/) const
+    {
+        _overridden_natural = false;
+        return std::tuple<bool, std::vector<double>>{false, {}};
+    }
+
+    //! Tells if initializeDataContainer() has been overridden in the derived
+    //! class in Python.
+    //!
+    //! \pre initializeDataContainer() must already have been called
+    //! once.
+    bool isOverriddenEssential() const { return _overridden_essential; }
+
+    //! Tells if tespySolver() has been overridden in the derived class in
+    //! Python.
+    //!
+    //! \pre tespySolver() must already have been called once.
+    bool isOverriddenNatural() const { return _overridden_natural; }
+
+    // BHE network dataframe container
+    std::tuple<bool,
+               double,
+               std::vector<double>,
+               std::vector<double>,
+               std::vector<int>,
+               std::vector<double>>
+        dataframe_network;
+
+    virtual ~BHEInflowPythonBoundaryConditionPythonSideInterface() = default;
+
+private:
+    //! Tells if initializeDataContainer() has been overridden in the derived
+    //! class in Python.
+    mutable bool _overridden_essential = true;
+    //! Tells if tespySolver() has been overridden in the derived class in
+    //! Python.
+    mutable bool _overridden_natural = true;
+};
+}  // namespace ProcessLib
diff --git a/ProcessLib/BoundaryCondition/Python/CMakeLists.txt b/ProcessLib/BoundaryCondition/Python/CMakeLists.txt
index e8b743be32072db61e6d0a20b830da1c0dbabd0c..c576bc52fc6ebdc924ad3c1dd512dca47ac41b7f 100644
--- a/ProcessLib/BoundaryCondition/Python/CMakeLists.txt
+++ b/ProcessLib/BoundaryCondition/Python/CMakeLists.txt
@@ -1,7 +1,9 @@
 add_library(ProcessLibBoundaryConditionPython
             PythonBoundaryCondition.cpp PythonBoundaryCondition.h
             PythonBoundaryConditionLocalAssembler.h
-            PythonBoundaryConditionPythonSideInterface.h)
+            PythonBoundaryConditionPythonSideInterface.h
+            BHEInflowPythonBoundaryCondition.h
+            BHEInflowPythonBoundaryConditionPythonSideInterface.h)
 if(BUILD_SHARED_LIBS)
     install(TARGETS ProcessLibBoundaryConditionPython
             LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
@@ -21,7 +23,8 @@ target_link_libraries(ProcessLibBoundaryConditionPython
 
 # For the embedded Python module
 add_library(ProcessLibBoundaryConditionPythonModule
-            PythonBoundaryConditionModule.cpp PythonBoundaryConditionModule.h)
+            PythonBoundaryConditionModule.cpp PythonBoundaryConditionModule.h
+            BHEInflowPythonBoundaryConditionModule.cpp BHEInflowPythonBoundaryConditionModule.h)
 if(BUILD_SHARED_LIBS)
     install(TARGETS ProcessLibBoundaryConditionPythonModule
             LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
diff --git a/ProcessLib/HeatTransportBHE/CMakeLists.txt b/ProcessLib/HeatTransportBHE/CMakeLists.txt
index daa5a552549346e2036b54607a48f15971d122aa..8e8fd364eb69ab35920a6b560e99dfba47e38cba 100644
--- a/ProcessLib/HeatTransportBHE/CMakeLists.txt
+++ b/ProcessLib/HeatTransportBHE/CMakeLists.txt
@@ -4,6 +4,13 @@ append_source_files(SOURCES BoundaryConditions)
 append_source_files(SOURCES LocalAssemblers)
 
 add_library(HeatTransportBHE ${SOURCES})
+
+if(OGS_USE_PYTHON)
+    target_link_libraries(HeatTransportBHE PUBLIC ProcessLib PRIVATE pybind11::pybind11)
+else()
+    target_link_libraries(HeatTransportBHE PUBLIC ProcessLib)
+endif()
+
 if(BUILD_SHARED_LIBS)
     install(TARGETS HeatTransportBHE
             LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})