G+Smo  24.08.0
Geometry + Simulation Modules
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
gsRemapInterface.hpp
1 
15 #pragma once
16 
17 #include <gsCore/gsMultiPatch.h>
18 #include <gsUtils/gsSortedVector.h>
21 
22 namespace gismo {
23 
24 template <class T>
26  const gsMultiBasis<T> & basis,
27  const boundaryInterface & bi,
28  const gsOptionList & opt)
29  : m_g1(&(mp[bi.first().patch])), m_g2(&(mp[bi.second().patch])),
30  m_b1(&(basis[bi.first().patch])), m_b2(&(basis[bi.second().patch])),
31  m_bi(bi),
32  m_isMatching(true), m_isAffine(true),
33  m_equalityTolerance(opt.getReal("EqualityTolerance")), m_newtonTolerance(opt.getReal("NewtonTolerance"))
34 {
35  const index_t checkAffine = opt.getInt("CheckAffine");
36  const index_t numSamplePoints = opt.getInt("NumSamplePoints");
37  const index_t intervalsOfFittingCurve = opt.getInt("IntervalsOfFittingCurve");
38  const index_t degreeOfFittingCurve = opt.getInt("DegreeOfFittingCurve");
39 
40  GISMO_ASSERT( m_g1->geoDim()==m_g2->geoDim(), "gsRemapInterface: Dimensions do not agree." );
41 
42  // First we construct the affine mapping
44 
45  // Setup the affine mapping
47  bi.dirMap(m_bi.first()),
48  bi.dirOrientation(m_bi.first()),
51  );
52 
53  // Next, we check (if so desired by called) if the affine mapping coincides with the real mapping
54  GISMO_ASSERT( checkAffine > 0 || checkAffine == neverAffine || checkAffine == alwaysAffine,
55  "gsRemapInterface: Parameter checkAffine has invalid value:" << checkAffine );
56  if (checkAffine==neverAffine)
57  m_isAffine = false;
58  else if (checkAffine > 0)
60 
61  if (!m_isAffine)
62  {
64  "gsRemapInterface: Can handle non-affine interfaces only for 2 dimensions." );
65 
66  constructFittingCurve(numSamplePoints, intervalsOfFittingCurve, degreeOfFittingCurve);
67  }
68 
70 
71 }
72 
73 namespace {
74 
75 template <class T>
76 gsMatrix<T> determineCorners(const gsGeometry<T> & geo, const boxSide s)
77 {
78  gsMatrix<T> pr = geo.parameterRange();
79  const index_t dim = pr.rows();
80  gsVector<T> lower(dim);
81  gsVector<T> upper(dim);
82  gsVector<unsigned> numberGridPoints(dim);
83  for (index_t i = 0; i<dim; ++i)
84  {
85  if (s.direction()==i)
86  {
87  lower[i] = upper[i] = pr( i, s.parameter() );
88  numberGridPoints[i] = 1;
89  }
90  else
91  {
92  lower[i] = pr(i, 0);
93  upper[i] = pr(i, 1);
94  numberGridPoints[i] = 2;
95  }
96  }
97  return gsPointGrid(lower,upper,numberGridPoints);
98 }
99 
100 template <class T>
101 void transferCorners(const gsMatrix<T> &corners, const gsGeometry<T> &g1, const gsGeometry<T> &g2,
102  const T newtonTolerance, const T equalityTolerance, gsMatrix<T> &cornerstransferred, gsVector<bool> &converged)
103 {
104  cornerstransferred.resize(corners.rows(), corners.cols());
105  converged.resize(corners.cols());
106 
107  gsVector<T> cornerTransferred = g2.parameterRange().col(0);
108  for (index_t i=0; i<corners.cols(); ++i)
109  {
110  gsVector<T> cornerPhysical = g1.eval(corners.col(i));
111  g2.newtonRaphson(cornerPhysical, cornerTransferred, true, newtonTolerance, 100);
112  cornerstransferred.col(i) = cornerTransferred;
113  converged[i] = (cornerPhysical - g2.eval(cornerTransferred)).norm() < equalityTolerance;
114  }
115 }
116 
117 template <class Vector, class T>
118 void widenParameterBounds(const Vector &point, gsMatrix<T> &parameterBounds)
119 {
120  if (parameterBounds.cols()==0)
121  {
122  parameterBounds.resize(point.rows(),2);
123  parameterBounds.col(0) = point;
124  parameterBounds.col(1) = point;
125  }
126  else
127  {
128  for (index_t i=0; i<parameterBounds.rows(); ++i)
129  {
130  parameterBounds(i,0) = std::min( parameterBounds(i,0), point(i,0) );
131  parameterBounds(i,1) = std::max( parameterBounds(i,1), point(i,0) );
132  }
133  }
134 
135 }
136 
137 } // end anonymous namespace
138 
139 template <class T>
141 {
142  gsMatrix<T> corners1 = determineCorners(*m_g1,m_bi.first());
143  gsMatrix<T> corners2 = determineCorners(*m_g2,m_bi.second());
144 
145  // Transfer the corners to the other patch and determine if the Newton
146  // did converge
147  gsMatrix<T> corners1transferredTo2, corners2transferredTo1;
148  gsVector<bool> converged1, converged2;
149  transferCorners(corners1, *m_g1, *m_g2, m_newtonTolerance, m_equalityTolerance, corners1transferredTo2, converged1);
150  transferCorners(corners2, *m_g2, *m_g1, m_newtonTolerance, m_equalityTolerance, corners2transferredTo1, converged2);
151 
152  // The parameter bounds are set such that all corners that are located on
153  // the (common part of the) interface are in the parameterBound
154  m_isMatching = true;
155  for (index_t i=0; i<corners1.cols(); ++i)
156  {
157  if (converged1[i])
158  {
159  widenParameterBounds(corners1.col(i), m_parameterBounds1);
160  widenParameterBounds(corners1transferredTo2.col(i), m_parameterBounds2);
161  }
162  else
163  {
164  m_isMatching = false;
165  }
166  }
167 
168  for (index_t i=0; i<corners2.cols(); ++i)
169  {
170  if (converged2[i])
171  {
172  widenParameterBounds(corners2.col(i), m_parameterBounds2);
173  widenParameterBounds(corners2transferredTo1.col(i), m_parameterBounds1);
174  }
175  else
176  {
177  m_isMatching = false;
178  }
179  }
180 
181  // Make sure that the proper value is chosen in the normal direction
182  const index_t d1 = m_bi.first().direction();
183  m_parameterBounds1.row(d1).setConstant(m_g1->parameterRange()(d1,m_bi.first().parameter()));
184 
185  const index_t d2 = m_bi.second().direction();
186  m_parameterBounds2.row(d2).setConstant(m_g2->parameterRange()(d2,m_bi.second().parameter()));
187 
188  GISMO_ASSERT ( m_parameterBounds1.cols()&&m_parameterBounds2.cols(),
189  "gsRemapInterface<T>::constructInterfaceBox: Could not find an interface.");
190  for (index_t j=0; j<domainDim(); ++j)
191  {
192  GISMO_ASSERT ( j==m_bi.first().direction() ||m_parameterBounds1(j,0) < m_parameterBounds1(j,1),
193  "gsRemapInterface<T>::constructInterfaceBox: Could not find an interface.");
194  GISMO_ASSERT ( j==m_bi.second().direction()||m_parameterBounds2(j,0) < m_parameterBounds2(j,1),
195  "gsRemapInterface<T>::constructInterfaceBox: Could not find an interface.");
196  }
197 
198 }
199 
200 template <class T>
202 {
203  gsVector<T> lower = m_parameterBounds1.col(0);
204  gsVector<T> upper = m_parameterBounds1.col(1);
205  gsVector<unsigned> numberGridPoints = gsVector<unsigned>::Constant(domainDim(),2+steps);
206  numberGridPoints[m_bi.first().direction()] = 1;
207  gsMatrix<T> points1 = gsPointGrid(lower,upper,numberGridPoints);
208  gsMatrix<T> points2;
209  eval_into(points1, points2);
210 
211  return (
212  m_g1->eval(points1)
213  -
214  m_g2->eval(points2)
215  ).template lpNorm<gsEigen::Infinity>();
216 }
217 
218 namespace {
219 
220 // Takes the univariate parameter on interface and returns the corresponding (bivariate)
221 // points on parameter domain
222 template <class T>
223 void enrichToVector(const gsMatrix<T> & u,
224  const index_t direction,
225  const gsMatrix<T> & parameterBounds,
226  gsMatrix <T> & result)
227 {
228  const index_t dim = 2;
229  result.resize(dim, u.cols());
230  for (index_t i=0; i<dim; ++i)
231  {
232  if (direction==i)
233  result.row(i).setConstant( parameterBounds(i,0) );
234  else
235  result.row(i) = u;
236  }
237 }
238 
239 } // End anonyomous namespace
240 
241 template <class T>
243  const index_t intervalsOfFittingCurve,
244  const index_t degreeOfFittingCurve)
245 {
246  // assert domainDim()==2 taken care by constructor
247 
248  const short_t direction1 = m_bi.first().direction();
249 
250  // In 2d, there is one tangential direction
251  const short_t tangential1 = ! direction1;
252 
253  // In 2d, the second side could have same or flipped direction
254  const bool flipSide2 = (m_bi.dirOrientation()(tangential1) == 0);
255 
256  // Setup of point grid on patch1
257  gsVector<unsigned> numPoints = gsVector<unsigned>::Constant(2,numSamplePoints);
258  numPoints[direction1] = 1;
259  gsVector<T> lower = m_parameterBounds1.col(0);
260  gsVector<T> upper = m_parameterBounds1.col(1);
261  gsMatrix<T> points = gsPointGrid(lower, upper, numPoints);
262 
263  // Map points to physical domain
264  gsMatrix<T> physPoints = m_g1->eval(points);
265 
266  // Map the points to patch2
267  gsMatrix<T> B(numSamplePoints, 2);
268  // Find a suitable start value for the Newton iteration. After the first iteration,
269  // we just continue where we are.
270  gsVector<T> pointMapped = m_parameterBounds2.col(flipSide2 ? 1 : 0);
271  for (index_t i = 0; i < numSamplePoints; ++i)
272  {
273  m_g2->newtonRaphson(physPoints.col(i), pointMapped, true, m_newtonTolerance, 100);
274  GISMO_ASSERT ( (physPoints.col(i)-m_g2->eval(pointMapped)).norm() <= m_equalityTolerance,
275  "gsRemapInterface<T>::constructFittingCurve: Newton did not converge." );
276  B.row(i) = pointMapped.transpose();
277  }
278 
279  // Use equidstant knot vector for fitting curve
280  gsKnotVector<T> KV(m_parameterBounds1(tangential1, 0), m_parameterBounds1(tangential1, 1),
281  intervalsOfFittingCurve, degreeOfFittingCurve+1);
282  gsCurveFitting<T> fit(points.row(tangential1).transpose(), B, KV);
283  fit.compute();
284 
285  m_intfMap = fit.curve().clone();
286 
287 }
288 
289 namespace {
290 
291 template <class T, class Vector>
292 inline void addBreaks(std::vector< std::vector<T> > & breaks,
293  const gsMatrix<T> & parameterBounds,
294  const Vector & point,
295  const T equalityTolerance)
296 {
297  const index_t dim = point.rows();
298  for (index_t d=0; d<dim; ++d)
299  {
300  const T t = point(d,0);
301  if ( parameterBounds(d,0) <= t && t <= parameterBounds(d,1) )
302  {
303  // As in gsSortedVector::push_sorted_unique
304  typename std::vector<T>::iterator pos = std::lower_bound(breaks[d].begin(), breaks[d].end(), t-equalityTolerance );
305  if ( pos == breaks[d].end() || *pos > t+equalityTolerance ) // If not found
306  breaks[d].insert(pos, t);
307  }
308  }
309 }
310 
311 template <class T, class Vector>
312 inline bool inRange(const gsMatrix<T> & range,
313  const Vector & point)
314 {
315  for (index_t i=0; i<range.rows(); ++i)
316  if ( point(i,0) < range(i,0) || point(i,0) > range(i,1) )
317  return false;
318  return true;
319 }
320 
321 
322 } // end anonymous namespace
323 
324 template <class T>
326 {
327  m_breakpoints.resize(domainDim());
328 
329  const typename gsBasis<T>::domainIter domIt1 = m_b1->makeDomainIterator(m_bi.first());
330  addBreaks(m_breakpoints, m_parameterBounds1, m_parameterBounds1.col(0), m_equalityTolerance);
331  for (; domIt1->good(); domIt1->next())
332  addBreaks(m_breakpoints, m_parameterBounds1, domIt1->upperCorner(), m_equalityTolerance);
333  addBreaks(m_breakpoints, m_parameterBounds1, m_parameterBounds1.col(1), m_equalityTolerance);
334 
335  const typename gsBasis<T>::domainIter domIt2 = m_b2->makeDomainIterator(m_bi.second());
336  if (m_isAffine)
337  {
338  gsAffineFunction<T> intfMap_inverse(m_bi.dirMap(m_bi.second()), m_bi.dirOrientation(m_bi.second()),
339  m_parameterBounds2, m_parameterBounds1);
340 
341  for (; domIt2->good(); domIt2->next())
342  addBreaks(m_breakpoints, m_parameterBounds1, intfMap_inverse.eval(domIt2->upperCorner()), m_equalityTolerance);
343  }
344  else
345  {
346  gsVector<T> breakpoint_transferred = m_parameterBounds1.col(0); // initial guess
347  for (; domIt2->good(); domIt2->next())
348  {
349  if (inRange(m_parameterBounds2, domIt2->upperCorner()))
350  {
351  gsMatrix<T> breakpoint_phys = m_g2->eval(domIt2->upperCorner());
352 
353  m_g1->newtonRaphson( breakpoint_phys, breakpoint_transferred, true, m_newtonTolerance, 100 );
354  GISMO_ASSERT ( (breakpoint_phys-m_g1->eval(breakpoint_transferred)).norm() <= m_equalityTolerance,
355  "gsRemapInterface::constructBreaks: Newton method did not find point in parameter domain."
356  << (breakpoint_phys-m_g1->eval(breakpoint_transferred)).norm() );
357 
358  addBreaks(m_breakpoints, m_parameterBounds1, breakpoint_transferred, m_equalityTolerance);
359  }
360  }
361  }
362 
363 }
364 
365 namespace {
366 
367 // Check if incoming parameters are located on interface
368 template <class T>
369 bool checkIfOnInterface(const gsMatrix<T> & u,
370  const gsMatrix<T> & parameterBounds,
371  const T equalityTolerance)
372 {
373  for (index_t r=0; r<u.rows(); ++r)
374  {
375  const T begin = parameterBounds(r, 0);
376  const T end = parameterBounds(r, 1);
377  for (index_t c=0; c<u.cols(); ++c)
378  {
379  if ( u(r,c) < begin-equalityTolerance || u(r,c) > end+equalityTolerance )
380  return false;
381  }
382  }
383 
384  return true;
385 }
386 
387 // Map the parameterization onto interface
388 template <class T>
389 void projectOntoInterface(gsMatrix<T> & u, const gsMatrix<T> & parameterBounds)
390 {
391  for(index_t r = 0; r < u.rows(); r++)
392  {
393  const T begin = parameterBounds(r, 0);
394  const T end = parameterBounds(r, 1);
395 
396  for(index_t c = 0; c < u.cols(); c++)
397  u(r,c) = std::min( std::max( u(r,c), begin ), end );
398  }
399 }
400 
401 
402 } // End anonyomous namespace
403 
404 template <class T>
406 {
407  GISMO_ASSERT( u.rows() == domainDim(), "gsRemapInterface::eval_into: "
408  "The rows of the evaluation points must be equal to the dimension of the domain." );
409 
410  GISMO_ASSERT( checkIfOnInterface( u, m_parameterBounds1, m_equalityTolerance ),
411  "gsRemapInterface::eval_into: The incoming coefficients are not located on the interface."
412  <<"\nu=\n"<<u<<"\nm_parameterBounds1=\n"<<m_parameterBounds1);
413 
414  if (m_isAffine)
415  {
416  m_intfMap->eval_into(u, result);
417  }
418  else
419  {
420  gsMatrix<T> uProjected = u;
421  projectOntoInterface(uProjected, m_parameterBounds1);
422 
423  // Since we are in 2D, there is exactly one tangential direction
424  const index_t direction1 = m_bi.first().direction();
425  const index_t tangential1 = ! direction1;
426  m_intfMap->eval_into(uProjected.row(tangential1),result);
427 
428  projectOntoInterface(result, m_parameterBounds2);
429  }
430 }
431 
432 
433 template <class T>
435 {
437  for (index_t i=0; i<domainDim(); ++i)
438  {
439  if (i!=m_bi.first().direction())
440  tdi->setBreaks(m_breakpoints[i],i);
441  }
442  return typename gsDomainIterator<T>::uPtr(tdi);
443 }
444 
445 
446 template <class T>
447 std::ostream& gsRemapInterface<T>::print(std::ostream & os) const
448 {
449  os << "gsRemapInterface:"
450  << "\n First side: " << m_bi.first()
451  << "\n Second side: " << m_bi.second()
452  << "\n Is Affine: " << ( m_isAffine ? "yes" : "no")
453  << "\n Is Matching: " << ( m_isMatching ? "yes" : "no")
454  << "\n Bounding box 1 min: " << m_parameterBounds1.transpose().row(0)
455  << "\n max: " << m_parameterBounds1.transpose().row(1)
456  << "\n Bounding box 2 min: " << m_parameterBounds2.transpose().row(0)
457  << "\n max: " << m_parameterBounds2.transpose().row(1);
458 
459  for (size_t i=0; i<m_breakpoints.size(); ++i)
460  {
461  os << "\n Beakpoints " << (i<3?char('x'+i):' ') << ": ";
462  if ( m_breakpoints[i].size() <= 10 )
463  {
464  for (size_t j=0; j<m_breakpoints[i].size(); ++j)
465  os << " " << m_breakpoints[i][j];
466  }
467  else
468  {
469  for (size_t j=0; j<5; ++j)
470  os << " " << m_breakpoints[i][j];
471  os << " ...";
472  for (size_t j=m_breakpoints[i].size()-5; j<m_breakpoints[i].size(); ++j)
473  os << " " << m_breakpoints[i][j];
474  }
475  }
476  if (!m_isAffine)
477  os << "\n Fitting curve:\n" << *m_intfMap;
478  os << "\n Error of reparam: " << estimateReparamError(20);
479 
480  os << "\n";
481  return os;
482 }
483 
484 
485 } // End namespace gismo
boundaryInterface m_bi
Corresponding boundary interface.
Definition: gsRemapInterface.h:168
Abstract base class representing a geometry map.
Definition: gsGeometry.h:92
void constructBreaks()
Constructs the breakpoints m_breakpoints.
Definition: gsRemapInterface.hpp:325
gsMatrix< T > parameterRange() const
Returns the range of parameters as a matrix with two columns, [lower upper].
Definition: gsGeometry.hpp:198
void constructFittingCurve(index_t numSamplePoints, index_t intervalsOfFittingCurve, index_t degreeOfFittingCurve)
Constructs the fitting curve m_intfMap in the non-affine case.
Definition: gsRemapInterface.hpp:242
gsFunction< T >::Ptr m_intfMap
Iff affine, interface map, otherwise the fitting curve.
Definition: gsRemapInterface.h:175
#define short_t
Definition: gsConfig.h:35
bool parameter() const
Returns the parameter value (false=0=start, true=1=end) that corresponds to this side.
Definition: gsBoundary.h:128
virtual void eval_into(const gsMatrix< T > &u, gsMatrix< T > &result) const
Evaluates the interface map.
Definition: gsRemapInterface.hpp:405
gsMatrix< T > eval(const gsMatrix< T > &u) const
Evaluate the function,.
Definition: gsFunctionSet.hpp:120
#define index_t
Definition: gsConfig.h:32
#define GISMO_ENSURE(cond, message)
Definition: gsDebug.h:102
gsMatrix< T > m_parameterBounds1
The bounds of the box that represents .
Definition: gsRemapInterface.h:182
const index_t & getInt(const std::string &label) const
Reads value for option label from options.
Definition: gsOptionList.cpp:37
#define GISMO_ASSERT(cond, message)
Definition: gsDebug.h:89
gsMatrix< T > m_parameterBounds2
The bounds of the box that represents .
Definition: gsRemapInterface.h:187
T m_equalityTolerance
Tolerance for considering points to be equal.
Definition: gsRemapInterface.h:189
memory::unique_ptr< gsDomainIterator > uPtr
Unique pointer for gsDomainIterator.
Definition: gsDomainIterator.h:73
void setBreaks(std::vector< T > newBreaks, index_t i)
Function to set the breakpoints in direction i manually.
Definition: gsTensorDomainBoundaryIterator.h:194
Representation of an affine function.
Definition: gsAffineFunction.h:29
Provides declaration of TensorNurbsBasis abstract interface.
Holds a set of patch-wise bases and their topology information.
Definition: gsMultiBasis.h:36
Provides declaration of the MultiPatch class.
Re-implements gsDomainIterator for iteration over all elements of the boundary of a tensor product pa...
Definition: gsTensorDomainBoundaryIterator.h:37
GISMO_DEPRECATED index_t direction(index_t s)
Returns the parametric direction that corresponds to side s.
Definition: gsBoundary.h:1048
gsMatrix< T > gsPointGrid(gsVector< T > const &a, gsVector< T > const &b, gsVector< unsigned > const &np)
Construct a Cartesian grid of uniform points in a hypercube, using np[i] points in direction i...
Definition: gsPointGrid.hpp:82
Class for performing a least squares fit to get a open/closed B-Spline curve for some given data...
Definition: gsCurveFitting.h:32
Fits a (closed) B-spline curve to some given data.
void compute()
computes the least squares fit for a (closed) B-spline curve
Definition: gsCurveFitting.h:100
T estimateReparamError(index_t steps) const
Estimates error between geometry1 and geometry2 after repearametrization on physical domain...
Definition: gsRemapInterface.hpp:201
Container class for a set of geometry patches and their topology, that is, the interface connections ...
Definition: gsMultiPatch.h:33
Struct which represents a certain side of a box.
Definition: gsBoundary.h:84
virtual short_t domainDim() const
Returns parameter dimension of the domains.
Definition: gsRemapInterface.h:125
virtual std::ostream & print(std::ostream &os) const
Prints the state of the object.
Definition: gsRemapInterface.hpp:447
static uPtr make(const gsMatrix< T > mat, const gsVector< T > trans)
Affine maps are the composition of a linear map with a translation this constructor takes the two com...
Definition: gsAffineFunction.h:75
An std::vector with sorting capabilities.
Class for representing a knot vector.
Definition: gsKnotVector.h:79
void constructInterfaceBox()
Computes the box which represents the intersection of sides of incoming patches.
Definition: gsRemapInterface.hpp:140
Instructs constructor always to use an affine mapping.
Definition: gsRemapInterface.h:67
Struct which represents an interface between two patches.
Definition: gsBoundary.h:649
const gsGeometry< T > * m_g1
Geometry of first patch.
Definition: gsRemapInterface.h:162
Class which holds a list of parameters/options, and provides easy access to them. ...
Definition: gsOptionList.h:32
const gsGeometry< T > * m_g2
Geometry of second patch.
Definition: gsRemapInterface.h:163
gsDomainIterator< T >::uPtr makeDomainIterator() const
Returns a domain iterator.
Definition: gsRemapInterface.hpp:434
bool m_isAffine
True iff the interface is affine.
Definition: gsRemapInterface.h:171
gsRemapInterface(const gsMultiPatch< T > &mp, const gsMultiBasis< T > &mb, const boundaryInterface &bi, const gsOptionList &opt=defaultOptions())
Constructor.
Definition: gsRemapInterface.hpp:25
short_t direction() const
Returns the parametric direction orthogonal to this side.
Definition: gsBoundary.h:113
Instructs constructor not to use an affine mapping.
Definition: gsRemapInterface.h:66
patchSide & first()
first, returns the first patchSide of this interface
Definition: gsBoundary.h:776