From 9c21b9d4518cac74868f8f0f23c7da9ebc668b0b Mon Sep 17 00:00:00 2001
From: Dmitrij Naumov <dmitrij@naumov.de>
Date: Tue, 21 Feb 2017 14:19:45 +0100
Subject: [PATCH] [PL] BCs: Implement pressure boundary condition.

---
 .../BoundaryCondition/BoundaryCondition.cpp   |  35 ++++
 .../BoundaryCondition/BoundaryCondition.h     |   8 +
 .../PressureBoundaryCondition-impl.h          | 115 ++++++++++++
 .../PressureBoundaryCondition.h               |  83 +++++++++
 .../PressureBoundaryConditionLocalAssembler.h | 174 ++++++++++++++++++
 5 files changed, 415 insertions(+)
 create mode 100644 ProcessLib/BoundaryCondition/PressureBoundaryCondition-impl.h
 create mode 100644 ProcessLib/BoundaryCondition/PressureBoundaryCondition.h
 create mode 100644 ProcessLib/BoundaryCondition/PressureBoundaryConditionLocalAssembler.h

diff --git a/ProcessLib/BoundaryCondition/BoundaryCondition.cpp b/ProcessLib/BoundaryCondition/BoundaryCondition.cpp
index 6e90534adf2..55edb6fda62 100644
--- a/ProcessLib/BoundaryCondition/BoundaryCondition.cpp
+++ b/ProcessLib/BoundaryCondition/BoundaryCondition.cpp
@@ -16,6 +16,7 @@
 #include "MeshGeoToolsLib/SearchLength.h"
 #include "NeumannBoundaryCondition.h"
 #include "NonuniformNeumannBoundaryCondition.h"
+#include "PressureBoundaryCondition.h"
 #include "RobinBoundaryCondition.h"
 
 namespace ProcessLib
@@ -56,6 +57,15 @@ BoundaryConditionBuilder::createBoundaryCondition(
             shapefunction_order);
     }
 
+    //
+    // Special boundary conditions
+    //
+    if (type == "Pressure")
+    {
+        return createPressureBoundaryCondition(config, dof_table, mesh,
+                                               variable_id, integration_order,
+                                               shapefunction_order, parameters);
+    }
     OGS_FATAL("Unknown boundary condition type: `%s'.", type.c_str());
 }
 
@@ -177,6 +187,31 @@ BoundaryConditionBuilder::createNonuniformNeumannBoundaryCondition(
         integration_order, shapefunction_order, mesh);
 }
 
+std::unique_ptr<BoundaryCondition>
+BoundaryConditionBuilder::createPressureBoundaryCondition(
+    const BoundaryConditionConfig& config,
+    const NumLib::LocalToGlobalIndexMap& dof_table, const MeshLib::Mesh& mesh,
+    const int variable_id, const unsigned integration_order,
+    const unsigned shapefunction_order,
+    const std::vector<std::unique_ptr<ProcessLib::ParameterBase>>& parameters)
+{
+    std::unique_ptr<MeshGeoToolsLib::SearchLength> search_length_algorithm =
+        MeshGeoToolsLib::createSearchLengthAlgorithm(config.config, mesh);
+
+    MeshGeoToolsLib::MeshNodeSearcher const& mesh_node_searcher =
+        MeshGeoToolsLib::MeshNodeSearcher::getMeshNodeSearcher(
+            mesh, std::move(search_length_algorithm));
+
+    MeshGeoToolsLib::BoundaryElementsSearcher boundary_element_searcher(
+        mesh, mesh_node_searcher);
+
+    return ProcessLib::PressureBoundaryCondition::createPressureBoundaryCondition(
+        config.config,
+        getClonedElements(boundary_element_searcher, config.geometry),
+        dof_table, variable_id, mesh.isAxiallySymmetric(), integration_order,
+        shapefunction_order, mesh.getDimension(), parameters);
+}
+
 std::vector<MeshLib::Element*> BoundaryConditionBuilder::getClonedElements(
     MeshGeoToolsLib::BoundaryElementsSearcher& boundary_element_searcher,
     GeoLib::GeoObject const& geometry)
