void constructPreconditionerAndSolve(O& opA, DuneMatrix& istlAe, Vector& x, Vector& istlb, const P& parallelInformation_arg, const P& parallelInformationAe, Dune::InverseOperatorResult& result) const { typedef Dune::ScalarProductChooser<Vector,P,category> ScalarProductChooser; std::unique_ptr<typename ScalarProductChooser::ScalarProduct> sp(ScalarProductChooser::construct(parallelInformation_arg)); // Construct preconditioner. // typedef Dune::SeqILU0<Mat,Vector,Vector> Preconditioner; typedef Opm::CPRPreconditioner<Mat,Vector,Vector,P> Preconditioner; parallelInformation_arg.copyOwnerToAll(istlb, istlb); Preconditioner precond(cpr_param_, opA.getmat(), istlAe, parallelInformation_arg, parallelInformationAe); // TODO: Revise when linear solvers interface opm-core is done // Construct linear solver. // GMRes solver if ( newton_use_gmres_ ) { Dune::RestartedGMResSolver<Vector> linsolve(opA, *sp, precond, linear_solver_reduction_, linear_solver_restart_, linear_solver_maxiter_, linear_solver_verbosity_); // Solve system. linsolve.apply(x, istlb, result); } else { // BiCGstab solver Dune::BiCGSTABSolver<Vector> linsolve(opA, *sp, precond, linear_solver_reduction_, linear_solver_maxiter_, linear_solver_verbosity_); // Solve system. linsolve.apply(x, istlb, result); } }
void solveElliptic(Y& x, Y& de) { // Linear solver parameters const double tolerance = param_.cpr_solver_tol_; const int maxit = param_.cpr_max_ell_iter_; const int verbosity = ( param_.cpr_solver_verbose_ && comm_.communicator().rank()==0 ) ? 1 : 0; // operator result containing iterations etc. Dune::InverseOperatorResult result; // the scalar product chooser typedef Dune::ScalarProductChooser<X,ParallelInformation,category> ScalarProductChooser; // the scalar product. std::unique_ptr<typename ScalarProductChooser::ScalarProduct> sp(ScalarProductChooser::construct(commAe_)); if( amg_ ) { // Solve system with AMG if( param_.cpr_use_bicgstab_ ) { Dune::BiCGSTABSolver<X> linsolve(*opAe_, *sp, (*amg_), tolerance, maxit, verbosity); linsolve.apply(x, de, result); } else { Dune::CGSolver<X> linsolve(*opAe_, *sp, (*amg_), tolerance, maxit, verbosity); linsolve.apply(x, de, result); } } else { assert( precond_ ); // Solve system with ILU-0 if( param_.cpr_use_bicgstab_ ) { Dune::BiCGSTABSolver<X> linsolve(*opAe_, *sp, (*precond_), tolerance, maxit, verbosity); linsolve.apply(x, de, result); } else { Dune::CGSolver<X> linsolve(*opAe_, *sp, (*precond_), tolerance, maxit, verbosity); linsolve.apply(x, de, result); } } if (!result.converged) { OPM_THROW(LinearSolverProblem, "CPRPreconditioner failed to solve elliptic subsystem."); } }
Y solveElliptic(Y& de) { // std::cout << "solveElliptic()" << std::endl; // Construct operator, scalar product and vectors needed. typedef Dune::MatrixAdapter<M,X,X> Operator; Operator opAe(Ae_); Dune::SeqScalarProduct<X> sp; // Right hand side. // System solution X x(opAe.getmat().M()); x = 0.0; // Construct preconditioner. typedef typename Dune::SeqILU0<M,X,X> Preconditioner; const double relax = 1.0; Preconditioner precond(Ae_, relax); // Construct linear solver. const double tolerance = 1e-4; const int maxit = 5000; const int verbosity = 0; Dune::BiCGSTABSolver<X> linsolve(opAe, sp, precond, tolerance, maxit, verbosity); // Solve system. Dune::InverseOperatorResult result; linsolve.apply(x, de, result); if (!result.converged) { OPM_THROW(std::runtime_error, "CPRPreconditioner failed to solve elliptic subsystem."); } return x; }
void solve(Operator& opA, Vector& x, Vector& istlb, ScalarProd& sp, Precond& precond, Dune::InverseOperatorResult& result) const { // TODO: Revise when linear solvers interface opm-core is done // Construct linear solver. // GMRes solver if ( parameters_.newton_use_gmres_ ) { Dune::RestartedGMResSolver<Vector> linsolve(opA, sp, precond, parameters_.linear_solver_reduction_, parameters_.linear_solver_restart_, parameters_.linear_solver_maxiter_, parameters_.linear_solver_verbosity_); // Solve system. linsolve.apply(x, istlb, result); } else { // BiCGstab solver Dune::BiCGSTABSolver<Vector> linsolve(opA, sp, precond, parameters_.linear_solver_reduction_, parameters_.linear_solver_maxiter_, parameters_.linear_solver_verbosity_); // Solve system. linsolve.apply(x, istlb, result); } }
/* Fit frequency-domain data with photobleaching. */ int fitexpsin( char *data, /* data array of doubles */ int data_stride, /* number of bytes to move from one data value to next */ int numdata, /* number of double values in data array */ double *poly, /* precalculated normalized Chebyshev polynomial Tj(t) */ double *coef, /* buffer for dnj of shape (numexps+1, numcoef+1) */ int numcoef, /* number of coefficients */ double deltat, /* duration between data points */ int startcoef, /* start coefficient. usually equals numexps-1 */ double *buff, /* working buffer of shape (numexps, numdata) */ double *result, /* buffer to receive fitted parameters offset, tau, frq, amp[numexps] */ char *fitt, /* buffer to receive fitted data in double [numpoints] */ int fitt_stride) /* number bytes to move from one fitted value to next */ { PyThreadState *_save = NULL; Py_complex xroots[5]; Py_complex xcoefs[6]; double matrix[3*3]; double *pbuff; double *ppoly; double *pcoef; double *pmat; double *pvec; double *prow; double *pcol; double *pdn0; double *pdn1; double *off = result; double *rat = result + 1; double *amp = result + 2; double sum, temp, ratn, frqn; double cosw, cos2w, t0, t1, t2, t3, t4, t5, t6; int i, j, k, t, n, N, row, col, error; int stride = numcoef + 1; /* discrete Chebyshev coefficients dj */ ppoly = poly; pcoef = coef; if (data_stride == sizeof(double)) { double *pdata; for (j = 0; j < numcoef; j++) { pdata = (double *)data; sum = 0.0; for (t = 0; t < numdata; t++) { sum += (*pdata++) * (*ppoly++); } *pcoef++ = sum; } } else { char *pdata; for (j = 0; j < numcoef; j++) { pdata = data; sum = 0.0; for (t = 0; t < numdata; t++) { sum += (*((double *) pdata)) * (*ppoly++); pdata += data_stride; } *pcoef++ = sum; } } _save = PyEval_SaveThread(); /* integral coefficients dnj */ N = numdata - 1; pdn0 = coef; pdn1 = coef + stride; for (n = 0; n < 3; n++) { pdn0[numcoef] = 0.0; pdn1[0] = 0.0; for (j = 1; j < numcoef; j++) { pdn1[j] = ((N + j + 2) * pdn0[j + 1] / (2*j + 3) - pdn0[j] - (N - j + 1) * pdn0[j - 1] / (2*j - 1)) / 2.0; } pdn0 += stride; pdn1 += stride; } /* cubic polynomial coefficients a + bx + cx^2 + dx^3 */ frqn = TWOPI / numdata; cosw = cos(frqn); cos2w = cos(frqn * 2.0); t0 = 1.0 / (1.0 + 2.0*cosw); t1 = t0 * t0 * t0; t2 = 9.0 * t0 - 3.0; t3 = (16.0*cosw - 4.0*cosw*cosw + 8.0*cos2w - 8.0*cosw*cos2w - 12.0) * t1; t4 = 2.0 - 6.0 * t0; t5 = (14.0 - 14.0*cosw - 8.0*cos2w + 4.0*cosw*cosw + 4.0*cosw*cos2w) * t1; t6 = (4.0*cosw + 2.0*cos2w - 6.0) * t1; pbuff = buff; for (j = startcoef; j < (numcoef - 3); j++) { *pbuff++ = coef[j] + coef[j+stride*2] * t2 + coef[j+stride*3] * t3; *pbuff++ = coef[j+stride] + coef[j+stride*2] * t4 + coef[j+stride*3] * t5; *pbuff++ = coef[j+stride*2] * t0 + coef[j+stride*3] * t6; *pbuff++ = coef[j+stride*3] * t1; } /* regression coefficients */ /* quintic polynomial (a + bx + cx^2 + dx^3)*(b + 2cx + 3dx^2) */ memset(xcoefs, 0, 6 * sizeof(Py_complex)); pbuff = buff; for (j = startcoef; j < (numcoef - 3); j++) { xcoefs[0].real += pbuff[0]*pbuff[1]; xcoefs[1].real += pbuff[0]*pbuff[2]*2.0 + pbuff[1]*pbuff[1]; xcoefs[2].real += pbuff[1]*pbuff[2]*3.0 + pbuff[0]*pbuff[3]*3.0; xcoefs[3].real += pbuff[2]*pbuff[2]*2.0 + pbuff[1]*pbuff[3]*4.0; xcoefs[4].real += pbuff[2]*pbuff[3]*5.0; xcoefs[5].real += pbuff[3]*pbuff[3]*3.0; pbuff += 4; } /* roots of quintic polynomial */ error = polyroots(6, xcoefs, xroots); if (error != 0) { PyEval_RestoreThread(_save); return error; } /* decay rate and frequency of harmonics */ /* find smallest chi-square */ t0 = DBL_MAX; for (i = 0; i < 5; i++) { pbuff = buff; t1 = xroots[i].real; t2 = t1*t1; t3 = t1*t1*t1; sum = 0.0; for (j = 0; j < (numcoef - startcoef - 3); j++) { temp = pbuff[0] + t1*pbuff[1] + t2*pbuff[2] + t3*pbuff[3]; sum += temp*temp; pbuff += 4; } if (sum < t0) { t0 = sum; k = i; } } *rat = ratn = log((3.0 - xroots[k].real) / (2.0*cosw + 1.0)); /* fitting amplitudes */ /* Chebyshev transform signal for each exponential component */ pcoef = coef + stride; /* decay component */ buff[0] = 1.0; for (t = 1; t < numdata; t++) buff[t] = exp(ratn*(double)t); ppoly = poly; for (j = 0; j < numcoef; j++) { sum = 0.0; for (t = 0; t < numdata; t++) sum += buff[t] * (*ppoly++); *pcoef++ = sum; } pcoef++; /* sine component */ pbuff = buff + numdata; pbuff[0] = 0.0; for (t = 1; t < numdata; t++) pbuff[t] = -buff[t] * sin(frqn*(double)t); ppoly = poly; for (j = 0; j < numcoef; j++) { sum = 0.0; for (t = 0; t < numdata; t++) sum += pbuff[t] * (*ppoly++); *pcoef++ = sum; } pcoef++; /* cosine component */ pbuff += numdata; pbuff[0] = 1.0; for (t = 1; t < numdata; t++) pbuff[t] = buff[t] * cos(frqn*(double)t); ppoly = poly; for (j = 0; j < numcoef; j++) { sum = 0.0; for (t = 0; t < numdata; t++) sum += pbuff[t] * (*ppoly++); *pcoef++ = sum; } pcoef++; *rat = -deltat / *rat; /* regression matrix for fitting amplitudes */ pmat = matrix; pcol = coef; for (col = 0; col < 3; col++) { pcol += stride; prow = coef; for (row = 0; row < 3; row++) { prow += stride; sum = 0.0; for (j = 1; j < numcoef; j++) sum += prow[j] * pcol[j]; *pmat++ = sum; } } /* regression vector for fitting amplitudes */ pvec = amp; pcoef = coef; for (row = 0; row < 3; row++) { pcoef += stride; sum = 0.0; for (j = 1; j < numcoef; j++) sum += coef[j] * pcoef[j]; *pvec++ = sum; } /* solve linear equation system for amplitudes */ error = linsolve(3, matrix, amp); if (error != 0) { PyEval_RestoreThread(_save); return error; } /* calculate offset from zero Chebyshev coefficients */ pcoef = coef + stride; temp = *coef; for (n = 0; n < 3; n++) { temp -= amp[n] * (*pcoef); pcoef += stride; } *off = temp; /* calculate fitted data */ if (fitt != NULL) { if (fitt_stride == sizeof(double)) { double *pfitt = (double *)fitt; temp = *off; for (t = 0; t < numdata; t++) *pfitt++ = temp; pbuff = buff; for (n = 0; n < 3; n++) { pfitt = (double *)fitt; temp = amp[n]; for (t = 0; t < numdata; t++) *pfitt++ += temp * (*pbuff++); } } else { char *pfitt = fitt; temp = *off; for (t = 0; t < numdata; t++) { *(double *)pfitt = temp; pfitt += fitt_stride; } pbuff = buff; for (n = 0; n < 3; n++) { pfitt = fitt; temp = amp[n]; for (t = 0; t < numdata; t++) { *(double *)pfitt += temp * (*pbuff++); pfitt += fitt_stride; } } } } PyEval_RestoreThread(_save); return 0; }
/* Fit multiple exponential function. */ int fitexps( char *data, /* data array of doubles */ int data_stride, /* number of bytes to move from one data value to next */ int numdata, /* number of double values in data array */ double *poly, /* precalculated normalized Chebyshev polynomial Tj(t) */ double *coef, /* buffer for dnj of shape (numexps+1, numcoef+1) */ int numcoef, /* number of coefficients */ int numexps, /* number of exponentials to fit */ double deltat, /* duration between data points */ int startcoef, /* start coefficient. usually equals numexps-1 */ double *buff, /* working buffer of shape (numexps, numdata) */ double *result, /* buffer to receive fitted parameters offset, amp[numexps], tau[numexps], frq[numexps] */ char *fitt, /* buffer to receive fitted data in double [numpoints] */ int fitt_stride) /* number bytes to move from one fitted value to next */ { PyThreadState *_save = NULL; Py_complex xroots[MAXEXPS]; Py_complex xcoefs[MAXEXPS+1]; double matrix[MAXEXPS*MAXEXPS]; double vector[MAXEXPS]; double *pbuff; double *ppoly; double *pcoef; double *pmat; double *pvec; double *prow; double *pcol; double *pdn0; double *pdn1; double *off = result; double *amp = result + 1; double *rat = result + 1 + numexps; double *frq = result + 1 + numexps + numexps; double sum, temp, frqn, ratn; int j, t, n, N, row, col, error; int stride = numcoef + 1; /* discrete Chebyshev coefficients dj */ ppoly = poly; pcoef = coef; if (data_stride == sizeof(double)) { double *pdata; for (j = 0; j < numcoef; j++) { pdata = (double *)data; sum = 0.0; for (t = 0; t < numdata; t++) { sum += (*pdata++) * (*ppoly++); } *pcoef++ = sum; } } else { char *pdata; for (j = 0; j < numcoef; j++) { pdata = data; sum = 0.0; for (t = 0; t < numdata; t++) { sum += (*((double *) pdata)) * (*ppoly++); pdata += data_stride; } *pcoef++ = sum; } } _save = PyEval_SaveThread(); /* integral coefficients dnj */ N = numdata - 1; pdn0 = coef; pdn1 = coef + stride; for (n = 0; n < numexps; n++) { pdn0[numcoef] = 0.0; pdn1[0] = 0.0; for (j = 1; j < numcoef; j++) { pdn1[j] = ((N + j + 2) * pdn0[j + 1] / (2*j + 3) - pdn0[j] - (N - j + 1) * pdn0[j - 1] / (2*j - 1)) / 2.0; } pdn0 += stride; pdn1 += stride; } /* regression matrix */ pmat = matrix; pcol = coef; for (col = 0; col < numexps; col++) { pcol += stride; prow = coef; for (row = 0; row < numexps; row++) { prow += stride; sum = 0.0; for (j = startcoef; j < numcoef; j++) { sum += prow[j] * pcol[j]; } *pmat++ = sum; } } /* regression vector */ pvec = vector; pcoef = coef; for (row = 0; row < numexps; row++) { pcoef += stride; sum = 0.0; for (j = startcoef; j < numcoef; j++) { sum += coef[j] * pcoef[j]; } *pvec++ = sum; } /* solve linear equation system */ error = linsolve(numexps, matrix, vector); if (error != 0) { PyEval_RestoreThread(_save); return error; } /* roots of polynomial */ for (n = 0; n < numexps; n++) { xcoefs[n].real = -vector[numexps - n - 1]; xcoefs[n].imag = 0.0; } xcoefs[numexps].real = 1.0; xcoefs[numexps].imag = 0.0; error = polyroots(numexps+1, xcoefs, xroots); if (error != 0) { PyEval_RestoreThread(_save); return error; } /* decay rate and frequency of harmonics */ for (n = 0; n < numexps; n++) { temp = xroots[n].real + 1.0; rat[n] = -log(temp*temp + xroots[n].imag*xroots[n].imag) / 2.0; frq[n] = atan2(xroots[n].imag, temp); } /* fitting amplitudes */ /* Chebyshev transform signal for each exponential component */ pcoef = coef + (numcoef + 1); pbuff = buff; for (n = 0; n < numexps; n++) { frqn = frq[n]; ratn = rat[n]; if (frqn == 0.0) { *pbuff++ = 1.0; for (t = 1; t < numdata; t++) { *pbuff++ = exp(-ratn*t); } } else if (frqn > 0) { *pbuff++ = 1.0; for (t = 1; t < numdata; t++) { *pbuff++ = exp(-ratn*t) * cos(frqn*t); } } else { *pbuff++ = 0.0; for (t = 1; t < numdata; t++) { *pbuff++ = -exp(-ratn*t) * sin(frqn*t); } } rat[n] = deltat / ratn; frq[n] /= deltat; /* forward Chebyshev transform */ ppoly = poly; for (j = 0; j < numcoef; j++) { pbuff -= numdata; sum = 0.0; for (t = 0; t < numdata; t++) { sum += (*pbuff++) * (*ppoly++); } *pcoef++ = sum; } pcoef++; } /* regression matrix for fitting amplitudes */ pmat = matrix; pcol = coef; for (col = 0; col < numexps; col++) { pcol += stride; prow = coef; for (row = 0; row < numexps; row++) { prow += stride; sum = 0.0; for (j = 1; j < numcoef; j++) { sum += prow[j] * pcol[j]; } *pmat++ = sum; } } /* regression vector for fitting amplitudes */ pvec = amp; pcoef = coef; for (row = 0; row < numexps; row++) { pcoef += stride; sum = 0.0; for (j = 1; j < numcoef; j++) { sum += coef[j] * pcoef[j]; } *pvec++ = sum; } /* solve linear equation system for amplitudes */ error = linsolve(numexps, matrix, amp); if (error != 0) { PyEval_RestoreThread(_save); return error; } /* calculate offset from zero Chebyshev coefficients */ pcoef = coef + stride; temp = *coef; for (n = 0; n < numexps; n++) { temp -= amp[n] * (*pcoef); pcoef += stride; } *off = temp; /* calculate fitted data */ if (fitt != NULL) { if (fitt_stride == sizeof(double)) { double *pfitt = (double *)fitt; temp = *off; for (t = 0; t < numdata; t++) { *pfitt++ = temp; } pbuff = buff; for (n = 0; n < numexps; n++) { pfitt = (double *)fitt; temp = amp[n]; for (t = 0; t < numdata; t++) { *pfitt++ += temp * (*pbuff++); } } } else { char *pfitt = fitt; temp = *off; for (t = 0; t < numdata; t++) { *(double *)pfitt = temp; pfitt += fitt_stride; } pbuff = buff; for (n = 0; n < numexps; n++) { pfitt = fitt; temp = amp[n]; for (t = 0; t < numdata; t++) { *(double *)pfitt += temp * (*pbuff++); pfitt += fitt_stride; } } } } PyEval_RestoreThread(_save); return 0; }
/*! gts_vertex_principal_directions: * @v: a #WVertex. * @s: a #GtsSurface. * @Kh: mean curvature normal (a #Vec3r). * @Kg: Gaussian curvature (a real). * @e1: first principal curvature direction (direction of largest curvature). * @e2: second principal curvature direction. * * Computes the principal curvature directions at a point given @Kh and @Kg, the mean curvature normal and * Gaussian curvatures at that point, computed with gts_vertex_mean_curvature_normal() and * gts_vertex_gaussian_curvature(), respectively. * * Note that this computation is very approximate and tends to be unstable. Smoothing of the surface or the principal * directions may be necessary to achieve reasonable results. */ void gts_vertex_principal_directions(WVertex *v, Vec3r Kh, real Kg, Vec3r &e1, Vec3r &e2) { Vec3r N; real normKh; Vec3r basis1, basis2, d, eig; real ve2, vdotN; real aterm_da, bterm_da, cterm_da, const_da; real aterm_db, bterm_db, cterm_db, const_db; real a, b, c; real K1, K2; real *weights, *kappas, *d1s, *d2s; int edge_count; real err_e1, err_e2; int e; WVertex::incoming_edge_iterator itE; /* compute unit normal */ normKh = Kh.norm(); if (normKh > 0.0) { Kh.normalize(); } else { /* This vertex is a point of zero mean curvature (flat or saddle point). Compute a normal by averaging * the adjacent triangles */ N[0] = N[1] = N[2] = 0.0; for (itE = v->incoming_edges_begin(); itE != v->incoming_edges_end(); itE++) N = Vec3r(N + (*itE)->GetaFace()->GetNormal()); real normN = N.norm(); if (normN <= 0.0) return; N.normalize(); } /* construct a basis from N: */ /* set basis1 to any component not the largest of N */ basis1[0] = basis1[1] = basis1[2] = 0.0; if (fabs (N[0]) > fabs (N[1])) basis1[1] = 1.0; else basis1[0] = 1.0; /* make basis2 orthogonal to N */ basis2 = (N ^ basis1); basis2.normalize(); /* make basis1 orthogonal to N and basis2 */ basis1 = (N ^ basis2); basis1.normalize(); aterm_da = bterm_da = cterm_da = const_da = 0.0; aterm_db = bterm_db = cterm_db = const_db = 0.0; int nb_edges = v->GetEdges().size(); weights = (real *)malloc(sizeof(real) * nb_edges); kappas = (real *)malloc(sizeof(real) * nb_edges); d1s = (real *)malloc(sizeof(real) * nb_edges); d2s = (real *)malloc(sizeof(real) * nb_edges); edge_count = 0; for (itE = v->incoming_edges_begin(); itE != v->incoming_edges_end(); itE++) { WOEdge *e; WFace *f1, *f2; real weight, kappa, d1, d2; Vec3r vec_edge; if (!*itE) continue; e = *itE; /* since this vertex passed the tests in gts_vertex_mean_curvature_normal(), this should be true. */ //g_assert(gts_edge_face_number (e, s) == 2); /* identify the two triangles bordering e in s */ f1 = e->GetaFace(); f2 = e->GetbFace(); /* We are solving for the values of the curvature tensor * B = [ a b ; b c ]. * The computations here are from section 5 of [Meyer et al 2002]. * * The first step is to calculate the linear equations governing the values of (a,b,c). These can be computed * by setting the derivatives of the error E to zero (section 5.3). * * Since a + c = norm(Kh), we only compute the linear equations for dE/da and dE/db. (NB: [Meyer et al 2002] * has the equation a + b = norm(Kh), but I'm almost positive this is incorrect). * * Note that the w_ij (defined in section 5.2) are all scaled by (1/8*A_mixed). We drop this uniform scale * factor because the solution of the linear equations doesn't rely on it. * * The terms of the linear equations are xterm_dy with x in {a,b,c} and y in {a,b}. There are also const_dy * terms that are the constant factors in the equations. */ /* find the vector from v along edge e */ vec_edge = Vec3r(-1 * e->GetVec()); ve2 = vec_edge.squareNorm(); vdotN = vec_edge * N; /* section 5.2 - There is a typo in the computation of kappa. The edges should be x_j-x_i. */ kappa = 2.0 * vdotN / ve2; /* section 5.2 */ /* I don't like performing a minimization where some of the weights can be negative (as can be the case * if f1 or f2 are obtuse). To ensure all-positive weights, we check for obtuseness. */ weight = 0.0; if (!triangle_obtuse(v, f1)) { weight += ve2 * cotan(f1->GetNextOEdge(e->twin())->GetbVertex(), e->GetaVertex(), e->GetbVertex()) / 8.0; } else { if (angle_obtuse(v, f1)) { weight += ve2 * f1->getArea() / 4.0; } else { weight += ve2 * f1->getArea() / 8.0; } } if (!triangle_obtuse(v, f2)) { weight += ve2 * cotan (f2->GetNextOEdge(e)->GetbVertex(), e->GetaVertex(), e->GetbVertex()) / 8.0; } else { if (angle_obtuse(v, f2)) { weight += ve2 * f1->getArea() / 4.0; } else { weight += ve2 * f1->getArea() / 8.0; } } /* projection of edge perpendicular to N (section 5.3) */ d[0] = vec_edge[0] - vdotN * N[0]; d[1] = vec_edge[1] - vdotN * N[1]; d[2] = vec_edge[2] - vdotN * N[2]; d.normalize(); /* not explicit in the paper, but necessary. Move d to 2D basis. */ d1 = d * basis1; d2 = d * basis2; /* store off the curvature, direction of edge, and weights for later use */ weights[edge_count] = weight; kappas[edge_count] = kappa; d1s[edge_count] = d1; d2s[edge_count] = d2; edge_count++; /* Finally, update the linear equations */ aterm_da += weight * d1 * d1 * d1 * d1; bterm_da += weight * d1 * d1 * 2 * d1 * d2; cterm_da += weight * d1 * d1 * d2 * d2; const_da += weight * d1 * d1 * (-kappa); aterm_db += weight * d1 * d2 * d1 * d1; bterm_db += weight * d1 * d2 * 2 * d1 * d2; cterm_db += weight * d1 * d2 * d2 * d2; const_db += weight * d1 * d2 * (-kappa); } /* now use the identity (Section 5.3) a + c = |Kh| = 2 * kappa_h */ aterm_da -= cterm_da; const_da += cterm_da * normKh; aterm_db -= cterm_db; const_db += cterm_db * normKh; /* check for solvability of the linear system */ if (((aterm_da * bterm_db - aterm_db * bterm_da) != 0.0) && ((const_da != 0.0) || (const_db != 0.0))) { linsolve(aterm_da, bterm_da, -const_da, aterm_db, bterm_db, -const_db, &a, &b); c = normKh - a; eigenvector(a, b, c, eig); } else { /* region of v is planar */ eig[0] = 1.0; eig[1] = 0.0; } /* Although the eigenvectors of B are good estimates of the principal directions, it seems that which one is * attached to which curvature direction is a bit arbitrary. This may be a bug in my implementation, or just * a side-effect of the inaccuracy of B due to the discrete nature of the sampling. * * To overcome this behavior, we'll evaluate which assignment best matches the given eigenvectors by comparing * the curvature estimates computed above and the curvatures calculated from the discrete differential operators. */ gts_vertex_principal_curvatures(0.5 * normKh, Kg, &K1, &K2); err_e1 = err_e2 = 0.0; /* loop through the values previously saved */ for (e = 0; e < edge_count; e++) { real weight, kappa, d1, d2; real temp1, temp2; real delta; weight = weights[e]; kappa = kappas[e]; d1 = d1s[e]; d2 = d2s[e]; temp1 = fabs (eig[0] * d1 + eig[1] * d2); temp1 = temp1 * temp1; temp2 = fabs (eig[1] * d1 - eig[0] * d2); temp2 = temp2 * temp2; /* err_e1 is for K1 associated with e1 */ delta = K1 * temp1 + K2 * temp2 - kappa; err_e1 += weight * delta * delta; /* err_e2 is for K1 associated with e2 */ delta = K2 * temp1 + K1 * temp2 - kappa; err_e2 += weight * delta * delta; } free (weights); free (kappas); free (d1s); free (d2s); /* rotate eig by a right angle if that would decrease the error */ if (err_e2 < err_e1) { real temp = eig[0]; eig[0] = eig[1]; eig[1] = -temp; } e1[0] = eig[0] * basis1[0] + eig[1] * basis2[0]; e1[1] = eig[0] * basis1[1] + eig[1] * basis2[1]; e1[2] = eig[0] * basis1[2] + eig[1] * basis2[2]; e1.normalize(); /* make N,e1,e2 a right handed coordinate sytem */ e2 = N ^ e1; e2.normalize(); }
/** * gts_vertex_principal_directions: * @v: a #GtsVertex. * @s: a #GtsSurface. * @Kh: mean curvature normal (a #GtsVector). * @Kg: Gaussian curvature (a gdouble). * @e1: first principal curvature direction (direction of largest curvature). * @e2: second principal curvature direction. * * Computes the principal curvature directions at a point given @Kh * and @Kg, the mean curvature normal and Gaussian curvatures at that * point, computed with gts_vertex_mean_curvature_normal() and * gts_vertex_gaussian_curvature(), respectively. * * Note that this computation is very approximate and tends to be * unstable. Smoothing of the surface or the principal directions may * be necessary to achieve reasonable results. */ void gts_vertex_principal_directions (GtsVertex * v, GtsSurface * s, GtsVector Kh, gdouble Kg, GtsVector e1, GtsVector e2) { GtsVector N; gdouble normKh; GSList * i, * j; GtsVector basis1, basis2, d, eig; gdouble ve2, vdotN; gdouble aterm_da, bterm_da, cterm_da, const_da; gdouble aterm_db, bterm_db, cterm_db, const_db; gdouble a, b, c; gdouble K1, K2; gdouble *weights, *kappas, *d1s, *d2s; gint edge_count; gdouble err_e1, err_e2; int e; /* compute unit normal */ normKh = sqrt (gts_vector_scalar (Kh, Kh)); if (normKh > 0.0) { N[0] = Kh[0] / normKh; N[1] = Kh[1] / normKh; N[2] = Kh[2] / normKh; } else { /* This vertex is a point of zero mean curvature (flat or saddle * point). Compute a normal by averaging the adjacent triangles */ N[0] = N[1] = N[2] = 0.0; i = gts_vertex_faces (v, s, NULL); while (i) { gdouble x, y, z; gts_triangle_normal (GTS_TRIANGLE ((GtsFace *) i->data), &x, &y, &z); N[0] += x; N[1] += y; N[2] += z; i = i->next; } g_return_if_fail (gts_vector_norm (N) > 0.0); gts_vector_normalize (N); } /* construct a basis from N: */ /* set basis1 to any component not the largest of N */ basis1[0] = basis1[1] = basis1[2] = 0.0; if (fabs (N[0]) > fabs (N[1])) basis1[1] = 1.0; else basis1[0] = 1.0; /* make basis2 orthogonal to N */ gts_vector_cross (basis2, N, basis1); gts_vector_normalize (basis2); /* make basis1 orthogonal to N and basis2 */ gts_vector_cross (basis1, N, basis2); gts_vector_normalize (basis1); aterm_da = bterm_da = cterm_da = const_da = 0.0; aterm_db = bterm_db = cterm_db = const_db = 0.0; weights = g_malloc (sizeof (gdouble)*g_slist_length (v->segments)); kappas = g_malloc (sizeof (gdouble)*g_slist_length (v->segments)); d1s = g_malloc (sizeof (gdouble)*g_slist_length (v->segments)); d2s = g_malloc (sizeof (gdouble)*g_slist_length (v->segments)); edge_count = 0; i = v->segments; while (i) { GtsEdge * e; GtsFace * f1, * f2; gdouble weight, kappa, d1, d2; GtsVector vec_edge; if (! GTS_IS_EDGE (i->data)) { i = i->next; continue; } e = i->data; /* since this vertex passed the tests in * gts_vertex_mean_curvature_normal(), this should be true. */ g_assert (gts_edge_face_number (e, s) == 2); /* identify the two triangles bordering e in s */ f1 = f2 = NULL; j = e->triangles; while (j) { if ((! GTS_IS_FACE (j->data)) || (! gts_face_has_parent_surface (GTS_FACE (j->data), s))) { j = j->next; continue; } if (f1 == NULL) f1 = GTS_FACE (j->data); else { f2 = GTS_FACE (j->data); break; } j = j->next; } g_assert (f2 != NULL); /* We are solving for the values of the curvature tensor * B = [ a b ; b c ]. * The computations here are from section 5 of [Meyer et al 2002]. * * The first step is to calculate the linear equations governing * the values of (a,b,c). These can be computed by setting the * derivatives of the error E to zero (section 5.3). * * Since a + c = norm(Kh), we only compute the linear equations * for dE/da and dE/db. (NB: [Meyer et al 2002] has the * equation a + b = norm(Kh), but I'm almost positive this is * incorrect.) * * Note that the w_ij (defined in section 5.2) are all scaled by * (1/8*A_mixed). We drop this uniform scale factor because the * solution of the linear equations doesn't rely on it. * * The terms of the linear equations are xterm_dy with x in * {a,b,c} and y in {a,b}. There are also const_dy terms that are * the constant factors in the equations. */ /* find the vector from v along edge e */ gts_vector_init (vec_edge, GTS_POINT (v), GTS_POINT ((GTS_SEGMENT (e)->v1 == v) ? GTS_SEGMENT (e)->v2 : GTS_SEGMENT (e)->v1)); ve2 = gts_vector_scalar (vec_edge, vec_edge); vdotN = gts_vector_scalar (vec_edge, N); /* section 5.2 - There is a typo in the computation of kappa. The * edges should be x_j-x_i. */ kappa = 2.0 * vdotN / ve2; /* section 5.2 */ /* I don't like performing a minimization where some of the * weights can be negative (as can be the case if f1 or f2 are * obtuse). To ensure all-positive weights, we check for * obtuseness and use values similar to those in region_area(). */ weight = 0.0; if (! triangle_obtuse(v, f1)) { weight += ve2 * cotan (gts_triangle_vertex_opposite (GTS_TRIANGLE (f1), e), GTS_SEGMENT (e)->v1, GTS_SEGMENT (e)->v2) / 8.0; } else { if (angle_obtuse (v, f1)) { weight += ve2 * gts_triangle_area (GTS_TRIANGLE (f1)) / 4.0; } else { weight += ve2 * gts_triangle_area (GTS_TRIANGLE (f1)) / 8.0; } } if (! triangle_obtuse(v, f2)) { weight += ve2 * cotan (gts_triangle_vertex_opposite (GTS_TRIANGLE (f2), e), GTS_SEGMENT (e)->v1, GTS_SEGMENT (e)->v2) / 8.0; } else { if (angle_obtuse (v, f2)) { weight += ve2 * gts_triangle_area (GTS_TRIANGLE (f2)) / 4.0; } else { weight += ve2 * gts_triangle_area (GTS_TRIANGLE (f2)) / 8.0; } } /* projection of edge perpendicular to N (section 5.3) */ d[0] = vec_edge[0] - vdotN * N[0]; d[1] = vec_edge[1] - vdotN * N[1]; d[2] = vec_edge[2] - vdotN * N[2]; gts_vector_normalize (d); /* not explicit in the paper, but necessary. Move d to 2D basis. */ d1 = gts_vector_scalar (d, basis1); d2 = gts_vector_scalar (d, basis2); /* store off the curvature, direction of edge, and weights for later use */ weights[edge_count] = weight; kappas[edge_count] = kappa; d1s[edge_count] = d1; d2s[edge_count] = d2; edge_count++; /* Finally, update the linear equations */ aterm_da += weight * d1 * d1 * d1 * d1; bterm_da += weight * d1 * d1 * 2 * d1 * d2; cterm_da += weight * d1 * d1 * d2 * d2; const_da += weight * d1 * d1 * (- kappa); aterm_db += weight * d1 * d2 * d1 * d1; bterm_db += weight * d1 * d2 * 2 * d1 * d2; cterm_db += weight * d1 * d2 * d2 * d2; const_db += weight * d1 * d2 * (- kappa); i = i->next; } /* now use the identity (Section 5.3) a + c = |Kh| = 2 * kappa_h */ aterm_da -= cterm_da; const_da += cterm_da * normKh; aterm_db -= cterm_db; const_db += cterm_db * normKh; /* check for solvability of the linear system */ if (((aterm_da * bterm_db - aterm_db * bterm_da) != 0.0) && ((const_da != 0.0) || (const_db != 0.0))) { linsolve (aterm_da, bterm_da, -const_da, aterm_db, bterm_db, -const_db, &a, &b); c = normKh - a; eigenvector (a, b, c, eig); } else { /* region of v is planar */ eig[0] = 1.0; eig[1] = 0.0; } /* Although the eigenvectors of B are good estimates of the * principal directions, it seems that which one is attached to * which curvature direction is a bit arbitrary. This may be a bug * in my implementation, or just a side-effect of the inaccuracy of * B due to the discrete nature of the sampling. * * To overcome this behavior, we'll evaluate which assignment best * matches the given eigenvectors by comparing the curvature * estimates computed above and the curvatures calculated from the * discrete differential operators. */ gts_vertex_principal_curvatures (0.5 * normKh, Kg, &K1, &K2); err_e1 = err_e2 = 0.0; /* loop through the values previously saved */ for (e = 0; e < edge_count; e++) { gdouble weight, kappa, d1, d2; gdouble temp1, temp2; gdouble delta; weight = weights[e]; kappa = kappas[e]; d1 = d1s[e]; d2 = d2s[e]; temp1 = fabs (eig[0] * d1 + eig[1] * d2); temp1 = temp1 * temp1; temp2 = fabs (eig[1] * d1 - eig[0] * d2); temp2 = temp2 * temp2; /* err_e1 is for K1 associated with e1 */ delta = K1 * temp1 + K2 * temp2 - kappa; err_e1 += weight * delta * delta; /* err_e2 is for K1 associated with e2 */ delta = K2 * temp1 + K1 * temp2 - kappa; err_e2 += weight * delta * delta; } g_free (weights); g_free (kappas); g_free (d1s); g_free (d2s); /* rotate eig by a right angle if that would decrease the error */ if (err_e2 < err_e1) { gdouble temp = eig[0]; eig[0] = eig[1]; eig[1] = -temp; } e1[0] = eig[0] * basis1[0] + eig[1] * basis2[0]; e1[1] = eig[0] * basis1[1] + eig[1] * basis2[1]; e1[2] = eig[0] * basis1[2] + eig[1] * basis2[2]; gts_vector_normalize (e1); /* make N,e1,e2 a right handed coordinate sytem */ gts_vector_cross (e2, N, e1); gts_vector_normalize (e2); }