/*===========================================================================*\ * * * CoMISo * * Copyright (C) 2008-2019 by Computer Graphics Group, RWTH Aachen * * www.rwth-graphics.de * * * *---------------------------------------------------------------------------* * This file is part of CoMISo. * * * * CoMISo is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * CoMISo is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with CoMISo. If not, see . * * * \*===========================================================================*/ #include "SymmetricDirichletProblem.hh" namespace COMISO { double SymmetricDirichletElement::eval_f(const VecV& _x, const VecC& _c) { double y = 0.0; Vector12 x; x << _x[0], _x[1], _x[2], _x[3], _x[4], _x[5], _c[0], _c[1], _c[2], _c[3], _c[4], _c[5]; if(tape_available_) { int ec = function(tape_, 1, 12, const_cast(x.data()), &y); #ifdef ADOLC_RET_CODES std::cout << "Info: function() returned code " << ec << std::endl; #endif // tape not valid anymore? retape and evaluate again if(ec < 0) { retape(); function(tape_, 1, 12, const_cast(x.data()), &y); } } else { retape(); function(tape_, 1, 12, const_cast(x.data()), &y); } return y; } void SymmetricDirichletElement::eval_gradient(const VecV& _x, const VecC& _c, VecV& _g) { Vector12 x; x << _x[0], _x[1], _x[2], _x[3], _x[4], _x[5], _c[0], _c[1], _c[2], _c[3], _c[4], _c[5]; Vector12 grad; grad.setZero(); // evaluate gradient int ec = gradient(tape_, 12, x.data(), grad.data()); // check if retaping is required if(ec < 0) { #ifdef ADOLC_RET_CODES std::cout << __FUNCTION__ << " invokes retaping of function due to discontinuity! Return code: " << ec << std::endl; #endif retape(); gradient(tape_, 12, x.data(), grad.data()); } _g = grad.block(0,0,6,1); } void SymmetricDirichletElement::eval_hessian(const VecV& _x, const VecC& _c, std::vector& _triplets) { Vector12 x; x << _x[0], _x[1], _x[2], _x[3], _x[4], _x[5], _c[0], _c[1], _c[2], _c[3], _c[4], _c[5]; // dense hessian { auto dense_hessian = new double*[12]; for(int i = 0; i < 12; ++i) dense_hessian[i] = new double[i+1]; int ec = hessian(tape_, 12, const_cast(x.data()), dense_hessian); if(ec < 0) { #ifdef ADOLC_RET_CODES std::cout << __FUNCTION__ << " invokes retaping of function due to discontinuity! Return code: " << ec << std::endl; #endif // Retape function if return code indicates discontinuity retape(); ec = hessian(tape_, 12, const_cast(x.data()), dense_hessian); } #ifdef ADOLC_RET_CODES std::cout << "Info: hessian() returned code " << ec << std::endl; #endif Eigen::MatrixXd H(6,6); for (int i = 0; i < 6; ++i) { H(i,i) = dense_hessian[i][i]; for (int j = 0; j < i; ++j) { H(i,j) = dense_hessian[i][j]; H(j,i) = dense_hessian[i][j]; } } Eigen::MatrixXd Hspd(6,6); project_hessian(H, Hspd, 1e-6); // Hspd = H; _triplets.reserve(6*6); for (int i = 0; i < 6; ++i) for (int j = 0; j < 6; ++j) _triplets.push_back(Triplet(i,j,Hspd(i,j))); for (int i = 0; i < 6; ++i) delete dense_hessian[i]; delete[] dense_hessian; } } void SymmetricDirichletElement::project_hessian(Eigen::MatrixXd& H_orig, Eigen::MatrixXd& H_spd, double eps) { // Compute eigen-decomposition (of symmetric matrix) Eigen::SelfAdjointEigenSolver eig(H_orig); Eigen::MatrixXd V = eig.eigenvectors(); Eigen::MatrixXd D = eig.eigenvalues().asDiagonal(); // Clamp all eigenvalues to eps for (int i = 0; i < H_orig.rows(); ++i) D(i, i) = std::max(eps, D(i, i)); H_spd = V * D * V.inverse(); } double SymmetricDirichletElement::max_feasible_step(const VecV& _x, const VecV& _v, const VecC&) { // get quadratic coefficients (ax^2 + b^x + c) auto U11 = _x[0]; auto U12 = _x[1]; auto U21 = _x[2]; auto U22 = _x[3]; auto U31 = _x[4]; auto U32 = _x[5]; auto V11 = _v[0]; auto V12 = _v[1]; auto V21 = _v[2]; auto V22 = _v[3]; auto V31 = _v[4]; auto V32 = _v[5]; double a = V11*V22 - V12*V21 - V11*V32 + V12*V31 + V21*V32 - V22*V31; double b = U11*V22 - U12*V21 - U21*V12 + U22*V11 - U11*V32 + U12*V31 + U31*V12 - U32*V11 + U21*V32 - U22*V31 - U31*V22 + U32*V21; double c = U11*U22 - U12*U21 - U11*U32 + U12*U31 + U21*U32 - U22*U31; double delta_in = pow(b,2) - 4*a*c; if (delta_in < 0) { return std::numeric_limits::max(); } double delta = sqrt(delta_in); double t1 = (-b + delta)/ (2*a); double t2 = (-b - delta)/ (2*a); double tmp_n = std::min(t1,t2); t1 = std::max(t1,t2); t2 = tmp_n; // return the smallest negative root if it exists, otherwise return infinity if (t1 > 0) { if (t2 > 0) { return 0.999 * t2; } else { return 0.999 * t1; } } else { return std::numeric_limits::max(); } } adouble SymmetricDirichletElement::f_adouble(const adouble* _x) { Matrix2x2ad B; B(0,0) = _x[2]-_x[0]; B(0,1) = _x[4]-_x[0]; B(1,0) = _x[3]-_x[1]; B(1,1) = _x[5]-_x[1]; Matrix2x2ad Bin = B.inverse(); Matrix2x2ad R; R(0,0) = _x[6+2]-_x[6+0]; R(0,1) = _x[6+4]-_x[6+0]; R(1,0) = _x[6+3]-_x[6+1]; R(1,1) = _x[6+5]-_x[6+1]; Matrix2x2ad Rin = R.inverse(); adouble area = 0.5 * R.determinant(); if (B.determinant() * area <= 0) { adouble res = std::numeric_limits::max(); return res; } Matrix2x2ad J = B * Rin; Matrix2x2ad Jin = R * Bin; adouble res = J.squaredNorm() + Jin.squaredNorm(); return area * (res - 4); } void SymmetricDirichletElement::retape() { std::cerr << "re-tape..." << std::endl; adouble y_d = 0.0; double y = 0.0; trace_on(tape_); // Start taping adouble* xa = new adouble[12]; // Fill data vectors xa[0] <<= 0.0; xa[1] <<= 0.0; xa[2] <<= 1.0; xa[3] <<= 0.0; xa[4] <<= 0.0; xa[5] <<= 1.0; xa[6+0] <<= 0.0; xa[6+1] <<= 0.0; xa[6+2] <<= 1.0; xa[6+3] <<= 0.0; xa[6+4] <<= 0.0; xa[6+5] <<= 1.0; y_d = f_adouble(xa); y_d >>= y; trace_off(); #ifdef ADOLC_STATS print_stats(); #endif delete[] xa; } void SymmetricDirichletElement::init_tape() { static bool tape_initialized = false; static short int tape; if (!tape_initialized) { tape = static_cast(TapeIDSingleton::Instance()->requestId()); tape_ = tape; retape(); } tape_initialized = true; tape_available_ = true; tape_ = tape; } /// Default constructor SymmetricDirichletProblem::SymmetricDirichletProblem(const unsigned int _n_vertices) : FiniteElementProblem(2*_n_vertices) { FiniteElementProblem::add_set(&element_set); } void SymmetricDirichletProblem::add_triangle(const IndexVector& _vertex_indices, const ReferencePositionVector2D& _reference_positions) { SymmetricDirichletElement::VecI indices; indices << 2*_vertex_indices[0], 2*_vertex_indices[0]+1, 2*_vertex_indices[1], 2*_vertex_indices[1]+1, 2*_vertex_indices[2], 2*_vertex_indices[2]+1; SymmetricDirichletElement::VecC constants; constants << _reference_positions(0, 0), _reference_positions(0, 1), _reference_positions(1, 0), _reference_positions(1, 1), _reference_positions(2, 0), _reference_positions(2, 1); element_set.instances().add_element(indices, constants); } void SymmetricDirichletProblem::add_fix_point_constraint(int _vertex_index, double _fix_u, double _fix_v) { add_fix_coordinate_constraint(_vertex_index, 0, _fix_u); add_fix_coordinate_constraint(_vertex_index, 1, _fix_v); } void SymmetricDirichletProblem::add_fix_coordinate_constraint(int _vertex_index, int _coordinate, double _fix_coordinate) { fix_points.push_back(std::make_pair(2*_vertex_index + _coordinate, _fix_coordinate)); } void SymmetricDirichletProblem::get_constraints(SMatrixD& _A, VectorD& _b) { _A.resize(fix_points.size(), n_unknowns()); _b.resize(fix_points.size()); std::vector> triplets; for (size_t i = 0; i < fix_points.size(); ++i) { _b[i] = fix_points[i].second; triplets.push_back(Eigen::Triplet(i, fix_points[i].first, 1)); } _A.setFromTriplets(triplets.begin(), triplets.end()); } }