diff --git a/ProcessLib/BoundaryCondition/ConstraintDirichletBoundaryCondition.cpp b/ProcessLib/BoundaryCondition/ConstraintDirichletBoundaryCondition.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ec717c6c710c26651a4d1f3f08d610c551c7c50b
--- /dev/null
+++ b/ProcessLib/BoundaryCondition/ConstraintDirichletBoundaryCondition.cpp
@@ -0,0 +1,319 @@
+/**
+ * \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
+ *
+ */
+
+#include "ConstraintDirichletBoundaryCondition.h"
+
+#include <algorithm>
+#include <vector>
+#include <logog/include/logog.hpp>
+
+#include "MeshLib/Node.h"
+#include "MeshLib/MeshSearch/NodeSearch.h"  // for getUniqueNodes
+#include "ProcessLib/Utils/CreateLocalAssemblers.h"
+#include "ProcessLib/Utils/ProcessUtils.h"
+
+namespace ProcessLib
+{
+ConstraintDirichletBoundaryCondition::ConstraintDirichletBoundaryCondition(
+    Parameter<double> const& parameter,
+    NumLib::LocalToGlobalIndexMap const& dof_table_bulk, int const variable_id,
+    int const component_id, MeshLib::Mesh const& bc_mesh,
+    unsigned const integration_order, MeshLib::Mesh const& bulk_mesh,
+    double const constraint_threshold, bool const lower,
+    std::function<Eigen::Vector3d(std::size_t const, MathLib::Point3d const&,
+                                  double const, GlobalVector const&)>
+        getFlux)
+    : _parameter(parameter),
+      _variable_id(variable_id),
+      _component_id(component_id),
+      _bc_mesh(bc_mesh),
+      _integration_order(integration_order),
+      _constraint_threshold(constraint_threshold),
+      _lower(lower),
+      _bulk_mesh(bulk_mesh),
+      _getFlux(getFlux)
+{
+    if (variable_id >=
+            static_cast<int>(dof_table_bulk.getNumberOfVariables()) ||
+        component_id >=
+            dof_table_bulk.getNumberOfVariableComponents(variable_id))
+    {
+        OGS_FATAL(
+            "Variable id or component id too high. Actual values: (%d, "
+            "%d), maximum values: (%d, %d).",
+            variable_id, component_id, dof_table_bulk.getNumberOfVariables(),
+            dof_table_bulk.getNumberOfVariableComponents(variable_id));
+    }
+
+    std::vector<MeshLib::Node*> const& bc_nodes = _bc_mesh.getNodes();
+    DBUG(
+        "Found %d nodes for constraint Dirichlet BCs for the variable %d and "
+        "component %d",
+        bc_nodes.size(), variable_id, component_id);
+
+    MeshLib::MeshSubset bc_mesh_subset{_bc_mesh, bc_nodes};
+
+    // Create local DOF table from intersected mesh subsets for the given
+    // variable and component ids.
+    _dof_table_boundary.reset(dof_table_bulk.deriveBoundaryConstrainedMap(
+        variable_id, {component_id}, std::move(bc_mesh_subset)));
+
+    auto const& bc_elements(_bc_mesh.getElements());
+    _local_assemblers.resize(bc_elements.size());
+    _flux_values.resize(bc_elements.size());
+    // create _bulk_ids vector
+    auto const* bulk_element_ids =
+        _bc_mesh.getProperties().getPropertyVector<std::size_t>(
+            "bulk_element_ids");
+    if (!bulk_element_ids)
+    {
+        OGS_FATAL(
+            "The boundary mesh '%s' doesn't contain the needed property "
+            "'bulk_element_ids'.",
+            _bc_mesh.getName().c_str());
+    }
+    auto const* bulk_node_ids =
+        _bc_mesh.getProperties().getPropertyVector<std::size_t>(
+            "bulk_node_ids");
+    if (!bulk_node_ids)
+    {
+        OGS_FATAL(
+            "The boundary mesh '%s' doesn't contain the needed property "
+            "'bulk_node_ids'.",
+            _bc_mesh.getName().c_str());
+    }
+    auto const& bulk_nodes = bulk_mesh.getNodes();
+
+    auto get_bulk_element_face_id =
+        [&](auto const bulk_element_id, MeshLib::Element const* bc_elem) {
+            auto const* bulk_elem = _bulk_mesh.getElement(bulk_element_id);
+            std::array<MeshLib::Node*, 3> nodes{
+                {bulk_nodes[(*bulk_node_ids)[bc_elem->getNode(0)->getID()]],
+                 bulk_nodes[(*bulk_node_ids)[bc_elem->getNode(1)->getID()]],
+                 bulk_nodes[(*bulk_node_ids)[bc_elem->getNode(2)->getID()]]}};
+            return bulk_elem->identifyFace(nodes.data());
+        };
+
+    _bulk_ids.reserve(bc_elements.size());
+    std::transform(begin(bc_elements), end(bc_elements),
+                   std::back_inserter(_bulk_ids), [&](auto const* bc_element) {
+                       auto const bulk_element_id =
+                           (*bulk_element_ids)[bc_element->getID()];
+                       return std::make_pair(bulk_element_id,
+                                             get_bulk_element_face_id(
+                                                 bulk_element_id, bc_element));
+                   });
+
+    const int shape_function_order = 1;
+
+    ProcessLib::createLocalAssemblers<
+        ConstraintDirichletBoundaryConditionLocalAssembler>(
+        _bulk_mesh.getDimension(), _bc_mesh.getElements(), *_dof_table_boundary,
+        shape_function_order, _local_assemblers, _bc_mesh.isAxiallySymmetric(),
+        _integration_order, _bulk_ids);
+}
+
+void ConstraintDirichletBoundaryCondition::preTimestep(double t,
+                                                       GlobalVector const& x)
+{
+    DBUG(
+        "ConstraintDirichletBoundaryCondition::preTimestep: computing flux "
+        "constraints");
+    for (auto const* boundary_element : _bc_mesh.getElements())
+    {
+        _flux_values[boundary_element->getID()] =
+            _local_assemblers[boundary_element->getID()]->integrate(
+                x, t, _bulk_mesh,
+                [this](std::size_t const element_id,
+                       MathLib::Point3d const& pnt, double const t,
+                       GlobalVector const& x) {
+                    return _getFlux(element_id, pnt, t, x);
+                });
+    }
+}
+
+void ConstraintDirichletBoundaryCondition::getEssentialBCValues(
+    const double t, NumLib::IndexValueVector<GlobalIndexType>& bc_values) const
+{
+    SpatialPosition pos;
+
+    bc_values.ids.clear();
+    bc_values.values.clear();
+
+    std::vector<std::pair<GlobalIndexType, double>> tmp_bc_values;
+
+    auto isFlux = [&](const std::size_t element_id) {
+        return _lower ? _flux_values[element_id] < _constraint_threshold
+                      : _flux_values[element_id] > _constraint_threshold;
+    };
+
+    for (auto const* boundary_element : _bc_mesh.getElements())
+    {
+        // check if the boundary element is active
+        if (isFlux(boundary_element->getID()))
+        {
+            continue;
+        }
+
+        // loop over the boundary element nodes and set the dirichlet value
+        unsigned const number_nodes = boundary_element->getNumberOfNodes();
+        for (unsigned i = 0; i < number_nodes; ++i)
+        {
+            auto const id = boundary_element->getNode(i)->getID();
+            pos.setNodeID(id);
+
+            MeshLib::Location l(_bulk_mesh.getID(), MeshLib::MeshItemType::Node,
+                                id);
+            // TODO: that might be slow, but only done once
+            const auto g_idx = _dof_table_boundary->getGlobalIndex(
+                l, _variable_id, _component_id);
+            if (g_idx == NumLib::MeshComponentMap::nop)
+                continue;
+            // For the DDC approach (e.g. with PETSc option), the negative
+            // index of g_idx means that the entry by that index is a ghost one,
+            // which should be dropped. Especially for PETSc routines
+            // MatZeroRows and MatZeroRowsColumns, which are called to apply the
+            // Dirichlet BC, the negative index is not accepted like other
+            // matrix or vector PETSc routines. Therefore, the following
+            // if-condition is applied.
+            if (g_idx >= 0)
+            {
+                tmp_bc_values.emplace_back(g_idx, _parameter(t,pos).front());
+            }
+        }
+    }
+
+    if (tmp_bc_values.empty())
+    {
+        DBUG("The domain on which the boundary is defined is empty");
+        return;
+    }
+
+    // store unique pairs of node id and value with respect to the node id in
+    // the bc_values vector. The values related to the same node id are
+    // averaged.
+    // first: sort the (node id, value) pairs according to the node id
+    std::sort(tmp_bc_values.begin(), tmp_bc_values.end(),
+              [](std::pair<GlobalIndexType, double> const& a,
+                 std::pair<GlobalIndexType, double> const& b) {
+                  return a.first < b.first;
+              });
+    // second: average the values over equal node id ranges
+    unsigned cnt = 1;
+    GlobalIndexType current_id = tmp_bc_values.begin()->first;
+    double sum = tmp_bc_values.begin()->second;
+    for (auto const& tmp_bc_value : tmp_bc_values)
+    {
+        if (tmp_bc_value.first == current_id)
+        {
+            cnt++;
+            sum += tmp_bc_value.second;
+        }
+        else
+        {
+            bc_values.ids.emplace_back(current_id);
+            bc_values.values.emplace_back(sum/cnt);
+            cnt = 1;
+            sum = tmp_bc_value.second;
+            current_id = tmp_bc_value.first;
+        }
+    }
+    bc_values.ids.emplace_back(current_id);
+    bc_values.values.emplace_back(sum / cnt);
+
+    DBUG("Found %d dirichlet values.", bc_values.ids.size());
+    for (unsigned i = 0; i < bc_values.ids.size(); ++i)
+    {
+        DBUG("\tid: %d, value: %e", bc_values.ids[i], bc_values.values[i]);
+    }
+}
+
+std::unique_ptr<ConstraintDirichletBoundaryCondition>
+createConstraintDirichletBoundaryCondition(
+    BaseLib::ConfigTree const& config, MeshLib::Mesh const& bc_mesh,
+    NumLib::LocalToGlobalIndexMap const& dof_table_bulk, int const variable_id,
+    unsigned const integration_order, int const component_id,
+    std::vector<std::unique_ptr<ProcessLib::ParameterBase>> const& parameters,
+    Process const& constraining_process)
+{
+    DBUG("Constructing ConstraintDirichletBoundaryCondition from config.");
+    //! \ogs_file_param{prj__process_variables__process_variable__boundary_conditions__boundary_condition__type}
+    config.checkConfigParameter("type", "ConstraintDirichlet");
+
+    auto const constraint_type =
+        config.getConfigParameter<std::string>("constraint_type");
+    if (constraint_type != "Flux")
+    {
+        OGS_FATAL("The constraint type is '%s', but has to be 'Flux'.",
+                  constraint_type.c_str());
+    }
+
+    auto const constraining_process_variable =
+        config.getConfigParameter<std::string>("constraining_process_variable");
+
+    if (!constraining_process.isMonolithicSchemeUsed())
+    {
+        OGS_FATAL(
+            "The constraint dirichlet boundary condition is implemented only "
+            "for monolithic implemented processes.");
+    }
+    const int process_id = 0;
+    auto process_variables =
+        constraining_process.getProcessVariables(process_id);
+    auto constraining_pv =
+        std::find_if(process_variables.cbegin(), process_variables.cend(),
+                     [&constraining_process_variable](ProcessVariable const& pv) {
+                         return pv.getName() == constraining_process_variable;
+                     });
+    if (constraining_pv == std::end(process_variables))
+    {
+        auto const& constraining_process_variable_name =
+            process_variables[variable_id].get().getName();
+        OGS_FATAL(
+            "<constraining_process_variable> in process variable name '%s' at "
+            "geometry 'TODO' : The constraining process variable is set as "
+            "'%s', but this is not specified in the project file.",
+            constraining_process_variable_name.c_str(),
+            constraining_process_variable.c_str());
+    }
+
+    auto const constraint_threshold =
+        config.getConfigParameter<double>("constraint_threshold");
+
+    auto const constraint_direction_string =
+        config.getConfigParameter<std::string>("constraint_direction");
+    if (constraint_direction_string != "greater" &&
+        constraint_direction_string != "lower")
+    {
+        OGS_FATAL(
+            "The constraint direction is '%s', but has to be either 'greater' "
+            "or 'lower'.",
+            constraint_direction_string.c_str());
+    }
+    bool const lower = constraint_direction_string == "lower";
+
+
+    //! \ogs_file_param{prj__process_variables__process_variable__boundary_conditions__boundary_condition__parameter}
+    auto const param_name = config.getConfigParameter<std::string>("parameter");
+    DBUG("Using parameter %s", param_name.c_str());
+
+    auto& param = findParameter<double>(param_name, parameters, 1);
+
+    return std::make_unique<ConstraintDirichletBoundaryCondition>(
+        param, dof_table_bulk, variable_id, component_id, bc_mesh,
+        integration_order, constraining_process.getMesh(), constraint_threshold,
+        lower,
+        [&constraining_process](std::size_t const element_id,
+                                MathLib::Point3d const& pnt, double const t,
+                                GlobalVector const& x) {
+            return constraining_process.getFlux(element_id, pnt, t, x);
+        });
+}
+
+}  // namespace ProcessLib
diff --git a/ProcessLib/BoundaryCondition/ConstraintDirichletBoundaryCondition.h b/ProcessLib/BoundaryCondition/ConstraintDirichletBoundaryCondition.h
new file mode 100644
index 0000000000000000000000000000000000000000..253abfceb7fddd23c46f41ad64de4d8e63e7b1f8
--- /dev/null
+++ b/ProcessLib/BoundaryCondition/ConstraintDirichletBoundaryCondition.h
@@ -0,0 +1,128 @@
+/**
+ * \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
+
+#include "NumLib/DOF/LocalToGlobalIndexMap.h"
+#include "NumLib/IndexValueVector.h"
+#include "ProcessLib/Parameter/Parameter.h"
+#include "BoundaryCondition.h"
+
+#include "ConstraintDirichletBoundaryConditionLocalAssembler.h"
+
+namespace ProcessLib
+{
+/// The ConstraintDirichletBoundaryCondition class describes a Dirichlet-type
+/// boundary condition that is constant in space and time where the domain can
+/// shrink and grow within the simulation. The expected parameter in the passed
+/// configuration is "value" which, when not present defaults to zero.
+class ConstraintDirichletBoundaryCondition final : public BoundaryCondition
+{
+public:
+    /// @param parameter Used for setting the values for the boundary condition.
+    /// @param dof_table_bulk The bulk local to global index map is used to
+    /// derive the local to global index map for the boundary.
+    /// @param variable_id The variable id is needed to determine the global
+    /// index.
+    /// @param component_id The component id is needed to determine the global
+    /// index.
+    /// @param bc_mesh Lower dimensional mesh the boundary condition is defined
+    /// on. The bc_mesh must have the two PropertyVector objects
+    /// 'bulk_element_ids' and 'bulk_node_ids' containing the corresponding
+    /// information.
+    /// @param integration_order Order the order of integration used to compute
+    /// the constraint.
+    /// @param bulk_mesh The FE mesh for the simulation.
+    /// @param constraint_threshold The threshold value used for the switch
+    /// off/on decision.
+    /// @param lower Boolean value used for the calculation of the constraint
+    /// criterion, i.e., if lower is set to true the criterion 'calculated_value
+    /// < constraint_threshold' is evaluated to switch on/off the boundary
+    /// condition, else 'calculated_value > constraint_threshold' is evaluated.
+    /// @param getFlux The function used for the flux calculation.
+    /// @note The function has to be stored by value, else the process value is
+    /// not captured properly.
+    ConstraintDirichletBoundaryCondition(
+        Parameter<double> const& parameter,
+        NumLib::LocalToGlobalIndexMap const& dof_table_bulk,
+        int const variable_id, int const component_id,
+        MeshLib::Mesh const& bc_mesh, unsigned const integration_order,
+        MeshLib::Mesh const& bulk_mesh, double const constraint_threshold,
+        bool const lower,
+        std::function<Eigen::Vector3d(std::size_t const,
+                                      MathLib::Point3d const&, double const,
+                                      GlobalVector const&)>
+            getFlux);
+
+    void preTimestep(double t, GlobalVector const& x) override;
+
+    void getEssentialBCValues(
+        const double t,
+        NumLib::IndexValueVector<GlobalIndexType>& bc_values) const override;
+
+private:
+    Parameter<double> const& _parameter;
+
+    /// Local dof table, a subset of the global one restricted to the
+    /// participating number of elements of the boundary condition.
+    std::unique_ptr<NumLib::LocalToGlobalIndexMap> _dof_table_boundary;
+
+    int const _variable_id;
+    int const _component_id;
+
+    /// Vector of (lower-dimensional) boundary elements on which the boundary
+    /// condition is defined.
+    MeshLib::Mesh const& _bc_mesh;
+
+    /// Integration order for integration over the lower-dimensional elements
+    unsigned const _integration_order;
+
+    /// The first item of the pair is the element id in the bulk mesh, the
+    /// second item is the face id of the bulk element that is part of the
+    /// boundary
+    std::vector<std::pair<std::size_t, unsigned>> _bulk_ids;
+
+    /// Stores the results of the flux computations per boundary element.
+    std::vector<double> _flux_values;
+
+    /// Local assemblers for each boundary element.
+    std::vector<std::unique_ptr<
+        ConstraintDirichletBoundaryConditionLocalAssemblerInterface>>
+        _local_assemblers;
+
+    /// The threshold value used to the switch off/on the Dirichlet-type
+    /// boundary condition.
+    double const _constraint_threshold;
+
+    /// The boolean value lower is used for the calculation of the constraint
+    /// criterion, i.e., if lower is set to true the criterion 'calculated_value
+    /// < constraint_threshold' is evaluated to switch on/off the boundary
+    /// condition, else 'calculated_value > constraint_threshold' is evaluated.
+    bool const _lower;
+
+    /// The mesh _bulk_mesh is the discretized domain the process(es) are
+    /// defined on. It is needed to get values for the constraint calculation.
+    MeshLib::Mesh const& _bulk_mesh;
+
+    /// The function _getFlux calculates the flux through the boundary element.
+    std::function<Eigen::Vector3d(
+            std::size_t const, MathLib::Point3d const&, double const,
+            GlobalVector const&)> _getFlux;
+};
+
+/// The function parses the config tree and creates a
+/// ConstraintDirichletBoundaryCondition.
+std::unique_ptr<ConstraintDirichletBoundaryCondition>
+createConstraintDirichletBoundaryCondition(
+    BaseLib::ConfigTree const& config, MeshLib::Mesh const& bc_mesh,
+    NumLib::LocalToGlobalIndexMap const& dof_table_bulk, int const variable_id,
+    unsigned const integration_order, int const component_id,
+    std::vector<std::unique_ptr<ProcessLib::ParameterBase>> const& parameters,
+    Process const& constraining_process);
+}  // namespace ProcessLib
diff --git a/ProcessLib/BoundaryCondition/ConstraintDirichletBoundaryConditionLocalAssembler.h b/ProcessLib/BoundaryCondition/ConstraintDirichletBoundaryConditionLocalAssembler.h
new file mode 100644
index 0000000000000000000000000000000000000000..8fa1e191c5928f43279b3019133c1be73f6dccb4
--- /dev/null
+++ b/ProcessLib/BoundaryCondition/ConstraintDirichletBoundaryConditionLocalAssembler.h
@@ -0,0 +1,169 @@
+/**
+ * \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
+
+#include "NumLib/Fem/ShapeMatrixPolicy.h"
+
+#include "NumLib/DOF/DOFTableUtil.h"
+
+#include "ProcessLib/Process.h"
+#include "ProcessLib/Parameter/Parameter.h"
+#include "ProcessLib/Utils/InitShapeMatrices.h"
+
+#include "MeshLib/Elements/MapBulkElementPoint.h"
+#include "MeshLib/Elements/FaceRule.h"
+#include "MeshLib/Elements/Elements.h"
+
+namespace ProcessLib
+{
+class ConstraintDirichletBoundaryConditionLocalAssemblerInterface
+{
+public:
+    virtual ~ConstraintDirichletBoundaryConditionLocalAssemblerInterface() =
+        default;
+
+    virtual double integrate(
+        GlobalVector const& x, double const t, MeshLib::Mesh const& bulk_mesh,
+        std::function<Eigen::Vector3d(std::size_t const,
+                                      MathLib::Point3d const&, double const,
+                                      GlobalVector const&)> const& getFlux) = 0;
+};
+
+template <typename ShapeFunction, typename IntegrationMethod,
+          unsigned GlobalDim>
+class ConstraintDirichletBoundaryConditionLocalAssembler final
+    : public ConstraintDirichletBoundaryConditionLocalAssemblerInterface
+{
+protected:
+    using ShapeMatricesType = ShapeMatrixPolicyType<ShapeFunction, GlobalDim>;
+    using NodalMatrixType = typename ShapeMatricesType::NodalMatrixType;
+    using NodalVectorType = typename ShapeMatricesType::NodalVectorType;
+
+public:
+    /// Precomputes the shape matrices for a given surface element.
+    /// @param surface_element The surface element used for precomputing the
+    /// @param local_matrix_size Unused parameter, only necessary for the
+    /// creation scheme of local assemblers
+    /// shape matrices used later on for the integration.
+    /// @param is_axially_symmetric Corrects integration measure for cylinder
+    /// coordinates.
+    /// @param integration_order The order of the integration.
+    /// @param bulk_ids Pairs of bulk element ids and bulk element face ids.
+    ConstraintDirichletBoundaryConditionLocalAssembler(
+        MeshLib::Element const& surface_element,
+        std::size_t const local_matrix_size,
+        bool const is_axially_symmetric, unsigned const integration_order,
+        std::vector<std::pair<std::size_t, unsigned>> bulk_ids)
+        : _surface_element(surface_element),
+          _integration_method(integration_order),
+          _bulk_element_id(bulk_ids[_surface_element.getID()].first),
+          _bulk_face_id(bulk_ids[_surface_element.getID()].second)
+    {
+        (void)local_matrix_size; // unused, but needed for the interface
+        using FemType =
+            NumLib::TemplateIsoparametric<ShapeFunction, ShapeMatricesType>;
+
+        FemType fe(*static_cast<const typename ShapeFunction::MeshElement*>(
+            &_surface_element));
+
+        std::size_t const n_integration_points =
+            _integration_method.getNumberOfPoints();
+
+        std::vector<
+            typename ShapeMatricesType::ShapeMatrices,
+            Eigen::aligned_allocator<typename ShapeMatricesType::ShapeMatrices>>
+            shape_matrices;
+        shape_matrices.reserve(n_integration_points);
+        _detJ_times_integralMeasure_times_weight.reserve(n_integration_points);
+        for (std::size_t ip = 0; ip < n_integration_points; ++ip)
+        {
+            shape_matrices.emplace_back(ShapeFunction::DIM, GlobalDim,
+                                        ShapeFunction::NPOINTS);
+            fe.template computeShapeFunctions<NumLib::ShapeMatrixType::N_J>(
+                _integration_method.getWeightedPoint(ip).getCoords(),
+                shape_matrices[ip], GlobalDim, is_axially_symmetric);
+            auto const& wp = _integration_method.getWeightedPoint(ip);
+            _detJ_times_integralMeasure_times_weight.push_back(
+                shape_matrices[ip].detJ * shape_matrices[ip].integralMeasure *
+                wp.getWeight());
+        }
+    }
+
+    /// Integration for the element with the id \c element_id.
+    /// @param x The global vector containing the values for numerical
+    /// integration.
+    /// @param t The point in time the the integration will be performed.
+    /// @param bulk_mesh The bulk mesh the process is defined on.
+    /// @param getFlux The function of the constraining process used to
+    /// calculate the flux.
+    double integrate(
+        GlobalVector const& x, double const t, MeshLib::Mesh const& bulk_mesh,
+        std::function<Eigen::Vector3d(
+            std::size_t const, MathLib::Point3d const&, double const,
+            GlobalVector const&)> const& getFlux) override
+    {
+        MathLib::Vector3 surface_element_normal;
+        if (_surface_element.getDimension() < 2)
+        {
+            auto const bulk_element_normal =
+                MeshLib::FaceRule::getSurfaceNormal(
+                    bulk_mesh.getElement(_bulk_element_id));
+            MathLib::Vector3 const edge_vector(*_surface_element.getNode(0),
+                                               *_surface_element.getNode(1));
+            surface_element_normal =
+                MathLib::crossProduct(bulk_element_normal, edge_vector);
+        } else {
+            surface_element_normal =
+                MeshLib::FaceRule::getSurfaceNormal(&_surface_element);
+        }
+
+        surface_element_normal.normalize();
+        // At the moment (2018-04-26) the surface normal is not oriented
+        // according to the right hand rule
+        // for correct results it is necessary to multiply the normal with -1
+        surface_element_normal *= -1;
+
+        auto const n_integration_points =
+            _integration_method.getNumberOfPoints();
+        // integrated_value +=
+        //   int_{\Gamma_e} \alpha \cdot flux \cdot normal \d \Gamma
+        double integrated_value = 0;
+        for (auto ip(0); ip < n_integration_points; ip++)
+        {
+            auto const& wp = _integration_method.getWeightedPoint(ip);
+            auto const bulk_element_point = MeshLib::getBulkElementPoint(
+                bulk_mesh, _bulk_element_id, _bulk_face_id, wp);
+            auto const bulk_flux =
+                getFlux(_bulk_element_id, bulk_element_point, t, x);
+
+            // TODO find solution for 2d case
+            double const bulk_grad_times_normal(
+                Eigen::Map<Eigen::RowVectorXd const>(bulk_flux.data(),
+                                                     bulk_flux.size())
+                    .dot(Eigen::Map<Eigen::RowVectorXd const>(
+                        surface_element_normal.getCoords(), 3)));
+
+            integrated_value += bulk_grad_times_normal *
+                                _detJ_times_integralMeasure_times_weight[ip];
+        }
+        return integrated_value;
+    }
+
+private:
+    MeshLib::Element const& _surface_element;
+
+    std::vector<double> _detJ_times_integralMeasure_times_weight;
+
+    IntegrationMethod const _integration_method;
+    std::size_t const _bulk_element_id;
+    unsigned const _bulk_face_id;
+};
+
+}  // ProcessLib