/** * This function assemble the stiffnes matrix KK and the residual vector Res * Using automatic differentiation for Newton iterative scheme * J(u0) w = - F(u0) , * with u = u0 + w * - F = f(x) - J u = Res * J = \grad_u F * * thus * J w = f(x) - J u0 **/ void AssemblePoissonProblem_AD(MultiLevelProblem& ml_prob) { // ml_prob is the global object from/to where get/set all the data // level is the level of the PDE system to be assembled // levelMax is the Maximum level of the MultiLevelProblem // assembleMatrix is a flag that tells if only the residual or also the matrix should be assembled // call the adept stack object adept::Stack& s = FemusInit::_adeptStack; // extract pointers to the several objects that we are going to use LinearImplicitSystem* mlPdeSys = &ml_prob.get_system<LinearImplicitSystem> ("Poisson"); // pointer to the linear implicit system named "Poisson" const unsigned level = mlPdeSys->GetLevelToAssemble(); const unsigned levelMax = mlPdeSys->GetLevelMax(); const bool assembleMatrix = mlPdeSys->GetAssembleMatrix(); Mesh* msh = ml_prob._ml_msh->GetLevel(level); // pointer to the mesh (level) object elem* el = msh->el; // pointer to the elem object in msh (level) MultiLevelSolution* mlSol = ml_prob._ml_sol; // pointer to the multilevel solution object Solution* sol = ml_prob._ml_sol->GetSolutionLevel(level); // pointer to the solution (level) object LinearEquationSolver* pdeSys = mlPdeSys->_LinSolver[level]; // pointer to the equation (level) object SparseMatrix* KK = pdeSys->_KK; // pointer to the global stifness matrix object in pdeSys (level) NumericVector* RES = pdeSys->_RES; // pointer to the global residual vector object in pdeSys (level) const unsigned dim = msh->GetDimension(); // get the domain dimension of the problem unsigned dim2 = (3 * (dim - 1) + !(dim - 1)); // dim2 is the number of second order partial derivatives (1,3,6 depending on the dimension) const unsigned maxSize = static_cast< unsigned >(ceil(pow(3, dim))); // conservative: based on line3, quad9, hex27 unsigned iproc = msh->processor_id(); // get the process_id (for parallel computation) //solution variable unsigned soluIndex; soluIndex = mlSol->GetIndex("u"); // get the position of "u" in the ml_sol object unsigned soluType = mlSol->GetSolutionType(soluIndex); // get the finite element type for "u" unsigned soluPdeIndex; soluPdeIndex = mlPdeSys->GetSolPdeIndex("u"); // get the position of "u" in the pdeSys object vector < adept::adouble > solu; // local solution solu.reserve(maxSize); vector < vector < double > > x(dim); // local coordinates unsigned xType = 2; // get the finite element type for "x", it is always 2 (LAGRANGE QUADRATIC) for (unsigned i = 0; i < dim; i++) { x[i].reserve(maxSize); } vector <double> phi; // local test function vector <double> phi_x; // local test function first order partial derivatives vector <double> phi_xx; // local test function second order partial derivatives double weight; // gauss point weight phi.reserve(maxSize); phi_x.reserve(maxSize * dim); phi_xx.reserve(maxSize * dim2); vector< adept::adouble > aRes; // local redidual vector aRes.reserve(maxSize); vector< int > l2GMap; // local to global mapping l2GMap.reserve(maxSize); vector< double > Res; // local redidual vector Res.reserve(maxSize); vector < double > Jac; Jac.reserve(maxSize * maxSize); if (assembleMatrix) KK->zero(); // Set to zero all the entries of the Global Matrix // element loop: each process loops only on the elements that owns for (int iel = msh->IS_Mts2Gmt_elem_offset[iproc]; iel < msh->IS_Mts2Gmt_elem_offset[iproc + 1]; iel++) { unsigned kel = msh->IS_Mts2Gmt_elem[iel]; // mapping between paralell dof and mesh dof short unsigned kelGeom = el->GetElementType(kel); // element geometry type unsigned nDofu = el->GetElementDofNumber(kel, soluType); // number of solution element dofs unsigned nDofx = el->GetElementDofNumber(kel, xType); // number of coordinate element dofs // resize local arrays l2GMap.resize(nDofu); solu.resize(nDofu); for (int i = 0; i < dim; i++) { x[i].resize(nDofx); } aRes.resize(nDofu); //resize std::fill(aRes.begin(), aRes.end(), 0); //set aRes to zero // local storage of global mapping and solution for (unsigned i = 0; i < nDofu; i++) { unsigned iNode = el->GetMeshDof(kel, i, soluType); // local to global solution node unsigned solDof = msh->GetMetisDof(iNode, soluType); // global to global mapping between solution node and solution dof solu[i] = (*sol->_Sol[soluIndex])(solDof); // global extraction and local storage for the solution l2GMap[i] = pdeSys->GetKKDof(soluIndex, soluPdeIndex, iNode); // global to global mapping between solution node and pdeSys dof } // local storage of coordinates for (unsigned i = 0; i < nDofx; i++) { unsigned iNode = el->GetMeshDof(kel, i, xType); // local to global coordinates node unsigned xDof = msh->GetMetisDof(iNode, xType); // global to global mapping between coordinates node and coordinate dof for (unsigned jdim = 0; jdim < dim; jdim++) { x[jdim][i] = (*msh->_coordinate->_Sol[jdim])(xDof); // global extraction and local storage for the element coordinates } } if (level == levelMax || !el->GetRefinedElementIndex(kel)) { // do not care about this if now (it is used for the AMR) // start a new recording of all the operations involving adept::adouble variables s.new_recording(); // *** Gauss point loop *** for (unsigned ig = 0; ig < msh->_finiteElement[kelGeom][soluType]->GetGaussPointNumber(); ig++) { // *** get gauss point weight, test function and test function partial derivatives *** msh->_finiteElement[kelGeom][soluType]->Jacobian(x, ig, weight, phi, phi_x, phi_xx); // evaluate the solution, the solution derivatives and the coordinates in the gauss point adept::adouble solu_gss = 0; vector < adept::adouble > gradSolu_gss(dim, 0.); vector < double > x_gss(dim, 0.); for (unsigned i = 0; i < nDofu; i++) { solu_gss += phi[i] * solu[i]; for (unsigned jdim = 0; jdim < dim; jdim++) { gradSolu_gss[jdim] += phi_x[i * dim + jdim] * solu[i]; x_gss[jdim] += x[jdim][i] * phi[i]; } } // *** phi_i loop *** for (unsigned i = 0; i < nDofu; i++) { adept::adouble laplace = 0.; for (unsigned jdim = 0; jdim < dim; jdim++) { laplace += phi_x[i * dim + jdim] * gradSolu_gss[jdim]; } double srcTerm = - GetExactSolutionLaplace(x_gss); aRes[i] += (srcTerm * phi[i] - laplace) * weight; } // end phi_i loop } // end gauss point loop } // endif single element not refined or fine grid loop //-------------------------------------------------------------------------------------------------------- // Add the local Matrix/Vector into the global Matrix/Vector //copy the value of the adept::adoube aRes in double Res and store Res.resize(nDofu); //resize for (int i = 0; i < nDofu; i++) { Res[i] = - aRes[i].value(); } RES->add_vector_blocked(Res, l2GMap); if (assembleMatrix) { // define the dependent variables s.dependent(&aRes[0], nDofu); // define the independent variables s.independent(&solu[0], nDofu); // get the jacobian matrix (ordered by row major ) Jac.resize(nDofu * nDofu); //resize s.jacobian(&Jac[0], true); //store K in the global matrix KK KK->add_matrix_blocked(Jac, l2GMap, l2GMap); s.clear_independents(); s.clear_dependents(); } } //end element loop for each process RES->close(); if (assembleMatrix) KK->close(); // ***************** END ASSEMBLY ******************* }