G+Smo  24.08.0
Geometry + Simulation Modules
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
gsFunction.hpp
Go to the documentation of this file.
1 
14 #include <gsCore/gsLinearAlgebra.h>
15 #include <gsCore/gsFuncData.h>
18 
19 #ifdef gsHLBFGS_ENABLED
20 #include <gsHLBFGS/gsHLBFGS.h>
21 #endif
22 //#include <gsOptimizer/gsGradientDescent.h>
24 
25 #pragma once
26 
27 
28 namespace gismo
29 {
30 
31 template <class T>
33 {
34  return gsFuncCoordinate<T>(*this,c);
35 }
36 
37 template <class T>
40 {
41  gsMatrix<T> result;
42  this->jacobian_into( u, result );
43  return result;
44 }
45 
46 template <class T>
48 {
49  // Compute component gradients as columns of result
50  deriv_into(u, result);
51 
52  // Reshape the matrix to get one Jacobian block per evaluation point
53  const short_t d = domainDim(); // dimension of domain
54  result.resize(d, result.size()/d); //transposed Jacobians
55  result.blockTransposeInPlace( targetDim() );
56 }
57 
58 template <class T>
59 void gsFunction<T>::div_into(const gsMatrix<T>& u, gsMatrix<T>& result) const
60 {
61 
62  // Compute component gradients as columns of result
63  deriv_into(u, result);
64  //gsDebug << "deriv_into result:\n" << result << "\n";
65 
66 
67  const index_t numPts = u.cols(); // number of points to compute at
68  const index_t d = domainDim(); // dimension of domain
69 
70  gsVector<T> tmp_div(numPts); // tmp. divergence storage
71  tmp_div.setZero(); // initialize by zeros
72  gsVector<T> resCol(d * d);
73 
74  for ( index_t p = 0; p < numPts; p++ ) { // for all evaluation points
75  resCol = result.col(p);
76  for ( index_t i = 0; i < d; i++ ) { tmp_div(p) += resCol(i * d + i); }
77  }
78  // Resizing the result to store the
79  result.resize(1, numPts);
80  result = tmp_div.transpose();
81 
82  //gsDebug << "div_into:\n" << result << "\n";
83 
84 }
85 
86 template <class T>
87 void gsFunction<T>::deriv_into(const gsMatrix<T>& u, gsMatrix<T>& result) const
88 {
89 
90  //gsDebug<< "Using finite differences (gsFunction::deriv_into) for derivatives.\n";
91  const index_t parDim = u.rows(); // dimension of domain
92  const index_t tarDim = targetDim(); // dimension of codomain
93  const index_t numPts = u.cols(); // number of points to compute at
94 
95  gsVector<T> delta(parDim);
96  gsVector<T> tmp(tarDim);
97 
98  gsMatrix<T> ev, uc(parDim,4);
99  result.resize( parDim * tarDim, numPts );
100 
101  for ( index_t p = 0; p < numPts; p++ ) // for all evaluation points
102  {
103  for ( index_t j = 0; j<parDim; j++ ) // for all variables
104  {
105  delta.setZero();
106  delta(j) = (T)(0.00001);
107  uc.col(0) = u.col(p)+delta;
108  uc.col(1) = u.col(p)-delta;
109  delta(j) = (T)(0.00002);
110  uc.col(2) = u.col(p)+delta;
111  uc.col(3) = u.col(p)-delta;
112  //m_geo.eval_into(u, tmp);
113  this->eval_into(uc, ev );
114  tmp=(8*( ev.col(0)- ev.col(1)) + ev.col(3) - ev.col(2) ) / (T)(0.00012);
115 
116  for (index_t c=0; c<tarDim; ++c) // for all components
117  result(c*parDim+j,p)=tmp(c);
118  }
119  }
120 }
121 
122 
123 template <class T>
124 void gsFunction<T>::deriv2_into( const gsMatrix<T>& u, gsMatrix<T>& result ) const
125 {
126  //gsDebug << "Using finite differences (gsFunction::deriv2_into) for second derivatives.\n";
127  const int d = u.rows(); // dimension of domain
128  const int n = targetDim(); // dimension of codomain
129  const int numPts = u.cols(); // number of points to compute at
130  gsVector<T> tmp( d );
131  gsMatrix<T> ev, uc( d, 3 ), ucm( d, 4 );
132  const int stride = d + d * ( d - 1 ) / 2; // number of second derivatives per component [= d(d+1)/2]
133  result.resize( n * stride , numPts );
134  for ( int thisPt = 0; thisPt < numPts; thisPt++ )
135  {
136  int r = d;
137  for ( int j = 0; j < d; j++ )
138  {
139  // pure 2nd derivs
140  tmp.setZero();
141  tmp( j ) = (T)( 0.00001 );
142  uc.col( 0 ) = u.col( thisPt ) + tmp;
143  uc.col( 1 ) = u.col( thisPt ) ;
144  uc.col( 2 ) = u.col( thisPt ) - tmp;
145  this->eval_into( uc, ev );
146  for ( int k = 0; k < n; k++ ) // for all coordinates
147  result( k * stride + j, thisPt ) =
148  ( ev( k, 0 ) - (T)(2) * ev( k, 1 ) + ev( k, 2 ) ) / (T)( 0.0000000001 ) ;
149  // mixed 2nd derivs
150  for ( int l = j + 1; l < d; l++ )
151  {
152  tmp( l ) = (T)( 0.00001 );
153  ucm.col( 0 ) = u.col( thisPt ) + tmp;
154  ucm.col( 3 ) = u.col( thisPt ) - tmp;
155  tmp( l ) = (T)( -0.00001 );
156  ucm.col( 1 ) = u.col( thisPt ) + tmp;
157  ucm.col( 2 ) = u.col( thisPt ) - tmp;
158  tmp( l ) = (T)( 0 );
159  this->eval_into( ucm, ev );
160  for ( int k = 0; k < n; k++ ) // for all coordinates
161  result( k * stride + r, thisPt ) =
162  ( ev( k, 0 ) - ev( k, 1 ) - ev( k, 2 ) + ev( k, 3 ) ) / (T)( 0.0000000004 ) ;
163  r++;
164  }
165  }
166  }
167 }
168 
169 
170 template <class T>
172 {
173  //gsDebug << "Using finite differences (gsFunction::laplacian) for computing Laplacian.\n";
174  int d = u.rows();
175  gsVector<T> tmp( d );
176  gsMatrix<T> res( d, u.cols());
177  for ( int j = 0; j < d; j++ )
178  {
179  tmp.setZero();
180  tmp( j, 0 ) = (T)( 0.0000000001 );
181  res.row( j ) = 16 * ( this->eval(u.colwise() + tmp ) +
182  this->eval(u.colwise() - tmp ) ) -
183  30 * ( this->eval( u ) );
184  tmp( j, 0 ) = (T)( 0.0000000002 );
185  res.row( j ) -= ( this->eval( u.colwise() - tmp ) +
186  this->eval( u.colwise() + tmp ) ) ;
187  res.row( j ) /= (T)( 0.0000000012 ) ;
188  }
189  return res;
190 }
191 
192 template<class T>
194  gsMatrix<T> & result,
195  const T accuracy, const bool useInitialPoint) const
196 {
197  result.resize(this->domainDim(), points.cols() );
198  gsVector<T> arg;
199  for ( index_t i = 0; i!= points.cols(); ++i)
200  {
201  if (useInitialPoint)
202  arg = result.col(i);
203  else
204  arg = this->parameterCenter();
205  //arg = _argMinNormOnGrid(16);
206 
207  const int iter = this->newtonRaphson(points.col(i), arg, true, accuracy, 250);
208  gsInfo << " "<< iter;
209  if (iter>100)
210  gsWarn<< "Inversion took "<<iter<<" steps for "<< points.col(i).transpose() <<" (result="<< arg.transpose()<< ")\n";
211 
212  if (-1==iter)
213  {
214  gsWarn<< "Inversion failed for: "<< points.col(i).transpose() <<" (result="<< arg.transpose()<< ")\n";
215  result.col(i).setConstant( std::numeric_limits<T>::infinity() );
216  }
217  else
218  result.col(i) = arg;
219  }
220 /* // alternative impl using closestPointTo
221  result.resize(parDim(), points.cols() );
222  gsVector<T> pt, arg;
223  for ( index_t i = 0; i!= points.cols(); ++i )
224  {
225  pt = points.col(i);
226  if (useInitialPoint)
227  arg = result.col(i);
228 
229  this->closestPointTo(pt, arg, accuracy, useInitialPoint);
230  if ( (this->eval(arg)-pt).norm()<=accuracy )
231  result.col(i) = arg;
232  else
233  {
234  //result.col(i) = arg;
235  result.col(i).setConstant( std::numeric_limits<T>::infinity() );
236  }
237  }
238 */
239 }
240 
241 template<class T>
242 void gsFunction<T>::invertPointGrid(gsGridIterator<T,0> & git,
243  gsMatrix<T> & result, const T accuracy,
244  const bool useInitialPoint) const
245 {
246  GISMO_UNUSED(useInitialPoint);
247  result.resize(this->domainDim(), git.numPoints() );
248  gsVector<T> arg;
249  auto cw = git.numPointsCwise();
250  git.reset();
251  index_t i = 0;
252  int iter = -1;
253  gsInfo << "Iterations: ";
254  for(;git; ++git)
255  {
256  if (-1==iter)
257  arg = this->parameterCenter();
258  else
259  arg = (i%cw[0]==0 ? result.col(i-cw[0]) : result.col(i-1) );
260 
261  iter = this->newtonRaphson(*git, arg, true, accuracy, 500);
262  if (100 < iter) gsInfo << " "<< iter;
263  if (-1==iter)
264  {
265  gsWarn<< "--- "<<i<<" Inversion failed for: "<< git->transpose() <<" ("
266  << (i%cw[0]==0 ? result.col(i-cw[0]) : result.col(i-1) ).transpose() << " -> " << arg.transpose()<< " )\n";
267  //result.col(i).setConstant( std::numeric_limits<T>::infinity() );
268  }
269  //else gsWarn<< "+Inversion OK "<< git->transpose() <<" (result="<< arg.transpose()<< ")\n";
270 
271  result.col(i++) = arg;
272  }
273  gsInfo << "\n";
274 }
275 
276 template <class T>
277 template <int mode,int _Dim>
278 int gsFunction<T>::newtonRaphson_impl(
279  const gsVector<T> & value,
280  gsVector<T> & arg,
281  bool withSupport,
282  const T accuracy,
283  int max_loop,
284  T damping_factor, T scale) const
285 {
286  const index_t n = value.rows();
287  const bool squareJac = (n == domainDim());//assumed for _Dim!=-1
288 
289  GISMO_ASSERT( arg.size() == domainDim(),
290  "Input argument has wrong dimensions: "<< arg.transpose() );
291 
292  gsMatrix<T> supp;
293  gsMatrix<T,_Dim,(_Dim==-1?-1:1)> delta , residual;
294  gsMatrix<T,_Dim,_Dim> jac;
295 
296  if (withSupport)
297  {
298  supp = support();
299  GISMO_ASSERT( (arg.array()>=supp.col(0).array()).all() &&
300  (arg.array()<=supp.col(1).array()).all(),
301  "Initial point is outside the domain.\n point = "<<arg<<"\n domain = "<<supp);
302  }
303  int iter = 1;
304  T rnorm[2]; rnorm[0]=1e100;
305  //T alpha=.5, beta=.5;
306  gsFuncData<T> fd(0==mode?(NEED_VALUE|NEED_DERIV):(NEED_DERIV|NEED_HESSIAN));
307 
308  do {
309  this->compute(arg,fd);
310  residual = (0==mode?fd.values[0]:fd.values[1]);
311 
312  residual.noalias() = value - scale*residual;
313  rnorm[iter%2] = residual.norm();
314 
315  if(rnorm[iter%2] <= accuracy) // residual below threshold
316  {
317  //gsInfo <<"--- OK: Accuracy "<<rnorm[iter%2]<<" reached after "<<iter <<"iterations.\n";
318  return iter;
319  }
320 
321  // compute Jacobian
322  if (0==mode)
323  jac = scale * fd.jacobian(0);
324  else // (1==mode)
325  jac = scale * fd.hessian(0);
326 
327  // Solve for next update
328  if (squareJac)
329  {
330  //const T ddet = jac.determinant();
331  //if (math::abs(ddet)<1e-4)
332  // gsWarn<< "Singular Jacobian: "<< ddet <<"\n";
333 
334  if (-1==_Dim)
335  delta.noalias() = jac.partialPivLu().solve( residual );
336  else
337  delta.noalias() = jac.inverse() * residual;
338  }
339  else// use pseudo-inverse
340  delta.noalias() = jac.colPivHouseholderQr().solve(
341  gsMatrix<T>::Identity(n,n)) * residual;
342 
343  const T rr = ( 1==iter ? (T)1.51 : rnorm[(iter-1)%2]/rnorm[iter%2] ); //important to start with small step
344  damping_factor = rr<1.5 ? math::max((T)0.1 + (rr/99),(rr-(T)0.5)*damping_factor) : math::min((T)1,rr*damping_factor);
345 
346  //Line search
347  /*
348  real_t val = (this->eval(arg + damping_factor*delta)*scale - value).norm();
349 
350  for(index_t i = 1; i!=11; ++i)
351  {
352  val = 0 ;
353  }
354  */
355 
356  //gsInfo << "Newton it " << iter << " arg=" << arg.transpose() << ", f(arg)="
357  // << (0==mode?fd.values[0]:fd.values[1]).transpose() << ", res=" << residual.transpose()
358  // <<" ("<<rr<<" ~ "<<damping_factor<<"), norm=" << rnorm[iter%2] << "\n";
359 
360  // update arg
361  arg += damping_factor * delta;
362 
363  if ( withSupport )
364  {
365  if ( delta.norm()<accuracy )
366  {
367  //gsInfo <<"OK: Newton reached boundary of support "<< delta.norm() <<"\n";
368  return iter;
369  }
370  arg = arg.cwiseMax( supp.col(0) ).cwiseMin( supp.col(1) );
371  }
372 
373  } while (++iter <= max_loop);
374 
375  gsWarn <<"--- Newton method did not converge after "<< max_loop
376  <<" iterations. Residual norm: "<< rnorm[max_loop%2]<<".\n";
377 
378  return -1;
379 }
380 
381 template <class T>
382 gsVector<T> gsFunction<T>::_argMinOnGrid(index_t numpts) const
383 {
384  gsVector<T> result;
385  gsMatrix<T> supp = this->support();
386  if (0!=supp.size())
387  {
388  result = this->parameterCenter();
389  gsGridIterator<T,CUBE> pt(supp, numpts);//per direction
390  T val, mval = this->eval(result).value();
391  for(;pt; ++pt)
392  {
393  val = this->eval(*pt).value();
394  if (val < mval )
395  {
396  mval = val;
397  result = *pt;
398  }
399  }
400  }
401  else
402  {
403  //take random points?
404  result.setZero( domainDim() );
405  }
406  return result;
407 }
408 
409 template <class T>
410 gsVector<T> gsFunction<T>::_argMinNormOnGrid(index_t numpts) const
411 {
412  gsVector<T> result;
413  gsMatrix<T> supp = this->support();
414  if (0!=supp.size())
415  {
416  result = this->parameterCenter();
417  gsGridIterator<T,CUBE> pt(supp, numpts);//per direction
418  T val, mval = this->eval(result).squaredNorm();
419  for(;pt; ++pt)
420  {
421  if ( (val = this->eval(*pt).squaredNorm())<mval )
422  {
423  mval = val;
424  result = *pt;
425  }
426  }
427  }
428  else
429  {
430  //take random points?
431  result.setZero( domainDim() );
432  }
433  return result;
434 }
435 
436 template <class T>
438  gsVector<T> & arg,
439  bool withSupport,
440  const T accuracy,
441  int max_loop,
442  T damping_factor) const
443 {
444  GISMO_ASSERT( value.rows() == targetDim(),
445  "Invalid input values:"<< value.rows()<<"!="<<targetDim());
446  return newtonRaphson_impl<0>(value,arg,withSupport,accuracy,
447  max_loop,damping_factor);
448 }
449 
450 template <class T>
451 gsMatrix<T> gsFunction<T>::argMin(const T accuracy,
452  int max_loop,
453  gsMatrix<T> init,
454  T damping_factor) const
455 {
456  GISMO_ASSERT(1==targetDim(), "Currently argMin works for scalar functions");
457  gsVector<T> result;
458 
459  // Initial point
460  if ( 0 != init.size() )
461  result = give(init);
462  else
463  {
464  result = _argMinOnGrid(20);
465  }
466 
467  #ifdef gsHLBFGS_ENABLED
468  gsFunctionAdaptor<T> fmin(*this);
469  // gsIpOpt<T> solver( &fmin );
470  //gsGradientDescent<T> solver( &fmin );
471  gsHLBFGS<T> solver( &fmin );
472  solver.options().setInt("MaxIterations",100);
473  solver.options().setInt("Verbose",0);
474  //MinStep..1e-12
475  solver.solve(result);
476  result = solver.currentDesign();
477  //gsDebugVar(result);
478  return result;
479 #else
480  int dd=domainDim();
481  switch (dd)
482  {
483  case 2:
484  newtonRaphson_impl<1,2>(gsVector<T>::Zero(dd), result, true,
485  accuracy,max_loop,damping_factor,(T)1);//argMax: (T)(-1)
486  break;
487  default:
488  newtonRaphson_impl<1>(gsVector<T>::Zero(dd), result, true,
489  accuracy,max_loop,damping_factor,(T)1);//argMax: (T)(-1)
490  }
491 #endif
492 
493  return result;
494 }
495 
496 //argMax
497 
498 /*
499 template <class T>
500 int gsFunction<T>::domainDim() const
501 { GISMO_NO_IMPLEMENTATION }
502 
503 template <class T>
504 int gsFunction<T>::targetDim() const
505 { GISMO_NO_IMPLEMENTATION }
506 */
507 
508 template<class T>
510  const T accuracy) const
511 {
512  gsVector<index_t> ind(xyz.rows()-1);
513  for (index_t i = 0; i!= xyz.rows(); ++i)
514  if (i<k) ind[i]=i;
515  else if (i>k) ind[i-1]=i;
516 
517  gsMatrix<T> pt = xyz(ind,gsEigen::all);
518  gsFuncCoordinate<T> fc(*this, give(ind));
519 
520  //find low accuracy closest point
521  // uv.resize(this->domainDim(), xyz.cols() );
522  // for (index_t i = 0; i!= xyz.cols(); ++i)
523  // {
524  // gsSquaredDistance2<T> dist2(fc, pt.col(i));
525  // uv.col(i) = dist2.argMin(accuracy, 10000) ;
526  // }
527 
528  fc.invertPoints(pt,uv,accuracy,false); //true
529  xyz = this->eval(uv);
530  //possible check: pt close to xyz
531 }
532 
533 template<class T>
534 void gsFunction<T>::recoverPointGrid(gsGridIterator<T,0> & git,
535  gsMatrix<T> & xyz, gsMatrix<T> & uv,
536  index_t k, const T accuracy) const
537 {
538  gsVector<index_t> ind(xyz.rows()-1);
539  for (index_t i = 0; i!= xyz.rows(); ++i)
540  if (i<k) ind[i]=i;
541  else if (i>k) ind[i-1]=i;
542 
543  gsMatrix<T> pt = xyz(ind,gsEigen::all);
544  gsFuncCoordinate<T> fc(*this, give(ind));
545 
546  fc.invertPointGrid(git,uv,accuracy,false); //true
547  xyz = this->eval(uv);
548 }
549 
550 template <class T>
552  const index_t,
553  gsMatrix<T>&) const
555 
556 template<class T>
558 {
559  gsMatrix<T> supp = this->support();
560  const index_t dim = supp.rows();
561  gsMatrix<T> coordinates(dim,1);
562  gsVector<bool> boxPar = bc.parameters(dim);
563  for (index_t d=0; d<dim;++d)
564  {
565  if (boxPar(d))
566  coordinates(d,0) = supp(d,1);
567  else
568  coordinates(d,0) = supp(d,0);
569  }
570  return coordinates;
571 }
572 
573 template<class T>
575 {
576  gsMatrix<T> supp = this->support();
577  const index_t dim = supp.rows();
578  gsMatrix<T> coordinates(dim,1);
579  const index_t dir = bc.direction();
580  for (index_t d=0; d<dim;++d)
581  {
582  if (d != dir)
583  coordinates(d,0) = ( supp(d,1) + supp(d,0) ) / (T)(2);
584  else if (bc.parameter())
585  coordinates(d,0) = supp(d,1);
586  else
587  coordinates(d,0) = supp(d,0);
588  }
589  return coordinates;
590 }
591 
592 template <class T> void
594  index_t coord) const
595 {
596  gsMatrix<T> secDers;
597  this->deriv2_into(u, secDers);
598  const index_t dim = this->domainDim();
599  const index_t nd = dim*(dim+1)/2;
600  result = util::secDerToHessian(secDers.middleCols(coord*nd,nd), dim);
601 }
602 
603 template <typename T, short_t domDim, short_t tarDim>
604 inline void computeAuxiliaryData(const gsFunction<T> &src, gsMapData<T> & InOut, int d, int n)
605 {
606  //GISMO_ASSERT( domDim*tarDim == 1, "Both domDim and tarDim must have the same sign");
607  const index_t numPts = InOut.points.cols();
608 
609  // If the measure on a boundary is requested, calculate the outer normal vector as well
610  if ( InOut.side!=boundary::none && (InOut.flags & NEED_MEASURE) ) InOut.flags|=NEED_OUTER_NORMAL;
611 
612  // Outer normal vector
613  if ( InOut.flags & NEED_OUTER_NORMAL)
614  {
615  if (InOut.side==boundary::none)
616  gsWarn<< "Computing boundary normal without a valid side.\n";
617  // gsDebugVar( InOut.side);
618  const T sgn = sideOrientation(InOut.side);
619  // gsDebugVar( sgn );
620  const int dir = InOut.side.direction();
621  InOut.outNormals.resize(n,numPts);
622 
623  if (tarDim!=-1 ? tarDim == domDim : n==d)
624  {
625  if ( 1==n ) { InOut.outNormals.setConstant(sgn); return; } // 1D case
626 
627  T det_sgn = 0;
628  typename gsMatrix<T,domDim,tarDim>::FirstMinorMatrixType minor;
629  // Determine Jacobian determinant's sign, assume constant along boundary
630  if (InOut.flags & SAME_ELEMENT )
631  {
632  for (index_t p=0; p!=numPts; ++p)
633  {
634  const gsAsConstMatrix<T,domDim,tarDim> jacT(InOut.values[1].col(p).data(), d, n);
635  T detJacTcurr = jacT.determinant();
636  if ( math::abs(detJacTcurr) >= 1e-7 )
637  {
638  det_sgn = detJacTcurr < 0 ? -1 : 1;
639  break;
640  }
641  }
642  if ( 0 == det_sgn )
643  {
644  gsMatrix<T> parameterCenter = src.parameterCenter(InOut.side);
645  T detJacTcurr = src.jacobian(parameterCenter).determinant();
646  det_sgn = detJacTcurr < 0 ? -1 : 1;
647  }
648  }
649  for (index_t p=0; p!=numPts; ++p)
650  {
651  const gsAsConstMatrix<T,domDim,tarDim> jacT(InOut.values[1].col(p).data(), d, n);
652  // BUG: When the determinant is really close to zero but negative,
653  // the result might be the opposite of what is expected because of alt_sgn
654 
655  if (! (InOut.flags &SAME_ELEMENT) )
656  {
657  T detJacTcurr = jacT.determinant();
658  det_sgn = math::abs(detJacTcurr) < 1e-7 ? 0 :
659  ( detJacTcurr < 0 ? -1 : 1 );
660  if ( 0 == det_sgn )
661  {
662  gsMatrix<T> parameterCenter = src.parameterCenter(InOut.side);
663  detJacTcurr = src.jacobian(parameterCenter).determinant();
664  det_sgn = detJacTcurr < 0 ? -1 : 1;
665  }
666  }
667  GISMO_ENSURE(det_sgn!=0, "Cannot find a non-zero Jacobian determinant.\n" << InOut.points);
668 
669  T alt_sgn = sgn * det_sgn;
670  for (int i = 0; i != n; ++i) //for all components of the normal
671  {
672  jacT.firstMinor(dir, i, minor);
673  InOut.outNormals(i,p) = alt_sgn * minor.determinant();
674  alt_sgn *= -1;
675  }
676  }
677  }
678  else // lower-dim boundary case, d + 1 == n
679  {
680  gsMatrix<T,domDim,domDim> metric(d,d);
681  gsVector<T,domDim> param(d);
682  typename gsMatrix<T,domDim,domDim>::FirstMinorMatrixType minor;
683  for (index_t p=0; p!=numPts; ++p)
684  {
685  const gsAsConstMatrix<T,domDim,tarDim> jacT(InOut.values[1].col(p).data(), d, n);
686  metric= (jacT*jacT.transpose());
687  T alt_sgn = sgn;
688  for (int i = 0; i != (domDim!=-1?domDim:d); ++i) //for all components of the normal
689  {
690  metric.firstMinor(dir, i, minor);
691  param(i) = alt_sgn * minor.determinant();
692  alt_sgn *= -1;
693  }
694  InOut.outNormals.col(p)=jacT.transpose()*param/metric.determinant();
695  //InOut.outNormals.col(p)=(jacT.transpose()*param).normalized()*jacT.col(!dir).norm();
696  }
697  }
698 
699  }
700 
701  // Measure
702  if (InOut.flags & NEED_MEASURE)
703  {
704  InOut.measures.resize(1,numPts);
705  if (InOut.side==boundary::none) // If in the domain's interior
706  {
707  for (index_t p = 0; p < numPts; ++p) // for all points
708  {
709  typename gsAsConstMatrix<T,domDim,tarDim>::Tr jac =
710  gsAsConstMatrix<T,domDim,tarDim>(InOut.values[1].col(p).data(),d, n).transpose();
711  if ( tarDim!=-1 ? tarDim == domDim : n==d )
712  InOut.measures(0,p) = math::abs(jac.determinant());
713  else //unequal dimensions
714  InOut.measures(0,p) = math::sqrt( ( jac.transpose()*jac )
715  .determinant() );
716  }
717  }
718  else // If on boundary
719  {
720  GISMO_ASSERT(d==2, "Only works for boundary curves..");
721  const int dir = InOut.side.direction();
722  typename gsMatrix<T,domDim,tarDim>::ColMinorMatrixType minor;
723  InOut.measures.resize(1, numPts);
724  for (index_t p = 0; p != numPts; ++p) // for all points
725  {
726  const gsAsConstMatrix<T,domDim,tarDim> jacT(InOut.values[1].col(p).data(), d, n);
727  InOut.measures.at(p) = jacT.row(!dir).norm();
728  }
729  //InOut.measures = InOut.outNormals.colwise().norm(); // problematic on 3d curve boundary
730  }
731  }
732 
733  if (InOut.flags & NEED_GRAD_TRANSFORM)
734  {
735  // domDim<=tarDim makes sense
736 
737  InOut.jacInvTr.resize(d*n, numPts);
738  for (index_t p=0; p!=numPts; ++p)
739  {
740  const gsAsConstMatrix<T,domDim,tarDim> jacT(InOut.values[1].col(p).data(), d, n);
741 
742  if ( tarDim!=-1 ? tarDim == domDim : n==d )
743  gsAsMatrix<T,tarDim,domDim>(InOut.jacInvTr.col(p).data(), n, d)
744  = jacT.cramerInverse();
745  else
746  {
747  gsAsMatrix<T,tarDim,domDim>(InOut.jacInvTr.col(p).data(), n, d)
748  = jacT.transpose()*(jacT*jacT.transpose()).cramerInverse();
749  }
750  }
751  }
752 
753  // Normal vector of hypersurface
754  if ( (InOut.flags & NEED_NORMAL) && (tarDim!=-1 ? tarDim == domDim+1 : n==d+1) )
755  {
756  typename gsMatrix<T,domDim,tarDim>::ColMinorMatrixType minor;
757  InOut.normals.resize(n, numPts);
758 
759  for (index_t p = 0; p != numPts; ++p) // for all points
760  {
761  const gsAsConstMatrix<T,domDim,tarDim> jacT(InOut.values[1].col(p).data(), d, n);
762  T alt_sgn(1);
763  for (int i = 0; i != tarDim; ++i) //for all components of the normal
764  {
765  jacT.colMinor(i, minor);
766  InOut.normals(i,p) = alt_sgn * minor.determinant();
767  alt_sgn = -alt_sgn;
768  }
769  }
770  //gsDebugVar(InOut.normals);
771  }
772 
773  // Second Fundamantan form of surface
774  if (InOut.flags & NEED_2ND_FFORM)
775  {
776  // gsDebugVar(src);
777  // gsDebugVar(InOut.normals);
778  // gsDebugVar(n);
779  // gsDebugVar(d+1);
780  //domDim=2, tarDim=3
781  InOut.fundForms.resize(d*d, numPts);
782  const index_t sz = d*(d+1)/2;
783  for (index_t p=0; p!=numPts; ++p)
784  {
785  const gsAsConstMatrix<T,-1,tarDim> ddT(InOut.values[2].col(p).data(), sz, n);
786  const T nrm = InOut.normals.col(p).norm();
787  if (0!=nrm)
788  {
789  InOut.fundForms(0,p) = ddT.row(0).dot(InOut.normals.col(p)) / nrm;
790  InOut.fundForms(3,p) = ddT.row(1).dot(InOut.normals.col(p)) / nrm;
791  InOut.fundForms(1,p) = InOut.fundForms(2,p) =
792  ddT.row(2).dot(InOut.normals.col(p)) / nrm;
793  }
794  }
795  }
796 
797 
798 
799  /*
800  // Curvature of isoparametric curve
801  if ( InOut.flags & NEED_CURVATURE)
802  {
803  //domDim=2, tarDim=3
804  const int dir = InOut.side.direction();
805  const index_t sz = domDim*(domDim+1)/2;
806  InOut.curvature.resize(1,numPts);
807  for (index_t p=0; p!=numPts; ++p)
808  {
809  const gsAsConstMatrix<T,domDim,tarDim> jacT(InOut.values[1].col(p).data(), d, n);
810  const gsAsConstMatrix<T,-1,tarDim> ddT(InOut.values[2].col(p).data(), sz, n);
811  const T nrm = ddT.row(dir).norm(); // (d^2/dir^2)G
812  if (0!=nrm)
813  InOut.curvature.at(p) = jacT.row(dir).cross(ddT.row(dir)).norm() / (nrm*nrm*nrm);
814  }
815  }
816  */
817 }
818 
819 
820 // Computes map data out of this map
821 template <class T>
823 {
824  // Fill function data
825  if ( InOut.flags & (NEED_GRAD_TRANSFORM|NEED_MEASURE|NEED_NORMAL|NEED_OUTER_NORMAL) ) //NEED_JACOBIAN
826  InOut.flags |= NEED_DERIV;
827  if ( InOut.flags & (NEED_2ND_FFORM) ) //NEED_HESSIAN
828  InOut.flags |= NEED_DERIV | NEED_DERIV2 | NEED_NORMAL;
829 
830  this->compute(InOut.points, InOut);
831 
832  // Fill extra data
833  std::pair<short_t, short_t> Dim = this->dimensions();
834 
835  GISMO_ASSERT(Dim.first<10, "Domain dimension is too big");
836  GISMO_ASSERT(Dim.first<=Dim.second, "Singular map: target dimension is lower then the domain dimension");
837  switch (10 * Dim.second + Dim.first)
838  {
839  // curves
840  case 11: computeAuxiliaryData<T,1,1>(*this, InOut, Dim.first, Dim.second); break;
841  case 21: computeAuxiliaryData<T,1,2>(*this, InOut, Dim.first, Dim.second); break;
842 // case 31: computeAuxiliaryData<T,1,3>(*this, InOut, Dim.first, Dim.second); break;
843 // case 41: computeAuxiliaryData<T,1,4>(*this, InOut, Dim.first, Dim.second); break;
844  // surfaces
845 // case 12: computeAuxiliaryData<T,2,1>(*this, InOut, Dim.first, Dim.second); break;
846  case 22: computeAuxiliaryData<T,2,2>(*this, InOut, Dim.first, Dim.second); break;
847  case 32: computeAuxiliaryData<T,2,3>(*this, InOut, Dim.first, Dim.second); break;
848 // case 42: computeAuxiliaryData<T,2,4>(*this, InOut, Dim.first, Dim.second); break;
849 // volumes
850 // case 13: computeAuxiliaryData<T,3,1>(*this, InOut, Dim.first, Dim.second); break;
851 // case 23: computeAuxiliaryData<T,3,2>(*this, InOut, Dim.first, Dim.second); break;
852  case 33: computeAuxiliaryData<T,3,3>(*this, InOut, Dim.first, Dim.second); break;
853 // case 43: computeAuxiliaryData<T,3,4>(InOut, Dim.first, Dim.second); break;
854 // 4D bulks
855 // case 14: computeAuxiliaryData<T,4,1>(*this, InOut, Dim.first, Dim.second); break;
856 // case 24: computeAuxiliaryData<T,4,2>(*this, InOut, Dim.first, Dim.second); break;
857 // case 34: computeAuxiliaryData<T,4,3>(*this, InOut, Dim.first, Dim.second); break;
858  case 44: computeAuxiliaryData<T,4,4>(*this, InOut, Dim.first, Dim.second); break;
859  default: computeAuxiliaryData<T,-1,-1>(*this, InOut, Dim.first, Dim.second); break;
860  }
861 
862 }
863 
864 
865 } // namespace gismo
virtual void eval_component_into(const gsMatrix< T > &u, const index_t comp, gsMatrix< T > &result) const
Evaluate the function for component comp in the target dimension at points u into result...
Definition: gsFunction.hpp:551
Class defining an adaptor for using a gsFunction in an optimization problem.
#define GISMO_NO_IMPLEMENTATION
Definition: gsDebug.h:129
virtual void invertPoints(const gsMatrix< T > &points, gsMatrix< T > &result, const T accuracy=1e-6, const bool useInitialPoint=false) const
Definition: gsFunction.hpp:193
Gradient of the object.
Definition: gsForwardDeclarations.h:73
virtual void computeMap(gsMapData< T > &InOut) const
Computes map function data.
Definition: gsFunction.hpp:822
Matrix< Scalar, Dynamic, Dynamic > cramerInverse() const
Inversion for small matrices using Cramer&#39;s Rule.
Definition: gsMatrixAddons.h:55
virtual gsMatrix< T > parameterCenter() const
Returns a &quot;central&quot; point inside inside the parameter domain.
Definition: gsFunction.h:261
#define short_t
Definition: gsConfig.h:35
int newtonRaphson(const gsVector< T > &value, gsVector< T > &arg, bool withSupport=true, const T accuracy=1e-6, int max_loop=100, T damping_factor=1) const
Definition: gsFunction.hpp:437
bool parameter() const
Returns the parameter value (false=0=start, true=1=end) that corresponds to this side.
Definition: gsBoundary.h:128
Outward normal on the boundary.
Definition: gsForwardDeclarations.h:86
the gsMapData is a cache of pre-computed function (map) values.
Definition: gsFuncData.h:324
S give(S &x)
Definition: gsMemory.h:266
Second derivatives.
Definition: gsForwardDeclarations.h:80
#define index_t
Definition: gsConfig.h:32
#define GISMO_ENSURE(cond, message)
Definition: gsDebug.h:102
void recoverPoints(gsMatrix< T > &xyz, gsMatrix< T > &uv, index_t k, const T accuracy=1e-6) const
Definition: gsFunction.hpp:509
A function from a n-dimensional domain to an m-dimensional image.
Definition: gsFunction.h:59
#define GISMO_ASSERT(cond, message)
Definition: gsDebug.h:89
#define gsWarn
Definition: gsDebug.h:50
Normal vector of the object.
Definition: gsForwardDeclarations.h:85
Hessian matrix.
Definition: gsForwardDeclarations.h:82
#define gsInfo
Definition: gsDebug.h:43
void blockTransposeInPlace(const index_t colBlock)
Transposes in place the matrix block-wise. The matrix is.
Definition: gsMatrix.h:428
virtual void deriv_into(const gsMatrix< T > &u, gsMatrix< T > &result) const
Evaluate derivatives of the function at points u into result.
Definition: gsFunction.hpp:87
Struct which represents a certain side of a box.
Definition: gsBoundary.h:84
gsFuncCoordinate< T > coord(const index_t c) const
Returns the scalar function giving the i-th coordinate of this function.
Definition: gsFunction.hpp:32
Second fundamental form.
Definition: gsForwardDeclarations.h:87
int sideOrientation(short_t s)
Definition: gsBoundary.h:1029
The density of the measure pull back.
Definition: gsForwardDeclarations.h:76
Enable optimizations based on the assumption that all evaluation points are in the same bezier domain...
Definition: gsForwardDeclarations.h:89
void div_into(const gsMatrix< T > &u, gsMatrix< T > &result) const
Computes for each point u a block of result containing the divergence matrix.
Definition: gsFunction.hpp:59
Provides definition of the FuncCoordinate class.
Value of the object.
Definition: gsForwardDeclarations.h:72
#define GISMO_UNUSED(x)
Definition: gsDebug.h:112
virtual gsMatrix< T > laplacian(const gsMatrix< T > &u) const
Evaluate the Laplacian at points u.
Definition: gsFunction.hpp:171
This is the main header file that collects wrappers of Eigen for linear algebra.
EIGEN_STRONG_INLINE abs_expr< E > abs(const E &u)
Absolute value.
Definition: gsExpressions.h:4488
gsMatrix< T > points
input (parametric) points
Definition: gsFuncData.h:348
virtual void deriv2_into(const gsMatrix< T > &u, gsMatrix< T > &result) const
Evaluate second derivatives of the function at points u into result.
Definition: gsFunction.hpp:124
Gradient transformation matrix.
Definition: gsForwardDeclarations.h:77
Represents a certain component of a vector-valued function.
Definition: gsFuncCoordinate.h:34
Provides iteration over integer or numeric points in a (hyper-)cube.
This object is a cache for computed values from an evaluator.
Struct which represents a certain corner of a hyper-cube.
Definition: gsBoundary.h:291
short_t direction() const
Returns the parametric direction orthogonal to this side.
Definition: gsBoundary.h:113
virtual void jacobian_into(const gsMatrix< T > &u, gsMatrix< T > &result) const
Computes for each point u a block of result containing the Jacobian matrix.
Definition: gsFunction.hpp:47
EIGEN_STRONG_INLINE jac_expr< E > jac(const symbol_expr< E > &u)
The Jacobian matrix of a FE variable.
Definition: gsExpressions.h:4530