OGS
ParameterLib/CoordinateSystem.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 "CoordinateSystem.h"
5
6#include <Eigen/Dense>
7#include <Eigen/Geometry>
8#include <cmath>
9#include <limits>
10#include <typeinfo>
11
14#include "Parameter.h"
15
16namespace ParameterLib
17{
18static double const tolerance = std::numeric_limits<double>::epsilon();
19
20template <int Dim>
22 Eigen::Matrix<double, Dim, Dim, Eigen::ColMajor, Dim, Dim> const& t)
23{
24 if (std::abs(t.determinant() - 1) > tolerance)
25 {
27 "The determinant of the coordinate system transformation matrix is "
28 "'{:g}', which is not sufficiently close to unity with the "
29 "tolerance of '{:g}'. Please adjust the accuracy of the local "
30 "system bases",
31 t.determinant(), tolerance);
32 }
33 if (((t * t.transpose() - Eigen::Matrix<double, Dim, Dim>::Identity())
34 .array() > 2 * tolerance)
35 .any())
36 {
38 "The transformation is not orthogonal because the difference "
39 "A*A^T - I is:\n{}\nand at least one component deviates from zero "
40 "by more then '{:g}'.",
41 (t * t.transpose() - Eigen::Matrix<double, Dim, Dim>::Identity())
42 .eval(),
43 2 * tolerance);
44 }
45}
46
47template <typename Derived>
48static void checkNormalization(Eigen::MatrixBase<Derived> const& vec,
49 std::string_view const parmeter_name)
50{
51 if (std::abs(vec.squaredNorm() - 1.0) > tolerance)
52 {
54 "The direction vector given by parameter {:s} for the "
55 "local_coordinate_system is not normalized to unit length. Vector "
56 "norm is {:g} and |v|^2-1 = {:g}.",
57 parmeter_name, vec.norm(), vec.squaredNorm() - 1.0);
58 }
59}
60
62 : _base{nullptr, nullptr, &unit_direction}, _has_implicit_base(true)
63{
64 if (_base[2]->isTimeDependent())
65 {
67 "The unit_normal parameter named {} must not be time dependent.",
68 unit_direction.name);
69 }
70}
71
73 Parameter<double> const& e1)
74 : _base{&e0, &e1, nullptr}, _has_implicit_base(false)
75{
76 if (typeid(_base[0]) != typeid(_base[1]))
77 {
79 "The parameter types for the basis must be equal but they are "
80 "'{:s}' and '{:s}'.",
83 }
84 if (_base[0]->isTimeDependent() || _base[1]->isTimeDependent())
85 {
86 OGS_FATAL("The parameters for the basis must not be time dependent.");
87 }
88 if (_base[0]->getNumberOfGlobalComponents() != 2 ||
89 _base[1]->getNumberOfGlobalComponents() != 2)
90 {
91 OGS_FATAL("The parameters for the 2D basis must have two components.");
92 }
93}
94
96 Parameter<double> const& e1,
97 Parameter<double> const& e2)
98 : _base{&e0, &e1, &e2}, _has_implicit_base(false)
99{
100 if ((typeid(_base[0]) != typeid(_base[1])) ||
101 (typeid(_base[1]) != typeid(_base[2])) ||
102 (typeid(_base[2]) != typeid(_base[0])))
103 {
104 OGS_FATAL(
105 "The parameter types for the basis must be equal but they are "
106 "'{:s}', '{:s}', and '{:s}'.",
110 }
111 if (_base[0]->isTimeDependent() || _base[1]->isTimeDependent(),
112 _base[2]->isTimeDependent())
113 {
114 OGS_FATAL("The parameters for the basis must not be time dependent.");
115 }
116 if (_base[0]->getNumberOfGlobalComponents() != 3 ||
117 _base[1]->getNumberOfGlobalComponents() != 3 ||
118 _base[2]->getNumberOfGlobalComponents() != 3)
119 {
120 OGS_FATAL(
121 "The parameters for the 3D basis must have three components.");
122 }
123}
124
125Eigen::Matrix<double, 2, 2> getTransformationFromSingleBase2D(
126 Parameter<double> const& unit_direction, SpatialPosition const& pos)
127{
128 auto const& normal = unit_direction(0 /* time independent */, pos);
129 checkNormalization(Eigen::Map<Eigen::Vector2d const>(normal.data()),
130 unit_direction.name);
131
132 Eigen::Matrix<double, 2, 2> t;
133 // base 0: ( normal[1], -normal[0])^T
134 // base 1: ( normal[0], normal[1])^T
135 t << normal[1], normal[0], -normal[0], normal[1];
136
138 return t;
139}
140
141template <>
142Eigen::Matrix<double, 2, 2> CoordinateSystem::transformation<2>(
143 SpatialPosition const& pos) const
144{
146 {
148 }
149
150 if (_base[2] != nullptr)
151 {
152 OGS_FATAL(
153 "The coordinate system is 3D but a transformation for 2D case is "
154 "requested.");
155 }
156
157 Eigen::Matrix<double, 2, 2> t;
158 t.col(0) = Eigen::Map<Eigen::Vector2d>(
159 (*_base[0])(0 /* time independent */, pos).data());
160 t.col(1) = Eigen::Map<Eigen::Vector2d>(
161 (*_base[1])(0 /* time independent */, pos).data());
162
164 return t;
165}
166
167Eigen::Matrix<double, 3, 3> getTransformationFromSingleBase3D(
168 Parameter<double> const& unit_direction, SpatialPosition const& pos)
169{
170 auto const& normal = unit_direction(0 /* time independent */, pos);
171
172 Eigen::Matrix<double, 3, 3> t;
173 auto e2 = t.col(2);
174 e2 = Eigen::Map<Eigen::Vector3d const>(normal.data());
175 checkNormalization(e2, unit_direction.name);
176
177 // Find the id of the first non-zero component of e2:
178 int id;
179 e2.cwiseAbs().maxCoeff(&id);
180
181 // Get other two component ids:
182 const auto id_a = (id + 1) % 3;
183 const auto id_b = (id + 2) % 3;
184
185 // Compute basis vector e1 orthogonal to e2
186 auto e1 = t.col(1);
187 e1 = Eigen::Vector3d::Zero();
188
189 if (std::abs(e2[id_a]) < tolerance)
190 {
191 e1[id_a] = 1.0;
192 }
193 else if (std::abs(e2[id_b]) < tolerance)
194 {
195 e1[id_b] = 1.0;
196 }
197 else
198 {
199 e1[id_a] = 1.0;
200 e1[id_b] = -e2[id_a] / e2[id_b];
201 }
202
203 e1.normalize();
204
205 // |e0| = |e1 x e2| = |e1||e2|sin(theta) with theta the angle between e1 and
206 // e2. Since |e1| = |e2| = 1.0, and theta = pi/2, we have |e0|=1. Therefore
207 // e0 is normalized by nature.
208 t.col(0) = e1.cross(e2);
209
211
212 return t;
213}
214
215template <>
216Eigen::Matrix<double, 3, 3> CoordinateSystem::transformation<3>(
217 SpatialPosition const& pos) const
218{
220 {
222 }
223
224 if (_base[2] == nullptr)
225 {
226 OGS_FATAL(
227 "The coordinate system is 2D but a transformation for 3D case is "
228 "requested.");
229 }
230
231 Eigen::Matrix<double, 3, 3> t;
232 t.col(0) = Eigen::Map<Eigen::Vector3d>(
233 (*_base[0])(0 /* time independent */, pos).data());
234 t.col(1) = Eigen::Map<Eigen::Vector3d>(
235 (*_base[1])(0 /* time independent */, pos).data());
236 t.col(2) = Eigen::Map<Eigen::Vector3d>(
237 (*_base[2])(0 /* time independent */, pos).data());
238
240 return t;
241}
242
244 SpatialPosition const& pos) const
245{
246 // If base 2, which stores the unit direction vector, has three components:
247 if ((*_base[2])(0 /* time independent */, pos).size() == 3)
248 {
250 }
251
252 // If base 2, which stores the unit direction vector, has two components
253 Eigen::Matrix<double, 3, 3> t = Eigen::Matrix<double, 3, 3>::Identity();
254 t.template topLeftCorner<2, 2>() = transformation<2>(pos);
255
256 return t;
257}
258
259Eigen::Matrix<double, 3, 3> CoordinateSystem::transformation_3d(
260 SpatialPosition const& pos) const
261{
263 {
265 }
266
267 if (_base[2] != nullptr)
268 {
269 return transformation<3>(pos);
270 }
271
272 auto e0 = (*_base[0])(0 /* time independent */, pos);
273 auto e1 = (*_base[1])(0 /* time independent */, pos);
274 Eigen::Matrix<double, 3, 3> t = Eigen::Matrix<double, 3, 3>::Identity();
275 t.template topLeftCorner<2, 2>() << e0[0], e1[0], e0[1], e1[1];
276
278 return t;
279}
280
281template <int Dimension>
282Eigen::Matrix<double, Dimension, Dimension> CoordinateSystem::rotateTensor(
283 std::vector<double> const& values, SpatialPosition const& pos) const
284{
285 assert(values.size() == Dimension * Dimension ||
286 "Input vector has wrong dimension; expected 4 or 9 entries.");
287 auto const tensor =
288 Eigen::Map<Eigen::Matrix<double, Dimension, Dimension> const>(
289 values.data(), Dimension, Dimension);
290 return rotateTensor<Dimension>(tensor, pos);
291}
292
293template <int Dimension>
294Eigen::Matrix<double, Dimension, Dimension>
295CoordinateSystem::rotateDiagonalTensor(std::vector<double> const& values,
296 SpatialPosition const& pos) const
297{
298 assert(values.size() == Dimension ||
299 "Input vector has wrong dimension; expected 2 or 3 entries.");
300 auto const tensor = Eigen::Map<Eigen::Matrix<double, Dimension, 1> const>(
301 values.data(), Dimension, 1);
302 auto const R = transformation<Dimension>(pos);
303 return R * tensor.asDiagonal() * R.transpose();
304}
305
306template Eigen::Matrix<double, 2, 2> CoordinateSystem::rotateTensor<2>(
307 std::vector<double> const& values, SpatialPosition const& pos) const;
308template Eigen::Matrix<double, 3, 3> CoordinateSystem::rotateTensor<3>(
309 std::vector<double> const& values, SpatialPosition const& pos) const;
310template Eigen::Matrix<double, 2, 2> CoordinateSystem::rotateDiagonalTensor<2>(
311 std::vector<double> const& values, SpatialPosition const& pos) const;
312template Eigen::Matrix<double, 3, 3> CoordinateSystem::rotateDiagonalTensor<3>(
313 std::vector<double> const& values, SpatialPosition const& pos) const;
314} // namespace ParameterLib
#define OGS_FATAL(...)
Definition Error.h:19
std::string typeToString()
Eigen::Matrix< double, 2, 2 > getTransformationFromSingleBase2D(Parameter< double > const &unit_direction, SpatialPosition const &pos)
static double const tolerance
static void checkNormalization(Eigen::MatrixBase< Derived > const &vec, std::string_view const parmeter_name)
static void checkTransformationIsSON(Eigen::Matrix< double, Dim, Dim, Eigen::ColMajor, Dim, Dim > const &t)
Eigen::Matrix< double, 3, 3 > getTransformationFromSingleBase3D(Parameter< double > const &unit_direction, SpatialPosition const &pos)
std::array< Parameter< double > const *, 3 > _base
Eigen::Matrix< double, Dimension, Dimension > transformation(SpatialPosition const &pos) const
Eigen::Matrix< double, 3, 3 > transformationFromSingleBase_3d(SpatialPosition const &pos) const
Eigen::Matrix< double, 3, 3 > transformation_3d(SpatialPosition const &pos) const
Eigen::Matrix< double, Dimension, Dimension > rotateTensor(std::vector< double > const &values, SpatialPosition const &pos) const
Eigen::Matrix< double, Dimension, Dimension > rotateDiagonalTensor(std::vector< double > const &values, SpatialPosition const &pos) const
CoordinateSystem(Parameter< double > const &unit_direction)