https://mooseframework.inl.gov
MathUtils.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://mooseframework.inl.gov
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
10 #include "MathUtils.h"
11 #include "MooseUtils.h"
12 
13 namespace MathUtils
14 {
15 
16 void
17 kron(RealEigenMatrix & product, const RealEigenMatrix & mat_A, const RealEigenMatrix & mat_B)
18 {
19  product.resize(mat_A.rows() * mat_B.rows(), mat_A.cols() * mat_B.cols());
20  for (unsigned int i = 0; i < mat_A.rows(); i++)
21  for (unsigned int j = 0; j < mat_A.cols(); j++)
22  for (unsigned int k = 0; k < mat_B.rows(); k++)
23  for (unsigned int l = 0; l < mat_B.cols(); l++)
24  product(((i * mat_B.rows()) + k), ((j * mat_B.cols()) + l)) = mat_A(i, j) * mat_B(k, l);
25 }
26 
27 Real
28 plainLog(Real x, unsigned int derivative_order)
29 {
30  switch (derivative_order)
31  {
32  case 0:
33  return std::log(x);
34 
35  case 1:
36  return 1.0 / x;
37 
38  case 2:
39  return -1.0 / (x * x);
40 
41  case 3:
42  return 2.0 / (x * x * x);
43  }
44 
45  mooseError("Unsupported derivative order ", derivative_order);
46 }
47 
48 Real
49 poly1Log(Real x, Real tol, unsigned int derivative_order)
50 {
51  if (x >= tol)
52  return plainLog(x, derivative_order);
53 
54  const auto c1 = [&]() { return 1.0 / tol; };
55  const auto c2 = [&]() { return std::log(tol) - 1.0; };
56 
57  switch (derivative_order)
58  {
59  case 0:
60  return c1() * x + c2();
61 
62  case 1:
63  return c1();
64 
65  case 2:
66  return 0.0;
67 
68  case 3:
69  return 0.0;
70  }
71 
72  mooseError("Unsupported derivative order ", derivative_order);
73 }
74 
75 Real
76 poly2Log(Real x, Real tol, unsigned int derivative_order)
77 {
78  if (x >= tol)
79  return plainLog(x, derivative_order);
80 
81  const auto c1 = [&]() { return -0.5 / (tol * tol); };
82  const auto c2 = [&]() { return 2.0 / tol; };
83  const auto c3 = [&]() { return std::log(tol) - 3.0 / 2.0; };
84 
85  switch (derivative_order)
86  {
87  case 0:
88  return c1() * x * x + c2() * x + c3();
89 
90  case 1:
91  return 2.0 * c1() * x + c2();
92 
93  case 2:
94  return 2.0 * c1();
95 
96  case 3:
97  return 0.0;
98  }
99 
100  mooseError("Unsupported derivative order ", derivative_order);
101 }
102 
103 Real
104 poly3Log(Real x, Real tol, unsigned int derivative_order)
105 {
106  if (x >= tol)
107  return plainLog(x, derivative_order);
108 
109  const auto c1 = [&]() { return 1.0 / (3.0 * tol * tol * tol); };
110  const auto c2 = [&]() { return -3.0 / (2.0 * tol * tol); };
111  const auto c3 = [&]() { return 3.0 / tol; };
112  const auto c4 = [&]() { return std::log(tol) - 11.0 / 6.0; };
113 
114  switch (derivative_order)
115  {
116  case 0:
117  return c1() * x * x * x + c2() * x * x + c3() * x + c4();
118 
119  case 1:
120  return 3.0 * c1() * x * x + 2.0 * c2() * x + c3();
121 
122  case 2:
123  return 6.0 * c1() * x + 2.0 * c2();
124 
125  case 3:
126  return 6.0 * c1();
127  }
128 
129  mooseError("Unsupported derivative order ", derivative_order);
130 }
131 
132 Real
133 poly4Log(Real x, Real tol, unsigned int derivative_order)
134 {
135  if (x >= tol)
136  return plainLog(x, derivative_order);
137 
138  switch (derivative_order)
139  {
140  case 0:
141  return std::log(tol) + (x - tol) / tol -
142  Utility::pow<2>(x - tol) / (2.0 * Utility::pow<2>(tol)) +
143  Utility::pow<3>(x - tol) / (3.0 * Utility::pow<3>(tol)) -
144  Utility::pow<4>(x - tol) / (4.0 * Utility::pow<4>(tol)) +
145  Utility::pow<5>(x - tol) / (5.0 * Utility::pow<5>(tol)) -
146  Utility::pow<6>(x - tol) / (6.0 * Utility::pow<6>(tol));
147 
148  case 1:
149  return 1.0 / tol - (x - tol) / Utility::pow<2>(tol) +
150  Utility::pow<2>(x - tol) / Utility::pow<3>(tol) -
151  Utility::pow<3>(x - tol) / Utility::pow<4>(tol) +
152  Utility::pow<4>(x - tol) / Utility::pow<5>(tol) -
153  Utility::pow<5>(x - tol) / Utility::pow<6>(tol);
154 
155  case 2:
156  return -1.0 / Utility::pow<2>(tol) + 2.0 * (x - tol) / Utility::pow<3>(tol) -
157  3.0 * Utility::pow<2>(x - tol) / Utility::pow<4>(tol) +
158  4.0 * Utility::pow<3>(x - tol) / Utility::pow<5>(tol) -
159  5.0 * Utility::pow<4>(x - tol) / Utility::pow<6>(tol);
160 
161  case 3:
162  return 2.0 / Utility::pow<3>(tol) - 6.0 * (x - tol) / Utility::pow<4>(tol) +
163  12.0 * Utility::pow<2>(x - tol) / Utility::pow<5>(tol) -
164  20.0 * Utility::pow<3>(x - tol) / Utility::pow<6>(tol);
165  }
166 
167  mooseError("Unsupported derivative order ", derivative_order);
168 }
169 
171 Real
172 taylorLog(Real x)
173 {
174  Real y = (x - 1.0) / (x + 1.0);
175  Real val = 1.0;
176  for (unsigned int i = 0; i < 5; ++i)
177  {
178  Real exponent = i + 2.0;
179  val += 1.0 / (2.0 * (i + 1.0) + 1.0) * std::pow(y, exponent);
180  }
181 
182  return val * 2.0 * y;
183 }
184 
185 std::vector<std::vector<unsigned int>>
186 multiIndex(unsigned int dim, unsigned int order)
187 {
188  // first row all zero
189  std::vector<std::vector<unsigned int>> multi_index;
190  std::vector<std::vector<unsigned int>> n_choose_k;
191  std::vector<unsigned int> row(dim, 0);
192  multi_index.push_back(row);
193 
194  if (dim == 1)
195  for (unsigned int q = 1; q <= order; q++)
196  {
197  row[0] = q;
198  multi_index.push_back(row);
199  }
200  else
201  for (unsigned int q = 1; q <= order; q++)
202  {
203  n_choose_k = multiIndexHelper(dim + q - 1, dim - 1);
204  for (unsigned int r = 0; r < n_choose_k.size(); r++)
205  {
206  row.clear();
207  for (unsigned int c = 1; c < n_choose_k[0].size(); c++)
208  row.push_back(n_choose_k[r][c] - n_choose_k[r][c - 1] - 1);
209  multi_index.push_back(row);
210  }
211  }
212 
213  return multi_index;
214 }
215 
216 Point
217 barycentricToCartesian2D(const Point & p0,
218  const Point & p1,
219  const Point & p2,
220  const Real b0,
221  const Real b1,
222  const Real b2)
223 {
224  mooseAssert(!MooseUtils::isZero(b0 + b1 + b2 - 1.0), "Barycentric coordinates must sum to one!");
225 
226  Point center;
227 
228  for (unsigned int d = 0; d < 2; ++d)
229  center(d) = p0(d) * b0 + p1(d) * b1 + p2(d) * b2;
230  // p0, p1, p2 are vertices of triangle
231  // b0, b1, b2 are Barycentric coordinates of the triangle center
232 
233  return center;
234 }
235 
236 Point
237 barycentricToCartesian3D(const Point & p0,
238  const Point & p1,
239  const Point & p2,
240  const Point & p3,
241  const Real b0,
242  const Real b1,
243  const Real b2,
244  const Real b3)
245 {
246  mooseAssert(!MooseUtils::isZero(b0 + b1 + b2 + b3 - 1.0),
247  "Barycentric coordinates must sum to one!");
248 
249  Point center;
250 
251  for (unsigned int d = 0; d < 3; ++d)
252  center(d) = p0(d) * b0 + p1(d) * b1 + p2(d) * b2 + p3(d) * b3;
253  // p0, p1, p2, p3 are vertices of tetrahedron
254  // b0, b1, b2, b3 are Barycentric coordinates of the tetrahedron center
255 
256  return center;
257 }
258 
259 Point
260 circumcenter2D(const Point & p0, const Point & p1, const Point & p2)
261 {
262  // Square of triangle edge lengths
263  Real edge01 = (p0 - p1).norm_sq();
264  Real edge02 = (p0 - p2).norm_sq();
265  Real edge12 = (p1 - p2).norm_sq();
266 
267  // Barycentric weights for circumcenter
268  Real weight0 = edge12 * (edge01 + edge02 - edge12);
269  Real weight1 = edge02 * (edge01 + edge12 - edge02);
270  Real weight2 = edge01 * (edge02 + edge12 - edge01);
271 
272  Real sum_weights = weight0 + weight1 + weight2;
273 
274  // Check to make sure vertices are not collinear
275  if (MooseUtils::isZero(sum_weights))
276  mooseError("Cannot evaluate circumcenter. Points should be non-collinear.");
277 
278  Real inv_sum_weights = 1.0 / sum_weights;
279 
280  // Barycentric coordinates
281  Real b0 = weight0 * inv_sum_weights;
282  Real b1 = weight1 * inv_sum_weights;
283  Real b2 = weight2 * inv_sum_weights;
284 
285  return MathUtils::barycentricToCartesian2D(p0, p1, p2, b0, b1, b2);
286 }
287 
288 Point
289 circumcenter3D(const Point & p0, const Point & p1, const Point & p2, const Point & p3)
290 {
291  // Square of tetrahedron edge lengths
292  Real edge01 = (p0 - p1).norm_sq();
293  Real edge02 = (p0 - p2).norm_sq();
294  Real edge03 = (p0 - p3).norm_sq();
295  Real edge12 = (p1 - p2).norm_sq();
296  Real edge13 = (p1 - p3).norm_sq();
297  Real edge23 = (p2 - p3).norm_sq();
298 
299  // Barycentric weights for circumcenter
300  Real weight0 = -2 * edge12 * edge13 * edge23 + edge01 * edge23 * (edge13 + edge12 - edge23) +
301  edge02 * edge13 * (edge12 + edge23 - edge13) +
302  edge03 * edge12 * (edge13 + edge23 - edge12);
303  Real weight1 = -2 * edge02 * edge03 * edge23 + edge01 * edge23 * (edge02 + edge03 - edge23) +
304  edge13 * edge02 * (edge03 + edge23 - edge02) +
305  edge12 * edge03 * (edge02 + edge23 - edge03);
306  Real weight2 = -2 * edge01 * edge03 * edge13 + edge02 * edge13 * (edge01 + edge03 - edge13) +
307  edge23 * edge01 * (edge03 + edge13 - edge01) +
308  edge12 * edge03 * (edge01 + edge13 - edge03);
309  Real weight3 = -2 * edge01 * edge02 * edge12 + edge03 * edge12 * (edge01 + edge02 - edge12) +
310  edge23 * edge01 * (edge02 + edge12 - edge01) +
311  edge13 * edge02 * (edge01 + edge12 - edge02);
312 
313  Real sum_weights = weight0 + weight1 + weight2 + weight3;
314 
315  // Check to make sure vertices are not coplanar
316  if (MooseUtils::isZero(sum_weights))
317  mooseError("Cannot evaluate circumcenter. Points should be non-coplanar.");
318 
319  Real inv_sum_weights = 1.0 / sum_weights;
320 
321  // Barycentric coordinates
322  Real b0 = weight0 * inv_sum_weights;
323  Real b1 = weight1 * inv_sum_weights;
324  Real b2 = weight2 * inv_sum_weights;
325  Real b3 = weight3 * inv_sum_weights;
326 
327  return MathUtils::barycentricToCartesian3D(p0, p1, p2, p3, b0, b1, b2, b3);
328 }
329 
330 } // namespace MathUtils
331 
332 std::vector<std::vector<unsigned int>>
333 multiIndexHelper(unsigned int N, unsigned int K)
334 {
335  std::vector<std::vector<unsigned int>> n_choose_k;
336  std::vector<unsigned int> row;
337  std::string bitmask(K, 1); // K leading 1's
338  bitmask.resize(N, 0); // N-K trailing 0's
339 
340  do
341  {
342  row.clear();
343  row.push_back(0);
344  for (unsigned int i = 0; i < N; ++i) // [0..N-1] integers
345  if (bitmask[i])
346  row.push_back(i + 1);
347  row.push_back(N + 1);
348  n_choose_k.push_back(row);
349  } while (std::prev_permutation(bitmask.begin(), bitmask.end()));
350 
351  return n_choose_k;
352 }
Point circumcenter2D(const Point &p0, const Point &p1, const Point &p2)
Evaluate circumcenter of a triangle given three arbitrary points.
Definition: MathUtils.C:260
Point barycentricToCartesian2D(const Point &p0, const Point &p1, const Point &p2, const Real b0, const Real b1, const Real b2)
Evaluate Cartesian coordinates of any center point of a triangle given Barycentric coordinates of cen...
Definition: MathUtils.C:217
void kron(RealEigenMatrix &product, const RealEigenMatrix &mat_A, const RealEigenMatrix &mat_B)
Computes the Kronecker product of two matrices.
Definition: MathUtils.C:17
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:302
bool isZero(const T &value, const Real tolerance=TOLERANCE *TOLERANCE *TOLERANCE)
Definition: MooseUtils.h:691
Real poly1Log(Real x, Real tol, unsigned int derivative_order)
Definition: MathUtils.C:49
Real poly2Log(Real x, Real tol, unsigned int derivative_order)
Definition: MathUtils.C:76
static constexpr std::size_t dim
This is the dimension of all vector and tensor datastructures used in MOOSE.
Definition: Moose.h:154
Point barycentricToCartesian3D(const Point &p0, const Point &p1, const Point &p2, const Point &p3, const Real b0, const Real b1, const Real b2, const Real b3)
Evaluate Cartesian coordinates of any center point of a tetrahedron given Barycentric coordinates of ...
Definition: MathUtils.C:237
Real plainLog(Real x, unsigned int derivative_order)
Definition: MathUtils.C:28
Real taylorLog(Real x)
Definition: MathUtils.C:172
Eigen::Matrix< Real, Eigen::Dynamic, Eigen::Dynamic > RealEigenMatrix
Definition: MooseTypes.h:149
auto log(const T &)
Real poly3Log(Real x, Real tol, unsigned int derivative_order)
Definition: MathUtils.C:104
Point circumcenter3D(const Point &p0, const Point &p1, const Point &p2, const Point &p3)
Evaluate circumcenter of a tetrahedrom given four arbitrary points.
Definition: MathUtils.C:289
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
auto norm_sq(const T &a) -> decltype(std::norm(a))
std::vector< std::vector< unsigned int > > multiIndex(unsigned int dim, unsigned int order)
generate a complete multi index table for given dimension and order i.e.
Definition: MathUtils.C:186
std::vector< std::vector< unsigned int > > multiIndexHelper(unsigned int N, unsigned int K)
A helper function for MathUtils::multiIndex.
Definition: MathUtils.C:333
Real poly4Log(Real x, Real tol, unsigned int derivative_order)
Definition: MathUtils.C:133
MooseUnits pow(const MooseUnits &, int)
Definition: Units.C:537
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template pow< 2 >(tan(_arg))+1.0) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(sqrt