GetFEM  5.5
bgeot_geotrans_inv.cc
1 /*===========================================================================
2 
3  Copyright (C) 2000-2026 Yves Renard
4 
5  This file is a part of GetFEM
6 
7  GetFEM is free software; you can redistribute it and/or modify it
8  under the terms of the GNU Lesser General Public License as published
9  by the Free Software Foundation; either version 3 of the License, or
10  (at your option) any later version along with the GCC Runtime Library
11  Exception either version 3.1 or (at your option) any later version.
12  This program is distributed in the hope that it will be useful, but
13  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15  License and GCC Runtime Library Exception for more details.
16  You should have received a copy of the GNU Lesser General Public License
17  along with this program. If not, see https://www.gnu.org/licenses/.
18 
19 ===========================================================================*/
20 
23 #include "getfem/bgeot_torus.h"
24 #include "gmm/gmm_solver_bfgs.h"
25 
26 
27 namespace bgeot
28 {
29  void project_into_convex(base_node &x, const pgeometric_trans pgt) {
30 
31  for (auto &coord : x) {
32  if (coord < 0.0) coord = 0.0;
33  if (coord > 1.0) coord = 1.0;
34  }
35 
36 
37  auto pgt_torus = std::dynamic_pointer_cast<const torus_geom_trans>(pgt);
38  const pgeometric_trans
39  orig_pgt = pgt_torus ? pgt_torus->get_original_transformation()
40  : pgt;
41 
42  auto pbasic_convex_ref = basic_convex_ref(orig_pgt->convex_ref());
43  auto nb_simplices = pbasic_convex_ref->simplexified_convex()->nb_convex();
44 
45  if (nb_simplices == 1) { // simplex
46  auto sum_coordinates = 0.0;
47  for (const auto &coord : x) sum_coordinates += coord;
48 
49  if (sum_coordinates > 1.0) gmm::scale(x, 1.0 / sum_coordinates);
50  }
51  else if (pgt->dim() == 3 && nb_simplices != 4) { // prism
52  auto sum_coordinates = x[0] + x[1];
53  if (sum_coordinates > 1.0) {
54  x[0] /= sum_coordinates;
55  x[1] /= sum_coordinates;
56  }
57  }
58  }
59 
61  scalar_type IN_EPS,
62  bool project_into_element) {
63  bool converged = true;
64  return invert(n, n_ref, converged, IN_EPS, project_into_element);
65  }
66 
68  bool &converged,
69  scalar_type IN_EPS,
70  bool project_into_element) {
71  assert(pgt);
72  n_ref.resize(pgt->structure()->dim());
73  converged = true;
74  if (pgt->is_linear())
75  return invert_lin(n, n_ref, IN_EPS);
76  else
77  return invert_nonlin(n, n_ref, IN_EPS, converged, false,
78  project_into_element);
79  }
80 
81  /* inversion for linear geometric transformations */
82  bool geotrans_inv_convex::invert_lin(const base_node& n, base_node& n_ref,
83  scalar_type IN_EPS) {
84  base_node y(n); for (size_type i=0; i < N; ++i) y[i] -= G(i,0);
85  mult(transposed(B), y, n_ref);
86  y = pgt->transform(n_ref, G);
87  add(gmm::scaled(n, -1.0), y);
88 
89  return (pgt->convex_ref()->is_in(n_ref) < IN_EPS) &&
90  (gmm::vect_norm2(y) < IN_EPS);
91  }
92 
93  bool geotrans_inv_convex::update_B() {
94  if (P != N) {
95  pgt->compute_K_matrix(G, pc, K);
96  gmm::mult(gmm::transposed(K), K, CS);
97  double det = bgeot::lu_inverse(&(*(CS.begin())), P, false);
98  if (det == 0)
99  return false;
100  gmm::mult(K, CS, B);
101  } else {
102  // L'inversion peut être optimisée par le non calcul global de B
103  // et la resolution d'un système linéaire.
104  base_matrix KT(K.nrows(), K.ncols());
105  pgt->compute_K_matrix(G, pc, KT);
106  gmm::copy(gmm::transposed(KT), K);
107  gmm::copy(K, B);
108  double det = bgeot::lu_inverse(&(*(K.begin())), P, false);
109  if (det == 0)
110  return false;
111  B.swap(K);
112  }
113  return true;
114  }
115 
116  class geotrans_inv_convex_bfgs {
117  geotrans_inv_convex &gic;
118  base_node xreal;
119  public:
120  geotrans_inv_convex_bfgs(geotrans_inv_convex &gic_,
121  const base_node &xr) : gic(gic_), xreal(xr) {}
122  scalar_type operator()(const base_node& x) const {
123  base_node r = gic.pgt->transform(x, gic.G) - xreal;
124  return gmm::vect_norm2_sqr(r)/2.;
125  }
126  void operator()(const base_node& x, base_small_vector& gr) const {
127  gic.pgt->poly_vector_grad(x, gic.pc);
128  gic.update_B();
129  base_node r = gic.pgt->transform(x, gic.G) - xreal;
130  gr.resize(x.size());
131  gmm::mult(gmm::transposed(gic.K), r, gr);
132  }
133  };
134 
135  void geotrans_inv_convex::update_linearization() {
136 
137  const convex_ind_ct &dir_pt_ind = pgt->structure()->ind_dir_points();
138  const stored_point_tab &ref_nodes = pgt->geometric_nodes();
139 
140  has_linearized_approx = true;
141 
142  auto n_points = dir_pt_ind.size();
143  auto N_ref = ref_nodes.begin()->size();
144 
145  std::vector<base_node> dir_pts, dir_pts_ref;
146  for (auto i : dir_pt_ind) {
147  dir_pts.push_back(base_node(N));
148  gmm::copy(mat_col(G, i), dir_pts.back());
149  dir_pts_ref.push_back(ref_nodes[i]);
150  }
151 
152  base_matrix K_lin(N, n_points - 1),
153  B_transp_lin(n_points - 1, N),
154  K_ref_lin(N_ref, n_points - 1);
155 
156  P_lin = dir_pts[0];
157  P_ref_lin = dir_pts_ref[0];
158 
159  for (size_type i = 1; i < n_points; ++i) {
160  add(dir_pts[i], gmm::scaled(P_lin, -1.0), mat_col(K_lin, i - 1));
161  add(dir_pts_ref[i], gmm::scaled(P_ref_lin, -1.0),
162  mat_col(K_ref_lin, i - 1));
163  }
164 
165  if (K_lin.nrows() == K_lin.ncols()) {
166  lu_inverse(K_lin);
167  gmm::copy(K_lin, B_transp_lin);
168  }
169  else {
170  base_matrix temp(n_points - 1, n_points - 1);
171  mult(transposed(K_lin), K_lin, temp);
172  lu_inverse(temp);
173  mult(temp, transposed(K_lin), B_transp_lin);
174  }
175 
176  K_ref_B_transp_lin.base_resize(N_ref, N);
177  mult(K_ref_lin, B_transp_lin, K_ref_B_transp_lin);
178  }
179 
180 
181  /* inversion for non-linear geometric transformations
182  (Newton on Grad(pgt)(y - pgt(x)) = 0 )
183  */
184  bool geotrans_inv_convex::invert_nonlin(const base_node& xreal,
185  base_node& xref,
186  scalar_type IN_EPS,
187  bool &converged,
188  bool /* throw_except */,
189  bool project_into_element) {
190  converged = false;
191  base_node x0_ref(P), x0_real(N), diff(N);
192  scalar_type maxnormG = gmm::mat_maxnorm(G); // element size
193  { // find initial guess
194  x0_ref = pgt->geometric_nodes()[0];
195  scalar_type res = gmm::vect_dist2(mat_col(G, 0), xreal);
196  for (size_type j = 1; j < pgt->nb_points(); ++j) {
197  scalar_type res0 = gmm::vect_dist2(mat_col(G, j), xreal);
198  if (res0 < res) {
199  res = res0;
200  x0_ref = pgt->geometric_nodes()[j];
201  }
202  }
203  if (res < maxnormG * IN_EPS/100.) {
204  gmm::copy(x0_ref, xref);
205  if (project_into_element) project_into_convex(xref, pgt);
206  x0_real = pgt->transform(xref, G);
207  add(x0_real, gmm::scaled(xreal, -1.0), diff);
208  res = gmm::vect_norm2(diff);
209  if (res < maxnormG * IN_EPS/100. &&
210  pgt->convex_ref()->is_in(xref) < IN_EPS) {
211  converged = true;
212  return true;
213  }
214  }
215 
216  scalar_type res0 = std::numeric_limits<scalar_type>::max();
217  if (has_linearized_approx) {
218 
219  add(xreal, gmm::scaled(P_lin, -1.0), diff);
220  mult(K_ref_B_transp_lin, diff, xref);
221  gmm::add(P_ref_lin, xref);
222 
223  if (project_into_element) project_into_convex(xref, pgt);
224  res0 = gmm::vect_dist2(pgt->transform(xref, G), xreal);
225  }
226 
227  if (res < res0) gmm::copy(x0_ref, xref);
228  if (res < IN_EPS) // TODO: fix this too intrusive hack
229  xref *= 0.999888783; // For pyramid element to avoid the singularity
230  }
231 
232  add(pgt->transform(xref, G), gmm::scaled(xreal, -1.0), diff);
233  scalar_type res = gmm::vect_norm2(diff);
234  scalar_type res0 = std::numeric_limits<scalar_type>::max();
235  scalar_type factor = 1.0;
236 
237  while (res >= maxnormG * IN_EPS/100.) {
238  if ((gmm::abs(res - res0) < IN_EPS/100.) || (factor < IN_EPS)) { // stalled?
239  // relaxed convergence criterion depending on the size and position
240  // of the real element
241  converged = (res < maxnormG * IN_EPS/100.);
242  return converged && (pgt->convex_ref()->is_in(xref) < IN_EPS);
243  }
244  if (res > res0) {
245  add(gmm::scaled(x0_ref, factor), xref);
246  x0_real = pgt->transform(xref, G);
247  add(x0_real, gmm::scaled(xreal, -1.0), diff);
248  factor *= 0.5;
249  } else {
250  if (factor < 1.0-IN_EPS) factor *= 2.0;
251  res0 = res;
252  }
253  pgt->poly_vector_grad(xref, pc);
254  if (!update_B()) {
255  converged = false;
256  return false;
257  }
258  mult(transposed(B), diff, x0_ref);
259  add(gmm::scaled(x0_ref, -factor), xref);
260  if (project_into_element) project_into_convex(xref, pgt);
261  x0_real = pgt->transform(xref, G);
262  add(x0_real, gmm::scaled(xreal, -1.0), diff);
263  res = gmm::vect_norm2(diff);
264  }
265  converged = true;
266  return (pgt->convex_ref()->is_in(xref) < IN_EPS);
267  }
268 
269 } /* end of namespace bgeot. */
Inversion of geometric transformations.
Mesh structure definition.
Provides mesh of torus.
bool invert(const base_node &n, base_node &n_ref, scalar_type IN_EPS=1e-12, bool project_into_element=false)
given the node on the real element, returns the node on the reference element (even if it is outside ...
void copy(const L1 &l1, L2 &l2)
*‍/
Definition: gmm_blas.h:976
number_traits< typename linalg_traits< V >::value_type >::magnitude_type vect_norm2(const V &v)
Euclidean norm of a vector.
Definition: gmm_blas.h:556
number_traits< typename linalg_traits< M >::value_type >::magnitude_type mat_maxnorm(const M &m)
*‍/
Definition: gmm_blas.h:869
number_traits< typename linalg_traits< V >::value_type >::magnitude_type vect_norm2_sqr(const V &v)
squared Euclidean norm of a vector.
Definition: gmm_blas.h:543
number_traits< typename linalg_traits< V1 >::value_type >::magnitude_type vect_dist2(const V1 &v1, const V2 &v2)
Euclidean distance between two vectors.
Definition: gmm_blas.h:596
void mult(const L1 &l1, const L2 &l2, L3 &l3)
*‍/
Definition: gmm_blas.h:1663
void add(const L1 &l1, L2 &l2)
*‍/
Definition: gmm_blas.h:1275
Implements BFGS (Broyden, Fletcher, Goldfarb, Shanno) algorithm.
Basic Geometric Tools.
size_t size_type
used as the common size type in the library
Definition: bgeot_poly.h:48
std::shared_ptr< const bgeot::geometric_trans > pgeometric_trans
pointer type for a geometric transformation
pconvex_ref basic_convex_ref(pconvex_ref cvr)
return the associated order 1 reference convex.