G+Smo  25.01.0
Geometry + Simulation Modules
 
Loading...
Searching...
No Matches
gsRemapInterface.hpp
1
15#pragma once
16
17#include <gsCore/gsMultiPatch.h>
21
22namespace gismo {
23
24template <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
73namespace {
74
75template <class T>
76gsMatrix<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
100template <class T>
101void 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
117template <class Vector, class T>
118void 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
139template <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
200template <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
218namespace {
219
220// Takes the univariate parameter on interface and returns the corresponding (bivariate)
221// points on parameter domain
222template <class T>
223void 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
241template <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
289namespace {
290
291template <class T, class Vector>
292inline 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
311template <class T, class Vector>
312inline 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
324template <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
365namespace {
366
367// Check if incoming parameters are located on interface
368template <class T>
369bool 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
388template <class T>
389void 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
404template <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
433template <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
446template <class T>
447std::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
Struct which represents a certain side of a box.
Definition gsBoundary.h:85
bool parameter() const
Returns the parameter value (false=0=start, true=1=end) that corresponds to this side.
Definition gsBoundary.h:128
short_t direction() const
Returns the parametric direction orthogonal to this side.
Definition gsBoundary.h:113
Representation of an affine function.
Definition gsAffineFunction.h:30
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
Class for performing a least squares fit to get a open/closed B-Spline curve for some given data.
Definition gsCurveFitting.h:33
void compute()
computes the least squares fit for a (closed) B-spline curve
Definition gsCurveFitting.h:100
const gsBSpline< T > & curve() const
gives back the computed B-spline curve
Definition gsCurveFitting.h:72
memory::unique_ptr< gsDomainIterator > uPtr
Unique pointer for gsDomainIterator.
Definition gsDomainIterator.h:73
gsMatrix< T > eval(const gsMatrix< T > &u) const
Evaluate the function,.
Definition gsFunctionSet.hpp:120
Abstract base class representing a geometry map.
Definition gsGeometry.h:93
gsMatrix< T > parameterRange() const
Returns the range of parameters as a matrix with two columns, [lower upper].
Definition gsGeometry.hpp:198
Class for representing a knot vector.
Definition gsKnotVector.h:80
A matrix with arbitrary coefficient type and fixed or dynamic size.
Definition gsMatrix.h:41
Holds a set of patch-wise bases and their topology information.
Definition gsMultiBasis.h:37
Container class for a set of geometry patches and their topology, that is, the interface connections ...
Definition gsMultiPatch.h:100
Class which holds a list of parameters/options, and provides easy access to them.
Definition gsOptionList.h:33
const index_t & getInt(const std::string &label) const
Reads value for option label from options.
Definition gsOptionList.cpp:37
gsDomainIterator< T >::uPtr makeDomainIterator() const
Returns a domain iterator.
Definition gsRemapInterface.hpp:434
void constructInterfaceBox()
Computes the box which represents the intersection of sides of incoming patches.
Definition gsRemapInterface.hpp:140
boundaryInterface m_bi
Corresponding boundary interface.
Definition gsRemapInterface.h:168
gsMatrix< T > m_parameterBounds1
The bounds of the box that represents .
Definition gsRemapInterface.h:182
bool m_isAffine
True iff the interface is affine.
Definition gsRemapInterface.h:171
virtual short_t domainDim() const
Returns parameter dimension of the domains.
Definition gsRemapInterface.h:125
gsFunction< T >::Ptr m_intfMap
Iff affine, interface map, otherwise the fitting curve.
Definition gsRemapInterface.h:175
virtual void eval_into(const gsMatrix< T > &u, gsMatrix< T > &result) const
Evaluates the interface map.
Definition gsRemapInterface.hpp:405
const gsGeometry< T > * m_g1
Geometry of first patch.
Definition gsRemapInterface.h:162
gsRemapInterface(const gsMultiPatch< T > &mp, const gsMultiBasis< T > &mb, const boundaryInterface &bi, const gsOptionList &opt=defaultOptions())
Constructor.
Definition gsRemapInterface.hpp:25
gsMatrix< T > m_parameterBounds2
The bounds of the box that represents .
Definition gsRemapInterface.h:187
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
T m_equalityTolerance
Tolerance for considering points to be equal.
Definition gsRemapInterface.h:189
virtual std::ostream & print(std::ostream &os) const
Prints the state of the object.
Definition gsRemapInterface.hpp:447
const gsGeometry< T > * m_g2
Geometry of second patch.
Definition gsRemapInterface.h:163
@ neverAffine
Instructs constructor not to use an affine mapping.
Definition gsRemapInterface.h:66
@ alwaysAffine
Instructs constructor always to use an affine mapping.
Definition gsRemapInterface.h:67
T estimateReparamError(index_t steps) const
Estimates error between geometry1 and geometry2 after repearametrization on physical domain.
Definition gsRemapInterface.hpp:201
void constructBreaks()
Constructs the breakpoints m_breakpoints.
Definition gsRemapInterface.hpp:325
Re-implements gsDomainIterator for iteration over all elements of the boundary of a tensor product pa...
Definition gsTensorDomainBoundaryIterator.h:38
void setBreaks(std::vector< T > newBreaks, index_t i)
Function to set the breakpoints in direction i manually.
Definition gsTensorDomainBoundaryIterator.h:248
A vector with arbitrary coefficient type and fixed or dynamic size.
Definition gsVector.h:37
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
#define short_t
Definition gsConfig.h:35
#define index_t
Definition gsConfig.h:32
Fits a (closed) B-spline curve to some given data.
#define GISMO_ENSURE(cond, message)
Definition gsDebug.h:102
#define GISMO_ASSERT(cond, message)
Definition gsDebug.h:89
Provides declaration of the MultiPatch class.
An std::vector with sorting capabilities.
Provides declaration of TensorNurbsBasis abstract interface.
The G+Smo namespace, containing all definitions for the library.
GISMO_DEPRECATED index_t direction(index_t s)
Returns the parametric direction that corresponds to side s.
Definition gsBoundary.h:1048
Struct which represents an interface between two patches.
Definition gsBoundary.h:650
patchSide & first()
first, returns the first patchSide of this interface
Definition gsBoundary.h:776