diff --git a/ProcessLib/BoundaryCondition/BoundaryCondition.h b/ProcessLib/BoundaryCondition/BoundaryCondition.h
index c7dda2d1554..8c6a28a1b0b 100644
--- a/ProcessLib/BoundaryCondition/BoundaryCondition.h
+++ b/ProcessLib/BoundaryCondition/BoundaryCondition.h
@@ -114,6 +114,14 @@ protected:
         const MeshLib::Mesh& mesh, const int variable_id,
         const unsigned integration_order, const unsigned shapefunction_order);
 
+    virtual std::unique_ptr<BoundaryCondition> createPressureBoundaryCondition(
+        const BoundaryConditionConfig& config,
+        const NumLib::LocalToGlobalIndexMap& dof_table,
+        const MeshLib::Mesh& mesh, const int variable_id,
+        const unsigned integration_order, const unsigned shapefunction_order,
+        const std::vector<std::unique_ptr<ProcessLib::ParameterBase>>&
+            parameters);
+
     static std::vector<MeshLib::Element*> getClonedElements(
         MeshGeoToolsLib::BoundaryElementsSearcher& boundary_element_searcher,
         GeoLib::GeoObject const& geometry);
diff --git a/ProcessLib/BoundaryCondition/PressureBoundaryCondition-impl.h b/ProcessLib/BoundaryCondition/PressureBoundaryCondition-impl.h
new file mode 100644
index 00000000000..d1b62717f20
--- /dev/null
+++ b/ProcessLib/BoundaryCondition/PressureBoundaryCondition-impl.h
@@ -0,0 +1,115 @@
+/**
+ * \file
+ * \copyright
+ * Copyright (c) 2012-2017, 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 "MeshLib/MeshSearch/NodeSearch.h"
+#include "ProcessLib/Utils/CreateLocalAssemblers.h"
+#include "ProcessLib/Utils/ProcessUtils.h"
+
+#include "PressureBoundaryConditionLocalAssembler.h"
+
+namespace ProcessLib
+{
+namespace PressureBoundaryCondition
+{
+template <template <typename, typename, unsigned>
+          class LocalAssemblerImplementation>
+PressureBoundaryCondition<LocalAssemblerImplementation>::
+    PressureBoundaryCondition(
+        bool const is_axially_symmetric, unsigned const integration_order,
+        unsigned const shapefunction_order,
+        NumLib::LocalToGlobalIndexMap const& dof_table_bulk,
+        int const variable_id, unsigned const global_dim,
+        std::vector<MeshLib::Element*>&& elements,
+        Parameter<double> const& pressure)
+    : _elements(std::move(elements)),
+      _integration_order(integration_order),
+      _pressure(pressure)
+{
+    std::vector<MeshLib::Node*> const nodes =
+        MeshLib::getUniqueNodes(_elements);
+    DBUG("Found %d nodes for Natural BCs for the variable %d", nodes.size(),
+         variable_id);
+
+    // Assume that the mesh subsets are equal for all components of the
+    // variable.
+    auto const& mesh_subsets = dof_table_bulk.getMeshSubsets(variable_id, 0);
+
+    // TODO extend the node intersection to all parts of mesh_subsets, i.e.
+    // to each of the MeshSubset in the mesh_subsets.
+    _mesh_subset_all_nodes.reset(
+        mesh_subsets.getMeshSubset(0).getIntersectionByNodes(nodes));
+    MeshLib::MeshSubsets all_mesh_subsets{_mesh_subset_all_nodes.get()};
+
+    // Create component ids vector for the current variable.
+    auto const& number_of_components =
+        dof_table_bulk.getNumberOfVariableComponents(variable_id);
+    std::vector<int> component_ids(number_of_components);
+    std::iota(std::begin(component_ids), std::end(component_ids), 0);
+
+    // 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_ids, std::move(all_mesh_subsets), _elements));
+
+    createLocalAssemblers<LocalAssemblerImplementation>(
+        global_dim, _elements, *_dof_table_boundary, shapefunction_order,
+        _local_assemblers, is_axially_symmetric, _integration_order, _pressure);
+}
+
+template <template <typename, typename, unsigned>
+          class LocalAssemblerImplementation>
+PressureBoundaryCondition<
+    LocalAssemblerImplementation>::~PressureBoundaryCondition()
+{
+    for (auto e : _elements)
+        delete e;
+}
+
+template <template <typename, typename, unsigned>
+          class LocalAssemblerImplementation>
+void PressureBoundaryCondition<LocalAssemblerImplementation>::applyNaturalBC(
+    const double t, const GlobalVector& x, GlobalMatrix& K, GlobalVector& b)
+{
+    GlobalExecutor::executeMemberOnDereferenced(
+        &PressureBoundaryConditionLocalAssemblerInterface::assemble,
+        _local_assemblers, *_dof_table_boundary, t, x, K, b);
+}
+
+std::unique_ptr<
+    PressureBoundaryCondition<PressureBoundaryConditionLocalAssembler>>
+createPressureBoundaryCondition(
+    BaseLib::ConfigTree const& config,
+    std::vector<MeshLib::Element*>&& elements,
+    NumLib::LocalToGlobalIndexMap const& dof_table, int const variable_id,
+    bool is_axially_symmetric, unsigned const integration_order,
+    unsigned const shapefunction_order, unsigned const global_dim,
+    std::vector<std::unique_ptr<ParameterBase>> const& parameters)
+{
+    DBUG("Constructing PressureBoundaryCondition from config.");
+    //! \ogs_file_param{prj__process_variables__process_variable__boundary_conditions__boundary_condition__type}
+    config.checkConfigParameter("type", "Pressure");
+
+    auto const parameter_name =
+    //! \ogs_file_param{prj__process_variables__process_variable__boundary_conditions__boundary_condition__Pressure__parameter}
+        config.getConfigParameter<std::string>("parameter");
+    DBUG("Using parameter %s", parameter_name.c_str());
+
+    auto const& pressure = findParameter<double>(parameter_name, parameters, 1);
+    return std::unique_ptr<
+        PressureBoundaryCondition<PressureBoundaryConditionLocalAssembler>>{
+        new PressureBoundaryCondition<PressureBoundaryConditionLocalAssembler>{
+            is_axially_symmetric, integration_order, shapefunction_order,
+            dof_table, variable_id, global_dim, std::move(elements), pressure}};
+}
+
+}  // namespace PressureBoundaryCondition
+}  // ProcessLib
diff --git a/ProcessLib/BoundaryCondition/PressureBoundaryCondition.h b/ProcessLib/BoundaryCondition/PressureBoundaryCondition.h
new file mode 100644
index 00000000000..2feeb227b34
--- /dev/null
+++ b/ProcessLib/BoundaryCondition/PressureBoundaryCondition.h
@@ -0,0 +1,83 @@
+/**
+ * \file
+ * \copyright
+ * Copyright (c) 2012-2017, 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 "MeshLib/MeshSubset.h"
+#include "BoundaryCondition.h"
+#include "PressureBoundaryConditionLocalAssembler.h"
+
+namespace ProcessLib
+{
+namespace PressureBoundaryCondition
+{
+class PressureBoundaryConditionLocalAssemblerInterface;
+
+template <template <typename, typename, unsigned>
+          class LocalAssemblerImplementation>
+class PressureBoundaryCondition final : public BoundaryCondition
+{
+public:
+    /// Create a boundary condition process from given config,
+    /// DOF-table, and a mesh subset for a given variable and its component.
+    /// A local DOF-table, a subset of the given one, is constructed.
+    PressureBoundaryCondition(
+        bool const is_axially_symmetric, unsigned const integration_order,
+        unsigned const shapefunction_order,
+        NumLib::LocalToGlobalIndexMap const& dof_table_bulk,
+        int const variable_id, unsigned const global_dim,
+        std::vector<MeshLib::Element*>&& elements,
+        Parameter<double> const& pressure);
+
+    ~PressureBoundaryCondition() override;
+
+    /// Calls local assemblers which calculate their contributions to the global
+    /// matrix and the right-hand-side.
+    void applyNaturalBC(const double t,
+                        GlobalVector const& x,
+                        GlobalMatrix& K,
+                        GlobalVector& b) override;
+
+private:
+    /// Vector of lower-dimensional elements on which the boundary condition is
+    /// defined.
+    std::vector<MeshLib::Element*> _elements;
+
+    std::unique_ptr<MeshLib::MeshSubset const> _mesh_subset_all_nodes;
+
+    /// Local dof table, a subset of the global one restricted to the
+    /// participating #_elements of the boundary condition.
+    std::unique_ptr<NumLib::LocalToGlobalIndexMap> _dof_table_boundary;
+
+    /// Integration order for integration over the lower-dimensional elements
+    unsigned const _integration_order;
+
+    /// Local assemblers for each element of #_elements.
+    std::vector<
+        std::unique_ptr<PressureBoundaryConditionLocalAssemblerInterface>>
+        _local_assemblers;
+
+    Parameter<double> const& _pressure;
+};
+
+std::unique_ptr<
+    PressureBoundaryCondition<PressureBoundaryConditionLocalAssembler>>
+createPressureBoundaryCondition(
+    BaseLib::ConfigTree const& config,
+    std::vector<MeshLib::Element*>&& elements,
+    NumLib::LocalToGlobalIndexMap const& dof_table, int const variable_id,
+    bool is_axially_symmetric, unsigned const integration_order,
+    unsigned const shapefunction_order, unsigned const global_dim,
+    std::vector<std::unique_ptr<ParameterBase>> const& parameters);
+
+}  // namespace PressureBoundaryCondition
+}  // namespace ProcessLib
+
+#include "PressureBoundaryCondition-impl.h"
diff --git a/ProcessLib/BoundaryCondition/PressureBoundaryConditionLocalAssembler.h b/ProcessLib/BoundaryCondition/PressureBoundaryConditionLocalAssembler.h
new file mode 100644
index 00000000000..59cdb469afb
--- /dev/null
+++ b/ProcessLib/BoundaryCondition/PressureBoundaryConditionLocalAssembler.h
@@ -0,0 +1,174 @@
+/**
+ * \file
+ * \copyright
+ * Copyright (c) 2012-2017, 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 "MathLib/LinAlg/Eigen/EigenMapTools.h"
+#include "NumLib/DOF/DOFTableUtil.h"
+#include "ProcessLib/Parameter/Parameter.h"
+
+#include "GenericNaturalBoundaryConditionLocalAssembler.h"
+
+namespace ProcessLib
+{
+namespace PressureBoundaryCondition
+{
+template <typename ShapeMatricesTypeDisplacement, int GlobalDim, int NPoints>
+struct IntegrationPointData final
+{
+    IntegrationPointData(
+        typename ShapeMatricesTypeDisplacement::template VectorType<
+            NPoints * GlobalDim> const& Nu_times_n_,
+        double const integration_weight_)
+        : Nu_times_n(Nu_times_n_), integration_weight(integration_weight_)
+    {
+    }
+
+    // Shape matrix (for displacement) times element's normal vector.
+    typename ShapeMatricesTypeDisplacement::template VectorType<
+        NPoints * GlobalDim> const Nu_times_n;
+
+    double const integration_weight;
+};
+
+class PressureBoundaryConditionLocalAssemblerInterface
+{
+public:
+    virtual void assemble(
+        std::size_t const id,
+        NumLib::LocalToGlobalIndexMap const& dof_table_boundary, double const t,
+        const GlobalVector& /*x*/, GlobalMatrix& /*K*/, GlobalVector& b) = 0;
+    virtual ~PressureBoundaryConditionLocalAssemblerInterface() = default;
+};
+
+template <typename ShapeFunctionDisplacement, typename IntegrationMethod,
+          unsigned GlobalDim>
+class PressureBoundaryConditionLocalAssembler final
+    : public PressureBoundaryConditionLocalAssemblerInterface
+{
+public:
+    using ShapeMatricesTypeDisplacement =
+        ShapeMatrixPolicyType<ShapeFunctionDisplacement, GlobalDim>;
+    using GlobalDimVectorType =
+        typename ShapeMatrixPolicyType<ShapeFunctionDisplacement,
+                                       GlobalDim>::GlobalDimVectorType;
+
+    PressureBoundaryConditionLocalAssembler(MeshLib::Element const& e,
+                                            std::size_t const local_matrix_size,
+                                            bool is_axially_symmetric,
+                                            unsigned const integration_order,
+                                            Parameter<double> const& pressure)
+        : _integration_method(integration_order), _pressure(pressure)
+    {
+        _local_rhs.setZero(local_matrix_size);
+
+        unsigned const n_integration_points =
+            _integration_method.getNumberOfPoints();
+
+        _ip_data.reserve(n_integration_points);
+
+        auto const shape_matrices_u =
+            initShapeMatrices<ShapeFunctionDisplacement,
+                              ShapeMatricesTypeDisplacement, IntegrationMethod,
+                              GlobalDim>(e, is_axially_symmetric,
+                                         _integration_method);
+
+        GlobalDimVectorType element_normal(GlobalDim);
+
+        // TODO Extend to rotated 2d meshes and line elements.
+        if (e.getGeomType() == MeshLib::MeshElemType::LINE)
+        {
+            auto v1 = (*e.getNode(1)) - (*e.getNode(0));
+            element_normal[0] = -v1[1];
+            element_normal[1] = v1[0];
+            element_normal.normalize();
+        }
+        else
+        {
+            auto const element_normal_vector =
+                MeshLib::FaceRule::getSurfaceNormal(&e).getNormalizedVector();
+
+            std::copy_n(element_normal_vector.getCoords(), GlobalDim,
+                        element_normal.data());
+        }
+
+        for (unsigned ip = 0; ip < n_integration_points; ip++)
+        {
+            typename ShapeMatricesTypeDisplacement::template MatrixType<
+                GlobalDim, displacement_size>
+                N_u = ShapeMatricesTypeDisplacement::template MatrixType<
+                    GlobalDim, displacement_size>::Zero(GlobalDim,
+                                                        displacement_size);
+            for (int i = 0; i < static_cast<int>(GlobalDim); ++i)
+                N_u.template block<1, displacement_size / GlobalDim>(
+                       i, i * displacement_size / GlobalDim)
+                    .noalias() = shape_matrices_u[ip].N;
+
+            double const integration_weight =
+                _integration_method.getWeightedPoint(ip).getWeight() *
+                shape_matrices_u[ip].integralMeasure *
+                shape_matrices_u[ip].detJ;
+
+            _ip_data.emplace_back(N_u.transpose() * element_normal,
+                                  integration_weight);
+        }
+    }
+
+    void assemble(std::size_t const id,
+                  NumLib::LocalToGlobalIndexMap const& dof_table_boundary,
+                  double const t, const GlobalVector& /*x*/,
+                  GlobalMatrix& /*K*/, GlobalVector& local_rhs)
+    {
+        _local_rhs.setZero();
+
+        unsigned const n_integration_points =
+            _integration_method.getNumberOfPoints();
+
+        SpatialPosition pos;
+        pos.setElementID(id);
+
+        for (unsigned ip = 0; ip < n_integration_points; ip++)
+        {
+            pos.setIntegrationPoint(ip);
+
+            auto const& w = _ip_data[ip].integration_weight;
+            auto const& Nu_times_n = _ip_data[ip].Nu_times_n;
+
+            _local_rhs.noalias() -=
+                Nu_times_n.transpose() * _pressure(t, pos)[0] * w;
+        }
+
+        auto const indices = NumLib::getIndices(id, dof_table_boundary);
+        local_rhs.add(indices, _local_rhs);
+    }
+
+private:
+    IntegrationMethod const _integration_method;
+    Parameter<double> const& _pressure;
+
+    static const int displacement_size =
+        ShapeFunctionDisplacement::NPOINTS * GlobalDim;
+    std::vector<IntegrationPointData<ShapeMatricesTypeDisplacement, GlobalDim,
+                                     ShapeFunctionDisplacement::NPOINTS>,
+                Eigen::aligned_allocator<IntegrationPointData<
+                    ShapeMatricesTypeDisplacement, GlobalDim,
+                    ShapeFunctionDisplacement::NPOINTS>>>
+        _ip_data;
+
+    typename ShapeMatricesTypeDisplacement::template VectorType<
+        displacement_size>
+        _local_rhs;
+
+public:
+    EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
+};
+
+}  // namespace PressureBoundaryCondition
+}  // namespace ProcessLib
-- 
GitLab