OGS
EmbeddedAnchor.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
4#include "EmbeddedAnchor.h"
5
6#include <Eigen/Core>
7#include <cassert>
8#include <numeric>
9#include <range/v3/view/enumerate.hpp>
10#include <unsupported/Eigen/KroneckerProduct>
11
21
22namespace ProcessLib
23{
24// The local indices are, due to the nature of the DOF table, all even
25// for the first node and odd for the second node.
26template <int GlobalDim>
27auto nodeLocalIndices(std::size_t const i)
28{
29 return Eigen::seqN(i, Eigen::fix<GlobalDim>, Eigen::fix<2>);
30}
31
32template <int GlobalDim>
33Eigen::RowVectorXd computeShapeMatrix(
34 MeshLib::Element const& bulk_element,
35 std::span<double const, 3> const nat_coords)
36{
37 using ShapeMatricesType =
38 EigenDynamicShapeMatrixPolicy<void /* dummy */, -1 /* dynamic order */>;
39
40 typename ShapeMatricesType::ShapeMatrices shape_matrix(
41 bulk_element.getDimension(), GlobalDim,
42 bulk_element.getNumberOfNodes());
43
44 bool matched = false;
45
47 [&](auto* tag)
48 {
49 using ElementTrait = std::remove_pointer_t<decltype(tag)>;
50 using ShapeFunction = typename ElementTrait::ShapeFunction;
51 using MeshElement = typename ElementTrait::Element;
52
53 if (!matched &&
54 dynamic_cast<MeshElement const*>(&bulk_element) != nullptr)
55 {
57 ShapeFunction, ShapeMatricesType>(bulk_element);
58
59 fe.template computeShapeFunctions<NumLib::ShapeMatrixType::N>(
60 nat_coords.data(), shape_matrix, GlobalDim,
61 false /* axisymmetric */);
62
63 matched = true;
64 }
65 });
66
67 if (!matched)
68 {
69 OGS_FATAL("Element type '{:s}' is not supported as anchor element.",
71 }
72
73 return shape_matrix.N;
74}
75
76template <int GlobalDim>
77std::tuple<Eigen::VectorXd, Eigen::MatrixXd> assembleLocalBJac(
78 Eigen::Vector<double, GlobalDim> const& f,
79 Eigen::Matrix<double, GlobalDim, GlobalDim> const& Df,
80 std::vector<Eigen::RowVectorXd> const& shape_matrices,
81 std::size_t const num_dof,
82 std::array<std::size_t, 2> const& nodes_per_element)
83{
84 // Signs for the two nodes alternate.
85 constexpr auto even_odd_sign = [](std::size_t const n)
86 { return (n % 2 == 0) ? 1.0 : -1.0; };
87
88 Eigen::VectorXd local_rhs(num_dof);
89 Eigen::MatrixXd local_Jac(num_dof, num_dof);
90 local_rhs.setZero();
91 local_Jac.setZero();
92
93 for (std::size_t node_idx = 0; node_idx < 2; ++node_idx)
94 {
95 // Select the correct block according to the node index.
96 // Replicate shape matrices of the corresponding element to GlobalDim
97 // dimensions. Multiply component-wise with the force vector f.
98 local_rhs.segment(node_idx * GlobalDim * nodes_per_element[0],
99 GlobalDim * nodes_per_element[node_idx]) =
100 even_odd_sign(node_idx) *
101 shape_matrices[node_idx]
102 .transpose()
103 .replicate<GlobalDim, 1>()
104 .cwiseProduct(f.transpose()
105 .replicate(nodes_per_element[node_idx], 1)
106 .reshaped());
107 for (std::size_t node_idx_inner = 0; node_idx_inner < 2;
108 ++node_idx_inner)
109 {
110 Eigen::MatrixXd const& ones = Eigen::MatrixXd::Ones(
111 nodes_per_element[node_idx], nodes_per_element[node_idx_inner]);
112
113 // Select the correct block according to the node index.
114 // Replicate shape matrices of the corresponding element to
115 // GlobalDim dimensions. Multiply component-wise with the derivative
116 // Df of force vector f.
117 local_Jac.block(node_idx * GlobalDim * nodes_per_element[0],
118 node_idx_inner * GlobalDim * nodes_per_element[0],
119 GlobalDim * nodes_per_element[node_idx],
120 GlobalDim * nodes_per_element[node_idx_inner]) =
121 (even_odd_sign(node_idx) *
122 shape_matrices[node_idx]
123 .transpose()
124 .replicate<GlobalDim, 1>() *
125 even_odd_sign(node_idx_inner) *
126 shape_matrices[node_idx_inner].replicate<1, GlobalDim>())
127 .cwiseProduct(kroneckerProduct(Df, ones));
128 }
129 }
130 return {local_rhs, local_Jac};
131}
132
133template <int GlobalDim>
135 MeshLib::Mesh const& bulk_mesh,
136 NumLib::LocalToGlobalIndexMap const& dof_table_bulk,
137 std::size_t const source_term_mesh_id,
138 MeshLib::Mesh const& st_mesh,
139 const int variable_id)
140 : dof_table_bulk_(dof_table_bulk),
141 bulk_mesh_(bulk_mesh),
142 source_term_mesh_id_(source_term_mesh_id),
143 st_mesh_(st_mesh),
144 variable_id_(variable_id),
146 []
147 {
148 std::array<int, GlobalDim> arr{};
149 std::iota(arr.begin(), arr.end(), 0);
150 return arr;
151 }())
152{
153 DBUG("Create EmbeddedAnchor.");
154 std::string_view const bulk_element_ids_string = "bulk_element_ids";
155 bulk_element_ids_ =
156 st_mesh_.getProperties().template getPropertyVector<std::size_t>(
157 bulk_element_ids_string);
158
159 std::string_view const natural_coordinates_string = "natural_coordinates";
160 natural_coordinates_ =
161 st_mesh_.getProperties().template getPropertyVector<double>(
162 natural_coordinates_string);
163
164 std::string_view const maximum_anchor_stress_string =
165 "maximum_anchor_stress";
166 maximum_anchor_stress_ =
167 st_mesh_.getProperties().template getPropertyVector<double>(
168 maximum_anchor_stress_string);
169
170 std::string_view const initial_anchor_stress_string =
171 "initial_anchor_stress";
172 initial_anchor_stress_ =
173 st_mesh_.getProperties().template getPropertyVector<double>(
174 initial_anchor_stress_string);
175
176 std::string_view const residual_anchor_stress_string =
177 "residual_anchor_stress";
178 residual_anchor_stress_ =
179 st_mesh_.getProperties().template getPropertyVector<double>(
180 residual_anchor_stress_string);
181
182 std::string_view const anchor_cross_sectional_area_string =
183 "anchor_cross_sectional_area";
184 cross_sectional_area_ =
185 st_mesh_.getProperties().template getPropertyVector<double>(
186 anchor_cross_sectional_area_string);
187
188 std::string_view const anchor_stiffness_string = "anchor_stiffness";
189 anchor_stiffness_ =
190 st_mesh_.getProperties().template getPropertyVector<double>(
191 anchor_stiffness_string);
192}
193
194template <int GlobalDim>
197 MeshLib::Element const* const anchor_element,
198 std::array<std::size_t, 2>& nodes_per_element,
199 std::vector<Eigen::RowVectorXd>& shape_matrices,
200 std::vector<GlobalIndexType>& global_indices,
201 Eigen::Vector<double, 2 * GlobalDim>& local_x,
202 GlobalVector const& x,
204{
205 for (auto&& [node_idx, anchor_node_id] : ranges::views::enumerate(
206 anchor_element->nodes() | MeshLib::views::ids))
207 {
208 // used pos in integrate function will be of the last node in anchor
209 // element, i,e, only one uniform stiffness is supported
210 auto const bulk_element_id = (*bulk_element_ids_)[anchor_node_id];
211 pos.setElementID(bulk_element_id);
212
213 auto const& bulk_element = *bulk_mesh_.getElement(bulk_element_id);
214 nodes_per_element[node_idx] = bulk_element.nodes().size();
215
216 std::span<double const, 3> const nat_coords(
217 &(*natural_coordinates_)[3 * anchor_node_id], 3);
218 auto N = computeShapeMatrix<GlobalDim>(bulk_element, nat_coords);
219 shape_matrices.push_back(std::move(N));
220
221 for (int component = 0; component < GlobalDim; ++component)
222 {
223 auto const& global_indices_percomponent = getIndices(
224 variable_id_, component, bulk_element_id, dof_table_bulk_);
225
226 Eigen::VectorXd const local_x_element_percomponent =
227 MathLib::toVector(x.get(global_indices_percomponent));
228
229 global_indices.insert(global_indices.cend(),
230 global_indices_percomponent.begin(),
231 global_indices_percomponent.end());
232
233 // this maps the displacement of each component of all nodes
234 // of the element to the desired anchor location in that element
235 // where all even local_x entries are for the first node and all odd
236 // local_x entries are for the second node
238 local_x_element_percomponent,
239 shape_matrices[node_idx],
240 local_x[nodeLocalIndices<GlobalDim>(node_idx)[component]]);
241 }
242 }
243}
244
245template <int GlobalDim>
247 GlobalVector const& x,
248 GlobalVector& b,
249 GlobalMatrix* jac) const
250{
251 DBUG("Assemble EmbeddedAnchor.");
252
253 using GlobalDimVector = Eigen::Vector<double, GlobalDim>;
254 using GlobalDimMatrix = Eigen::Matrix<double, GlobalDim, GlobalDim>;
255
256 for (MeshLib::Element const* const anchor_element : st_mesh_.getElements())
257 {
258 auto const anchor_element_id = anchor_element->getID();
259 std::vector<GlobalIndexType> global_indices;
260 Eigen::Vector<double, 2 * GlobalDim> local_x;
261 std::vector<Eigen::RowVectorXd> shape_matrices;
262 std::array<std::size_t, 2> nodes_per_element;
265 anchor_element, nodes_per_element, shape_matrices, global_indices,
266 local_x, x, pos);
267
268 auto node_coords = [anchor_element](int const i)
269 { return anchor_element->getNode(i)->asEigenVector3d(); };
270 GlobalDimVector const l_original =
271 (node_coords(1) - node_coords(0)).template head<GlobalDim>();
272 double const l_original_norm = l_original.norm();
273
274 // Displacement in the two nodes.
275 auto u = [&local_x](int const i)
276 { return local_x(nodeLocalIndices<GlobalDim>(i)); };
277 GlobalDimVector const l = l_original + u(1) - u(0);
278
279 double const K = (*cross_sectional_area_)[anchor_element_id] *
280 (*anchor_stiffness_)[anchor_element_id];
281 double const initial_force =
282 (*cross_sectional_area_)[anchor_element_id] *
283 (*initial_anchor_stress_)[anchor_element_id];
284 double const max_force = (*cross_sectional_area_)[anchor_element_id] *
285 (*maximum_anchor_stress_)[anchor_element_id];
286 double const residual_force =
287 (*cross_sectional_area_)[anchor_element_id] *
288 (*residual_anchor_stress_)[anchor_element_id];
289
290 double const strain = (l.norm() - l_original_norm) / l_original_norm;
291
292 GlobalDimVector const f_friction =
293 residual_force * l_original / l_original_norm;
294 GlobalDimVector const f_elastic =
295 l_original / l_original_norm * (initial_force + K * strain);
296 GlobalDimVector const f =
297 ((f_elastic.norm() < max_force)) ? f_elastic : f_friction;
298
299 GlobalDimMatrix const Df_friction = GlobalDimMatrix::Zero();
300 GlobalDimMatrix const Df_elastic = l_original / l_original_norm * K *
301 l.transpose() / l.norm() /
302 l_original_norm;
303 GlobalDimMatrix const Df =
304 (f_elastic.norm() < max_force) ? Df_elastic : Df_friction;
305
306 auto const& [local_rhs, local_Jac] = assembleLocalBJac<GlobalDim>(
307 f, Df, shape_matrices, global_indices.size(), nodes_per_element);
308
309 b.add(global_indices, local_rhs);
310 if (jac)
311 {
312 jac->add({global_indices, global_indices}, local_Jac);
313 }
314 }
315}
316
317template class EmbeddedAnchor<2>;
318template class EmbeddedAnchor<3>;
319
320} // namespace ProcessLib
#define OGS_FATAL(...)
Definition Error.h:19
MathLib::EigenMatrix GlobalMatrix
MathLib::EigenVector GlobalVector
void DBUG(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:22
int add(IndexType row, IndexType col, double val)
Definition EigenMatrix.h:80
void add(IndexType rowId, double v)
add entry
Definition EigenVector.h:70
double get(IndexType rowId) const
get entry
Definition EigenVector.h:52
virtual CellType getCellType() const =0
virtual unsigned getNumberOfNodes() const =0
virtual constexpr unsigned getDimension() const =0
Get dimension of the mesh element.
constexpr std::span< Node *const > nodes() const
Span of element's nodes, their pointers actually.
Definition Element.h:63
void setElementID(std::size_t element_id)
void getShapeMatricesAndGlobalIndicesAndDisplacements(MeshLib::Element const *const anchor_element, std::array< std::size_t, 2 > &nodes_per_element, std::vector< Eigen::RowVectorXd > &shape_matrices, std::vector< GlobalIndexType > &global_indices, Eigen::Vector< double, 2 *GlobalDim > &local_x, GlobalVector const &x, ParameterLib::SpatialPosition &pos) const
MeshLib::Mesh const & st_mesh_
std::array< int, GlobalDim > const component_ids_
std::size_t const source_term_mesh_id_
void integrate(const double t, GlobalVector const &x, GlobalVector &b, GlobalMatrix *jac) const override
NumLib::LocalToGlobalIndexMap const & dof_table_bulk_
MeshLib::PropertyVector< double > const * natural_coordinates_
MeshLib::Mesh const & bulk_mesh_
EmbeddedAnchor(MeshLib::Mesh const &bulk_mesh, NumLib::LocalToGlobalIndexMap const &dof_table_bulk, std::size_t const source_term_mesh_id, MeshLib::Mesh const &st_mesh, const int variable_id)
void foreach(Function &&f)
Definition TMP.h:150
Eigen::Map< const Vector > toVector(std::vector< double > const &data, Eigen::VectorXd::Index size)
Creates an Eigen mapped vector from the given data vector.
constexpr ranges::views::view_closure ids
For an element of a range view return its id.
Definition Mesh.h:216
std::string CellType2String(const CellType t)
Given a MeshElemType this returns the appropriate string.
void shapeFunctionInterpolate(const NodalValues &, const ShapeMatrix &)
NumLib::TemplateIsoparametric< ShapeFunction, ShapeMatricesType > createIsoparametricFiniteElement(MeshLib::Element const &e)
std::tuple< Eigen::VectorXd, Eigen::MatrixXd > assembleLocalBJac(Eigen::Vector< double, GlobalDim > const &f, Eigen::Matrix< double, GlobalDim, GlobalDim > const &Df, std::vector< Eigen::RowVectorXd > const &shape_matrices, std::size_t const num_dof, std::array< std::size_t, 2 > const &nodes_per_element)
Eigen::RowVectorXd computeShapeMatrix(MeshLib::Element const &bulk_element, std::span< double const, 3 > const nat_coords)
auto nodeLocalIndices(std::size_t const i)