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.");
            }
        }
Esempio n. 3
0
        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;
        }
Esempio n. 4
0
 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);
     }
 }
Esempio n. 5
0
/*
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;
}
Esempio n. 6
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;
}
Esempio n. 7
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();
}
Esempio n. 8
0
/** 
 * 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);
}