#ifndef AVERAGE_INTERFACE_HH #define AVERAGE_INTERFACE_HH #include <dune/geometry/referenceelements.hh> #include <dune/geometry/quadraturerules.hh> #include <dune/common/fmatrix.hh> #include <dune/localfunctions/lagrange/pqkfactory.hh> #include <dune/istl/solvers.hh> #include <dune/istl/preconditioners.hh> #include dune/matrix-vector/crossproduct.hh> #include <dune/fufem/dgindexset.hh> #include <dune/fufem/arithmetic.hh> #include <dune/fufem/surfmassmatrix.hh> #include <dune/fufem/functions/basisgridfunction.hh> #include <dune/solvers/common/numproc.hh> #include "svd.hh" #include "rigidbodymotion.hh" #ifdef HAVE_IPOPT #include "IpTNLP.hpp" #include "IpIpoptApplication.hpp" #else #error You need IPOpt for this header! #endif template <class GridView> class PressureAverager : public Ipopt::TNLP { typedef double field_type; typedef Dune::BCRSMatrix<Dune::FieldMatrix<double,1,1> > MatrixType; typedef typename MatrixType::row_type RowType; constexpr static int dim = GridView::dimension; public: /** \brief Constructor */ PressureAverager(const BoundaryPatch<GridView>* patch, Dune::BlockVector<Dune::FieldVector<double,dim> >* result, const Dune::FieldVector<double,dim>& resultantForce, const Dune::FieldVector<double,dim>& resultantTorque, const Dune::BCRSMatrix<Dune::FieldMatrix<double,1,1> >* massMatrix, const Dune::BlockVector<Dune::FieldVector<double,1> >* nodalWeights, const Dune::BCRSMatrix<Dune::FieldMatrix<double,1,1> >* constraintJacobian) : jacobianCutoff_(1e-12), patch_(patch), massMatrix_(massMatrix), nodalWeights_(nodalWeights), constraintJacobian_(constraintJacobian), x_(result), resultantForce_(resultantForce), resultantTorque_(resultantTorque) { patchArea_ = patch->area(); } /** default destructor */ virtual ~PressureAverager() {}; /**@name Overloaded from TNLP */ //@{ /** Method to return some info about the nlp */ virtual bool get_nlp_info(Ipopt::Index& n, Ipopt::Index& m, Ipopt::Index& nnz_jac_g, Ipopt::Index& nnz_h_lag, IndexStyleEnum& index_style); /** Method to return the bounds for my problem */ virtual bool get_bounds_info(Ipopt::Index n, Ipopt::Number* x_l, Ipopt::Number* x_u, Ipopt::Index m, Ipopt::Number* g_l, Ipopt::Number* g_u); /** Method to return the starting point for the algorithm */ virtual bool get_starting_point(Ipopt::Index n, bool init_x, Ipopt::Number* x, bool init_z, Ipopt::Number* z_L, Ipopt::Number* z_U, Ipopt::Index m, bool init_lambda, Ipopt::Number* lambda); /** Method to return the objective value */ virtual bool eval_f(Ipopt::Index n, const Ipopt::Number* x, bool new_x, Ipopt::Number& obj_value); /** Method to return the gradient of the objective */ virtual bool eval_grad_f(Ipopt::Index n, const Ipopt::Number* x, bool new_x, Ipopt::Number* grad_f); /** Method to return the constraint residuals */ virtual bool eval_g(Ipopt::Index n, const Ipopt::Number* x, bool new_x, Ipopt::Index m, Ipopt::Number* g); /** Method to return: * 1) The structure of the jacobian (if "values" is NULL) * 2) The values of the jacobian (if "values" is not NULL) */ virtual bool eval_jac_g(Ipopt::Index n, const Ipopt::Number* x, bool new_x, Ipopt::Index m, Ipopt::Index nele_jac, Ipopt::Index* iRow, Ipopt::Index *jCol, Ipopt::Number* values); /** Method to return: * 1) The structure of the hessian of the lagrangian (if "values" is NULL) * 2) The values of the hessian of the lagrangian (if "values" is not NULL) */ virtual bool eval_h(Ipopt::Index n, const Ipopt::Number* x, bool new_x, Ipopt::Number obj_factor, Ipopt::Index m, const Ipopt::Number* lambda, bool new_lambda, Ipopt::Index nele_hess, Ipopt::Index* iRow, Ipopt::Index* jCol, Ipopt::Number* values); //@} /** @name Solution Methods */ //@{ /** This method is called when the algorithm is complete so the TNLP can store/write the solution */ virtual void finalize_solution(Ipopt::SolverReturn status, Ipopt::Index n, const Ipopt::Number* x, const Ipopt::Number* z_L, const Ipopt::Number* z_U, Ipopt::Index m, const Ipopt::Number* g, const Ipopt::Number* lambda, Ipopt::Number obj_value, const Ipopt::IpoptData* ip_data, Ipopt::IpoptCalculatedQuantities* ip_cq); //@} // ///////////////////////////////// // Data // ///////////////////////////////// /** \brief All entries in the constraint Jacobian smaller than the value here are removed. Apparently this is unnecessary though. */ const double jacobianCutoff_; const BoundaryPatch<GridView>* patch_; double patchArea_; const Dune::BCRSMatrix<Dune::FieldMatrix<double,1,1> >* massMatrix_; const Dune::BlockVector<Dune::FieldVector<double,1> >* nodalWeights_; const Dune::BCRSMatrix<Dune::FieldMatrix<double,1,1> >* constraintJacobian_; Dune::BlockVector<Dune::FieldVector<double,dim> >* x_; Dune::FieldVector<double,dim> resultantForce_; Dune::FieldVector<double,dim> resultantTorque_; private: /**@name Methods to block default compiler methods. */ //@{ // PressureAverager(); PressureAverager(const PressureAverager&); PressureAverager& operator=(const PressureAverager&); //@} }; // returns the size of the problem template <class GridView> bool PressureAverager<GridView>:: get_nlp_info(Ipopt::Index& n, Ipopt::Index& m, Ipopt::Index& nnz_jac_g, Ipopt::Index& nnz_h_lag, IndexStyleEnum& index_style) { // One variable for each vertex on the coupling boundary, and three for the closest constant field n = patch_->numVertices()*dim + dim; // prescribed total forces and moments m = 2*dim; // number of nonzeroes in the constraint Jacobian // leave out the very small ones, as they create instabilities nnz_jac_g = 0; for (int i=0; i<m; i++) { const RowType& jacobianRow = (*constraintJacobian_)[i]; for (typename RowType::ConstIterator cIt = jacobianRow.begin(); cIt!=jacobianRow.end(); ++cIt) if ( std::abs((*cIt)[0][0]) > jacobianCutoff_ ) nnz_jac_g++; } // We only need the lower left corner of the Hessian (since it is symmetric) if (!massMatrix_) DUNE_THROW(SolverError, "No mass matrix has been supplied!"); nnz_h_lag = 0; // use the C style indexing (0-based) index_style = Ipopt::TNLP::C_STYLE; return true; } // returns the variable bounds template <class GridView> bool PressureAverager<GridView>:: get_bounds_info(Ipopt::Index n, Ipopt::Number* x_l, Ipopt::Number* x_u, Ipopt::Index m, Ipopt::Number* g_l, Ipopt::Number* g_u) { // here, the n and m we gave IPOPT in get_nlp_info are passed back to us. // If desired, we could assert to make sure they are what we think they are. //assert(n == x_->dim()); //assert(m == 0); // Be on the safe side: unset all variable bounds for (int i=0; i<n; i++) { x_l[i] = -std::numeric_limits<double>::max(); x_u[i] = std::numeric_limits<double>::max(); } for (int i=0; i<dim; i++) { g_l[i] = g_u[i] = resultantForce_[i]; g_l[i+dim] = g_u[i+dim] = resultantTorque_[i]; } return true; } // returns the initial point for the problem template <class GridView> bool PressureAverager<GridView>:: get_starting_point(Ipopt::Index n, bool init_x, Ipopt::Number* x, bool init_z, Ipopt::Number* z_L, Ipopt::Number* z_U, Ipopt::Index m, bool init_lambda, Ipopt::Number* lambda) { // Here, we assume we only have starting values for x, if you code // your own NLP, you can provide starting values for the dual variables // if you wish assert(init_x == true); assert(init_z == false); assert(init_lambda == false); // initialize to the given starting point for (int i=0; i<n/dim; i++) for (int j=0; j<dim; j++) x[i*dim+j] = resultantForce_[j]/patchArea_; return true; } // returns the value of the objective function template <class GridView> bool PressureAverager<GridView>:: eval_f(Ipopt::Index n, const Ipopt::Number* x, bool new_x, Ipopt::Number& obj_value) { // Init return value obj_value = 0; //////////////////////////////////// // Compute x^T*A*x //////////////////////////////////// for (int rowIdx=0; rowIdx<massMatrix_->N(); rowIdx++) { const typename MatrixType::row_type& row = (*massMatrix_)[rowIdx]; typename MatrixType::row_type::ConstIterator cIt = row.begin(); typename MatrixType::row_type::ConstIterator cEndIt = row.end(); for (; cIt!=cEndIt; ++cIt) for (int i=0; i<dim; i++) obj_value += x[dim*rowIdx+i] * x[dim*cIt.index()+i] * (*cIt)[0][0]; } // for (int i=0; i<nodalWeights_->size(); i++) for (int j=0; j<dim; j++) obj_value -= 2 * x[n-dim + j] * x[i*dim+j] * (*nodalWeights_)[i]; // += c^2 * \int 1 ds for (int i=0; i<dim; i++) obj_value += patchArea_ * (x[n-dim + i] * x[n-dim + i]); return true; } // return the gradient of the objective function grad_{x} f(x) template <class GridView> bool PressureAverager<GridView>:: eval_grad_f(Ipopt::Index n, const Ipopt::Number* x, bool new_x, Ipopt::Number* grad_f) { //std::cout << "### eval_grad_f ###" << std::endl; // \nabla J = A(x,.) - b(x) for (int i=0; i<n; i++) grad_f[i] = 0; for (int i=0; i<massMatrix_->N(); i++) { const typename MatrixType::row_type& row = (*massMatrix_)[i]; typename MatrixType::row_type::ConstIterator cIt = row.begin(); typename MatrixType::row_type::ConstIterator cEndIt = row.end(); for (; cIt!=cEndIt; ++cIt) for (int j=0; j<dim; j++) grad_f[i*dim+j] += 2 * (*cIt)[0][0] * x[cIt.index()*dim+j]; for (int j=0; j<dim; j++) grad_f[i*dim+j] -= 2 * x[n-dim+j] * (*nodalWeights_)[i]; } for (int i=0; i<dim; i++) { for (int j=0; j<nodalWeights_->size(); j++) grad_f[n-dim+i] -= 2* (*nodalWeights_)[j]*x[j*dim+i]; grad_f[n-dim+i] += 2*x[n-dim+i]*patchArea_; } // for (int i=0; i<n; i++) { // std::cout << "x = " << x[i] << std::endl; // std::cout << "grad = " << grad_f[i] << std::endl; // } return true; } // return the value of the constraints: g(x) template <class GridView> bool PressureAverager<GridView>:: eval_g(Ipopt::Index n, const Ipopt::Number* x, bool new_x, Ipopt::Index m, Ipopt::Number* g) { for (int i=0; i<m; i++) { // init g[i] = 0; const RowType& jacobianRow = (*constraintJacobian_)[i]; for (typename RowType::ConstIterator cIt = jacobianRow.begin(); cIt!=jacobianRow.end(); ++cIt) if ( std::abs((*cIt)[0][0]) > jacobianCutoff_ ) { //printf("adding %g times %g\n", (*cIt)[0][0], x[cIt.index()]); g[i] += (*cIt)[0][0] * x[cIt.index()]; } } // for (int i=0; i<m; i++) // std::cout << "g[" << i << "]: " << g[i] << std::endl; return true; } // return the structure or values of the jacobian template <class GridView> bool PressureAverager<GridView>:: eval_jac_g(Ipopt::Index n, const Ipopt::Number* x, bool new_x, Ipopt::Index m, Ipopt::Index nele_jac, Ipopt::Index* iRow, Ipopt::Index *jCol, Ipopt::Number* values) { int idx = 0; if (values==NULL) { for (int i=0; i<m; i++) { const RowType& jacobianRow = (*constraintJacobian_)[i]; for (typename RowType::ConstIterator cIt = jacobianRow.begin(); cIt!=jacobianRow.end(); ++cIt) { if ( std::abs((*cIt)[0][0]) > jacobianCutoff_ ) { iRow[idx] = i; jCol[idx] = cIt.index(); idx++; } } } } else { for (int i=0; i<m; i++) { const RowType& jacobianRow = (*constraintJacobian_)[i]; for (typename RowType::ConstIterator cIt = jacobianRow.begin(); cIt!=jacobianRow.end(); ++cIt) if ( std::abs((*cIt)[0][0]) > jacobianCutoff_ ) values[idx++] = (*cIt)[0][0]; } } return true; } //return the structure or values of the hessian template <class GridView> bool PressureAverager<GridView>:: eval_h(Ipopt::Index n, const Ipopt::Number* x, bool new_x, Ipopt::Number obj_factor, Ipopt::Index m, const Ipopt::Number* lambda, bool new_lambda, Ipopt::Index nele_hess, Ipopt::Index* iRow, Ipopt::Index* jCol, Ipopt::Number* values) { // We are using a quasi-Hessian approximation return false; } template <class GridView> void PressureAverager<GridView>:: finalize_solution(Ipopt::SolverReturn status, Ipopt::Index n, const Ipopt::Number* x, const Ipopt::Number* z_L, const Ipopt::Number* z_U, Ipopt::Index m, const Ipopt::Number* g, const Ipopt::Number* lambda, Ipopt::Number obj_value, const Ipopt::IpoptData* ip_data, Ipopt::IpoptCalculatedQuantities* ip_cq) { x_->resize(patch_->numVertices()); for (int i=0; i<x_->size(); i++) for (int j=0; j<dim; j++) (*x_)[i][j] = x[i*dim+j]; } template <class GridView> void weakToStrongBoundaryStress(const BoundaryPatch<GridView>& boundary, const Dune::BlockVector<Dune::FieldVector<double, GridView::dimension> >& weakBoundaryStress, Dune::BlockVector<Dune::FieldVector<double, GridView::dimension> >& strongBoundaryStress) { static const int dim = GridView::dimension; typedef Dune::BCRSMatrix<Dune::FieldMatrix<double,dim,dim> > MatrixType; typedef Dune::BlockVector<Dune::FieldVector<double,dim> > VectorType; // turn residual (== weak form of the Neumann forces) to the strong form MatrixType surfaceMassMatrix; assembleSurfaceMassMatrix(boundary, surfaceMassMatrix); std::vector<int> globalToLocal; boundary.makeGlobalToLocal(globalToLocal); VectorType localWeakBoundaryStress(surfaceMassMatrix.N()); assert(globalToLocal.size()==weakBoundaryStress.size()); for (int i=0; i<globalToLocal.size(); i++) if (globalToLocal[i] >= 0) localWeakBoundaryStress[globalToLocal[i]] = weakBoundaryStress[i]; VectorType localStrongBoundaryStress(surfaceMassMatrix.N()); localStrongBoundaryStress = 0; // solve with a cg solver Dune::MatrixAdapter<MatrixType,VectorType,VectorType> op(surfaceMassMatrix); Dune::SeqILU0<MatrixType,VectorType,VectorType> ilu0(surfaceMassMatrix,1.0); Dune::CGSolver<VectorType> cg(op,ilu0,1E-4,100,0); Dune::InverseOperatorResult statistics; cg.apply(localStrongBoundaryStress, localWeakBoundaryStress, statistics); VectorType neumannForces(weakBoundaryStress.size()); neumannForces = 0; for (int i=0; i<globalToLocal.size(); i++) if (globalToLocal[i] >= 0) strongBoundaryStress[i] = localStrongBoundaryStress[globalToLocal[i]]; } /** \param center Compute total torque around this point */ template <class GridView> void computeTotalForceAndTorque(const BoundaryPatch<GridView>& interface, const Dune::BlockVector<Dune::FieldVector<double, GridView::dimension> >& boundaryStress, const Dune::FieldVector<double,3>& center, Dune::FieldVector<double,3>& totalForce, Dune::FieldVector<double,3>& totalTorque) { const int dim = GridView::dimension; // /////////////////////////////////////////// // Initialize output configuration // /////////////////////////////////////////// totalForce = 0; totalTorque = 0; //////////////////////////////////////////////////////////////// // Interpret the Neumann value coefficients as a P1 function //////////////////////////////////////////////////////////////// typedef P1NodalBasis<GridView,double> P1Basis; P1Basis p1Basis(interface.gridView()); BasisGridFunction<P1Basis, Dune::BlockVector<Dune::FieldVector<double, GridView::dimension> > > neumannFunction(p1Basis, boundaryStress); // /////////////////////////////////////////// // Loop and integrate over the interface // /////////////////////////////////////////// typename BoundaryPatch<GridView>::iterator it = interface.begin(); typename BoundaryPatch<GridView>::iterator endIt = interface.end(); for (; it!=endIt; ++it) { const typename BoundaryPatch<GridView>::iterator::Intersection::Geometry& segmentGeometry = it->geometry(); // Get quadrature rule const Dune::QuadratureRule<double, dim-1>& quad = Dune::QuadratureRules<double, dim-1>::rule(segmentGeometry.type(), dim-1); /* Loop over all integration points */ for (size_t ip=0; ip<quad.size(); ip++) { // Local position of the quadrature point const Dune::FieldVector<double,dim> quadPos = it->geometryInInside().global(quad[ip].position()); const Dune::FieldVector<double,dim> worldPos = it->geometry().global(quad[ip].position()); const double integrationElement = segmentGeometry.integrationElement(quad[ip].position()); Dune::FieldVector<double,dim> value; neumannFunction.evaluateLocal(*it->inside(), quadPos, value); totalForce.axpy(quad[ip].weight() * integrationElement, value); totalTorque.axpy(quad[ip].weight() * integrationElement, MatrixVector::crossProduct(worldPos-center,value)); } } } // Given a resultant force and torque (from a rod problem), this method computes the corresponding // Neumann data for a 3d elasticity problem. template <class GridView> void computeAveragePressure(const typename RigidBodyMotion<double,GridView::dimension>::TangentVector& resultantForceTorque, const BoundaryPatch<GridView>& interface, const Dune::FieldVector<double,GridView::dimension>& centerOfTorque, Dune::BlockVector<Dune::FieldVector<double, GridView::dimension> >& pressure) { const GridView& gridView = interface.gridView(); const typename GridView::IndexSet& indexSet = gridView.indexSet(); const int dim = GridView::dimension; typedef double field_type; Dune::PQkLocalFiniteElementCache<double,double, dim, 1> finiteElementCache; // Create the matrix of constraints Dune::BCRSMatrix<Dune::FieldMatrix<field_type,1,1> > constraints(2*dim, dim*interface.numVertices(), Dune::BCRSMatrix<Dune::FieldMatrix<double,1,1> >::random); for (int i=0; i<dim; i++) { constraints.setrowsize(i, interface.numVertices()); constraints.setrowsize(i+dim, dim*interface.numVertices()); } constraints.endrowsizes(); for (int i=0; i<dim; i++) for (int j=0; j<interface.numVertices(); j++) constraints.addindex(i, dim*j+i); for (int i=0; i<dim; i++) for (int j=0; j<dim*interface.numVertices(); j++) constraints.addindex(i+dim, j); constraints.endindices(); constraints = 0; // Create the surface mass matrix Dune::BCRSMatrix<Dune::FieldMatrix<field_type,1,1> > massMatrix; assembleSurfaceMassMatrix<GridView, 1>(interface, massMatrix); // Make global-to-local array std::vector<int> globalToLocal; interface.makeGlobalToLocal(globalToLocal); // Make array of nodal weights Dune::BlockVector<Dune::FieldVector<double,1> > nodalWeights(interface.numVertices()); nodalWeights = 0; typename BoundaryPatch<GridView>::iterator it = interface.begin(); typename BoundaryPatch<GridView>::iterator endIt = interface.end(); for (; it!=endIt; ++it) { // Get shape functions const typename Dune::PQkLocalFiniteElementCache<double,double, dim, 1>::FiniteElementType& localFiniteElement = finiteElementCache.get(it->inside()->type()); const Dune::ReferenceElement<double,dim>& refElement = Dune::ReferenceElements<double, dim>::general(it->inside()->type()); // four rows because a face may have no more than four vertices Dune::FieldVector<double,4> mu(0); Dune::FieldVector<double,3> mu_tilde[4][3]; for (int i=0; i<4; i++) for (int j=0; j<3; j++) mu_tilde[i][j] = 0; for (int i=0; i<it->geometry().corners(); i++) { const Dune::QuadratureRule<double, dim-1>& quad = Dune::QuadratureRules<double, dim-1>::rule(it->type(), dim-1); for (size_t qp=0; qp<quad.size(); qp++) { // Local position of the quadrature point const Dune::FieldVector<double,dim>& quadPos = it->geometryInInside().global(quad[qp].position()); const double integrationElement = it->geometry().integrationElement(quad[qp].position()); std::vector<Dune::FieldVector<double,1> > shapeFunctionValues; localFiniteElement.localBasis().evaluateFunction(quadPos, shapeFunctionValues); // \mu_i = \int_t \varphi_i \ds int indexInFace = refElement.subEntity(it->indexInInside(), 1, i, dim); mu[i] += quad[qp].weight() * integrationElement * shapeFunctionValues[indexInFace]; // \tilde{\mu}_i^j = \int_t (x - x_0) \times \varphi_i \ds Dune::FieldVector<double,dim> worldPos = it->geometry().global(quad[qp].position()); for (int j=0; j<dim; j++) { // Vector-valued basis function Dune::FieldVector<double,dim> phi_i(0); phi_i[j] = shapeFunctionValues[indexInFace]; mu_tilde[i][j].axpy(quad[qp].weight() * integrationElement, Arithmetic::crossProduct(Dune::FieldVector<double,dim>(worldPos-centerOfTorque), phi_i)); } } } // Set up matrix for (int i=0; i<refElement.size(it->indexInInside(),1,dim); i++) { int faceIdxi = refElement.subEntity(it->indexInInside(), 1, i, dim); int subIndex = globalToLocal[indexSet.subIndex(*it->inside(), faceIdxi, dim)]; nodalWeights[subIndex] += mu[i]; for (int j=0; j<dim; j++) constraints[j][subIndex*dim+j] += mu[i]; for (int j=0; j<3; j++) for (int k=0; k<3; k++) constraints[dim+k][dim*subIndex+j] += mu_tilde[i][j][k]; } } //printmatrix(std::cout, constraints, "jacobian", "--"); //printmatrix(std::cout, massMatrix, "mass", "--"); // ///////////////////////////////////////////////////////////////////////////////////// // Set up and start the interior-point solver // ///////////////////////////////////////////////////////////////////////////////////// Dune::FieldVector<double,dim> resultantForce, resultantTorque; for (int i=0; i<dim; i++) { resultantForce[i] = resultantForceTorque[i]; resultantTorque[i] = resultantForceTorque[dim+i]; } // Create a new instance of IpoptApplication Ipopt::SmartPtr<Ipopt::IpoptApplication> app = new Ipopt::IpoptApplication(); // Change some options app->Options()->SetNumericValue("tol", 1e-8); app->Options()->SetIntegerValue("max_iter", 1000); app->Options()->SetStringValue("mu_strategy", "adaptive"); app->Options()->SetStringValue("output_file", "ipopt.out"); app->Options()->SetStringValue("hessian_approximation", "limited-memory"); app->Options()->SetStringValue("jac_c_constant", "yes"); app->Options()->SetIntegerValue("print_level", 0); // Initialize the IpoptApplication and process the options Ipopt::ApplicationReturnStatus status; status = app->Initialize(); if (status != Ipopt::Solve_Succeeded) DUNE_THROW(SolverError, "Error during IPOpt initialization!"); // Ask Ipopt to solve the problem Dune::BlockVector<Dune::FieldVector<double,dim> > localPressure; Ipopt::SmartPtr<Ipopt::TNLP> defectSolverSmart = new PressureAverager<GridView>(&interface, &localPressure, resultantForce, resultantTorque, &massMatrix, &nodalWeights, &constraints); status = app->OptimizeTNLP(defectSolverSmart); if (status != Ipopt::Solve_Succeeded && status != Ipopt::Solved_To_Acceptable_Level) { //DUNE_THROW(SolverError, "Solving the defect problem failed!"); std::cout << "IPOpt returned error code " << status << "!" << std::endl; } // ////////////////////////////////////////////////////////////////////////////// // Get result // ////////////////////////////////////////////////////////////////////////////// // set up output array pressure.resize(indexSet.size(dim)); pressure = 0; for (size_t i=0; i<globalToLocal.size(); i++) if (globalToLocal[i]>=0) pressure[i] = localPressure[globalToLocal[i]]; // ///////////////////////////////////////////////////////////////////////////////////// // Compute the overall force and torque to see whether the preceding code is correct // ///////////////////////////////////////////////////////////////////////////////////// Dune::FieldVector<double,3> outputForce(0), outputTorque(0); computeTotalForceAndTorque(interface, pressure, centerOfTorque, outputForce, outputTorque); outputForce -= resultantForce; outputTorque -= resultantTorque; assert( outputForce.infinity_norm() < 1e-6 ); assert( outputTorque.infinity_norm() < 1e-6 ); // std::cout << "Output force: " << outputForce << std::endl; // std::cout << "Output torque: " << outputTorque << " " << resultantTorque[0]/outputTorque[0] << std::endl; } template <class GridView> void computeAverageInterface(const BoundaryPatch<GridView>& interface, const Dune::BlockVector<Dune::FieldVector<double,GridView::dimension> > deformation, RigidBodyMotion<double,3>& average) { using namespace Dune; typedef typename GridView::template Codim<0>::Entity EntityType; typedef typename GridView::IntersectionIterator NeighborIterator; const GridView& gridView = interface.gridView(); const typename GridView::IndexSet& indexSet = gridView.indexSet(); const int dim = GridView::dimension; Dune::PQkLocalFiniteElementCache<double,double, dim, 1> finiteElementCache; // /////////////////////////////////////////// // Initialize output configuration // /////////////////////////////////////////// average.r = 0; double interfaceArea = 0; FieldMatrix<double,dim,dim> deformationGradient(0); // /////////////////////////////////////////// // Loop and integrate over the interface // /////////////////////////////////////////// typename BoundaryPatch<GridView>::iterator it = interface.begin(); typename BoundaryPatch<GridView>::iterator endIt = interface.end(); for (; it!=endIt; ++it) { const typename NeighborIterator::Intersection::Geometry& segmentGeometry = it->geometry(); // Get quadrature rule const QuadratureRule<double, dim-1>& quad = QuadratureRules<double, dim-1>::rule(segmentGeometry.type(), dim-1); // Get set of shape functions on this segment const typename Dune::PQkLocalFiniteElementCache<double,double, dim, 1>::FiniteElementType& localFiniteElement = finiteElementCache.get(it->inside()->type()); /* Loop over all integration points */ for (int ip=0; ip<quad.size(); ip++) { // Local position of the quadrature point const FieldVector<double,dim> quadPos = it->geometryInInside().global(quad[ip].position()); const double integrationElement = segmentGeometry.integrationElement(quad[ip].position()); std::vector<Dune::FieldVector<double,1> > values; localFiniteElement.localBasis().evaluateFunction(quadPos,values); // Evaluate base functions FieldVector<double,dim> posAtQuadPoint(0); for(int i=0; i<it->inside()->geometry().corners(); i++) { int idx = indexSet.subIndex(*it->inside(), i, dim); // Deformation at the quadrature point posAtQuadPoint.axpy(values[i], deformation[idx]); } const FieldMatrix<double,dim,dim>& inv = it->inside()->geometry().jacobianInverseTransposed(quadPos); /**********************************************/ /* compute gradients of the shape functions */ /**********************************************/ std::vector<FieldMatrix<double, 1, dim> > shapeGrads; localFiniteElement.localBasis().evaluateJacobian(quadPos,shapeGrads); for (int dof=0; dof<it->inside()->geometry().corners(); dof++) { FieldVector<double,dim> tmp = shapeGrads[dof][0]; // multiply with jacobian inverse shapeGrads[dof] = 0; inv.umv(tmp, shapeGrads[dof][0]); } /****************************************************/ // The deformation gradient of the deformation // in formulas: F(i,j) = $\partial \phi_i / \partial x_j$ // or F(i,j) = Id + $\partial u_i / \partial x_j$ /****************************************************/ FieldMatrix<double, dim, dim> F; for (int i=0; i<dim; i++) { for (int j=0; j<dim; j++) { F[i][j] = (i==j) ? 1 : 0; for (int k=0; k<it->inside()->geometry().corners(); k++) F[i][j] += deformation[indexSet.subIndex(*it->inside(), k, dim)][i]*shapeGrads[k][0][j]; } } // Sum up interface area interfaceArea += quad[ip].weight() * integrationElement; // Sum up average displacement average.r.axpy(quad[ip].weight() * integrationElement, posAtQuadPoint); // Sum up average deformation gradient for (int i=0; i<dim; i++) for (int j=0; j<dim; j++) deformationGradient[i][j] += F[i][j] * quad[ip].weight() * integrationElement; } } // average deformation of the interface is the integral over its // deformation divided by its area average.r /= interfaceArea; // average deformation is the integral over the deformation gradient // divided by its area deformationGradient /= interfaceArea; //std::cout << "deformationGradient: " << std::endl << deformationGradient << std::endl; // Get the rotational part of the deformation gradient by performing a // polar composition. FieldVector<double,dim> W; FieldMatrix<double,dim,dim> V, VT, U; // returns a decomposition U W VT, where U is returned in the first argument svdcmp<double,dim,dim>(deformationGradient, W, V); for (int i=0; i<3; i++) for (int j=0; j<3; j++) VT[i][j] = V[j][i]; deformationGradient.rightmultiply(VT); //std::cout << deformationGradient << std::endl; // deformationGradient now contains the orthogonal part of the polar decomposition assert( std::abs(1-deformationGradient.determinant()) < 1e-3); average.q.set(deformationGradient); } /** \brief Set a Dirichlet value that corresponds to a given rigid body motion */ template <class GridView> void setRotation(const BoundaryPatch<GridView>& dirichletBoundary, Dune::BlockVector<Dune::FieldVector<double,GridView::dimension> >& deformation, const RigidBodyMotion<double,3>& referenceInterface, const RigidBodyMotion<double,3>& lambda) { const typename GridView::IndexSet& indexSet = dirichletBoundary.gridView().indexSet(); const int dim = GridView::dimension; const int dimworld = GridView::dimensionworld; // Get the relative rotation, first as a quaternion... Rotation<double,3> relativeRotation; relativeRotation = Rotation<double,3>(referenceInterface.q.inverse()); relativeRotation = lambda.q.mult(relativeRotation); // ... then as a matrix Dune::FieldMatrix<double,3,3> rotation; relativeRotation.matrix(rotation); // /////////////////////////////////////////// // Loop over all vertices // /////////////////////////////////////////// typename BoundaryPatch<GridView>::iterator it = dirichletBoundary.begin(); typename BoundaryPatch<GridView>::iterator endIt = dirichletBoundary.end(); for (; it!=endIt; ++it) { int indexInInside = it->indexInInside(); int nCorners = Dune::ReferenceElements<double,dim>::general(it->inside()->type()).size(indexInInside, 1, dim); for (int i=0; i<nCorners; i++) { int cornerIdx = Dune::ReferenceElements<double,dim>::general(it->inside()->type()).subEntity(it->indexInInside(), 1, i, dim); int globalIdx = indexSet.subIndex(*it->inside(), cornerIdx, dim); // Get vertex position const Dune::FieldVector<double,dimworld> pos = it->inside()->geometry().corner(cornerIdx); // Action of the rigid body motion Dune::FieldVector<double,dimworld> rpos; rotation.mv(pos-referenceInterface.r, rpos); rpos += lambda.r; // We compute _displacements_, not positions rpos -= pos; deformation[globalIdx] = rpos; } } } #endif