// This function computes the Jacobian K(x) void LaplaceYoung::jacobian (const NumericVector<Number> &soln, SparseMatrix<Number> &jacobian, NonlinearImplicitSystem &system) { // Get a constant reference to the mesh object. const MeshBase& mesh = es.get_mesh(); // The dimension that we are running const unsigned int dim = mesh.mesh_dimension(); // A reference to the \p DofMap object for this system. The \p DofMap // object handles the index translation from node and element numbers // to degree of freedom numbers. We will talk more about the \p DofMap // in future examples. const DofMap& dof_map = system.get_dof_map(); // Get a constant reference to the Finite Element type // for the first (and only) variable in the system. FEType fe_type = dof_map.variable_type(0); // Build a Finite Element object of the specified type. Since the // \p FEBase::build() member dynamically creates memory we will // store the object as an \p AutoPtr<FEBase>. This can be thought // of as a pointer that will clean up after itself. AutoPtr<FEBase> fe (FEBase::build(dim, fe_type)); // A 5th order Gauss quadrature rule for numerical integration. QGauss qrule (dim, FIFTH); // Tell the finite element object to use our quadrature rule. fe->attach_quadrature_rule (&qrule); // Here we define some references to cell-specific data that // will be used to assemble the linear system. // We begin with the element Jacobian * quadrature weight at each // integration point. const std::vector<Real>& JxW = fe->get_JxW(); // The element shape functions evaluated at the quadrature points. const std::vector<std::vector<Real> >& phi = fe->get_phi(); // The element shape function gradients evaluated at the quadrature // points. const std::vector<std::vector<RealGradient> >& dphi = fe->get_dphi(); // Define data structures to contain the Jacobian element matrix. // Following basic finite element terminology we will denote these // "Ke". More detail is in example 3. DenseMatrix<Number> Ke; // This vector will hold the degree of freedom indices for // the element. These define where in the global system // the element degrees of freedom get mapped. std::vector<dof_id_type> dof_indices; // Now we will loop over all the active elements in the mesh which // are local to this processor. // We will compute the element Jacobian contribution. MeshBase::const_element_iterator el = mesh.active_local_elements_begin(); const MeshBase::const_element_iterator end_el = mesh.active_local_elements_end(); for ( ; el != end_el; ++el) { // Store a pointer to the element we are currently // working on. This allows for nicer syntax later. const Elem* elem = *el; // Get the degree of freedom indices for the // current element. These define where in the global // matrix and right-hand-side this element will // contribute to. dof_map.dof_indices (elem, dof_indices); // Compute the element-specific data for the current // element. This involves computing the location of the // quadrature points (q_point) and the shape functions // (phi, dphi) for the current element. fe->reinit (elem); // Zero the element Jacobian before // summing them. We use the resize member here because // the number of degrees of freedom might have changed from // the last element. Note that this will be the case if the // element type is different (i.e. the last element was a // triangle, now we are on a quadrilateral). Ke.resize (dof_indices.size(), dof_indices.size()); // Now we will build the element Jacobian. This involves // a double loop to integrate the test funcions (i) against // the trial functions (j). Note that the Jacobian depends // on the current solution x, which we access using the soln // vector. // for (unsigned int qp=0; qp<qrule.n_points(); qp++) { Gradient grad_u; for (unsigned int i=0; i<phi.size(); i++) grad_u += dphi[i][qp]*soln(dof_indices[i]); const Number K = 1./std::sqrt(1. + grad_u*grad_u); for (unsigned int i=0; i<phi.size(); i++) for (unsigned int j=0; j<phi.size(); j++) Ke(i,j) += JxW[qp]*( K*(dphi[i][qp]*dphi[j][qp]) + kappa*phi[i][qp]*phi[j][qp] ); } dof_map.constrain_element_matrix (Ke, dof_indices); // Add the element matrix to the system Jacobian. jacobian.add_matrix (Ke, dof_indices); } // That's it. }
static PetscErrorCode DMCreateDomainDecomposition_libMesh(DM dm, PetscInt *len, char ***namelist, IS **innerislist, IS **outerislist, DM **dmlist) { PetscFunctionBegin; PetscErrorCode ierr; DM_libMesh *dlm = (DM_libMesh *)(dm->data); NonlinearImplicitSystem *sys = dlm->sys; IS emb; if(dlm->decomposition_type != DMLIBMESH_DOMAIN_DECOMPOSITION) PetscFunctionReturn(0); *len = dlm->decomposition->size(); if(namelist) {ierr = PetscMalloc(*len*sizeof(char*), namelist); CHKERRQ(ierr);} if(innerislist) {ierr = PetscMalloc(*len*sizeof(IS), innerislist); CHKERRQ(ierr);} if(outerislist) *outerislist = PETSC_NULL; /* FIX: allow mesh-based overlap. */ if(dmlist) {ierr = PetscMalloc(*len*sizeof(DM), dmlist); CHKERRQ(ierr);} for(unsigned int d = 0; d < dlm->decomposition->size(); ++d) { std::set<unsigned int> dindices; std::string dname; std::map<std::string, unsigned int> dblockids; std::map<unsigned int,std::string> dblocknames; unsigned int dbcount = 0; for(std::set<unsigned int>::const_iterator bit = (*dlm->decomposition)[d].begin(); bit != (*dlm->decomposition)[d].end(); ++bit){ unsigned int b = *bit; std::string bname = (*dlm->blocknames)[b]; dblockids.insert(std::pair<std::string, unsigned int>(bname,b)); dblocknames.insert(std::pair<unsigned int,std::string>(b,bname)); if(!dbcount) dname = bname; else dname += "_" + bname; ++dbcount; if(!innerislist) continue; MeshBase::const_element_iterator el = sys->get_mesh().active_local_subdomain_elements_begin(b); const MeshBase::const_element_iterator end_el = sys->get_mesh().active_local_subdomain_elements_end(b); for ( ; el != end_el; ++el) { const Elem* elem = *el; std::vector<unsigned int> evindices; /* Iterate only over this DM's variables. */ for(std::map<std::string, unsigned int>::const_iterator vit = dlm->varids->begin(); vit != dlm->varids->end(); ++vit) { unsigned int v = vit->second; // Get the degree of freedom indices for the given variable off the current element. sys->get_dof_map().dof_indices(elem, evindices, v); for(unsigned int i = 0; i < evindices.size(); ++i) { unsigned int dof = evindices[i]; if(dof >= sys->get_dof_map().first_dof() && dof < sys->get_dof_map().end_dof()) /* might want to use variable_first/last_local_dof instead */ dindices.insert(dof); } } } } if(namelist) { ierr = PetscStrallocpy(dname.c_str(),(*namelist)+d); CHKERRQ(ierr); } if(innerislist) { PetscInt *darray; IS dis; ierr = PetscMalloc(sizeof(PetscInt)*dindices.size(), &darray); CHKERRQ(ierr); unsigned int i = 0; for(std::set<unsigned int>::const_iterator it = dindices.begin(); it != dindices.end(); ++it) { darray[i] = *it; ++i; } ierr = ISCreateGeneral(((PetscObject)dm)->comm, dindices.size(),darray, PETSC_OWN_POINTER, &dis); CHKERRQ(ierr); if(dlm->embedding) { /* Create a relative embedding into the parent's index space. */ ierr = ISMapFactorRight(dis,dlm->embedding, PETSC_TRUE, &emb); CHKERRQ(ierr); PetscInt elen, dlen; ierr = ISGetLocalSize(emb, &elen); CHKERRQ(ierr); ierr = ISGetLocalSize(dis, &dlen); CHKERRQ(ierr); if(elen != dlen) SETERRQ1(((PetscObject)dm)->comm, PETSC_ERR_PLIB, "Failed to embed field %D", d); ierr = ISDestroy(&dis); CHKERRQ(ierr); dis = emb; } else { emb = dis; } if(innerislist) { ierr = PetscObjectReference((PetscObject)dis); CHKERRQ(ierr); (*innerislist)[d] = dis; } ierr = ISDestroy(&dis); CHKERRQ(ierr); } if(dmlist) { DM ddm; ierr = DMCreate(((PetscObject)dm)->comm, &ddm); CHKERRQ(ierr); ierr = DMSetType(ddm, DMLIBMESH); CHKERRQ(ierr); DM_libMesh *ddlm = (DM_libMesh*)(ddm->data); ddlm->sys = dlm->sys; /* copy over the varids and varnames */ *ddlm->varids = *dlm->varids; *ddlm->varnames = *dlm->varnames; /* set the blocks from the d-th part of the decomposition. */ *ddlm->blockids = dblockids; *ddlm->blocknames = dblocknames; ierr = PetscObjectReference((PetscObject)emb); CHKERRQ(ierr); ddlm->embedding = emb; ddlm->embedding_type = DMLIBMESH_DOMAIN_EMBEDDING; ierr = DMLibMeshSetUpName_Private(ddm); CHKERRQ(ierr); ierr = DMSetFromOptions(ddm); CHKERRQ(ierr); (*dmlist)[d] = ddm; } } PetscFunctionReturn(0); }
// Here we compute the residual R(x) = K(x)*x - f. The current solution // x is passed in the soln vector void LaplaceYoung::residual (const NumericVector<Number> &soln, NumericVector<Number> &residual, NonlinearImplicitSystem &system) { // Get a constant reference to the mesh object. const MeshBase& mesh = es.get_mesh(); // The dimension that we are running const unsigned int dim = mesh.mesh_dimension(); libmesh_assert_equal_to (dim, 2); // A reference to the \p DofMap object for this system. The \p DofMap // object handles the index translation from node and element numbers // to degree of freedom numbers. We will talk more about the \p DofMap // in future examples. const DofMap& dof_map = system.get_dof_map(); // Get a constant reference to the Finite Element type // for the first (and only) variable in the system. FEType fe_type = dof_map.variable_type(0); // Build a Finite Element object of the specified type. Since the // \p FEBase::build() member dynamically creates memory we will // store the object as an \p AutoPtr<FEBase>. This can be thought // of as a pointer that will clean up after itself. AutoPtr<FEBase> fe (FEBase::build(dim, fe_type)); // A 5th order Gauss quadrature rule for numerical integration. QGauss qrule (dim, FIFTH); // Tell the finite element object to use our quadrature rule. fe->attach_quadrature_rule (&qrule); // Declare a special finite element object for // boundary integration. AutoPtr<FEBase> fe_face (FEBase::build(dim, fe_type)); // Boundary integration requires one quadraure rule, // with dimensionality one less than the dimensionality // of the element. QGauss qface(dim-1, FIFTH); // Tell the finte element object to use our // quadrature rule. fe_face->attach_quadrature_rule (&qface); // Here we define some references to cell-specific data that // will be used to assemble the linear system. // We begin with the element Jacobian * quadrature weight at each // integration point. const std::vector<Real>& JxW = fe->get_JxW(); // The element shape functions evaluated at the quadrature points. const std::vector<std::vector<Real> >& phi = fe->get_phi(); // The element shape function gradients evaluated at the quadrature // points. const std::vector<std::vector<RealGradient> >& dphi = fe->get_dphi(); // Define data structures to contain the resdual contributions DenseVector<Number> Re; // This vector will hold the degree of freedom indices for // the element. These define where in the global system // the element degrees of freedom get mapped. std::vector<dof_id_type> dof_indices; // Now we will loop over all the active elements in the mesh which // are local to this processor. // We will compute the element residual. residual.zero(); MeshBase::const_element_iterator el = mesh.active_local_elements_begin(); const MeshBase::const_element_iterator end_el = mesh.active_local_elements_end(); for ( ; el != end_el; ++el) { // Store a pointer to the element we are currently // working on. This allows for nicer syntax later. const Elem* elem = *el; // Get the degree of freedom indices for the // current element. These define where in the global // matrix and right-hand-side this element will // contribute to. dof_map.dof_indices (elem, dof_indices); // Compute the element-specific data for the current // element. This involves computing the location of the // quadrature points (q_point) and the shape functions // (phi, dphi) for the current element. fe->reinit (elem); // We use the resize member here because // the number of degrees of freedom might have changed from // the last element. Note that this will be the case if the // element type is different (i.e. the last element was a // triangle, now we are on a quadrilateral). Re.resize (dof_indices.size()); // Now we will build the residual. This involves // the construction of the matrix K and multiplication of it // with the current solution x. We rearrange this into two loops: // In the first, we calculate only the contribution of // K_ij*x_j which is independent of the row i. In the second loops, // we multiply with the row-dependent part and add it to the element // residual. for (unsigned int qp=0; qp<qrule.n_points(); qp++) { Number u = 0; Gradient grad_u; for (unsigned int j=0; j<phi.size(); j++) { u += phi[j][qp]*soln(dof_indices[j]); grad_u += dphi[j][qp]*soln(dof_indices[j]); } const Number K = 1./std::sqrt(1. + grad_u*grad_u); for (unsigned int i=0; i<phi.size(); i++) Re(i) += JxW[qp]*( K*(dphi[i][qp]*grad_u) + kappa*phi[i][qp]*u ); } // At this point the interior element integration has // been completed. However, we have not yet addressed // boundary conditions. // The following loops over the sides of the element. // If the element has no neighbor on a side then that // side MUST live on a boundary of the domain. for (unsigned int side=0; side<elem->n_sides(); side++) if (elem->neighbor(side) == NULL) { // The value of the shape functions at the quadrature // points. const std::vector<std::vector<Real> >& phi_face = fe_face->get_phi(); // The Jacobian * Quadrature Weight at the quadrature // points on the face. const std::vector<Real>& JxW_face = fe_face->get_JxW(); // Compute the shape function values on the element face. fe_face->reinit(elem, side); // Loop over the face quadrature points for integration. for (unsigned int qp=0; qp<qface.n_points(); qp++) { // This is the right-hand-side contribution (f), // which has to be subtracted from the current residual for (unsigned int i=0; i<phi_face.size(); i++) Re(i) -= JxW_face[qp]*sigma*phi_face[i][qp]; } } dof_map.constrain_element_vector (Re, dof_indices); residual.add_vector (Re, dof_indices); } // That's it. }