G+Smo  25.01.0
Geometry + Simulation Modules
 
Loading...
Searching...
No Matches
gsFitting.hpp
Go to the documentation of this file.
1
16#include <gsCore/gsBasis.h>
17#include <gsCore/gsGeometry.h>
22#include <gsNurbs/gsBSpline.h>
24
26
27namespace gismo
28{
29// deconstructor
30template<class T>
32{
33 if ( m_result!=nullptr )
34 delete m_result;
35}
36
37
38// constructor
39template<class T>
41 gsMatrix<T> const & points,
42 gsBasis<T> & basis)
43{
44 GISMO_ASSERT(points.cols()==param_values.cols(), "Pointset dimensions problem "<< points.cols() << " != " <<param_values.cols() );
45 GISMO_ASSERT(basis.domainDim()==param_values.rows(), "Parameter values are inconsistent: "<< basis.domainDim() << " != " <<param_values.rows() );
46
47 m_param_values = param_values;
48 m_points = points;
49 m_result = nullptr;
50 m_basis = &basis;
51 m_points.transposeInPlace();
52
53 m_offset.resize(2);
54 m_offset[0] = 0;
55 m_offset[1] = m_points.rows();
56}
57
58
59// constructor
60template<class T>
62 gsMatrix<T> const& points,
63 gsVector<index_t> offset,
64 gsMappedBasis<2,T> & mbasis)
65{
66 m_param_values = param_values;
67 m_points = points;
68 m_result = nullptr;
69 m_basis = &mbasis;
70 m_points.transposeInPlace();
71 m_offset = give(offset);
72}
73
74
75// compute the coefficients of the spline geometry via penalized least squares
76template<class T>
78{
79
80 m_last_lambda = lambda;
81
82 // Wipe out previous result
83 if ( m_result!=nullptr )
84 delete m_result;
85
86 const int num_basis = m_basis->size();
87 const short_t dimension = m_points.cols();
88
89 //left side matrix
90 //gsMatrix<T> A_mat(num_basis,num_basis);
91 gsSparseMatrix<T> A_mat(num_basis + m_constraintsLHS.rows(), num_basis + m_constraintsLHS.rows());
92 //gsMatrix<T>A_mat(num_basis,num_basis);
93 //To optimize sparse matrix an estimation of nonzero elements per
94 //column can be given here
95 int nonZerosPerCol = 1;
96 for (int i = 0; i < m_basis->domainDim(); ++i) // to do: improve
97 // nonZerosPerCol *= m_basis->degree(i) + 1;
98 nonZerosPerCol *= ( 2 * m_basis->basis(0).degree(i) + 1 ) * 4;
99 // TODO: improve by taking constraints nonzeros into account.
100 A_mat.reservePerColumn( nonZerosPerCol );
101
102 //right side vector (more dimensional!)
103 gsMatrix<T> m_B(num_basis + m_constraintsRHS.rows(), dimension);
104 m_B.setZero(); // enusure that all entries are zero in the beginning
105
106 // building the matrix A and the vector b of the system of linear
107 // equations A*x==b
108
109 assembleSystem(A_mat, m_B);
110
111
112 // --- Smoothing matrix computation
113 //test degree >=3
114 if(lambda > 0)
115 applySmoothing(lambda, A_mat);
116
117 if(m_constraintsLHS.rows() > 0)
118 extendSystem(A_mat, m_B);
119
120 //Solving the system of linear equations A*x=b (works directly for a right side which has a dimension with higher than 1)
121 A_mat.makeCompressed();
122
123 typename gsSparseSolver<T>::BiCGSTABILUT solver( A_mat );
124
125 if ( solver.preconditioner().info() != gsEigen::Success )
126 {
127 gsWarn<< "The preconditioner failed. Aborting.\n";
128
129 return;
130 }
131 //Solves for many right hand side columns
132 gsMatrix<T> x;
133
134 x = solver.solve(m_B); //toDense()
135
136 // If there were constraints, we obtained too many coefficients.
137 x.conservativeResize(num_basis, gsEigen::NoChange);
138
139 //gsMatrix<T> x (m_B.rows(), m_B.cols());
140 //x=A_mat.fullPivHouseholderQr().solve( m_B);
141 // Solves for many right hand side columns
142 // finally generate the B-spline geometry
143
144 if (const gsBasis<T> * bb = dynamic_cast<const gsBasis<T> *>(m_basis))
145 m_result = bb->makeGeometry( give(x) ).release();
146 else
147 m_mresult = gsMappedSpline<2,T> ( *static_cast<gsMappedBasis<2,T>*>(m_basis),give(x));
148
149}
150
151
152// update the geometry with the given coefficients and parameters
153template<class T>
155 gsMatrix<T> parameters)
156{
157 if (!this->m_result)
158 {
159 if (const gsBasis<T> * bb = dynamic_cast<const gsBasis<T> *>(m_basis))
160 m_result = bb->makeGeometry( give( coefficients ) ).release();
161 else
162 m_mresult = gsMappedSpline<2,T> ( *static_cast<gsMappedBasis<2,T>*>(m_basis),give(coefficients));
163 }
164 else
165 {
166 this->m_result->coefs() = coefficients;
167 }
168 this->m_param_values = parameters;
169 this->computeErrors();
170}
171
172
173// Initialize the geometry with the given coefficients and parameters
174template<class T>
176 const gsMatrix<T> & parameters)
177{
178 if (!this->m_result)
179 {
180 if (const gsBasis<T> * bb = dynamic_cast<const gsBasis<T> *>(m_basis))
181 m_result = bb->makeGeometry( give( coefficients ) ).release();
182 else
183 m_mresult = gsMappedSpline<2,T> ( *static_cast<gsMappedBasis<2,T>*>(m_basis),give(coefficients));
184 }
185 else
186 {
187 this->m_result->coefs() = coefficients;
188 }
189 this->m_param_values = parameters;
190}
191
192
193// compute normals at input parameters
194template<class T>
195void gsFitting<T>::compute_normals(const index_t & num_int, const gsMatrix<T> & params_int, gsSparseMatrix<T> & N_int)
196{
197 GISMO_UNUSED(params_int);
199 auto G = ev.getMap(*m_result);
200
201 // sn: compute the normals
202 gsMatrix<T> normals(3, m_param_values.cols());
203 normals.setZero();
204
205 N_int.resize(num_int * 3, num_int);
206 N_int.setZero();
207
208 for(index_t j=0; j < num_int; j++)
209 {
210 normals.col(j) = ev.eval(sn(G).normalized(), m_param_values.col(j));
211 N_int(j, j) = normals(0, j);
212 N_int(num_int+j, j) = normals(1, j);
213 N_int(2*num_int+j, j) = normals(2, j);
214 }
215 N_int.makeCompressed();
216
217}
218
219
220// vector of size (num_int,1) containing all the point-wise errors; store also the max err value
221template<class T>
223{
224 gsMatrix<T> matrix(num_int, 1);
225 for (index_t d = 0; d<num_int; d++)
226 {
227 matrix(d,0) = m_pointErrors[d];
228 if (max_err_int < m_pointErrors[d])
229 max_err_int = m_pointErrors[d];
230 }
231 return matrix;
232}
233
234
235// compute the principal curvatures at the given parameters
236template<class T>
238{
239 index_t numData = params.cols();
240 m_pointCurvature.resize(numData, 2);
242 auto G = ev.getMap(*m_result);
243
244 gsVector<T> pm(2);
245 gsMatrix<T> pcs, out;
246
247 // (c1, c2) = principal_curvatures
248 for (index_t d = 0; d<numData; d++)
249 {
250 pm = params.col(d);
251 out = ev.eval( shapeop(G), pm );
252
253 pcs = out.template selfadjointView<gsEigen::Lower>().eigenvalues();
254
255 m_pointCurvature.row(d) = pcs.transpose();
256
257 }
258
259 return m_pointCurvature;
260
261}
262
263
264// compute the inverse of the principal curvatures at the given parameters
265template<class T>
267{
268
269 gsMatrix<T> inv_c(num_int, 1);
270 gsMatrix<T> pcs;
271 if (m_pointCurvature.size() == 0)
272 pcs = principal_curvatures(params_int);
273 else
274 pcs = m_pointCurvature;
275
276 pcs = pcs.cwiseAbs();
277
278 // rho = 1/max(c1, c2)
279 for (index_t d = 0; d<num_int; d++)
280 {
281 T den = pcs(d,0);
282 if ( pcs(d,1) > pcs(d,0) )
283 den = pcs(d,1);
284
285 inv_c(d,0) = 1./den;
286 }
287
288 return inv_c;
289}
290
291
292// compute the weights for the pdm-tdm balance in the hybrid method.
293template<class T>
294void gsFitting<T>::blending_weights(const gsSparseMatrix<T> & N_int, const index_t & num_int, const T & mu, const T & sigma,
295 const gsMatrix<T> & params_int,
296 tdm_method method, gsSparseMatrix<T> & NNT)
297{
298 NNT.resize(3 * num_int, 3 * num_int);
299 if (method == hybrid_pdm_tdm_boundary_pdm)
300 {
301 // constant blending weights: mu * PDM + sigma * TDM, mu and sigma given in input.
302 gsSparseMatrix<T> Im(3 * num_int, 3 * num_int);
303 Im.setIdentity();
304 NNT = mu * Im + sigma * N_int * N_int.transpose();
305 }
306 else
307 {
308 gsVector<T> MK(num_int);
309 if (m_pointErrors.size() == 0)
310 {
311 gsWarn << "No point-wise errors found. Computing them now.\n";
312 computeErrors();
313 }
314
315 T max_err_int = m_pointErrors[0];
316
317 gsMatrix<T> points_int_errors, rho_c, dist_plus_rho;
318
319 points_int_errors = fill_pointWiseErrors(num_int, max_err_int);
320
321 if (method == hybrid_error_pdm_tdm_boundary_pdm)
322 {
323 // error blending weights: err*PDM + (1-err)*TDM, err = error/max_error
324 MK = (0.5/max_err_int) * points_int_errors;//.asDiagonal();
325 }
326 else if (method == hybrid_curvature_pdm_tdm_boundary_pdm)
327 {
328 // curvature blending weights: rho1*PDM + rho2*TDM
329 rho_c = inverse_principal_curvatures(num_int, params_int);
330 dist_plus_rho = (points_int_errors + rho_c);
331 MK = (points_int_errors.cwiseProduct( dist_plus_rho.cwiseInverse() ));//.asDiagonal();
332 }
333 else
334 gsWarn << "Unknown method." << std::endl;
335
336 NNT = MK.replicate(3,1).asDiagonal();
337 gsSparseMatrix<T> N_int_tr = N_int.transpose();
338 NNT += ( N_int * ( gsVector<T>::Ones(num_int) - MK).asDiagonal() ) * N_int_tr ;
339 }
340
341 NNT.makeCompressed();
342
343}
344
345
346// Assemble the linear system for Hybrid Distance Minimization
347template<class T>
348void gsFitting<T>::assembleSystem(const gsMatrix<T> & points_int, const gsMatrix<T> & params_int,
349 const gsMatrix<T> & points_bdy, const gsMatrix<T> & params_bdy,
350 const index_t & num_basis, const gsSparseMatrix<T> & NNT,
351 gsSparseMatrix<T> & A_tilde, gsMatrix<T> & rhs)
352{
353
354 gsSparseMatrix<T> B_int, B_bdy;
355 gsMatrix<T> X_int, X_bdy;
356
357 // interior colloc
358 assembleBlockB(points_int, params_int, num_basis, B_int);
359 assembleBlockX(points_int, X_int);
360
361 // boundary colloc
362 assembleBlockB(points_bdy, params_bdy, num_basis, B_bdy);
363 assembleBlockX(points_bdy, X_bdy);
364
365 // normal equations
366 A_tilde = B_int.transpose() * NNT * B_int + B_bdy.transpose() * B_bdy;
367 rhs = B_int.transpose() * NNT * X_int + B_bdy.transpose() * X_bdy;
368
369 A_tilde.makeCompressed();
370}
371
372
373// compute the geometry coefficients with Hybrid Distance Minimization method
374template<class T>
375void gsFitting<T>::compute_tdm(T lambda, T mu, T sigma, const std::vector<index_t> & interpIdx, tdm_method method)
376{
377 // dimensions initialization
378 const index_t num_basis = m_basis->size(); // dimension of the approximation space
379 const index_t dim_pts = m_points.cols(); // physical space dimension
380 const index_t dim_par = m_param_values.rows(); // parametric space dimension
381 const index_t num_pts = m_points.rows(); // number of points to fit
382 const index_t num_int = interpIdx[0]; // number of interior points
383 const index_t num_bdy = num_pts - num_int; // number of boundary points
384
385 gsMatrix<T> points_int = m_points.block(0, 0, num_int, dim_pts);
386 gsMatrix<T> points_bdy = m_points.block(num_int, 0, num_bdy, dim_pts);
387 gsMatrix<T> params_int = m_param_values.block(0, 0, dim_par, num_int);
388 gsMatrix<T> params_bdy = m_param_values.block(0, num_int, dim_par, num_bdy);
389
390 m_last_lambda = lambda;
391 if ( !m_result )
392 {
393 gsWarn << "No existing geometry. Computing it now as Penalized Least Squares model, with lambda = "<< m_last_lambda <<".\n";
394 compute(m_last_lambda);
395 gsMatrix<T> refCoefs = m_result->coefs();
396
397 // Wipe out previous result
398 if ( m_result )
399 delete m_result;
400
401 if (const gsBasis<T> * bb = dynamic_cast<const gsBasis<T> *>(m_basis))
402 m_result = bb->makeGeometry( give(refCoefs) ).release();
403 else
404 m_mresult = gsMappedSpline<2,T> ( *static_cast<gsMappedBasis<2,T>*>(m_basis),give(refCoefs));
405 }
406 else
407 {
408 if( interpIdx.size() == 0)
409 {
410 GISMO_ERROR("Input point cloud needs to be ordered:\n"
411 "interior points, south boundary curve, east boundary curve, north boundary curve, west boundary curve.\n");
412 return;
413 }
414
415 gsSparseMatrix<T> N_int, NNT, A_tilde;
416 gsMatrix<T> rhs;
417
418 // compute the error of the current geometry.
419 computeErrors();
420
421 //T max_err_int = m_pointErrors[0];
422
423 // nomals
424 compute_normals(num_int, params_int, N_int);
425
426 // compute weights
427 blending_weights(N_int, num_int, mu, sigma, params_int, method, NNT);
428
429 // assemble System
430 // A_interiors + A_boundary
431 assembleSystem(points_int, params_int, points_bdy, params_bdy, num_basis, NNT, A_tilde, rhs);
432
433 // apply smoothing
434 if(lambda > 0)
435 {
437 m_G.resize(num_basis, num_basis);
438 gsSparseMatrix<T> G_mat(A_tilde.rows(), A_tilde.cols());
439
440 applySmoothing(lambda, m_G);
441 threeOnDiag(m_G, G_mat);
442 A_tilde += lambda * G_mat;
443 }
444
445 A_tilde.makeCompressed();
446
447 // typename gsSparseSolver<T>::QR solver(A_tilde);
448 typename gsSparseSolver<T>::BiCGSTABILUT solver( A_tilde );
449 if ( solver.preconditioner().info() != gsEigen::Success )
450 {
451 gsWarn<< "The preconditioner failed. Aborting.\n";
452
453 return;
454 }
455
456 gsMatrix<T> sol_tilde = solver.solve(rhs);
457
458 // If there were constraints, we obtained too many coefficients.
459 sol_tilde.conservativeResize(num_basis * 3, gsEigen::NoChange);
460
461 gsMatrix<T> coefs_tilde(m_basis->size(), 3);
462 for(index_t j=0; j<m_basis->size(); j++)
463 {
464 coefs_tilde(j,0) = sol_tilde(j);
465 coefs_tilde(j,1) = sol_tilde(m_basis->size()+j);
466 coefs_tilde(j,2) = sol_tilde(2*m_basis->size()+j);
467 }
468
469 // Wipe out previous result
470 if ( m_result )
471 delete m_result;
472
473 if (const gsBasis<T> * bb = dynamic_cast<const gsBasis<T> *>(m_basis))
474 m_result = bb->makeGeometry( give(coefs_tilde) ).release();
475 else
476 m_mresult = gsMappedSpline<2,T> ( *static_cast<gsMappedBasis<2,T>*>(m_basis),give(coefs_tilde));
477
478 }// fi
479}
480
481
482// check if the given parameter is a corner of the domain
483template <class T>
485 gsVector<T> & param)
486{
487 bool corner_check = false;
488 if( (math::abs(param(0) - p_domain(0,0)) < 1e-15) && (math::abs(param(1) - p_domain(1,0)) < 1e-15) ){
489 corner_check = true;
490 }
491 else if( (math::abs(param(0) - p_domain(0,1)) < 1e-15) && (math::abs(param(1) - p_domain(1,0)) < 1e-15) ){
492 corner_check = true;
493 }
494 else if( (math::abs(param(0) - p_domain(0,1)) < 1e-15) && (math::abs(param(1) - p_domain(1,1)) < 1e-15) ){
495 corner_check = true;
496 }
497 else if( (math::abs(param(0) - p_domain(0,0)) < 1e-15) && (math::abs(param(1) - p_domain(1,1)) < 1e-15) ){
498 corner_check = true;
499 }
500 else{
501 corner_check = false;
502 }
503 return corner_check;
504}
505
506
507// difference with is_point_inside_support in the inclusion of the left and right interval extremes.
508template <class T>
509bool is_point_within_cell(const gsMatrix<T>& parameter,
510 const gsMatrix<T>& element)
511{
512 const real_t x = parameter(0, 0);
513 const real_t y = parameter(1, 0);
514
515 return element(0, 0) < x && x < element(0, 1) &&
516 element(1, 0) < y && y < element(1, 1);
517}
518
519
520template <class T>
521bool is_point_within_cell(const T x,
522 const T y,
523 const gsMatrix<T>& element)
524{
525 bool condition = (element(0, 0) < x && x < element(0, 1) && element(1, 0) < y && y < element(1, 1));
526 return condition;
527}
528
529
530template <class T>
531bool is_point_inside_support(const gsMatrix<T>& parameter,
532 const gsMatrix<T>& support)
533{
534 const real_t x = parameter(0, 0);
535 const real_t y = parameter(1, 0);
536
537 return support(0, 0) <= x && x < support(0, 1) &&
538 support(1, 0) <= y && y < support(1, 1);
539}
540
541
542template <class T>
543bool is_point_inside_support(const T x,
544 const T y,
545 const gsMatrix<T>& support)
546{
547 return support(0, 0) <= x && x < support(0, 1) &&
548 support(1, 0) <= y && y < support(1, 1);
549}
550
551
552// project the points onto the fitted geometry, separating interior and boundary points
553template <class T>
554void gsFitting<T>::parameterProjectionSepBoundary(T accuracy,const std::vector<index_t>& interpIdx)
555{
556 if ( !m_result )
557 {
558 compute(m_last_lambda);
559 }
560//# pragma omp parallel for default(shared) private(newParam)
561 //Parameter projection for interior points
562 for (index_t i = 0; i < interpIdx[0]; ++i)
563 {
564 gsVector<T> newParam;
565 const auto & curr = m_points.row(i).transpose();
566 newParam = m_param_values.col(i);
567 m_result->closestPointTo(curr, newParam, accuracy, true); // true: use initial point
568
569 // Decide whether to accept the correction or to drop it
570 if ((m_result->eval(newParam) - curr).norm()
571 < (m_result->eval(m_param_values.col(i)) - curr).norm())
572 {
573 m_param_values.col(i) = newParam;
574 }
575 }
576
577 // south boundary parameters: (u,0)
578 for (index_t i = interpIdx[0]+1; i < interpIdx[1]; ++i)
579 {
580 gsVector<> newParam(1,1);
581 gsVector<> oldParam(1,1);
582 newParam(0,0) = m_param_values(0,i);
583 oldParam(0,0) = m_param_values(0,i);
584 const auto & curr = m_points.row(i).transpose();
585 typename gsGeometry<T>::uPtr b = m_result->boundary(3); // south edge
586 b->closestPointTo(curr, newParam, accuracy, true);
587
588 if ((b->eval(newParam) - curr).norm()
589 < (b->eval(oldParam) - curr).norm())
590 {
591 m_param_values(0,i) = newParam(0,0);
592
593 }
594 }
595
596 // east boundary parameters: (1,v)
597 for (index_t i = interpIdx[1]+1; i < interpIdx[2]; ++i)
598 {
599 gsVector<> newParam(1,1);
600 gsVector<> oldParam(1,1);
601 newParam(0,0) = m_param_values(1,i); // we consider the v of the i-th parameter
602 oldParam(0,0) = m_param_values(1,i); // we consider the v of the i-th parameter
603 const auto & curr = m_points.row(i).transpose();
604 typename gsGeometry<T>::uPtr b = m_result->boundary(2); // east edge
605 b->closestPointTo(curr, newParam, accuracy, true);
606
607 if ((b->eval(newParam) - curr).norm()
608 < (b->eval(oldParam) - curr).norm())
609 m_param_values(1,i) = newParam(0,0);
610 }
611 //north boundary parameters: (u,1)
612 for (index_t i = interpIdx[2]+1; i < interpIdx[3]; ++i)
613 {
614 gsVector<> newParam(1,1);
615 gsVector<> oldParam(1,1);
616 newParam(0,0) = m_param_values(0,i); // we consider the u of the i-th parameter
617 oldParam(0,0) = m_param_values(0,i); // we consider the u of the i-th parameter
618 const auto & curr = m_points.row(i).transpose();
619 typename gsGeometry<T>::uPtr b = m_result->boundary(4); // north edge
620 b->closestPointTo(curr, newParam, accuracy, true);
621
622 if ((b->eval(newParam) - curr).norm()
623 < (b->eval(oldParam) - curr).norm())
624 m_param_values(0,i) = newParam(0,0);
625 }
626 //west boundary parameters: (0,v)
627 for (index_t i = interpIdx[3]+1; i < m_points.rows(); ++i)
628 {
629 gsVector<> newParam(1,1);
630 gsVector<> oldParam(1,1);
631 newParam(0,0) = m_param_values(1,i); // we consider the v of the i-th parameter
632 oldParam(0,0) = m_param_values(1,i); // we consider the v of the i-th parameter
633 const auto & curr = m_points.row(i).transpose();
634 typename gsGeometry<T>::uPtr b = m_result->boundary(1); // west edge
635 b->closestPointTo(curr, newParam, accuracy, true);
636
637 if ((b->eval(newParam) - curr).norm()
638 < (b->eval(oldParam) - curr).norm())
639 m_param_values(1,i) = newParam(0,0);
640 }
641}
642
643
644// apply maxIter steps of parameter correction for HDM method, separating interior and boundary points
645template <class T>
647 index_t maxIter,
648 T mu, T sigma,
649 const std::vector<index_t>& interpIdx,
650 tdm_method method)
651{
652 if ( !m_result )
653 {
654 compute(m_last_lambda);
655 }
656
657 //const index_t d = m_param_values.rows();
658 //const index_t n = m_points.cols();
659 for (index_t it = 0; it<maxIter; ++it)
660 {
661//# pragma omp parallel for default(shared) private(newParam)
662 parameterProjectionSepBoundary(accuracy, interpIdx); // projection of the points on the geometry
663 compute_tdm(m_last_lambda, mu, sigma, interpIdx, method); // updates of the coefficients with HDM
664 }// step of PC
665}
666
667
668// apply maxIter steps of parameter correction for PDM method, separating interior and boundary points
669template <class T>
671 index_t maxIter,
672 const std::vector<index_t>& interpIdx)
673{
674 if ( !m_result )
675 {
676 compute(m_last_lambda);
677 }
678
679 //const index_t d = m_param_values.rows();
680 //const index_t n = m_points.cols();
681 for (index_t it = 0; it<maxIter; ++it)
682 {
683//# pragma omp parallel for default(shared) private(newParam)
684 parameterProjectionSepBoundary(accuracy, interpIdx); // projection of the points on the geometry
685 compute(m_last_lambda); // updates of the coefficients with PDM
686 }// step of PC
687}
688
689
690// apply maxIter steps of parameter correction
691template <class T>
693 index_t maxIter,
694 T tolOrth)
695{
696 GISMO_UNUSED(tolOrth);
697 if ( !m_result )
698 compute(m_last_lambda);
699
700 //const index_t d = m_param_values.rows();
701 //const index_t n = m_points.cols();
702
703 for (index_t it = 0; it<maxIter; ++it)
704 {
705//# pragma omp parallel for
706 for (index_t i = 0; i<m_points.rows(); ++i)
707 //for (index_t i = 1; i<m_points.rows()-1; ++i) //(!curve) skip first last pt
708 {
709 const auto & curr = m_points.row(i).transpose();
710 gsVector<T> newParam = m_param_values.col(i);
711 m_result->closestPointTo(curr, newParam, accuracy, true);
712
713 // Decide whether to accept the correction or to drop it
714 if ((m_result->eval(newParam) - curr).norm()
715 < (m_result->eval(m_param_values.col(i))
716 - curr).norm())
717 m_param_values.col(i) = newParam;
718
719 // (!) There might be the same parameter for two points
720 // or ordering constraints in the case of structured/grid data
721 }
722
723 // refit
724 compute(m_last_lambda);
725 }
726}
727
728
729// assemble the global system for least-squares fitting
730template <class T>
732 gsMatrix<T>& m_B)
733{
734 const int num_patches ( m_basis->nPieces() ); //initialize
735
736 //for computing the value of the basis function
737 gsMatrix<T> value, curr_point;
738 gsMatrix<index_t> actives;
739
740 for (index_t h = 0; h < num_patches; h++ )
741 {
742 auto & basis = m_basis->basis(h);
743
744//# pragma omp parallel for default(shared) private(curr_point,actives,value)
745 for (index_t k = m_offset[h]; k < m_offset[h+1]; ++k)
746 {
747 curr_point = m_param_values.col(k);
748
749 //computing the values of the basis functions at the current point
750 basis.eval_into(curr_point, value);
751
752 // which functions have been computed i.e. which are active
753 basis.active_into(curr_point, actives);
754
755 const index_t numActive = actives.rows();
756
757 for (index_t i = 0; i != numActive; ++i)
758 {
759 const index_t ii = actives.at(i);
760//# pragma omp critical (acc_m_B)
761 m_B.row(ii) += value.at(i) * m_points.row(k);
762 for (index_t j = 0; j != numActive; ++j)
763//# pragma omp critical (acc_A_mat)
764 A_mat(ii, actives.at(j)) += value.at(i) * value.at(j);
765 }
766 }
767 }
768}
769
770
771// Extends the system of equations by taking constraints into account
772template <class T>
774 gsMatrix<T>& m_B)
775{
776 index_t basisSize = m_basis->size();
777
778 // Idea: maybe we can resize here instead of doing it in compute().
779
780 // This way does not work, as these block operations are read-only for sparse matrices.
781 /*A_mat.topRightCorner(m_constraintsLHS.cols(), m_constraintsLHS.rows()) = m_constraintsLHS.transpose();
782 A_mat.bottomLeftCorner(m_constraintsLHS.rows(), m_constraintsLHS.cols()) = m_constraintsLHS;*/
783 m_B.bottomRows(m_constraintsRHS.rows()) = m_constraintsRHS;
784
785 for (index_t k=0; k<m_constraintsLHS.outerSize(); ++k)
786 {
787 for (typename gsSparseMatrix<T>::InnerIterator it(m_constraintsLHS,k); it; ++it)
788 {
789 A_mat(basisSize + it.row(), it.col()) = it.value();
790 A_mat(it.col(), basisSize + it.row()) = it.value();
791 }
792 }
793}
794
795
796// compute the smoorhing matrix
797template<class T>
799{
800 m_last_lambda = lambda;
801
802 const int num_basis=m_basis->size();
803
804 gsSparseMatrix<T> A_mat(num_basis + m_constraintsLHS.rows(), num_basis + m_constraintsLHS.rows());
805 int nonZerosPerCol = 1;
806 for (int i = 0; i < m_basis->domainDim(); ++i) // to do: improve
807 nonZerosPerCol *= ( 2 * m_basis->basis(0).degree(i) + 1 );
808 A_mat.reservePerColumn( nonZerosPerCol );
809 const_cast<gsFitting*>(this)->applySmoothing(lambda, A_mat);
810 return A_mat;
811}
812
813
814// apply smoothing to the system of equations
815template<class T>
817{
818 m_last_lambda = lambda;
819 const int num_patches(m_basis->nPieces()); //initialize
820 const short_t dim(m_basis->domainDim());
821 const short_t stride = dim * (dim + 1) / 2;
822
823 gsVector<index_t> numNodes(dim);
824 gsMatrix<T> quNodes, der2, localA;
825 gsVector<T> quWeights;
826 gsMatrix<index_t> actives;
827
828# ifdef _OPENMP
829 const int tid = omp_get_thread_num();
830 const int nt = omp_get_num_threads();
831# endif
832
833 for (index_t h = 0; h < num_patches; h++)
834 {
835 auto & basis = m_basis->basis(h);
836
837 //gsDebugVar(dim);
838 //gsDebugVar(stride);
839
840 for (short_t i = 0; i != dim; ++i)
841 {
842 numNodes[i] = basis.degree(i);//+1;
843 }
844
845 gsGaussRule<T> QuRule(numNodes); // Reference Quadrature rule
846
847 typename gsBasis<T>::domainIter domIt = basis.makeDomainIterator();
848
849
850# ifdef _OPENMP
851 for ( domIt->next(tid); domIt->good(); domIt->next(nt) )
852# else
853 for (; domIt->good(); domIt->next() )
854# endif
855 {
856 // Map the Quadrature rule to the element and compute basis derivatives
857 QuRule.mapTo(domIt->lowerCorner(), domIt->upperCorner(), quNodes, quWeights);
858 basis.deriv2_into(quNodes, der2);
859 basis.active_into(domIt->center, actives);
860 const index_t numActive = actives.rows();
861 localA.setZero(numActive, numActive);
862
863 // perform the quadrature
864 for (index_t k = 0; k < quWeights.rows(); ++k)
865 {
866 const T weight = quWeights[k] * lambda;
867
868 for (index_t i = 0; i != numActive; ++i)
869 for (index_t j = 0; j != numActive; ++j)
870 {
871 T localAij = 0; // temporary variable
872
873 for (short_t s = 0; s < stride; s++)
874 {
875 // The pure second derivatives
876 // d^2u N_i * d^2u N_j + ...
877 if (s < dim)
878 {
879 localAij += der2(i * stride + s, k) *
880 der2(j * stride + s, k);
881 }
882 // Mixed derivatives 2 * dudv N_i * dudv N_j + ...
883 else
884 {
885 localAij += T(2) * der2(i * stride + s, k) *
886 der2(j * stride + s, k);
887 }
888 }
889 localA(i, j) += weight * localAij;
890 }
891 }
892
893 for (index_t i = 0; i != numActive; ++i)
894 {
895 const int ii = actives(i, 0);
896 for (index_t j = 0; j != numActive; ++j)
897 A_mat(ii, actives(j, 0)) += localA(i, j);
898 }
899 }
900
901 }
902}
903
904
905// compute the approximation errors
906template<class T>
908{
909 m_pointErrors.clear();
910
911 gsMatrix<T> val_i;
912 m_result->eval_into(m_param_values, val_i);
913 m_pointErrors.push_back( (m_points.row(0) - val_i.col(0).transpose()).norm() );
914 m_max_error = m_min_error = m_pointErrors.back();
915
916 for (index_t i = 1; i < m_points.rows(); i++)
917 {
918 const T err = (m_points.row(i) - val_i.col(i).transpose()).norm() ;
919 m_pointErrors.push_back(err);
920
921 if ( err > m_max_error ) m_max_error = err;
922 if ( err < m_min_error ) m_min_error = err;
923 }
924}
925
926
927template<class T>
929{
930
931 gsMatrix<T> eval;
932 m_result->eval_into(parameters, eval);
933 gsMatrix<T> ptwErrors(1, eval.cols());
934
935 for (index_t col = 0; col != eval.cols(); col++)
936 {
937 ptwErrors(0, col) = (eval.col(col) - points.col(col)).norm();
938 }
939
940 return ptwErrors;
941}
942
943
944template<class T>
945std::vector<T> gsFitting<T>::computeErrors(const gsMatrix<> & parameters,const gsMatrix<> & points)
946{
947 std::vector<T> min_max_mse;
948 gsMatrix<T> eval;
949 m_result->eval_into(parameters, eval);
950
951 gsMatrix<T> pointWiseErrors(1, eval.cols());
952
953 for (index_t col = 0; col != eval.cols(); col++)
954 {
955 pointWiseErrors(0, col) = (eval.col(col) - points.col(col)).norm();
956 }
957
958 T min_error = 1e6;
959 T max_error = 0;
960 T mse_error = 0;
961
962 for (index_t i = 1; i < pointWiseErrors.cols(); i++)
963 {
964 const real_t err = pointWiseErrors(0,i) ;
965 mse_error += err * err ;
966 if ( err > max_error ) max_error = err;
967 if ( err < min_error ) min_error = err;
968 }
969
970 min_max_mse.push_back(min_error);
971 min_max_mse.push_back(max_error);
972 min_max_mse.push_back(mse_error/pointWiseErrors.cols());
973
974 return min_max_mse;
975}
976
977
978template<class T>
980{
981 m_pointErrors.clear();
982 gsMatrix<T> values;
983 const int num_patches(m_basis->nPieces());
984
985 for (index_t h = 0; h < num_patches; h++)
986 {
987 for (index_t k = m_offset[h]; k < m_offset[h + 1]; ++k)
988 {
989 auto curr_point = m_param_values.col(k);
990
991 if (m_result)
992 m_result->eval_into(curr_point, values);
993 else
994 {
995 m_mresult.eval_into(h, curr_point, values);
996 }
997
998 //const T err = (m_points.row(k) - values.col(k).transpose()).cwiseAbs().maxCoeff();
999 const T err = (m_points.row(k) - values.transpose()).template lpNorm<gsEigen::Infinity>();
1000
1001 m_pointErrors.push_back(err);
1002
1003 if ( k == 0 || m_max_error < err ) m_max_error = err;
1004 if ( k == 0 || err < m_min_error ) m_min_error = err;
1005 }
1006 }
1007}
1008
1009
1010template<class T>
1011void gsFitting<T>::computeApproxError(T& error, int type) const
1012
1013{
1014 gsMatrix<T> results;
1015
1016 const int num_patches(m_basis->nPieces());
1017
1018 error = 0;
1019
1020 for (index_t h = 0; h < num_patches; h++)
1021 {
1022
1023 for (index_t k = m_offset[h]; k < m_offset[h + 1]; ++k)
1024 {
1025 auto curr_point = m_param_values.col(k);
1026
1027 if (m_result)
1028 m_result->eval_into(curr_point, results);
1029 else
1030 {
1031 m_mresult.eval_into(h, curr_point, results);
1032 }
1033
1034 const T err = (m_points.row(k) - results.transpose()).squaredNorm();
1035
1036 switch (type) {
1037 case 0:
1038 error += err;
1039 break;
1040 case 1:
1041 error += math::sqrt(err);
1042 break;
1043 default:
1044 gsWarn << "Unknown type in computeApproxError(error, type)...\n";
1045 break;
1046 }
1047 }
1048 }
1049}
1050
1051
1052template<class T>
1053void gsFitting<T>::get_Error(std::vector<T>& errors, int type) const
1054{
1055 errors.clear();
1056 errors.reserve(m_points.rows());
1057
1058 gsMatrix<T> curr_point, results;
1059
1060 T err = 0;
1061
1062 const int num_patches(m_basis->nPieces());
1063
1064 for (index_t h = 0; h < num_patches; h++)
1065 {
1066 for (index_t k = m_offset[h]; k < m_offset[h + 1]; ++k)
1067 {
1068 curr_point = m_param_values.col(k);
1069
1070 if (m_result)
1071 m_result->eval_into(curr_point, results);
1072 else
1073 {
1074 m_mresult.eval_into(h, curr_point, results);
1075 }
1076
1077 results.transposeInPlace();
1078
1079 err = (m_points.row(k) - results).template lpNorm<gsEigen::Infinity>();
1080
1081 switch (type)
1082 {
1083 case 0:
1084 errors.push_back(err);
1085 break;
1086 default:
1087 gsWarn << "Unknown type in get_Error(errors, type)...\n";
1088 break;
1089 }
1090 }
1091 }
1092}
1093
1094template<class T>
1095void gsFitting<T>::setConstraints(const std::vector<index_t>& indices,
1096 const std::vector<gsMatrix<T> >& coefs)
1097{
1098 if(coefs.size() == 0)
1099 return;
1100
1101 GISMO_ASSERT(indices.size() == coefs.size(),
1102 "Prescribed indices and coefs must have the same length.");
1103
1104 gsSparseMatrix<T> lhs(indices.size(), m_basis->size());
1105 gsMatrix<T> rhs(indices.size(), coefs[0].cols());
1106
1107 index_t duplicates = 0;
1108 for(size_t r=0; r<indices.size(); r++)
1109 {
1110 index_t fix = indices[r];
1111 lhs(r-duplicates, fix) = 1;
1112 rhs.row(r-duplicates) = coefs[r];
1113 }
1114
1115 setConstraints(lhs, rhs);
1116}
1117
1118template<class T>
1119void gsFitting<T>::setConstraints(const std::vector<boxSide>& fixedSides)
1120{
1121 if(fixedSides.size() == 0)
1122 return;
1123
1124 std::vector<index_t> indices;
1125 std::vector<gsMatrix<T> > coefs;
1126
1127 for(std::vector<boxSide>::const_iterator it=fixedSides.begin(); it!=fixedSides.end(); ++it)
1128 {
1129 gsMatrix<index_t> ind = this->m_basis->basis(0).boundary(*it);
1130 for(index_t r=0; r<ind.rows(); r++)
1131 {
1132 index_t fix = ind(r,0);
1133 // If it is a new constraint, add it.
1134 if(std::find(indices.begin(), indices.end(), fix) == indices.end())
1135 {
1136 indices.push_back(fix);
1137 coefs.push_back(this->m_result->coef(fix));
1138 }
1139 }
1140 }
1141 setConstraints(indices, coefs);
1142}
1143
1144template<class T>
1145void gsFitting<T>::setConstraints(const std::vector<boxSide>& fixedSides,
1146 const std::vector<gsBSpline<T> >& fixedCurves)
1147{
1148 std::vector<gsBSpline<T> > tmp = fixedCurves;
1149 std::vector<gsGeometry<T> *> fixedCurves_input(tmp.size());
1150 for (size_t k=0; k!=fixedCurves.size(); k++)
1151 fixedCurves_input[k] = dynamic_cast<gsGeometry<T> *>(&(tmp[k]));
1152 setConstraints(fixedSides, fixedCurves_input);
1153}
1154
1155template<class T>
1156void gsFitting<T>::setConstraints(const std::vector<boxSide>& fixedSides,
1157 const std::vector<gsGeometry<T> * >& fixedCurves)
1158{
1159 if(fixedSides.size() == 0)
1160 return;
1161
1162 GISMO_ASSERT(fixedCurves.size() == fixedSides.size(),
1163 "fixedCurves and fixedSides are of different sizes.");
1164
1165 std::vector<index_t> indices;
1166 std::vector<gsMatrix<T> > coefs;
1167 for(size_t s=0; s<fixedSides.size(); s++)
1168 {
1169 gsMatrix<T> coefsThisSide = fixedCurves[s]->coefs();
1170 gsMatrix<index_t> indicesThisSide = m_basis->basis(0).boundaryOffset(fixedSides[s],0);
1171 GISMO_ASSERT(coefsThisSide.rows() == indicesThisSide.rows(),
1172 "Coef number mismatch between prescribed curve and basis side.");
1173
1174 for(index_t r=0; r<indicesThisSide.rows(); r++)
1175 {
1176 index_t fix = indicesThisSide(r,0);
1177 // If it is a new constraint, add it.
1178 if(std::find(indices.begin(), indices.end(), fix) == indices.end())
1179 {
1180 indices.push_back(fix);
1181 coefs.push_back(coefsThisSide.row(r));
1182 }
1183 }
1184 }
1185
1186 setConstraints(indices, coefs);
1187}
1188
1189
1190} // namespace gismo
A B-spline function of one argument, with arbitrary target dimension.
Definition gsBSpline.h:51
A basis represents a family of scalar basis functions defined over a common parameter domain.
Definition gsBasis.h:79
Generic evaluator of isogeometric expressions.
Definition gsExprEvaluator.h:39
void eval(const expr::_expr< E > &expr, gsGridIterator< T, mode, d > &git, const index_t patchInd=0)
geometryMap getMap(const gsMultiPatch< T > &mp)
Registers mp as an isogeometric geometry map and return a handle to it.
Definition gsExprEvaluator.h:116
Class for performing a fit of a parametrized point cloud with a gsGeometry.
Definition gsFitting.h:32
gsFitting()
default constructor
Definition gsFitting.h:35
tdm_method
choose the method for computing the coefficients: TDM, PDM, HDM with different blending weights
Definition gsFitting.h:81
gsMatrix< T > fill_pointWiseErrors(const index_t &num_int, T &max_err_int)
vector of length num_int containing all the point-wise errors; store also the max err value in max_er...
Definition gsFitting.hpp:222
void extendSystem(gsSparseMatrix< T > &A_mat, gsMatrix< T > &m_B)
Extends the system of equations by taking constraints into account.
Definition gsFitting.hpp:773
void computeApproxError(T &error, int type=0) const
Definition gsFitting.hpp:1011
void assembleSystem(gsSparseMatrix< T > &A_mat, gsMatrix< T > &B)
Assembles system for the least square fit.
Definition gsFitting.hpp:731
const std::vector< T > & pointWiseErrors() const
Return the errors for each point.
Definition gsFitting.h:183
void parameterCorrectionSepBoundary_tdm(T accuracy, index_t maxIter, T mu, T sigma, const std::vector< index_t > &sepIndex, tdm_method method=hybrid_curvature_pdm_tdm_boundary_pdm)
parameterCorrectionSepBoundary_tdm: apply maxIter steps of parameter correction for HDM method,...
Definition gsFitting.hpp:646
void applySmoothing(T lambda, gsSparseMatrix< T > &A_mat)
Adds to the matrix A_mat terms for minimization of second derivative, weighted with parameter lambda.
Definition gsFitting.hpp:816
void initializeGeometry(const gsMatrix< T > &coefficients, const gsMatrix< T > &parameters)
initializeGeometry: Initializes the fitted geometry with given coefficients and parameters
Definition gsFitting.hpp:175
gsMatrix< T > inverse_principal_curvatures(const index_t &num_int, const gsMatrix< T > &params_int)
vector of length num_int containing rho = 1/max(c1, c2), where c1, c2 are the principal curvature val...
Definition gsFitting.hpp:266
void compute_normals(const index_t &num_int, const gsMatrix< T > &params_int, gsSparseMatrix< T > &N_int)
compute_normals: Computes the normals of the fitted geometry at the input parameter values params_int
Definition gsFitting.hpp:195
void compute_tdm(T lambda, T mu, T sigma, const std::vector< index_t > &interpIdx, tdm_method method=hybrid_curvature_pdm_tdm_boundary_pdm)
compute_hdm: computes the coefficients of the spline geometry via Hybrid Distance Minimization (HDM)
Definition gsFitting.hpp:375
void setConstraints(const gsSparseMatrix< T > &lhs, const gsMatrix< T > &rhs)
Definition gsFitting.h:272
void parameterProjectionSepBoundary(T accuracy, const std::vector< index_t > &interpIdx)
parameterProjectionSepBoundary: project the points onto the fitted geometry, separating interior and ...
Definition gsFitting.hpp:554
void get_Error(std::vector< T > &errors, int type=0) const
Definition gsFitting.hpp:1053
void compute(T lambda=0)
compute: Computes the coefficients of the spline geometry via penalized least squares
Definition gsFitting.hpp:77
void computeMaxNormErrors()
Definition gsFitting.hpp:979
virtual ~gsFitting()
Destructor.
Definition gsFitting.hpp:31
void parameterCorrectionSepBoundary_pdm(T accuracy, index_t maxIter, const std::vector< index_t > &sepIndex)
parameterCorrectionSepBoundary_pdm: apply maxIter steps of parameter correction for PDM method,...
Definition gsFitting.hpp:670
void parameterCorrection(T accuracy=1e-8, index_t maxIter=10, T tolOrth=1e-6)
parameterCorrection: globally apply maxIter steps of parameter correction to the least squares fitted...
Definition gsFitting.hpp:692
void blending_weights(const gsSparseMatrix< T > &N_int, const index_t &num_int, const T &mu, const T &sigma, const gsMatrix< T > &params_int, tdm_method method, gsSparseMatrix< T > &NNT)
blending_weights: computes the blending weights mu and sigma for the balance mu * PDM + sigma * TDM i...
Definition gsFitting.hpp:294
void updateGeometry(gsMatrix< T > coefficients, gsMatrix< T > parameters)
updateGeometry: Updates the fitted geometry with new coefficients and parameters
Definition gsFitting.hpp:154
gsMatrix< T > principal_curvatures(const gsMatrix< T > &params)
compute the principal curvatures (c1, c2) at the given parameters params
Definition gsFitting.hpp:237
bool is_corner(gsMatrix< T > &parametric_domain, gsVector< T > &parameter)
check if the given parameter parameter is a corner of the domain parametric_domain
Definition gsFitting.hpp:484
void computeErrors()
Definition gsFitting.hpp:907
gsMatrix< T > eval(const gsMatrix< T > &u) const
Evaluate the function,.
Definition gsFunctionSet.hpp:120
Class that represents the (tensor) Gauss-Legendre quadrature rule.
Definition gsGaussRule.h:28
Abstract base class representing a geometry map.
Definition gsGeometry.h:93
memory::unique_ptr< gsGeometry > uPtr
Unique pointer for gsGeometry.
Definition gsGeometry.h:100
T closestPointTo(const gsVector< T > &pt, gsVector< T > &result, const T accuracy=1e-6, const bool useInitialPoint=false) const
Definition gsGeometry.hpp:339
A matrix with arbitrary coefficient type and fixed or dynamic size.
Definition gsMatrix.h:41
T at(index_t i) const
Returns the i-th element of the vectorization of the matrix.
Definition gsMatrix.h:211
virtual void mapTo(const gsVector< T > &lower, const gsVector< T > &upper, gsMatrix< T > &nodes, gsVector< T > &weights) const
Maps quadrature rule (i.e., points and weights) from the reference domain to an element.
Definition gsQuadRule.h:177
Sparse matrix class, based on gsEigen::SparseMatrix.
Definition gsSparseMatrix.h:139
A vector with arbitrary coefficient type and fixed or dynamic size.
Definition gsVector.h:37
Represents a B-spline curve/function with one parameter.
Provides declaration of Basis abstract interface.
#define short_t
Definition gsConfig.h:35
#define index_t
Definition gsConfig.h:32
#define GISMO_ERROR(message)
Definition gsDebug.h:118
#define gsWarn
Definition gsDebug.h:50
#define GISMO_UNUSED(x)
Definition gsDebug.h:112
#define GISMO_ASSERT(cond, message)
Definition gsDebug.h:89
Generic expressions evaluator.
Generic expressions helper.
Defines different expressions.
Provides declaration of Geometry abstract interface.
This is the main header file that collects wrappers of Eigen for linear algebra.
Utility functions required by gsModeling classes.
Iterator over the elements of a tensor-structured grid.
The G+Smo namespace, containing all definitions for the library.
void threeOnDiag(const gsSparseMatrix< T > &block, gsSparseMatrix< T > &result)
Definition gsModelingUtils.hpp:664
S give(S &x)
Definition gsMemory.h:266
GISMO_DEPRECATED bool parameter(int s)
Returns the parameter value (false=0=start, true=1=end) that corresponds to side s.
Definition gsBoundary.h:1069